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

SpaceNinjaServer

A simple server for a small space ninja game

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

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
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

3 个文件 +123 -112
Modified src/controllers/api/nemesisController.ts +2 -2
@@ -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 ];
Modified src/helpers/nemesisHelpers.ts +51 -78
@@ -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 };
Modified src/services/missionInventoryUpdateService.ts +70 -32
@@ -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;