From da6d75c74897f1ac8f21e2772bb94d766c839635 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Tue, 6 May 2025 02:29:11 -0700 Subject: [PATCH] fix: don't give sortie assassination rewards if mission type differs (#1992) Closes #1987 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1992 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- src/services/missionInventoryUpdateService.ts | 21 ++++++- src/services/worldStateService.ts | 60 ++++++++++--------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 9a8d770b..df7c6121 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -58,7 +58,7 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers"; import { Loadout } from "../models/inventoryModels/loadoutModel"; import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes"; -import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService"; +import { getLiteSortie, getSortie, getWorldState, idToDay, idToWeek } from "./worldStateService"; import { config } from "./configService"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; @@ -1357,9 +1357,24 @@ function getRandomMissionDrops( // Invasion assassination has Phorid has the boss who should drop Nyx parts // TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"]; - } else if (RewardInfo.sortieId && region.missionIndex != 0) { + } else if (RewardInfo.sortieId) { // Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes. Assassinations are an exception to this. - rewardManifests = []; + if (region.missionIndex == 0) { + const arr = RewardInfo.sortieId.split("_"); + let sortieId = arr[1]; + if (sortieId == "Lite") { + sortieId = arr[2]; + } + const sortie = getSortie(idToDay(sortieId)); + const mission = sortie.Variants.find(x => x.node == arr[0])!; + if (mission.missionType == "MT_ASSASSINATION") { + rewardManifests = region.rewardManifests; + } else { + rewardManifests = []; + } + } else { + rewardManifests = []; + } } else { rewardManifests = region.rewardManifests; } diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index a8129194..669824ab 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -12,6 +12,7 @@ import { ICalendarSeason, ILiteSortie, ISeasonChallenge, + ISortie, ISortieMission, IWorldState } from "../types/worldStateTypes"; @@ -193,12 +194,6 @@ const pushSyndicateMissions = ( idSuffix: string, syndicateTag: string ): void => { - const dayStart = getSortieTime(day); - if (Date.now() >= dayStart) { - return; // The client does not seem to respect activation. - } - const dayEnd = getSortieTime(day + 1); - const nodeOptions: string[] = [...syndicateMissions]; const rng = new CRng(seed); @@ -209,6 +204,8 @@ const pushSyndicateMissions = ( nodeOptions.splice(index, 1); } + const dayStart = getSortieTime(day); + const dayEnd = getSortieTime(day + 1); worldState.SyndicateMissions.push({ _id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + idSuffix }, Activation: { $date: { $numberLong: dayStart.toString() } }, @@ -219,16 +216,7 @@ const pushSyndicateMissions = ( }); }; -const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => { - const dayStart = getSortieTime(day); - if (!isBeforeNextExpectedWorldStateRefresh(dayStart)) { - return; - } - const dayEnd = getSortieTime(day + 1); - if (Date.now() >= dayEnd) { - return; - } - +export const getSortie = (day: number): ISortie => { const seed = new CRng(day).randomInt(0, 0xffff); const rng = new CRng(seed); @@ -375,7 +363,9 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => { missionTypes.add(missionType); } - worldState.Sorties.push({ + const dayStart = getSortieTime(day); + const dayEnd = getSortieTime(day + 1); + return { _id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "d4d932c97c0a3acd" }, Activation: { $date: { $numberLong: dayStart.toString() } }, Expiry: { $date: { $numberLong: dayEnd.toString() } }, @@ -383,14 +373,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => { Seed: seed, Boss: boss, Variants: selectedNodes - }); - - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48049", "ArbitersSyndicate"); - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804a", "CephalonSudaSyndicate"); - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804e", "NewLokaSyndicate"); - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48050", "PerrinSyndicate"); - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4805e", "RedVeilSyndicate"); - pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48061", "SteelMeridianSyndicate"); + }; }; const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x => @@ -1099,8 +1082,25 @@ export const getWorldState = (buildLabel?: string): IWorldState => { } // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST) - pushSortieIfRelevant(worldState, day - 1); - pushSortieIfRelevant(worldState, day); + { + const rollover = getSortieTime(day); + + if (Date.now() < rollover) { + worldState.Sorties.push(getSortie(day - 1)); + } + if (isBeforeNextExpectedWorldStateRefresh(rollover)) { + worldState.Sorties.push(getSortie(day)); + } + + // The client does not seem to respect activation for classic syndicate missions, so only pushing current ones. + const rng = new CRng(Date.now() >= rollover ? day : day - 1); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48049", "ArbitersSyndicate"); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804a", "CephalonSudaSyndicate"); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804e", "NewLokaSyndicate"); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48050", "PerrinSyndicate"); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4805e", "RedVeilSyndicate"); + pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48061", "SteelMeridianSyndicate"); + } // Archon Hunt cycling every week worldState.LiteSorties.push(getLiteSortie(week)); @@ -1178,8 +1178,12 @@ export const getWorldState = (buildLabel?: string): IWorldState => { return worldState; }; +export const idToDay = (id: string): number => { + return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 86400_000); +}; + export const idToWeek = (id: string): number => { - return (parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800000; + return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800_000); }; export const getLiteSortie = (week: number): ILiteSortie => {