返回提交历史
Modified
src/controllers/api/inventoryController.ts
+68
-2
Modified
src/services/missionInventoryUpdateService.ts
+2
-1
Modified
src/services/worldStateService.ts
+91
-1
Modified
src/types/worldStateTypes.ts
+23
-0
Added
static/fixed_responses/worldState/invasionNodes.json
+114
-0
Added
static/fixed_responses/worldState/invasionRewards.json
+190
-0
Modified
static/fixed_responses/worldState/worldState.json
+0
-40
XFEstudio/XFESpaceNinjaServer
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: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2458 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
7eb95c99
代码差异
7 个文件
+488
-44
@@ -6,10 +6,18 @@ import allDialogue from "@/static/fixed_responses/allDialogue.json";
6
6
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
7
7
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
8
8
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
9
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
9
import {
10
eFaction,
11
ExportCustoms,
12
ExportFlavour,
13
ExportResources,
14
ExportVirtuals,
15
ICountedItem
16
} from "warframe-public-export-plus";
10
17
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
11
18
import {
12
19
addEmailItem,
20
addItem,
13
21
addMiscItems,
14
22
allDailyAffiliationKeys,
15
23
checkCalendarAutoAdvance,
@@ -30,7 +38,8 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
30
38
import { DailyDeal } from "@/src/models/worldStateModel";
31
39
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
32
40
import { generateRewardSeed } from "@/src/services/rngService";
33
import { getWorldState } from "@/src/services/worldStateService";
41
import { getInvasionByOid, getWorldState } from "@/src/services/worldStateService";
42
import { createMessage } from "@/src/services/inboxService";
34
43
35
44
export const inventoryController: RequestHandler = async (request, response) => {
36
45
const account = await getAccountForRequest(request);
@@ -186,6 +195,63 @@ export const inventoryController: RequestHandler = async (request, response) =>
186
195
//await inventory.save();
187
196
}
188
197
198
for (let i = 0; i != inventory.QualifyingInvasions.length; ) {
199
const qi = inventory.QualifyingInvasions[i];
200
const invasion = getInvasionByOid(qi.invasionId.toString());
201
if (!invasion) {
202
logger.debug(`removing QualifyingInvasions entry for unknown invasion: ${qi.invasionId.toString()}`);
203
inventory.QualifyingInvasions.splice(i, 1);
204
continue;
205
}
206
if (invasion.Completed) {
207
let factionSidedWith: string | undefined;
208
let battlePay: ICountedItem[] | undefined;
209
if (qi.AttackerScore >= 3) {
210
factionSidedWith = invasion.Faction;
211
battlePay = invasion.AttackerReward.countedItems;
212
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
213
} else if (qi.DefenderScore >= 3) {
214
factionSidedWith = invasion.DefenderFaction;
215
battlePay = invasion.DefenderReward.countedItems;
216
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
217
}
218
if (factionSidedWith) {
219
if (battlePay) {
220
// Decoupling rewards from the inbox message because it may delete itself without being read
221
for (const item of battlePay) {
222
await addItem(inventory, item.ItemType, item.ItemCount);
223
}
224
await createMessage(account._id, [
225
{
226
sndr: eFaction.find(x => x.tag == factionSidedWith)?.name ?? factionSidedWith, // TOVERIFY
227
msg: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageBody`,
228
sub: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageSubject`,
229
countedAtt: battlePay,
230
attVisualOnly: true,
231
icon:
232
factionSidedWith == "FC_GRINEER"
233
? "/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
234
: "/Lotus/Interface/Icons/Npcs/CrewmanNormal.png", // My best source for this is https://www.youtube.com/watch?v=rxrCCFm73XE around 1:37
235
// TOVERIFY: highPriority?
236
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.
237
}
238
]);
239
}
240
if (invasion.Faction != "FC_INFESTATION") {
241
// Sided with grineer -> opposed corpus -> send zanuka (harvester)
242
// Sided with corpus -> opposed grineer -> send g3 (death squad)
243
inventory[factionSidedWith != "FC_GRINEER" ? "DeathSquadable" : "Harvestable"] = true;
244
// TOVERIFY: Should this happen earlier?
245
// TOVERIFY: Should this send an (ephemeral) email?
246
}
247
}
248
logger.debug(`removing QualifyingInvasions entry for completed invasion: ${qi.invasionId.toString()}`);
249
inventory.QualifyingInvasions.splice(i, 1);
250
continue;
251
}
252
++i;
253
}
254
189
255
if (inventory.LastInventorySync) {
190
256
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
191
257
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
@@ -558,6 +558,7 @@ export const addMissionInventoryUpdates = async (
558
558
}
559
559
]);
560
560
}
561
inventory.DeathSquadable = false;
561
562
break;
562
563
}
563
564
case "LockedWeaponGroup": {
@@ -576,7 +577,7 @@ export const addMissionInventoryUpdates = async (
576
577
break;
577
578
}
578
579
case "IncHarvester": {
579
inventory.Harvestable = true;
580
// Unsure what to do with this
580
581
break;
581
582
}
582
583
case "CurrentLoadOutIds": {
@@ -6,15 +6,18 @@ import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.j
6
6
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
7
7
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
8
8
import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
9
import invasionNodes from "@/static/fixed_responses/worldState/invasionNodes.json";
10
import invasionRewards from "@/static/fixed_responses/worldState/invasionRewards.json";
9
11
import { buildConfig } from "@/src/services/buildConfigService";
10
12
import { unixTimesInMs } from "@/src/constants/timeConstants";
11
13
import { config } from "@/src/services/configService";
12
14
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "@/src/services/rngService";
13
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
15
import { eMissionType, ExportRegions, ExportSyndicates, IMissionReward, IRegion } from "warframe-public-export-plus";
14
16
import {
15
17
ICalendarDay,
16
18
ICalendarEvent,
17
19
ICalendarSeason,
20
IInvasion,
18
21
ILiteSortie,
19
22
IPrimeVaultTrader,
20
23
IPrimeVaultTraderOffer,
@@ -1227,6 +1230,78 @@ const getAllVarziaManifests = (): IPrimeVaultTraderOffer[] => {
1227
1230
return [...dualPacks, ...singlePacks, ...items, ...bobbleHeads, ...relics];
1228
1231
};
1229
1232
1233
const createInvasion = (day: number, idx: number): IInvasion => {
1234
const id = day * 3 + idx;
1235
const defender = (["FC_GRINEER", "FC_CORPUS", day % 2 ? "FC_GRINEER" : "FC_CORPUS"] as const)[idx];
1236
const rng = new SRng(new SRng(id).randomInt(0, 1_000_000));
1237
const isInfestationOutbreak = rng.randomInt(0, 1) == 0;
1238
const attacker = isInfestationOutbreak ? "FC_INFESTATION" : defender == "FC_GRINEER" ? "FC_CORPUS" : "FC_GRINEER";
1239
const startMs = EPOCH + day * 86400_000;
1240
const oid =
1241
((startMs / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
1242
"fd148cb8" +
1243
(idx & 0xffffffff).toString(16).padStart(8, "0");
1244
const node = sequentiallyUniqueRandomElement(invasionNodes[defender], id, 5, 690175)!; // Can't repeat the other 2 on this day nor the last 3
1245
const progress = (Date.now() - startMs) / 86400_000;
1246
const countMultiplier = isInfestationOutbreak || rng.randomInt(0, 1) ? -1 : 1; // if defender is winning, count is negative
1247
const fiftyPercent = rng.randomInt(1000, 29000); // introduce some 'yitter' for the percentages
1248
const rewardFloat = rng.randomFloat();
1249
const rewardTier = rewardFloat < 0.201 ? "RARE" : rewardFloat < 0.7788 ? "COMMON" : "UNCOMMON";
1250
const attackerReward: IMissionReward = {};
1251
const defenderReward: IMissionReward = {};
1252
if (isInfestationOutbreak) {
1253
defenderReward.countedItems = [
1254
rng.randomElement(invasionRewards[rng.randomInt(0, 1) ? "FC_INFESTATION" : defender][rewardTier])!
1255
];
1256
} else {
1257
attackerReward.countedItems = [rng.randomElement(invasionRewards[attacker][rewardTier])!];
1258
defenderReward.countedItems = [rng.randomElement(invasionRewards[defender][rewardTier])!];
1259
}
1260
return {
1261
_id: { $oid: oid },
1262
Faction: attacker,
1263
DefenderFaction: defender,
1264
Node: node,
1265
Count: Math.round(
1266
(progress < 0.5 ? progress * 2 * fiftyPercent : fiftyPercent + (30_000 - fiftyPercent) * (progress - 0.5)) *
1267
countMultiplier
1268
),
1269
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.
1270
LocTag: isInfestationOutbreak
1271
? ExportRegions[node].missionIndex == 0
1272
? "/Lotus/Language/Menu/InfestedInvasionBoss"
1273
: "/Lotus/Language/Menu/InfestedInvasionGeneric"
1274
: attacker == "FC_CORPUS"
1275
? "/Lotus/Language/Menu/CorpusInvasionGeneric"
1276
: "/Lotus/Language/Menu/GrineerInvasionGeneric",
1277
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.
1278
ChainID: { $oid: oid },
1279
AttackerReward: attackerReward,
1280
AttackerMissionInfo: {
1281
seed: rng.randomInt(0, 1_000_000),
1282
faction: defender
1283
},
1284
DefenderReward: defenderReward,
1285
DefenderMissionInfo: {
1286
seed: rng.randomInt(0, 1_000_000),
1287
faction: attacker
1288
},
1289
Activation: {
1290
$date: {
1291
$numberLong: startMs.toString()
1292
}
1293
}
1294
};
1295
};
1296
1297
export const getInvasionByOid = (oid: string): IInvasion | undefined => {
1298
const arr = oid.split("fd148cb8");
1299
if (arr.length == 2 && arr[0].length == 8 && arr[1].length == 8) {
1300
return createInvasion(idToDay(oid), parseInt(arr[1], 16));
1301
}
1302
return undefined;
1303
};
1304
1230
1305
export const getWorldState = (buildLabel?: string): IWorldState => {
1231
1306
const constraints: ITimeConstraint[] = [];
1232
1307
if (config.worldState?.eidolonOverride) {
@@ -1275,6 +1350,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
1275
1350
LiteSorties: [],
1276
1351
ActiveMissions: [],
1277
1352
GlobalUpgrades: [],
1353
Invasions: [],
1278
1354
VoidTraders: [],
1279
1355
PrimeVaultTraders: [],
1280
1356
VoidStorms: [],
@@ -1477,6 +1553,20 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
1477
1553
});
1478
1554
}
1479
1555
1556
// Rough outline of dynamic invasions.
1557
// TODO: Invasions chains, e.g. an infestation mission would soon lead to other nodes on that planet also having an infestation invasion.
1558
// TODO: Grineer/Corpus to fund their death stars with each invasion win.
1559
{
1560
worldState.Invasions.push(createInvasion(day, 0));
1561
worldState.Invasions.push(createInvasion(day, 1));
1562
worldState.Invasions.push(createInvasion(day, 2));
1563
1564
// Completed invasions stay for up to 24 hours as the winner 'occupies' that node
1565
worldState.Invasions.push(createInvasion(day - 1, 0));
1566
worldState.Invasions.push(createInvasion(day - 1, 1));
1567
worldState.Invasions.push(createInvasion(day - 1, 2));
1568
}
1569
1480
1570
// Baro
1481
1571
{
1482
1572
const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
@@ -12,6 +12,7 @@ export interface IWorldState {
12
12
SyndicateMissions: ISyndicateMissionInfo[];
13
13
ActiveMissions: IFissure[];
14
14
GlobalUpgrades: IGlobalUpgrade[];
15
Invasions: IInvasion[];
15
16
NodeOverrides: INodeOverride[];
16
17
VoidTraders: IVoidTrader[];
17
18
PrimeVaultTraders: IPrimeVaultTrader[];
@@ -82,6 +83,28 @@ export interface IGlobalUpgrade {
82
83
LocalizeDescTag: string;
83
84
}
84
85
86
export interface IInvasion {
87
_id: IOid;
88
Faction: string;
89
DefenderFaction: string;
90
Node: string;
91
Count: number;
92
Goal: number;
93
LocTag: string;
94
Completed: boolean;
95
ChainID: IOid;
96
AttackerReward: IMissionReward;
97
AttackerMissionInfo: IInvasionMissionInfo;
98
DefenderReward: IMissionReward;
99
DefenderMissionInfo: IInvasionMissionInfo;
100
Activation: IMongoDate;
101
}
102
103
export interface IInvasionMissionInfo {
104
seed: number;
105
faction: string;
106
}
107
85
108
export interface IFissure {
86
109
_id: IOid;
87
110
Region: number;
@@ -0,0 +1,114 @@
1
{
2
"FC_CORPUS": [
3
"SettlementNode1",
4
"SettlementNode2",
5
"SettlementNode3",
6
"SettlementNode11",
7
"SettlementNode12",
8
"SettlementNode14",
9
"SettlementNode15",
10
"SettlementNode20",
11
"SolNode1",
12
"SolNode2",
13
"SolNode4",
14
"SolNode6",
15
"SolNode10",
16
"SolNode17",
17
"SolNode21",
18
"SolNode22",
19
"SolNode23",
20
"SolNode25",
21
"SolNode38",
22
"SolNode43",
23
"SolNode48",
24
"SolNode49",
25
"SolNode51",
26
"SolNode53",
27
"SolNode56",
28
"SolNode57",
29
"SolNode61",
30
"SolNode62",
31
"SolNode65",
32
"SolNode66",
33
"SolNode72",
34
"SolNode73",
35
"SolNode74",
36
"SolNode76",
37
"SolNode78",
38
"SolNode81",
39
"SolNode84",
40
"SolNode88",
41
"SolNode97",
42
"SolNode100",
43
"SolNode101",
44
"SolNode102",
45
"SolNode104",
46
"SolNode107",
47
"SolNode109",
48
"SolNode118",
49
"SolNode121",
50
"SolNode123",
51
"SolNode125",
52
"SolNode126",
53
"SolNode127",
54
"SolNode128",
55
"SolNode203",
56
"SolNode205",
57
"SolNode209",
58
"SolNode210",
59
"SolNode211",
60
"SolNode212",
61
"SolNode214",
62
"SolNode216",
63
"SolNode217",
64
"SolNode220"
65
],
66
"FC_GRINEER": [
67
"SolNode11",
68
"SolNode16",
69
"SolNode18",
70
"SolNode19",
71
"SolNode20",
72
"SolNode30",
73
"SolNode31",
74
"SolNode32",
75
"SolNode36",
76
"SolNode41",
77
"SolNode42",
78
"SolNode45",
79
"SolNode46",
80
"SolNode50",
81
"SolNode58",
82
"SolNode67",
83
"SolNode68",
84
"SolNode70",
85
"SolNode82",
86
"SolNode93",
87
"SolNode96",
88
"SolNode99",
89
"SolNode106",
90
"SolNode113",
91
"SolNode131",
92
"SolNode132",
93
"SolNode135",
94
"SolNode137",
95
"SolNode138",
96
"SolNode139",
97
"SolNode140",
98
"SolNode141",
99
"SolNode144",
100
"SolNode146",
101
"SolNode147",
102
"SolNode149",
103
"SolNode177",
104
"SolNode181",
105
"SolNode184",
106
"SolNode185",
107
"SolNode187",
108
"SolNode188",
109
"SolNode189",
110
"SolNode191",
111
"SolNode195",
112
"SolNode196"
113
]
114
}
@@ -0,0 +1,190 @@
1
{
2
"FC_GRINEER": {
3
"COMMON": [
4
{
5
"ItemType": "/Lotus/Types/Items/Research/ChemComponent",
6
"ItemCount": 3
7
}
8
],
9
"UNCOMMON": [
10
{
11
"ItemType": "/Lotus/Types/Recipes/Weapons/KarakWraithBlueprint",
12
"ItemCount": 1
13
},
14
{
15
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithBarrel",
16
"ItemCount": 1
17
},
18
{
19
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithReceiver",
20
"ItemCount": 1
21
},
22
{
23
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithStock",
24
"ItemCount": 1
25
},
26
{
27
"ItemType": "/Lotus/Types/Recipes/Weapons/StrunWraithBlueprint",
28
"ItemCount": 1
29
},
30
{
31
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithBarrel",
32
"ItemCount": 1
33
},
34
{
35
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithReceiver",
36
"ItemCount": 1
37
},
38
{
39
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithStock",
40
"ItemCount": 1
41
},
42
{
43
"ItemType": "/Lotus/Types/Recipes/Weapons/LatronWraithBlueprint",
44
"ItemCount": 1
45
},
46
{
47
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithBarrel",
48
"ItemCount": 1
49
},
50
{
51
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithReceiver",
52
"ItemCount": 1
53
},
54
{
55
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithStock",
56
"ItemCount": 1
57
},
58
{
59
"ItemType": "/Lotus/Types/Recipes/Weapons/TwinVipersWraithBlueprint",
60
"ItemCount": 1
61
},
62
{
63
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithBarrel",
64
"ItemCount": 1
65
},
66
{
67
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithLink",
68
"ItemCount": 1
69
},
70
{
71
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithReceiver",
72
"ItemCount": 1
73
},
74
{
75
"ItemType": "/Lotus/Types/Recipes/Weapons/GrineerCombatKnifeSortieBlueprint",
76
"ItemCount": 1
77
},
78
{
79
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHilt",
80
"ItemCount": 1
81
},
82
{
83
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeBlade",
84
"ItemCount": 1
85
},
86
{
87
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHeatsink",
88
"ItemCount": 1
89
}
90
],
91
"RARE": [
92
{
93
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
94
"ItemCount": 1
95
},
96
{
97
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
98
"ItemCount": 1
99
},
100
{
101
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
102
"ItemCount": 1
103
},
104
{
105
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
106
"ItemCount": 1
107
}
108
]
109
},
110
"FC_CORPUS": {
111
"COMMON": [
112
{
113
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
114
"ItemCount": 3
115
}
116
],
117
"UNCOMMON": [
118
{
119
"ItemType": "/Lotus/Types/Recipes/Weapons/DeraVandalBlueprint",
120
"ItemCount": 1
121
},
122
{
123
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalBarrel",
124
"ItemCount": 1
125
},
126
{
127
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalReceiver",
128
"ItemCount": 1
129
},
130
{
131
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalStock",
132
"ItemCount": 1
133
},
134
{
135
"ItemType": "/Lotus/Types/Recipes/Weapons/SnipetronVandalBlueprint",
136
"ItemCount": 1
137
},
138
{
139
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalStock",
140
"ItemCount": 1
141
},
142
{
143
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalReceiver",
144
"ItemCount": 1
145
},
146
{
147
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalBarrel",
148
"ItemCount": 1
149
}
150
],
151
"RARE": [
152
{
153
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
154
"ItemCount": 1
155
},
156
{
157
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
158
"ItemCount": 1
159
},
160
{
161
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
162
"ItemCount": 1
163
},
164
{
165
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
166
"ItemCount": 1
167
}
168
]
169
},
170
"FC_INFESTATION": {
171
"COMMON": [
172
{
173
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
174
"ItemCount": 1
175
}
176
],
177
"UNCOMMON": [
178
{
179
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
180
"ItemCount": 2
181
}
182
],
183
"RARE": [
184
{
185
"ItemType": "/Lotus/Types/Items/MiscItems/InfestedAladCoordinate",
186
"ItemCount": 1
187
}
188
]
189
}
190
}
@@ -117,46 +117,6 @@
117
117
]
118
118
}
119
119
},
120
"Invasions": [
121
{
122
"_id": {
123
"$oid": "67c8ec8b3d0d86b236c1c18f"
124
},
125
"Faction": "FC_INFESTATION",
126
"DefenderFaction": "FC_CORPUS",
127
"Node": "SolNode53",
128
"Count": -28558,
129
"Goal": 30000,
130
"LocTag": "/Lotus/Language/Menu/InfestedInvasionBoss",
131
"Completed": false,
132
"ChainID": {
133
"$oid": "67c8b6a2bde0dfd0f7c1c18d"
134
},
135
"AttackerReward": [],
136
"AttackerMissionInfo": {
137
"seed": 488863,
138
"faction": "FC_CORPUS"
139
},
140
"DefenderReward": {
141
"countedItems": [
142
{
143
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
144
"ItemCount": 3
145
}
146
]
147
},
148
"DefenderMissionInfo": {
149
"seed": 127653,
150
"faction": "FC_INFESTATION",
151
"missionReward": []
152
},
153
"Activation": {
154
"$date": {
155
"$numberLong": "1741221003031"
156
}
157
}
158
}
159
],
160
120
"SyndicateMissions": [
161
121
{
162
122
"_id": { "$oid": "663a4fc5ba6f84724fa4804c" },