feat: infested lich rewards (#1898)
Closes #1884 Reviewed-on: OpenWF/SpaceNinjaServer#1898 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									66ee550ccd
								
							
						
					
					
						commit
						9042e85355
					
				@ -13,6 +13,7 @@ import { addItems, combineInventoryChanges, getInventory } from "@/src/services/
 | 
				
			|||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
 | 
					import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
 | 
				
			||||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
					import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
				
			||||||
 | 
					import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const inboxController: RequestHandler = async (req, res) => {
 | 
					export const inboxController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
 | 
					    const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
 | 
				
			||||||
@ -48,7 +49,7 @@ export const inboxController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            await addItems(
 | 
					            await addItems(
 | 
				
			||||||
                inventory,
 | 
					                inventory,
 | 
				
			||||||
                attachmentItems.map(attItem => ({
 | 
					                attachmentItems.map(attItem => ({
 | 
				
			||||||
                    ItemType: attItem,
 | 
					                    ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
 | 
				
			||||||
                    ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
 | 
					                    ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
 | 
				
			||||||
                })),
 | 
					                })),
 | 
				
			||||||
                inventoryChanges
 | 
					                inventoryChanges
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,14 @@
 | 
				
			|||||||
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
 | 
					import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
 | 
				
			||||||
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { SRng } from "@/src/services/rngService";
 | 
					import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
 | 
				
			||||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
					import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
				
			||||||
import { logger } from "../utils/logger";
 | 
					import { logger } from "../utils/logger";
 | 
				
			||||||
import { IOid } from "../types/commonTypes";
 | 
					import { IOid } from "../types/commonTypes";
 | 
				
			||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { addMods } from "../services/inventoryService";
 | 
					import { addMods, generateRewardSeed } from "../services/inventoryService";
 | 
				
			||||||
import { isArchwingMission } from "../services/worldStateService";
 | 
					import { isArchwingMission } from "../services/worldStateService";
 | 
				
			||||||
 | 
					import { fromStoreItem, toStoreItem } from "../services/itemDataService";
 | 
				
			||||||
 | 
					import { createMessage } from "../services/inboxService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
 | 
					export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
 | 
				
			||||||
    const infNodes = [];
 | 
					    const infNodes = [];
 | 
				
			||||||
@ -340,3 +342,109 @@ export const getInnateDamageValue = (fp: bigint): number => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return Math.trunc(value * 0x40000000);
 | 
					    return Math.trunc(value * 0x40000000);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getKillTokenRewardCount = (fp: bigint): number => {
 | 
				
			||||||
 | 
					    const rng = new SRng(fp);
 | 
				
			||||||
 | 
					    return rng.randomInt(10, 15);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// /Lotus/Types/Enemies/InfestedLich/InfestedLichRewardManifest
 | 
				
			||||||
 | 
					const infestedLichRotA = [
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomHuman", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomInfested", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitHuman", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitInfested", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveHuman", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveInfested", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketHuman", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketInfested", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeHuman", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeInfested", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterA", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterB", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandDespairPoster", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandGridPoster", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandHuddlePoster", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandJumpPoster", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLimoPoster", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterDay", probability: 0.046 },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterNight",
 | 
				
			||||||
 | 
					        probability: 0.045
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandSillyPoster", probability: 0.046 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhiteBluePoster", probability: 0.045 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhitePinkPoster", probability: 0.045 }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					const infestedLichRotB = [
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraA", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraB", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraC", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraD", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraE", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraF", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraG", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraH", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDJRomHype", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/Emotes/DancePacketWindmillShuffle", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/Emotes/DanceHarddrivePony", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDrillbitCrisscross", probability: 0.072 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/Emotes/DanceZekeCanthavethis", probability: 0.071 },
 | 
				
			||||||
 | 
					    { type: "/Lotus/StoreItems/Types/Items/PhotoBooth/PhotoboothTileRJLasXStadiumBossArena", probability: 0.071 }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					export const getInfestedLichItemRewards = (fp: bigint): string[] => {
 | 
				
			||||||
 | 
					    const rng = new SRng(fp);
 | 
				
			||||||
 | 
					    const rotAReward = getRewardAtPercentage(infestedLichRotA, rng.randomFloat())!.type;
 | 
				
			||||||
 | 
					    rng.randomFloat(); // unused afaict
 | 
				
			||||||
 | 
					    const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
 | 
				
			||||||
 | 
					    return [rotAReward, rotBReward];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const sendCodaFinishedMessage = async (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    fp: bigint = generateRewardSeed(),
 | 
				
			||||||
 | 
					    name: string = "ZEKE_BEATWOMAN_TM.1999",
 | 
				
			||||||
 | 
					    killed: boolean = true
 | 
				
			||||||
 | 
					): Promise<void> => {
 | 
				
			||||||
 | 
					    const att: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // First vanquish/convert gives a sigil
 | 
				
			||||||
 | 
					    const sigil = killed
 | 
				
			||||||
 | 
					        ? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
 | 
				
			||||||
 | 
					        : "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
 | 
				
			||||||
 | 
					    if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
 | 
				
			||||||
 | 
					        att.push(toStoreItem(sigil));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
 | 
				
			||||||
 | 
					    att.push(fromStoreItem(rotAReward));
 | 
				
			||||||
 | 
					    att.push(fromStoreItem(rotBReward));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let countedAtt: ITypeCount[] | undefined;
 | 
				
			||||||
 | 
					    if (killed) {
 | 
				
			||||||
 | 
					        countedAtt = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
 | 
				
			||||||
 | 
					                ItemCount: getKillTokenRewardCount(fp)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await createMessage(inventory.accountOwnerId, [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            sndr: "/Lotus/Language/Bosses/Ordis",
 | 
				
			||||||
 | 
					            msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
 | 
				
			||||||
 | 
					            arg: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Key: "LICH_NAME",
 | 
				
			||||||
 | 
					                    Tag: name
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            att: att,
 | 
				
			||||||
 | 
					            countedAtt: countedAtt,
 | 
				
			||||||
 | 
					            sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
 | 
				
			||||||
 | 
					            icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
 | 
				
			||||||
 | 
					            highPriority: true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.
 | 
				
			|||||||
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
 | 
					import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
 | 
				
			||||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
 | 
					import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
 | 
				
			||||||
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
 | 
					import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
 | 
				
			||||||
import { getInfNodes, getWeaponsForManifest } from "@/src/helpers/nemesisHelpers";
 | 
					import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
 | 
				
			||||||
import { Loadout } from "../models/inventoryModels/loadoutModel";
 | 
					import { Loadout } from "../models/inventoryModels/loadoutModel";
 | 
				
			||||||
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
 | 
					import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
 | 
				
			||||||
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
 | 
					import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
 | 
				
			||||||
@ -639,7 +639,10 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (value.killed) {
 | 
					                    if (value.killed) {
 | 
				
			||||||
                        if (value.weaponLoc) {
 | 
					                        if (
 | 
				
			||||||
 | 
					                            value.weaponLoc &&
 | 
				
			||||||
 | 
					                            inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
                            const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
 | 
					                            const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
 | 
				
			||||||
                                inventory.Nemesis.WeaponIdx
 | 
					                                inventory.Nemesis.WeaponIdx
 | 
				
			||||||
                            ];
 | 
					                            ];
 | 
				
			||||||
@ -657,6 +660,11 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
 | 
				
			||||||
 | 
					                    if (inventory.Nemesis.Faction == "FC_INFESTATION") {
 | 
				
			||||||
 | 
					                        await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    inventory.Nemesis = undefined;
 | 
					                    inventory.Nemesis = undefined;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,10 @@ export const getRandomInt = (min: number, max: number): number => {
 | 
				
			|||||||
    return Math.floor(Math.random() * (max - min + 1)) + min;
 | 
					    return Math.floor(Math.random() * (max - min + 1)) + min;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
 | 
					export const getRewardAtPercentage = <T extends { probability: number }>(
 | 
				
			||||||
 | 
					    pool: T[],
 | 
				
			||||||
 | 
					    percentage: number
 | 
				
			||||||
 | 
					): T | undefined => {
 | 
				
			||||||
    if (pool.length == 0) return;
 | 
					    if (pool.length == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
 | 
					    const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user