Compare commits
6 Commits
effba86797
...
59b5ab5bac
Author | SHA1 | Date | |
---|---|---|---|
59b5ab5bac | |||
2ce5cc4562 | |||
099f12a197 | |||
bfe2e93c76 | |||
8b97bb4b0a | |||
85a45a04ea |
8
package-lock.json
generated
8
package-lock.json
generated
@ -18,7 +18,7 @@
|
|||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.60",
|
"warframe-public-export-plus": "^0.5.62",
|
||||||
"warframe-riven-info": "^0.1.2",
|
"warframe-riven-info": "^0.1.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0"
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
@ -3703,9 +3703,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/warframe-public-export-plus": {
|
"node_modules/warframe-public-export-plus": {
|
||||||
"version": "0.5.60",
|
"version": "0.5.62",
|
||||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.60.tgz",
|
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.62.tgz",
|
||||||
"integrity": "sha512-vMfytUc4xRi+b7RTSq+TJEl91vwEegpQKxLtXwRPfs9ZHhntxc4rmDYSNWJTvgf/aWXsFUxQlqL/GV5OLPGM7g=="
|
"integrity": "sha512-D8ZzjkU9rrK/59VqCfpMoV31HVmwHZV1dNZxPO85AOlcjg/G81Fu3kgITQTaw9sdNagLPLQnFaiXY58pxxRwgA=="
|
||||||
},
|
},
|
||||||
"node_modules/warframe-riven-info": {
|
"node_modules/warframe-riven-info": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.60",
|
"warframe-public-export-plus": "^0.5.62",
|
||||||
"warframe-riven-info": "^0.1.2",
|
"warframe-riven-info": "^0.1.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0"
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
@ -7,8 +8,15 @@ import { Types } from "mongoose";
|
|||||||
|
|
||||||
export const crewMembersController: RequestHandler = async (req, res) => {
|
export const crewMembersController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const inventory = await getInventory(accountId, "CrewMembers");
|
const inventory = await getInventory(accountId, "CrewMembers NemesisHistory");
|
||||||
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
|
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
|
||||||
|
if (data.crewMember.SecondInCommand) {
|
||||||
|
clearOnCall(inventory);
|
||||||
|
}
|
||||||
|
if (data.crewMember.ItemId.$oid == "000000000000000000000000") {
|
||||||
|
const convertedNemesis = inventory.NemesisHistory!.find(x => x.fp == data.crewMember.NemesisFingerprint)!;
|
||||||
|
convertedNemesis.SecondInCommand = data.crewMember.SecondInCommand;
|
||||||
|
} else {
|
||||||
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
|
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
|
||||||
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
|
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
|
||||||
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
|
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
|
||||||
@ -16,6 +24,7 @@ export const crewMembersController: RequestHandler = async (req, res) => {
|
|||||||
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
|
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
|
||||||
dbCrewMember.Configs = data.crewMember.Configs;
|
dbCrewMember.Configs = data.crewMember.Configs;
|
||||||
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
|
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
|
||||||
|
}
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json({
|
res.json({
|
||||||
crewMemberId: data.crewMember.ItemId.$oid,
|
crewMemberId: data.crewMember.ItemId.$oid,
|
||||||
@ -26,3 +35,20 @@ export const crewMembersController: RequestHandler = async (req, res) => {
|
|||||||
interface ICrewMembersRequest {
|
interface ICrewMembersRequest {
|
||||||
crewMember: ICrewMemberClient;
|
crewMember: ICrewMemberClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearOnCall = (inventory: TInventoryDatabaseDocument): void => {
|
||||||
|
for (const cm of inventory.CrewMembers) {
|
||||||
|
if (cm.SecondInCommand) {
|
||||||
|
cm.SecondInCommand = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inventory.NemesisHistory) {
|
||||||
|
for (const cm of inventory.NemesisHistory) {
|
||||||
|
if (cm.SecondInCommand) {
|
||||||
|
cm.SecondInCommand = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -61,7 +61,11 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
||||||
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
!(
|
||||||
|
missionReport.RewardInfo?.jobId ||
|
||||||
|
missionReport.RewardInfo?.challengeMissionId ||
|
||||||
|
missionReport.RewardInfo?.T
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if (missionReport.EndOfMatchUpload) {
|
if (missionReport.EndOfMatchUpload) {
|
||||||
inventory.RewardSeed = generateRewardSeed();
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
|
||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
import { addEmailItem, getDialogue, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
@ -107,26 +106,3 @@ interface IOtherDialogueInfo {
|
|||||||
Tag: string;
|
Tag: string;
|
||||||
Value: number;
|
Value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
|
||||||
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == dialogueName);
|
|
||||||
if (!dialogue) {
|
|
||||||
dialogue =
|
|
||||||
inventory.DialogueHistory!.Dialogues![
|
|
||||||
inventory.DialogueHistory!.Dialogues!.push({
|
|
||||||
Rank: 0,
|
|
||||||
Chemistry: 0,
|
|
||||||
AvailableDate: new Date(0),
|
|
||||||
AvailableGiftDate: new Date(0),
|
|
||||||
RankUpExpiry: new Date(0),
|
|
||||||
BountyChemExpiry: new Date(0),
|
|
||||||
QueuedDialogues: [],
|
|
||||||
Gifts: [],
|
|
||||||
Booleans: [],
|
|
||||||
Completed: [],
|
|
||||||
DialogueName: dialogueName
|
|
||||||
}) - 1
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return dialogue;
|
|
||||||
};
|
|
||||||
|
@ -2,11 +2,12 @@ import { RequestHandler } from "express";
|
|||||||
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
||||||
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
|
||||||
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
|
||||||
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
|
const body: ISaveLoadoutRequest = getJSONfromString<ISaveLoadoutRequest>(String(req.body));
|
||||||
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
@ -47,6 +47,42 @@ export const showdownNodes: Record<TNemesisFaction, string> = {
|
|||||||
FC_INFESTATION: "CrewBattleNode559"
|
FC_INFESTATION: "CrewBattleNode559"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ephemeraProbabilities: Record<TNemesisFaction, number> = {
|
||||||
|
FC_GRINEER: 0.05,
|
||||||
|
FC_CORPUS: 0.2,
|
||||||
|
FC_INFESTATION: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
@ -271,21 +307,25 @@ export const isNemesisCompatibleWithVersion = (
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInnateDamageTag = (
|
export const getInnateDamageTag = (KillingSuit: string): TInnateDamageTag => {
|
||||||
KillingSuit: string
|
|
||||||
):
|
|
||||||
| "InnateElectricityDamage"
|
|
||||||
| "InnateFreezeDamage"
|
|
||||||
| "InnateHeatDamage"
|
|
||||||
| "InnateImpactDamage"
|
|
||||||
| "InnateMagDamage"
|
|
||||||
| "InnateRadDamage"
|
|
||||||
| "InnateToxinDamage" => {
|
|
||||||
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
|
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
|
export interface INemesisProfile {
|
||||||
export const getInnateDamageValue = (fp: bigint): number => {
|
innateDamageTag: TInnateDamageTag;
|
||||||
|
innateDamageValue: number;
|
||||||
|
ephemera?: string;
|
||||||
|
petHead?: string;
|
||||||
|
petBody?: string;
|
||||||
|
petLegs?: string;
|
||||||
|
petTail?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateNemesisProfile = (
|
||||||
|
fp: bigint = generateRewardSeed(),
|
||||||
|
Faction: TNemesisFaction = "FC_CORPUS",
|
||||||
|
killingSuit: string = "/Lotus/Powersuits/Ember/Ember"
|
||||||
|
): INemesisProfile => {
|
||||||
const rng = new SRng(fp);
|
const rng = new SRng(fp);
|
||||||
rng.randomFloat(); // used for the weapon index
|
rng.randomFloat(); // used for the weapon index
|
||||||
const WeaponUpgradeValueAttenuationExponent = 2.25;
|
const WeaponUpgradeValueAttenuationExponent = 2.25;
|
||||||
@ -293,7 +333,37 @@ export const getInnateDamageValue = (fp: bigint): number => {
|
|||||||
if (value >= 0.941428) {
|
if (value >= 0.941428) {
|
||||||
value = 1;
|
value = 1;
|
||||||
}
|
}
|
||||||
return Math.trunc(value * 0x40000000);
|
const profile: INemesisProfile = {
|
||||||
|
innateDamageTag: getInnateDamageTag(killingSuit),
|
||||||
|
innateDamageValue: Math.trunc(value * 0x40000000) // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
|
||||||
|
};
|
||||||
|
if (rng.randomFloat() <= ephemeraProbabilities[Faction] && Faction != "FC_INFESTATION") {
|
||||||
|
profile.ephemera = ephmeraTypes[Faction][profile.innateDamageTag];
|
||||||
|
}
|
||||||
|
rng.randomFloat(); // something related to sentinel agent maybe
|
||||||
|
if (Faction == "FC_CORPUS") {
|
||||||
|
profile.petHead = rng.randomElement([
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
|
||||||
|
])!;
|
||||||
|
profile.petBody = rng.randomElement([
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
|
||||||
|
])!;
|
||||||
|
profile.petLegs = rng.randomElement([
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
|
||||||
|
])!;
|
||||||
|
profile.petTail = rng.randomElement([
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
|
||||||
|
])!;
|
||||||
|
}
|
||||||
|
return profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getKillTokenRewardCount = (fp: bigint): number => {
|
export const getKillTokenRewardCount = (fp: bigint): number => {
|
||||||
|
@ -1318,7 +1318,7 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
|||||||
InfNodes: { type: [infNodeSchema], default: undefined },
|
InfNodes: { type: [infNodeSchema], default: undefined },
|
||||||
HenchmenKilled: Number,
|
HenchmenKilled: Number,
|
||||||
HintProgress: Number,
|
HintProgress: Number,
|
||||||
Hints: { type: [Number], default: undefined },
|
Hints: { type: [Number], default: [] },
|
||||||
GuessHistory: { type: [Number], default: undefined },
|
GuessHistory: { type: [Number], default: undefined },
|
||||||
MissionCount: Number,
|
MissionCount: Number,
|
||||||
LastEnc: Number
|
LastEnc: Number
|
||||||
|
@ -28,7 +28,8 @@ import {
|
|||||||
ITraits,
|
ITraits,
|
||||||
ICalendarProgress,
|
ICalendarProgress,
|
||||||
INemesisWeaponTargetFingerprint,
|
INemesisWeaponTargetFingerprint,
|
||||||
INemesisPetTargetFingerprint
|
INemesisPetTargetFingerprint,
|
||||||
|
IDialogueDatabase
|
||||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||||
@ -83,7 +84,7 @@ import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from ".
|
|||||||
import { createMessage } from "./inboxService";
|
import { createMessage } from "./inboxService";
|
||||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||||
import { getWorldState } from "./worldStateService";
|
import { getWorldState } from "./worldStateService";
|
||||||
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
|
import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
|
||||||
|
|
||||||
export const createInventory = async (
|
export const createInventory = async (
|
||||||
accountOwnerId: Types.ObjectId,
|
accountOwnerId: Types.ObjectId,
|
||||||
@ -1906,6 +1907,29 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||||
|
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == dialogueName);
|
||||||
|
if (!dialogue) {
|
||||||
|
dialogue =
|
||||||
|
inventory.DialogueHistory!.Dialogues![
|
||||||
|
inventory.DialogueHistory!.Dialogues!.push({
|
||||||
|
Rank: 0,
|
||||||
|
Chemistry: 0,
|
||||||
|
AvailableDate: new Date(0),
|
||||||
|
AvailableGiftDate: new Date(0),
|
||||||
|
RankUpExpiry: new Date(0),
|
||||||
|
BountyChemExpiry: new Date(0),
|
||||||
|
QueuedDialogues: [],
|
||||||
|
Gifts: [],
|
||||||
|
Booleans: [],
|
||||||
|
Completed: [],
|
||||||
|
DialogueName: dialogueName
|
||||||
|
}) - 1
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return dialogue;
|
||||||
|
};
|
||||||
|
|
||||||
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
||||||
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||||
|
|
||||||
@ -1945,8 +1969,7 @@ export const giveNemesisWeaponRecipe = (
|
|||||||
weaponType: string,
|
weaponType: string,
|
||||||
nemesisName: string = "AGOR ROK",
|
nemesisName: string = "AGOR ROK",
|
||||||
weaponLoc?: string,
|
weaponLoc?: string,
|
||||||
KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
|
profile: INemesisProfile = generateNemesisProfile()
|
||||||
fp: bigint = generateRewardSeed()
|
|
||||||
): void => {
|
): void => {
|
||||||
if (!weaponLoc) {
|
if (!weaponLoc) {
|
||||||
weaponLoc = ExportWeapons[weaponType].name;
|
weaponLoc = ExportWeapons[weaponType].name;
|
||||||
@ -1967,8 +1990,8 @@ export const giveNemesisWeaponRecipe = (
|
|||||||
compat: weaponType,
|
compat: weaponType,
|
||||||
buffs: [
|
buffs: [
|
||||||
{
|
{
|
||||||
Tag: getInnateDamageTag(KillingSuit),
|
Tag: profile.innateDamageTag,
|
||||||
Value: getInnateDamageValue(fp)
|
Value: profile.innateDamageValue
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -1977,27 +2000,15 @@ export const giveNemesisWeaponRecipe = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
|
export const giveNemesisPetRecipe = (
|
||||||
const head = getRandomElement([
|
inventory: TInventoryDatabaseDocument,
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
|
nemesisName: string = "AGOR ROK",
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
|
profile: INemesisProfile = generateNemesisProfile()
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
|
): void => {
|
||||||
])!;
|
const head = profile.petHead!;
|
||||||
const body = getRandomElement([
|
const body = profile.petBody!;
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
|
const legs = profile.petLegs!;
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
|
const tail = profile.petTail!;
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
|
|
||||||
])!;
|
|
||||||
const legs = getRandomElement([
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
|
|
||||||
])!;
|
|
||||||
const tail = getRandomElement([
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
|
|
||||||
])!;
|
|
||||||
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
|
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
|
||||||
addRecipes(inventory, [
|
addRecipes(inventory, [
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,7 @@ import {
|
|||||||
combineInventoryChanges,
|
combineInventoryChanges,
|
||||||
generateRewardSeed,
|
generateRewardSeed,
|
||||||
getCalendarProgress,
|
getCalendarProgress,
|
||||||
|
getDialogue,
|
||||||
giveNemesisPetRecipe,
|
giveNemesisPetRecipe,
|
||||||
giveNemesisWeaponRecipe,
|
giveNemesisWeaponRecipe,
|
||||||
updateCurrency,
|
updateCurrency,
|
||||||
@ -55,10 +56,24 @@ import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.
|
|||||||
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
|
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
|
||||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
|
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
|
||||||
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
|
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
|
||||||
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
|
import {
|
||||||
|
generateNemesisProfile,
|
||||||
|
getInfNodes,
|
||||||
|
getNemesisPasscode,
|
||||||
|
getWeaponsForManifest,
|
||||||
|
sendCodaFinishedMessage
|
||||||
|
} from "@/src/helpers/nemesisHelpers";
|
||||||
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
||||||
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
||||||
import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClassicBounties } from "./worldStateService";
|
import {
|
||||||
|
getLiteSortie,
|
||||||
|
getSortie,
|
||||||
|
getWorldState,
|
||||||
|
idToBountyCycle,
|
||||||
|
idToDay,
|
||||||
|
idToWeek,
|
||||||
|
pushClassicBounties
|
||||||
|
} from "./worldStateService";
|
||||||
import { config } from "./configService";
|
import { config } from "./configService";
|
||||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||||
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
|
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
|
||||||
@ -168,6 +183,14 @@ export const addMissionInventoryUpdates = async (
|
|||||||
}
|
}
|
||||||
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
|
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
|
||||||
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress;
|
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress;
|
||||||
|
if (inventory.Nemesis.Faction != "FC_INFESTATION" && inventory.Nemesis.Hints.length != 3) {
|
||||||
|
const progressNeeded = [35, 60, 100][inventory.Nemesis.Hints.length];
|
||||||
|
if (inventory.Nemesis.HintProgress >= progressNeeded) {
|
||||||
|
inventory.Nemesis.HintProgress -= progressNeeded;
|
||||||
|
const passcode = getNemesisPasscode(inventory.Nemesis);
|
||||||
|
inventory.Nemesis.Hints.push(passcode[inventory.Nemesis.Hints.length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
|
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
|
||||||
// e.g. for Profit-Taker Phase 1:
|
// e.g. for Profit-Taker Phase 1:
|
||||||
@ -631,12 +654,18 @@ export const addMissionInventoryUpdates = async (
|
|||||||
Rank: inventory.Nemesis.Rank,
|
Rank: inventory.Nemesis.Rank,
|
||||||
Traded: inventory.Nemesis.Traded,
|
Traded: inventory.Nemesis.Traded,
|
||||||
PrevOwners: inventory.Nemesis.PrevOwners,
|
PrevOwners: inventory.Nemesis.PrevOwners,
|
||||||
SecondInCommand: inventory.Nemesis.SecondInCommand,
|
SecondInCommand: false,
|
||||||
Weakened: inventory.Nemesis.Weakened,
|
Weakened: inventory.Nemesis.Weakened,
|
||||||
// And set killed flag
|
// And set killed flag
|
||||||
k: value.killed
|
k: value.killed
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const profile = generateNemesisProfile(
|
||||||
|
inventory.Nemesis.fp,
|
||||||
|
inventory.Nemesis.Faction,
|
||||||
|
inventory.Nemesis.KillingSuit
|
||||||
|
);
|
||||||
|
|
||||||
if (value.killed) {
|
if (value.killed) {
|
||||||
if (
|
if (
|
||||||
value.weaponLoc &&
|
value.weaponLoc &&
|
||||||
@ -645,20 +674,18 @@ export const addMissionInventoryUpdates = async (
|
|||||||
const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
|
const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
|
||||||
inventory.Nemesis.WeaponIdx
|
inventory.Nemesis.WeaponIdx
|
||||||
];
|
];
|
||||||
giveNemesisWeaponRecipe(
|
giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
|
||||||
inventory,
|
|
||||||
weaponType,
|
|
||||||
value.nemesisName,
|
|
||||||
value.weaponLoc,
|
|
||||||
inventory.Nemesis.KillingSuit,
|
|
||||||
inventory.Nemesis.fp
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (value.petLoc) {
|
if (value.petLoc) {
|
||||||
giveNemesisPetRecipe(inventory);
|
giveNemesisPetRecipe(inventory, value.nemesisName, profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Players will receive a Lich's Ephemera regardless of whether they Vanquish or Convert them."
|
||||||
|
if (profile.ephemera) {
|
||||||
|
addSkin(inventory, profile.ephemera);
|
||||||
|
}
|
||||||
|
|
||||||
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
|
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
|
||||||
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
|
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
|
||||||
await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
|
await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
|
||||||
@ -1175,8 +1202,9 @@ export const addMissionRewards = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rewardInfo.challengeMissionId) {
|
if (rewardInfo.challengeMissionId) {
|
||||||
const [syndicateTag, tierStr] = rewardInfo.challengeMissionId.split("_"); // TODO: third part in HexSyndicate jobs - Chemistry points
|
const [syndicateTag, tierStr, chemistryStr] = rewardInfo.challengeMissionId.split("_");
|
||||||
const tier = Number(tierStr);
|
const tier = Number(tierStr);
|
||||||
|
const chemistry = Number(chemistryStr);
|
||||||
const isSteelPath = missions?.Tier;
|
const isSteelPath = missions?.Tier;
|
||||||
if (syndicateTag === "ZarimanSyndicate") {
|
if (syndicateTag === "ZarimanSyndicate") {
|
||||||
let medallionAmount = tier + 1;
|
let medallionAmount = tier + 1;
|
||||||
@ -1193,6 +1221,23 @@ export const addMissionRewards = async (
|
|||||||
if (isSteelPath) standingAmount *= 1.5;
|
if (isSteelPath) standingAmount *= 1.5;
|
||||||
AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount));
|
AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount));
|
||||||
}
|
}
|
||||||
|
if (syndicateTag == "HexSyndicate" && chemistry && tier < 6) {
|
||||||
|
const seed = getWorldState().SyndicateMissions.find(x => x.Tag == "HexSyndicate")!.Seed;
|
||||||
|
const { nodes, buddies } = getHexBounties(seed);
|
||||||
|
const buddy = buddies[tier];
|
||||||
|
logger.debug(`Hex seed is ${seed}, giving chemistry for ${buddy}`);
|
||||||
|
if (missions?.Tag != nodes[tier]) {
|
||||||
|
logger.warn(
|
||||||
|
`Uh-oh, tier ${tier} bounty should've been on ${nodes[tier]} but you were just on ${missions?.Tag}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const tomorrowAt0Utc = config.noKimCooldowns
|
||||||
|
? Date.now()
|
||||||
|
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
|
||||||
|
const dialogue = getDialogue(inventory, buddy);
|
||||||
|
dialogue.Chemistry += chemistry;
|
||||||
|
dialogue.BountyChemExpiry = new Date(tomorrowAt0Utc);
|
||||||
|
}
|
||||||
if (isSteelPath) {
|
if (isSteelPath) {
|
||||||
await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1);
|
await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1);
|
||||||
MissionRewards.push({
|
MissionRewards.push({
|
||||||
@ -1403,6 +1448,37 @@ function getRandomMissionDrops(
|
|||||||
} else {
|
} else {
|
||||||
rewardManifests = [];
|
rewardManifests = [];
|
||||||
}
|
}
|
||||||
|
} else if (RewardInfo.T == 13) {
|
||||||
|
// Undercroft extra/side portal (normal mode), gives 1 Pathos Clamp + Duviri Arcane.
|
||||||
|
drops.push({
|
||||||
|
StoreItem: "/Lotus/StoreItems/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
|
||||||
|
ItemCount: 1
|
||||||
|
});
|
||||||
|
rewardManifests = [
|
||||||
|
"/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriStaticUndercroftResourceRewards"
|
||||||
|
];
|
||||||
|
} else if (RewardInfo.T == 14) {
|
||||||
|
// Undercroft extra/side portal (steel path), gives 3 Pathos Clamps + Eidolon Arcane.
|
||||||
|
drops.push({
|
||||||
|
StoreItem: "/Lotus/StoreItems/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
|
||||||
|
ItemCount: 3
|
||||||
|
});
|
||||||
|
rewardManifests = [
|
||||||
|
"/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriSteelPathStaticUndercroftResourceRewards"
|
||||||
|
];
|
||||||
|
} else if (RewardInfo.T == 15) {
|
||||||
|
rewardManifests = [
|
||||||
|
mission?.Tier == 1
|
||||||
|
? "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoSteelPathRNGRewards"
|
||||||
|
: "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoNormalRNGRewards"
|
||||||
|
];
|
||||||
|
} else if (RewardInfo.T == 70) {
|
||||||
|
// Orowyrm chest, gives 10 Pathos Clamps, or 15 on Steel Path.
|
||||||
|
drops.push({
|
||||||
|
StoreItem: "/Lotus/StoreItems/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
|
||||||
|
ItemCount: mission?.Tier == 1 ? 15 : 10
|
||||||
|
});
|
||||||
|
rewardManifests = [];
|
||||||
} else {
|
} else {
|
||||||
rewardManifests = region.rewardManifests;
|
rewardManifests = region.rewardManifests;
|
||||||
}
|
}
|
||||||
@ -1721,3 +1797,55 @@ const libraryPersonalTargetToAvatar: Record<string, string> = {
|
|||||||
"/Lotus/Types/Game/Library/Targets/Research10Target":
|
"/Lotus/Types/Game/Library/Targets/Research10Target":
|
||||||
"/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
|
"/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const node_excluded_buddies: Record<string, string> = {
|
||||||
|
SolNode856: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||||
|
SolNode852: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||||
|
SolNode851: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||||
|
SolNode850: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||||
|
SolNode853: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||||
|
SolNode854: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHexBounties = (seed: number): { nodes: string[]; buddies: string[] } => {
|
||||||
|
// We're gonna shuffle these arrays, so they're not truly 'const'.
|
||||||
|
const nodes: string[] = [
|
||||||
|
"SolNode850",
|
||||||
|
"SolNode851",
|
||||||
|
"SolNode852",
|
||||||
|
"SolNode853",
|
||||||
|
"SolNode854",
|
||||||
|
"SolNode856",
|
||||||
|
"SolNode858"
|
||||||
|
];
|
||||||
|
const excludable_nodes: string[] = ["SolNode851", "SolNode852", "SolNode853", "SolNode854"];
|
||||||
|
const buddies: string[] = [
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||||
|
"/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
|
||||||
|
];
|
||||||
|
|
||||||
|
const rng = new SRng(seed);
|
||||||
|
rng.shuffleArray(nodes);
|
||||||
|
rng.shuffleArray(excludable_nodes);
|
||||||
|
while (nodes.length > buddies.length) {
|
||||||
|
nodes.splice(
|
||||||
|
nodes.findIndex(x => x == excludable_nodes[0]),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
excludable_nodes.splice(0, 1);
|
||||||
|
}
|
||||||
|
rng.shuffleArray(buddies);
|
||||||
|
for (let i = 0; i != 6; ++i) {
|
||||||
|
if (buddies[i] == node_excluded_buddies[nodes[i]]) {
|
||||||
|
const swapIdx = (i + 1) % buddies.length;
|
||||||
|
const tmp = buddies[swapIdx];
|
||||||
|
buddies[swapIdx] = buddies[i];
|
||||||
|
buddies[i] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { nodes, buddies };
|
||||||
|
};
|
||||||
|
@ -115,4 +115,19 @@ export class SRng {
|
|||||||
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||||
return getRewardAtPercentage(pool, this.randomFloat());
|
return getRewardAtPercentage(pool, this.randomFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
churnSeed(its: number): void {
|
||||||
|
while (its--) {
|
||||||
|
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffleArray<T>(arr: T[]): void {
|
||||||
|
for (let lastIdx = arr.length - 1; lastIdx >= 1; --lastIdx) {
|
||||||
|
const swapIdx = this.randomInt(0, lastIdx);
|
||||||
|
const tmp = arr[swapIdx];
|
||||||
|
arr[swapIdx] = arr[lastIdx];
|
||||||
|
arr[lastIdx] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,7 @@ export interface IRewardInfo {
|
|||||||
PurgatoryRewardQualifications?: string;
|
PurgatoryRewardQualifications?: string;
|
||||||
rewardSeed?: number | bigint;
|
rewardSeed?: number | bigint;
|
||||||
periodicMissionTag?: string;
|
periodicMissionTag?: string;
|
||||||
|
T?: number; // Duviri
|
||||||
ConquestType?: string;
|
ConquestType?: string;
|
||||||
ConquestCompleted?: number;
|
ConquestCompleted?: number;
|
||||||
ConquestEquipmentSuggestionsFulfilled?: number;
|
ConquestEquipmentSuggestionsFulfilled?: number;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user