forked from OpenWF/SpaceNinjaServer
feat: initial invasions (#2458)
A rough generation of 3 invasions that change at daily reset, so missing the planet-based invasion 'chains'. Battle pay is fully working tho, just a few points of uncertainty there due to missing research and logs. Death marks are also roughly working. Re #1097 Reviewed-on: OpenWF/SpaceNinjaServer#2458 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
parent
dc8f32d4d8
commit
7eb95c995c
@ -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