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