forked from OpenWF/SpaceNinjaServer
		
	feat: start nemesis (#1227)
Closes #446 As discussed there, some support for 64-bit integers without precision loss had to be hacked in. Reviewed-on: OpenWF/SpaceNinjaServer#1227
This commit is contained in:
		
							parent
							
								
									ae05172ad8
								
							
						
					
					
						commit
						0e1973e246
					
				
							
								
								
									
										7
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -14,6 +14,7 @@
 | 
				
			|||||||
        "copyfiles": "^2.4.1",
 | 
					        "copyfiles": "^2.4.1",
 | 
				
			||||||
        "crc-32": "^1.2.2",
 | 
					        "crc-32": "^1.2.2",
 | 
				
			||||||
        "express": "^5",
 | 
					        "express": "^5",
 | 
				
			||||||
 | 
					        "json-with-bigint": "^3.2.1",
 | 
				
			||||||
        "mongoose": "^8.11.0",
 | 
					        "mongoose": "^8.11.0",
 | 
				
			||||||
        "morgan": "^1.10.0",
 | 
					        "morgan": "^1.10.0",
 | 
				
			||||||
        "typescript": ">=5.5 <5.6.0",
 | 
					        "typescript": ">=5.5 <5.6.0",
 | 
				
			||||||
@ -2346,6 +2347,12 @@
 | 
				
			|||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/json-with-bigint": {
 | 
				
			||||||
 | 
					      "version": "3.2.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.2.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-0f8RHpU1AwBFwIPmtm71W+cFxzlXdiBmzc3JqydsNDSKSAsr0Lso6KXRbz0h2LRwTIRiHAk/UaD+xaAN5f577w==",
 | 
				
			||||||
 | 
					      "license": "MIT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/json5": {
 | 
					    "node_modules/json5": {
 | 
				
			||||||
      "version": "2.2.3",
 | 
					      "version": "2.2.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@
 | 
				
			|||||||
    "copyfiles": "^2.4.1",
 | 
					    "copyfiles": "^2.4.1",
 | 
				
			||||||
    "crc-32": "^1.2.2",
 | 
					    "crc-32": "^1.2.2",
 | 
				
			||||||
    "express": "^5",
 | 
					    "express": "^5",
 | 
				
			||||||
 | 
					    "json-with-bigint": "^3.2.1",
 | 
				
			||||||
    "mongoose": "^8.11.0",
 | 
					    "mongoose": "^8.11.0",
 | 
				
			||||||
    "morgan": "^1.10.0",
 | 
					    "morgan": "^1.10.0",
 | 
				
			||||||
    "typescript": ">=5.5 <5.6.0",
 | 
					    "typescript": ">=5.5 <5.6.0",
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ import { config } from "@/src/services/configService";
 | 
				
			|||||||
import { GuildPermission, ITechProjectClient, ITechProjectDatabase } from "@/src/types/guildTypes";
 | 
					import { GuildPermission, ITechProjectClient, ITechProjectDatabase } from "@/src/types/guildTypes";
 | 
				
			||||||
import { TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
					import { TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
				
			||||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
 | 
					import { toMongoDate } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const guildTechController: RequestHandler = async (req, res) => {
 | 
					export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
@ -219,6 +220,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        await guild.save();
 | 
					        await guild.save();
 | 
				
			||||||
        res.end();
 | 
					        res.end();
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					        logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
 | 
				
			||||||
        throw new Error(`unknown guildTech action: ${data.Action}`);
 | 
					        throw new Error(`unknown guildTech action: ${data.Action}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -355,6 +355,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
 | 
					            logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
 | 
				
			||||||
            throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`);
 | 
					            throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										152
									
								
								src/controllers/api/nemesisController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/controllers/api/nemesisController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { SRng } from "@/src/services/rngService";
 | 
				
			||||||
 | 
					import { IMongoDate } from "@/src/types/commonTypes";
 | 
				
			||||||
 | 
					import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { ExportRegions } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const nemesisController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    if ((req.query.mode as string) == "s") {
 | 
				
			||||||
 | 
					        const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					        const inventory = await getInventory(accountId, "Nemesis NemesisAbandonedRewards");
 | 
				
			||||||
 | 
					        const body = getJSONfromString<INemesisStartRequest>(String(req.body));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const infNodes: IInfNode[] = [];
 | 
				
			||||||
 | 
					        for (const [key, value] of Object.entries(ExportRegions)) {
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                value.systemIndex == 2 && // earth
 | 
				
			||||||
 | 
					                value.nodeType != 3 && // not hub
 | 
				
			||||||
 | 
					                value.nodeType != 7 && // not junction
 | 
				
			||||||
 | 
					                value.missionIndex && // must have a mission type and not assassination
 | 
				
			||||||
 | 
					                value.missionIndex != 28 && // not open world
 | 
				
			||||||
 | 
					                value.missionIndex != 32 && // not railjack
 | 
				
			||||||
 | 
					                value.missionIndex != 41 && // not saya's visions
 | 
				
			||||||
 | 
					                value.name.indexOf("Archwing") == -1
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                //console.log(dict_en[value.name]);
 | 
				
			||||||
 | 
					                infNodes.push({ Node: key, Influence: 1 });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let weapons: readonly string[];
 | 
				
			||||||
 | 
					        if (body.target.manifest == "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix") {
 | 
				
			||||||
 | 
					            weapons = kuvaLichVersionSixWeapons;
 | 
				
			||||||
 | 
					        } else if (
 | 
				
			||||||
 | 
					            body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour" ||
 | 
				
			||||||
 | 
					            body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            weapons = corpusVersionThreeWeapons;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            throw new Error(`unknown nemesis manifest: ${body.target.manifest}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        body.target.fp = BigInt(body.target.fp);
 | 
				
			||||||
 | 
					        const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
 | 
				
			||||||
 | 
					        let weaponIdx = initialWeaponIdx;
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            const weapon = weapons[weaponIdx];
 | 
				
			||||||
 | 
					            if (!body.target.DisallowedWeapons.find(x => x == weapon)) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            weaponIdx = (weaponIdx + 1) % weapons.length;
 | 
				
			||||||
 | 
					        } while (weaponIdx != initialWeaponIdx);
 | 
				
			||||||
 | 
					        inventory.Nemesis = {
 | 
				
			||||||
 | 
					            fp: body.target.fp,
 | 
				
			||||||
 | 
					            manifest: body.target.manifest,
 | 
				
			||||||
 | 
					            KillingSuit: body.target.KillingSuit,
 | 
				
			||||||
 | 
					            killingDamageType: body.target.killingDamageType,
 | 
				
			||||||
 | 
					            ShoulderHelmet: body.target.ShoulderHelmet,
 | 
				
			||||||
 | 
					            WeaponIdx: weaponIdx,
 | 
				
			||||||
 | 
					            AgentIdx: body.target.AgentIdx,
 | 
				
			||||||
 | 
					            BirthNode: body.target.BirthNode,
 | 
				
			||||||
 | 
					            Faction: body.target.Faction,
 | 
				
			||||||
 | 
					            Rank: 0,
 | 
				
			||||||
 | 
					            k: false,
 | 
				
			||||||
 | 
					            Traded: false,
 | 
				
			||||||
 | 
					            d: new Date(),
 | 
				
			||||||
 | 
					            InfNodes: infNodes,
 | 
				
			||||||
 | 
					            GuessHistory: [],
 | 
				
			||||||
 | 
					            Hints: [],
 | 
				
			||||||
 | 
					            HintProgress: 0,
 | 
				
			||||||
 | 
					            Weakened: body.target.Weakened,
 | 
				
			||||||
 | 
					            PrevOwners: 0,
 | 
				
			||||||
 | 
					            HenchmenKilled: 0,
 | 
				
			||||||
 | 
					            SecondInCommand: body.target.SecondInCommand
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        inventory.NemesisAbandonedRewards = []; // unclear if we need to do this since the client also submits this with missionInventoryUpdate
 | 
				
			||||||
 | 
					        await inventory.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res.json({
 | 
				
			||||||
 | 
					            target: inventory.toJSON().Nemesis
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
 | 
				
			||||||
 | 
					        throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface INemesisStartRequest {
 | 
				
			||||||
 | 
					    target: {
 | 
				
			||||||
 | 
					        fp: number | bigint;
 | 
				
			||||||
 | 
					        manifest: string;
 | 
				
			||||||
 | 
					        KillingSuit: string;
 | 
				
			||||||
 | 
					        killingDamageType: number;
 | 
				
			||||||
 | 
					        ShoulderHelmet: string;
 | 
				
			||||||
 | 
					        DisallowedWeapons: string[];
 | 
				
			||||||
 | 
					        WeaponIdx: number;
 | 
				
			||||||
 | 
					        AgentIdx: number;
 | 
				
			||||||
 | 
					        BirthNode: string;
 | 
				
			||||||
 | 
					        Faction: string;
 | 
				
			||||||
 | 
					        Rank: number;
 | 
				
			||||||
 | 
					        k: boolean;
 | 
				
			||||||
 | 
					        Traded: boolean;
 | 
				
			||||||
 | 
					        d: IMongoDate;
 | 
				
			||||||
 | 
					        InfNodes: [];
 | 
				
			||||||
 | 
					        GuessHistory: [];
 | 
				
			||||||
 | 
					        Hints: [];
 | 
				
			||||||
 | 
					        HintProgress: number;
 | 
				
			||||||
 | 
					        Weakened: boolean;
 | 
				
			||||||
 | 
					        PrevOwners: number;
 | 
				
			||||||
 | 
					        HenchmenKilled: number;
 | 
				
			||||||
 | 
					        SecondInCommand: boolean;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
 | 
					import { JSONParse } from "json-with-bigint";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getJSONfromString = <T>(str: string): T => {
 | 
					export const getJSONfromString = <T>(str: string): T => {
 | 
				
			||||||
    const jsonSubstring = str.substring(0, str.lastIndexOf("}") + 1);
 | 
					    const jsonSubstring = str.substring(0, str.lastIndexOf("}") + 1);
 | 
				
			||||||
    return JSON.parse(jsonSubstring) as T;
 | 
					    return JSONParse<T>(jsonSubstring);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getSubstringFromKeyword = (str: string, keyword: string): string => {
 | 
					export const getSubstringFromKeyword = (str: string, keyword: string): string => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/index.ts
									
									
									
									
									
								
							@ -10,6 +10,21 @@ import { config, validateConfig } from "./services/configService";
 | 
				
			|||||||
import { registerLogFileCreationListener } from "@/src/utils/logger";
 | 
					import { registerLogFileCreationListener } from "@/src/utils/logger";
 | 
				
			||||||
import mongoose from "mongoose";
 | 
					import mongoose from "mongoose";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Patch JSON.stringify to work flawlessly with Bigints. Yeah, it's not pretty.
 | 
				
			||||||
 | 
					// TODO: Might wanna use json-with-bigint if/when possible.
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
 | 
				
			||||||
 | 
					    (BigInt.prototype as any).toJSON = function (): string {
 | 
				
			||||||
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | 
				
			||||||
 | 
					        return "<BIGINT>" + this.toString() + "</BIGINT>";
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const og_stringify = JSON.stringify;
 | 
				
			||||||
 | 
					    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
 | 
				
			||||||
 | 
					    (JSON as any).stringify = (obj: any): string => {
 | 
				
			||||||
 | 
					        return og_stringify(obj).split(`"<BIGINT>`).join(``).split(`</BIGINT>"`).join(``);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
registerLogFileCreationListener();
 | 
					registerLogFileCreationListener();
 | 
				
			||||||
validateConfig();
 | 
					validateConfig();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -79,7 +79,10 @@ import {
 | 
				
			|||||||
    ICrewShipWeaponDatabase,
 | 
					    ICrewShipWeaponDatabase,
 | 
				
			||||||
    IRecentVendorPurchaseDatabase,
 | 
					    IRecentVendorPurchaseDatabase,
 | 
				
			||||||
    IVendorPurchaseHistoryEntryDatabase,
 | 
					    IVendorPurchaseHistoryEntryDatabase,
 | 
				
			||||||
    IVendorPurchaseHistoryEntryClient
 | 
					    IVendorPurchaseHistoryEntryClient,
 | 
				
			||||||
 | 
					    INemesisDatabase,
 | 
				
			||||||
 | 
					    INemesisClient,
 | 
				
			||||||
 | 
					    IInfNode
 | 
				
			||||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
					} from "../../types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IOid } from "../../types/commonTypes";
 | 
					import { IOid } from "../../types/commonTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -1058,6 +1061,54 @@ const libraryDailyTaskInfoSchema = new Schema<ILibraryDailyTaskInfo>(
 | 
				
			|||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const infNodeSchema = new Schema<IInfNode>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Node: String,
 | 
				
			||||||
 | 
					        Influence: Number
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nemesisSchema = new Schema<INemesisDatabase>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        fp: BigInt,
 | 
				
			||||||
 | 
					        manifest: String,
 | 
				
			||||||
 | 
					        KillingSuit: String,
 | 
				
			||||||
 | 
					        killingDamageType: Number,
 | 
				
			||||||
 | 
					        ShoulderHelmet: String,
 | 
				
			||||||
 | 
					        WeaponIdx: Number,
 | 
				
			||||||
 | 
					        AgentIdx: Number,
 | 
				
			||||||
 | 
					        BirthNode: String,
 | 
				
			||||||
 | 
					        Faction: String,
 | 
				
			||||||
 | 
					        Rank: Number,
 | 
				
			||||||
 | 
					        k: Boolean,
 | 
				
			||||||
 | 
					        Traded: Boolean,
 | 
				
			||||||
 | 
					        d: Date,
 | 
				
			||||||
 | 
					        PrevOwners: Number,
 | 
				
			||||||
 | 
					        SecondInCommand: Boolean,
 | 
				
			||||||
 | 
					        Weakened: Boolean,
 | 
				
			||||||
 | 
					        InfNodes: [infNodeSchema],
 | 
				
			||||||
 | 
					        HenchmenKilled: Number,
 | 
				
			||||||
 | 
					        HintProgress: Number,
 | 
				
			||||||
 | 
					        Hints: [Number],
 | 
				
			||||||
 | 
					        GuessHistory: [Number]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nemesisSchema.set("toJSON", {
 | 
				
			||||||
 | 
					    virtuals: true,
 | 
				
			||||||
 | 
					    transform(_doc, obj) {
 | 
				
			||||||
 | 
					        const db = obj as INemesisDatabase;
 | 
				
			||||||
 | 
					        const client = obj as INemesisClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.d = toMongoDate(db.d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        delete obj._id;
 | 
				
			||||||
 | 
					        delete obj.__v;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const alignmentSchema = new Schema<IAlignment>(
 | 
					const alignmentSchema = new Schema<IAlignment>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Alignment: Number,
 | 
					        Alignment: Number,
 | 
				
			||||||
@ -1341,7 +1392,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        //CorpusLich or GrineerLich
 | 
					        //CorpusLich or GrineerLich
 | 
				
			||||||
        NemesisAbandonedRewards: { type: [String], default: [] },
 | 
					        NemesisAbandonedRewards: { type: [String], default: [] },
 | 
				
			||||||
        //CorpusLich\KuvaLich
 | 
					        Nemesis: nemesisSchema,
 | 
				
			||||||
        NemesisHistory: [Schema.Types.Mixed],
 | 
					        NemesisHistory: [Schema.Types.Mixed],
 | 
				
			||||||
        LastNemesisAllySpawnTime: Schema.Types.Mixed,
 | 
					        LastNemesisAllySpawnTime: Schema.Types.Mixed,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -69,6 +69,7 @@ import { missionInventoryUpdateController } from "@/src/controllers/api/missionI
 | 
				
			|||||||
import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController";
 | 
					import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController";
 | 
				
			||||||
import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController";
 | 
					import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController";
 | 
				
			||||||
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
 | 
					import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
 | 
				
			||||||
 | 
					import { nemesisController } from "@/src/controllers/api/nemesisController";
 | 
				
			||||||
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
 | 
					import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
 | 
				
			||||||
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
 | 
					import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
 | 
				
			||||||
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
 | 
					import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
 | 
				
			||||||
@ -204,6 +205,7 @@ apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
 | 
				
			|||||||
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
 | 
					apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
 | 
				
			||||||
apiRouter.post("/modularWeaponSale.php", modularWeaponSaleController);
 | 
					apiRouter.post("/modularWeaponSale.php", modularWeaponSaleController);
 | 
				
			||||||
apiRouter.post("/nameWeapon.php", nameWeaponController);
 | 
					apiRouter.post("/nameWeapon.php", nameWeaponController);
 | 
				
			||||||
 | 
					apiRouter.post("/nemesis.php", nemesisController);
 | 
				
			||||||
apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
 | 
					apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
 | 
				
			||||||
apiRouter.post("/playerSkills.php", playerSkillsController);
 | 
					apiRouter.post("/playerSkills.php", playerSkillsController);
 | 
				
			||||||
apiRouter.post("/projectionManager.php", projectionManagerController);
 | 
					apiRouter.post("/projectionManager.php", projectionManagerController);
 | 
				
			||||||
 | 
				
			|||||||
@ -70,6 +70,7 @@ export const getRandomWeightedRewardUc = <T extends { Rarity: TRarity }>(
 | 
				
			|||||||
    return getRandomReward(resultPool);
 | 
					    return getRandomReward(resultPool);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Seeded RNG for internal usage. Based on recommendations in the ISO C standards.
 | 
				
			||||||
export class CRng {
 | 
					export class CRng {
 | 
				
			||||||
    state: number;
 | 
					    state: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,3 +93,21 @@ export class CRng {
 | 
				
			|||||||
        return arr[Math.floor(this.random() * arr.length)];
 | 
					        return arr[Math.floor(this.random() * arr.length)];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Seeded RNG for cases where we need identical results to the game client. Based on work by Donald Knuth.
 | 
				
			||||||
 | 
					export class SRng {
 | 
				
			||||||
 | 
					    state: bigint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(seed: bigint) {
 | 
				
			||||||
 | 
					        this.state = seed;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    randomInt(min: number, max: number): number {
 | 
				
			||||||
 | 
					        const diff = max - min;
 | 
				
			||||||
 | 
					        if (diff != 0) {
 | 
				
			||||||
 | 
					            this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
 | 
				
			||||||
 | 
					            min += (Number(this.state >> 32n) & 0x3fffffff) % (diff + 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return min;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -43,6 +43,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
            | "Drones"
 | 
					            | "Drones"
 | 
				
			||||||
            | "RecentVendorPurchases"
 | 
					            | "RecentVendorPurchases"
 | 
				
			||||||
            | "NextRefill"
 | 
					            | "NextRefill"
 | 
				
			||||||
 | 
					            | "Nemesis"
 | 
				
			||||||
            | TEquipmentKey
 | 
					            | TEquipmentKey
 | 
				
			||||||
        >,
 | 
					        >,
 | 
				
			||||||
        InventoryDatabaseEquipment {
 | 
					        InventoryDatabaseEquipment {
 | 
				
			||||||
@ -71,6 +72,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
    Drones: IDroneDatabase[];
 | 
					    Drones: IDroneDatabase[];
 | 
				
			||||||
    RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
 | 
					    RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
 | 
				
			||||||
    NextRefill?: Date;
 | 
					    NextRefill?: Date;
 | 
				
			||||||
 | 
					    Nemesis?: INemesisDatabase;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IQuestKeyDatabase {
 | 
					export interface IQuestKeyDatabase {
 | 
				
			||||||
@ -288,7 +290,8 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    SeasonChallengeHistory: ISeasonChallenge[];
 | 
					    SeasonChallengeHistory: ISeasonChallenge[];
 | 
				
			||||||
    EquippedInstrument?: string;
 | 
					    EquippedInstrument?: string;
 | 
				
			||||||
    InvasionChainProgress: IInvasionChainProgress[];
 | 
					    InvasionChainProgress: IInvasionChainProgress[];
 | 
				
			||||||
    NemesisHistory: INemesisHistory[];
 | 
					    Nemesis?: INemesisClient;
 | 
				
			||||||
 | 
					    NemesisHistory: INemesisBaseClient[];
 | 
				
			||||||
    LastNemesisAllySpawnTime?: IMongoDate;
 | 
					    LastNemesisAllySpawnTime?: IMongoDate;
 | 
				
			||||||
    Settings: ISettings;
 | 
					    Settings: ISettings;
 | 
				
			||||||
    PersonalTechProjects: IPersonalTechProject[];
 | 
					    PersonalTechProjects: IPersonalTechProject[];
 | 
				
			||||||
@ -782,38 +785,44 @@ export interface IMission extends IMissionDatabase {
 | 
				
			|||||||
    RewardsCooldownTime?: IMongoDate;
 | 
					    RewardsCooldownTime?: IMongoDate;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface INemesisHistory {
 | 
					export interface INemesisBaseClient {
 | 
				
			||||||
    fp: number;
 | 
					    fp: bigint;
 | 
				
			||||||
    manifest: Manifest;
 | 
					    manifest: string;
 | 
				
			||||||
    KillingSuit: string;
 | 
					    KillingSuit: string;
 | 
				
			||||||
    killingDamageType: number;
 | 
					    killingDamageType: number;
 | 
				
			||||||
    ShoulderHelmet: string;
 | 
					    ShoulderHelmet: string;
 | 
				
			||||||
 | 
					    WeaponIdx: number;
 | 
				
			||||||
    AgentIdx: number;
 | 
					    AgentIdx: number;
 | 
				
			||||||
    BirthNode: BirthNode;
 | 
					    BirthNode: string;
 | 
				
			||||||
 | 
					    Faction: string;
 | 
				
			||||||
    Rank: number;
 | 
					    Rank: number;
 | 
				
			||||||
    k: boolean;
 | 
					    k: boolean;
 | 
				
			||||||
 | 
					    Traded: boolean;
 | 
				
			||||||
    d: IMongoDate;
 | 
					    d: IMongoDate;
 | 
				
			||||||
    GuessHistory?: number[];
 | 
					    PrevOwners: number;
 | 
				
			||||||
    currentGuess?: number;
 | 
					    SecondInCommand: boolean;
 | 
				
			||||||
    Traded?: boolean;
 | 
					    Weakened: boolean;
 | 
				
			||||||
    PrevOwners?: number;
 | 
					 | 
				
			||||||
    SecondInCommand?: boolean;
 | 
					 | 
				
			||||||
    Faction?: string;
 | 
					 | 
				
			||||||
    Weakened?: boolean;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum BirthNode {
 | 
					export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "d"> {
 | 
				
			||||||
    SolNode181 = "SolNode181",
 | 
					    d: Date;
 | 
				
			||||||
    SolNode4 = "SolNode4",
 | 
					 | 
				
			||||||
    SolNode70 = "SolNode70",
 | 
					 | 
				
			||||||
    SolNode76 = "SolNode76"
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum Manifest {
 | 
					export interface INemesisClient extends INemesisBaseClient {
 | 
				
			||||||
    LotusTypesEnemiesCorpusLawyersLawyerManifest = "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifest",
 | 
					    InfNodes: IInfNode[];
 | 
				
			||||||
    LotusTypesGameNemesisKuvaLichKuvaLichManifest = "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifest",
 | 
					    HenchmenKilled: number;
 | 
				
			||||||
    LotusTypesGameNemesisKuvaLichKuvaLichManifestVersionThree = "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionThree",
 | 
					    HintProgress: number;
 | 
				
			||||||
    LotusTypesGameNemesisKuvaLichKuvaLichManifestVersionTwo = "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionTwo"
 | 
					    Hints: number[];
 | 
				
			||||||
 | 
					    GuessHistory: number[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface INemesisDatabase extends Omit<INemesisClient, "d"> {
 | 
				
			||||||
 | 
					    d: Date;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IInfNode {
 | 
				
			||||||
 | 
					    Node: string;
 | 
				
			||||||
 | 
					    Influence: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPendingCouponDatabase {
 | 
					export interface IPendingCouponDatabase {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user