Compare commits
	
		
			2 Commits
		
	
	
		
			2b054d1728
			...
			ce43da79b4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ce43da79b4 | |||
| 5ceddddd81 | 
@ -1,3 +1,5 @@
 | 
			
		||||
export const EPOCH = 1734307200_000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be the start of winter in 1999 iteration 0
 | 
			
		||||
 | 
			
		||||
const millisecondsPerSecond = 1000;
 | 
			
		||||
const secondsPerMinute = 60;
 | 
			
		||||
const minutesPerHour = 60;
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import type { RequestHandler } from "express";
 | 
			
		||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
			
		||||
import { getInventory } from "../../services/inventoryService.ts";
 | 
			
		||||
import { EPOCH, getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
 | 
			
		||||
import { unixTimesInMs } from "../../constants/timeConstants.ts";
 | 
			
		||||
import { getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
 | 
			
		||||
import { EPOCH, unixTimesInMs } from "../../constants/timeConstants.ts";
 | 
			
		||||
import type { ISeasonChallenge } from "../../types/worldStateTypes.ts";
 | 
			
		||||
import { ExportChallenges } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										430
									
								
								src/services/conquestService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								src/services/conquestService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,430 @@
 | 
			
		||||
import { ExportFactions, type TFaction, type TMissionType } from "warframe-public-export-plus";
 | 
			
		||||
import type { CalendarSeasonType, IConquest, IConquestMission, TConquestType } from "../types/worldStateTypes.ts";
 | 
			
		||||
import { mixSeeds, SRng } from "./rngService.ts";
 | 
			
		||||
import { EPOCH } from "../constants/timeConstants.ts";
 | 
			
		||||
 | 
			
		||||
/*const missionTypes: Record<TConquestType, TMissionType[]> = {
 | 
			
		||||
    CT_LAB: ["MT_EXTERMINATION", "MT_SURVIVAL", "MT_ALCHEMY", "MT_DEFENSE", "MT_ARTIFACT"],
 | 
			
		||||
    CT_HEX: ["MT_EXTERMINATION", "MT_SURVIVAL", "MT_DEFENSE", "MT_ENDLESS_CAPTURE"]
 | 
			
		||||
};*/
 | 
			
		||||
 | 
			
		||||
const missionAndFactionTypes: Record<TConquestType, Partial<Record<TMissionType, TFaction[]>>> = {
 | 
			
		||||
    CT_LAB: {
 | 
			
		||||
        MT_EXTERMINATION: ["FC_MITW"],
 | 
			
		||||
        MT_SURVIVAL: ["FC_MITW"],
 | 
			
		||||
        MT_ALCHEMY: ["FC_MITW"],
 | 
			
		||||
        MT_DEFENSE: ["FC_MITW"],
 | 
			
		||||
        MT_ARTIFACT: ["FC_MITW"]
 | 
			
		||||
    },
 | 
			
		||||
    CT_HEX: {
 | 
			
		||||
        MT_EXTERMINATION: ["FC_SCALDRA", "FC_TECHROT"],
 | 
			
		||||
        MT_SURVIVAL: ["FC_SCALDRA", "FC_TECHROT"],
 | 
			
		||||
        MT_DEFENSE: ["FC_SCALDRA"],
 | 
			
		||||
        MT_ENDLESS_CAPTURE: ["FC_TECHROT"]
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const assassinationFactionOptions: Record<TConquestType, TFaction[]> = {
 | 
			
		||||
    CT_LAB: ["FC_MITW"],
 | 
			
		||||
    CT_HEX: ["FC_SCALDRA"]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type TConquestDifficulty = "CD_NORMAL" | "CD_HARD";
 | 
			
		||||
 | 
			
		||||
interface IConquestConditional {
 | 
			
		||||
    tag: string;
 | 
			
		||||
    missionType?: TMissionType;
 | 
			
		||||
    conquest?: TConquestType;
 | 
			
		||||
    difficulty?: TConquestDifficulty;
 | 
			
		||||
    season?: CalendarSeasonType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const deviations: readonly IConquestConditional[] = [
 | 
			
		||||
    {
 | 
			
		||||
        tag: "AlchemicalShields",
 | 
			
		||||
        missionType: "MT_ALCHEMY"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ContaminationZone",
 | 
			
		||||
        missionType: "MT_SURVIVAL",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "DoubleTrouble",
 | 
			
		||||
        missionType: "MT_ARTIFACT"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "EscalateImmediately",
 | 
			
		||||
        missionType: "MT_EXTERMINATION",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "EximusGrenadiers",
 | 
			
		||||
        missionType: "MT_ALCHEMY"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "FortifiedFoes",
 | 
			
		||||
        missionType: "MT_EXTERMINATION"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "FragileNodes",
 | 
			
		||||
        missionType: "MT_ARTIFACT"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "GrowingIncursion",
 | 
			
		||||
        missionType: "MT_EXTERMINATION",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "HarshWords",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "HighScalingLegacyte",
 | 
			
		||||
        missionType: "MT_ENDLESS_CAPTURE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "DoubleTroubleLegacyte",
 | 
			
		||||
        missionType: "MT_ENDLESS_CAPTURE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "HostileSecurity",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "InfiniteTide",
 | 
			
		||||
        missionType: "MT_ASSASSINATION",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "LostInTranslation",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "MutatedEnemies",
 | 
			
		||||
        missionType: "MT_ENDLESS_CAPTURE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "NecramechActivation",
 | 
			
		||||
        missionType: "MT_SURVIVAL",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "Reinforcements",
 | 
			
		||||
        missionType: "MT_ASSASSINATION",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "StickyFingers",
 | 
			
		||||
        missionType: "MT_ARTIFACT",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "TankStrongArmor",
 | 
			
		||||
        missionType: "MT_ASSASSINATION",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "TankReinforcements",
 | 
			
		||||
        missionType: "MT_ASSASSINATION",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "TankSuperToxic",
 | 
			
		||||
        missionType: "MT_ASSASSINATION",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "TechrotConjunction",
 | 
			
		||||
        missionType: "MT_SURVIVAL",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "UnpoweredCapsules",
 | 
			
		||||
        missionType: "MT_SURVIVAL",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "VolatileGrenades",
 | 
			
		||||
        missionType: "MT_ALCHEMY"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "GestatingTumors",
 | 
			
		||||
        missionType: "MT_SURVIVAL",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ChemicalNoise",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ExplosiveEnergy",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "DisruptiveSounds",
 | 
			
		||||
        missionType: "MT_DEFENSE",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const risks: readonly IConquestConditional[] = [
 | 
			
		||||
    {
 | 
			
		||||
        tag: "Voidburst"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "RegeneratingEnemies"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "VoidAberration"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ShieldedFoes"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "PointBlank"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "Deflectors",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "AcceleratedEnemies"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "DrainingResiduals"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "Quicksand"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "AntiMaterialWeapons",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ExplosiveCrawlers",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "EMPBlackHole",
 | 
			
		||||
        conquest: "CT_LAB"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ArtilleryBeacons",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "InfectedTechrot",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "BalloonFest",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "MiasmiteHive",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "CompetitionSpillover",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "HostileOvergrowth",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "MurmurIncursion",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "FactionSwarm_Techrot",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        difficulty: "CD_NORMAL"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "FactionSwarm_Scaldra",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        difficulty: "CD_NORMAL"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "HeavyWarfare",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ArcadeAutomata",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        difficulty: "CD_NORMAL"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "EfervonFog",
 | 
			
		||||
        conquest: "CT_HEX"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "WinterFrost",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        season: "CST_WINTER"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "JadeSpring",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        season: "CST_SPRING"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "ExplosiveSummer",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        season: "CST_SUMMER"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        tag: "FallFog",
 | 
			
		||||
        conquest: "CT_HEX",
 | 
			
		||||
        season: "CST_FALL"
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const filterConditionals = (
 | 
			
		||||
    arr: readonly IConquestConditional[],
 | 
			
		||||
    missionType: TMissionType | null,
 | 
			
		||||
    conquest: TConquestType | null,
 | 
			
		||||
    difficulty: TConquestDifficulty | null,
 | 
			
		||||
    season: CalendarSeasonType | null
 | 
			
		||||
): string[] => {
 | 
			
		||||
    const applicable = [];
 | 
			
		||||
    for (const cond of arr) {
 | 
			
		||||
        if (
 | 
			
		||||
            (!cond.missionType || cond.missionType == missionType) &&
 | 
			
		||||
            (!cond.conquest || cond.conquest == conquest) &&
 | 
			
		||||
            (!cond.difficulty || cond.difficulty == difficulty) &&
 | 
			
		||||
            (!cond.season || cond.season == season)
 | 
			
		||||
        ) {
 | 
			
		||||
            applicable.push(cond.tag);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return applicable;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const buildMission = (
 | 
			
		||||
    rng: SRng,
 | 
			
		||||
    conquest: TConquestType,
 | 
			
		||||
    missionType: TMissionType,
 | 
			
		||||
    faction: TFaction,
 | 
			
		||||
    season: CalendarSeasonType | null
 | 
			
		||||
): IConquestMission => {
 | 
			
		||||
    const deviation = rng.randomElement(filterConditionals(deviations, missionType, conquest, null, season))!;
 | 
			
		||||
    const easyRisk = rng.randomElement(filterConditionals(risks, missionType, conquest, "CD_NORMAL", season))!;
 | 
			
		||||
    const hardRiskOptions = filterConditionals(risks, missionType, conquest, "CD_HARD", season);
 | 
			
		||||
    {
 | 
			
		||||
        const i = hardRiskOptions.indexOf(easyRisk);
 | 
			
		||||
        if (i != -1) {
 | 
			
		||||
            hardRiskOptions.splice(i, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const hardRisk = rng.randomElement(hardRiskOptions)!;
 | 
			
		||||
    return {
 | 
			
		||||
        faction,
 | 
			
		||||
        missionType,
 | 
			
		||||
        difficulties: [
 | 
			
		||||
            {
 | 
			
		||||
                type: "CD_NORMAL",
 | 
			
		||||
                deviation,
 | 
			
		||||
                risks: [easyRisk]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                type: "CD_HARD",
 | 
			
		||||
                deviation,
 | 
			
		||||
                risks: [easyRisk, hardRisk]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const conquestStartingDay: Record<TConquestType, number> = {
 | 
			
		||||
    CT_LAB: 3703,
 | 
			
		||||
    CT_HEX: 4053
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This function produces identical results to clients pre-40.0.0.
 | 
			
		||||
const getFrameVariables = (conquestType: TConquestType, time: number): [string, string, string, string] => {
 | 
			
		||||
    const day = Math.floor((time - 1391990400_000) / 86400_000) - conquestStartingDay[conquestType];
 | 
			
		||||
    const week = Math.floor(day / 7) + 1;
 | 
			
		||||
    const frameVariables = [
 | 
			
		||||
        "Framecurse",
 | 
			
		||||
        "Knifestep",
 | 
			
		||||
        "Exhaustion",
 | 
			
		||||
        "Gearless",
 | 
			
		||||
        "TimeDilation",
 | 
			
		||||
        "Armorless",
 | 
			
		||||
        "Starvation",
 | 
			
		||||
        "ShieldDelay",
 | 
			
		||||
        "Withering",
 | 
			
		||||
        "ContactDamage",
 | 
			
		||||
        "AbilityLockout",
 | 
			
		||||
        "OperatorLockout",
 | 
			
		||||
        "EnergyStarved",
 | 
			
		||||
        "OverSensitive",
 | 
			
		||||
        "AntiGuard",
 | 
			
		||||
        "DecayingFlesh",
 | 
			
		||||
        "VoidEnergyOverload",
 | 
			
		||||
        "DullBlades",
 | 
			
		||||
        "Undersupplied"
 | 
			
		||||
    ];
 | 
			
		||||
    const mag = Math.floor(frameVariables.length / 4);
 | 
			
		||||
    const rng = new SRng(conquestStartingDay[conquestType] + Math.floor(week / mag));
 | 
			
		||||
    rng.shuffleArray(frameVariables);
 | 
			
		||||
    const i = week % mag;
 | 
			
		||||
    return [frameVariables[i], frameVariables[i + 1], frameVariables[i + 2], frameVariables[i + 3]];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getConquest = (
 | 
			
		||||
    conquestType: TConquestType,
 | 
			
		||||
    week: number,
 | 
			
		||||
    season: CalendarSeasonType | null
 | 
			
		||||
): IConquest => {
 | 
			
		||||
    const rng = new SRng(mixSeeds(conquestStartingDay[conquestType], week));
 | 
			
		||||
 | 
			
		||||
    const missions: IConquestMission[] = [];
 | 
			
		||||
    {
 | 
			
		||||
        const missionOptions = Object.entries(missionAndFactionTypes[conquestType]);
 | 
			
		||||
        {
 | 
			
		||||
            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
			
		||||
            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
			
		||||
            missions.push(
 | 
			
		||||
                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
			
		||||
            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
			
		||||
            missions.push(
 | 
			
		||||
                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        missionOptions.push(["MT_ASSASSINATION", assassinationFactionOptions[conquestType]]);
 | 
			
		||||
        {
 | 
			
		||||
            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
			
		||||
            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
			
		||||
            missions.push(
 | 
			
		||||
                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const weekStart = EPOCH + week * 604800000;
 | 
			
		||||
    const weekEnd = weekStart + 604800000;
 | 
			
		||||
    return {
 | 
			
		||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
			
		||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
			
		||||
        Type: conquestType,
 | 
			
		||||
        Missions: missions,
 | 
			
		||||
        Variables: getFrameVariables(conquestType, weekStart),
 | 
			
		||||
        RandomSeed: rng.randomInt(0, 1_000_000)
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@ -10,7 +10,7 @@ import invasionNodes from "../../static/fixed_responses/worldState/invasionNodes
 | 
			
		||||
import invasionRewards from "../../static/fixed_responses/worldState/invasionRewards.json" with { type: "json" };
 | 
			
		||||
import pvpChallenges from "../../static/fixed_responses/worldState/pvpChallenges.json" with { type: "json" };
 | 
			
		||||
import { buildConfig } from "./buildConfigService.ts";
 | 
			
		||||
import { unixTimesInMs } from "../constants/timeConstants.ts";
 | 
			
		||||
import { EPOCH, unixTimesInMs } from "../constants/timeConstants.ts";
 | 
			
		||||
import { config } from "./configService.ts";
 | 
			
		||||
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "./rngService.ts";
 | 
			
		||||
import type { IMissionReward, IRegion, TFaction } from "warframe-public-export-plus";
 | 
			
		||||
@ -41,6 +41,7 @@ import type {
 | 
			
		||||
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers.ts";
 | 
			
		||||
import { logger } from "../utils/logger.ts";
 | 
			
		||||
import { DailyDeal, Fissure } from "../models/worldStateModel.ts";
 | 
			
		||||
import { getConquest } from "./conquestService.ts";
 | 
			
		||||
 | 
			
		||||
const sortieBosses = [
 | 
			
		||||
    "SORTIE_BOSS_HYENA",
 | 
			
		||||
@ -276,8 +277,6 @@ const microplanetEndlessJobs: readonly string[] = [
 | 
			
		||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
 | 
			
		||||
 | 
			
		||||
const isBeforeNextExpectedWorldStateRefresh = (nowMs: number, thenMs: number): boolean => {
 | 
			
		||||
    return nowMs + 300_000 > thenMs;
 | 
			
		||||
};
 | 
			
		||||
@ -3469,6 +3468,18 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
 | 
			
		||||
    {
 | 
			
		||||
        const rollover = getSortieTime(day);
 | 
			
		||||
@ -3551,16 +3562,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);
 | 
			
		||||
    if (!buildLabel || version_compare(buildLabel, "2025.10.14.16.10") >= 0) {
 | 
			
		||||
        worldState.Conquests = [];
 | 
			
		||||
        {
 | 
			
		||||
            const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[week % 4];
 | 
			
		||||
            worldState.Conquests.push(getConquest("CT_LAB", week, null));
 | 
			
		||||
            worldState.Conquests.push(getConquest("CT_HEX", week, season));
 | 
			
		||||
        }
 | 
			
		||||
        if (isBeforeNextExpectedWorldStateRefresh(timeMs, weekEnd)) {
 | 
			
		||||
            const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[(week + 1) % 4];
 | 
			
		||||
            worldState.Conquests.push(getConquest("CT_LAB", week, null));
 | 
			
		||||
            worldState.Conquests.push(getConquest("CT_HEX", week, season));
 | 
			
		||||
        }
 | 
			
		||||
    pushVoidStorms(worldState.VoidStorms, hour - 1);
 | 
			
		||||
    if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
 | 
			
		||||
        pushVoidStorms(worldState.VoidStorms, hour);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sentient Anomaly + Xtra Cheese cycles
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import type { IOrbiterClient } from "../personalRoomsTypes.ts";
 | 
			
		||||
import type { ICountedStoreItem } from "warframe-public-export-plus";
 | 
			
		||||
import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
 | 
			
		||||
import type { ILoadOutPresets } from "../saveLoadoutTypes.ts";
 | 
			
		||||
import type { CalendarSeasonType } from "../worldStateTypes.ts";
 | 
			
		||||
 | 
			
		||||
export type InventoryDatabaseEquipment = {
 | 
			
		||||
    [_ in TEquipmentKey]: IEquipmentDatabase[];
 | 
			
		||||
@ -1180,7 +1181,7 @@ export interface IMarker {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ISeasonProgress {
 | 
			
		||||
    SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
			
		||||
    SeasonType: CalendarSeasonType;
 | 
			
		||||
    LastCompletedDayIdx: number;
 | 
			
		||||
    LastCompletedChallengeDayIdx: number;
 | 
			
		||||
    ActivatedChallenges: string[];
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ export interface IWorldState {
 | 
			
		||||
        ActiveChallenges: ISeasonChallenge[];
 | 
			
		||||
    };
 | 
			
		||||
    KnownCalendarSeasons: ICalendarSeason[];
 | 
			
		||||
    Conquests?: IConquest[];
 | 
			
		||||
    Tmp?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -352,10 +353,11 @@ export interface ISeasonChallenge {
 | 
			
		||||
    Challenge: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CalendarSeasonType = "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
			
		||||
export interface ICalendarSeason {
 | 
			
		||||
    Activation: IMongoDate;
 | 
			
		||||
    Expiry: IMongoDate;
 | 
			
		||||
    Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
			
		||||
    Season: CalendarSeasonType;
 | 
			
		||||
    Days: ICalendarDay[];
 | 
			
		||||
    YearIteration: number;
 | 
			
		||||
    Version: number;
 | 
			
		||||
@ -416,6 +418,33 @@ export interface IGameMarketCategory {
 | 
			
		||||
    Items?: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// >= 40.0.0
 | 
			
		||||
export type TConquestType = "CT_LAB" | "CT_HEX";
 | 
			
		||||
export interface IConquest {
 | 
			
		||||
    Activation: IMongoDate;
 | 
			
		||||
    Expiry: IMongoDate;
 | 
			
		||||
    Type: TConquestType;
 | 
			
		||||
    Missions: IConquestMission[];
 | 
			
		||||
    Variables: [string, string, string, string];
 | 
			
		||||
    RandomSeed: number;
 | 
			
		||||
}
 | 
			
		||||
export interface IConquestMission {
 | 
			
		||||
    faction: TFaction;
 | 
			
		||||
    missionType: TMissionType;
 | 
			
		||||
    difficulties: [
 | 
			
		||||
        {
 | 
			
		||||
            type: "CD_NORMAL";
 | 
			
		||||
            deviation: string;
 | 
			
		||||
            risks: [string];
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            type: "CD_HARD";
 | 
			
		||||
            deviation: string;
 | 
			
		||||
            risks: [string, string];
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITmp {
 | 
			
		||||
    cavabegin: string;
 | 
			
		||||
    PurchasePlatformLockEnabled: boolean; // Seems unused
 | 
			
		||||
@ -423,6 +452,8 @@ export interface ITmp {
 | 
			
		||||
    ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
 | 
			
		||||
    mbrt?: boolean; // Related to mobile app rating request
 | 
			
		||||
    fbst: IFbst;
 | 
			
		||||
    lqo?: IConquestOverride;
 | 
			
		||||
    hqo?: IConquestOverride;
 | 
			
		||||
    sfn: number;
 | 
			
		||||
    edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
 | 
			
		||||
}
 | 
			
		||||
@ -451,3 +482,12 @@ interface IFbst {
 | 
			
		||||
    e: number;
 | 
			
		||||
    n: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// < 40.0.0
 | 
			
		||||
interface IConquestOverride {
 | 
			
		||||
    mt?: string[]; // mission types but "Exterminate" instead of "MT_EXTERMINATION", etc. and "DualDefense" instead of "Defense" for hex conquest
 | 
			
		||||
    mv?: string[];
 | 
			
		||||
    mf?: number[]; // hex conquest only
 | 
			
		||||
    c?: [string, string][];
 | 
			
		||||
    fv?: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user