返回提交历史
Modified
src/controllers/api/nemesisController.ts
+2
-2
Modified
src/helpers/nemesisHelpers.ts
+51
-78
Modified
src/services/missionInventoryUpdateService.ts
+70
-32
XFEstudio/SpaceNinjaServer
feat: classic lich vanquish inbox mesage (#2074)
Closes #1897 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2074 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
52c8802d
代码差异
3 个文件
+123
-112
@@ -7,7 +7,7 @@ import {
7
7
getNemesisPasscodeModTypes,
8
8
getWeaponsForManifest,
9
9
IKnifeResponse,
10
showdownNodes
10
nemesisFactionInfos
11
11
} from "@/src/helpers/nemesisHelpers";
12
12
import { getJSONfromString } from "@/src/helpers/stringHelpers";
13
13
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
@@ -223,7 +223,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
223
223
224
224
inventory.Nemesis!.InfNodes = [
225
225
{
226
Node: showdownNodes[inventory.Nemesis!.Faction],
226
Node: nemesisFactionInfos[inventory.Nemesis!.Faction].showdownNode,
227
227
Influence: 1
228
228
}
229
229
];
@@ -1,5 +1,5 @@
1
1
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
2
import { IInfNode, ITypeCount, TNemesisFaction } from "@/src/types/inventoryTypes/inventoryTypes";
2
import { IInfNode, TNemesisFaction } from "@/src/types/inventoryTypes/inventoryTypes";
3
3
import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
4
4
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
5
5
import { logger } from "../utils/logger";
@@ -7,13 +7,51 @@ import { IOid } from "../types/commonTypes";
7
7
import { Types } from "mongoose";
8
8
import { addMods, generateRewardSeed } from "../services/inventoryService";
9
9
import { isArchwingMission } from "../services/worldStateService";
10
import { fromStoreItem, toStoreItem } from "../services/itemDataService";
11
import { createMessage } from "../services/inboxService";
12
10
import { version_compare } from "./inventoryHelpers";
13
11
12
export interface INemesisFactionInfo {
13
systemIndexes: number[];
14
showdownNode: string;
15
ephemeraChance: number;
16
firstKillReward: string;
17
firstConvertReward: string;
18
messageTitle: string;
19
messageBody: string;
20
}
21
22
export const nemesisFactionInfos: Record<TNemesisFaction, INemesisFactionInfo> = {
23
FC_GRINEER: {
24
systemIndexes: [2, 3, 9, 11, 18],
25
showdownNode: "CrewBattleNode557",
26
ephemeraChance: 0.05,
27
firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Clan/LichKillerBadgeItem",
28
firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/KuvaLichSigil",
29
messageTitle: "/Lotus/Language/Inbox/VanquishKuvaMsgTitle",
30
messageBody: "/Lotus/Language/Inbox/VanquishLichMsgBody"
31
},
32
FC_CORPUS: {
33
systemIndexes: [1, 15, 4, 7, 8],
34
showdownNode: "CrewBattleNode558",
35
ephemeraChance: 0.2,
36
firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Clan/CorpusLichBadgeItem",
37
firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/CorpusLichSigil",
38
messageTitle: "/Lotus/Language/Inbox/VanquishLawyerMsgTitle",
39
messageBody: "/Lotus/Language/Inbox/VanquishLichMsgBody"
40
},
41
FC_INFESTATION: {
42
systemIndexes: [23],
43
showdownNode: "CrewBattleNode559",
44
ephemeraChance: 0,
45
firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichVanquishedSigil",
46
firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichConvertedSigil",
47
messageTitle: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
48
messageBody: "/Lotus/Language/Inbox/VanquishBandMsgBody"
49
}
50
};
51
14
52
export const getInfNodes = (faction: TNemesisFaction, rank: number): IInfNode[] => {
15
53
const infNodes = [];
16
const systemIndex = systemIndexes[faction][rank];
54
const systemIndex = nemesisFactionInfos[faction].systemIndexes[rank];
17
55
for (const [key, value] of Object.entries(ExportRegions)) {
18
56
if (
19
57
value.systemIndex === systemIndex &&
@@ -35,24 +73,6 @@ export const getInfNodes = (faction: TNemesisFaction, rank: number): IInfNode[]
35
73
return infNodes;
36
74
};
37
75
38
const systemIndexes: Record<TNemesisFaction, number[]> = {
39
FC_GRINEER: [2, 3, 9, 11, 18],
40
FC_CORPUS: [1, 15, 4, 7, 8],
41
FC_INFESTATION: [23]
42
};
43
44
export const showdownNodes: Record<TNemesisFaction, string> = {
45
FC_GRINEER: "CrewBattleNode557",
46
FC_CORPUS: "CrewBattleNode558",
47
FC_INFESTATION: "CrewBattleNode559"
48
};
49
50
const ephemeraProbabilities: Record<TNemesisFaction, number> = {
51
FC_GRINEER: 0.05,
52
FC_CORPUS: 0.2,
53
FC_INFESTATION: 0
54
};
55
56
76
type TInnateDamageTag =
57
77
| "InnateElectricityDamage"
58
78
| "InnateHeatDamage"
@@ -311,11 +331,17 @@ export const getInnateDamageTag = (KillingSuit: string): TInnateDamageTag => {
311
331
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
312
332
};
313
333
334
const petHeads = [
335
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
336
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
337
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
338
] as const;
339
314
340
export interface INemesisProfile {
315
341
innateDamageTag: TInnateDamageTag;
316
342
innateDamageValue: number;
317
343
ephemera?: string;
318
petHead?: string;
344
petHead?: (typeof petHeads)[number];
319
345
petBody?: string;
320
346
petLegs?: string;
321
347
petTail?: string;
@@ -337,16 +363,12 @@ export const generateNemesisProfile = (
337
363
innateDamageTag: getInnateDamageTag(killingSuit),
338
364
innateDamageValue: Math.trunc(value * 0x40000000) // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
339
365
};
340
if (rng.randomFloat() <= ephemeraProbabilities[Faction] && Faction != "FC_INFESTATION") {
366
if (rng.randomFloat() <= nemesisFactionInfos[Faction].ephemeraChance && Faction != "FC_INFESTATION") {
341
367
profile.ephemera = ephmeraTypes[Faction][profile.innateDamageTag];
342
368
}
343
369
rng.randomFloat(); // something related to sentinel agent maybe
344
370
if (Faction == "FC_CORPUS") {
345
profile.petHead = rng.randomElement([
346
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
347
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
348
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
349
])!;
371
profile.petHead = rng.randomElement(petHeads)!;
350
372
profile.petBody = rng.randomElement([
351
373
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
352
374
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
@@ -422,52 +444,3 @@ export const getInfestedLichItemRewards = (fp: bigint): string[] => {
422
444
const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
423
445
return [rotAReward, rotBReward];
424
446
};
425
426
export const sendCodaFinishedMessage = async (
427
inventory: TInventoryDatabaseDocument,
428
fp: bigint = generateRewardSeed(),
429
name: string = "ZEKE_BEATWOMAN_TM.1999",
430
killed: boolean = true
431
): Promise<void> => {
432
const att: string[] = [];
433
434
// First vanquish/convert gives a sigil
435
const sigil = killed
436
? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
437
: "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
438
if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
439
att.push(toStoreItem(sigil));
440
}
441
442
const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
443
att.push(fromStoreItem(rotAReward));
444
att.push(fromStoreItem(rotBReward));
445
446
let countedAtt: ITypeCount[] | undefined;
447
if (killed) {
448
countedAtt = [
449
{
450
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
451
ItemCount: getKillTokenRewardCount(fp)
452
}
453
];
454
}
455
456
await createMessage(inventory.accountOwnerId, [
457
{
458
sndr: "/Lotus/Language/Bosses/Ordis",
459
msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
460
arg: [
461
{
462
Key: "LICH_NAME",
463
Tag: name
464
}
465
],
466
att: att,
467
countedAtt: countedAtt,
468
sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
469
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
470
highPriority: true
471
}
472
]);
473
};
@@ -10,7 +10,7 @@ import {
10
10
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
11
11
import { logger } from "@/src/utils/logger";
12
12
import { IRngResult, SRng, getRandomElement, getRandomReward } from "@/src/services/rngService";
13
import { equipmentKeys, IMission, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
13
import { equipmentKeys, IMission, ITypeCount, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
14
14
import {
15
15
addBooster,
16
16
addChallenges,
@@ -44,7 +44,7 @@ import {
44
44
import { updateQuestKey } from "@/src/services/questService";
45
45
import { Types } from "mongoose";
46
46
import { IAffiliationMods, IInventoryChanges } from "@/src/types/purchaseTypes";
47
import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService";
47
import { fromStoreItem, getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService";
48
48
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
49
49
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
50
50
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
@@ -58,10 +58,12 @@ import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPe
58
58
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
59
59
import {
60
60
generateNemesisProfile,
61
getInfestedLichItemRewards,
61
62
getInfNodes,
63
getKillTokenRewardCount,
62
64
getNemesisPasscode,
63
65
getWeaponsForManifest,
64
sendCodaFinishedMessage
66
nemesisFactionInfos
65
67
} from "@/src/helpers/nemesisHelpers";
66
68
import { Loadout } from "../models/inventoryModels/loadoutModel";
67
69
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
@@ -665,6 +667,9 @@ export const addMissionInventoryUpdates = async (
665
667
inventory.Nemesis.Faction,
666
668
inventory.Nemesis.KillingSuit
667
669
);
670
const nemesisFactionInfo = nemesisFactionInfos[inventory.Nemesis.Faction];
671
const att: string[] = [];
672
let countedAtt: ITypeCount[] | undefined;
668
673
669
674
if (value.killed) {
670
675
if (
@@ -675,45 +680,78 @@ export const addMissionInventoryUpdates = async (
675
680
inventory.Nemesis.WeaponIdx
676
681
];
677
682
giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
683
att.push(weaponType);
678
684
}
679
if (value.petLoc) {
685
//if (value.petLoc) {
686
if (profile.petHead) {
680
687
giveNemesisPetRecipe(inventory, value.nemesisName, profile);
688
att.push(
689
{
690
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
691
"/Lotus/Types/Recipes/ZanukaPet/ZanukaPetCompleteHeadABlueprint",
692
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
693
"/Lotus/Types/Recipes/ZanukaPet/ZanukaPetCompleteHeadBBlueprint",
694
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
695
"/Lotus/Types/Recipes/ZanukaPet/ZanukaPetCompleteHeadCBlueprint"
696
}[profile.petHead]
697
);
681
698
}
682
699
}
683
700
684
701
// "Players will receive a Lich's Ephemera regardless of whether they Vanquish or Convert them."
685
702
if (profile.ephemera) {
686
703
addSkin(inventory, profile.ephemera);
704
att.push(profile.ephemera);
687
705
}
688
706
689
switch (inventory.Nemesis.Faction) {
690
case "FC_GRINEER":
691
addSkin(
692
inventory,
693
value.killed
694
? "/Lotus/Upgrades/Skins/Clan/LichKillerBadgeItem"
695
: "/Lotus/Upgrades/Skins/Sigils/KuvaLichSigil"
696
);
697
break;
698
699
case "FC_CORPUS":
700
addSkin(
701
inventory,
702
value.killed
703
? "/Lotus/Upgrades/Skins/Clan/CorpusLichBadgeItem"
704
: "/Lotus/Upgrades/Skins/Sigils/CorpusLichSigil"
705
);
706
break;
707
708
case "FC_INFESTATION":
709
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
710
await sendCodaFinishedMessage(
711
inventory,
712
inventory.Nemesis.fp,
713
value.nemesisName,
714
value.killed
715
);
716
break;
707
const skinRewardStoreItem = value.killed
708
? nemesisFactionInfo.firstKillReward
709
: nemesisFactionInfo.firstConvertReward;
710
if (Object.keys(addSkin(inventory, fromStoreItem(skinRewardStoreItem))).length != 0) {
711
att.push(skinRewardStoreItem);
712
}
713
714
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
715
const [rotARewardStoreItem, rotBRewardStoreItem] = getInfestedLichItemRewards(
716
inventory.Nemesis.fp
717
);
718
const rotAReward = fromStoreItem(rotARewardStoreItem);
719
const rotBReward = fromStoreItem(rotBRewardStoreItem);
720
await addItem(inventory, rotAReward);
721
await addItem(inventory, rotBReward);
722
att.push(rotAReward);
723
att.push(rotBReward);
724
725
if (value.killed) {
726
countedAtt = [
727
{
728
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
729
ItemCount: getKillTokenRewardCount(inventory.Nemesis.fp)
730
}
731
];
732
addMiscItems(inventory, countedAtt);
733
}
734
}
735
736
if (value.killed) {
737
await createMessage(inventory.accountOwnerId, [
738
{
739
sndr: "/Lotus/Language/Bosses/Ordis",
740
msg: nemesisFactionInfo.messageBody,
741
arg: [
742
{
743
Key: "LICH_NAME",
744
Tag: value.nemesisName
745
}
746
],
747
att: att,
748
countedAtt: countedAtt,
749
attVisualOnly: true,
750
sub: nemesisFactionInfo.messageTitle,
751
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
752
highPriority: true
753
}
754
]);
717
755
}
718
756
719
757
inventory.Nemesis = undefined;