merge upstream
This commit is contained in:
commit
b8b201214f
@ -1,4 +1,4 @@
|
|||||||
FROM node:18-alpine3.19
|
FROM node:24-alpine3.21
|
||||||
|
|
||||||
ENV APP_MONGODB_URL=mongodb://mongodb:27017/openWF
|
ENV APP_MONGODB_URL=mongodb://mongodb:27017/openWF
|
||||||
ENV APP_MY_ADDRESS=localhost
|
ENV APP_MY_ADDRESS=localhost
|
||||||
|
@ -33,3 +33,4 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
|
|||||||
- `RadioLegion2Syndicate` for The Emissary
|
- `RadioLegion2Syndicate` for The Emissary
|
||||||
- `RadioLegionIntermissionSyndicate` for Intermission I
|
- `RadioLegionIntermissionSyndicate` for Intermission I
|
||||||
- `RadioLegionSyndicate` for The Wolf of Saturn Six
|
- `RadioLegionSyndicate` for The Wolf of Saturn Six
|
||||||
|
- `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`)
|
||||||
|
@ -58,7 +58,8 @@
|
|||||||
"starDays": true,
|
"starDays": true,
|
||||||
"eidolonOverride": "",
|
"eidolonOverride": "",
|
||||||
"vallisOverride": "",
|
"vallisOverride": "",
|
||||||
"nightwaveOverride": ""
|
"nightwaveOverride": "",
|
||||||
|
"circuitGameModes": null
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"keepVendorsExpired": false
|
"keepVendorsExpired": false
|
||||||
|
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.67",
|
"warframe-public-export-plus": "^0.5.68",
|
||||||
"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"
|
||||||
@ -3814,9 +3814,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/warframe-public-export-plus": {
|
"node_modules/warframe-public-export-plus": {
|
||||||
"version": "0.5.67",
|
"version": "0.5.68",
|
||||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.67.tgz",
|
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.68.tgz",
|
||||||
"integrity": "sha512-LsnZD2E5PTA+5MK9kDGvM/hFDtg8sb0EwQ4hKH5ILqrSgz30a9W8785v77RSsL1AEVF8dfb/lZcSTCJq1DZHzQ=="
|
"integrity": "sha512-KMmwCVeQ4k+EN73UZqxnM+qQdPsST8geWoJCP7US5LT6JcRxa8ptmqYXwCzaLtckBLZyVbamsxKZAxPPJckxsA=="
|
||||||
},
|
},
|
||||||
"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.67",
|
"warframe-public-export-plus": "^0.5.68",
|
||||||
"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"
|
||||||
|
107
src/controllers/api/crewShipFusionController.ts
Normal file
107
src/controllers/api/crewShipFusionController.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { addMiscItems, freeUpSlot, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
|
import { ICrewShipComponentFingerprint, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus";
|
||||||
|
|
||||||
|
export const crewShipFusionController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
const payload = getJSONfromString<ICrewShipFusionRequest>(String(req.body));
|
||||||
|
|
||||||
|
const isWeapon = inventory.CrewShipWeapons.id(payload.PartA.$oid);
|
||||||
|
const itemA = isWeapon ?? inventory.CrewShipWeaponSkins.id(payload.PartA.$oid)!;
|
||||||
|
const category = isWeapon ? "CrewShipWeapons" : "CrewShipWeaponSkins";
|
||||||
|
const salvageCategory = isWeapon ? "CrewShipSalvagedWeapons" : "CrewShipSalvagedWeaponSkins";
|
||||||
|
const itemB = inventory[payload.SourceRecipe ? salvageCategory : category].id(payload.PartB.$oid)!;
|
||||||
|
const tierA = itemA.ItemType.charCodeAt(itemA.ItemType.length - 1) - 65;
|
||||||
|
const tierB = itemB.ItemType.charCodeAt(itemB.ItemType.length - 1) - 65;
|
||||||
|
|
||||||
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
|
|
||||||
|
// Charge partial repair cost if fusing with an identified but unrepaired part
|
||||||
|
if (payload.SourceRecipe) {
|
||||||
|
const recipe = ExportDojoRecipes.research[payload.SourceRecipe];
|
||||||
|
updateCurrency(inventory, Math.round(recipe.price * 0.4), false, inventoryChanges);
|
||||||
|
const miscItemChanges = recipe.ingredients.map(x => ({ ...x, ItemCount: Math.round(x.ItemCount * -0.4) }));
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
inventoryChanges.MiscItems = miscItemChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove inferior item
|
||||||
|
if (payload.SourceRecipe) {
|
||||||
|
inventory[salvageCategory].pull({ _id: payload.PartB.$oid });
|
||||||
|
inventoryChanges.RemovedIdItems = [{ ItemId: payload.PartB }];
|
||||||
|
} else {
|
||||||
|
const inferiorId = tierA < tierB ? payload.PartA : payload.PartB;
|
||||||
|
inventory[category].pull({ _id: inferiorId.$oid });
|
||||||
|
inventoryChanges.RemovedIdItems = [{ ItemId: inferiorId }];
|
||||||
|
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||||
|
inventoryChanges[InventorySlot.RJ_COMPONENT_AND_ARMAMENTS] = { count: -1, platinum: 0, Slots: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade superior item
|
||||||
|
const superiorItem = tierA < tierB ? itemB : itemA;
|
||||||
|
const inferiorItem = tierA < tierB ? itemA : itemB;
|
||||||
|
const fingerprint: ICrewShipComponentFingerprint = JSON.parse(
|
||||||
|
superiorItem.UpgradeFingerprint!
|
||||||
|
) as ICrewShipComponentFingerprint;
|
||||||
|
const inferiorFingerprint: ICrewShipComponentFingerprint = inferiorItem.UpgradeFingerprint
|
||||||
|
? (JSON.parse(inferiorItem.UpgradeFingerprint) as ICrewShipComponentFingerprint)
|
||||||
|
: { compat: "", buffs: [] };
|
||||||
|
if (isWeapon) {
|
||||||
|
for (let i = 0; i != fingerprint.buffs.length; ++i) {
|
||||||
|
const buffA = fingerprint.buffs[i];
|
||||||
|
const buffB = i < inferiorFingerprint.buffs.length ? inferiorFingerprint.buffs[i] : undefined;
|
||||||
|
const fvalA = buffA.Value / 0x3fffffff;
|
||||||
|
const fvalB = (buffB?.Value ?? 0) / 0x3fffffff;
|
||||||
|
const percA = 0.3 + fvalA * (0.6 - 0.3);
|
||||||
|
const percB = 0.3 + fvalB * (0.6 - 0.3);
|
||||||
|
const newPerc = Math.min(0.6, Math.max(percA, percB) * FUSE_MULTIPLIERS[Math.abs(tierA - tierB)]);
|
||||||
|
const newFval = (newPerc - 0.3) / (0.6 - 0.3);
|
||||||
|
buffA.Value = Math.trunc(newFval * 0x3fffffff);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const superiorMeta = ExportCustoms[superiorItem.ItemType].randomisedUpgrades ?? [];
|
||||||
|
const inferiorMeta = ExportCustoms[inferiorItem.ItemType].randomisedUpgrades ?? [];
|
||||||
|
for (let i = 0; i != inferiorFingerprint.buffs.length; ++i) {
|
||||||
|
const buffA = fingerprint.buffs[i];
|
||||||
|
const buffB = inferiorFingerprint.buffs[i];
|
||||||
|
const fvalA = buffA.Value / 0x3fffffff;
|
||||||
|
const fvalB = buffB.Value / 0x3fffffff;
|
||||||
|
const rangeA = superiorMeta[i].range;
|
||||||
|
const rangeB = inferiorMeta[i].range;
|
||||||
|
const percA = rangeA[0] + fvalA * (rangeA[1] - rangeA[0]);
|
||||||
|
const percB = rangeB[0] + fvalB * (rangeB[1] - rangeB[0]);
|
||||||
|
const newPerc = Math.min(rangeA[1], Math.max(percA, percB) * FUSE_MULTIPLIERS[Math.abs(tierA - tierB)]);
|
||||||
|
const newFval = (newPerc - rangeA[0]) / (rangeA[1] - rangeA[0]);
|
||||||
|
buffA.Value = Math.trunc(newFval * 0x3fffffff);
|
||||||
|
}
|
||||||
|
if (inferiorFingerprint.SubroutineIndex) {
|
||||||
|
const useSuperiorSubroutine = tierA < tierB ? !payload.UseSubroutineA : payload.UseSubroutineA;
|
||||||
|
if (!useSuperiorSubroutine) {
|
||||||
|
fingerprint.SubroutineIndex = inferiorFingerprint.SubroutineIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
superiorItem.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
inventoryChanges[category] = [superiorItem.toJSON() as any];
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ICrewShipFusionRequest {
|
||||||
|
PartA: IOid;
|
||||||
|
PartB: IOid;
|
||||||
|
SourceRecipe: string;
|
||||||
|
UseSubroutineA: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FUSE_MULTIPLIERS = [1.1, 1.05, 1.02];
|
@ -11,7 +11,7 @@ import {
|
|||||||
scaleRequiredCount,
|
scaleRequiredCount,
|
||||||
setGuildTechLogState
|
setGuildTechLogState
|
||||||
} from "@/src/services/guildService";
|
} from "@/src/services/guildService";
|
||||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
import { ExportDojoRecipes, ExportRailjackWeapons } from "warframe-public-export-plus";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import {
|
import {
|
||||||
addCrewShipWeaponSkin,
|
addCrewShipWeaponSkin,
|
||||||
@ -442,6 +442,7 @@ const finishComponentRepair = (
|
|||||||
...(category == "CrewShipWeaponSkins"
|
...(category == "CrewShipWeaponSkins"
|
||||||
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
||||||
: addEquipment(inventory, category, salvageItem.ItemType, {
|
: addEquipment(inventory, category, salvageItem.ItemType, {
|
||||||
|
UpgradeType: ExportRailjackWeapons[salvageItem.ItemType].defaultUpgrades?.[0].ItemType,
|
||||||
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||||
})),
|
})),
|
||||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
IKnifeResponse
|
IKnifeResponse
|
||||||
} from "@/src/helpers/nemesisHelpers";
|
} from "@/src/helpers/nemesisHelpers";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||||
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountForRequest } from "@/src/services/loginService";
|
import { getAccountForRequest } from "@/src/services/loginService";
|
||||||
@ -202,16 +203,28 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
guess[body.position].result = correct ? GUESS_CORRECT : GUESS_INCORRECT;
|
guess[body.position].result = correct ? GUESS_CORRECT : GUESS_INCORRECT;
|
||||||
inventory.Nemesis!.GuessHistory[inventory.Nemesis!.GuessHistory.length - 1] = encodeNemesisGuess(guess);
|
inventory.Nemesis!.GuessHistory[inventory.Nemesis!.GuessHistory.length - 1] = encodeNemesisGuess(guess);
|
||||||
|
|
||||||
// Increase rank if incorrect
|
const response: INemesisRequiemResponse = {};
|
||||||
let RankIncrease: number | undefined;
|
if (correct) {
|
||||||
if (!correct) {
|
if (body.position == 2) {
|
||||||
RankIncrease = 1;
|
// That was all 3 guesses correct, nemesis is now weakened.
|
||||||
|
inventory.Nemesis!.InfNodes = [
|
||||||
|
{
|
||||||
|
Node: getNemesisManifest(inventory.Nemesis!.manifest).showdownNode,
|
||||||
|
Influence: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
inventory.Nemesis!.Weakened = true;
|
||||||
|
await consumePasscodeModCharges(inventory, response);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Guess was incorrect, increase rank
|
||||||
|
response.RankIncrease = 1;
|
||||||
const manifest = getNemesisManifest(inventory.Nemesis!.manifest);
|
const manifest = getNemesisManifest(inventory.Nemesis!.manifest);
|
||||||
inventory.Nemesis!.Rank = Math.min(inventory.Nemesis!.Rank + 1, manifest.systemIndexes.length - 1);
|
inventory.Nemesis!.Rank = Math.min(inventory.Nemesis!.Rank + 1, manifest.systemIndexes.length - 1);
|
||||||
inventory.Nemesis!.InfNodes = getInfNodes(manifest, inventory.Nemesis!.Rank);
|
inventory.Nemesis!.InfNodes = getInfNodes(manifest, inventory.Nemesis!.Rank);
|
||||||
}
|
}
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json({ RankIncrease });
|
res.json(response);
|
||||||
}
|
}
|
||||||
} else if ((req.query.mode as string) == "rs") {
|
} else if ((req.query.mode as string) == "rs") {
|
||||||
// report spawn; POST but no application data in body
|
// report spawn; POST but no application data in body
|
||||||
@ -299,20 +312,11 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
];
|
];
|
||||||
inventory.Nemesis!.Weakened = true;
|
inventory.Nemesis!.Weakened = true;
|
||||||
|
|
||||||
const response: IKnifeResponse & { target: INemesisClient } = {
|
const response: INemesisWeakenResponse = {
|
||||||
target: inventory.toJSON<IInventoryClient>().Nemesis!
|
target: inventory.toJSON<IInventoryClient>().Nemesis!
|
||||||
};
|
};
|
||||||
|
|
||||||
// Consume charge of the correct requiem mod(s)
|
await consumePasscodeModCharges(inventory, response);
|
||||||
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
|
|
||||||
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
|
|
||||||
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
|
|
||||||
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
|
|
||||||
const modTypes = getNemesisPasscodeModTypes(inventory.Nemesis!);
|
|
||||||
for (const modType of modTypes) {
|
|
||||||
const upgrade = getKnifeUpgrade(inventory, dataknifeUpgrades, modType);
|
|
||||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
|
||||||
}
|
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json(response);
|
res.json(response);
|
||||||
@ -370,11 +374,19 @@ interface INemesisRequiemRequest {
|
|||||||
knife?: IKnife;
|
knife?: IKnife;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface INemesisRequiemResponse extends IKnifeResponse {
|
||||||
|
RankIncrease?: number;
|
||||||
|
}
|
||||||
|
|
||||||
// interface INemesisWeakenRequest {
|
// interface INemesisWeakenRequest {
|
||||||
// target: INemesisClient;
|
// target: INemesisClient;
|
||||||
// knife: IKnife;
|
// knife: IKnife;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
interface INemesisWeakenResponse extends IKnifeResponse {
|
||||||
|
target: INemesisClient;
|
||||||
|
}
|
||||||
|
|
||||||
interface IKnife {
|
interface IKnife {
|
||||||
Item: IEquipmentClient;
|
Item: IEquipmentClient;
|
||||||
Skins: IWeaponSkinClient[];
|
Skins: IWeaponSkinClient[];
|
||||||
@ -383,3 +395,18 @@ interface IKnife {
|
|||||||
AttachedUpgrades: IUpgradeClient[];
|
AttachedUpgrades: IUpgradeClient[];
|
||||||
HiddenWhenHolstered: boolean;
|
HiddenWhenHolstered: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const consumePasscodeModCharges = async (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
response: IKnifeResponse
|
||||||
|
): Promise<void> => {
|
||||||
|
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
|
||||||
|
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
|
||||||
|
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
|
||||||
|
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
|
||||||
|
const modTypes = getNemesisPasscodeModTypes(inventory.Nemesis!);
|
||||||
|
for (const modType of modTypes) {
|
||||||
|
const upgrade = getKnifeUpgrade(inventory, dataknifeUpgrades, modType);
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -11,8 +11,11 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
|
|||||||
|
|
||||||
const inventory = await getInventory(
|
const inventory = await getInventory(
|
||||||
account._id.toString(),
|
account._id.toString(),
|
||||||
"ChallengeProgress SeasonChallengeHistory Affiliations"
|
"ChallengesFixVersion ChallengeProgress SeasonChallengeHistory Affiliations"
|
||||||
);
|
);
|
||||||
|
if (challenges.ChallengesFixVersion !== undefined) {
|
||||||
|
inventory.ChallengesFixVersion = challenges.ChallengesFixVersion;
|
||||||
|
}
|
||||||
let affiliationMods: IAffiliationMods[] = [];
|
let affiliationMods: IAffiliationMods[] = [];
|
||||||
if (challenges.ChallengeProgress) {
|
if (challenges.ChallengeProgress) {
|
||||||
affiliationMods = addChallenges(
|
affiliationMods = addChallenges(
|
||||||
@ -40,6 +43,7 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IUpdateChallengeProgressRequest {
|
interface IUpdateChallengeProgressRequest {
|
||||||
|
ChallengesFixVersion?: number;
|
||||||
ChallengeProgress?: IChallengeProgress[];
|
ChallengeProgress?: IChallengeProgress[];
|
||||||
SeasonChallengeHistory?: ISeasonChallenge[];
|
SeasonChallengeHistory?: ISeasonChallenge[];
|
||||||
SeasonChallengeCompletions?: ISeasonChallenge[];
|
SeasonChallengeCompletions?: ISeasonChallenge[];
|
||||||
|
@ -1703,7 +1703,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
LastInventorySync: Schema.Types.ObjectId,
|
LastInventorySync: Schema.Types.ObjectId,
|
||||||
Mailbox: MailboxSchema,
|
Mailbox: MailboxSchema,
|
||||||
HandlerPoints: Number,
|
HandlerPoints: Number,
|
||||||
ChallengesFixVersion: { type: Number, default: 6 },
|
ChallengesFixVersion: Number,
|
||||||
PlayedParkourTutorial: Boolean,
|
PlayedParkourTutorial: Boolean,
|
||||||
//ActiveLandscapeTraps: [Schema.Types.Mixed],
|
//ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||||
//RepVotes: [Schema.Types.Mixed],
|
//RepVotes: [Schema.Types.Mixed],
|
||||||
|
@ -33,6 +33,7 @@ import { createAllianceController } from "@/src/controllers/api/createAllianceCo
|
|||||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
||||||
import { creditsController } from "@/src/controllers/api/creditsController";
|
import { creditsController } from "@/src/controllers/api/creditsController";
|
||||||
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
|
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
|
||||||
|
import { crewShipFusionController } from "@/src/controllers/api/crewShipFusionController";
|
||||||
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
|
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
|
||||||
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
|
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
|
||||||
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
|
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
|
||||||
@ -247,6 +248,7 @@ apiRouter.post("/contributeToVault.php", contributeToVaultController);
|
|||||||
apiRouter.post("/createAlliance.php", createAllianceController);
|
apiRouter.post("/createAlliance.php", createAllianceController);
|
||||||
apiRouter.post("/createGuild.php", createGuildController);
|
apiRouter.post("/createGuild.php", createGuildController);
|
||||||
apiRouter.post("/crewMembers.php", crewMembersController);
|
apiRouter.post("/crewMembers.php", crewMembersController);
|
||||||
|
apiRouter.post("/crewShipFusion.php", crewShipFusionController);
|
||||||
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
|
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
|
||||||
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
|
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
|
||||||
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
|
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
|
||||||
|
@ -65,6 +65,7 @@ interface IConfig {
|
|||||||
eidolonOverride?: string;
|
eidolonOverride?: string;
|
||||||
vallisOverride?: string;
|
vallisOverride?: string;
|
||||||
nightwaveOverride?: string;
|
nightwaveOverride?: string;
|
||||||
|
circuitGameModes?: string[];
|
||||||
};
|
};
|
||||||
dev?: {
|
dev?: {
|
||||||
keepVendorsExpired?: boolean;
|
keepVendorsExpired?: boolean;
|
||||||
|
@ -50,14 +50,17 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
|
|||||||
await account.save();
|
await account.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createMessage = async (accountId: string | Types.ObjectId, messages: IMessageCreationTemplate[]) => {
|
export const createMessage = async (
|
||||||
|
accountId: string | Types.ObjectId,
|
||||||
|
messages: IMessageCreationTemplate[]
|
||||||
|
): Promise<HydratedDocument<IMessageDatabase>[]> => {
|
||||||
const ownerIdMessages = messages.map(m => ({
|
const ownerIdMessages = messages.map(m => ({
|
||||||
...m,
|
...m,
|
||||||
ownerId: accountId
|
ownerId: accountId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const savedMessages = await Inbox.insertMany(ownerIdMessages);
|
const savedMessages = await Inbox.insertMany(ownerIdMessages);
|
||||||
return savedMessages;
|
return savedMessages as HydratedDocument<IMessageDatabase>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IMessageCreationTemplate extends Omit<IMessageDatabase, "_id" | "date" | "ownerId"> {
|
export interface IMessageCreationTemplate extends Omit<IMessageDatabase, "_id" | "date" | "ownerId"> {
|
||||||
|
@ -3,7 +3,6 @@ import { isDev } from "@/src/helpers/pathHelper";
|
|||||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { mixSeeds, SRng } from "@/src/services/rngService";
|
import { mixSeeds, SRng } from "@/src/services/rngService";
|
||||||
import { IMongoDate } from "@/src/types/commonTypes";
|
|
||||||
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
|
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { ExportVendors, IRange, IVendor, IVendorOffer } from "warframe-public-export-plus";
|
import { ExportVendors, IRange, IVendor, IVendorOffer } from "warframe-public-export-plus";
|
||||||
@ -25,7 +24,6 @@ import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo
|
|||||||
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
||||||
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
||||||
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
|
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
|
||||||
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
|
|
||||||
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
|
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
|
||||||
|
|
||||||
const rawVendorManifests: IVendorManifest[] = [
|
const rawVendorManifests: IVendorManifest[] = [
|
||||||
@ -46,7 +44,6 @@ const rawVendorManifests: IVendorManifest[] = [
|
|||||||
OstronPetVendorManifest,
|
OstronPetVendorManifest,
|
||||||
SolarisDebtTokenVendorRepossessionsManifest,
|
SolarisDebtTokenVendorRepossessionsManifest,
|
||||||
Temple1999VendorManifest,
|
Temple1999VendorManifest,
|
||||||
TeshinHardModeVendorManifest, // uses preprocessing
|
|
||||||
ZarimanCommisionsManifestArchimedean
|
ZarimanCommisionsManifestArchimedean
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -87,12 +84,16 @@ const gcd = (a: number, b: number): number => {
|
|||||||
const getCycleDuration = (manifest: IVendor): number => {
|
const getCycleDuration = (manifest: IVendor): number => {
|
||||||
let dur = 0;
|
let dur = 0;
|
||||||
for (const item of manifest.items) {
|
for (const item of manifest.items) {
|
||||||
if (typeof item.durationHours != "number") {
|
if (item.alwaysOffered) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const durationHours = item.rotatedWeekly ? 168 : item.durationHours;
|
||||||
|
if (typeof durationHours != "number") {
|
||||||
dur = 1;
|
dur = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (dur != item.durationHours) {
|
if (dur != durationHours) {
|
||||||
dur = gcd(dur, item.durationHours);
|
dur = gcd(dur, durationHours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dur * unixTimesInMs.hour;
|
return dur * unixTimesInMs.hour;
|
||||||
@ -101,7 +102,7 @@ const getCycleDuration = (manifest: IVendor): number => {
|
|||||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
|
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
|
||||||
for (const vendorManifest of rawVendorManifests) {
|
for (const vendorManifest of rawVendorManifests) {
|
||||||
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
||||||
return preprocessVendorManifest(vendorManifest);
|
return vendorManifest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const vendorInfo of generatableVendors) {
|
for (const vendorInfo of generatableVendors) {
|
||||||
@ -124,7 +125,7 @@ export const getVendorManifestByTypeName = (typeName: string): IVendorManifest |
|
|||||||
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
|
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
|
||||||
for (const vendorManifest of rawVendorManifests) {
|
for (const vendorManifest of rawVendorManifests) {
|
||||||
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
||||||
return preprocessVendorManifest(vendorManifest);
|
return vendorManifest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const vendorInfo of generatableVendors) {
|
for (const vendorInfo of generatableVendors) {
|
||||||
@ -183,30 +184,6 @@ export const applyStandingToVendorManifest = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
|
|
||||||
if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
|
|
||||||
const manifest = structuredClone(originalManifest);
|
|
||||||
const info = manifest.VendorInfo;
|
|
||||||
refreshExpiry(info.Expiry);
|
|
||||||
for (const offer of info.ItemManifest) {
|
|
||||||
refreshExpiry(offer.Expiry);
|
|
||||||
}
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
return originalManifest;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshExpiry = (expiry: IMongoDate): void => {
|
|
||||||
const period = parseInt(expiry.$date.$numberLong);
|
|
||||||
if (Date.now() >= period) {
|
|
||||||
const epoch = 1734307200_000; // Monday (for weekly schedules)
|
|
||||||
const iteration = Math.trunc((Date.now() - epoch) / period);
|
|
||||||
const start = epoch + iteration * period;
|
|
||||||
const end = start + period;
|
|
||||||
expiry.$date.$numberLong = end.toString();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toRange = (value: IRange | number): IRange => {
|
const toRange = (value: IRange | number): IRange => {
|
||||||
if (typeof value == "number") {
|
if (typeof value == "number") {
|
||||||
return { minValue: value, maxValue: value };
|
return { minValue: value, maxValue: value };
|
||||||
@ -230,6 +207,18 @@ const getCycleDurationRange = (manifest: IVendor): IRange | undefined => {
|
|||||||
return res.maxValue != 0 ? res : undefined;
|
return res.maxValue != 0 ? res : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TOfferId = string;
|
||||||
|
|
||||||
|
const getOfferId = (offer: IVendorOffer | IItemManifest): TOfferId => {
|
||||||
|
if ("storeItem" in offer) {
|
||||||
|
// IVendorOffer
|
||||||
|
return offer.storeItem + "x" + offer.quantity;
|
||||||
|
} else {
|
||||||
|
// IItemManifest
|
||||||
|
return offer.StoreItem + "x" + offer.QuantityMultiplier;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const vendorManifestCache: Record<string, IVendorManifest> = {};
|
const vendorManifestCache: Record<string, IVendorManifest> = {};
|
||||||
|
|
||||||
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
|
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
|
||||||
@ -270,7 +259,8 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
|
|||||||
const rng = new SRng(mixSeeds(vendorSeed, cycleIndex));
|
const rng = new SRng(mixSeeds(vendorSeed, cycleIndex));
|
||||||
const offersToAdd: IVendorOffer[] = [];
|
const offersToAdd: IVendorOffer[] = [];
|
||||||
if (!manifest.isOneBinPerCycle) {
|
if (!manifest.isOneBinPerCycle) {
|
||||||
const remainingItemCapacity: Record<string, number> = {};
|
// Compute vendor requirements, subtracting existing offers
|
||||||
|
const remainingItemCapacity: Record<TOfferId, number> = {};
|
||||||
const missingItemsPerBin: Record<number, number> = {};
|
const missingItemsPerBin: Record<number, number> = {};
|
||||||
let numOffersThatNeedToMatchABin = 0;
|
let numOffersThatNeedToMatchABin = 0;
|
||||||
if (manifest.numItemsPerBin) {
|
if (manifest.numItemsPerBin) {
|
||||||
@ -280,56 +270,59 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const item of manifest.items) {
|
for (const item of manifest.items) {
|
||||||
remainingItemCapacity[item.storeItem] = 1 + item.duplicates;
|
remainingItemCapacity[getOfferId(item)] = 1 + item.duplicates;
|
||||||
}
|
}
|
||||||
for (const offer of info.ItemManifest) {
|
for (const offer of info.ItemManifest) {
|
||||||
remainingItemCapacity[offer.StoreItem] -= 1;
|
remainingItemCapacity[getOfferId(offer)] -= 1;
|
||||||
const bin = parseInt(offer.Bin.substring(4));
|
const bin = parseInt(offer.Bin.substring(4));
|
||||||
if (missingItemsPerBin[bin]) {
|
if (missingItemsPerBin[bin]) {
|
||||||
missingItemsPerBin[bin] -= 1;
|
missingItemsPerBin[bin] -= 1;
|
||||||
numOffersThatNeedToMatchABin -= 1;
|
numOffersThatNeedToMatchABin -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (manifest.numItems && manifest.items.length != manifest.numItems.minValue) {
|
|
||||||
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
|
// Add permanent offers
|
||||||
|
let numUncountedOffers = 0;
|
||||||
|
let offset = 0;
|
||||||
|
for (const item of manifest.items) {
|
||||||
|
if (item.alwaysOffered || item.rotatedWeekly) {
|
||||||
|
++numUncountedOffers;
|
||||||
|
const id = getOfferId(item);
|
||||||
|
if (remainingItemCapacity[id] != 0) {
|
||||||
|
remainingItemCapacity[id] -= 1;
|
||||||
|
offersToAdd.push(item);
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add counted offers
|
||||||
|
if (manifest.numItems) {
|
||||||
|
const useRng = manifest.numItems.minValue != manifest.numItems.maxValue;
|
||||||
|
const numItemsTarget =
|
||||||
|
numUncountedOffers +
|
||||||
|
(useRng
|
||||||
|
? rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue)
|
||||||
|
: manifest.numItems.minValue);
|
||||||
|
let i = 0;
|
||||||
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
|
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
|
||||||
// TODO: Consider item probability weightings
|
const item = useRng ? rng.randomElement(manifest.items)! : manifest.items[i++];
|
||||||
const item = rng.randomElement(manifest.items)!;
|
|
||||||
if (
|
if (
|
||||||
remainingItemCapacity[item.storeItem] != 0 &&
|
!item.alwaysOffered &&
|
||||||
|
remainingItemCapacity[getOfferId(item)] != 0 &&
|
||||||
(numOffersThatNeedToMatchABin == 0 || missingItemsPerBin[item.bin])
|
(numOffersThatNeedToMatchABin == 0 || missingItemsPerBin[item.bin])
|
||||||
) {
|
) {
|
||||||
remainingItemCapacity[item.storeItem] -= 1;
|
remainingItemCapacity[getOfferId(item)] -= 1;
|
||||||
if (missingItemsPerBin[item.bin]) {
|
if (missingItemsPerBin[item.bin]) {
|
||||||
missingItemsPerBin[item.bin] -= 1;
|
missingItemsPerBin[item.bin] -= 1;
|
||||||
numOffersThatNeedToMatchABin -= 1;
|
numOffersThatNeedToMatchABin -= 1;
|
||||||
}
|
}
|
||||||
offersToAdd.push(item);
|
offersToAdd.splice(offset, 0, item);
|
||||||
|
}
|
||||||
|
if (i == manifest.items.length) {
|
||||||
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (const item of manifest.items) {
|
|
||||||
if (!item.alwaysOffered && remainingItemCapacity[item.storeItem] != 0) {
|
|
||||||
remainingItemCapacity[item.storeItem] -= 1;
|
|
||||||
offersToAdd.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const e of Object.entries(remainingItemCapacity)) {
|
|
||||||
const item = manifest.items.find(x => x.storeItem == e[0])!;
|
|
||||||
if (!item.alwaysOffered) {
|
|
||||||
while (e[1] != 0) {
|
|
||||||
e[1] -= 1;
|
|
||||||
offersToAdd.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const item of manifest.items) {
|
|
||||||
if (item.alwaysOffered && remainingItemCapacity[item.storeItem] != 0) {
|
|
||||||
remainingItemCapacity[item.storeItem] -= 1;
|
|
||||||
offersToAdd.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offersToAdd.reverse();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
const binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
||||||
@ -342,16 +335,21 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
|
|||||||
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
|
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
|
||||||
for (const rawItem of offersToAdd) {
|
for (const rawItem of offersToAdd) {
|
||||||
const durationHoursRange = toRange(rawItem.durationHours ?? cycleDuration);
|
const durationHoursRange = toRange(rawItem.durationHours ?? cycleDuration);
|
||||||
const expiry =
|
const expiry = rawItem.alwaysOffered
|
||||||
cycleStart +
|
? 2051240400_000
|
||||||
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
|
: cycleStart +
|
||||||
|
(rawItem.rotatedWeekly
|
||||||
|
? unixTimesInMs.week
|
||||||
|
: rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour);
|
||||||
const item: IItemManifest = {
|
const item: IItemManifest = {
|
||||||
StoreItem: rawItem.storeItem,
|
StoreItem: rawItem.storeItem,
|
||||||
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||||
Bin: "BIN_" + rawItem.bin,
|
Bin: "BIN_" + rawItem.bin,
|
||||||
QuantityMultiplier: rawItem.quantity,
|
QuantityMultiplier: rawItem.quantity,
|
||||||
Expiry: { $date: { $numberLong: expiry.toString() } },
|
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||||
AllowMultipurchase: false,
|
PurchaseQuantityLimit: rawItem.purchaseLimit,
|
||||||
|
RotatedWeekly: rawItem.rotatedWeekly,
|
||||||
|
AllowMultipurchase: rawItem.purchaseLimit !== 1,
|
||||||
Id: {
|
Id: {
|
||||||
$oid:
|
$oid:
|
||||||
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
||||||
@ -422,6 +420,13 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
|
if (
|
||||||
|
getCycleDuration(ExportVendors["/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest"]) !=
|
||||||
|
unixTimesInMs.week
|
||||||
|
) {
|
||||||
|
logger.warn(`getCycleDuration self test failed`);
|
||||||
|
}
|
||||||
|
|
||||||
const ads = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest")!
|
const ads = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest")!
|
||||||
.VendorInfo.ItemManifest;
|
.VendorInfo.ItemManifest;
|
||||||
if (
|
if (
|
||||||
|
@ -16,8 +16,10 @@ import {
|
|||||||
ISortie,
|
ISortie,
|
||||||
ISortieMission,
|
ISortieMission,
|
||||||
ISyndicateMissionInfo,
|
ISyndicateMissionInfo,
|
||||||
|
ITmp,
|
||||||
IVoidStorm,
|
IVoidStorm,
|
||||||
IWorldState
|
IWorldState,
|
||||||
|
TCircuitGameMode
|
||||||
} from "../types/worldStateTypes";
|
} from "../types/worldStateTypes";
|
||||||
import { version_compare } from "../helpers/inventoryHelpers";
|
import { version_compare } from "../helpers/inventoryHelpers";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
@ -1297,12 +1299,15 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
pushVoidStorms(worldState.VoidStorms, hour);
|
pushVoidStorms(worldState.VoidStorms, hour);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sentient Anomaly cycling every 30 minutes
|
// Sentient Anomaly + Xtra Cheese cycles
|
||||||
const halfHour = Math.trunc(timeMs / (unixTimesInMs.hour / 2));
|
const halfHour = Math.trunc(timeMs / (unixTimesInMs.hour / 2));
|
||||||
const tmp = {
|
const hourInSeconds = 3600;
|
||||||
|
const cheeseInterval = hourInSeconds * 8;
|
||||||
|
const cheeseDuration = hourInSeconds * 2;
|
||||||
|
const cheeseIndex = Math.trunc(timeSecs / cheeseInterval);
|
||||||
|
const tmp: ITmp = {
|
||||||
cavabegin: "1690761600",
|
cavabegin: "1690761600",
|
||||||
PurchasePlatformLockEnabled: true,
|
PurchasePlatformLockEnabled: true,
|
||||||
tcsn: true,
|
|
||||||
pgr: {
|
pgr: {
|
||||||
ts: "1732572900",
|
ts: "1732572900",
|
||||||
en: "CUSTOM DECALS @ ZEVILA",
|
en: "CUSTOM DECALS @ ZEVILA",
|
||||||
@ -1323,8 +1328,16 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
},
|
},
|
||||||
ennnd: true,
|
ennnd: true,
|
||||||
mbrt: true,
|
mbrt: true,
|
||||||
|
fbst: {
|
||||||
|
a: cheeseIndex * cheeseInterval, // This has a bug where the client shows a negative time for "Xtra cheese starts in ..." until it refreshes the world state. This is because we're only providing the new activation as soon as that time/date is reached. However, this is 100% faithful to live.
|
||||||
|
e: cheeseIndex * cheeseInterval + cheeseDuration,
|
||||||
|
n: (cheeseIndex + 1) * cheeseInterval
|
||||||
|
},
|
||||||
sfn: [550, 553, 554, 555][halfHour % 4]
|
sfn: [550, 553, 554, 555][halfHour % 4]
|
||||||
};
|
};
|
||||||
|
if (Array.isArray(config.worldState?.circuitGameModes)) {
|
||||||
|
tmp.edg = config.worldState.circuitGameModes as TCircuitGameMode[];
|
||||||
|
}
|
||||||
worldState.Tmp = JSON.stringify(tmp);
|
worldState.Tmp = JSON.stringify(tmp);
|
||||||
|
|
||||||
return worldState;
|
return worldState;
|
||||||
|
@ -234,7 +234,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
|||||||
HandlerPoints: number;
|
HandlerPoints: number;
|
||||||
MiscItems: IMiscItem[];
|
MiscItems: IMiscItem[];
|
||||||
HasOwnedVoidProjectionsPreviously?: boolean;
|
HasOwnedVoidProjectionsPreviously?: boolean;
|
||||||
ChallengesFixVersion: number;
|
ChallengesFixVersion?: number;
|
||||||
ChallengeProgress: IChallengeProgress[];
|
ChallengeProgress: IChallengeProgress[];
|
||||||
RawUpgrades: IRawUpgrade[];
|
RawUpgrades: IRawUpgrade[];
|
||||||
ReceivedStartingGear: boolean;
|
ReceivedStartingGear: boolean;
|
||||||
|
@ -191,3 +191,48 @@ export interface ICalendarEvent {
|
|||||||
dialogueName?: string;
|
dialogueName?: string;
|
||||||
dialogueConvo?: string;
|
dialogueConvo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TCircuitGameMode =
|
||||||
|
| "Survival"
|
||||||
|
| "VoidFlood"
|
||||||
|
| "Excavation"
|
||||||
|
| "Defense"
|
||||||
|
| "Exterminate"
|
||||||
|
| "Assassination"
|
||||||
|
| "Alchemy";
|
||||||
|
|
||||||
|
export interface ITmp {
|
||||||
|
cavabegin: string;
|
||||||
|
PurchasePlatformLockEnabled: boolean; // Seems unused
|
||||||
|
pgr: IPgr;
|
||||||
|
ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
|
||||||
|
mbrt?: boolean; // Related to mobile app rating request
|
||||||
|
fbst: IFbst;
|
||||||
|
sfn: number;
|
||||||
|
edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPgr {
|
||||||
|
ts: string;
|
||||||
|
en: string;
|
||||||
|
fr: string;
|
||||||
|
it: string;
|
||||||
|
de: string;
|
||||||
|
es: string;
|
||||||
|
pt: string;
|
||||||
|
ru: string;
|
||||||
|
pl: string;
|
||||||
|
uk: string;
|
||||||
|
tr: string;
|
||||||
|
ja: string;
|
||||||
|
zh: string;
|
||||||
|
ko: string;
|
||||||
|
tc: string;
|
||||||
|
th: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFbst {
|
||||||
|
a: number;
|
||||||
|
e: number;
|
||||||
|
n: number;
|
||||||
|
}
|
||||||
|
@ -1,603 +0,0 @@
|
|||||||
{
|
|
||||||
"VendorInfo": {
|
|
||||||
"_id": {
|
|
||||||
"$oid": "63ed01efbdaa38891767bac9"
|
|
||||||
},
|
|
||||||
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest",
|
|
||||||
"ItemManifest": [
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinArmsBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9947"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinBodyBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 25,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9948"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinHeadBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 20,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9949"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinLegsBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 25,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994a"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994b"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994c"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Components/FormaStanceBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 10,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Effects/OrbsEphemera",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 3,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994e"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Effects/TatsuSkullEphemera",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 85,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e994f"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawShotgunRandomMod",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 75,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9950"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Components/UmbraFormaBlueprint",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 150,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9951"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 55,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 50000,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9952"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularPistolRandomMod",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 75,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9953"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 75,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 3,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9954"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularMeleeRandomMod",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 75,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9955"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/EvergreenLoginRewardFusionBundle",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 150,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9956"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawRifleRandomMod",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 75,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9957"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/WeaponRecoilReductionMod",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 35,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9958"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/TeshinBobbleHead",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 35,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e9959"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGaussVED",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995a"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGrendelVED",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995b"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageProteaAction",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995c"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/TeaSet",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageXakuAction",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995e"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/RivenIdentifier",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 20,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "2051240400000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"RotatedWeekly": true,
|
|
||||||
"AllowMultipurchase": false,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e995f"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RandomSyndicateProjectionPack",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "604800000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 25,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e997c"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemCount": 15,
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 10000,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "604800000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 25,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "66fd60b20ba592c4c95e997d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"PropertyTextHash": "0A0F20AFA748FBEE490510DBF5A33A0D",
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "604800000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,8 +3,8 @@ dict = {
|
|||||||
general_inventoryUpdateNote: `Nota: Los cambios realizados aquí se reflejarán en el juego cuando este sincronice el inventario. Usar la navegación debería ser la forma más sencilla de activar esto.`,
|
general_inventoryUpdateNote: `Nota: Los cambios realizados aquí se reflejarán en el juego cuando este sincronice el inventario. Usar la navegación debería ser la forma más sencilla de activar esto.`,
|
||||||
general_addButton: `Agregar`,
|
general_addButton: `Agregar`,
|
||||||
general_bulkActions: `Acciones masivas`,
|
general_bulkActions: `Acciones masivas`,
|
||||||
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
|
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
|
||||||
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
|
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
|
||||||
code_nonValidAuthz: `Tus credenciales no son válidas.`,
|
code_nonValidAuthz: `Tus credenciales no son válidas.`,
|
||||||
code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`,
|
code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`,
|
||||||
code_deleteAccountConfirm: `¿Estás seguro de que deseas eliminar tu cuenta |DISPLAYNAME| (|EMAIL|)? Esta acción es permanente.`,
|
code_deleteAccountConfirm: `¿Estás seguro de que deseas eliminar tu cuenta |DISPLAYNAME| (|EMAIL|)? Esta acción es permanente.`,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user