返回提交历史
Modified
src/controllers/api/inboxController.ts
+2
-1
Modified
src/helpers/nemesisHelpers.ts
+111
-3
Modified
src/services/missionInventoryUpdateService.ts
+10
-2
Modified
src/services/rngService.ts
+4
-1
XFEstudio/SpaceNinjaServer
feat: infested lich rewards (#1898)
Closes #1884 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1898 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
9042e853
代码差异
4 个文件
+127
-7
@@ -13,6 +13,7 @@ import { addItems, combineInventoryChanges, getInventory } from "@/src/services/
13
13
import { logger } from "@/src/utils/logger";
14
14
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
15
15
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
16
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
16
17
17
18
export const inboxController: RequestHandler = async (req, res) => {
18
19
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
@@ -48,7 +49,7 @@ export const inboxController: RequestHandler = async (req, res) => {
48
49
await addItems(
49
50
inventory,
50
51
attachmentItems.map(attItem => ({
51
ItemType: attItem,
52
ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
52
53
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
53
54
})),
54
55
inventoryChanges
@@ -1,12 +1,14 @@
1
1
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
2
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
3
import { SRng } from "@/src/services/rngService";
2
import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
3
import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
4
4
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
5
5
import { logger } from "../utils/logger";
6
6
import { IOid } from "../types/commonTypes";
7
7
import { Types } from "mongoose";
8
import { addMods } from "../services/inventoryService";
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";
10
12
11
13
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
12
14
const infNodes = [];
@@ -340,3 +342,109 @@ export const getInnateDamageValue = (fp: bigint): number => {
340
342
}
341
343
return Math.trunc(value * 0x40000000);
342
344
};
345
346
export const getKillTokenRewardCount = (fp: bigint): number => {
347
const rng = new SRng(fp);
348
return rng.randomInt(10, 15);
349
};
350
351
// /Lotus/Types/Enemies/InfestedLich/InfestedLichRewardManifest
352
const infestedLichRotA = [
353
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomHuman", probability: 0.046 },
354
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomInfested", probability: 0.045 },
355
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitHuman", probability: 0.046 },
356
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitInfested", probability: 0.045 },
357
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveHuman", probability: 0.046 },
358
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveInfested", probability: 0.045 },
359
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketHuman", probability: 0.046 },
360
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketInfested", probability: 0.045 },
361
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeHuman", probability: 0.046 },
362
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeInfested", probability: 0.045 },
363
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterA", probability: 0.045 },
364
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterB", probability: 0.046 },
365
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandDespairPoster", probability: 0.045 },
366
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandGridPoster", probability: 0.046 },
367
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandHuddlePoster", probability: 0.045 },
368
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandJumpPoster", probability: 0.046 },
369
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLimoPoster", probability: 0.045 },
370
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterDay", probability: 0.046 },
371
{
372
type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterNight",
373
probability: 0.045
374
},
375
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandSillyPoster", probability: 0.046 },
376
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhiteBluePoster", probability: 0.045 },
377
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhitePinkPoster", probability: 0.045 }
378
];
379
const infestedLichRotB = [
380
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraA", probability: 0.072 },
381
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraB", probability: 0.071 },
382
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraC", probability: 0.072 },
383
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraD", probability: 0.071 },
384
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraE", probability: 0.072 },
385
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraF", probability: 0.071 },
386
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraG", probability: 0.071 },
387
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraH", probability: 0.072 },
388
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDJRomHype", probability: 0.071 },
389
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DancePacketWindmillShuffle", probability: 0.072 },
390
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceHarddrivePony", probability: 0.071 },
391
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDrillbitCrisscross", probability: 0.072 },
392
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceZekeCanthavethis", probability: 0.071 },
393
{ type: "/Lotus/StoreItems/Types/Items/PhotoBooth/PhotoboothTileRJLasXStadiumBossArena", probability: 0.071 }
394
];
395
export const getInfestedLichItemRewards = (fp: bigint): string[] => {
396
const rng = new SRng(fp);
397
const rotAReward = getRewardAtPercentage(infestedLichRotA, rng.randomFloat())!.type;
398
rng.randomFloat(); // unused afaict
399
const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
400
return [rotAReward, rotBReward];
401
};
402
403
export const sendCodaFinishedMessage = async (
404
inventory: TInventoryDatabaseDocument,
405
fp: bigint = generateRewardSeed(),
406
name: string = "ZEKE_BEATWOMAN_TM.1999",
407
killed: boolean = true
408
): Promise<void> => {
409
const att: string[] = [];
410
411
// First vanquish/convert gives a sigil
412
const sigil = killed
413
? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
414
: "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
415
if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
416
att.push(toStoreItem(sigil));
417
}
418
419
const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
420
att.push(fromStoreItem(rotAReward));
421
att.push(fromStoreItem(rotBReward));
422
423
let countedAtt: ITypeCount[] | undefined;
424
if (killed) {
425
countedAtt = [
426
{
427
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
428
ItemCount: getKillTokenRewardCount(fp)
429
}
430
];
431
}
432
433
await createMessage(inventory.accountOwnerId, [
434
{
435
sndr: "/Lotus/Language/Bosses/Ordis",
436
msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
437
arg: [
438
{
439
Key: "LICH_NAME",
440
Tag: name
441
}
442
],
443
att: att,
444
countedAtt: countedAtt,
445
sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
446
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
447
highPriority: true
448
}
449
]);
450
};
@@ -55,7 +55,7 @@ import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.
55
55
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
56
56
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
57
57
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
58
import { getInfNodes, getWeaponsForManifest } from "@/src/helpers/nemesisHelpers";
58
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
59
59
import { Loadout } from "../models/inventoryModels/loadoutModel";
60
60
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
61
61
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
@@ -639,7 +639,10 @@ export const addMissionInventoryUpdates = async (
639
639
});
640
640
641
641
if (value.killed) {
642
if (value.weaponLoc) {
642
if (
643
value.weaponLoc &&
644
inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
645
) {
643
646
const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
644
647
inventory.Nemesis.WeaponIdx
645
648
];
@@ -657,6 +660,11 @@ export const addMissionInventoryUpdates = async (
657
660
}
658
661
}
659
662
663
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
664
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
665
await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
666
}
667
660
668
inventory.Nemesis = undefined;
661
669
}
662
670
break;
@@ -18,7 +18,10 @@ export const getRandomInt = (min: number, max: number): number => {
18
18
return Math.floor(Math.random() * (max - min + 1)) + min;
19
19
};
20
20
21
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
21
export const getRewardAtPercentage = <T extends { probability: number }>(
22
pool: T[],
23
percentage: number
24
): T | undefined => {
22
25
if (pool.length == 0) return;
23
26
24
27
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);