forked from OpenWF/SpaceNinjaServer
		
	feat: support all nemesis manifests down to 26.0.0 (#2086)
Also fixes the ephemera chance for kuva lich manifest v2 and up Reviewed-on: OpenWF/SpaceNinjaServer#2086 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
							
								
									ce59086f7d
								
							
						
					
					
						commit
						77a3b64f49
					
				@ -24,7 +24,7 @@ import {
 | 
				
			|||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
 | 
					import { catBreadHash } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers";
 | 
					import { getNemesisManifest } from "@/src/helpers/nemesisHelpers";
 | 
				
			||||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
 | 
					import { getPersonalRooms } from "@/src/services/personalRoomsService";
 | 
				
			||||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
 | 
					import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
 | 
				
			||||||
import { Ship } from "@/src/models/shipModel";
 | 
					import { Ship } from "@/src/models/shipModel";
 | 
				
			||||||
@ -308,7 +308,10 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (buildLabel) {
 | 
					    if (buildLabel) {
 | 
				
			||||||
        // Fix nemesis for older versions
 | 
					        // Fix nemesis for older versions
 | 
				
			||||||
        if (inventoryResponse.Nemesis && !isNemesisCompatibleWithVersion(inventoryResponse.Nemesis, buildLabel)) {
 | 
					        if (
 | 
				
			||||||
 | 
					            inventoryResponse.Nemesis &&
 | 
				
			||||||
 | 
					            version_compare(getNemesisManifest(inventoryResponse.Nemesis.manifest).minBuild, buildLabel) < 0
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
            inventoryResponse.Nemesis = undefined;
 | 
					            inventoryResponse.Nemesis = undefined;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,18 @@
 | 
				
			|||||||
 | 
					import { version_compare } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    consumeModCharge,
 | 
					    consumeModCharge,
 | 
				
			||||||
    encodeNemesisGuess,
 | 
					    encodeNemesisGuess,
 | 
				
			||||||
    getInfNodes,
 | 
					    getInfNodes,
 | 
				
			||||||
    getKnifeUpgrade,
 | 
					    getKnifeUpgrade,
 | 
				
			||||||
 | 
					    getNemesisManifest,
 | 
				
			||||||
    getNemesisPasscode,
 | 
					    getNemesisPasscode,
 | 
				
			||||||
    getNemesisPasscodeModTypes,
 | 
					    getNemesisPasscodeModTypes,
 | 
				
			||||||
    getWeaponsForManifest,
 | 
					    IKnifeResponse
 | 
				
			||||||
    IKnifeResponse,
 | 
					 | 
				
			||||||
    nemesisFactionInfos
 | 
					 | 
				
			||||||
} from "@/src/helpers/nemesisHelpers";
 | 
					} from "@/src/helpers/nemesisHelpers";
 | 
				
			||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
					import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
				
			||||||
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
 | 
					import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { SRng } from "@/src/services/rngService";
 | 
					import { SRng } from "@/src/services/rngService";
 | 
				
			||||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
 | 
					import { IMongoDate, IOid } from "@/src/types/commonTypes";
 | 
				
			||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
					import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
@ -31,10 +31,10 @@ import { logger } from "@/src/utils/logger";
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const nemesisController: RequestHandler = async (req, res) => {
 | 
					export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
    if ((req.query.mode as string) == "f") {
 | 
					    if ((req.query.mode as string) == "f") {
 | 
				
			||||||
        const body = getJSONfromString<IValenceFusionRequest>(String(req.body));
 | 
					        const body = getJSONfromString<IValenceFusionRequest>(String(req.body));
 | 
				
			||||||
        const inventory = await getInventory(accountId, body.Category + " WeaponBin");
 | 
					        const inventory = await getInventory(account._id.toString(), body.Category + " WeaponBin");
 | 
				
			||||||
        const destWeapon = inventory[body.Category].id(body.DestWeapon.$oid)!;
 | 
					        const destWeapon = inventory[body.Category].id(body.DestWeapon.$oid)!;
 | 
				
			||||||
        const sourceWeapon = inventory[body.Category].id(body.SourceWeapon.$oid)!;
 | 
					        const sourceWeapon = inventory[body.Category].id(body.SourceWeapon.$oid)!;
 | 
				
			||||||
        const destFingerprint = JSON.parse(destWeapon.UpgradeFingerprint!) as IInnateDamageFingerprint;
 | 
					        const destFingerprint = JSON.parse(destWeapon.UpgradeFingerprint!) as IInnateDamageFingerprint;
 | 
				
			||||||
@ -69,7 +69,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } else if ((req.query.mode as string) == "p") {
 | 
					    } else if ((req.query.mode as string) == "p") {
 | 
				
			||||||
        const inventory = await getInventory(accountId, "Nemesis");
 | 
					        const inventory = await getInventory(account._id.toString(), "Nemesis");
 | 
				
			||||||
        const body = getJSONfromString<INemesisPrespawnCheckRequest>(String(req.body));
 | 
					        const body = getJSONfromString<INemesisPrespawnCheckRequest>(String(req.body));
 | 
				
			||||||
        const passcode = getNemesisPasscode(inventory.Nemesis!);
 | 
					        const passcode = getNemesisPasscode(inventory.Nemesis!);
 | 
				
			||||||
        let guessResult = 0;
 | 
					        let guessResult = 0;
 | 
				
			||||||
@ -90,7 +90,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        res.json({ GuessResult: guessResult });
 | 
					        res.json({ GuessResult: guessResult });
 | 
				
			||||||
    } else if (req.query.mode == "r") {
 | 
					    } else if (req.query.mode == "r") {
 | 
				
			||||||
        const inventory = await getInventory(
 | 
					        const inventory = await getInventory(
 | 
				
			||||||
            accountId,
 | 
					            account._id.toString(),
 | 
				
			||||||
            "Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
 | 
					            "Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        const body = getJSONfromString<INemesisRequiemRequest>(String(req.body));
 | 
					        const body = getJSONfromString<INemesisRequiemRequest>(String(req.body));
 | 
				
			||||||
@ -144,7 +144,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            if (inventory.Nemesis!.HenchmenKilled >= 100) {
 | 
					            if (inventory.Nemesis!.HenchmenKilled >= 100) {
 | 
				
			||||||
                inventory.Nemesis!.HenchmenKilled = 100;
 | 
					                inventory.Nemesis!.HenchmenKilled = 100;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
 | 
					            inventory.Nemesis!.InfNodes = getInfNodes(getNemesisManifest(inventory.Nemesis!.manifest), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await inventory.save();
 | 
					            await inventory.save();
 | 
				
			||||||
            res.json(response);
 | 
					            res.json(response);
 | 
				
			||||||
@ -154,25 +154,35 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                res.end();
 | 
					                res.end();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                inventory.Nemesis!.Rank += 1;
 | 
					                inventory.Nemesis!.Rank += 1;
 | 
				
			||||||
                inventory.Nemesis!.InfNodes = getInfNodes(inventory.Nemesis!.Faction, inventory.Nemesis!.Rank);
 | 
					                inventory.Nemesis!.InfNodes = getInfNodes(
 | 
				
			||||||
 | 
					                    getNemesisManifest(inventory.Nemesis!.manifest),
 | 
				
			||||||
 | 
					                    inventory.Nemesis!.Rank
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                await inventory.save();
 | 
					                await inventory.save();
 | 
				
			||||||
                res.json({ RankIncrease: 1 });
 | 
					                res.json({ RankIncrease: 1 });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else if ((req.query.mode as string) == "rs") {
 | 
					    } else if ((req.query.mode as string) == "rs") {
 | 
				
			||||||
        // report spawn; POST but no application data in body
 | 
					        // report spawn; POST but no application data in body
 | 
				
			||||||
        const inventory = await getInventory(accountId, "Nemesis");
 | 
					        const inventory = await getInventory(account._id.toString(), "Nemesis");
 | 
				
			||||||
        inventory.Nemesis!.LastEnc = inventory.Nemesis!.MissionCount;
 | 
					        inventory.Nemesis!.LastEnc = inventory.Nemesis!.MissionCount;
 | 
				
			||||||
        await inventory.save();
 | 
					        await inventory.save();
 | 
				
			||||||
        res.json({ LastEnc: inventory.Nemesis!.LastEnc });
 | 
					        res.json({ LastEnc: inventory.Nemesis!.LastEnc });
 | 
				
			||||||
    } else if ((req.query.mode as string) == "s") {
 | 
					    } else if ((req.query.mode as string) == "s") {
 | 
				
			||||||
        const inventory = await getInventory(accountId, "Nemesis");
 | 
					        const inventory = await getInventory(account._id.toString(), "Nemesis");
 | 
				
			||||||
        const body = getJSONfromString<INemesisStartRequest>(String(req.body));
 | 
					        const body = getJSONfromString<INemesisStartRequest>(String(req.body));
 | 
				
			||||||
        body.target.fp = BigInt(body.target.fp);
 | 
					        body.target.fp = BigInt(body.target.fp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const manifest = getNemesisManifest(body.target.manifest);
 | 
				
			||||||
 | 
					        if (account.BuildLabel && version_compare(manifest.minBuild, account.BuildLabel) < 0) {
 | 
				
			||||||
 | 
					            logger.warn(
 | 
				
			||||||
 | 
					                `client on version ${account.BuildLabel} provided nemesis manifest ${body.target.manifest} which was expected to require ${manifest.minBuild} or above. please file a bug report.`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let weaponIdx = -1;
 | 
					        let weaponIdx = -1;
 | 
				
			||||||
        if (body.target.Faction != "FC_INFESTATION") {
 | 
					        if (body.target.Faction != "FC_INFESTATION") {
 | 
				
			||||||
            const weapons = getWeaponsForManifest(body.target.manifest);
 | 
					            const weapons: readonly string[] = manifest.weapons;
 | 
				
			||||||
            const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
 | 
					            const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
 | 
				
			||||||
            weaponIdx = initialWeaponIdx;
 | 
					            weaponIdx = initialWeaponIdx;
 | 
				
			||||||
            do {
 | 
					            do {
 | 
				
			||||||
@ -198,7 +208,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            k: false,
 | 
					            k: false,
 | 
				
			||||||
            Traded: false,
 | 
					            Traded: false,
 | 
				
			||||||
            d: new Date(),
 | 
					            d: new Date(),
 | 
				
			||||||
            InfNodes: getInfNodes(body.target.Faction, 0),
 | 
					            InfNodes: getInfNodes(manifest, 0),
 | 
				
			||||||
            GuessHistory: [],
 | 
					            GuessHistory: [],
 | 
				
			||||||
            Hints: [],
 | 
					            Hints: [],
 | 
				
			||||||
            HintProgress: 0,
 | 
					            HintProgress: 0,
 | 
				
			||||||
@ -216,14 +226,14 @@ export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    } else if ((req.query.mode as string) == "w") {
 | 
					    } else if ((req.query.mode as string) == "w") {
 | 
				
			||||||
        const inventory = await getInventory(
 | 
					        const inventory = await getInventory(
 | 
				
			||||||
            accountId,
 | 
					            account._id.toString(),
 | 
				
			||||||
            "Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
 | 
					            "Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        //const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
 | 
					        //const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        inventory.Nemesis!.InfNodes = [
 | 
					        inventory.Nemesis!.InfNodes = [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Node: nemesisFactionInfos[inventory.Nemesis!.Faction].showdownNode,
 | 
					                Node: getNemesisManifest(inventory.Nemesis!.manifest).showdownNode,
 | 
				
			||||||
                Influence: 1
 | 
					                Influence: 1
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
				
			|||||||
@ -7,51 +7,197 @@ import { IOid } from "../types/commonTypes";
 | 
				
			|||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { addMods, generateRewardSeed } from "../services/inventoryService";
 | 
					import { addMods, generateRewardSeed } from "../services/inventoryService";
 | 
				
			||||||
import { isArchwingMission } from "../services/worldStateService";
 | 
					import { isArchwingMission } from "../services/worldStateService";
 | 
				
			||||||
import { version_compare } from "./inventoryHelpers";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface INemesisFactionInfo {
 | 
					type TInnateDamageTag =
 | 
				
			||||||
    systemIndexes: number[];
 | 
					    | "InnateElectricityDamage"
 | 
				
			||||||
 | 
					    | "InnateHeatDamage"
 | 
				
			||||||
 | 
					    | "InnateFreezeDamage"
 | 
				
			||||||
 | 
					    | "InnateToxinDamage"
 | 
				
			||||||
 | 
					    | "InnateMagDamage"
 | 
				
			||||||
 | 
					    | "InnateRadDamage"
 | 
				
			||||||
 | 
					    | "InnateImpactDamage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface INemesisManifest {
 | 
				
			||||||
 | 
					    weapons: readonly string[];
 | 
				
			||||||
 | 
					    systemIndexes: readonly number[];
 | 
				
			||||||
    showdownNode: string;
 | 
					    showdownNode: string;
 | 
				
			||||||
    ephemeraChance: number;
 | 
					    ephemeraChance: number;
 | 
				
			||||||
 | 
					    ephemeraTypes?: Record<TInnateDamageTag, string>;
 | 
				
			||||||
    firstKillReward: string;
 | 
					    firstKillReward: string;
 | 
				
			||||||
    firstConvertReward: string;
 | 
					    firstConvertReward: string;
 | 
				
			||||||
    messageTitle: string;
 | 
					    messageTitle: string;
 | 
				
			||||||
    messageBody: string;
 | 
					    messageBody: string;
 | 
				
			||||||
 | 
					    minBuild: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const nemesisFactionInfos: Record<TNemesisFaction, INemesisFactionInfo> = {
 | 
					class KuvaLichManifest implements INemesisManifest {
 | 
				
			||||||
    FC_GRINEER: {
 | 
					    weapons = [
 | 
				
			||||||
        systemIndexes: [2, 3, 9, 11, 18],
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
 | 
				
			||||||
        showdownNode: "CrewBattleNode557",
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
 | 
				
			||||||
        ephemeraChance: 0.05,
 | 
					        "/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
 | 
				
			||||||
        firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Clan/LichKillerBadgeItem",
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
 | 
				
			||||||
        firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/KuvaLichSigil",
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
 | 
				
			||||||
        messageTitle: "/Lotus/Language/Inbox/VanquishKuvaMsgTitle",
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
 | 
				
			||||||
        messageBody: "/Lotus/Language/Inbox/VanquishLichMsgBody"
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
 | 
				
			||||||
    },
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
 | 
				
			||||||
    FC_CORPUS: {
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
 | 
				
			||||||
        systemIndexes: [1, 15, 4, 7, 8],
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
 | 
				
			||||||
        showdownNode: "CrewBattleNode558",
 | 
					        "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
 | 
				
			||||||
        ephemeraChance: 0.2,
 | 
					        "/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
 | 
				
			||||||
        firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Clan/CorpusLichBadgeItem",
 | 
					        "/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon"
 | 
				
			||||||
        firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/CorpusLichSigil",
 | 
					    ];
 | 
				
			||||||
        messageTitle: "/Lotus/Language/Inbox/VanquishLawyerMsgTitle",
 | 
					    systemIndexes = [2, 3, 9, 11, 18];
 | 
				
			||||||
        messageBody: "/Lotus/Language/Inbox/VanquishLichMsgBody"
 | 
					    showdownNode = "CrewBattleNode557";
 | 
				
			||||||
    },
 | 
					    ephemeraChance = 0.05;
 | 
				
			||||||
    FC_INFESTATION: {
 | 
					    ephemeraTypes = {
 | 
				
			||||||
        systemIndexes: [23],
 | 
					        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaLightningEphemera",
 | 
				
			||||||
        showdownNode: "CrewBattleNode559",
 | 
					        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaFireEphemera",
 | 
				
			||||||
        ephemeraChance: 0,
 | 
					        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaIceEphemera",
 | 
				
			||||||
        firstKillReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichVanquishedSigil",
 | 
					        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaToxinEphemera",
 | 
				
			||||||
        firstConvertReward: "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichConvertedSigil",
 | 
					        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaMagneticEphemera",
 | 
				
			||||||
        messageTitle: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
 | 
					        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaTricksterEphemera",
 | 
				
			||||||
        messageBody: "/Lotus/Language/Inbox/VanquishBandMsgBody"
 | 
					        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaImpactEphemera"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Clan/LichKillerBadgeItem";
 | 
				
			||||||
 | 
					    firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/KuvaLichSigil";
 | 
				
			||||||
 | 
					    messageTitle = "/Lotus/Language/Inbox/VanquishKuvaMsgTitle";
 | 
				
			||||||
 | 
					    messageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
 | 
				
			||||||
 | 
					    minBuild = "2019.10.31.22.42"; // 26.0.0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KuvaLichManifestVersionTwo extends KuvaLichManifest {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.ephemeraChance = 0.1;
 | 
				
			||||||
 | 
					        this.minBuild = "2020.03.05.16.06"; // Unsure about this one, so using the same value as in version three.
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KuvaLichManifestVersionThree extends KuvaLichManifestVersionTwo {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon");
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind");
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor");
 | 
				
			||||||
 | 
					        this.ephemeraChance = 0.2;
 | 
				
			||||||
 | 
					        this.minBuild = "2020.03.05.16.06"; // This is 27.2.0, tho 27.1.0 should also recognise this.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KuvaLichManifestVersionFour extends KuvaLichManifestVersionThree {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.minBuild = "2021.07.05.17.03"; // Unsure about this one, so using the same value as in version five.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KuvaLichManifestVersionFive extends KuvaLichManifestVersionFour {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon");
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr");
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler");
 | 
				
			||||||
 | 
					        this.minBuild = "2021.07.05.17.03"; // 30.5.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KuvaLichManifestVersionSix extends KuvaLichManifestVersionFive {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek");
 | 
				
			||||||
 | 
					        this.minBuild = "2024.05.15.11.07"; // 35.6.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LawyerManifest implements INemesisManifest {
 | 
				
			||||||
 | 
					    weapons = [
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
 | 
				
			||||||
 | 
					        "/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol"
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    systemIndexes = [1, 15, 4, 7, 8];
 | 
				
			||||||
 | 
					    showdownNode = "CrewBattleNode558";
 | 
				
			||||||
 | 
					    ephemeraChance = 0.2;
 | 
				
			||||||
 | 
					    ephemeraTypes = {
 | 
				
			||||||
 | 
					        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraA",
 | 
				
			||||||
 | 
					        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraB",
 | 
				
			||||||
 | 
					        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraC",
 | 
				
			||||||
 | 
					        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraD",
 | 
				
			||||||
 | 
					        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraE",
 | 
				
			||||||
 | 
					        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraF",
 | 
				
			||||||
 | 
					        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraG"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Clan/CorpusLichBadgeItem";
 | 
				
			||||||
 | 
					    firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/CorpusLichSigil";
 | 
				
			||||||
 | 
					    messageTitle = "/Lotus/Language/Inbox/VanquishLawyerMsgTitle";
 | 
				
			||||||
 | 
					    messageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
 | 
				
			||||||
 | 
					    minBuild = "2021.07.05.17.03"; // 30.5.0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LawyerManifestVersionTwo extends LawyerManifest {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon");
 | 
				
			||||||
 | 
					        this.minBuild = "2022.11.30.08.13"; // 32.2.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LawyerManifestVersionThree extends LawyerManifestVersionTwo {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.weapons.push("/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion");
 | 
				
			||||||
 | 
					        this.minBuild = "2024.05.15.11.07"; // 35.6.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LawyerManifestVersionFour extends LawyerManifestVersionThree {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.minBuild = "2024.10.01.11.03"; // 37.0.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InfestedLichManfest implements INemesisManifest {
 | 
				
			||||||
 | 
					    weapons = [];
 | 
				
			||||||
 | 
					    systemIndexes = [23];
 | 
				
			||||||
 | 
					    showdownNode = "CrewBattleNode559";
 | 
				
			||||||
 | 
					    ephemeraChance = 0;
 | 
				
			||||||
 | 
					    firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichVanquishedSigil";
 | 
				
			||||||
 | 
					    firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichConvertedSigil";
 | 
				
			||||||
 | 
					    messageTitle = "/Lotus/Language/Inbox/VanquishBandMsgTitle";
 | 
				
			||||||
 | 
					    messageBody = "/Lotus/Language/Inbox/VanquishBandMsgBody";
 | 
				
			||||||
 | 
					    minBuild = "2025.03.18.09.51"; // 38.5.0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nemesisManifests: Record<string, INemesisManifest> = {
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifest": new KuvaLichManifest(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionTwo": new KuvaLichManifestVersionTwo(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionThree": new KuvaLichManifestVersionThree(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionFour": new KuvaLichManifestVersionFour(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionFive": new KuvaLichManifestVersionFive(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix": new KuvaLichManifestVersionSix(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifest": new LawyerManifest(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionTwo": new LawyerManifestVersionTwo(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree": new LawyerManifestVersionThree(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour": new LawyerManifestVersionFour(),
 | 
				
			||||||
 | 
					    "/Lotus/Types/Enemies/InfestedLich/InfestedLichManifest": new InfestedLichManfest()
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getInfNodes = (faction: TNemesisFaction, rank: number): IInfNode[] => {
 | 
					export const getNemesisManifest = (manifest: string): INemesisManifest => {
 | 
				
			||||||
 | 
					    if (manifest in nemesisManifests) {
 | 
				
			||||||
 | 
					        return nemesisManifests[manifest];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw new Error(`unknown nemesis manifest: ${manifest}`);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getInfNodes = (manifest: INemesisManifest, rank: number): IInfNode[] => {
 | 
				
			||||||
    const infNodes = [];
 | 
					    const infNodes = [];
 | 
				
			||||||
    const systemIndex = nemesisFactionInfos[faction].systemIndexes[rank];
 | 
					    const systemIndex = manifest.systemIndexes[rank];
 | 
				
			||||||
    for (const [key, value] of Object.entries(ExportRegions)) {
 | 
					    for (const [key, value] of Object.entries(ExportRegions)) {
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            value.systemIndex === systemIndex &&
 | 
					            value.systemIndex === systemIndex &&
 | 
				
			||||||
@ -73,36 +219,6 @@ export const getInfNodes = (faction: TNemesisFaction, rank: number): IInfNode[]
 | 
				
			|||||||
    return infNodes;
 | 
					    return infNodes;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TInnateDamageTag =
 | 
					 | 
				
			||||||
    | "InnateElectricityDamage"
 | 
					 | 
				
			||||||
    | "InnateHeatDamage"
 | 
					 | 
				
			||||||
    | "InnateFreezeDamage"
 | 
					 | 
				
			||||||
    | "InnateToxinDamage"
 | 
					 | 
				
			||||||
    | "InnateMagDamage"
 | 
					 | 
				
			||||||
    | "InnateRadDamage"
 | 
					 | 
				
			||||||
    | "InnateImpactDamage";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ephmeraTypes: Record<"FC_GRINEER" | "FC_CORPUS", Record<TInnateDamageTag, string>> = {
 | 
					 | 
				
			||||||
    FC_GRINEER: {
 | 
					 | 
				
			||||||
        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaLightningEphemera",
 | 
					 | 
				
			||||||
        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaFireEphemera",
 | 
					 | 
				
			||||||
        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaIceEphemera",
 | 
					 | 
				
			||||||
        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaToxinEphemera",
 | 
					 | 
				
			||||||
        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaMagneticEphemera",
 | 
					 | 
				
			||||||
        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaTricksterEphemera",
 | 
					 | 
				
			||||||
        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaImpactEphemera"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    FC_CORPUS: {
 | 
					 | 
				
			||||||
        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraA",
 | 
					 | 
				
			||||||
        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraB",
 | 
					 | 
				
			||||||
        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraC",
 | 
					 | 
				
			||||||
        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraD",
 | 
					 | 
				
			||||||
        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraE",
 | 
					 | 
				
			||||||
        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraF",
 | 
					 | 
				
			||||||
        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraG"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
 | 
					// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
 | 
				
			||||||
export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: TNemesisFaction }): number[] => {
 | 
					export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: TNemesisFaction }): number[] => {
 | 
				
			||||||
    const rng = new SRng(nemesis.fp);
 | 
					    const rng = new SRng(nemesis.fp);
 | 
				
			||||||
@ -256,77 +372,6 @@ export const consumeModCharge = (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const kuvaLichVersionSixWeapons = [
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const corpusVersionThreeWeapons = [
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
 | 
					 | 
				
			||||||
    "/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getWeaponsForManifest = (manifest: string): readonly string[] => {
 | 
					 | 
				
			||||||
    switch (manifest) {
 | 
					 | 
				
			||||||
        case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix": // >= 35.6.0
 | 
					 | 
				
			||||||
            return kuvaLichVersionSixWeapons;
 | 
					 | 
				
			||||||
        case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree": // >= 35.6.0
 | 
					 | 
				
			||||||
        case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour": // >= 37.0.0
 | 
					 | 
				
			||||||
            return corpusVersionThreeWeapons;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    throw new Error(`unknown nemesis manifest: ${manifest}`);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const isNemesisCompatibleWithVersion = (
 | 
					 | 
				
			||||||
    nemesis: { manifest: string; Faction: TNemesisFaction },
 | 
					 | 
				
			||||||
    buildLabel: string
 | 
					 | 
				
			||||||
): boolean => {
 | 
					 | 
				
			||||||
    // Anything below 35.6.0 is not going to be okay given our set of supported manifests.
 | 
					 | 
				
			||||||
    if (version_compare(buildLabel, "2024.05.15.11.07") < 0) {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (nemesis.Faction == "FC_INFESTATION") {
 | 
					 | 
				
			||||||
        // Anything below 38.5.0 isn't gonna like an infested lich.
 | 
					 | 
				
			||||||
        if (version_compare(buildLabel, "2025.03.18.16.07") < 0) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else if (nemesis.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour") {
 | 
					 | 
				
			||||||
        // Anything below 37.0.0 isn't gonna know version 4, but version 3 is identical in terms of weapon choices, so we can spoof it to that.
 | 
					 | 
				
			||||||
        if (version_compare(buildLabel, "2024.10.01.11.03") < 0) {
 | 
					 | 
				
			||||||
            nemesis.manifest = "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getInnateDamageTag = (KillingSuit: string): TInnateDamageTag => {
 | 
					export const getInnateDamageTag = (KillingSuit: string): TInnateDamageTag => {
 | 
				
			||||||
    return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
 | 
					    return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -349,7 +394,7 @@ export interface INemesisProfile {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const generateNemesisProfile = (
 | 
					export const generateNemesisProfile = (
 | 
				
			||||||
    fp: bigint = generateRewardSeed(),
 | 
					    fp: bigint = generateRewardSeed(),
 | 
				
			||||||
    Faction: TNemesisFaction = "FC_CORPUS",
 | 
					    manifest: INemesisManifest = new LawyerManifest(),
 | 
				
			||||||
    killingSuit: string = "/Lotus/Powersuits/Ember/Ember"
 | 
					    killingSuit: string = "/Lotus/Powersuits/Ember/Ember"
 | 
				
			||||||
): INemesisProfile => {
 | 
					): INemesisProfile => {
 | 
				
			||||||
    const rng = new SRng(fp);
 | 
					    const rng = new SRng(fp);
 | 
				
			||||||
@ -363,11 +408,11 @@ export const generateNemesisProfile = (
 | 
				
			|||||||
        innateDamageTag: getInnateDamageTag(killingSuit),
 | 
					        innateDamageTag: getInnateDamageTag(killingSuit),
 | 
				
			||||||
        innateDamageValue: Math.trunc(value * 0x40000000) // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
 | 
					        innateDamageValue: Math.trunc(value * 0x40000000) // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if (rng.randomFloat() <= nemesisFactionInfos[Faction].ephemeraChance && Faction != "FC_INFESTATION") {
 | 
					    if (rng.randomFloat() <= manifest.ephemeraChance && manifest.ephemeraTypes) {
 | 
				
			||||||
        profile.ephemera = ephmeraTypes[Faction][profile.innateDamageTag];
 | 
					        profile.ephemera = manifest.ephemeraTypes[profile.innateDamageTag];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    rng.randomFloat(); // something related to sentinel agent maybe
 | 
					    rng.randomFloat(); // something related to sentinel agent maybe
 | 
				
			||||||
    if (Faction == "FC_CORPUS") {
 | 
					    if (manifest instanceof LawyerManifest) {
 | 
				
			||||||
        profile.petHead = rng.randomElement(petHeads)!;
 | 
					        profile.petHead = rng.randomElement(petHeads)!;
 | 
				
			||||||
        profile.petBody = rng.randomElement([
 | 
					        profile.petBody = rng.randomElement([
 | 
				
			||||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
 | 
					            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
 | 
				
			||||||
 | 
				
			|||||||
@ -61,9 +61,8 @@ import {
 | 
				
			|||||||
    getInfestedLichItemRewards,
 | 
					    getInfestedLichItemRewards,
 | 
				
			||||||
    getInfNodes,
 | 
					    getInfNodes,
 | 
				
			||||||
    getKillTokenRewardCount,
 | 
					    getKillTokenRewardCount,
 | 
				
			||||||
    getNemesisPasscode,
 | 
					    getNemesisManifest,
 | 
				
			||||||
    getWeaponsForManifest,
 | 
					    getNemesisPasscode
 | 
				
			||||||
    nemesisFactionInfos
 | 
					 | 
				
			||||||
} from "@/src/helpers/nemesisHelpers";
 | 
					} 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";
 | 
				
			||||||
@ -662,12 +661,12 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                        k: value.killed
 | 
					                        k: value.killed
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const manifest = getNemesisManifest(inventory.Nemesis.manifest);
 | 
				
			||||||
                    const profile = generateNemesisProfile(
 | 
					                    const profile = generateNemesisProfile(
 | 
				
			||||||
                        inventory.Nemesis.fp,
 | 
					                        inventory.Nemesis.fp,
 | 
				
			||||||
                        inventory.Nemesis.Faction,
 | 
					                        manifest,
 | 
				
			||||||
                        inventory.Nemesis.KillingSuit
 | 
					                        inventory.Nemesis.KillingSuit
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                    const nemesisFactionInfo = nemesisFactionInfos[inventory.Nemesis.Faction];
 | 
					 | 
				
			||||||
                    const att: string[] = [];
 | 
					                    const att: string[] = [];
 | 
				
			||||||
                    let countedAtt: ITypeCount[] | undefined;
 | 
					                    let countedAtt: ITypeCount[] | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -676,9 +675,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                            value.weaponLoc &&
 | 
					                            value.weaponLoc &&
 | 
				
			||||||
                            inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
 | 
					                            inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
 | 
				
			||||||
                        ) {
 | 
					                        ) {
 | 
				
			||||||
                            const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
 | 
					                            const weaponType = manifest.weapons[inventory.Nemesis.WeaponIdx];
 | 
				
			||||||
                                inventory.Nemesis.WeaponIdx
 | 
					 | 
				
			||||||
                            ];
 | 
					 | 
				
			||||||
                            giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
 | 
					                            giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
 | 
				
			||||||
                            att.push(weaponType);
 | 
					                            att.push(weaponType);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@ -704,9 +701,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                        att.push(profile.ephemera);
 | 
					                        att.push(profile.ephemera);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    const skinRewardStoreItem = value.killed
 | 
					                    const skinRewardStoreItem = value.killed ? manifest.firstKillReward : manifest.firstConvertReward;
 | 
				
			||||||
                        ? nemesisFactionInfo.firstKillReward
 | 
					 | 
				
			||||||
                        : nemesisFactionInfo.firstConvertReward;
 | 
					 | 
				
			||||||
                    if (Object.keys(addSkin(inventory, fromStoreItem(skinRewardStoreItem))).length != 0) {
 | 
					                    if (Object.keys(addSkin(inventory, fromStoreItem(skinRewardStoreItem))).length != 0) {
 | 
				
			||||||
                        att.push(skinRewardStoreItem);
 | 
					                        att.push(skinRewardStoreItem);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -737,7 +732,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                        await createMessage(inventory.accountOwnerId, [
 | 
					                        await createMessage(inventory.accountOwnerId, [
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                sndr: "/Lotus/Language/Bosses/Ordis",
 | 
					                                sndr: "/Lotus/Language/Bosses/Ordis",
 | 
				
			||||||
                                msg: nemesisFactionInfo.messageBody,
 | 
					                                msg: manifest.messageBody,
 | 
				
			||||||
                                arg: [
 | 
					                                arg: [
 | 
				
			||||||
                                    {
 | 
					                                    {
 | 
				
			||||||
                                        Key: "LICH_NAME",
 | 
					                                        Key: "LICH_NAME",
 | 
				
			||||||
@ -747,7 +742,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                                att: att,
 | 
					                                att: att,
 | 
				
			||||||
                                countedAtt: countedAtt,
 | 
					                                countedAtt: countedAtt,
 | 
				
			||||||
                                attVisualOnly: true,
 | 
					                                attVisualOnly: true,
 | 
				
			||||||
                                sub: nemesisFactionInfo.messageTitle,
 | 
					                                sub: manifest.messageTitle,
 | 
				
			||||||
                                icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
 | 
					                                icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
 | 
				
			||||||
                                highPriority: true
 | 
					                                highPriority: true
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
@ -1187,7 +1182,10 @@ export const addMissionRewards = async (
 | 
				
			|||||||
                    inventory.Nemesis.Rank = Math.min(inventory.Nemesis.Rank + 1, 4);
 | 
					                    inventory.Nemesis.Rank = Math.min(inventory.Nemesis.Rank + 1, 4);
 | 
				
			||||||
                    inventoryChanges.Nemesis.Rank = inventory.Nemesis.Rank;
 | 
					                    inventoryChanges.Nemesis.Rank = inventory.Nemesis.Rank;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                inventory.Nemesis.InfNodes = getInfNodes(inventory.Nemesis.Faction, inventory.Nemesis.Rank);
 | 
					                inventory.Nemesis.InfNodes = getInfNodes(
 | 
				
			||||||
 | 
					                    getNemesisManifest(inventory.Nemesis.manifest),
 | 
				
			||||||
 | 
					                    inventory.Nemesis.Rank
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (inventory.Nemesis.Faction == "FC_INFESTATION") {
 | 
					            if (inventory.Nemesis.Faction == "FC_INFESTATION") {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user