From c2a633b549e21b2c56c43b8f0fb4821882402152 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Sat, 19 Apr 2025 09:06:38 -0700 Subject: [PATCH] chore: improve LiteSortie handling at week rollover (#1735) WorldState now provides the upcoming LiteSortie if relevant and the boss is derived from the sortieId so completing it at rollover should work as expected. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1735 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- src/services/missionInventoryUpdateService.ts | 8 +- src/services/worldStateService.ts | 138 ++++++++++-------- src/types/worldStateTypes.ts | 2 +- 3 files changed, 81 insertions(+), 67 deletions(-) diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index a7308aa6..7b27faa9 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -53,7 +53,7 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js import { getInfNodes } from "@/src/helpers/nemesisHelpers"; import { Loadout } from "../models/inventoryModels/loadoutModel"; import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes"; -import { getWorldState } from "./worldStateService"; +import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService"; import { config } from "./configService"; const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => { @@ -990,11 +990,7 @@ function getRandomMissionDrops( if (sortieId == "Lite") { sortieId = arr[2]; - // TODO: Some way to get from sortieId to reward to make this faster + more reliable at week rollover. - const boss = getWorldState().LiteSorties[0].Boss as - | "SORTIE_BOSS_AMAR" - | "SORTIE_BOSS_NIRA" - | "SORTIE_BOSS_BOREAL"; + const boss = getLiteSortie(idToWeek(sortieId)).Boss; let crystalType = { SORTIE_BOSS_AMAR: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar", SORTIE_BOSS_NIRA: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira", diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index ed65a8fc..6083dcb5 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -4,7 +4,14 @@ import { unixTimesInMs } from "@/src/constants/timeConstants"; import { config } from "@/src/services/configService"; import { CRng } from "@/src/services/rngService"; import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus"; -import { ICalendarDay, ICalendarSeason, ISeasonChallenge, ISortie, IWorldState } from "../types/worldStateTypes"; +import { + ICalendarDay, + ICalendarSeason, + ILiteSortie, + ISeasonChallenge, + ISortie, + IWorldState +} from "../types/worldStateTypes"; const sortieBosses = [ "SORTIE_BOSS_HYENA", @@ -941,65 +948,9 @@ export const getWorldState = (buildLabel?: string): IWorldState => { pushSortieIfRelevant(worldState.Sorties, day); // Archon Hunt cycling every week - // TODO: Handle imminent rollover - { - const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3]; - const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3]; - const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth - - const nodes: string[] = []; - for (const [key, value] of Object.entries(ExportRegions)) { - if ( - value.systemIndex === systemIndex && - value.factionIndex !== undefined && - value.factionIndex < 2 && - value.name.indexOf("Archwing") == -1 && - value.missionIndex != 0 // Exclude MT_ASSASSINATION - ) { - nodes.push(key); - } - } - - const rng = new CRng(week); - const firstNodeIndex = rng.randomInt(0, nodes.length - 1); - const firstNode = nodes[firstNodeIndex]; - nodes.splice(firstNodeIndex, 1); - worldState.LiteSorties.push({ - _id: { - $oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c" - }, - Activation: { $date: { $numberLong: weekStart.toString() } }, - Expiry: { $date: { $numberLong: weekEnd.toString() } }, - Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards", - Seed: week, - Boss: boss, - Missions: [ - { - missionType: rng.randomElement([ - "MT_INTEL", - "MT_MOBILE_DEFENSE", - "MT_EXTERMINATION", - "MT_SABOTAGE", - "MT_RESCUE" - ]), - node: firstNode - }, - { - missionType: rng.randomElement([ - "MT_DEFENSE", - "MT_TERRITORY", - "MT_ARTIFACT", - "MT_EXCAVATE", - "MT_SURVIVAL" - ]), - node: rng.randomElement(nodes) - }, - { - missionType: "MT_ASSASSINATION", - node: showdownNode - } - ] - }); + worldState.LiteSorties.push(getLiteSortie(week)); + if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) { + worldState.LiteSorties.push(getLiteSortie(week + 1)); } // Circuit choices cycling every week @@ -1071,3 +1022,70 @@ export const getWorldState = (buildLabel?: string): IWorldState => { return worldState; }; + +export const idToWeek = (id: string): number => { + return (parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800000; +}; + +export const getLiteSortie = (week: number): ILiteSortie => { + const boss = (["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"] as const)[week % 3]; + const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3]; + const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth + + const nodes: string[] = []; + for (const [key, value] of Object.entries(ExportRegions)) { + if ( + value.systemIndex === systemIndex && + value.factionIndex !== undefined && + value.factionIndex < 2 && + value.name.indexOf("Archwing") == -1 && + value.missionIndex != 0 // Exclude MT_ASSASSINATION + ) { + nodes.push(key); + } + } + + const rng = new CRng(week); + const firstNodeIndex = rng.randomInt(0, nodes.length - 1); + const firstNode = nodes[firstNodeIndex]; + nodes.splice(firstNodeIndex, 1); + + const weekStart = EPOCH + week * 604800000; + const weekEnd = weekStart + 604800000; + return { + _id: { + $oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c" + }, + Activation: { $date: { $numberLong: weekStart.toString() } }, + Expiry: { $date: { $numberLong: weekEnd.toString() } }, + Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards", + Seed: week, + Boss: boss, + Missions: [ + { + missionType: rng.randomElement([ + "MT_INTEL", + "MT_MOBILE_DEFENSE", + "MT_EXTERMINATION", + "MT_SABOTAGE", + "MT_RESCUE" + ]), + node: firstNode + }, + { + missionType: rng.randomElement([ + "MT_DEFENSE", + "MT_TERRITORY", + "MT_ARTIFACT", + "MT_EXCAVATE", + "MT_SURVIVAL" + ]), + node: rng.randomElement(nodes) + }, + { + missionType: "MT_ASSASSINATION", + node: showdownNode + } + ] + }; +}; diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index 278f6e95..67178c73 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -103,7 +103,7 @@ export interface ILiteSortie { Expiry: IMongoDate; Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards"; Seed: number; - Boss: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL" + Boss: "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"; Missions: { missionType: string; node: string;