feat: initial invasions #2458
@ -6,10 +6,18 @@ import allDialogue from "@/static/fixed_responses/allDialogue.json";
 | 
				
			|||||||
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
 | 
					import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
 | 
				
			||||||
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
					import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
 | 
					import {
 | 
				
			||||||
 | 
					    eFaction,
 | 
				
			||||||
 | 
					    ExportCustoms,
 | 
				
			||||||
 | 
					    ExportFlavour,
 | 
				
			||||||
 | 
					    ExportResources,
 | 
				
			||||||
 | 
					    ExportVirtuals,
 | 
				
			||||||
 | 
					    ICountedItem
 | 
				
			||||||
 | 
					} from "warframe-public-export-plus";
 | 
				
			||||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
 | 
					import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    addEmailItem,
 | 
					    addEmailItem,
 | 
				
			||||||
 | 
					    addItem,
 | 
				
			||||||
    addMiscItems,
 | 
					    addMiscItems,
 | 
				
			||||||
    allDailyAffiliationKeys,
 | 
					    allDailyAffiliationKeys,
 | 
				
			||||||
    checkCalendarAutoAdvance,
 | 
					    checkCalendarAutoAdvance,
 | 
				
			||||||
@ -30,7 +38,8 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			|||||||
import { DailyDeal } from "@/src/models/worldStateModel";
 | 
					import { DailyDeal } from "@/src/models/worldStateModel";
 | 
				
			||||||
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
 | 
					import { EquipmentFeatures } from "@/src/types/equipmentTypes";
 | 
				
			||||||
import { generateRewardSeed } from "@/src/services/rngService";
 | 
					import { generateRewardSeed } from "@/src/services/rngService";
 | 
				
			||||||
