XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: handle NemesisKillConvert at missionInventoryUpdate (#1880)

Closes #1848 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1880 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

ee1a49f5
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

8 个文件 +344 -61
Modified src/controllers/api/claimCompletedRecipeController.ts +8 -1
@@ -133,7 +133,14 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
133 133 if (recipe.secretIngredientAction != "SIA_UNBRAND") {
134 134 InventoryChanges = {
135 135 ...InventoryChanges,
136 ...(await addItem(inventory, recipe.resultType, recipe.num, false))
136 ...(await addItem(
137 inventory,
138 recipe.resultType,
139 recipe.num,
140 false,
141 undefined,
142 pendingRecipe.TargetFingerprint
143 ))
137 144 };
138 145 }
139 146 await inventory.save();
Modified src/controllers/api/nemesisController.ts +2 -48
@@ -3,6 +3,7 @@ import {
3 3 encodeNemesisGuess,
4 4 getInfNodes,
5 5 getNemesisPasscode,
6 getWeaponsForManifest,
6 7 IKnifeResponse
7 8 } from "@/src/helpers/nemesisHelpers";
8 9 import { getJSONfromString } from "@/src/helpers/stringHelpers";
@@ -170,18 +171,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
170 171
171 172 let weaponIdx = -1;
172 173 if (body.target.Faction != "FC_INFESTATION") {
173 let weapons: readonly string[];
174 if (body.target.manifest == "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix") {
175 weapons = kuvaLichVersionSixWeapons;
176 } else if (
177 body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour" ||
178 body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"
179 ) {
180 weapons = corpusVersionThreeWeapons;
181 } else {
182 throw new Error(`unknown nemesis manifest: ${body.target.manifest}`);
183 }
184
174 const weapons = getWeaponsForManifest(body.target.manifest);
185 175 const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
186 176 weaponIdx = initialWeaponIdx;
187 177 do {
@@ -283,39 +273,3 @@ interface INemesisRequiemRequest {
283 273 HiddenWhenHolstered: boolean;
284 274 };
285 275 }
286
287 const kuvaLichVersionSixWeapons = [
288 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
289 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
290 "/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
291 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
292 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
293 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
294 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
295 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
296 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
297 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
298 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
299 "/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
300 "/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
301 "/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
302 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
303 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
304 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
305 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
306 "/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
307 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
308 ];
309
310 const corpusVersionThreeWeapons = [
311 "/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
312 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
313 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
314 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
315 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
316 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
317 "/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
318 "/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
319 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
320 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
321 ];
Modified src/helpers/nemesisHelpers.ts +145 -1
@@ -1,4 +1,4 @@
1 import { ExportRegions } from "warframe-public-export-plus";
1 import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
2 2 import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
3 3 import { SRng } from "@/src/services/rngService";
4 4 import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
@@ -129,3 +129,147 @@ export const consumeModCharge = (
129 129 response.UpgradeNew.push(true);
130 130 }
131 131 };
132
133 const kuvaLichVersionSixWeapons = [
134 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
135 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
136 "/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
137 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
138 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
139 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
140 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
141 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
142 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
143 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
144 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
145 "/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
146 "/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
147 "/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
148 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
149 "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
150 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
151 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
152 "/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
153 "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
154 ];
155
156 const corpusVersionThreeWeapons = [
157 "/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
158 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
159 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
160 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
161 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
162 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
163 "/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
164 "/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
165 "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
166 "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
167 ];
168
169 export const getWeaponsForManifest = (manifest: string): readonly string[] => {
170 switch (manifest) {
171 case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix":
172 return kuvaLichVersionSixWeapons;
173 case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree":
174 case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour":
175 return corpusVersionThreeWeapons;
176 }
177 throw new Error(`unknown nemesis manifest: ${manifest}`);
178 };
179
180 // TODO: This sucks.
181 export const getInnateDamageTag = (
182 KillingSuit: string
183 ):
184 | "InnateElectricityDamage"
185 | "InnateFreezeDamage"
186 | "InnateHeatDamage"
187 | "InnateImpactDamage"
188 | "InnateMagDamage"
189 | "InnateRadDamage"
190 | "InnateToxinDamage" => {
191 const baseSuitType = ExportWarframes[KillingSuit].parentName;
192 switch (baseSuitType) {
193 case "/Lotus/Powersuits/Volt/VoltBaseSuit":
194 case "/Lotus/Powersuits/Excalibur/ExcaliburBaseSuit":
195 case "/Lotus/Powersuits/AntiMatter/NovaBaseSuit":
196 case "/Lotus/Powersuits/Banshee/BansheeBaseSuit":
197 case "/Lotus/Powersuits/Berserker/BerserkerBaseSuit":
198 case "/Lotus/Powersuits/Magician/MagicianBaseSuit":
199 case "/Lotus/Powersuits/Sentient/SentientBaseSuit":
200 case "/Lotus/Powersuits/Gyre/GyreBaseSuit":
201 return "InnateElectricityDamage";
202 case "/Lotus/Powersuits/Ember/EmberBaseSuit":
203 case "/Lotus/Powersuits/Dragon/DragonBaseSuit":
204 case "/Lotus/Powersuits/Nezha/NezhaBaseSuit":
205 case "/Lotus/Powersuits/Sandman/SandmanBaseSuit":
206 case "/Lotus/Powersuits/Trapper/TrapperBaseSuit":
207 case "/Lotus/Powersuits/Wisp/WispBaseSuit":
208 case "/Lotus/Powersuits/Odalisk/OdaliskBaseSuit":
209 case "/Lotus/Powersuits/PaxDuviricus/PaxDuviricusBaseSuit":
210 case "/Lotus/Powersuits/Choir/ChoirBaseSuit":
211 case "/Lotus/Powersuits/Temple/TempleBaseSuit":
212 return "InnateHeatDamage";
213 case "/Lotus/Powersuits/Frost/FrostBaseSuit":
214 case "/Lotus/Powersuits/Glass/GlassBaseSuit":
215 case "/Lotus/Powersuits/Fairy/FairyBaseSuit":
216 case "/Lotus/Powersuits/IronFrame/IronFrameBaseSuit":
217 case "/Lotus/Powersuits/Revenant/RevenantBaseSuit":
218 case "/Lotus/Powersuits/Trinity/TrinityBaseSuit":
219 case "/Lotus/Powersuits/Hoplite/HopliteBaseSuit":
220 case "/Lotus/Powersuits/Koumei/KoumeiBaseSuit":
221 return "InnateFreezeDamage";
222 case "/Lotus/Powersuits/Saryn/SarynBaseSuit":
223 case "/Lotus/Powersuits/Paladin/PaladinBaseSuit":
224 case "/Lotus/Powersuits/Brawler/BrawlerBaseSuit":
225 case "/Lotus/Powersuits/Infestation/InfestationBaseSuit":
226 case "/Lotus/Powersuits/Necro/NecroBaseSuit":
227 case "/Lotus/Powersuits/Khora/KhoraBaseSuit":
228 case "/Lotus/Powersuits/Ranger/RangerBaseSuit":
229 case "/Lotus/Powersuits/Dagath/DagathBaseSuit":
230 return "InnateToxinDamage";
231 case "/Lotus/Powersuits/Mag/MagBaseSuit":
232 case "/Lotus/Powersuits/Pirate/PirateBaseSuit":
233 case "/Lotus/Powersuits/Cowgirl/CowgirlBaseSuit":
234 case "/Lotus/Powersuits/Priest/PriestBaseSuit":
235 case "/Lotus/Powersuits/BrokenFrame/BrokenFrameBaseSuit":
236 case "/Lotus/Powersuits/Alchemist/AlchemistBaseSuit":
237 case "/Lotus/Powersuits/Yareli/YareliBaseSuit":
238 case "/Lotus/Powersuits/Geode/GeodeBaseSuit":
239 case "/Lotus/Powersuits/Frumentarius/FrumentariusBaseSuit":
240 return "InnateMagDamage";
241 case "/Lotus/Powersuits/Loki/LokiBaseSuit":
242 case "/Lotus/Powersuits/Ninja/NinjaBaseSuit":
243 case "/Lotus/Powersuits/Jade/JadeBaseSuit":
244 case "/Lotus/Powersuits/Bard/BardBaseSuit":
245 case "/Lotus/Powersuits/Harlequin/HarlequinBaseSuit":
246 case "/Lotus/Powersuits/Garuda/GarudaBaseSuit":
247 case "/Lotus/Powersuits/YinYang/YinYangBaseSuit":
248 case "/Lotus/Powersuits/Werewolf/WerewolfBaseSuit":
249 case "/Lotus/Powersuits/ConcreteFrame/ConcreteFrameBaseSuit":
250 return "InnateRadDamage";
251 case "/Lotus/Powersuits/Rhino/RhinoBaseSuit":
252 case "/Lotus/Powersuits/Tengu/TenguBaseSuit":
253 case "/Lotus/Powersuits/MonkeyKing/MonkeyKingBaseSuit":
254 case "/Lotus/Powersuits/Runner/RunnerBaseSuit":
255 case "/Lotus/Powersuits/Pacifist/PacifistBaseSuit":
256 case "/Lotus/Powersuits/Devourer/DevourerBaseSuit":
257 case "/Lotus/Powersuits/Wraith/WraithBaseSuit":
258 case "/Lotus/Powersuits/Pagemaster/PagemasterBaseSuit":
259 return "InnateImpactDamage";
260 }
261 logger.warn(`unknown innate damage type for ${KillingSuit}, using heat as a fallback`);
262 return "InnateHeatDamage";
263 };
264
265 // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
266 export const getInnateDamageValue = (fp: bigint): number => {
267 const rng = new SRng(fp);
268 rng.randomFloat(); // used for the weapon index
269 const WeaponUpgradeValueAttenuationExponent = 2.25;
270 let value = Math.pow(rng.randomFloat(), WeaponUpgradeValueAttenuationExponent);
271 if (value >= 0.941428) {
272 value = 1;
273 }
274 return Math.trunc(value * 0x40000000);
275 };
Modified src/models/inventoryModels/inventoryModel.ts +6 -4
@@ -1039,6 +1039,8 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
1039 1039 {
1040 1040 ItemType: String,
1041 1041 CompletionDate: Date,
1042 TargetItemId: String,
1043 TargetFingerprint: String,
1042 1044 LongGuns: { type: [EquipmentSchema], default: undefined },
1043 1045 Pistols: { type: [EquipmentSchema], default: undefined },
1044 1046 Melee: { type: [EquipmentSchema], default: undefined },
@@ -1260,11 +1262,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
1260 1262 PrevOwners: Number,
1261 1263 SecondInCommand: Boolean,
1262 1264 Weakened: Boolean,
1263 InfNodes: [infNodeSchema],
1265 InfNodes: { type: [infNodeSchema], default: undefined },
1264 1266 HenchmenKilled: Number,
1265 1267 HintProgress: Number,
1266 Hints: [Number],
1267 GuessHistory: [Number],
1268 Hints: { type: [Number], default: undefined },
1269 GuessHistory: { type: [Number], default: undefined },
1268 1270 MissionCount: Number,
1269 1271 LastEnc: Number
1270 1272 },
@@ -1609,7 +1611,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1609 1611 //CorpusLich or GrineerLich
1610 1612 NemesisAbandonedRewards: { type: [String], default: [] },
1611 1613 Nemesis: nemesisSchema,
1612 NemesisHistory: [Schema.Types.Mixed],
1614 NemesisHistory: { type: [nemesisSchema], default: undefined },
1613 1615 LastNemesisAllySpawnTime: Schema.Types.Mixed,
1614 1616
1615 1617 //TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
Modified src/services/inventoryService.ts +108 -2
@@ -26,7 +26,9 @@ import {
26 26 Status,
27 27 IKubrowPetDetailsDatabase,
28 28 ITraits,
29 ICalendarProgress
29 ICalendarProgress,
30 INemesisWeaponTargetFingerprint,
31 INemesisPetTargetFingerprint
30 32 } from "@/src/types/inventoryTypes/inventoryTypes";
31 33 import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
32 34 import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@@ -79,6 +81,7 @@ import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from ".
79 81 import { createMessage } from "./inboxService";
80 82 import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
81 83 import { getWorldState } from "./worldStateService";
84 import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
82 85
83 86 export const createInventory = async (
84 87 accountOwnerId: Types.ObjectId,
@@ -327,7 +330,8 @@ export const addItem = async (
327 330 typeName: string,
328 331 quantity: number = 1,
329 332 premiumPurchase: boolean = false,
330 seed?: bigint
333 seed?: bigint,
334 targetFingerprint?: string
331 335 ): Promise<IInventoryChanges> => {
332 336 // Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
333 337 if (typeName in ExportBundles) {
@@ -530,6 +534,12 @@ export const addItem = async (
530 534 ]
531 535 });
532 536 }
537 if (targetFingerprint) {
538 const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisWeaponTargetFingerprint;
539 defaultOverwrites.UpgradeType = targetFingerprintObj.ItemType;
540 defaultOverwrites.UpgradeFingerprint = JSON.stringify(targetFingerprintObj.UpgradeFingerprint);
541 defaultOverwrites.ItemName = targetFingerprintObj.Name;
542 }
533 543 const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites);
534 544 if (weapon.additionalItems) {
535 545 for (const item of weapon.additionalItems) {
@@ -544,6 +554,27 @@ export const addItem = async (
544 554 premiumPurchase
545 555 )
546 556 };
557 } else if (targetFingerprint) {
558 // Sister's Hound
559 const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisPetTargetFingerprint;
560 const head = targetFingerprintObj.Parts[0];
561 const defaultOverwrites: Partial<IEquipmentDatabase> = {
562 ModularParts: targetFingerprintObj.Parts,
563 ItemName: targetFingerprintObj.Name,
564 Configs: applyDefaultUpgrades(inventory, ExportWeapons[head].defaultUpgrades)
565 };
566 const itemType = {
567 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
568 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
569 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
570 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
571 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
572 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
573 }[head] as string;
574 return {
575 ...addEquipment(inventory, "MoaPets", itemType, defaultOverwrites),
576 ...occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase)
577 };
547 578 } else {
548 579 // Modular weapon parts
549 580 const miscItemChanges = [
@@ -1851,3 +1882,78 @@ export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICal
1851 1882
1852 1883 return inventory.CalendarProgress;
1853 1884 };
1885
1886 export const giveNemesisWeaponRecipe = (
1887 inventory: TInventoryDatabaseDocument,
1888 weaponType: string,
1889 nemesisName: string = "AGOR ROK",
1890 weaponLoc?: string,
1891 KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
1892 fp: bigint = generateRewardSeed()
1893 ): void => {
1894 if (!weaponLoc) {
1895 weaponLoc = ExportWeapons[weaponType].name;
1896 }
1897 const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == weaponType)![0];
1898 addRecipes(inventory, [
1899 {
1900 ItemType: recipeType,
1901 ItemCount: 1
1902 }
1903 ]);
1904 inventory.PendingRecipes.push({
1905 CompletionDate: new Date(),
1906 ItemType: recipeType,
1907 TargetFingerprint: JSON.stringify({
1908 ItemType: "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod",
1909 UpgradeFingerprint: {
1910 compat: weaponType,
1911 buffs: [
1912 {
1913 Tag: getInnateDamageTag(KillingSuit),
1914 Value: getInnateDamageValue(fp)
1915 }
1916 ]
1917 },
1918 Name: weaponLoc + "|" + nemesisName
1919 } satisfies INemesisWeaponTargetFingerprint)
1920 });
1921 };
1922
1923 export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
1924 const head = getRandomElement([
1925 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
1926 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
1927 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
1928 ]);
1929 const body = getRandomElement([
1930 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
1931 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
1932 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
1933 ]);
1934 const legs = getRandomElement([
1935 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
1936 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
1937 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
1938 ]);
1939 const tail = getRandomElement([
1940 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
1941 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
1942 "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
1943 ]);
1944 const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
1945 addRecipes(inventory, [
1946 {
1947 ItemType: recipeType,
1948 ItemCount: 1
1949 }
1950 ]);
1951 inventory.PendingRecipes.push({
1952 CompletionDate: new Date(),
1953 ItemType: recipeType,
1954 TargetFingerprint: JSON.stringify({
1955 Parts: [head, body, legs, tail],
1956 Name: "/Lotus/Language/Pets/ZanukaPetName|" + nemesisName
1957 } satisfies INemesisPetTargetFingerprint)
1958 });
1959 };
Modified src/services/missionInventoryUpdateService.ts +49 -1
@@ -35,6 +35,8 @@ import {
35 35 combineInventoryChanges,
36 36 generateRewardSeed,
37 37 getCalendarProgress,
38 giveNemesisPetRecipe,
39 giveNemesisWeaponRecipe,
38 40 updateCurrency,
39 41 updateSyndicate
40 42 } from "@/src/services/inventoryService";
@@ -53,7 +55,7 @@ import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.
53 55 import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
54 56 import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
55 57 import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
56 import { getInfNodes } from "@/src/helpers/nemesisHelpers";
58 import { getInfNodes, getWeaponsForManifest } from "@/src/helpers/nemesisHelpers";
57 59 import { Loadout } from "../models/inventoryModels/loadoutModel";
58 60 import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
59 61 import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
@@ -612,6 +614,52 @@ export const addMissionInventoryUpdates = async (
612 614 }
613 615 break;
614 616 }
617 case "NemesisKillConvert":
618 if (inventory.Nemesis) {
619 inventory.NemesisHistory ??= [];
620 inventory.NemesisHistory.push({
621 // Copy over all 'base' values
622 fp: inventory.Nemesis.fp,
623 d: inventory.Nemesis.d,
624 manifest: inventory.Nemesis.manifest,
625 KillingSuit: inventory.Nemesis.KillingSuit,
626 killingDamageType: inventory.Nemesis.killingDamageType,
627 ShoulderHelmet: inventory.Nemesis.ShoulderHelmet,
628 WeaponIdx: inventory.Nemesis.WeaponIdx,
629 AgentIdx: inventory.Nemesis.AgentIdx,
630 BirthNode: inventory.Nemesis.BirthNode,
631 Faction: inventory.Nemesis.Faction,
632 Rank: inventory.Nemesis.Rank,
633 Traded: inventory.Nemesis.Traded,
634 PrevOwners: inventory.Nemesis.PrevOwners,
635 SecondInCommand: inventory.Nemesis.SecondInCommand,
636 Weakened: inventory.Nemesis.Weakened,
637 // And set killed flag
638 k: value.killed
639 });
640
641 if (value.killed) {
642 if (value.weaponLoc) {
643 const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
644 inventory.Nemesis.WeaponIdx
645 ];
646 giveNemesisWeaponRecipe(
647 inventory,
648 weaponType,
649 value.nemesisName,
650 value.weaponLoc,
651 inventory.Nemesis.KillingSuit,
652 inventory.Nemesis.fp
653 );
654 }
655 if (value.petLoc) {
656 giveNemesisPetRecipe(inventory);
657 }
658 }
659
660 inventory.Nemesis = undefined;
661 }
662 break;
615 663 default:
616 664 // Equipment XP updates
617 665 if (equipmentKeys.includes(key as TEquipmentKey)) {
Modified src/types/inventoryTypes/inventoryTypes.ts +16 -3
@@ -43,6 +43,7 @@ export interface IInventoryDatabase
43 43 | "RecentVendorPurchases"
44 44 | "NextRefill"
45 45 | "Nemesis"
46 | "NemesisHistory"
46 47 | "EntratiVaultCountResetDate"
47 48 | "BrandedSuits"
48 49 | "LockedWeaponGroup"
@@ -79,6 +80,7 @@ export interface IInventoryDatabase
79 80 RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
80 81 NextRefill?: Date;
81 82 Nemesis?: INemesisDatabase;
83 NemesisHistory?: INemesisBaseDatabase[];
82 84 EntratiVaultCountResetDate?: Date;
83 85 BrandedSuits?: Types.ObjectId[];
84 86 LockedWeaponGroup?: ILockedWeaponGroupDatabase;
@@ -313,7 +315,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
313 315 EquippedInstrument?: string;
314 316 InvasionChainProgress: IInvasionChainProgress[];
315 317 Nemesis?: INemesisClient;
316 NemesisHistory: INemesisBaseClient[];
318 NemesisHistory?: INemesisBaseClient[];
317 319 LastNemesisAllySpawnTime?: IMongoDate;
318 320 Settings?: ISettings;
319 321 PersonalTechProjects: IPersonalTechProjectClient[];
@@ -902,8 +904,8 @@ export interface IPendingRecipeDatabase {
902 904 ItemType: string;
903 905 CompletionDate: Date;
904 906 ItemId: IOid;
905 TargetItemId?: string; // likely related to liches
906 TargetFingerprint?: string; // likely related to liches
907 TargetItemId?: string; // unsure what this is for
908 TargetFingerprint?: string;
907 909 LongGuns?: IEquipmentDatabase[];
908 910 Pistols?: IEquipmentDatabase[];
909 911 Melee?: IEquipmentDatabase[];
@@ -951,6 +953,17 @@ export interface ICrewShipComponentFingerprint extends IInnateDamageFingerprint
951 953 SubroutineIndex?: number;
952 954 }
953 955
956 export interface INemesisWeaponTargetFingerprint {
957 ItemType: string;
958 UpgradeFingerprint: IInnateDamageFingerprint;
959 Name: string;
960 }
961
962 export interface INemesisPetTargetFingerprint {
963 Parts: string[];
964 Name: string;
965 }
966
954 967 export enum GettingSlotOrderInfo {
955 968 Empty = "",
956 969 LotusUpgradesModsRandomizedPlayerMeleeWeaponRandomModRare0 = "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare:0",
Modified src/types/requestTypes.ts +10 -1
@@ -22,7 +22,8 @@ import {
22 22 ILoadOutPresets,
23 23 IInvasionProgressClient,
24 24 IWeaponSkinClient,
25 IKubrowPetEggClient
25 IKubrowPetEggClient,
26 INemesisClient
26 27 } from "./inventoryTypes/inventoryTypes";
27 28 import { IGroup } from "./loginTypes";
28 29
@@ -74,6 +75,14 @@ export type IMissionInventoryUpdateRequest = {
74 75 PS: string;
75 76 ActiveDojoColorResearch: string;
76 77 RewardInfo?: IRewardInfo;
78 NemesisKillConvert?: {
79 nemesisName: string;
80 weaponLoc: string;
81 petLoc: "" | "/Lotus/Language/Pets/ZanukaPetName";
82 fingerprint: bigint | number;
83 killed: boolean;
84 };
85 target?: INemesisClient;
77 86 ReceivedCeremonyMsg: boolean;
78 87 LastCeremonyResetDate: number;
79 88 MissionPTS: number;