diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index c5e30868..253bdc21 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -16,6 +16,7 @@ import { ISortie, ISortieMission, ISyndicateMissionInfo, + IVoidStorm, IWorldState } from "../types/worldStateTypes"; import { version_compare } from "../helpers/inventoryHelpers"; @@ -963,6 +964,61 @@ const getCalendarSeason = (week: number): ICalendarSeason => { }; }; +// Not very faithful, but to avoid the same node coming up back-to-back (which is not valid), I've split these into 2 arrays which we're alternating between. + +const voidStormMissionsA = { + VoidT1: ["CrewBattleNode519", "CrewBattleNode518", "CrewBattleNode515", "CrewBattleNode503"], + VoidT2: ["CrewBattleNode501", "CrewBattleNode534", "CrewBattleNode530"], + VoidT3: ["CrewBattleNode521", "CrewBattleNode516"], + VoidT4: [ + "CrewBattleNode555", + "CrewBattleNode553", + "CrewBattleNode554", + "CrewBattleNode539", + "CrewBattleNode531", + "CrewBattleNode527" + ] +}; + +const voidStormMissionsB = { + VoidT1: ["CrewBattleNode509", "CrewBattleNode522", "CrewBattleNode511", "CrewBattleNode512"], + VoidT2: ["CrewBattleNode535", "CrewBattleNode533"], + VoidT3: ["CrewBattleNode524", "CrewBattleNode525"], + VoidT4: [ + "CrewBattleNode542", + "CrewBattleNode538", + "CrewBattleNode543", + "CrewBattleNode536", + "CrewBattleNode550", + "CrewBattleNode529" + ] +}; + +const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => { + const activation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute; + const expiry = activation + 90 * unixTimesInMs.minute; + let accum = 0; + const rng = new SRng(new SRng(hour).randomInt(0, 100_000)); + const voidStormMissions = structuredClone(hour & 1 ? voidStormMissionsA : voidStormMissionsB); + for (const tier of ["VoidT1", "VoidT1", "VoidT2", "VoidT3", "VoidT4", "VoidT4"] as const) { + const idx = rng.randomInt(0, voidStormMissions[tier].length - 1); + const node = voidStormMissions[tier][idx]; + voidStormMissions[tier].splice(idx, 1); + arr.push({ + _id: { + $oid: + ((activation / 1000) & 0xffffffff).toString(16).padStart(8, "0") + + "0321e89b" + + (accum++).toString().padStart(8, "0") + }, + Node: node, + Activation: { $date: { $numberLong: activation.toString() } }, + Expiry: { $date: { $numberLong: expiry.toString() } }, + ActiveMissionTier: tier + }); + } +}; + const doesTimeSatsifyConstraints = (timeSecs: number): boolean => { if (config.worldState?.eidolonOverride) { const eidolonEpoch = 1391992660; @@ -1032,6 +1088,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => { Sorties: [], LiteSorties: [], GlobalUpgrades: [], + VoidStorms: [], EndlessXpChoices: [], KnownCalendarSeasons: [], ...staticWorldState, @@ -1228,6 +1285,18 @@ export const getWorldState = (buildLabel?: string): IWorldState => { worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1)); } + // Void Storms + const hour = Math.trunc(timeMs / unixTimesInMs.hour); + const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute; + const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute; + if (overLastHourStormExpiry > timeMs) { + pushVoidStorms(worldState.VoidStorms, hour - 2); + } + pushVoidStorms(worldState.VoidStorms, hour - 1); + if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) { + pushVoidStorms(worldState.VoidStorms, hour); + } + // Sentient Anomaly cycling every 30 minutes const halfHour = Math.trunc(timeMs / (unixTimesInMs.hour / 2)); const tmp = { diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index 78c1f330..880c5800 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -12,6 +12,7 @@ export interface IWorldState { GlobalUpgrades: IGlobalUpgrade[]; ActiveMissions: IFissure[]; NodeOverrides: INodeOverride[]; + VoidStorms: IVoidStorm[]; PVPChallengeInstances: IPVPChallengeInstance[]; EndlessXpChoices: IEndlessXpChoice[]; SeasonInfo?: { @@ -131,6 +132,14 @@ export interface ILiteSortie { }[]; } +export interface IVoidStorm { + _id: IOid; + Node: string; + Activation: IMongoDate; + Expiry: IMongoDate; + ActiveMissionTier: string; +} + export interface IPVPChallengeInstance { _id: IOid; challengeTypeRefID: string; diff --git a/static/fixed_responses/worldState/worldState.json b/static/fixed_responses/worldState/worldState.json index 95b5fde2..73d48ce1 100644 --- a/static/fixed_responses/worldState/worldState.json +++ b/static/fixed_responses/worldState/worldState.json @@ -2562,50 +2562,6 @@ ] } ], - "VoidStorms": [ - { - "_id": { "$oid": "663a7581ced28e18f694b550" }, - "Node": "CrewBattleNode519", - "Activation": { "$date": { "$numberLong": "1715109601821" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT1" - }, - { - "_id": { "$oid": "663a7581ced28e18f694b551" }, - "Node": "CrewBattleNode515", - "Activation": { "$date": { "$numberLong": "1715109601825" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT1" - }, - { - "_id": { "$oid": "663a7581ced28e18f694b554" }, - "Node": "CrewBattleNode536", - "Activation": { "$date": { "$numberLong": "1715109601832" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT4" - }, - { - "_id": { "$oid": "663a7581ced28e18f694b555" }, - "Node": "CrewBattleNode539", - "Activation": { "$date": { "$numberLong": "1715109601834" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT4" - }, - { - "_id": { "$oid": "663a7581ced28e18f694b553" }, - "Node": "CrewBattleNode521", - "Activation": { "$date": { "$numberLong": "1715109601829" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT3" - }, - { - "_id": { "$oid": "663a7581ced28e18f694b552" }, - "Node": "CrewBattleNode535", - "Activation": { "$date": { "$numberLong": "1715109601827" } }, - "Expiry": { "$date": { "$numberLong": "2000000000000" } }, - "ActiveMissionTier": "VoidT2" - } - ], "PrimeAccessAvailability": { "State": "PRIME1" }, "PrimeVaultAvailabilities": [false, false, false, false, false], "PrimeTokenAvailability": true,