import { getWorldState } from "@/src/services/worldStateService";
 | 
					import { getInvasionByOid, getWorldState } from "@/src/services/worldStateService";
 | 
				
			||||||
 | 
					import { createMessage } from "@/src/services/inboxService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const inventoryController: RequestHandler = async (request, response) => {
 | 
					export const inventoryController: RequestHandler = async (request, response) => {
 | 
				
			||||||
    const account = await getAccountForRequest(request);
 | 
					    const account = await getAccountForRequest(request);
 | 
				
			||||||
@ -186,6 +195,63 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
				
			|||||||
        //await inventory.save();
 | 
					        //await inventory.save();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let i = 0; i != inventory.QualifyingInvasions.length; ) {
 | 
				
			||||||
 | 
					        const qi = inventory.QualifyingInvasions[i];
 | 
				
			||||||
 | 
					        const invasion = getInvasionByOid(qi.invasionId.toString());
 | 
				
			||||||
 | 
					        if (!invasion) {
 | 
				
			||||||
 | 
					            logger.debug(`removing QualifyingInvasions entry for unknown invasion: ${qi.invasionId.toString()}`);
 | 
				
			||||||
 | 
					            inventory.QualifyingInvasions.splice(i, 1);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (invasion.Completed) {
 | 
				
			||||||
 | 
					            let factionSidedWith: string | undefined;
 | 
				
			||||||
 | 
					            let battlePay: ICountedItem[] | undefined;
 | 
				
			||||||
 | 
					            if (qi.AttackerScore >= 3) {
 | 
				
			||||||
 | 
					                factionSidedWith = invasion.Faction;
 | 
				
			||||||
 | 
					                battlePay = invasion.AttackerReward.countedItems;
 | 
				
			||||||
 | 
					                logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
 | 
				
			||||||
 | 
					            } else if (qi.DefenderScore >= 3) {
 | 
				
			||||||
 | 
					                factionSidedWith = invasion.DefenderFaction;
 | 
				
			||||||
 | 
					                battlePay = invasion.DefenderReward.countedItems;
 | 
				
			||||||
 | 
					                logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (factionSidedWith) {
 | 
				
			||||||
 | 
					                if (battlePay) {
 | 
				
			||||||
 | 
					                    // Decoupling rewards from the inbox message because it may delete itself without being read
 | 
				
			||||||
 | 
					                    for (const item of battlePay) {
 | 
				
			||||||
 | 
					                        await addItem(inventory, item.ItemType, item.ItemCount);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    await createMessage(account._id, [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            sndr: eFaction.find(x => x.tag == factionSidedWith)?.name ?? factionSidedWith, // TOVERIFY
 | 
				
			||||||
 | 
					                            msg: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageBody`,
 | 
				
			||||||
 | 
					                            sub: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageSubject`,
 | 
				
			||||||
 | 
					                            countedAtt: battlePay,
 | 
				
			||||||
 | 
					                            attVisualOnly: true,
 | 
				
			||||||
 | 
					                            icon:
 | 
				
			||||||
 | 
					                                factionSidedWith == "FC_GRINEER"
 | 
				
			||||||
 | 
					                                    ? "/Lotus/Interface/Icons/Npcs/EliteRifleLancerAvatar.png" // Source: https://www.reddit.com/r/Warframe/comments/1aj4usx/battle_pay_worth_10_plat/, https://www.youtube.com/watch?v=XhNZ6ai6BOY
 | 
				
			||||||
 | 
					                                    : "/Lotus/Interface/Icons/Npcs/CrewmanNormal.png", // My best source for this is https://www.youtube.com/watch?v=rxrCCFm73XE around 1:37
 | 
				
			||||||
 | 
					                            // TOVERIFY: highPriority?
 | 
				
			||||||
 | 
					                            endDate: new Date(Date.now() + 86400_000) // TOVERIFY: This type of inbox message seems to automatically delete itself. We'll just delete it after 24 hours, but it's not clear if this is correct.
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (invasion.Faction != "FC_INFESTATION") {
 | 
				
			||||||
 | 
					                    // Sided with grineer -> opposed corpus -> send zanuka (harvester)
 | 
				
			||||||
 | 
					                    // Sided with corpus -> opposed grineer -> send g3 (death squad)
 | 
				
			||||||
 | 
					                    inventory[factionSidedWith != "FC_GRINEER" ? "DeathSquadable" : "Harvestable"] = true;
 | 
				
			||||||
 | 
					                    // TOVERIFY: Should this happen earlier?
 | 
				
			||||||
 | 
					                    // TOVERIFY: Should this send an (ephemeral) email?
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            logger.debug(`removing QualifyingInvasions entry for completed invasion: ${qi.invasionId.toString()}`);
 | 
				
			||||||
 | 
					            inventory.QualifyingInvasions.splice(i, 1);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (inventory.LastInventorySync) {
 | 
					    if (inventory.LastInventorySync) {
 | 
				
			||||||
        const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
 | 
					        const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
 | 
				
			||||||
        const currentDuviriMood = Math.trunc(Date.now() / 7200000);
 | 
					        const currentDuviriMood = Math.trunc(Date.now() / 7200000);
 | 
				
			||||||
 | 
				
			|||||||
@ -558,6 +558,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    ]);
 | 
					                    ]);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                inventory.DeathSquadable = false;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "LockedWeaponGroup": {
 | 
					            case "LockedWeaponGroup": {
 | 
				
			||||||
@ -576,7 +577,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "IncHarvester": {
 | 
					            case "IncHarvester": {
 | 
				
			||||||
                inventory.Harvestable = true;
 | 
					                // Unsure what to do with this
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "CurrentLoadOutIds": {
 | 
					            case "CurrentLoadOutIds": {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,15 +6,18 @@ import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.j
 | 
				
			|||||||
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
					import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
				
			||||||
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
					import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
				
			||||||
import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
 | 
					import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
 | 
				
			||||||
 | 
					import invasionNodes from "@/static/fixed_responses/worldState/invasionNodes.json";
 | 
				
			||||||
 | 
					import invasionRewards from "@/static/fixed_responses/worldState/invasionRewards.json";
 | 
				
			||||||
import { buildConfig } from "@/src/services/buildConfigService";
 | 
					import { buildConfig } from "@/src/services/buildConfigService";
 | 
				
			||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "@/src/services/rngService";
 | 
					import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "@/src/services/rngService";
 | 
				
			||||||
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
 | 
					import { eMissionType, ExportRegions, ExportSyndicates, IMissionReward, IRegion } from "warframe-public-export-plus";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ICalendarDay,
 | 
					    ICalendarDay,
 | 
				
			||||||
    ICalendarEvent,
 | 
					    ICalendarEvent,
 | 
				
			||||||
    ICalendarSeason,
 | 
					    ICalendarSeason,
 | 
				
			||||||
 | 
					    IInvasion,
 | 
				
			||||||
    ILiteSortie,
 | 
					    ILiteSortie,
 | 
				
			||||||
    IPrimeVaultTrader,
 | 
					    IPrimeVaultTrader,
 | 
				
			||||||
    IPrimeVaultTraderOffer,
 | 
					    IPrimeVaultTraderOffer,
 | 
				
			||||||
@ -1227,6 +1230,78 @@ const getAllVarziaManifests = (): IPrimeVaultTraderOffer[] => {
 | 
				
			|||||||
    return [...dualPacks, ...singlePacks, ...items, ...bobbleHeads, ...relics];
 | 
					    return [...dualPacks, ...singlePacks, ...items, ...bobbleHeads, ...relics];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const createInvasion = (day: number, idx: number): IInvasion => {
 | 
				
			||||||
 | 
					    const id = day * 3 + idx;
 | 
				
			||||||
 | 
					    const defender = (["FC_GRINEER", "FC_CORPUS", day % 2 ? "FC_GRINEER" : "FC_CORPUS"] as const)[idx];
 | 
				
			||||||
 | 
					    const rng = new SRng(new SRng(id).randomInt(0, 1_000_000));
 | 
				
			||||||
 | 
					    const isInfestationOutbreak = rng.randomInt(0, 1) == 0;
 | 
				
			||||||
 | 
					    const attacker = isInfestationOutbreak ? "FC_INFESTATION" : defender == "FC_GRINEER" ? "FC_CORPUS" : "FC_GRINEER";
 | 
				
			||||||
 | 
					    const startMs = EPOCH + day * 86400_000;
 | 
				
			||||||
 | 
					    const oid =
 | 
				
			||||||
 | 
					        ((startMs / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
 | 
				
			||||||
 | 
					        "fd148cb8" +
 | 
				
			||||||
 | 
					        (idx & 0xffffffff).toString(16).padStart(8, "0");
 | 
				
			||||||
 | 
					    const node = sequentiallyUniqueRandomElement(invasionNodes[defender], id, 5, 690175)!; // Can't repeat the other 2 on this day nor the last 3
 | 
				
			||||||
 | 
					    const progress = (Date.now() - startMs) / 86400_000;
 | 
				
			||||||
 | 
					    const countMultiplier = isInfestationOutbreak || rng.randomInt(0, 1) ? -1 : 1; // if defender is winning, count is negative
 | 
				
			||||||
 | 
					    const fiftyPercent = rng.randomInt(1000, 29000); // introduce some 'yitter' for the percentages
 | 
				
			||||||
 | 
					    const rewardFloat = rng.randomFloat();
 | 
				
			||||||
 | 
					    const rewardTier = rewardFloat < 0.201 ? "RARE" : rewardFloat < 0.7788 ? "COMMON" : "UNCOMMON";
 | 
				
			||||||
 | 
					    const attackerReward: IMissionReward = {};
 | 
				
			||||||
 | 
					    const defenderReward: IMissionReward = {};
 | 
				
			||||||
 | 
					    if (isInfestationOutbreak) {
 | 
				
			||||||
 | 
					        defenderReward.countedItems = [
 | 
				
			||||||
 | 
					            rng.randomElement(invasionRewards[rng.randomInt(0, 1) ? "FC_INFESTATION" : defender][rewardTier])!
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        attackerReward.countedItems = [rng.randomElement(invasionRewards[attacker][rewardTier])!];
 | 
				
			||||||
 | 
					        defenderReward.countedItems = [rng.randomElement(invasionRewards[defender][rewardTier])!];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        _id: { $oid: oid },
 | 
				
			||||||
 | 
					        Faction: attacker,
 | 
				
			||||||
 | 
					        DefenderFaction: defender,
 | 
				
			||||||
 | 
					        Node: node,
 | 
				
			||||||
 | 
					        Count: Math.round(
 | 
				
			||||||
 | 
					            (progress < 0.5 ? progress * 2 * fiftyPercent : fiftyPercent + (30_000 - fiftyPercent) * (progress - 0.5)) *
 | 
				
			||||||
 | 
					                countMultiplier
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        Goal: 30000, // Value seems to range from 30000 to 98000 in intervals of 1000. Higher values are increasingly rare. I don't think this is relevant for the frontend besides dividing count by it.
 | 
				
			||||||
 | 
					        LocTag: isInfestationOutbreak
 | 
				
			||||||
 | 
					            ? ExportRegions[node].missionIndex == 0
 | 
				
			||||||
 | 
					                ? "/Lotus/Language/Menu/InfestedInvasionBoss"
 | 
				
			||||||
 | 
					                : "/Lotus/Language/Menu/InfestedInvasionGeneric"
 | 
				
			||||||
 | 
					            : attacker == "FC_CORPUS"
 | 
				
			||||||
 | 
					              ? "/Lotus/Language/Menu/CorpusInvasionGeneric"
 | 
				
			||||||
 | 
					              : "/Lotus/Language/Menu/GrineerInvasionGeneric",
 | 
				
			||||||
 | 
					        Completed: startMs + 86400_000 < Date.now(), // Sorta unfaithful. Invasions on live are (at least in part) in fluenced by people completing them. And otherwise also probably not hardcoded to last 24 hours.
 | 
				
			||||||
 | 
					        ChainID: { $oid: oid },
 | 
				
			||||||
 | 
					        AttackerReward: attackerReward,
 | 
				
			||||||
 | 
					        AttackerMissionInfo: {
 | 
				
			||||||
 | 
					            seed: rng.randomInt(0, 1_000_000),
 | 
				
			||||||
 | 
					            faction: defender
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        DefenderReward: defenderReward,
 | 
				
			||||||
 | 
					        DefenderMissionInfo: {
 | 
				
			||||||
 | 
					            seed: rng.randomInt(0, 1_000_000),
 | 
				
			||||||
 | 
					            faction: attacker
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Activation: {
 | 
				
			||||||
 | 
					            $date: {
 | 
				
			||||||
 | 
					                $numberLong: startMs.toString()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getInvasionByOid = (oid: string): IInvasion | undefined => {
 | 
				
			||||||
 | 
					    const arr = oid.split("fd148cb8");
 | 
				
			||||||
 | 
					    if (arr.length == 2 && arr[0].length == 8 && arr[1].length == 8) {
 | 
				
			||||||
 | 
					        return createInvasion(idToDay(oid), parseInt(arr[1], 16));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return undefined;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
					export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			||||||
    const constraints: ITimeConstraint[] = [];
 | 
					    const constraints: ITimeConstraint[] = [];
 | 
				
			||||||
    if (config.worldState?.eidolonOverride) {
 | 
					    if (config.worldState?.eidolonOverride) {
 | 
				
			||||||
@ -1275,6 +1350,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        LiteSorties: [],
 | 
					        LiteSorties: [],
 | 
				
			||||||
        ActiveMissions: [],
 | 
					        ActiveMissions: [],
 | 
				
			||||||
        GlobalUpgrades: [],
 | 
					        GlobalUpgrades: [],
 | 
				
			||||||
 | 
					        Invasions: [],
 | 
				
			||||||
        VoidTraders: [],
 | 
					        VoidTraders: [],
 | 
				
			||||||
        PrimeVaultTraders: [],
 | 
					        PrimeVaultTraders: [],
 | 
				
			||||||
        VoidStorms: [],
 | 
					        VoidStorms: [],
 | 
				
			||||||
@ -1477,6 +1553,20 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Rough outline of dynamic invasions.
 | 
				
			||||||
 | 
					    // TODO: Invasions chains, e.g. an infestation mission would soon lead to other nodes on that planet also having an infestation invasion.
 | 
				
			||||||
 | 
					    // TODO: Grineer/Corpus to fund their death stars with each invasion win.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day, 0));
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day, 1));
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day, 2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Completed invasions stay for up to 24 hours as the winner 'occupies' that node
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day - 1, 0));
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day - 1, 1));
 | 
				
			||||||
 | 
					        worldState.Invasions.push(createInvasion(day - 1, 2));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Baro
 | 
					    // Baro
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
 | 
					        const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ export interface IWorldState {
 | 
				
			|||||||
    SyndicateMissions: ISyndicateMissionInfo[];
 | 
					    SyndicateMissions: ISyndicateMissionInfo[];
 | 
				
			||||||
    ActiveMissions: IFissure[];
 | 
					    ActiveMissions: IFissure[];
 | 
				
			||||||
    GlobalUpgrades: IGlobalUpgrade[];
 | 
					    GlobalUpgrades: IGlobalUpgrade[];
 | 
				
			||||||
 | 
					    Invasions: IInvasion[];
 | 
				
			||||||
    NodeOverrides: INodeOverride[];
 | 
					    NodeOverrides: INodeOverride[];
 | 
				
			||||||
    VoidTraders: IVoidTrader[];
 | 
					    VoidTraders: IVoidTrader[];
 | 
				
			||||||
    PrimeVaultTraders: IPrimeVaultTrader[];
 | 
					    PrimeVaultTraders: IPrimeVaultTrader[];
 | 
				
			||||||
@ -82,6 +83,28 @@ export interface IGlobalUpgrade {
 | 
				
			|||||||
    LocalizeDescTag: string;
 | 
					    LocalizeDescTag: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IInvasion {
 | 
				
			||||||
 | 
					    _id: IOid;
 | 
				
			||||||
 | 
					    Faction: string;
 | 
				
			||||||
 | 
					    DefenderFaction: string;
 | 
				
			||||||
 | 
					    Node: string;
 | 
				
			||||||
 | 
					    Count: number;
 | 
				
			||||||
 | 
					    Goal: number;
 | 
				
			||||||
 | 
					    LocTag: string;
 | 
				
			||||||
 | 
					    Completed: boolean;
 | 
				
			||||||
 | 
					    ChainID: IOid;
 | 
				
			||||||
 | 
					    AttackerReward: IMissionReward;
 | 
				
			||||||
 | 
					    AttackerMissionInfo: IInvasionMissionInfo;
 | 
				
			||||||
 | 
					    DefenderReward: IMissionReward;
 | 
				
			||||||
 | 
					    DefenderMissionInfo: IInvasionMissionInfo;
 | 
				
			||||||
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IInvasionMissionInfo {
 | 
				
			||||||
 | 
					    seed: number;
 | 
				
			||||||
 | 
					    faction: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IFissure {
 | 
					export interface IFissure {
 | 
				
			||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    Region: number;
 | 
					    Region: number;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										114
									
								
								static/fixed_responses/worldState/invasionNodes.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								static/fixed_responses/worldState/invasionNodes.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "FC_CORPUS": [
 | 
				
			||||||
 | 
					    "SettlementNode1",
 | 
				
			||||||
 | 
					    "SettlementNode2",
 | 
				
			||||||
 | 
					    "SettlementNode3",
 | 
				
			||||||
 | 
					    "SettlementNode11",
 | 
				
			||||||
 | 
					    "SettlementNode12",
 | 
				
			||||||
 | 
					    "SettlementNode14",
 | 
				
			||||||
 | 
					    "SettlementNode15",
 | 
				
			||||||
 | 
					    "SettlementNode20",
 | 
				
			||||||
 | 
					    "SolNode1",
 | 
				
			||||||
 | 
					    "SolNode2",
 | 
				
			||||||
 | 
					    "SolNode4",
 | 
				
			||||||
 | 
					    "SolNode6",
 | 
				
			||||||
 | 
					    "SolNode10",
 | 
				
			||||||
 | 
					    "SolNode17",
 | 
				
			||||||
 | 
					    "SolNode21",
 | 
				
			||||||
 | 
					    "SolNode22",
 | 
				
			||||||
 | 
					    "SolNode23",
 | 
				
			||||||
 | 
					    "SolNode25",
 | 
				
			||||||
 | 
					    "SolNode38",
 | 
				
			||||||
 | 
					    "SolNode43",
 | 
				
			||||||
 | 
					    "SolNode48",
 | 
				
			||||||
 | 
					    "SolNode49",
 | 
				
			||||||
 | 
					    "SolNode51",
 | 
				
			||||||
 | 
					    "SolNode53",
 | 
				
			||||||
 | 
					    "SolNode56",
 | 
				
			||||||
 | 
					    "SolNode57",
 | 
				
			||||||
 | 
					    "SolNode61",
 | 
				
			||||||
 | 
					    "SolNode62",
 | 
				
			||||||
 | 
					    "SolNode65",
 | 
				
			||||||
 | 
					    "SolNode66",
 | 
				
			||||||
 | 
					    "SolNode72",
 | 
				
			||||||
 | 
					    "SolNode73",
 | 
				
			||||||
 | 
					    "SolNode74",
 | 
				
			||||||
 | 
					    "SolNode76",
 | 
				
			||||||
 | 
					    "SolNode78",
 | 
				
			||||||
 | 
					    "SolNode81",
 | 
				
			||||||
 | 
					    "SolNode84",
 | 
				
			||||||
 | 
					    "SolNode88",
 | 
				
			||||||
 | 
					    "SolNode97",
 | 
				
			||||||
 | 
					    "SolNode100",
 | 
				
			||||||
 | 
					    "SolNode101",
 | 
				
			||||||
 | 
					    "SolNode102",
 | 
				
			||||||
 | 
					    "SolNode104",
 | 
				
			||||||
 | 
					    "SolNode107",
 | 
				
			||||||
 | 
					    "SolNode109",
 | 
				
			||||||
 | 
					    "SolNode118",
 | 
				
			||||||
 | 
					    "SolNode121",
 | 
				
			||||||
 | 
					    "SolNode123",
 | 
				
			||||||
 | 
					    "SolNode125",
 | 
				
			||||||
 | 
					    "SolNode126",
 | 
				
			||||||
 | 
					    "SolNode127",
 | 
				
			||||||
 | 
					    "SolNode128",
 | 
				
			||||||
 | 
					    "SolNode203",
 | 
				
			||||||
 | 
					    "SolNode205",
 | 
				
			||||||
 | 
					    "SolNode209",
 | 
				
			||||||
 | 
					    "SolNode210",
 | 
				
			||||||
 | 
					    "SolNode211",
 | 
				
			||||||
 | 
					    "SolNode212",
 | 
				
			||||||
 | 
					    "SolNode214",
 | 
				
			||||||
 | 
					    "SolNode216",
 | 
				
			||||||
 | 
					    "SolNode217",
 | 
				
			||||||
 | 
					    "SolNode220"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "FC_GRINEER": [
 | 
				
			||||||
 | 
					    "SolNode11",
 | 
				
			||||||
 | 
					    "SolNode16",
 | 
				
			||||||
 | 
					    "SolNode18",
 | 
				
			||||||
 | 
					    "SolNode19",
 | 
				
			||||||
 | 
					    "SolNode20",
 | 
				
			||||||
 | 
					    "SolNode30",
 | 
				
			||||||
 | 
					    "SolNode31",
 | 
				
			||||||
 | 
					    "SolNode32",
 | 
				
			||||||
 | 
					    "SolNode36",
 | 
				
			||||||
 | 
					    "SolNode41",
 | 
				
			||||||
 | 
					    "SolNode42",
 | 
				
			||||||
 | 
					    "SolNode45",
 | 
				
			||||||
 | 
					    "SolNode46",
 | 
				
			||||||
 | 
					    "SolNode50",
 | 
				
			||||||
 | 
					    "SolNode58",
 | 
				
			||||||
 | 
					    "SolNode67",
 | 
				
			||||||
 | 
					    "SolNode68",
 | 
				
			||||||
 | 
					    "SolNode70",
 | 
				
			||||||
 | 
					    "SolNode82",
 | 
				
			||||||
 | 
					    "SolNode93",
 | 
				
			||||||
 | 
					    "SolNode96",
 | 
				
			||||||
 | 
					    "SolNode99",
 | 
				
			||||||
 | 
					    "SolNode106",
 | 
				
			||||||
 | 
					    "SolNode113",
 | 
				
			||||||
 | 
					    "SolNode131",
 | 
				
			||||||
 | 
					    "SolNode132",
 | 
				
			||||||
 | 
					    "SolNode135",
 | 
				
			||||||
 | 
					    "SolNode137",
 | 
				
			||||||
 | 
					    "SolNode138",
 | 
				
			||||||
 | 
					    "SolNode139",
 | 
				
			||||||
 | 
					    "SolNode140",
 | 
				
			||||||
 | 
					    "SolNode141",
 | 
				
			||||||
 | 
					    "SolNode144",
 | 
				
			||||||
 | 
					    "SolNode146",
 | 
				
			||||||
 | 
					    "SolNode147",
 | 
				
			||||||
 | 
					    "SolNode149",
 | 
				
			||||||
 | 
					    "SolNode177",
 | 
				
			||||||
 | 
					    "SolNode181",
 | 
				
			||||||
 | 
					    "SolNode184",
 | 
				
			||||||
 | 
					    "SolNode185",
 | 
				
			||||||
 | 
					    "SolNode187",
 | 
				
			||||||
 | 
					    "SolNode188",
 | 
				
			||||||
 | 
					    "SolNode189",
 | 
				
			||||||
 | 
					    "SolNode191",
 | 
				
			||||||
 | 
					    "SolNode195",
 | 
				
			||||||
 | 
					    "SolNode196"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										190
									
								
								static/fixed_responses/worldState/invasionRewards.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								static/fixed_responses/worldState/invasionRewards.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "FC_GRINEER": {
 | 
				
			||||||
 | 
					    "COMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Items/Research/ChemComponent",
 | 
				
			||||||
 | 
					        "ItemCount": 3
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "UNCOMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/KarakWraithBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithStock",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/StrunWraithBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithStock",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/LatronWraithBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithStock",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/TwinVipersWraithBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithLink",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/GrineerCombatKnifeSortieBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHilt",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeBlade",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHeatsink",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "RARE": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "FC_CORPUS": {
 | 
				
			||||||
 | 
					    "COMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
 | 
				
			||||||
 | 
					        "ItemCount": 3
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "UNCOMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/DeraVandalBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalStock",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/SnipetronVandalBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalStock",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalReceiver",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalBarrel",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "RARE": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "FC_INFESTATION": {
 | 
				
			||||||
 | 
					    "COMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Items/Research/BioComponent",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "UNCOMMON": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Items/Research/BioComponent",
 | 
				
			||||||
 | 
					        "ItemCount": 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "RARE": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ItemType": "/Lotus/Types/Items/MiscItems/InfestedAladCoordinate",
 | 
				
			||||||
 | 
					        "ItemCount": 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -117,46 +117,6 @@
 | 
				
			|||||||
      ]
 | 
					      ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "Invasions": [
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "_id": {
 | 
					 | 
				
			||||||
        "$oid": "67c8ec8b3d0d86b236c1c18f"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Faction": "FC_INFESTATION",
 | 
					 | 
				
			||||||
      "DefenderFaction": "FC_CORPUS",
 | 
					 | 
				
			||||||
      "Node": "SolNode53",
 | 
					 | 
				
			||||||
      "Count": -28558,
 | 
					 | 
				
			||||||
      "Goal": 30000,
 | 
					 | 
				
			||||||
      "LocTag": "/Lotus/Language/Menu/InfestedInvasionBoss",
 | 
					 | 
				
			||||||
      "Completed": false,
 | 
					 | 
				
			||||||
      "ChainID": {
 | 
					 | 
				
			||||||
        "$oid": "67c8b6a2bde0dfd0f7c1c18d"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "AttackerReward": [],
 | 
					 | 
				
			||||||
      "AttackerMissionInfo": {
 | 
					 | 
				
			||||||
        "seed": 488863,
 | 
					 | 
				
			||||||
        "faction": "FC_CORPUS"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "DefenderReward": {
 | 
					 | 
				
			||||||
        "countedItems": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
 | 
					 | 
				
			||||||
            "ItemCount": 3
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "DefenderMissionInfo": {
 | 
					 | 
				
			||||||
        "seed": 127653,
 | 
					 | 
				
			||||||
        "faction": "FC_INFESTATION",
 | 
					 | 
				
			||||||
        "missionReward": []
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Activation": {
 | 
					 | 
				
			||||||
        "$date": {
 | 
					 | 
				
			||||||
          "$numberLong": "1741221003031"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  "SyndicateMissions": [
 | 
					  "SyndicateMissions": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
 | 
					      "_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user