Compare commits
1 Commits
main
...
syndicate-
Author | SHA1 | Date | |
---|---|---|---|
8378797c36 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,4 +1,4 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto eol=lf
|
||||
* text=auto
|
||||
|
||||
static/webui/libs/ linguist-vendored
|
||||
|
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -15,12 +15,11 @@ jobs:
|
||||
- run: npm run verify
|
||||
- run: npm run lint:ci
|
||||
- run: npm run prettier
|
||||
- run: npm run update-translations
|
||||
- name: Fail if there are uncommitted changes
|
||||
run: |
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
echo "Uncommitted changes detected:"
|
||||
git status
|
||||
git --no-pager diff
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
@ -10,8 +10,6 @@ To get an idea of what functionality you can expect to be missing [have a look t
|
||||
|
||||
## config.json
|
||||
|
||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config.json.example](config.json.example), which has most cheats disabled.
|
||||
|
||||
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
|
||||
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
|
||||
- `worldState.lockTime` will lock the time provided in worldState if nonzero, e.g. `1743202800` for night in POE.
|
||||
|
@ -1,6 +1,7 @@
|
||||
@echo off
|
||||
|
||||
echo Updating SpaceNinjaServer...
|
||||
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
|
||||
git fetch --prune
|
||||
git stash
|
||||
git reset --hard origin/main
|
||||
|
@ -19,7 +19,6 @@
|
||||
"infiniteEndo": false,
|
||||
"infiniteRegalAya": false,
|
||||
"infiniteHelminthMaterials": false,
|
||||
"dontSubtractConsumables": false,
|
||||
"unlockAllShipFeatures": false,
|
||||
"unlockAllShipDecorations": false,
|
||||
"unlockAllFlavourItems": false,
|
||||
@ -30,17 +29,11 @@
|
||||
"unlockExilusEverywhere": false,
|
||||
"unlockArcanesEverywhere": false,
|
||||
"noDailyStandingLimits": false,
|
||||
"noDailyFocusLimit": false,
|
||||
"noArgonCrystalDecay": false,
|
||||
"noMasteryRankUpCooldown": false,
|
||||
"noVendorPurchaseLimits": true,
|
||||
"noDeathMarks": false,
|
||||
"noKimCooldowns": false,
|
||||
"instantResourceExtractorDrones": false,
|
||||
"noResourceExtractorDronesDamage": false,
|
||||
"skipClanKeyCrafting": false,
|
||||
"noDojoRoomBuildStage": false,
|
||||
"noDecoBuildStage": false,
|
||||
"fastDojoRoomDestruction": false,
|
||||
"noDojoResearchCosts": false,
|
||||
"noDojoResearchTime": false,
|
||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -17,8 +17,8 @@
|
||||
"mongoose": "^8.11.0",
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"warframe-public-export-plus": "^0.5.59",
|
||||
"typescript": ">=5.5 <5.6.0",
|
||||
"warframe-public-export-plus": "^0.5.52",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
@ -3720,9 +3720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -3789,9 +3789,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/warframe-public-export-plus": {
|
||||
"version": "0.5.59",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.59.tgz",
|
||||
"integrity": "sha512-/SUCVjngVDBz6gahz7CdVLywtHLODL6O5nmNtQcxFDUwrUGnF1lETcG8/UO+WLeGxBVAy4BDPbq+9ZWlYZM4uQ=="
|
||||
"version": "0.5.52",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.52.tgz",
|
||||
"integrity": "sha512-mJyQbTFMDwgBSkhUYJzcfJg9qrMTrL1pyZuAxV/Dov68xUikK5zigQSYM3ZkKYbhwBtg0Bx/+7q9GAmPzGaRhA=="
|
||||
},
|
||||
"node_modules/warframe-riven-info": {
|
||||
"version": "0.1.2",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"description": "WF Emulator",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
|
||||
"start": "node --import ./build/src/pathman.js build/src/index.js",
|
||||
"dev": "ts-node-dev --openssl-legacy-provider -r tsconfig-paths/register src/index.ts ",
|
||||
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
|
||||
"verify": "tsgo --noEmit",
|
||||
@ -24,8 +24,8 @@
|
||||
"mongoose": "^8.11.0",
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"warframe-public-export-plus": "^0.5.59",
|
||||
"typescript": ">=5.5 <5.6.0",
|
||||
"warframe-public-export-plus": "^0.5.52",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
|
@ -4,7 +4,7 @@
|
||||
const fs = require("fs");
|
||||
|
||||
function extractStrings(content) {
|
||||
const regex = /([a-zA-Z0-9_]+): `([^`]*)`,/g;
|
||||
const regex = /([a-zA-Z_]+): `([^`]*)`,/g;
|
||||
let matches;
|
||||
const strings = {};
|
||||
while ((matches = regex.exec(content)) !== null) {
|
||||
@ -15,7 +15,7 @@ function extractStrings(content) {
|
||||
|
||||
const source = fs.readFileSync("../static/webui/translations/en.js", "utf8");
|
||||
const sourceStrings = extractStrings(source);
|
||||
const sourceLines = source.substring(0, source.length - 1).split("\n");
|
||||
const sourceLines = source.split("\n");
|
||||
|
||||
fs.readdirSync("../static/webui/translations").forEach(file => {
|
||||
if (fs.lstatSync(`../static/webui/translations/${file}`).isFile() && file !== "en.js") {
|
||||
@ -36,7 +36,7 @@ fs.readdirSync("../static/webui/translations").forEach(file => {
|
||||
fs.writeSync(fileHandle, ` ${key}: \`[UNTRANSLATED] ${value}\`,\n`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
} else if (line.length) {
|
||||
fs.writeSync(fileHandle, line + "\n");
|
||||
}
|
||||
});
|
||||
|
@ -16,9 +16,9 @@ import { webuiRouter } from "@/src/routes/webui";
|
||||
const app = express();
|
||||
|
||||
app.use((req, _res, next) => {
|
||||
// 38.5.0 introduced "ezip" for encrypted body blobs and "e" for request verification only (encrypted body blobs with no application data).
|
||||
// 38.5.0 introduced "ezip" for encrypted body blobs.
|
||||
// The bootstrapper decrypts it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
|
||||
if (req.headers["content-encoding"] == "ezip" || req.headers["content-encoding"] == "e") {
|
||||
if (req.headers["content-encoding"] == "ezip") {
|
||||
req.headers["content-encoding"] = undefined;
|
||||
}
|
||||
next();
|
||||
@ -26,7 +26,7 @@ app.use((req, _res, next) => {
|
||||
|
||||
app.use(bodyParser.raw());
|
||||
app.use(express.json({ limit: "4mb" }));
|
||||
app.use(bodyParser.text({ limit: "4mb" }));
|
||||
app.use(bodyParser.text());
|
||||
app.use(requestLogger);
|
||||
|
||||
app.use("/api", apiRouter);
|
||||
|
@ -2,18 +2,15 @@ const millisecondsPerSecond = 1000;
|
||||
const secondsPerMinute = 60;
|
||||
const minutesPerHour = 60;
|
||||
const hoursPerDay = 24;
|
||||
const daysPerWeek = 7;
|
||||
|
||||
const unixSecond = millisecondsPerSecond;
|
||||
const unixMinute = secondsPerMinute * millisecondsPerSecond;
|
||||
const unixHour = unixMinute * minutesPerHour;
|
||||
const unixDay = hoursPerDay * unixHour;
|
||||
const unixWeek = daysPerWeek * unixDay;
|
||||
|
||||
export const unixTimesInMs = {
|
||||
second: unixSecond,
|
||||
minute: unixMinute,
|
||||
hour: unixHour,
|
||||
day: unixDay,
|
||||
week: unixWeek
|
||||
day: unixDay
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType])!;
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]);
|
||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||
const upgradeIndex =
|
||||
inventory.Upgrades.push({
|
||||
|
@ -1,30 +0,0 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Account, Ignore } from "@/src/models/loginModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const addIgnoredUserController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const data = getJSONfromString<IAddIgnoredUserRequest>(String(req.body));
|
||||
const ignoreeAccount = await Account.findOne(
|
||||
{ DisplayName: data.playerName.substring(0, data.playerName.length - 1) },
|
||||
"_id"
|
||||
);
|
||||
if (ignoreeAccount) {
|
||||
await Ignore.create({ ignorer: accountId, ignoree: ignoreeAccount._id });
|
||||
res.json({
|
||||
Ignored: {
|
||||
_id: toOid(ignoreeAccount._id),
|
||||
DisplayName: data.playerName
|
||||
} satisfies IFriendInfo
|
||||
});
|
||||
} else {
|
||||
res.status(400).end();
|
||||
}
|
||||
};
|
||||
|
||||
interface IAddIgnoredUserRequest {
|
||||
playerName: string;
|
||||
}
|
@ -28,7 +28,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
});
|
||||
|
||||
const rawRivenType = getRandomRawRivenType();
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType])!;
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType]);
|
||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||
|
||||
const upgradeIndex =
|
||||
@ -57,16 +57,12 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
payload.Consumed.forEach(upgrade => {
|
||||
const meta = ExportUpgrades[upgrade.ItemType];
|
||||
counts[meta.rarity] += upgrade.ItemCount;
|
||||
if (upgrade.ItemId.$oid != "000000000000000000000000") {
|
||||
inventory.Upgrades.pull({ _id: upgrade.ItemId.$oid });
|
||||
} else {
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: upgrade.ItemType,
|
||||
ItemCount: upgrade.ItemCount * -1
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/AttackTransmuteCore") {
|
||||
forcedPolarity = "AP_ATTACK";
|
||||
} else if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/DefenseTransmuteCore") {
|
||||
@ -76,15 +72,6 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
}
|
||||
});
|
||||
|
||||
let newModType: string | undefined;
|
||||
for (const specialModSet of specialModSets) {
|
||||
if (specialModSet.indexOf(payload.Consumed[0].ItemType) != -1) {
|
||||
newModType = getRandomElement(specialModSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!newModType) {
|
||||
// Based on the table on https://wiki.warframe.com/w/Transmutation
|
||||
const weights: Record<TRarity, number> = {
|
||||
COMMON: counts.COMMON * 95 + counts.UNCOMMON * 15 + counts.RARE * 4,
|
||||
@ -100,9 +87,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
}
|
||||
});
|
||||
|
||||
newModType = getRandomWeightedReward(options, weights)!.uniqueName;
|
||||
}
|
||||
|
||||
const newModType = getRandomWeightedReward(options, weights)!.uniqueName;
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: newModType,
|
||||
@ -145,34 +130,3 @@ interface IAgnosticUpgradeClient {
|
||||
ItemCount: number;
|
||||
LastAdded: IOid;
|
||||
}
|
||||
|
||||
const specialModSets: string[][] = [
|
||||
[
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalFourMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalFiveMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalWildcardMod"
|
||||
],
|
||||
[
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusFourMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusFiveMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusSixMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusSevenMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
|
||||
],
|
||||
[
|
||||
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod",
|
||||
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod",
|
||||
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod",
|
||||
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod",
|
||||
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod"
|
||||
]
|
||||
];
|
||||
|
@ -15,12 +15,6 @@ export const changeDojoRootController: RequestHandler = async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Example POST body: {"pivot":[0, 0, -64],"components":"{\"670429301ca0a63848ccc467\":{\"R\":[0,0,0],\"P\":[0,3,32]},\"6704254a1ca0a63848ccb33c\":{\"R\":[0,0,0],\"P\":[0,9.25,-32]},\"670429461ca0a63848ccc731\":{\"R\":[-90,0,0],\"P\":[-47.999992370605,3,16]}}"}
|
||||
if (req.body) {
|
||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||
throw new Error("dojo reparent operation should not need deco repositioning"); // because we always provide SortId
|
||||
}
|
||||
|
||||
const idToNode: Record<string, INode> = {};
|
||||
guild.DojoComponents.forEach(x => {
|
||||
idToNode[x._id.toString()] = {
|
||||
@ -49,13 +43,23 @@ export const changeDojoRootController: RequestHandler = async (req, res) => {
|
||||
newRoot.component.pp = undefined;
|
||||
newRoot.parent = undefined;
|
||||
|
||||
// Set/update SortId in top-to-bottom order
|
||||
// Don't even ask me why this is needed because I don't know either
|
||||
const stack: INode[] = [newRoot];
|
||||
let i = 0;
|
||||
const idMap: Record<string, Types.ObjectId> = {};
|
||||
while (stack.length != 0) {
|
||||
const top = stack.shift()!;
|
||||
top.component.SortId = new Types.ObjectId();
|
||||
idMap[top.component._id.toString()] = new Types.ObjectId(
|
||||
(++i).toString(16).padStart(8, "0") + top.component._id.toString().substr(8)
|
||||
);
|
||||
top.children.forEach(x => stack.push(x));
|
||||
}
|
||||
guild.DojoComponents.forEach(x => {
|
||||
x._id = idMap[x._id.toString()];
|
||||
if (x.pi) {
|
||||
x.pi = idMap[x.pi.toString()];
|
||||
}
|
||||
});
|
||||
|
||||
logger.debug("New tree:\n" + treeToString(newRoot));
|
||||
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
interface IClaimCompletedRecipeRequest {
|
||||
RecipeIds: IOid[];
|
||||
@ -81,7 +80,6 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
} else {
|
||||
logger.debug("Claiming Recipe", { recipe, pendingRecipe });
|
||||
|
||||
let BrandedSuits: undefined | IOid[];
|
||||
if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
|
||||
inventory.PendingSpectreLoadouts ??= [];
|
||||
inventory.SpectreLoadouts ??= [];
|
||||
@ -106,10 +104,9 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
|
||||
1
|
||||
);
|
||||
BrandedSuits = [toOid(pendingRecipe.SuitToUnbrand!)];
|
||||
}
|
||||
|
||||
let InventoryChanges: IInventoryChanges = {};
|
||||
let InventoryChanges = {};
|
||||
if (recipe.consumeOnUse) {
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
@ -133,17 +130,10 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...(await addItem(
|
||||
inventory,
|
||||
recipe.resultType,
|
||||
recipe.num,
|
||||
false,
|
||||
undefined,
|
||||
pendingRecipe.TargetFingerprint
|
||||
))
|
||||
...(await addItem(inventory, recipe.resultType, recipe.num, false))
|
||||
};
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({ InventoryChanges, BrandedSuits });
|
||||
res.json({ InventoryChanges });
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
@ -17,7 +17,7 @@ export const claimLibraryDailyTaskRewardController: RequestHandler = async (req,
|
||||
}
|
||||
syndicate.Standing += rewardStanding;
|
||||
|
||||
addFusionPoints(inventory, 80 * rewardQuantity);
|
||||
inventory.FusionPoints += 80 * rewardQuantity;
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
|
@ -1,41 +0,0 @@
|
||||
import { getCalendarProgress, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { getWorldState } from "@/src/services/worldStateService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER
|
||||
export const completeCalendarEventController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const calendarProgress = getCalendarProgress(inventory);
|
||||
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||
let inventoryChanges: IInventoryChanges = {};
|
||||
let dayIndex = 0;
|
||||
for (const day of currentSeason.Days) {
|
||||
if (day.events.length == 0 || day.events[0].type != "CET_CHALLENGE") {
|
||||
if (dayIndex == calendarProgress.SeasonProgress.LastCompletedDayIdx) {
|
||||
if (day.events.length != 0) {
|
||||
const selection = day.events[parseInt(req.query.CompletedEventIdx as string)];
|
||||
if (selection.type == "CET_REWARD") {
|
||||
inventoryChanges = (await handleStoreItemAcquisition(selection.reward!, inventory))
|
||||
.InventoryChanges;
|
||||
} else if (selection.type == "CET_UPGRADE") {
|
||||
calendarProgress.YearProgress.Upgrades.push(selection.upgrade!);
|
||||
} else if (selection.type != "CET_PLOT") {
|
||||
throw new Error(`unexpected selection type: ${selection.type}`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
++dayIndex;
|
||||
}
|
||||
}
|
||||
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
CalendarProgress: inventory.CalendarProgress
|
||||
});
|
||||
};
|
@ -1,14 +1,8 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import {
|
||||
deleteGuild,
|
||||
getGuildClient,
|
||||
giveClanKey,
|
||||
hasGuildPermission,
|
||||
removeDojoKeyItems
|
||||
} from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
|
||||
import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
@ -47,7 +41,14 @@ export const confirmGuildInvitationGetController: RequestHandler = async (req, r
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
giveClanKey(inventory, inventoryChanges);
|
||||
const recipeChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addRecipes(inventory, recipeChanges);
|
||||
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
|
||||
await inventory.save();
|
||||
|
||||
const guild = (await Guild.findById(req.query.clanId as string))!;
|
||||
@ -95,9 +96,14 @@ export const confirmGuildInvitationPostController: RequestHandler = async (req,
|
||||
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
||||
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes");
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
giveClanKey(inventory);
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
await inventory.save();
|
||||
|
||||
// Add join to clan log
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild } from "@/src/models/guildModel";
|
||||
import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService";
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
@ -30,13 +31,49 @@ export const contributeGuildClassController: RequestHandler = async (req, res) =
|
||||
|
||||
guild.CeremonyContributors.push(new Types.ObjectId(accountId));
|
||||
|
||||
await checkClanAscensionHasRequiredContributors(guild);
|
||||
// Once required contributor count is hit, the class is committed and there's 72 hours to claim endo.
|
||||
if (guild.CeremonyContributors.length == payload.RequiredContributors) {
|
||||
guild.Class = guild.CeremonyClass!;
|
||||
guild.CeremonyClass = undefined;
|
||||
guild.CeremonyResetDate = new Date(Date.now() + (config.fastClanAscension ? 5_000 : 72 * 3600_000));
|
||||
if (!config.fastClanAscension) {
|
||||
// Send message to all active guild members
|
||||
const members = await GuildMember.find({ guildId: payload.GuildId, status: 0 }, "accountId");
|
||||
for (const member of members) {
|
||||
// somewhat unfaithful as on live the "msg" is not a loctag, but since we don't have the string, we'll let the client fill it in with "arg".
|
||||
await createMessage(member.accountId, [
|
||||
{
|
||||
sndr: guild.Name,
|
||||
msg: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgressDetails",
|
||||
arg: [
|
||||
{
|
||||
Key: "RESETDATE",
|
||||
Tag:
|
||||
guild.CeremonyResetDate.getUTCMonth() +
|
||||
"/" +
|
||||
guild.CeremonyResetDate.getUTCDate() +
|
||||
"/" +
|
||||
(guild.CeremonyResetDate.getUTCFullYear() % 100) +
|
||||
" " +
|
||||
guild.CeremonyResetDate.getUTCHours().toString().padStart(2, "0") +
|
||||
":" +
|
||||
guild.CeremonyResetDate.getUTCMinutes().toString().padStart(2, "0")
|
||||
}
|
||||
],
|
||||
sub: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgress",
|
||||
icon: "/Lotus/Interface/Graphics/ClanTileImages/ClanEnterDojo.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
|
||||
// Either way, endo is given to the contributor.
|
||||
const inventory = await getInventory(accountId, "FusionPoints");
|
||||
addFusionPoints(inventory, guild.CeremonyEndo!);
|
||||
inventory.FusionPoints += guild.CeremonyEndo!;
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
|
@ -2,9 +2,8 @@ import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { createUniqueClanName, getGuildClient } from "@/src/services/guildService";
|
||||
import { addRecipes, getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const createGuildController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -27,15 +26,26 @@ export const createGuildController: RequestHandler = async (req, res) => {
|
||||
rank: 0
|
||||
});
|
||||
|
||||
const inventory = await getInventory(accountId, "GuildId LevelKeys Recipes");
|
||||
const inventory = await getInventory(accountId, "GuildId Recipes");
|
||||
inventory.GuildId = guild._id;
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
giveClanKey(inventory, inventoryChanges);
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
...(await getGuildClient(guild, accountId)),
|
||||
InventoryChanges: inventoryChanges
|
||||
InventoryChanges: {
|
||||
Recipes: [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const crewMembersController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "CrewMembers");
|
||||
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
|
||||
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
|
||||
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
|
||||
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
|
||||
dbCrewMember.WeaponConfigIdx = data.crewMember.WeaponConfigIdx;
|
||||
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
|
||||
dbCrewMember.Configs = data.crewMember.Configs;
|
||||
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
|
||||
await inventory.save();
|
||||
res.json({
|
||||
crewMemberId: data.crewMember.ItemId.$oid,
|
||||
NemesisFingerprint: data.crewMember.NemesisFingerprint
|
||||
});
|
||||
};
|
||||
|
||||
interface ICrewMembersRequest {
|
||||
crewMember: ICrewMemberClient;
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
import {
|
||||
addCrewShipSalvagedWeaponSkin,
|
||||
addCrewShipRawSalvage,
|
||||
getInventory,
|
||||
addEquipment
|
||||
} from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { ICrewShipComponentFingerprint, IInnateDamageFingerprint } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportCustoms, ExportRailjackWeapons, ExportUpgrades } from "warframe-public-export-plus";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { getRandomInt } from "@/src/services/rngService";
|
||||
import { IFingerprintStat } from "@/src/helpers/rivenHelper";
|
||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
|
||||
export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(
|
||||
accountId,
|
||||
"CrewShipSalvagedWeaponSkins CrewShipSalvagedWeapons CrewShipRawSalvage"
|
||||
);
|
||||
const payload = getJSONfromString<ICrewShipIdentifySalvageRequest>(String(req.body));
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (payload.ItemType in ExportCustoms) {
|
||||
const meta = ExportCustoms[payload.ItemType];
|
||||
let upgradeFingerprint: ICrewShipComponentFingerprint = { compat: payload.ItemType, buffs: [] };
|
||||
if (meta.subroutines) {
|
||||
upgradeFingerprint = {
|
||||
SubroutineIndex: getRandomInt(0, meta.subroutines.length - 1),
|
||||
...upgradeFingerprint
|
||||
};
|
||||
}
|
||||
for (const upgrade of meta.randomisedUpgrades!) {
|
||||
upgradeFingerprint.buffs.push({ Tag: upgrade.tag, Value: Math.trunc(Math.random() * 0x40000000) });
|
||||
}
|
||||
addCrewShipSalvagedWeaponSkin(
|
||||
inventory,
|
||||
payload.ItemType,
|
||||
JSON.stringify(upgradeFingerprint),
|
||||
inventoryChanges
|
||||
);
|
||||
} else {
|
||||
const meta = ExportRailjackWeapons[payload.ItemType];
|
||||
let defaultOverwrites: Partial<IEquipmentDatabase> | undefined;
|
||||
if (meta.defaultUpgrades?.[0]) {
|
||||
const upgradeType = meta.defaultUpgrades[0].ItemType;
|
||||
const upgradeMeta = ExportUpgrades[upgradeType];
|
||||
const buffs: IFingerprintStat[] = [];
|
||||
for (const buff of upgradeMeta.upgradeEntries!) {
|
||||
buffs.push({
|
||||
Tag: buff.tag,
|
||||
Value: Math.trunc(Math.random() * 0x40000000)
|
||||
});
|
||||
}
|
||||
defaultOverwrites = {
|
||||
UpgradeType: upgradeType,
|
||||
UpgradeFingerprint: JSON.stringify({
|
||||
compat: payload.ItemType,
|
||||
buffs
|
||||
} satisfies IInnateDamageFingerprint)
|
||||
};
|
||||
}
|
||||
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, defaultOverwrites, inventoryChanges);
|
||||
}
|
||||
|
||||
inventoryChanges.CrewShipRawSalvage = [
|
||||
{
|
||||
ItemType: payload.ItemType,
|
||||
ItemCount: -1
|
||||
}
|
||||
];
|
||||
addCrewShipRawSalvage(inventory, inventoryChanges.CrewShipRawSalvage);
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
};
|
||||
|
||||
interface ICrewShipIdentifySalvageRequest {
|
||||
ItemType: string;
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
// Arbiter Dojo endpoints, not really used by us as we don't provide a ContentURL.
|
||||
|
||||
export const dojoController: RequestHandler = (_req, res) => {
|
||||
res.json("-1"); // Tell client to use authorised request.
|
||||
};
|
||||
|
||||
export const setDojoURLController: RequestHandler = (_req, res) => {
|
||||
res.end();
|
||||
};
|
||||
|
@ -55,7 +55,7 @@ export const dronesController: RequestHandler = async (req, res) => {
|
||||
? new Date()
|
||||
: new Date(Date.now() + getRandomInt(3 * 3600 * 1000, 4 * 3600 * 1000));
|
||||
drone.PendingDamage =
|
||||
!config.noResourceExtractorDronesDamage && Math.random() < system.damageChance
|
||||
Math.random() < system.damageChance
|
||||
? getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue)
|
||||
: 0;
|
||||
const resource = getRandomWeightedRewardUc(system.resources, droneMeta.probabilities)!;
|
||||
|
@ -21,12 +21,10 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
|
||||
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
||||
if (inventory.EntratiLabConquestUnlocked) {
|
||||
inventory.EntratiLabConquestUnlocked = 0;
|
||||
inventory.EntratiLabConquestCacheScoreMission = 0;
|
||||
inventory.EntratiLabConquestActiveFrameVariants = [];
|
||||
}
|
||||
if (inventory.EchoesHexConquestUnlocked) {
|
||||
inventory.EchoesHexConquestUnlocked = 0;
|
||||
inventory.EchoesHexConquestCacheScoreMission = 0;
|
||||
inventory.EchoesHexConquestActiveFrameVariants = [];
|
||||
inventory.EchoesHexConquestActiveStickers = [];
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItems, addStanding, getInventory } from "@/src/services/inventoryService";
|
||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||
import { addMiscItems, getInventory, getStandingLimit, updateStandingLimit } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { ExportResources } from "warframe-public-export-plus";
|
||||
import { ExportResources, ExportSyndicates } from "warframe-public-export-plus";
|
||||
|
||||
export const fishmongerController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -30,15 +31,32 @@ export const fishmongerController: RequestHandler = async (req, res) => {
|
||||
miscItemChanges.push({ ItemType: fish.ItemType, ItemCount: fish.ItemCount * -1 });
|
||||
}
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
let affiliationMod;
|
||||
if (gainedStanding && syndicateTag) affiliationMod = addStanding(inventory, syndicateTag, gainedStanding);
|
||||
if (gainedStanding && syndicateTag) {
|
||||
let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag);
|
||||
if (!syndicate) {
|
||||
syndicate = inventory.Affiliations[inventory.Affiliations.push({ Tag: syndicateTag, Standing: 0 }) - 1];
|
||||
}
|
||||
const syndicateMeta = ExportSyndicates[syndicateTag];
|
||||
|
||||
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
|
||||
if (syndicate.Standing + gainedStanding > max) {
|
||||
gainedStanding = max - syndicate.Standing;
|
||||
}
|
||||
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
|
||||
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
|
||||
}
|
||||
|
||||
syndicate.Standing += gainedStanding;
|
||||
|
||||
updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
MiscItems: miscItemChanges
|
||||
},
|
||||
SyndicateTag: syndicateTag,
|
||||
StandingChange: affiliationMod?.Standing || 0
|
||||
StandingChange: gainedStanding
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -104,14 +104,13 @@ export const focusController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
case FocusOperation.SentTrainingAmplifier: {
|
||||
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
|
||||
ModularParts: [
|
||||
const parts: string[] = [
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
|
||||
]
|
||||
});
|
||||
];
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
|
||||
occupySlot(inventory, InventorySlot.AMPS, false);
|
||||
await inventory.save();
|
||||
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
|
||||
|
@ -1,84 +0,0 @@
|
||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
|
||||
import { toStoreItem } from "@/src/services/itemDataService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IMongoDate } from "@/src/types/commonTypes";
|
||||
import { IMissionReward } from "@/src/types/missionTypes";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { IGardeningClient } from "@/src/types/shipTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { dict_en, ExportResources } from "warframe-public-export-plus";
|
||||
|
||||
export const gardeningController: RequestHandler = async (req, res) => {
|
||||
const data = getJSONfromString<IGardeningRequest>(String(req.body));
|
||||
if (data.Mode != "HarvestAll") {
|
||||
throw new Error(`unexpected gardening mode: ${data.Mode}`);
|
||||
}
|
||||
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const [inventory, personalRooms] = await Promise.all([
|
||||
getInventory(accountId, "MiscItems"),
|
||||
getPersonalRooms(accountId, "Apartment")
|
||||
]);
|
||||
|
||||
// Harvest plants
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const rewards: Record<string, IMissionReward[][]> = {};
|
||||
for (const planter of personalRooms.Apartment.Gardening.Planters) {
|
||||
rewards[planter.Name] = [];
|
||||
for (const plant of planter.Plants) {
|
||||
const itemType =
|
||||
"/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" +
|
||||
plant.PlantType.substring(plant.PlantType.length - 1);
|
||||
const itemCount = Math.random() < 0.775 ? 2 : 4;
|
||||
|
||||
addMiscItem(inventory, itemType, itemCount, inventoryChanges);
|
||||
|
||||
rewards[planter.Name].push([
|
||||
{
|
||||
StoreItem: toStoreItem(itemType),
|
||||
TypeName: itemType,
|
||||
ItemCount: itemCount,
|
||||
DailyCooldown: false,
|
||||
Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564,
|
||||
TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`,
|
||||
ProductCategory: "MiscItems"
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh garden
|
||||
personalRooms.Apartment.Gardening = createGarden();
|
||||
|
||||
await Promise.all([inventory.save(), personalRooms.save()]);
|
||||
|
||||
const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1];
|
||||
const plant = planter.Plants[planter.Plants.length - 1];
|
||||
res.json({
|
||||
GardenTagName: planter.Name,
|
||||
PlantType: plant.PlantType,
|
||||
PlotIndex: plant.PlotIndex,
|
||||
EndTime: toMongoDate(plant.EndTime),
|
||||
InventoryChanges: inventoryChanges,
|
||||
Gardening: personalRooms.toJSON<IPersonalRoomsClient>().Apartment.Gardening,
|
||||
Rewards: rewards
|
||||
} satisfies IGardeningResponse);
|
||||
};
|
||||
|
||||
interface IGardeningRequest {
|
||||
Mode: string;
|
||||
}
|
||||
|
||||
interface IGardeningResponse {
|
||||
GardenTagName: string;
|
||||
PlantType: string;
|
||||
PlotIndex: number;
|
||||
EndTime: IMongoDate;
|
||||
InventoryChanges: IInventoryChanges;
|
||||
Gardening: IGardeningClient;
|
||||
Rewards: Record<string, IMissionReward[][]>;
|
||||
}
|
@ -1,20 +1,16 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { Account, Ignore } from "@/src/models/loginModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||
import { parallelForeach } from "@/src/utils/async-utils";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const getIgnoredUsersController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const ignores = await Ignore.find({ ignorer: accountId });
|
||||
const ignoredUsers: IFriendInfo[] = [];
|
||||
await parallelForeach(ignores, async ignore => {
|
||||
const ignoreeAccount = (await Account.findById(ignore.ignoree, "DisplayName"))!;
|
||||
ignoredUsers.push({
|
||||
_id: toOid(ignore.ignoree),
|
||||
DisplayName: ignoreeAccount.DisplayName + ""
|
||||
const getIgnoredUsersController: RequestHandler = (_req, res) => {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/html",
|
||||
"Content-Length": "3"
|
||||
});
|
||||
});
|
||||
res.json({ IgnoredUsers: ignoredUsers });
|
||||
res.end(
|
||||
Buffer.from([
|
||||
0x7b, 0x22, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x38, 0x33, 0x30, 0x34, 0x30, 0x37, 0x37, 0x32, 0x32,
|
||||
0x34, 0x30, 0x32, 0x32, 0x32, 0x36, 0x31, 0x35, 0x30, 0x31, 0x7d
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
export { getIgnoredUsersController };
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { generateRewardSeed } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const getNewRewardSeedController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
const rewardSeed = generateRewardSeed();
|
||||
logger.debug(`generated new reward seed: ${rewardSeed}`);
|
||||
await Inventory.updateOne(
|
||||
{
|
||||
accountOwnerId: accountId
|
||||
|
@ -2,24 +2,17 @@ import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getShip } from "@/src/services/shipService";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { IGetShipResponse } from "@/src/types/shipTypes";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
|
||||
import { getLoadout } from "@/src/services/loadoutService";
|
||||
|
||||
export const getShipController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRoomsDb = await getPersonalRooms(accountId);
|
||||
|
||||
// Setup gardening if it's missing. Maybe should be done as part of some quest completion in the future.
|
||||
if (personalRoomsDb.Apartment.Gardening.Planters.length == 0) {
|
||||
personalRoomsDb.Apartment.Gardening = createGarden();
|
||||
await personalRoomsDb.save();
|
||||
}
|
||||
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRooms>();
|
||||
const loadout = await getLoadout(accountId);
|
||||
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");
|
||||
|
||||
@ -33,10 +26,7 @@ export const getShipController: RequestHandler = async (req, res) => {
|
||||
Colors: personalRooms.ShipInteriorColors,
|
||||
ShipAttachments: ship.ShipAttachments,
|
||||
SkinFlavourItem: ship.SkinFlavourItem
|
||||
},
|
||||
FavouriteLoadoutId: personalRooms.Ship.FavouriteLoadoutId
|
||||
? toOid(personalRooms.Ship.FavouriteLoadoutId)
|
||||
: undefined
|
||||
}
|
||||
},
|
||||
Apartment: personalRooms.Apartment,
|
||||
TailorShop: personalRooms.TailorShop
|
||||
|
@ -2,25 +2,36 @@ import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { WeaponTypeInternal } from "@/src/services/itemDataService";
|
||||
import { ArtifactPolarity, EquipmentFeatures, IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { ExportRecipes } from "warframe-public-export-plus";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
|
||||
const modularWeaponCategory: (WeaponTypeInternal | "Hoverboards")[] = [
|
||||
"LongGuns",
|
||||
"Pistols",
|
||||
"Melee",
|
||||
"OperatorAmps",
|
||||
"Hoverboards"
|
||||
];
|
||||
|
||||
interface IGildWeaponRequest {
|
||||
ItemName: string;
|
||||
Recipe: string; // e.g. /Lotus/Weapons/SolarisUnited/LotusGildKitgunBlueprint
|
||||
PolarizeSlot?: number;
|
||||
PolarizeValue?: ArtifactPolarity;
|
||||
ItemId: string;
|
||||
Category: TEquipmentKey;
|
||||
Category: WeaponTypeInternal | "Hoverboards";
|
||||
}
|
||||
|
||||
export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const data = getJSONfromString<IGildWeaponRequest>(String(req.body));
|
||||
data.ItemId = String(req.query.ItemId);
|
||||
data.Category = req.query.Category as TEquipmentKey;
|
||||
if (!modularWeaponCategory.includes(req.query.Category as WeaponTypeInternal | "Hoverboards")) {
|
||||
throw new Error(`Unknown modular weapon Category: ${String(req.query.Category)}`);
|
||||
}
|
||||
data.Category = req.query.Category as WeaponTypeInternal | "Hoverboards";
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const weaponIndex = inventory[data.Category].findIndex(x => String(x._id) === data.ItemId);
|
||||
@ -31,10 +42,8 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
const weapon = inventory[data.Category][weaponIndex];
|
||||
weapon.Features ??= 0;
|
||||
weapon.Features |= EquipmentFeatures.GILDED;
|
||||
if (data.Recipe != "webui") {
|
||||
weapon.ItemName = data.ItemName;
|
||||
weapon.XP = 0;
|
||||
}
|
||||
if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
|
||||
weapon.Polarity = [
|
||||
{
|
||||
@ -47,9 +56,6 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
inventoryChanges[data.Category] = [weapon.toJSON<IEquipmentClient>()];
|
||||
|
||||
const affiliationMods = [];
|
||||
|
||||
if (data.Recipe != "webui") {
|
||||
const recipe = ExportRecipes[data.Recipe];
|
||||
inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
|
||||
ItemType: ingredient.ItemType,
|
||||
@ -57,6 +63,7 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
}));
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
|
||||
const affiliationMods = [];
|
||||
if (recipe.syndicateStandingChange) {
|
||||
const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
|
||||
affiliation.Standing += recipe.syndicateStandingChange.value;
|
||||
@ -65,7 +72,6 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
Standing: recipe.syndicateStandingChange.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
|
@ -16,7 +16,7 @@ export const giveQuestKeyRewardController: RequestHandler = async (req, res) =>
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = await addItem(inventory, reward.ItemType, reward.Amount);
|
||||
await inventory.save();
|
||||
res.json(inventoryChanges);
|
||||
res.json(inventoryChanges.InventoryChanges);
|
||||
//TODO: consider whishlist changes
|
||||
};
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addLoreFragmentScans, addShipDecorations, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ILoreFragmentScan, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const giveShipDecoAndLoreFragmentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "LoreFragmentScans ShipDecorations");
|
||||
const data = getJSONfromString<IGiveShipDecoAndLoreFragmentRequest>(String(req.body));
|
||||
addLoreFragmentScans(inventory, data.LoreFragmentScans);
|
||||
addShipDecorations(inventory, data.ShipDecorations);
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IGiveShipDecoAndLoreFragmentRequest {
|
||||
LoreFragmentScans: ILoreFragmentScan[];
|
||||
ShipDecorations: ITypeCount[];
|
||||
}
|
@ -14,33 +14,29 @@ import {
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import {
|
||||
addCrewShipWeaponSkin,
|
||||
addEquipment,
|
||||
addItem,
|
||||
addMiscItems,
|
||||
addRecipes,
|
||||
combineInventoryChanges,
|
||||
getInventory,
|
||||
occupySlot,
|
||||
updateCurrency
|
||||
} from "@/src/services/inventoryService";
|
||||
import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { GuildPermission, ITechProjectClient } from "@/src/types/guildTypes";
|
||||
import { GuildMember } from "@/src/models/guildModel";
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
|
||||
export const guildTechController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
|
||||
if (data.Action == "Sync") {
|
||||
let needSave = false;
|
||||
const techProjects: ITechProjectClient[] = [];
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (guild.TechProjects) {
|
||||
for (const project of guild.TechProjects) {
|
||||
const techProject: ITechProjectClient = {
|
||||
@ -63,8 +59,6 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
res.json({ TechProjects: techProjects });
|
||||
} else if (data.Action == "Start") {
|
||||
if (data.Mode == "Guild") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
@ -95,109 +89,38 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
await guild.save();
|
||||
res.end();
|
||||
} else {
|
||||
const recipe = ExportDojoRecipes.research[data.RecipeType];
|
||||
if (data.TechProductCategory) {
|
||||
if (
|
||||
data.TechProductCategory != "CrewShipWeapons" &&
|
||||
data.TechProductCategory != "CrewShipWeaponSkins"
|
||||
) {
|
||||
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
|
||||
}
|
||||
if (!inventory[getSalvageCategory(data.TechProductCategory)].id(data.CategoryItemId)) {
|
||||
throw new Error(
|
||||
`no item with id ${data.CategoryItemId} in ${getSalvageCategory(data.TechProductCategory)} array`
|
||||
);
|
||||
}
|
||||
}
|
||||
const techProject =
|
||||
inventory.PersonalTechProjects[
|
||||
inventory.PersonalTechProjects.push({
|
||||
State: 0,
|
||||
ReqCredits: recipe.price,
|
||||
ItemType: data.RecipeType,
|
||||
ProductCategory: data.TechProductCategory,
|
||||
CategoryItemId: data.CategoryItemId,
|
||||
ReqItems: recipe.ingredients
|
||||
}) - 1
|
||||
];
|
||||
await inventory.save();
|
||||
res.json({
|
||||
isPersonal: true,
|
||||
action: "Start",
|
||||
personalTech: techProject.toJSON()
|
||||
});
|
||||
}
|
||||
} else if (data.Action == "Contribute") {
|
||||
if ((req.query.guildId as string) == "000000000000000000000000") {
|
||||
const techProject = inventory.PersonalTechProjects.id(data.ResearchId)!;
|
||||
|
||||
techProject.ReqCredits -= data.RegularCredits;
|
||||
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, data.RegularCredits, false);
|
||||
|
||||
const miscItemChanges = [];
|
||||
for (const miscItem of data.MiscItems) {
|
||||
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||
if (reqItem) {
|
||||
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||
miscItem.ItemCount = reqItem.ItemCount;
|
||||
}
|
||||
reqItem.ItemCount -= miscItem.ItemCount;
|
||||
miscItemChanges.push({
|
||||
ItemType: miscItem.ItemType,
|
||||
ItemCount: miscItem.ItemCount * -1
|
||||
});
|
||||
}
|
||||
}
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
inventoryChanges.MiscItems = miscItemChanges;
|
||||
|
||||
techProject.HasContributions = true;
|
||||
|
||||
if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
|
||||
techProject.State = 1;
|
||||
const recipe = ExportDojoRecipes.research[techProject.ItemType];
|
||||
techProject.CompletionDate = new Date(Date.now() + recipe.time * 1000);
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
PersonalResearch: { $oid: data.ResearchId },
|
||||
PersonalResearchDate: techProject.CompletionDate ? toMongoDate(techProject.CompletionDate) : undefined
|
||||
});
|
||||
} else {
|
||||
if (!hasAccessToDojo(inventory)) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const guildMember = (await GuildMember.findOne(
|
||||
{ accountId, guildId: guild._id },
|
||||
"RegularCreditsContributed MiscItemsContributed"
|
||||
))!;
|
||||
|
||||
const techProject = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
|
||||
const contributions = data;
|
||||
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
|
||||
|
||||
if (data.VaultCredits) {
|
||||
if (data.VaultCredits > techProject.ReqCredits) {
|
||||
data.VaultCredits = techProject.ReqCredits;
|
||||
if (contributions.VaultCredits) {
|
||||
if (contributions.VaultCredits > techProject.ReqCredits) {
|
||||
contributions.VaultCredits = techProject.ReqCredits;
|
||||
}
|
||||
techProject.ReqCredits -= data.VaultCredits;
|
||||
guild.VaultRegularCredits! -= data.VaultCredits;
|
||||
techProject.ReqCredits -= contributions.VaultCredits;
|
||||
guild.VaultRegularCredits! -= contributions.VaultCredits;
|
||||
}
|
||||
|
||||
if (data.RegularCredits > techProject.ReqCredits) {
|
||||
data.RegularCredits = techProject.ReqCredits;
|
||||
if (contributions.RegularCredits > techProject.ReqCredits) {
|
||||
contributions.RegularCredits = techProject.ReqCredits;
|
||||
}
|
||||
techProject.ReqCredits -= data.RegularCredits;
|
||||
techProject.ReqCredits -= contributions.RegularCredits;
|
||||
|
||||
guildMember.RegularCreditsContributed ??= 0;
|
||||
guildMember.RegularCreditsContributed += data.RegularCredits;
|
||||
guildMember.RegularCreditsContributed += contributions.RegularCredits;
|
||||
|
||||
if (data.VaultMiscItems.length) {
|
||||
for (const miscItem of data.VaultMiscItems) {
|
||||
if (contributions.VaultMiscItems.length) {
|
||||
for (const miscItem of contributions.VaultMiscItems) {
|
||||
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||
if (reqItem) {
|
||||
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||
@ -212,7 +135,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
const miscItemChanges = [];
|
||||
for (const miscItem of data.MiscItems) {
|
||||
for (const miscItem of contributions.MiscItems) {
|
||||
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||
if (reqItem) {
|
||||
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||
@ -228,7 +151,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, data.RegularCredits, false);
|
||||
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, contributions.RegularCredits, false);
|
||||
inventoryChanges.MiscItems = miscItemChanges;
|
||||
|
||||
// Check if research is fully funded now.
|
||||
@ -239,18 +162,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
InventoryChanges: inventoryChanges,
|
||||
Vault: getGuildVault(guild)
|
||||
});
|
||||
}
|
||||
} else if (data.Action.split(",")[0] == "Buy") {
|
||||
const purchase = data as IGuildTechBuyRequest;
|
||||
if (purchase.Mode == "Guild") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (
|
||||
!hasAccessToDojo(inventory) ||
|
||||
!(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))
|
||||
) {
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
const purchase = data as IGuildTechBuyRequest;
|
||||
const quantity = parseInt(data.Action.split(",")[1]);
|
||||
const recipeChanges = [
|
||||
{
|
||||
@ -272,15 +189,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
Recipes: recipeChanges
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const inventoryChanges = claimSalvagedComponent(inventory, purchase.CategoryItemId!);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges
|
||||
});
|
||||
}
|
||||
} else if (data.Action == "Fabricate") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
@ -297,7 +206,6 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
|
||||
res.json({ inventoryChanges: inventoryChanges });
|
||||
} else if (data.Action == "Pause") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
@ -309,7 +217,6 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
await removePigmentsFromGuildMembers(guild._id);
|
||||
res.end();
|
||||
} else if (data.Action == "Unpause") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
@ -319,66 +226,9 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
guild.ActiveDojoColorResearch = data.RecipeType;
|
||||
await guild.save();
|
||||
res.end();
|
||||
} else if (data.Action == "Cancel" && data.CategoryItemId) {
|
||||
const personalTechProjectIndex = inventory.PersonalTechProjects.findIndex(x =>
|
||||
x.CategoryItemId?.equals(data.CategoryItemId)
|
||||
);
|
||||
const personalTechProject = inventory.PersonalTechProjects[personalTechProjectIndex];
|
||||
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
|
||||
|
||||
const meta = ExportDojoRecipes.research[personalTechProject.ItemType];
|
||||
const contributedCredits = meta.price - personalTechProject.ReqCredits;
|
||||
const inventoryChanges = updateCurrency(inventory, contributedCredits * -1, false);
|
||||
inventoryChanges.MiscItems = [];
|
||||
for (const ingredient of meta.ingredients) {
|
||||
const reqItem = personalTechProject.ReqItems.find(x => x.ItemType == ingredient.ItemType);
|
||||
if (reqItem) {
|
||||
const contributedItems = ingredient.ItemCount - reqItem.ItemCount;
|
||||
inventoryChanges.MiscItems.push({
|
||||
ItemType: ingredient.ItemType,
|
||||
ItemCount: contributedItems
|
||||
});
|
||||
}
|
||||
}
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
action: "Cancel",
|
||||
isPersonal: true,
|
||||
inventoryChanges: inventoryChanges,
|
||||
personalTech: {
|
||||
ItemId: toOid(personalTechProject._id)
|
||||
}
|
||||
});
|
||||
} else if (data.Action == "Rush" && data.CategoryItemId) {
|
||||
const inventoryChanges: IInventoryChanges = {
|
||||
...updateCurrency(inventory, 20, true),
|
||||
...claimSalvagedComponent(inventory, data.CategoryItemId)
|
||||
};
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges
|
||||
});
|
||||
} else if (data.Action == "InstantFinish") {
|
||||
if (data.TechProductCategory != "CrewShipWeapons" && data.TechProductCategory != "CrewShipWeaponSkins") {
|
||||
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
|
||||
}
|
||||
const inventoryChanges = finishComponentRepair(inventory, data.TechProductCategory, data.CategoryItemId!);
|
||||
inventoryChanges.MiscItems = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/InstantSalvageRepairItem",
|
||||
ItemCount: -1
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges
|
||||
});
|
||||
} else {
|
||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||
throw new Error(`unhandled guildTech request`);
|
||||
throw new Error(`unknown guildTech action: ${data.Action}`);
|
||||
}
|
||||
};
|
||||
|
||||
@ -388,70 +238,23 @@ type TGuildTechRequest =
|
||||
| IGuildTechContributeRequest;
|
||||
|
||||
interface IGuildTechBasicRequest {
|
||||
Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush" | "InstantFinish";
|
||||
Mode: "Guild" | "Personal";
|
||||
Action: "Start" | "Fabricate" | "Pause" | "Unpause";
|
||||
Mode: "Guild";
|
||||
RecipeType: string;
|
||||
TechProductCategory?: string;
|
||||
CategoryItemId?: string;
|
||||
}
|
||||
|
||||
interface IGuildTechBuyRequest extends Omit<IGuildTechBasicRequest, "Action"> {
|
||||
interface IGuildTechBuyRequest {
|
||||
Action: string;
|
||||
Mode: "Guild";
|
||||
RecipeType: string;
|
||||
}
|
||||
|
||||
interface IGuildTechContributeRequest {
|
||||
Action: "Contribute";
|
||||
ResearchId: string;
|
||||
ResearchId: "";
|
||||
RecipeType: string;
|
||||
RegularCredits: number;
|
||||
MiscItems: IMiscItem[];
|
||||
VaultCredits: number;
|
||||
VaultMiscItems: IMiscItem[];
|
||||
}
|
||||
|
||||
const getSalvageCategory = (
|
||||
category: "CrewShipWeapons" | "CrewShipWeaponSkins"
|
||||
): "CrewShipSalvagedWeapons" | "CrewShipSalvagedWeaponSkins" => {
|
||||
return category == "CrewShipWeapons" ? "CrewShipSalvagedWeapons" : "CrewShipSalvagedWeaponSkins";
|
||||
};
|
||||
|
||||
const claimSalvagedComponent = (inventory: TInventoryDatabaseDocument, itemId: string): IInventoryChanges => {
|
||||
// delete personal tech project
|
||||
const personalTechProjectIndex = inventory.PersonalTechProjects.findIndex(x => x.CategoryItemId?.equals(itemId));
|
||||
const personalTechProject = inventory.PersonalTechProjects[personalTechProjectIndex];
|
||||
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
|
||||
|
||||
const category = personalTechProject.ProductCategory! as "CrewShipWeapons" | "CrewShipWeaponSkins";
|
||||
return finishComponentRepair(inventory, category, itemId);
|
||||
};
|
||||
|
||||
const finishComponentRepair = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
category: "CrewShipWeapons" | "CrewShipWeaponSkins",
|
||||
itemId: string
|
||||
): IInventoryChanges => {
|
||||
const salvageCategory = getSalvageCategory(category);
|
||||
|
||||
// find salved part & delete it
|
||||
const salvageIndex = inventory[salvageCategory].findIndex(x => x._id.equals(itemId));
|
||||
const salvageItem = inventory[salvageCategory][salvageIndex];
|
||||
inventory[salvageCategory].splice(salvageIndex, 1);
|
||||
|
||||
// add final item
|
||||
const inventoryChanges = {
|
||||
...(category == "CrewShipWeaponSkins"
|
||||
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
||||
: addEquipment(inventory, category, salvageItem.ItemType, {
|
||||
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||
})),
|
||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
||||
};
|
||||
|
||||
inventoryChanges.RemovedIdItems = [
|
||||
{
|
||||
ItemId: { $oid: itemId }
|
||||
}
|
||||
];
|
||||
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
@ -13,7 +13,6 @@ import { addItems, combineInventoryChanges, getInventory } from "@/src/services/
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
|
||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
|
||||
|
||||
export const inboxController: RequestHandler = async (req, res) => {
|
||||
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
|
||||
@ -49,7 +48,7 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
await addItems(
|
||||
inventory,
|
||||
attachmentItems.map(attItem => ({
|
||||
ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
|
||||
ItemType: attItem,
|
||||
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
|
||||
})),
|
||||
inventoryChanges
|
||||
|
@ -14,16 +14,9 @@ import {
|
||||
ExportVirtuals
|
||||
} from "warframe-public-export-plus";
|
||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
||||
import {
|
||||
addMiscItems,
|
||||
allDailyAffiliationKeys,
|
||||
cleanupInventory,
|
||||
createLibraryDailyTask,
|
||||
generateRewardSeed
|
||||
} from "@/src/services/inventoryService";
|
||||
import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const inventoryController: RequestHandler = async (request, response) => {
|
||||
const accountId = await getAccountIdForRequest(request);
|
||||
@ -86,10 +79,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
}
|
||||
}
|
||||
|
||||
cleanupInventory(inventory);
|
||||
|
||||
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
|
||||
//await inventory.save();
|
||||
await inventory.save();
|
||||
}
|
||||
|
||||
if (
|
||||
@ -98,19 +89,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
|
||||
) {
|
||||
handleSubsumeCompletion(inventory);
|
||||
//await inventory.save();
|
||||
}
|
||||
|
||||
if (inventory.LastInventorySync) {
|
||||
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
|
||||
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
|
||||
if (lastSyncDuviriMood != currentDuviriMood) {
|
||||
logger.debug(`refreshing duviri seed`);
|
||||
inventory.DuviriInfo.Seed = generateRewardSeed();
|
||||
}
|
||||
}
|
||||
inventory.LastInventorySync = new Types.ObjectId();
|
||||
await inventory.save();
|
||||
}
|
||||
|
||||
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
|
||||
};
|
||||
@ -169,7 +149,7 @@ export const getInventoryResponse = async (
|
||||
inventoryResponse.ShipDecorations = [];
|
||||
for (const [uniqueName, item] of Object.entries(ExportResources)) {
|
||||
if (item.productCategory == "ShipDecorations") {
|
||||
inventoryResponse.ShipDecorations.push({ ItemType: uniqueName, ItemCount: 999_999 });
|
||||
inventoryResponse.ShipDecorations.push({ ItemType: uniqueName, ItemCount: 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,8 +202,7 @@ export const getInventoryResponse = async (
|
||||
|
||||
if (config.universalPolarityEverywhere) {
|
||||
const Polarity: IPolarity[] = [];
|
||||
// 12 is needed for necramechs. 15 is needed for plexus/crewshipharness.
|
||||
for (let i = 0; i != 15; ++i) {
|
||||
for (let i = 0; i != 12; ++i) {
|
||||
Polarity.push({
|
||||
Slot: i,
|
||||
Value: ArtifactPolarity.Any
|
||||
@ -278,16 +257,12 @@ export const getInventoryResponse = async (
|
||||
}
|
||||
}
|
||||
|
||||
if (config.noDailyFocusLimit) {
|
||||
inventoryResponse.DailyFocus = Math.max(999_999, 250000 + inventoryResponse.PlayerLevel * 5000);
|
||||
}
|
||||
|
||||
if (inventoryResponse.InfestedFoundry) {
|
||||
applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
|
||||
}
|
||||
|
||||
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
|
||||
inventoryResponse.LastInventorySync = undefined;
|
||||
//inventoryResponse.LastInventorySync = toOid(new Types.ObjectId());
|
||||
|
||||
// Set 2FA enabled so trading post can be used
|
||||
inventoryResponse.HWIDProtectEnabled = true;
|
||||
|
@ -21,11 +21,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
|
||||
const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress;
|
||||
|
||||
if (
|
||||
!account &&
|
||||
((config.autoCreateAccount && loginRequest.ClientType != "webui") ||
|
||||
loginRequest.ClientType == "webui-register")
|
||||
) {
|
||||
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
||||
try {
|
||||
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
||||
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
|
||||
@ -41,7 +37,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
password: loginRequest.password,
|
||||
DisplayName: name,
|
||||
CountryCode: loginRequest.lang.toUpperCase(),
|
||||
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
||||
ClientType: loginRequest.ClientType,
|
||||
CrossPlatformAllowed: true,
|
||||
ForceLogoutVersion: 0,
|
||||
ConsentNeeded: false,
|
||||
@ -63,11 +59,6 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginRequest.ClientType == "webui-register") {
|
||||
response.status(400).json({ error: "account already exists" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
||||
response.status(400).json({ error: "incorrect login data" });
|
||||
return;
|
||||
|
@ -3,10 +3,9 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
||||
import { generateRewardSeed, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventoryResponse } from "./inventoryController";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||
|
||||
/*
|
||||
**** INPUT ****
|
||||
@ -54,16 +53,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
logger.debug("mission report:", missionReport);
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const firstCompletion = missionReport.SortieId
|
||||
? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
|
||||
: false;
|
||||
const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
|
||||
|
||||
if (
|
||||
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
||||
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
||||
) {
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
if (missionReport.MissionStatus !== "GS_SUCCESS") {
|
||||
await inventory.save();
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||
res.json({
|
||||
@ -73,16 +65,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
MissionRewards,
|
||||
inventoryChanges,
|
||||
credits,
|
||||
AffiliationMods,
|
||||
SyndicateXPItemReward,
|
||||
ConquestCompletedMissionsCount
|
||||
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||
const { MissionRewards, inventoryChanges, credits } = await addMissionRewards(inventory, missionReport);
|
||||
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
await inventory.save();
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||
|
||||
@ -93,11 +77,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
MissionRewards,
|
||||
...credits,
|
||||
...inventoryUpdates,
|
||||
//FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
|
||||
SyndicateXPItemReward,
|
||||
AffiliationMods,
|
||||
ConquestCompletedMissionsCount
|
||||
} satisfies IMissionInventoryUpdateResponse);
|
||||
FusionPoints: inventoryChanges?.FusionPoints
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -17,13 +17,12 @@ import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { getRandomInt } from "@/src/services/rngService";
|
||||
import { ExportSentinels, ExportWeapons, IDefaultUpgrade } from "warframe-public-export-plus";
|
||||
import { ExportSentinels } from "warframe-public-export-plus";
|
||||
import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
interface IModularCraftRequest {
|
||||
WeaponType: string;
|
||||
Parts: string[];
|
||||
isWebUi?: boolean;
|
||||
}
|
||||
|
||||
export const modularWeaponCraftingController: RequestHandler = async (req, res) => {
|
||||
@ -35,9 +34,9 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
const category = modularWeaponTypes[data.WeaponType];
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
let defaultUpgrades: IDefaultUpgrade[] | undefined;
|
||||
const defaultUpgrades = getDefaultUpgrades(data.Parts);
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
ModularParts: data.Parts
|
||||
Configs: applyDefaultUpgrades(inventory, defaultUpgrades)
|
||||
};
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (category == "KubrowPets") {
|
||||
@ -130,49 +129,25 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
// Only save mutagen & antigen in the ModularParts.
|
||||
defaultOverwrites.ModularParts = [data.Parts[1], data.Parts[2]];
|
||||
|
||||
const meta = ExportSentinels[data.WeaponType];
|
||||
|
||||
for (const specialItem of meta.exalted!) {
|
||||
for (const specialItem of ExportSentinels[data.WeaponType].exalted!) {
|
||||
addSpecialItem(inventory, specialItem, inventoryChanges);
|
||||
}
|
||||
|
||||
defaultUpgrades = meta.defaultUpgrades;
|
||||
} else {
|
||||
defaultUpgrades = getDefaultUpgrades(data.Parts);
|
||||
}
|
||||
|
||||
if (category == "MoaPets") {
|
||||
const weapon = ExportSentinels[data.WeaponType].defaultWeapon;
|
||||
if (weapon) {
|
||||
const category = ExportWeapons[weapon].productCategory;
|
||||
addEquipment(inventory, category, weapon, undefined, inventoryChanges);
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||
);
|
||||
}
|
||||
}
|
||||
defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||
addEquipment(inventory, category, data.WeaponType, defaultOverwrites, inventoryChanges);
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||
);
|
||||
addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
|
||||
combineInventoryChanges(inventoryChanges, occupySlot(inventory, productCategoryToInventoryBin(category)!, false));
|
||||
if (defaultUpgrades) {
|
||||
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
|
||||
}
|
||||
|
||||
// Remove credits & parts
|
||||
const miscItemChanges = [];
|
||||
let currencyChanges = {};
|
||||
if (!data.isWebUi) {
|
||||
for (const part of data.Parts) {
|
||||
miscItemChanges.push({
|
||||
ItemType: part,
|
||||
ItemCount: -1
|
||||
});
|
||||
}
|
||||
currencyChanges = updateCurrency(
|
||||
const currencyChanges = updateCurrency(
|
||||
inventory,
|
||||
category == "Hoverboards" ||
|
||||
category == "MoaPets" ||
|
||||
@ -184,9 +159,8 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
false
|
||||
);
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
|
||||
// Tell client what we did
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
|
@ -21,11 +21,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const partTypeToParts: Record<string, string[]> = {};
|
||||
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
||||
if (
|
||||
data.partType &&
|
||||
data.premiumPrice &&
|
||||
!data.excludeFromCodex // exclude pvp variants
|
||||
) {
|
||||
if (data.partType && data.premiumPrice) {
|
||||
partTypeToParts[data.partType] ??= [];
|
||||
partTypeToParts[data.partType].push(uniqueName);
|
||||
}
|
||||
@ -45,18 +41,24 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
||||
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||
const inventoryChanges: IInventoryChanges = {
|
||||
...addEquipment(inventory, category, weaponInfo.ItemType, {
|
||||
...addEquipment(
|
||||
inventory,
|
||||
category,
|
||||
weaponInfo.ItemType,
|
||||
weaponInfo.ModularParts,
|
||||
{},
|
||||
{
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
||||
ItemName: payload.ItemName,
|
||||
Configs: configs,
|
||||
ModularParts: weaponInfo.ModularParts,
|
||||
Polarity: [
|
||||
{
|
||||
Slot: payload.PolarizeSlot,
|
||||
Value: payload.PolarizeValue
|
||||
}
|
||||
]
|
||||
}),
|
||||
}
|
||||
),
|
||||
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
||||
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
||||
};
|
||||
@ -141,7 +143,7 @@ const getModularWeaponSale = (
|
||||
getItemType: (parts: string[]) => string
|
||||
): IModularWeaponSaleInfo => {
|
||||
const rng = new CRng(day);
|
||||
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType])!);
|
||||
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType]));
|
||||
let partsCost = 0;
|
||||
for (const part of parts) {
|
||||
partsCost += ExportWeapons[part].premiumPrice!;
|
||||
|
@ -1,31 +1,10 @@
|
||||
import {
|
||||
consumeModCharge,
|
||||
encodeNemesisGuess,
|
||||
getInfNodes,
|
||||
getKnifeUpgrade,
|
||||
getNemesisPasscode,
|
||||
getNemesisPasscodeModTypes,
|
||||
getWeaponsForManifest,
|
||||
IKnifeResponse,
|
||||
showdownNodes
|
||||
} from "@/src/helpers/nemesisHelpers";
|
||||
import { getInfNodes, getNemesisPasscode } from "@/src/helpers/nemesisHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { SRng } from "@/src/services/rngService";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import {
|
||||
IInnateDamageFingerprint,
|
||||
IInventoryClient,
|
||||
INemesisClient,
|
||||
InventorySlot,
|
||||
IUpgradeClient,
|
||||
IWeaponSkinClient,
|
||||
LoadoutIndex,
|
||||
TEquipmentKey
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInnateDamageFingerprint, InventorySlot, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
@ -70,7 +49,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
} else if ((req.query.mode as string) == "p") {
|
||||
const inventory = await getInventory(accountId, "Nemesis");
|
||||
const body = getJSONfromString<INemesisPrespawnCheckRequest>(String(req.body));
|
||||
const passcode = getNemesisPasscode(inventory.Nemesis!);
|
||||
const passcode = getNemesisPasscode(inventory.Nemesis!.fp, inventory.Nemesis!.Faction);
|
||||
let guessResult = 0;
|
||||
if (inventory.Nemesis!.Faction == "FC_INFESTATION") {
|
||||
for (let i = 0; i != 3; ++i) {
|
||||
@ -87,83 +66,6 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
res.json({ GuessResult: guessResult });
|
||||
} else if (req.query.mode == "r") {
|
||||
const inventory = await getInventory(
|
||||
accountId,
|
||||
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
|
||||
);
|
||||
const body = getJSONfromString<INemesisRequiemRequest>(String(req.body));
|
||||
if (inventory.Nemesis!.Faction == "FC_INFESTATION") {
|
||||
const guess: number[] = [body.guess & 0xf, (body.guess >> 4) & 0xf, (body.guess >> 8) & 0xf];
|
||||
const passcode = getNemesisPasscode(inventory.Nemesis!)[0];
|
||||
|
||||
// Add to GuessHistory
|
||||
const result1 = passcode == guess[0] ? 0 : 1;
|
||||
const result2 = passcode == guess[1] ? 0 : 1;
|
||||
const result3 = passcode == guess[2] ? 0 : 1;
|
||||
inventory.Nemesis!.GuessHistory.push(
|
||||
encodeNemesisGuess(guess[0], result1, guess[1], result2, guess[2], result3)
|
||||
);
|
||||
|
||||
// Increase antivirus if correct antivirus mod is installed
|
||||
const response: IKnifeResponse = {};
|
||||
if (result1 == 0 || result2 == 0 || result3 == 0) {
|
||||
let antivirusGain = 5;
|
||||
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!;
|
||||
for (const upgrade of body.knife!.AttachedUpgrades) {
|
||||
switch (upgrade.ItemType) {
|
||||
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod":
|
||||
antivirusGain += 10;
|
||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||
break;
|
||||
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod":
|
||||
antivirusGain += 10;
|
||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||
break;
|
||||
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod": // Instant Secure
|
||||
antivirusGain += 15;
|
||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||
break;
|
||||
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod": // Immuno Shield
|
||||
antivirusGain += 15;
|
||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||
break;
|
||||
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod":
|
||||
antivirusGain += 10;
|
||||
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inventory.Nemesis!.HenchmenKilled += antivirusGain;
|
||||
}
|
||||
|
||||
if (inventory.Nemesis!.HenchmenKilled >= 100) {
|
||||
inventory.Nemesis!.HenchmenKilled = 100;
|
||||
}
|
||||
inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
|
||||
|
||||
await inventory.save();
|
||||
res.json(response);
|
||||
} else {
|
||||
const passcode = getNemesisPasscode(inventory.Nemesis!);
|
||||
if (passcode[body.position] != body.guess) {
|
||||
res.end();
|
||||
} else {
|
||||
inventory.Nemesis!.Rank += 1;
|
||||
inventory.Nemesis!.InfNodes = getInfNodes(inventory.Nemesis!.Faction, inventory.Nemesis!.Rank);
|
||||
await inventory.save();
|
||||
res.json({ RankIncrease: 1 });
|
||||
}
|
||||
}
|
||||
} else if ((req.query.mode as string) == "rs") {
|
||||
// report spawn; POST but no application data in body
|
||||
const inventory = await getInventory(accountId, "Nemesis");
|
||||
inventory.Nemesis!.LastEnc = inventory.Nemesis!.MissionCount;
|
||||
await inventory.save();
|
||||
res.json({ LastEnc: inventory.Nemesis!.LastEnc });
|
||||
} else if ((req.query.mode as string) == "s") {
|
||||
const inventory = await getInventory(accountId, "Nemesis");
|
||||
const body = getJSONfromString<INemesisStartRequest>(String(req.body));
|
||||
@ -171,7 +73,18 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
|
||||
let weaponIdx = -1;
|
||||
if (body.target.Faction != "FC_INFESTATION") {
|
||||
const weapons = getWeaponsForManifest(body.target.manifest);
|
||||
let weapons: readonly string[];
|
||||
if (body.target.manifest == "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix") {
|
||||
weapons = kuvaLichVersionSixWeapons;
|
||||
} else if (
|
||||
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour" ||
|
||||
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"
|
||||
) {
|
||||
weapons = corpusVersionThreeWeapons;
|
||||
} else {
|
||||
throw new Error(`unknown nemesis manifest: ${body.target.manifest}`);
|
||||
}
|
||||
|
||||
const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
|
||||
weaponIdx = initialWeaponIdx;
|
||||
do {
|
||||
@ -213,38 +126,6 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
target: inventory.toJSON().Nemesis
|
||||
});
|
||||
} else if ((req.query.mode as string) == "w") {
|
||||
const inventory = await getInventory(
|
||||
accountId,
|
||||
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
|
||||
);
|
||||
//const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
|
||||
|
||||
inventory.Nemesis!.InfNodes = [
|
||||
{
|
||||
Node: showdownNodes[inventory.Nemesis!.Faction],
|
||||
Influence: 1
|
||||
}
|
||||
];
|
||||
inventory.Nemesis!.Weakened = true;
|
||||
|
||||
const response: IKnifeResponse & { target: INemesisClient } = {
|
||||
target: inventory.toJSON<IInventoryClient>().Nemesis!
|
||||
};
|
||||
|
||||
// Consume charge of the correct requiem mod(s)
|
||||
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();
|
||||
res.json(response);
|
||||
} else {
|
||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||
throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
|
||||
@ -292,23 +173,38 @@ interface INemesisPrespawnCheckRequest {
|
||||
potency?: number[];
|
||||
}
|
||||
|
||||
interface INemesisRequiemRequest {
|
||||
guess: number; // grn/crp: 4 bits | coda: 3x 4 bits
|
||||
position: number; // grn/crp: 0-2 | coda: 0
|
||||
// knife field provided for coda only
|
||||
knife?: IKnife;
|
||||
}
|
||||
const kuvaLichVersionSixWeapons = [
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
|
||||
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
|
||||
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
|
||||
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
|
||||
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
|
||||
];
|
||||
|
||||
// interface INemesisWeakenRequest {
|
||||
// target: INemesisClient;
|
||||
// knife: IKnife;
|
||||
// }
|
||||
|
||||
interface IKnife {
|
||||
Item: IEquipmentClient;
|
||||
Skins: IWeaponSkinClient[];
|
||||
ModSlot: number;
|
||||
CustSlot: number;
|
||||
AttachedUpgrades: IUpgradeClient[];
|
||||
HiddenWhenHolstered: boolean;
|
||||
}
|
||||
const corpusVersionThreeWeapons = [
|
||||
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
|
||||
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
|
||||
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
|
||||
];
|
||||
|
@ -13,7 +13,6 @@ import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { ExportDojoRecipes, ExportResources } from "warframe-public-export-plus";
|
||||
import { config } from "@/src/services/configService";
|
||||
|
||||
export const placeDecoInComponentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -37,7 +36,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
|
||||
deco.Pos = request.Pos;
|
||||
deco.Rot = request.Rot;
|
||||
deco.Scale = request.Scale;
|
||||
} else {
|
||||
const deco =
|
||||
component.Decos[
|
||||
@ -46,7 +44,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
Type: request.Type,
|
||||
Pos: request.Pos,
|
||||
Rot: request.Rot,
|
||||
Scale: request.Scale,
|
||||
Name: request.Name,
|
||||
Sockets: request.Sockets
|
||||
}) - 1
|
||||
@ -65,13 +62,14 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
guild.VaultShipDecorations!.find(x => x.ItemType == itemType)!.ItemCount -= 1;
|
||||
}
|
||||
}
|
||||
if (deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco") {
|
||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0) || config.noDojoDecoBuildStage) {
|
||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0)) {
|
||||
deco.CompletionTime = new Date();
|
||||
if (meta) {
|
||||
processDojoBuildMaterialsGathered(guild, meta);
|
||||
}
|
||||
} else if (guild.AutoContributeFromVault && guild.VaultRegularCredits && guild.VaultMiscItems) {
|
||||
} else if (
|
||||
guild.AutoContributeFromVault &&
|
||||
guild.VaultRegularCredits &&
|
||||
guild.VaultMiscItems &&
|
||||
deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco"
|
||||
) {
|
||||
if (guild.VaultRegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) {
|
||||
let enoughMiscItems = true;
|
||||
for (const ingredient of meta.ingredients) {
|
||||
@ -103,7 +101,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, component._id));
|
||||
@ -115,9 +112,9 @@ interface IPlaceDecoInComponentRequest {
|
||||
Type: string;
|
||||
Pos: number[];
|
||||
Rot: number[];
|
||||
Scale?: number;
|
||||
Name?: string;
|
||||
Sockets?: number;
|
||||
Scale?: number; // only provided alongside MoveId and seems to always be 1
|
||||
MoveId?: string;
|
||||
ShipDeco?: boolean;
|
||||
VaultDeco?: boolean;
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const playedParkourTutorialController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
await Inventory.updateOne({ accountOwnerId: accountId }, { PlayedParkourTutorial: true });
|
||||
res.end();
|
||||
};
|
@ -8,11 +8,7 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
||||
const inventory = await getInventory(accountId, "RegularCredits KubrowPets");
|
||||
const payload = getJSONfromString<IReleasePetRequest>(String(req.body));
|
||||
|
||||
const inventoryChanges = updateCurrency(
|
||||
inventory,
|
||||
payload.recipeName == "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" ? 25000 : 0,
|
||||
false
|
||||
);
|
||||
const inventoryChanges = updateCurrency(inventory, 25000, false);
|
||||
|
||||
inventoryChanges.RemovedIdItems = [{ ItemId: { $oid: payload.petId } }];
|
||||
inventory.KubrowPets.pull({ _id: payload.petId });
|
||||
@ -22,6 +18,6 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
||||
};
|
||||
|
||||
interface IReleasePetRequest {
|
||||
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" | "webui";
|
||||
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe";
|
||||
petId: string;
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Account, Ignore } from "@/src/models/loginModel";
|
||||
import { getAccountForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const removeIgnoredUserController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountForRequest(req);
|
||||
const data = getJSONfromString<IRemoveIgnoredUserRequest>(String(req.body));
|
||||
const ignoreeAccount = await Account.findOne(
|
||||
{ DisplayName: data.playerName.substring(0, data.playerName.length - 1) },
|
||||
"_id"
|
||||
);
|
||||
if (ignoreeAccount) {
|
||||
await Ignore.deleteOne({ ignorer: accountId, ignoree: ignoreeAccount._id });
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IRemoveIgnoredUserRequest {
|
||||
playerName: string;
|
||||
}
|
@ -1,29 +1,51 @@
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { addEmailItem, getInventory } from "@/src/services/inventoryService";
|
||||
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 { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = JSON.parse(String(req.body)) as SaveDialogueRequest;
|
||||
if ("YearIteration" in request) {
|
||||
const inventory = await getInventory(accountId, "DialogueHistory");
|
||||
inventory.DialogueHistory ??= {};
|
||||
const inventory = await getInventory(accountId);
|
||||
if (inventory.DialogueHistory) {
|
||||
inventory.DialogueHistory.YearIteration = request.YearIteration;
|
||||
} else {
|
||||
inventory.DialogueHistory = { YearIteration: request.YearIteration };
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
} else {
|
||||
const inventory = await getInventory(accountId);
|
||||
if (!inventory.DialogueHistory) {
|
||||
throw new Error("bad inventory state");
|
||||
}
|
||||
if (request.OtherDialogueInfos.length != 0) {
|
||||
logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
|
||||
}
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const tomorrowAt0Utc = config.noKimCooldowns
|
||||
? Date.now()
|
||||
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
|
||||
inventory.DialogueHistory ??= {};
|
||||
inventory.DialogueHistory.Dialogues ??= [];
|
||||
const dialogue = getDialogue(inventory, request.DialogueName);
|
||||
let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.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: request.DialogueName
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
dialogue.Rank = request.Rank;
|
||||
dialogue.Chemistry = request.Chemistry;
|
||||
dialogue.QueuedDialogues = request.QueuedDialogues;
|
||||
@ -43,38 +65,14 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
dialogue.Booleans.splice(index, 1);
|
||||
}
|
||||
}
|
||||
for (const info of request.OtherDialogueInfos) {
|
||||
const otherDialogue = getDialogue(inventory, info.Dialogue);
|
||||
if (info.Tag != "") {
|
||||
otherDialogue.QueuedDialogues.push(info.Tag);
|
||||
}
|
||||
otherDialogue.Chemistry += info.Value; // unsure
|
||||
}
|
||||
if (request.Data) {
|
||||
dialogue.Completed.push(request.Data);
|
||||
const tomorrowAt0Utc = (Math.trunc(Date.now() / (86400 * 1000)) + 1) * 86400 * 1000;
|
||||
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||
});
|
||||
} else if (request.Gift) {
|
||||
const inventoryChanges = updateCurrency(inventory, request.Gift.Cost, false);
|
||||
const gift = dialogue.Gifts.find(x => x.Item == request.Gift!.Item);
|
||||
if (gift) {
|
||||
gift.GiftedQuantity += 1;
|
||||
} else {
|
||||
dialogue.Gifts.push({ Item: request.Gift.Item, GiftedQuantity: 1 });
|
||||
}
|
||||
dialogue.AvailableGiftDate = new Date(tomorrowAt0Utc);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||
});
|
||||
} else {
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -90,16 +88,10 @@ interface SaveCompletedDialogueRequest {
|
||||
Chemistry: number;
|
||||
CompletionType: number;
|
||||
QueuedDialogues: string[];
|
||||
Gift?: {
|
||||
Item: string;
|
||||
GainedChemistry: number;
|
||||
Cost: number;
|
||||
GiftedQuantity: number;
|
||||
};
|
||||
Booleans: string[];
|
||||
ResetBooleans: string[];
|
||||
Data?: ICompletedDialogue;
|
||||
OtherDialogueInfos: IOtherDialogueInfo[];
|
||||
Data: ICompletedDialogue;
|
||||
OtherDialogueInfos: IOtherDialogueInfo[]; // unsure
|
||||
}
|
||||
|
||||
interface IOtherDialogueInfo {
|
||||
@ -107,26 +99,3 @@ interface IOtherDialogueInfo {
|
||||
Tag: string;
|
||||
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;
|
||||
};
|
||||
|
32
src/controllers/api/saveLoadout.ts
Normal file
32
src/controllers/api/saveLoadout.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
||||
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
|
||||
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||
//validate here
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
try {
|
||||
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
|
||||
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { UpgradeVer, ...equipmentChanges } = body;
|
||||
const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
||||
|
||||
//send back new loadout id, if new loadout was added
|
||||
if (newLoadoutId) {
|
||||
res.send(newLoadoutId);
|
||||
}
|
||||
res.status(200).end();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
logger.error(`error in saveLoadoutController: ${error.message}`);
|
||||
res.status(400).json({ error: error.message });
|
||||
} else {
|
||||
res.status(400).json({ error: "unknown error" });
|
||||
}
|
||||
}
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
||||
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
|
||||
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
|
||||
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { UpgradeVer, ...equipmentChanges } = body;
|
||||
const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
||||
|
||||
//send back new loadout id, if new loadout was added
|
||||
if (newLoadoutId) {
|
||||
res.send(newLoadoutId);
|
||||
}
|
||||
res.end();
|
||||
};
|
@ -6,20 +6,14 @@ import {
|
||||
addRecipes,
|
||||
addMiscItems,
|
||||
addConsumables,
|
||||
freeUpSlot,
|
||||
combineInventoryChanges,
|
||||
addCrewShipRawSalvage,
|
||||
addFusionPoints
|
||||
freeUpSlot
|
||||
} from "@/src/services/inventoryService";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
|
||||
export const sellController: RequestHandler = async (req, res) => {
|
||||
const payload = JSON.parse(String(req.body)) as ISellRequest;
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requiredFields = new Set<keyof TInventoryDatabaseDocument>();
|
||||
const requiredFields = new Set();
|
||||
if (payload.SellCurrency == "SC_RegularCredits") {
|
||||
requiredFields.add("RegularCredits");
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
@ -28,7 +22,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
requiredFields.add("MiscItems");
|
||||
}
|
||||
for (const key of Object.keys(payload.Items)) {
|
||||
requiredFields.add(key as keyof TInventoryDatabaseDocument);
|
||||
requiredFields.add(key);
|
||||
}
|
||||
if (requiredFields.has("Upgrades")) {
|
||||
requiredFields.add("RawUpgrades");
|
||||
@ -45,7 +39,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
||||
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
||||
}
|
||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) {
|
||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons) {
|
||||
requiredFields.add(InventorySlot.SENTINELS);
|
||||
}
|
||||
if (payload.Items.OperatorAmps) {
|
||||
@ -54,23 +48,13 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
if (payload.Items.Hoverboards) {
|
||||
requiredFields.add(InventorySlot.SPACESUITS);
|
||||
}
|
||||
if (payload.Items.CrewShipWeapons || payload.Items.CrewShipWeaponSkins) {
|
||||
requiredFields.add(InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||
requiredFields.add("CrewShipRawSalvage");
|
||||
if (payload.Items.CrewShipWeapons) {
|
||||
requiredFields.add("CrewShipSalvagedWeapons");
|
||||
}
|
||||
if (payload.Items.CrewShipWeaponSkins) {
|
||||
requiredFields.add("CrewShipSalvagedWeaponSkins");
|
||||
}
|
||||
}
|
||||
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
|
||||
|
||||
// Give currency
|
||||
if (payload.SellCurrency == "SC_RegularCredits") {
|
||||
inventory.RegularCredits += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
addFusionPoints(inventory, payload.SellPrice);
|
||||
inventory.FusionPoints += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -85,14 +69,10 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
ItemCount: payload.SellPrice
|
||||
}
|
||||
]);
|
||||
} else if (payload.SellCurrency == "SC_Resources") {
|
||||
// Will add appropriate MiscItems from CrewShipWeapons or CrewShipWeaponSkins
|
||||
} else {
|
||||
throw new Error("Unknown SellCurrency: " + payload.SellCurrency);
|
||||
}
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
|
||||
// Remove item(s)
|
||||
if (payload.Items.Suits) {
|
||||
payload.Items.Suits.forEach(sellItem => {
|
||||
@ -148,12 +128,6 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
freeUpSlot(inventory, InventorySlot.SENTINELS);
|
||||
});
|
||||
}
|
||||
if (payload.Items.MoaPets) {
|
||||
payload.Items.MoaPets.forEach(sellItem => {
|
||||
inventory.MoaPets.pull({ _id: sellItem.String });
|
||||
freeUpSlot(inventory, InventorySlot.SENTINELS);
|
||||
});
|
||||
}
|
||||
if (payload.Items.OperatorAmps) {
|
||||
payload.Items.OperatorAmps.forEach(sellItem => {
|
||||
inventory.OperatorAmps.pull({ _id: sellItem.String });
|
||||
@ -171,56 +145,6 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
inventory.Drones.pull({ _id: sellItem.String });
|
||||
});
|
||||
}
|
||||
if (payload.Items.CrewShipWeapons) {
|
||||
payload.Items.CrewShipWeapons.forEach(sellItem => {
|
||||
if (sellItem.String[0] == "/") {
|
||||
addCrewShipRawSalvage(inventory, [
|
||||
{
|
||||
ItemType: sellItem.String,
|
||||
ItemCount: sellItem.Count * -1
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
const index = inventory.CrewShipWeapons.findIndex(x => x._id.equals(sellItem.String));
|
||||
if (index != -1) {
|
||||
if (payload.SellCurrency == "SC_Resources") {
|
||||
refundPartialBuildCosts(inventory, inventory.CrewShipWeapons[index].ItemType, inventoryChanges);
|
||||
}
|
||||
inventory.CrewShipWeapons.splice(index, 1);
|
||||
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||
} else {
|
||||
inventory.CrewShipSalvagedWeapons.pull({ _id: sellItem.String });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (payload.Items.CrewShipWeaponSkins) {
|
||||
payload.Items.CrewShipWeaponSkins.forEach(sellItem => {
|
||||
if (sellItem.String[0] == "/") {
|
||||
addCrewShipRawSalvage(inventory, [
|
||||
{
|
||||
ItemType: sellItem.String,
|
||||
ItemCount: sellItem.Count * -1
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
const index = inventory.CrewShipWeaponSkins.findIndex(x => x._id.equals(sellItem.String));
|
||||
if (index != -1) {
|
||||
if (payload.SellCurrency == "SC_Resources") {
|
||||
refundPartialBuildCosts(
|
||||
inventory,
|
||||
inventory.CrewShipWeaponSkins[index].ItemType,
|
||||
inventoryChanges
|
||||
);
|
||||
}
|
||||
inventory.CrewShipWeaponSkins.splice(index, 1);
|
||||
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||
} else {
|
||||
inventory.CrewShipSalvagedWeaponSkins.pull({ _id: sellItem.String });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (payload.Items.Consumables) {
|
||||
const consumablesChanges = [];
|
||||
for (const sellItem of payload.Items.Consumables) {
|
||||
@ -267,9 +191,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
|
||||
});
|
||||
res.json({});
|
||||
};
|
||||
|
||||
interface ISellRequest {
|
||||
@ -287,12 +209,9 @@ interface ISellRequest {
|
||||
SpaceMelee?: ISellItem[];
|
||||
Sentinels?: ISellItem[];
|
||||
SentinelWeapons?: ISellItem[];
|
||||
MoaPets?: ISellItem[];
|
||||
OperatorAmps?: ISellItem[];
|
||||
Hoverboards?: ISellItem[];
|
||||
Drones?: ISellItem[];
|
||||
CrewShipWeapons?: ISellItem[];
|
||||
CrewShipWeaponSkins?: ISellItem[];
|
||||
};
|
||||
SellPrice: number;
|
||||
SellCurrency:
|
||||
@ -309,33 +228,3 @@ interface ISellItem {
|
||||
String: string; // oid or uniqueName
|
||||
Count: number;
|
||||
}
|
||||
|
||||
const refundPartialBuildCosts = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
itemType: string,
|
||||
inventoryChanges: IInventoryChanges
|
||||
): void => {
|
||||
// House versions
|
||||
const research = Object.values(ExportDojoRecipes.research).find(x => x.resultType == itemType);
|
||||
if (research) {
|
||||
const miscItemChanges = research.ingredients.map(x => ({
|
||||
ItemType: x.ItemType,
|
||||
ItemCount: Math.trunc(x.ItemCount * 0.8)
|
||||
}));
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||
return;
|
||||
}
|
||||
|
||||
// Sigma versions
|
||||
const recipe = Object.values(ExportDojoRecipes.fabrications).find(x => x.resultType == itemType);
|
||||
if (recipe) {
|
||||
const miscItemChanges = recipe.ingredients.map(x => ({
|
||||
ItemType: x.ItemType,
|
||||
ItemCount: Math.trunc(x.ItemCount * 0.8)
|
||||
}));
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const sendMsgToInBoxController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const data = getJSONfromString<ISendMsgToInBoxRequest>(String(req.body));
|
||||
await createMessage(accountId, [
|
||||
{
|
||||
sub: data.title,
|
||||
msg: data.message,
|
||||
sndr: data.sender ?? "/Lotus/Language/Bosses/Ordis",
|
||||
icon: data.senderIcon,
|
||||
highPriority: data.highPriority,
|
||||
transmission: data.transmission,
|
||||
att: data.attachments
|
||||
}
|
||||
]);
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface ISendMsgToInBoxRequest {
|
||||
title: string;
|
||||
message: string;
|
||||
sender?: string;
|
||||
senderIcon?: string;
|
||||
highPriority?: boolean;
|
||||
transmission?: string;
|
||||
attachments?: string[];
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IHubNpcCustomization } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setHubNpcCustomizationsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "HubNpcCustomizations");
|
||||
const upload = getJSONfromString<IHubNpcCustomization>(String(req.body));
|
||||
inventory.HubNpcCustomizations ??= [];
|
||||
const cust = inventory.HubNpcCustomizations.find(x => x.Tag == upload.Tag);
|
||||
if (cust) {
|
||||
cust.Colors = upload.Colors;
|
||||
cust.Pattern = upload.Pattern;
|
||||
} else {
|
||||
inventory.HubNpcCustomizations.push(upload);
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IPictureFrameInfo, ISetPlacedDecoInfoRequest } from "@/src/types/shipTypes";
|
||||
import { ISetPlacedDecoInfoRequest } from "@/src/types/shipTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { handleSetPlacedDecoInfo } from "@/src/services/shipCustomizationsService";
|
||||
|
||||
@ -7,17 +7,5 @@ export const setPlacedDecoInfoController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = JSON.parse(req.body as string) as ISetPlacedDecoInfoRequest;
|
||||
await handleSetPlacedDecoInfo(accountId, payload);
|
||||
res.json({
|
||||
DecoId: payload.DecoId,
|
||||
IsPicture: true,
|
||||
PictureFrameInfo: payload.PictureFrameInfo,
|
||||
BootLocation: payload.BootLocation
|
||||
} satisfies ISetPlacedDecoInfoResponse);
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface ISetPlacedDecoInfoResponse {
|
||||
DecoId: string;
|
||||
IsPicture: boolean;
|
||||
PictureFrameInfo?: IPictureFrameInfo;
|
||||
BootLocation?: string;
|
||||
}
|
||||
|
@ -3,40 +3,29 @@ import { RequestHandler } from "express";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { Types } from "mongoose";
|
||||
import { IFavouriteLoadoutDatabase, TBootLocation } from "@/src/types/shipTypes";
|
||||
|
||||
export const setShipFavouriteLoadoutController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
const body = JSON.parse(String(req.body)) as ISetShipFavouriteLoadoutRequest;
|
||||
if (body.BootLocation == "LISET") {
|
||||
personalRooms.Ship.FavouriteLoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
|
||||
} else if (body.BootLocation == "APARTMENT") {
|
||||
updateTaggedDisplay(personalRooms.Apartment.FavouriteLoadouts, body);
|
||||
} else if (body.BootLocation == "SHOP") {
|
||||
updateTaggedDisplay(personalRooms.TailorShop.FavouriteLoadouts, body);
|
||||
} else {
|
||||
console.log(body);
|
||||
if (body.BootLocation != "SHOP") {
|
||||
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
||||
}
|
||||
await personalRooms.save();
|
||||
res.json(body);
|
||||
};
|
||||
|
||||
interface ISetShipFavouriteLoadoutRequest {
|
||||
BootLocation: TBootLocation;
|
||||
FavouriteLoadoutId: IOid;
|
||||
TagName?: string;
|
||||
}
|
||||
|
||||
const updateTaggedDisplay = (arr: IFavouriteLoadoutDatabase[], body: ISetShipFavouriteLoadoutRequest): void => {
|
||||
const display = arr.find(x => x.Tag == body.TagName!);
|
||||
const display = personalRooms.TailorShop.FavouriteLoadouts.find(x => x.Tag == body.TagName);
|
||||
if (display) {
|
||||
display.LoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
|
||||
} else {
|
||||
arr.push({
|
||||
Tag: body.TagName!,
|
||||
personalRooms.TailorShop.FavouriteLoadouts.push({
|
||||
Tag: body.TagName,
|
||||
LoadoutId: new Types.ObjectId(body.FavouriteLoadoutId.$oid)
|
||||
});
|
||||
}
|
||||
await personalRooms.save();
|
||||
res.json({});
|
||||
};
|
||||
|
||||
interface ISetShipFavouriteLoadoutRequest {
|
||||
BootLocation: string;
|
||||
FavouriteLoadoutId: IOid;
|
||||
TagName: string;
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
import { addMiscItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setShipVignetteController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "MiscItems");
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
const body = JSON.parse(String(req.body)) as ISetShipVignetteRequest;
|
||||
personalRooms.Ship.Wallpaper = body.Wallpaper;
|
||||
personalRooms.Ship.Vignette = body.Vignette;
|
||||
personalRooms.Ship.VignetteFish ??= [];
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
for (let i = 0; i != body.Fish.length; ++i) {
|
||||
if (body.Fish[i] && !personalRooms.Ship.VignetteFish[i]) {
|
||||
logger.debug(`moving ${body.Fish[i]} from inventory to vignette slot ${i}`);
|
||||
const miscItemsDelta = [{ ItemType: body.Fish[i], ItemCount: -1 }];
|
||||
addMiscItems(inventory, miscItemsDelta);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
|
||||
} else if (personalRooms.Ship.VignetteFish[i] && !body.Fish[i]) {
|
||||
logger.debug(`moving ${personalRooms.Ship.VignetteFish[i]} from vignette slot ${i} to inventory`);
|
||||
const miscItemsDelta = [{ ItemType: personalRooms.Ship.VignetteFish[i], ItemCount: +1 }];
|
||||
addMiscItems(inventory, miscItemsDelta);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
|
||||
}
|
||||
}
|
||||
personalRooms.Ship.VignetteFish = body.Fish;
|
||||
if (body.VignetteDecos.length) {
|
||||
logger.error(`setShipVignette request not fully handled:`, body);
|
||||
}
|
||||
await Promise.all([inventory.save(), personalRooms.save()]);
|
||||
res.json({
|
||||
Wallpaper: body.Wallpaper,
|
||||
Vignette: body.Vignette,
|
||||
VignetteFish: body.Fish,
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
};
|
||||
|
||||
interface ISetShipVignetteRequest {
|
||||
Wallpaper: string;
|
||||
Vignette: string;
|
||||
Fish: string[];
|
||||
VignetteDecos: unknown[];
|
||||
}
|
@ -3,12 +3,15 @@ import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ExportNightwave, ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
|
||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import {
|
||||
addItem,
|
||||
addMiscItems,
|
||||
combineInventoryChanges,
|
||||
getInventory,
|
||||
updateCurrency
|
||||
} from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { isStoreItem, toStoreItem } from "@/src/services/itemDataService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
|
||||
const nightwaveCredsItemType = ExportNightwave.rewards[ExportNightwave.rewards.length - 1].uniqueName;
|
||||
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
|
||||
|
||||
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
|
||||
const accountId = await getAccountIdForRequest(request);
|
||||
@ -54,7 +57,7 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
|
||||
syndicate.Title ??= 0;
|
||||
syndicate.Title += 1;
|
||||
|
||||
if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
|
||||
if (syndicate.Title > 0 && manifest.favours.length != 0) {
|
||||
syndicate.FreeFavorsEarned ??= [];
|
||||
if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
|
||||
syndicate.FreeFavorsEarned.push(syndicate.Title);
|
||||
@ -74,17 +77,10 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
|
||||
res.NewEpisodeReward = true;
|
||||
const reward = ExportNightwave.rewards[index];
|
||||
let rewardType = reward.uniqueName;
|
||||
if (!isStoreItem(rewardType)) {
|
||||
rewardType = toStoreItem(rewardType);
|
||||
if (isStoreItem(rewardType)) {
|
||||
rewardType = fromStoreItem(rewardType);
|
||||
}
|
||||
const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount))
|
||||
.InventoryChanges;
|
||||
if (Object.keys(rewardInventoryChanges).length == 0) {
|
||||
logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`);
|
||||
rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
|
||||
addMiscItems(inventory, rewardInventoryChanges.MiscItems);
|
||||
}
|
||||
combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
|
||||
combineInventoryChanges(res.InventoryChanges, await addItem(inventory, rewardType, reward.itemCount ?? 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,16 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addMiscItems, addStanding, freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
||||
import {
|
||||
addMiscItems,
|
||||
freeUpSlot,
|
||||
getInventory,
|
||||
getStandingLimit,
|
||||
updateStandingLimit
|
||||
} from "@/src/services/inventoryService";
|
||||
import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { ExportSyndicates, ExportWeapons } from "warframe-public-export-plus";
|
||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
@ -54,13 +61,38 @@ export const syndicateStandingBonusController: RequestHandler = async (req, res)
|
||||
inventoryChanges[slotBin] = { count: -1, platinum: 0, Slots: 1 };
|
||||
}
|
||||
|
||||
const affiliationMod = addStanding(inventory, request.Operation.AffiliationTag, gainedStanding, true);
|
||||
let syndicate = inventory.Affiliations.find(x => x.Tag == request.Operation.AffiliationTag);
|
||||
if (!syndicate) {
|
||||
syndicate =
|
||||
inventory.Affiliations[
|
||||
inventory.Affiliations.push({ Tag: request.Operation.AffiliationTag, Standing: 0 }) - 1
|
||||
];
|
||||
}
|
||||
|
||||
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
|
||||
if (syndicate.Standing + gainedStanding > max) {
|
||||
gainedStanding = max - syndicate.Standing;
|
||||
}
|
||||
|
||||
if (syndicateMeta.medallionsCappedByDailyLimit) {
|
||||
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
|
||||
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
|
||||
}
|
||||
updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
|
||||
}
|
||||
|
||||
syndicate.Standing += gainedStanding;
|
||||
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
AffiliationMods: [affiliationMod]
|
||||
AffiliationMods: [
|
||||
{
|
||||
Tag: request.Operation.AffiliationTag,
|
||||
Standing: gainedStanding
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -25,13 +25,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
|
||||
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker"
|
||||
) {
|
||||
updateCurrency(inventory, 10, true);
|
||||
} else if (
|
||||
operation.OperationType != "UOT_SWAP_POLARITY" &&
|
||||
operation.OperationType != "UOT_ABILITY_OVERRIDE"
|
||||
) {
|
||||
if (!operation.UpgradeRequirement) {
|
||||
throw new Error(`${operation.OperationType} operation should be free?`);
|
||||
}
|
||||
} else if (operation.OperationType != "UOT_ABILITY_OVERRIDE") {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: operation.UpgradeRequirement,
|
||||
|
@ -1,16 +1,12 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const addCurrencyController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const request = req.body as IAddCurrencyRequest;
|
||||
const inventory = await getInventory(accountId, request.currency);
|
||||
if (request.currency == "FusionPoints") {
|
||||
addFusionPoints(inventory, request.delta);
|
||||
} else {
|
||||
inventory[request.currency] += request.delta;
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus";
|
||||
|
||||
export const addMissingMaxRankModsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "Upgrades");
|
||||
|
||||
const maxOwnedRanks: Record<string, number> = {};
|
||||
for (const upgrade of inventory.Upgrades) {
|
||||
const fingerprint = JSON.parse(upgrade.UpgradeFingerprint ?? "{}") as { lvl?: number };
|
||||
if (fingerprint.lvl) {
|
||||
maxOwnedRanks[upgrade.ItemType] ??= 0;
|
||||
if (fingerprint.lvl > maxOwnedRanks[upgrade.ItemType]) {
|
||||
maxOwnedRanks[upgrade.ItemType] = fingerprint.lvl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [uniqueName, data] of Object.entries(ExportUpgrades)) {
|
||||
if (data.fusionLimit != 0 && data.type != "PARAZON" && maxOwnedRanks[uniqueName] != data.fusionLimit) {
|
||||
inventory.Upgrades.push({
|
||||
ItemType: uniqueName,
|
||||
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [uniqueName, data] of Object.entries(ExportArcanes)) {
|
||||
if (
|
||||
data.name != "/Lotus/Language/Items/GenericCosmeticEnhancerName" &&
|
||||
maxOwnedRanks[uniqueName] != data.fusionLimit
|
||||
) {
|
||||
inventory.Upgrades.push({
|
||||
ItemType: uniqueName,
|
||||
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
98
src/controllers/custom/addModularEquipmentController.ts
Normal file
98
src/controllers/custom/addModularEquipmentController.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import {
|
||||
getInventory,
|
||||
addEquipment,
|
||||
occupySlot,
|
||||
productCategoryToInventoryBin,
|
||||
applyDefaultUpgrades
|
||||
} from "@/src/services/inventoryService";
|
||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||
import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { ExportWeapons } from "warframe-public-export-plus";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const addModularEquipmentController: RequestHandler = async (req, res) => {
|
||||
const requiredFields = new Set();
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IAddModularEquipmentRequest;
|
||||
const category = modularWeaponTypes[request.ItemType];
|
||||
const inventoryBin = productCategoryToInventoryBin(category)!;
|
||||
requiredFields.add(category);
|
||||
requiredFields.add(inventoryBin);
|
||||
|
||||
request.ModularParts.forEach(part => {
|
||||
if (ExportWeapons[part].gunType) {
|
||||
if (category == "LongGuns") {
|
||||
request.ItemType = {
|
||||
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam"
|
||||
}[ExportWeapons[part].gunType];
|
||||
} else {
|
||||
request.ItemType = {
|
||||
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
|
||||
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
|
||||
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||
}[ExportWeapons[part].gunType];
|
||||
}
|
||||
} else if (request.ItemType == "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit") {
|
||||
if (part.includes("ZanukaPetPartHead")) {
|
||||
request.ItemType = {
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
||||
}[part]!;
|
||||
}
|
||||
}
|
||||
});
|
||||
const defaultUpgrades = getDefaultUpgrades(request.ModularParts);
|
||||
if (defaultUpgrades) {
|
||||
requiredFields.add("RawUpgrades");
|
||||
}
|
||||
const defaultWeaponsMap: Record<string, string[]> = {
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit": [
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIP"
|
||||
],
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit": [
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIS"
|
||||
],
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit": [
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponPS"
|
||||
]
|
||||
};
|
||||
const defaultWeapons = defaultWeaponsMap[request.ItemType] as string[] | undefined;
|
||||
if (defaultWeapons) {
|
||||
for (const defaultWeapon of defaultWeapons) {
|
||||
const category = ExportWeapons[defaultWeapon].productCategory;
|
||||
requiredFields.add(category);
|
||||
requiredFields.add(productCategoryToInventoryBin(category));
|
||||
}
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
|
||||
if (defaultWeapons) {
|
||||
for (const defaultWeapon of defaultWeapons) {
|
||||
const category = ExportWeapons[defaultWeapon].productCategory;
|
||||
addEquipment(inventory, category, defaultWeapon);
|
||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, true);
|
||||
}
|
||||
}
|
||||
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
Configs: applyDefaultUpgrades(inventory, defaultUpgrades)
|
||||
};
|
||||
|
||||
addEquipment(inventory, category, request.ItemType, request.ModularParts, undefined, defaultOverwrites);
|
||||
occupySlot(inventory, inventoryBin, true);
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IAddModularEquipmentRequest {
|
||||
ItemType: string;
|
||||
ModularParts: string[];
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { Account, Ignore } from "@/src/models/loginModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||
@ -23,8 +23,6 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
await Promise.all([
|
||||
Account.deleteOne({ _id: accountId }),
|
||||
GuildMember.deleteMany({ accountId: accountId }),
|
||||
Ignore.deleteMany({ ignorer: accountId }),
|
||||
Ignore.deleteMany({ ignoree: accountId }),
|
||||
Inbox.deleteMany({ ownerId: accountId }),
|
||||
Inventory.deleteOne({ accountOwnerId: accountId }),
|
||||
Leaderboard.deleteMany({ ownerId: accountId }),
|
||||
|
@ -3,7 +3,6 @@ import { getDict, getItemName, getString } from "@/src/services/itemDataService"
|
||||
import {
|
||||
ExportArcanes,
|
||||
ExportAvionics,
|
||||
ExportCustoms,
|
||||
ExportDrones,
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
@ -20,7 +19,6 @@ import {
|
||||
TRelicQuality
|
||||
} from "warframe-public-export-plus";
|
||||
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
|
||||
import allIncarnons from "@/static/fixed_responses/allIncarnonList.json";
|
||||
|
||||
interface ListedItem {
|
||||
uniqueName: string;
|
||||
@ -30,30 +28,6 @@ interface ListedItem {
|
||||
badReason?: "starter" | "frivolous" | "notraw";
|
||||
partType?: string;
|
||||
chainLength?: number;
|
||||
parazon?: boolean;
|
||||
}
|
||||
|
||||
interface ItemLists {
|
||||
archonCrystalUpgrades: Record<string, string>;
|
||||
uniqueLevelCaps: Record<string, number>;
|
||||
Suits: ListedItem[];
|
||||
LongGuns: ListedItem[];
|
||||
Melee: ListedItem[];
|
||||
ModularParts: ListedItem[];
|
||||
Pistols: ListedItem[];
|
||||
Sentinels: ListedItem[];
|
||||
SentinelWeapons: ListedItem[];
|
||||
SpaceGuns: ListedItem[];
|
||||
SpaceMelee: ListedItem[];
|
||||
SpaceSuits: ListedItem[];
|
||||
MechSuits: ListedItem[];
|
||||
miscitems: ListedItem[];
|
||||
Syndicates: ListedItem[];
|
||||
OperatorAmps: ListedItem[];
|
||||
QuestKeys: ListedItem[];
|
||||
KubrowPets: ListedItem[];
|
||||
EvolutionProgress: ListedItem[];
|
||||
mods: ListedItem[];
|
||||
}
|
||||
|
||||
const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
||||
@ -65,28 +39,22 @@ const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
||||
|
||||
const getItemListsController: RequestHandler = (req, response) => {
|
||||
const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
|
||||
const res: ItemLists = {
|
||||
archonCrystalUpgrades,
|
||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||
Suits: [],
|
||||
LongGuns: [],
|
||||
Melee: [],
|
||||
ModularParts: [],
|
||||
Pistols: [],
|
||||
Sentinels: [],
|
||||
SentinelWeapons: [],
|
||||
SpaceGuns: [],
|
||||
SpaceMelee: [],
|
||||
SpaceSuits: [],
|
||||
MechSuits: [],
|
||||
miscitems: [],
|
||||
Syndicates: [],
|
||||
OperatorAmps: [],
|
||||
QuestKeys: [],
|
||||
KubrowPets: [],
|
||||
EvolutionProgress: [],
|
||||
mods: []
|
||||
};
|
||||
const res: Record<string, ListedItem[]> = {};
|
||||
res.Suits = [];
|
||||
res.LongGuns = [];
|
||||
res.Melee = [];
|
||||
res.ModularParts = [];
|
||||
res.Pistols = [];
|
||||
res.Sentinels = [];
|
||||
res.SentinelWeapons = [];
|
||||
res.SpaceGuns = [];
|
||||
res.SpaceMelee = [];
|
||||
res.SpaceSuits = [];
|
||||
res.MechSuits = [];
|
||||
res.miscitems = [];
|
||||
res.Syndicates = [];
|
||||
res.OperatorAmps = [];
|
||||
res.QuestKeys = [];
|
||||
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
|
||||
res[item.productCategory].push({
|
||||
uniqueName,
|
||||
@ -95,23 +63,20 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||
if (item.productCategory == "Sentinels") {
|
||||
res[item.productCategory].push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
exalted: item.exalted
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
|
||||
if (item.partType) {
|
||||
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||
res.ModularParts.push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
partType: item.partType
|
||||
});
|
||||
}
|
||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
@ -144,7 +109,6 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
let name = getString(item.name, lang);
|
||||
if ("dissectionParts" in item) {
|
||||
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
|
||||
if (item.syndicateTag == "CetusSyndicate") {
|
||||
if (uniqueName.indexOf("Large") != -1) {
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
|
||||
} else if (uniqueName.indexOf("Medium") != -1) {
|
||||
@ -152,21 +116,6 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
} else {
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
|
||||
}
|
||||
} else {
|
||||
if (uniqueName.indexOf("Large") != -1) {
|
||||
name = name
|
||||
.split("|FISH_SIZE|")
|
||||
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryElderAbbrev", lang));
|
||||
} else if (uniqueName.indexOf("Medium") != -1) {
|
||||
name = name
|
||||
.split("|FISH_SIZE|")
|
||||
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryMatureAbbrev", lang));
|
||||
} else {
|
||||
name = name
|
||||
.split("|FISH_SIZE|")
|
||||
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryYoungAbbrev", lang));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
name &&
|
||||
@ -222,13 +171,8 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportCustoms)) {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
|
||||
res.mods = [];
|
||||
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
|
||||
const mod: ListedItem = {
|
||||
uniqueName,
|
||||
@ -242,9 +186,6 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
} else if (upgrade.upgradeEntries) {
|
||||
mod.badReason = "notraw";
|
||||
}
|
||||
if (upgrade.type == "PARAZON") {
|
||||
mod.parazon = true;
|
||||
}
|
||||
res.mods.push(mod);
|
||||
}
|
||||
for (const [uniqueName, upgrade] of Object.entries(ExportAvionics)) {
|
||||
@ -286,14 +227,12 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const uniqueName of allIncarnons) {
|
||||
res.EvolutionProgress.push({
|
||||
uniqueName,
|
||||
name: getString(getItemName(uniqueName) || "", lang)
|
||||
});
|
||||
}
|
||||
|
||||
response.json(res);
|
||||
response.json({
|
||||
archonCrystalUpgrades,
|
||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||
...res
|
||||
});
|
||||
};
|
||||
|
||||
export { getItemListsController };
|
||||
|
23
src/controllers/custom/gildEquipmentController.ts
Normal file
23
src/controllers/custom/gildEquipmentController.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const gildEquipmentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IGildEquipmentRequest;
|
||||
const inventory = await getInventory(accountId, request.Category);
|
||||
const weapon = inventory[request.Category].id(request.ItemId);
|
||||
if (weapon) {
|
||||
weapon.Features ??= 0;
|
||||
weapon.Features |= EquipmentFeatures.GILDED;
|
||||
await inventory.save();
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
type IGildEquipmentRequest = {
|
||||
ItemId: string;
|
||||
Category: TEquipmentKey;
|
||||
};
|
@ -35,9 +35,11 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
|
||||
switch (operation) {
|
||||
case "completeAll": {
|
||||
if (allQuestKeys.includes(questItemType)) {
|
||||
for (const questKey of inventory.QuestKeys) {
|
||||
await completeQuest(inventory, questKey.ItemType);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "resetAll": {
|
||||
@ -54,12 +56,15 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
break;
|
||||
}
|
||||
case "deleteKey": {
|
||||
if (allQuestKeys.includes(questItemType)) {
|
||||
const questKey = inventory.QuestKeys.find(key => key.ItemType === questItemType);
|
||||
if (!questKey) {
|
||||
logger.error(`Quest key not found in inventory: ${questItemType}`);
|
||||
break;
|
||||
}
|
||||
|
||||
inventory.QuestKeys.pull({ ItemType: questItemType });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "completeKey": {
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setEvolutionProgressController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const payload = req.body as ISetEvolutionProgressRequest;
|
||||
|
||||
inventory.EvolutionProgress ??= [];
|
||||
payload.forEach(element => {
|
||||
const entry = inventory.EvolutionProgress!.find(entry => entry.ItemType === element.ItemType);
|
||||
|
||||
if (entry) {
|
||||
entry.Progress = 0;
|
||||
entry.Rank = element.Rank;
|
||||
} else {
|
||||
inventory.EvolutionProgress!.push({
|
||||
Progress: 0,
|
||||
Rank: element.Rank,
|
||||
ItemType: element.ItemType
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
type ISetEvolutionProgressRequest = {
|
||||
ItemType: string;
|
||||
Rank: number;
|
||||
}[];
|
@ -1,6 +1,611 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getWorldState } from "@/src/services/worldStateService";
|
||||
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
|
||||
import static1999FallDays from "@/static/fixed_responses/worldState/1999_fall_days.json";
|
||||
import static1999SpringDays from "@/static/fixed_responses/worldState/1999_spring_days.json";
|
||||
import static1999SummerDays from "@/static/fixed_responses/worldState/1999_summer_days.json";
|
||||
import static1999WinterDays from "@/static/fixed_responses/worldState/1999_winter_days.json";
|
||||
import { buildConfig } from "@/src/services/buildConfigService";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { CRng } from "@/src/services/rngService";
|
||||
import { ExportNightwave, ExportRegions } from "warframe-public-export-plus";
|
||||
import {
|
||||
EPOCH,
|
||||
getSortieTime,
|
||||
missionTags,
|
||||
sortieBosses,
|
||||
sortieBossNode,
|
||||
sortieBossToFaction,
|
||||
sortieFactionToFactionIndexes,
|
||||
sortieFactionToSystemIndexes
|
||||
} from "@/src/helpers/worlstateHelper";
|
||||
|
||||
export const worldStateController: RequestHandler = (req, res) => {
|
||||
res.json(getWorldState(req.query.buildLabel as string | undefined));
|
||||
const day = Math.trunc((Date.now() - EPOCH) / 86400000);
|
||||
const week = Math.trunc(day / 7);
|
||||
const weekStart = EPOCH + week * 604800000;
|
||||
const weekEnd = weekStart + 604800000;
|
||||
|
||||
const worldState: IWorldState = {
|
||||
BuildLabel:
|
||||
typeof req.query.buildLabel == "string"
|
||||
? req.query.buildLabel.split(" ").join("+")
|
||||
: buildConfig.buildLabel,
|
||||
Time: config.worldState?.lockTime || Math.round(Date.now() / 1000),
|
||||
Goals: [],
|
||||
GlobalUpgrades: [],
|
||||
Sorties: [],
|
||||
LiteSorties: [],
|
||||
EndlessXpChoices: [],
|
||||
SeasonInfo: {
|
||||
Activation: { $date: { $numberLong: "1715796000000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
AffiliationTag: "RadioLegionIntermission12Syndicate",
|
||||
Season: 14,
|
||||
Phase: 0,
|
||||
Params: "",
|
||||
ActiveChallenges: [
|
||||
getSeasonDailyChallenge(day - 2),
|
||||
getSeasonDailyChallenge(day - 1),
|
||||
getSeasonDailyChallenge(day - 0),
|
||||
getSeasonWeeklyChallenge(week, 0),
|
||||
getSeasonWeeklyChallenge(week, 1),
|
||||
getSeasonWeeklyHardChallenge(week, 2),
|
||||
getSeasonWeeklyHardChallenge(week, 3),
|
||||
{
|
||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Challenge:
|
||||
"/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
|
||||
},
|
||||
{
|
||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
|
||||
},
|
||||
{
|
||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
|
||||
}
|
||||
]
|
||||
},
|
||||
...staticWorldState
|
||||
};
|
||||
|
||||
if (config.worldState?.starDays) {
|
||||
worldState.Goals.push({
|
||||
_id: { $oid: "67a4dcce2a198564d62e1647" },
|
||||
Activation: { $date: { $numberLong: "1738868400000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
Count: 0,
|
||||
Goal: 0,
|
||||
Success: 0,
|
||||
Personal: true,
|
||||
Desc: "/Lotus/Language/Events/ValentinesFortunaName",
|
||||
ToolTip: "/Lotus/Language/Events/ValentinesFortunaName",
|
||||
Icon: "/Lotus/Interface/Icons/WorldStatePanel/ValentinesEventIcon.png",
|
||||
Tag: "FortunaValentines",
|
||||
Node: "SolarisUnitedHub1"
|
||||
});
|
||||
}
|
||||
|
||||
// Elite Sanctuary Onslaught cycling every week
|
||||
worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
|
||||
|
||||
// Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation
|
||||
const bountyCycle = Math.trunc(Date.now() / 9000000);
|
||||
const bountyCycleStart = bountyCycle * 9000000;
|
||||
const bountyCycleEnd = bountyCycleStart + 9000000;
|
||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "ZarimanSyndicate")] = {
|
||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000029" },
|
||||
Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
|
||||
Tag: "ZarimanSyndicate",
|
||||
Seed: bountyCycle,
|
||||
Nodes: []
|
||||
};
|
||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "EntratiLabSyndicate")] = {
|
||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000004" },
|
||||
Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
|
||||
Tag: "EntratiLabSyndicate",
|
||||
Seed: bountyCycle,
|
||||
Nodes: []
|
||||
};
|
||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "HexSyndicate")] = {
|
||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000006" },
|
||||
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
||||
Tag: "HexSyndicate",
|
||||
Seed: bountyCycle,
|
||||
Nodes: []
|
||||
};
|
||||
|
||||
if (config.worldState?.creditBoost) {
|
||||
worldState.GlobalUpgrades.push({
|
||||
_id: { $oid: "5b23106f283a555109666672" },
|
||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
||||
UpgradeType: "GAMEPLAY_MONEY_REWARD_AMOUNT",
|
||||
OperationType: "MULTIPLY",
|
||||
Value: 2,
|
||||
LocalizeTag: "",
|
||||
LocalizeDescTag: ""
|
||||
});
|
||||
}
|
||||
if (config.worldState?.affinityBoost) {
|
||||
worldState.GlobalUpgrades.push({
|
||||
_id: { $oid: "5b23106f283a555109666673" },
|
||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
||||
UpgradeType: "GAMEPLAY_KILL_XP_AMOUNT",
|
||||
OperationType: "MULTIPLY",
|
||||
Value: 2,
|
||||
LocalizeTag: "",
|
||||
LocalizeDescTag: ""
|
||||
});
|
||||
}
|
||||
if (config.worldState?.resourceBoost) {
|
||||
worldState.GlobalUpgrades.push({
|
||||
_id: { $oid: "5b23106f283a555109666674" },
|
||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
||||
UpgradeType: "GAMEPLAY_PICKUP_AMOUNT",
|
||||
OperationType: "MULTIPLY",
|
||||
Value: 2,
|
||||
LocalizeTag: "",
|
||||
LocalizeDescTag: ""
|
||||
});
|
||||
}
|
||||
|
||||
// Sortie cycling every day
|
||||
{
|
||||
let genDay;
|
||||
let dayStart;
|
||||
let dayEnd;
|
||||
const sortieRolloverToday = getSortieTime(day);
|
||||
if (Date.now() < sortieRolloverToday) {
|
||||
// Early in the day, generate sortie for `day - 1`, expiring at `sortieRolloverToday`.
|
||||
genDay = day - 1;
|
||||
dayStart = getSortieTime(genDay);
|
||||
dayEnd = sortieRolloverToday;
|
||||
} else {
|
||||
// Late in the day, generate sortie for `day`, expiring at `getSortieTime(day + 1)`.
|
||||
genDay = day;
|
||||
dayStart = sortieRolloverToday;
|
||||
dayEnd = getSortieTime(day + 1);
|
||||
}
|
||||
|
||||
const rng = new CRng(genDay);
|
||||
|
||||
const boss = rng.randomElement(sortieBosses);
|
||||
|
||||
const modifiers = [
|
||||
"SORTIE_MODIFIER_LOW_ENERGY",
|
||||
"SORTIE_MODIFIER_IMPACT",
|
||||
"SORTIE_MODIFIER_SLASH",
|
||||
"SORTIE_MODIFIER_PUNCTURE",
|
||||
"SORTIE_MODIFIER_EXIMUS",
|
||||
"SORTIE_MODIFIER_MAGNETIC",
|
||||
"SORTIE_MODIFIER_CORROSIVE",
|
||||
"SORTIE_MODIFIER_VIRAL",
|
||||
"SORTIE_MODIFIER_ELECTRICITY",
|
||||
"SORTIE_MODIFIER_RADIATION",
|
||||
"SORTIE_MODIFIER_GAS",
|
||||
"SORTIE_MODIFIER_FIRE",
|
||||
"SORTIE_MODIFIER_EXPLOSION",
|
||||
"SORTIE_MODIFIER_FREEZE",
|
||||
"SORTIE_MODIFIER_TOXIN",
|
||||
"SORTIE_MODIFIER_POISON",
|
||||
"SORTIE_MODIFIER_HAZARD_RADIATION",
|
||||
"SORTIE_MODIFIER_HAZARD_MAGNETIC",
|
||||
"SORTIE_MODIFIER_HAZARD_FOG", // TODO: push this if the mission tileset is Grineer Forest
|
||||
"SORTIE_MODIFIER_HAZARD_FIRE", // TODO: push this if the mission tileset is Corpus Ship or Grineer Galleon
|
||||
"SORTIE_MODIFIER_HAZARD_ICE",
|
||||
"SORTIE_MODIFIER_HAZARD_COLD",
|
||||
"SORTIE_MODIFIER_SECONDARY_ONLY",
|
||||
"SORTIE_MODIFIER_SHOTGUN_ONLY",
|
||||
"SORTIE_MODIFIER_SNIPER_ONLY",
|
||||
"SORTIE_MODIFIER_RIFLE_ONLY",
|
||||
"SORTIE_MODIFIER_MELEE_ONLY",
|
||||
"SORTIE_MODIFIER_BOW_ONLY"
|
||||
];
|
||||
|
||||
if (sortieBossToFaction[boss] == "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_SHIELDS");
|
||||
if (sortieBossToFaction[boss] != "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_ARMOR");
|
||||
|
||||
const nodes: string[] = [];
|
||||
const availableMissionIndexes: number[] = [];
|
||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||
if (
|
||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
||||
value.name.indexOf("Archwing") == -1 &&
|
||||
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
|
||||
value.missionIndex != 5 && // Exclude MT_CAPTURE
|
||||
value.missionIndex != 21 && // Exclude MT_PURIFY
|
||||
value.missionIndex != 23 && // Exclude MT_JUNCTION
|
||||
value.missionIndex <= 28
|
||||
) {
|
||||
if (!availableMissionIndexes.includes(value.missionIndex)) {
|
||||
availableMissionIndexes.push(value.missionIndex);
|
||||
}
|
||||
nodes.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const selectedNodes: { missionType: string; modifierType: string; node: string }[] = [];
|
||||
const missionTypes = new Set();
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const randomIndex = rng.randomInt(0, nodes.length - 1);
|
||||
const node = nodes[randomIndex];
|
||||
let missionIndex = ExportRegions[node].missionIndex;
|
||||
|
||||
if (
|
||||
!["SolNode404", "SolNode411"].includes(node) && // for some reason the game doesn't like missionType changes for these missions
|
||||
missionIndex != 28 &&
|
||||
rng.randomInt(0, 2) == 2
|
||||
) {
|
||||
missionIndex = rng.randomElement(availableMissionIndexes);
|
||||
}
|
||||
|
||||
if (i == 2 && rng.randomInt(0, 2) == 2) {
|
||||
const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY");
|
||||
const modifierType = rng.randomElement(filteredModifiers);
|
||||
|
||||
if (boss == "SORTIE_BOSS_PHORID") {
|
||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node });
|
||||
nodes.splice(randomIndex, 1);
|
||||
continue;
|
||||
} else if (sortieBossNode[boss]) {
|
||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node: sortieBossNode[boss] });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const missionType = missionTags[missionIndex];
|
||||
|
||||
if (missionTypes.has(missionType)) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
const filteredModifiers =
|
||||
missionType === "MT_TERRITORY"
|
||||
? modifiers.filter(mod => mod != "SORTIE_MODIFIER_HAZARD_RADIATION")
|
||||
: modifiers;
|
||||
|
||||
const modifierType = rng.randomElement(filteredModifiers);
|
||||
|
||||
selectedNodes.push({ missionType, modifierType, node });
|
||||
nodes.splice(randomIndex, 1);
|
||||
missionTypes.add(missionType);
|
||||
}
|
||||
|
||||
worldState.Sorties.push({
|
||||
_id: { $oid: Math.trunc(dayStart / 1000).toString(16) + "d4d932c97c0a3acd" },
|
||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
|
||||
Seed: genDay,
|
||||
Boss: boss,
|
||||
Variants: selectedNodes
|
||||
});
|
||||
}
|
||||
|
||||
// Archon Hunt cycling every week
|
||||
{
|
||||
const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
|
||||
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
|
||||
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
|
||||
|
||||
const nodes: string[] = [];
|
||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||
if (
|
||||
value.systemIndex === systemIndex &&
|
||||
value.factionIndex !== undefined &&
|
||||
value.factionIndex < 2 &&
|
||||
value.name.indexOf("Archwing") == -1 &&
|
||||
value.missionIndex != 0 // Exclude MT_ASSASSINATION
|
||||
) {
|
||||
nodes.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const rng = new CRng(week);
|
||||
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
|
||||
const firstNode = nodes[firstNodeIndex];
|
||||
nodes.splice(firstNodeIndex, 1);
|
||||
worldState.LiteSorties.push({
|
||||
_id: {
|
||||
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
|
||||
},
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
|
||||
Seed: week,
|
||||
Boss: boss,
|
||||
Missions: [
|
||||
{
|
||||
missionType: rng.randomElement([
|
||||
"MT_INTEL",
|
||||
"MT_MOBILE_DEFENSE",
|
||||
"MT_EXTERMINATION",
|
||||
"MT_SABOTAGE",
|
||||
"MT_RESCUE"
|
||||
]),
|
||||
node: firstNode
|
||||
},
|
||||
{
|
||||
missionType: rng.randomElement([
|
||||
"MT_DEFENSE",
|
||||
"MT_TERRITORY",
|
||||
"MT_ARTIFACT",
|
||||
"MT_EXCAVATE",
|
||||
"MT_SURVIVAL"
|
||||
]),
|
||||
node: rng.randomElement(nodes)
|
||||
},
|
||||
{
|
||||
missionType: "MT_ASSASSINATION",
|
||||
node: showdownNode
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Circuit choices cycling every week
|
||||
worldState.EndlessXpChoices.push({
|
||||
Category: "EXC_NORMAL",
|
||||
Choices: [
|
||||
["Nidus", "Octavia", "Harrow"],
|
||||
["Gara", "Khora", "Revenant"],
|
||||
["Garuda", "Baruuk", "Hildryn"],
|
||||
["Excalibur", "Trinity", "Ember"],
|
||||
["Loki", "Mag", "Rhino"],
|
||||
["Ash", "Frost", "Nyx"],
|
||||
["Saryn", "Vauban", "Nova"],
|
||||
["Nekros", "Valkyr", "Oberon"],
|
||||
["Hydroid", "Mirage", "Limbo"],
|
||||
["Mesa", "Chroma", "Atlas"],
|
||||
["Ivara", "Inaros", "Titania"]
|
||||
][week % 12]
|
||||
});
|
||||
worldState.EndlessXpChoices.push({
|
||||
Category: "EXC_HARD",
|
||||
Choices: [
|
||||
["Boar", "Gammacor", "Angstrum", "Gorgon", "Anku"],
|
||||
["Bo", "Latron", "Furis", "Furax", "Strun"],
|
||||
["Lex", "Magistar", "Boltor", "Bronco", "CeramicDagger"],
|
||||
["Torid", "DualToxocyst", "DualIchor", "Miter", "Atomos"],
|
||||
["AckAndBrunt", "Soma", "Vasto", "NamiSolo", "Burston"],
|
||||
["Zylok", "Sibear", "Dread", "Despair", "Hate"],
|
||||
["Dera", "Sybaris", "Cestra", "Sicarus", "Okina"],
|
||||
["Braton", "Lato", "Skana", "Paris", "Kunai"]
|
||||
][week % 8]
|
||||
});
|
||||
|
||||
// 1999 Calendar Season cycling every week + YearIteration every 4 weeks
|
||||
worldState.KnownCalendarSeasons[0].Activation = { $date: { $numberLong: weekStart.toString() } };
|
||||
worldState.KnownCalendarSeasons[0].Expiry = { $date: { $numberLong: weekEnd.toString() } };
|
||||
worldState.KnownCalendarSeasons[0].Season = ["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"][week % 4];
|
||||
worldState.KnownCalendarSeasons[0].Days = [
|
||||
static1999WinterDays,
|
||||
static1999SpringDays,
|
||||
static1999SummerDays,
|
||||
static1999FallDays
|
||||
][week % 4];
|
||||
worldState.KnownCalendarSeasons[0].YearIteration = Math.trunc(week / 4);
|
||||
|
||||
// Sentient Anomaly cycling every 30 minutes
|
||||
const halfHour = Math.trunc(Date.now() / (unixTimesInMs.hour / 2));
|
||||
const tmp = {
|
||||
cavabegin: "1690761600",
|
||||
PurchasePlatformLockEnabled: true,
|
||||
tcsn: true,
|
||||
pgr: {
|
||||
ts: "1732572900",
|
||||
en: "CUSTOM DECALS @ ZEVILA",
|
||||
fr: "DECALS CUSTOM @ ZEVILA",
|
||||
it: "DECALCOMANIE PERSONALIZZATE @ ZEVILA",
|
||||
de: "AUFKLEBER NACH WUNSCH @ ZEVILA",
|
||||
es: "CALCOMANÍAS PERSONALIZADAS @ ZEVILA",
|
||||
pt: "DECALQUES PERSONALIZADOS NA ZEVILA",
|
||||
ru: "ПОЛЬЗОВАТЕЛЬСКИЕ НАКЛЕЙКИ @ ЗеВиЛа",
|
||||
pl: "NOWE NAKLEJKI @ ZEVILA",
|
||||
uk: "КОРИСТУВАЦЬКІ ДЕКОЛІ @ ЗІВІЛА",
|
||||
tr: "ÖZEL ÇIKARTMALAR @ ZEVILA",
|
||||
ja: "カスタムデカール @ ゼビラ",
|
||||
zh: "定制贴花认准泽威拉",
|
||||
ko: "커스텀 데칼 @ ZEVILA",
|
||||
tc: "自訂貼花 @ ZEVILA",
|
||||
th: "รูปลอกสั่งทำที่ ZEVILA"
|
||||
},
|
||||
ennnd: true,
|
||||
mbrt: true,
|
||||
sfn: [550, 553, 554, 555][halfHour % 4]
|
||||
};
|
||||
worldState.Tmp = JSON.stringify(tmp);
|
||||
|
||||
res.json(worldState);
|
||||
};
|
||||
|
||||
interface IWorldState {
|
||||
Version: number; // for goals
|
||||
BuildLabel: string;
|
||||
Time: number;
|
||||
Goals: IGoal[];
|
||||
SyndicateMissions: ISyndicateMission[];
|
||||
GlobalUpgrades: IGlobalUpgrade[];
|
||||
Sorties: ISortie[];
|
||||
LiteSorties: ILiteSortie[];
|
||||
NodeOverrides: INodeOverride[];
|
||||
EndlessXpChoices: IEndlessXpChoice[];
|
||||
SeasonInfo: {
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
AffiliationTag: string;
|
||||
Season: number;
|
||||
Phase: number;
|
||||
Params: string;
|
||||
ActiveChallenges: ISeasonChallenge[];
|
||||
};
|
||||
KnownCalendarSeasons: ICalendarSeason[];
|
||||
Tmp?: string;
|
||||
}
|
||||
|
||||
interface IGoal {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Count: number;
|
||||
Goal: number;
|
||||
Success: number;
|
||||
Personal: boolean;
|
||||
Desc: string;
|
||||
ToolTip: string;
|
||||
Icon: string;
|
||||
Tag: string;
|
||||
Node: string;
|
||||
}
|
||||
|
||||
interface ISyndicateMission {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Tag: string;
|
||||
Seed: number;
|
||||
Nodes: string[];
|
||||
}
|
||||
|
||||
interface IGlobalUpgrade {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
ExpiryDate: IMongoDate;
|
||||
UpgradeType: string;
|
||||
OperationType: string;
|
||||
Value: number;
|
||||
LocalizeTag: string;
|
||||
LocalizeDescTag: string;
|
||||
}
|
||||
|
||||
interface INodeOverride {
|
||||
_id: IOid;
|
||||
Activation?: IMongoDate;
|
||||
Expiry?: IMongoDate;
|
||||
Node: string;
|
||||
Hide?: boolean;
|
||||
Seed?: number;
|
||||
LevelOverride?: string;
|
||||
Faction?: string;
|
||||
CustomNpcEncounters?: string;
|
||||
}
|
||||
|
||||
interface ISortie {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards";
|
||||
Seed: number;
|
||||
Boss: string;
|
||||
Variants: {
|
||||
missionType: string;
|
||||
modifierType: string;
|
||||
node: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface ILiteSortie {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards";
|
||||
Seed: number;
|
||||
Boss: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"
|
||||
Missions: {
|
||||
missionType: string;
|
||||
node: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface IEndlessXpChoice {
|
||||
Category: string;
|
||||
Choices: string[];
|
||||
}
|
||||
|
||||
interface ISeasonChallenge {
|
||||
_id: IOid;
|
||||
Daily?: boolean;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Challenge: string;
|
||||
}
|
||||
|
||||
interface ICalendarSeason {
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"
|
||||
Days: {
|
||||
day: number;
|
||||
}[];
|
||||
YearIteration: number;
|
||||
}
|
||||
|
||||
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
||||
x.startsWith("/Lotus/Types/Challenges/Seasons/Daily/")
|
||||
);
|
||||
|
||||
const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
|
||||
const dayStart = EPOCH + day * 86400000;
|
||||
const dayEnd = EPOCH + (day + 3) * 86400000;
|
||||
const rng = new CRng(day);
|
||||
return {
|
||||
_id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") },
|
||||
Daily: true,
|
||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||
Challenge: rng.randomElement(dailyChallenges)
|
||||
};
|
||||
};
|
||||
|
||||
const weeklyChallenges = Object.keys(ExportNightwave.challenges).filter(
|
||||
x =>
|
||||
x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
|
||||
!x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
|
||||
);
|
||||
|
||||
const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge => {
|
||||
const weekStart = EPOCH + week * 604800000;
|
||||
const weekEnd = weekStart + 604800000;
|
||||
const challengeId = week * 7 + id;
|
||||
const rng = new CRng(challengeId);
|
||||
return {
|
||||
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Challenge: rng.randomElement(weeklyChallenges)
|
||||
};
|
||||
};
|
||||
|
||||
const weeklyHardChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
||||
x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
|
||||
);
|
||||
|
||||
const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChallenge => {
|
||||
const weekStart = EPOCH + week * 604800000;
|
||||
const weekEnd = weekStart + 604800000;
|
||||
const challengeId = week * 7 + id;
|
||||
const rng = new CRng(challengeId);
|
||||
return {
|
||||
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
|
||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||
Challenge: rng.randomElement(weeklyHardChallenges)
|
||||
};
|
||||
};
|
||||
|
@ -27,15 +27,7 @@ const viewController: RequestHandler = async (req, res) => {
|
||||
for (const type of Object.keys(ExportEnemies.avatars)) {
|
||||
if (!scans.has(type)) scans.add(type);
|
||||
}
|
||||
|
||||
// Take any existing scans and also set them to 9999
|
||||
if (responseJson.Scans) {
|
||||
for (const scan of responseJson.Scans) {
|
||||
scans.add(scan.type);
|
||||
}
|
||||
}
|
||||
responseJson.Scans = [];
|
||||
|
||||
responseJson.Scans ??= [];
|
||||
for (const type of scans) {
|
||||
responseJson.Scans.push({ type: type, scans: 9999 });
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { Types } from "mongoose";
|
||||
import { TRarity } from "warframe-public-export-plus";
|
||||
|
||||
export const toOid = (objectId: Types.ObjectId): IOid => {
|
||||
return { $oid: objectId.toString() } satisfies IOid;
|
||||
@ -9,144 +8,3 @@ export const toOid = (objectId: Types.ObjectId): IOid => {
|
||||
export const toMongoDate = (date: Date): IMongoDate => {
|
||||
return { $date: { $numberLong: date.getTime().toString() } };
|
||||
};
|
||||
|
||||
export const kubrowWeights: Record<TRarity, number> = {
|
||||
COMMON: 6,
|
||||
UNCOMMON: 4,
|
||||
RARE: 2,
|
||||
LEGENDARY: 1
|
||||
};
|
||||
|
||||
export const kubrowFurPatternsWeights: Record<TRarity, number> = {
|
||||
COMMON: 6,
|
||||
UNCOMMON: 5,
|
||||
RARE: 2,
|
||||
LEGENDARY: 1
|
||||
};
|
||||
|
||||
export const catbrowDetails = {
|
||||
Colors: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseB", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseC", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseD", rarity: "COMMON" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryA", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryC", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryD", rarity: "UNCOMMON" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryA", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryB", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryD", rarity: "RARE" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsD", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
EyeColors: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesI", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesJ", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesK", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesL", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesM", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesN", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
FurPatterns: [{ type: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternA", rarity: "COMMON" as TRarity }],
|
||||
|
||||
BodyTypes: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
Heads: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadD", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
Tails: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailE", rarity: "LEGENDARY" as TRarity }
|
||||
]
|
||||
};
|
||||
|
||||
export const kubrowDetails = {
|
||||
Colors: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneA", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneC", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneD", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneE", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneF", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneG", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneH", rarity: "UNCOMMON" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidA", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidB", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidD", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidE", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidF", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidG", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidH", rarity: "RARE" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantH", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
EyeColors: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesI", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
FurPatterns: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternA", rarity: "UNCOMMON" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternD", rarity: "RARE" as TRarity },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternF", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
BodyTypes: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetHeavyBodyType", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetThinBodyType", rarity: "LEGENDARY" as TRarity }
|
||||
],
|
||||
|
||||
Heads: [],
|
||||
|
||||
Tails: []
|
||||
};
|
||||
|
@ -1,14 +1,6 @@
|
||||
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
|
||||
import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
|
||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
||||
import { logger } from "../utils/logger";
|
||||
import { IOid } from "../types/commonTypes";
|
||||
import { Types } from "mongoose";
|
||||
import { addMods, generateRewardSeed } from "../services/inventoryService";
|
||||
import { isArchwingMission } from "../services/worldStateService";
|
||||
import { fromStoreItem, toStoreItem } from "../services/itemDataService";
|
||||
import { createMessage } from "../services/inboxService";
|
||||
import { ExportRegions } from "warframe-public-export-plus";
|
||||
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { SRng } from "@/src/services/rngService";
|
||||
|
||||
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
||||
const infNodes = [];
|
||||
@ -25,7 +17,7 @@ export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
||||
value.missionIndex != 42 && // not face off
|
||||
value.name.indexOf("1999NodeI") == -1 && // not stage defence
|
||||
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
|
||||
!isArchwingMission(value)
|
||||
value.name.indexOf("Archwing") == -1
|
||||
) {
|
||||
//console.log(dict_en[value.name]);
|
||||
infNodes.push({ Node: key, Influence: 1 });
|
||||
@ -40,339 +32,13 @@ const systemIndexes: Record<string, number[]> = {
|
||||
FC_INFESTATION: [23]
|
||||
};
|
||||
|
||||
export const showdownNodes: Record<string, string> = {
|
||||
FC_GRINEER: "CrewBattleNode557",
|
||||
FC_CORPUS: "CrewBattleNode558",
|
||||
FC_INFESTATION: "CrewBattleNode559"
|
||||
};
|
||||
|
||||
// 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: string }): number[] => {
|
||||
const rng = new SRng(nemesis.fp);
|
||||
const choices = [0, 1, 2, 3, 5, 6, 7];
|
||||
let choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||
const passcode = [choices[choiceIndex]];
|
||||
if (nemesis.Faction != "FC_INFESTATION") {
|
||||
choices.splice(choiceIndex, 1);
|
||||
choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||
passcode.push(choices[choiceIndex]);
|
||||
|
||||
choices.splice(choiceIndex, 1);
|
||||
choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||
passcode.push(choices[choiceIndex]);
|
||||
export const getNemesisPasscode = (fp: bigint, faction: string): number[] => {
|
||||
const rng = new SRng(fp);
|
||||
const passcode = [rng.randomInt(0, 7)];
|
||||
if (faction != "FC_INFESTATION") {
|
||||
passcode.push(rng.randomInt(0, 7));
|
||||
passcode.push(rng.randomInt(0, 7));
|
||||
}
|
||||
return passcode;
|
||||
};
|
||||
|
||||
const reqiuemMods: readonly string[] = [
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalFourMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalFiveMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod"
|
||||
];
|
||||
|
||||
const antivirusMods: readonly string[] = [
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusFourMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusFiveMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusSixMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusSevenMod",
|
||||
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
|
||||
];
|
||||
|
||||
export const getNemesisPasscodeModTypes = (nemesis: { fp: bigint; Faction: string }): string[] => {
|
||||
const passcode = getNemesisPasscode(nemesis);
|
||||
return nemesis.Faction == "FC_INFESTATION"
|
||||
? passcode.map(i => antivirusMods[i])
|
||||
: passcode.map(i => reqiuemMods[i]);
|
||||
};
|
||||
|
||||
export const encodeNemesisGuess = (
|
||||
symbol1: number,
|
||||
result1: number,
|
||||
symbol2: number,
|
||||
result2: number,
|
||||
symbol3: number,
|
||||
result3: number
|
||||
): number => {
|
||||
return (
|
||||
(symbol1 & 0xf) |
|
||||
((result1 & 3) << 12) |
|
||||
((symbol2 << 4) & 0xff) |
|
||||
((result2 << 14) & 0xffff) |
|
||||
((symbol3 & 0xf) << 8) |
|
||||
((result3 & 3) << 16)
|
||||
);
|
||||
};
|
||||
|
||||
export const decodeNemesisGuess = (val: number): number[] => {
|
||||
return [val & 0xf, (val >> 12) & 3, (val & 0xff) >> 4, (val & 0xffff) >> 14, (val >> 8) & 0xf, (val >> 16) & 3];
|
||||
};
|
||||
|
||||
export interface IKnifeResponse {
|
||||
UpgradeIds?: string[];
|
||||
UpgradeTypes?: string[];
|
||||
UpgradeFingerprints?: { lvl: number }[];
|
||||
UpgradeNew?: boolean[];
|
||||
HasKnife?: boolean;
|
||||
}
|
||||
|
||||
export const getKnifeUpgrade = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
dataknifeUpgrades: string[],
|
||||
type: string
|
||||
): { ItemId: IOid; ItemType: string } => {
|
||||
if (dataknifeUpgrades.indexOf(type) != -1) {
|
||||
return {
|
||||
ItemId: { $oid: "000000000000000000000000" },
|
||||
ItemType: type
|
||||
};
|
||||
}
|
||||
for (const upgradeId of dataknifeUpgrades) {
|
||||
if (upgradeId.length == 24) {
|
||||
const upgrade = inventory.Upgrades.id(upgradeId);
|
||||
if (upgrade && upgrade.ItemType == type) {
|
||||
return {
|
||||
ItemId: { $oid: upgradeId },
|
||||
ItemType: type
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`${type} does not seem to be installed on parazon?!`);
|
||||
};
|
||||
|
||||
export const consumeModCharge = (
|
||||
response: IKnifeResponse,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
upgrade: { ItemId: IOid; ItemType: string },
|
||||
dataknifeUpgrades: string[]
|
||||
): void => {
|
||||
response.UpgradeIds ??= [];
|
||||
response.UpgradeTypes ??= [];
|
||||
response.UpgradeFingerprints ??= [];
|
||||
response.UpgradeNew ??= [];
|
||||
response.HasKnife = true;
|
||||
|
||||
if (upgrade.ItemId.$oid != "000000000000000000000000") {
|
||||
const dbUpgrade = inventory.Upgrades.id(upgrade.ItemId.$oid)!;
|
||||
const fingerprint = JSON.parse(dbUpgrade.UpgradeFingerprint!) as { lvl: number };
|
||||
fingerprint.lvl += 1;
|
||||
dbUpgrade.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||
|
||||
response.UpgradeIds.push(upgrade.ItemId.$oid);
|
||||
response.UpgradeTypes.push(upgrade.ItemType);
|
||||
response.UpgradeFingerprints.push(fingerprint);
|
||||
response.UpgradeNew.push(false);
|
||||
} else {
|
||||
const id = new Types.ObjectId();
|
||||
inventory.Upgrades.push({
|
||||
_id: id,
|
||||
ItemType: upgrade.ItemType,
|
||||
UpgradeFingerprint: `{"lvl":1}`
|
||||
});
|
||||
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: upgrade.ItemType,
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
|
||||
const dataknifeRawUpgradeIndex = dataknifeUpgrades.indexOf(upgrade.ItemType);
|
||||
if (dataknifeRawUpgradeIndex != -1) {
|
||||
dataknifeUpgrades[dataknifeRawUpgradeIndex] = id.toString();
|
||||
} else {
|
||||
logger.warn(`${upgrade.ItemType} not found in dataknife config`);
|
||||
}
|
||||
|
||||
response.UpgradeIds.push(id.toString());
|
||||
response.UpgradeTypes.push(upgrade.ItemType);
|
||||
response.UpgradeFingerprints.push({ lvl: 1 });
|
||||
response.UpgradeNew.push(true);
|
||||
}
|
||||
};
|
||||
|
||||
const kuvaLichVersionSixWeapons = [
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
|
||||
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
|
||||
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
|
||||
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
|
||||
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
|
||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
|
||||
];
|
||||
|
||||
const corpusVersionThreeWeapons = [
|
||||
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
|
||||
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
|
||||
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
|
||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
|
||||
];
|
||||
|
||||
export const getWeaponsForManifest = (manifest: string): readonly string[] => {
|
||||
switch (manifest) {
|
||||
case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix":
|
||||
return kuvaLichVersionSixWeapons;
|
||||
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree":
|
||||
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour":
|
||||
return corpusVersionThreeWeapons;
|
||||
}
|
||||
throw new Error(`unknown nemesis manifest: ${manifest}`);
|
||||
};
|
||||
|
||||
export const getInnateDamageTag = (
|
||||
KillingSuit: string
|
||||
):
|
||||
| "InnateElectricityDamage"
|
||||
| "InnateFreezeDamage"
|
||||
| "InnateHeatDamage"
|
||||
| "InnateImpactDamage"
|
||||
| "InnateMagDamage"
|
||||
| "InnateRadDamage"
|
||||
| "InnateToxinDamage" => {
|
||||
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
|
||||
};
|
||||
|
||||
// TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
|
||||
export const getInnateDamageValue = (fp: bigint): number => {
|
||||
const rng = new SRng(fp);
|
||||
rng.randomFloat(); // used for the weapon index
|
||||
const WeaponUpgradeValueAttenuationExponent = 2.25;
|
||||
let value = Math.pow(rng.randomFloat(), WeaponUpgradeValueAttenuationExponent);
|
||||
if (value >= 0.941428) {
|
||||
value = 1;
|
||||
}
|
||||
return Math.trunc(value * 0x40000000);
|
||||
};
|
||||
|
||||
export const getKillTokenRewardCount = (fp: bigint): number => {
|
||||
const rng = new SRng(fp);
|
||||
return rng.randomInt(10, 15);
|
||||
};
|
||||
|
||||
// /Lotus/Types/Enemies/InfestedLich/InfestedLichRewardManifest
|
||||
const infestedLichRotA = [
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomHuman", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomInfested", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitHuman", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitInfested", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveHuman", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveInfested", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketHuman", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketInfested", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeHuman", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeInfested", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterA", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterB", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandDespairPoster", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandGridPoster", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandHuddlePoster", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandJumpPoster", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLimoPoster", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterDay", probability: 0.046 },
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterNight",
|
||||
probability: 0.045
|
||||
},
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandSillyPoster", probability: 0.046 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhiteBluePoster", probability: 0.045 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhitePinkPoster", probability: 0.045 }
|
||||
];
|
||||
const infestedLichRotB = [
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraA", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraB", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraC", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraD", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraE", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraF", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraG", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraH", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDJRomHype", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DancePacketWindmillShuffle", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceHarddrivePony", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDrillbitCrisscross", probability: 0.072 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceZekeCanthavethis", probability: 0.071 },
|
||||
{ type: "/Lotus/StoreItems/Types/Items/PhotoBooth/PhotoboothTileRJLasXStadiumBossArena", probability: 0.071 }
|
||||
];
|
||||
export const getInfestedLichItemRewards = (fp: bigint): string[] => {
|
||||
const rng = new SRng(fp);
|
||||
const rotAReward = getRewardAtPercentage(infestedLichRotA, rng.randomFloat())!.type;
|
||||
rng.randomFloat(); // unused afaict
|
||||
const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
|
||||
return [rotAReward, rotBReward];
|
||||
};
|
||||
|
||||
export const sendCodaFinishedMessage = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
fp: bigint = generateRewardSeed(),
|
||||
name: string = "ZEKE_BEATWOMAN_TM.1999",
|
||||
killed: boolean = true
|
||||
): Promise<void> => {
|
||||
const att: string[] = [];
|
||||
|
||||
// First vanquish/convert gives a sigil
|
||||
const sigil = killed
|
||||
? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
|
||||
: "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
|
||||
if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
|
||||
att.push(toStoreItem(sigil));
|
||||
}
|
||||
|
||||
const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
|
||||
att.push(fromStoreItem(rotAReward));
|
||||
att.push(fromStoreItem(rotBReward));
|
||||
|
||||
let countedAtt: ITypeCount[] | undefined;
|
||||
if (killed) {
|
||||
countedAtt = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
|
||||
ItemCount: getKillTokenRewardCount(fp)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
|
||||
arg: [
|
||||
{
|
||||
Key: "LICH_NAME",
|
||||
Tag: name
|
||||
}
|
||||
],
|
||||
att: att,
|
||||
countedAtt: countedAtt,
|
||||
sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export interface IFingerprintStat {
|
||||
}
|
||||
|
||||
export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => {
|
||||
const challenge = getRandomElement(meta.availableChallenges!)!;
|
||||
const challenge = getRandomElement(meta.availableChallenges!);
|
||||
const fingerprintChallenge: IRivenChallenge = {
|
||||
Type: challenge.fullName,
|
||||
Progress: 0,
|
||||
@ -54,11 +54,11 @@ export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFinger
|
||||
|
||||
export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
|
||||
const fingerprint: IUnveiledRivenFingerprint = {
|
||||
compat: getRandomElement(meta.compatibleItems!)!,
|
||||
compat: getRandomElement(meta.compatibleItems!),
|
||||
lim: 0,
|
||||
lvl: 0,
|
||||
lvlReq: getRandomInt(8, 16),
|
||||
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"])!,
|
||||
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
|
||||
buffs: [],
|
||||
curses: []
|
||||
};
|
||||
@ -81,7 +81,7 @@ export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenF
|
||||
if (Math.random() < 0.5) {
|
||||
const entry = getRandomElement(
|
||||
meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
|
||||
)!;
|
||||
);
|
||||
fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
|
||||
}
|
||||
};
|
||||
|
130
src/helpers/worlstateHelper.ts
Normal file
130
src/helpers/worlstateHelper.ts
Normal file
@ -0,0 +1,130 @@
|
||||
export const missionTags = [
|
||||
"MT_ASSASSINATION",
|
||||
"MT_EXTERMINATION",
|
||||
"MT_SURVIVAL",
|
||||
"MT_RESCUE",
|
||||
"MT_SABOTAGE",
|
||||
"MT_CAPTURE",
|
||||
"MT_COUNTER_INTEL",
|
||||
"MT_INTEL",
|
||||
"MT_DEFENSE",
|
||||
"MT_MOBILE_DEFENSE",
|
||||
"MT_PVP",
|
||||
"MT_MASTERY",
|
||||
"MT_RECOVERY",
|
||||
"MT_TERRITORY",
|
||||
"MT_RETRIEVAL",
|
||||
"MT_HIVE",
|
||||
"MT_SALVAGE",
|
||||
"MT_EXCAVATE",
|
||||
"MT_RAID",
|
||||
"MT_PURGE",
|
||||
"MT_GENERIC",
|
||||
"MT_PURIFY",
|
||||
"MT_ARENA",
|
||||
"MT_JUNCTION",
|
||||
"MT_PURSUIT",
|
||||
"MT_RACE",
|
||||
"MT_ASSAULT",
|
||||
"MT_EVACUATION",
|
||||
"MT_LANDSCAPE",
|
||||
"MT_RESOURCE_THEFT",
|
||||
"MT_ENDLESS_EXTERMINATION",
|
||||
"MT_ENDLESS_DUVIRI",
|
||||
"MT_RAILJACK",
|
||||
"MT_ARTIFACT",
|
||||
"MT_CORRUPTION",
|
||||
"MT_VOID_CASCADE",
|
||||
"MT_ARMAGEDDON",
|
||||
"MT_VAULTS",
|
||||
"MT_ALCHEMY",
|
||||
"MT_ASCENSION",
|
||||
"MT_ENDLESS_CAPTURE",
|
||||
"MT_OFFERING",
|
||||
"MT_PVPVE"
|
||||
];
|
||||
|
||||
export const sortieBosses = [
|
||||
"SORTIE_BOSS_HYENA",
|
||||
"SORTIE_BOSS_KELA",
|
||||
"SORTIE_BOSS_VOR",
|
||||
"SORTIE_BOSS_RUK",
|
||||
"SORTIE_BOSS_HEK",
|
||||
"SORTIE_BOSS_KRIL",
|
||||
"SORTIE_BOSS_TYL",
|
||||
"SORTIE_BOSS_JACKAL",
|
||||
"SORTIE_BOSS_ALAD",
|
||||
"SORTIE_BOSS_AMBULAS",
|
||||
"SORTIE_BOSS_NEF",
|
||||
"SORTIE_BOSS_RAPTOR",
|
||||
"SORTIE_BOSS_PHORID",
|
||||
"SORTIE_BOSS_LEPHANTIS",
|
||||
"SORTIE_BOSS_INFALAD",
|
||||
"SORTIE_BOSS_CORRUPTED_VOR"
|
||||
];
|
||||
|
||||
export const sortieBossToFaction: Record<string, string> = {
|
||||
SORTIE_BOSS_HYENA: "FC_CORPUS",
|
||||
SORTIE_BOSS_KELA: "FC_GRINEER",
|
||||
SORTIE_BOSS_VOR: "FC_GRINEER",
|
||||
SORTIE_BOSS_RUK: "FC_GRINEER",
|
||||
SORTIE_BOSS_HEK: "FC_GRINEER",
|
||||
SORTIE_BOSS_KRIL: "FC_GRINEER",
|
||||
SORTIE_BOSS_TYL: "FC_GRINEER",
|
||||
SORTIE_BOSS_JACKAL: "FC_CORPUS",
|
||||
SORTIE_BOSS_ALAD: "FC_CORPUS",
|
||||
SORTIE_BOSS_AMBULAS: "FC_CORPUS",
|
||||
SORTIE_BOSS_NEF: "FC_CORPUS",
|
||||
SORTIE_BOSS_RAPTOR: "FC_CORPUS",
|
||||
SORTIE_BOSS_PHORID: "FC_INFESTATION",
|
||||
SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION",
|
||||
SORTIE_BOSS_INFALAD: "FC_INFESTATION",
|
||||
SORTIE_BOSS_CORRUPTED_VOR: "FC_CORRUPTED"
|
||||
};
|
||||
|
||||
export const sortieFactionToSystemIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0, 2, 3, 5, 6, 9, 11, 18],
|
||||
FC_CORPUS: [1, 4, 7, 8, 12, 15],
|
||||
FC_INFESTATION: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15],
|
||||
FC_CORRUPTED: [14]
|
||||
};
|
||||
|
||||
export const sortieFactionToFactionIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0],
|
||||
FC_CORPUS: [1],
|
||||
FC_INFESTATION: [0, 1, 2],
|
||||
FC_CORRUPTED: [3]
|
||||
};
|
||||
|
||||
export const sortieBossNode: Record<string, string> = {
|
||||
SORTIE_BOSS_HYENA: "SolNode127",
|
||||
SORTIE_BOSS_KELA: "SolNode193",
|
||||
SORTIE_BOSS_VOR: "SolNode108",
|
||||
SORTIE_BOSS_RUK: "SolNode32",
|
||||
SORTIE_BOSS_HEK: "SolNode24",
|
||||
SORTIE_BOSS_KRIL: "SolNode99",
|
||||
SORTIE_BOSS_TYL: "SolNode105",
|
||||
SORTIE_BOSS_JACKAL: "SolNode104",
|
||||
SORTIE_BOSS_ALAD: "SolNode53",
|
||||
SORTIE_BOSS_AMBULAS: "SolNode51",
|
||||
SORTIE_BOSS_NEF: "SettlementNode20",
|
||||
SORTIE_BOSS_RAPTOR: "SolNode210",
|
||||
SORTIE_BOSS_LEPHANTIS: "SolNode712",
|
||||
SORTIE_BOSS_INFALAD: "SolNode705"
|
||||
};
|
||||
|
||||
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
|
||||
|
||||
export const getSortieTime = (day: number): number => {
|
||||
const dayStart = EPOCH + day * 86400000;
|
||||
const date = new Date(dayStart);
|
||||
date.setUTCHours(12);
|
||||
const isDst = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: "America/Toronto",
|
||||
timeZoneName: "short"
|
||||
})
|
||||
.formatToParts(date)
|
||||
.find(part => part.type === "timeZoneName")!
|
||||
.value.includes("DT");
|
||||
return dayStart + (isDst ? 16 : 17) * 3600000;
|
||||
};
|
@ -23,7 +23,6 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
|
||||
Type: String,
|
||||
Pos: [Number],
|
||||
Rot: [Number],
|
||||
Scale: Number,
|
||||
Name: String,
|
||||
Sockets: Number,
|
||||
RegularCredits: Number,
|
||||
@ -44,7 +43,6 @@ const dojoLeaderboardEntrySchema = new Schema<IDojoLeaderboardEntry>(
|
||||
);
|
||||
|
||||
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
||||
SortId: Schema.Types.ObjectId,
|
||||
pf: { type: String, required: true },
|
||||
ppf: String,
|
||||
pi: Schema.Types.ObjectId,
|
||||
@ -192,7 +190,6 @@ const guildSchema = new Schema<IGuildDatabase>(
|
||||
VaultMiscItems: { type: [typeCountSchema], default: undefined },
|
||||
VaultShipDecorations: { type: [typeCountSchema], default: undefined },
|
||||
VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
|
||||
VaultDecoRecipes: { type: [typeCountSchema], default: undefined },
|
||||
TechProjects: { type: [techProjectSchema], default: undefined },
|
||||
ActiveDojoColorResearch: { type: String, default: "" },
|
||||
Class: { type: Number, default: 0 },
|
||||
|
@ -5,7 +5,7 @@ import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export interface IMessageClient
|
||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"> {
|
||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly"> {
|
||||
_id?: IOid;
|
||||
date: IMongoDate;
|
||||
startDate?: IMongoDate;
|
||||
@ -16,8 +16,6 @@ export interface IMessageClient
|
||||
export interface IMessageDatabase extends IMessage {
|
||||
ownerId: Types.ObjectId;
|
||||
date: Date; //created at
|
||||
attVisualOnly?: boolean;
|
||||
expiry?: Date;
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
@ -32,6 +30,7 @@ export interface IMessage {
|
||||
endDate?: Date;
|
||||
att?: string[];
|
||||
countedAtt?: ITypeCount[];
|
||||
attVisualOnly?: boolean;
|
||||
transmission?: string;
|
||||
arg?: Arg[];
|
||||
gifts?: IGift[];
|
||||
@ -138,14 +137,14 @@ messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
|
||||
messageSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
delete returnedObject.ownerId;
|
||||
|
||||
const messageDatabase = returnedObject as IMessageDatabase;
|
||||
const messageClient = returnedObject as IMessageClient;
|
||||
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.ownerId;
|
||||
delete returnedObject.attVisualOnly;
|
||||
delete returnedObject.expiry;
|
||||
|
||||
messageClient.date = toMongoDate(messageDatabase.date);
|
||||
|
||||
@ -158,6 +157,5 @@ messageSchema.set("toJSON", {
|
||||
});
|
||||
|
||||
messageSchema.index({ ownerId: 1 });
|
||||
messageSchema.index({ expiry: 1 }, { expireAfterSeconds: 0 });
|
||||
|
||||
export const Inbox = model<IMessageDatabase>("Inbox", messageSchema, "inbox");
|
||||
|
@ -39,9 +39,10 @@ import {
|
||||
ILoreFragmentScan,
|
||||
IEvolutionProgress,
|
||||
IEndlessXpProgress,
|
||||
ICrewShipPortGuns,
|
||||
ICrewShipCustomization,
|
||||
ICrewShipWeapon,
|
||||
ICrewShipWeaponEmplacements,
|
||||
ICrewShipPilotWeapon,
|
||||
IShipExterior,
|
||||
IHelminthFoodRecord,
|
||||
ICrewShipMembersDatabase,
|
||||
@ -83,21 +84,7 @@ import {
|
||||
IInfNode,
|
||||
IDiscoveredMarker,
|
||||
IWeeklyMission,
|
||||
ILockedWeaponGroupDatabase,
|
||||
IPersonalTechProjectDatabase,
|
||||
IPersonalTechProjectClient,
|
||||
ILastSortieRewardDatabase,
|
||||
ILastSortieRewardClient,
|
||||
ICrewMemberSkill,
|
||||
ICrewMemberSkillEfficiency,
|
||||
ICrewMemberDatabase,
|
||||
ICrewMemberClient,
|
||||
ISortieRewardAttenuation,
|
||||
IInvasionProgressDatabase,
|
||||
IInvasionProgressClient,
|
||||
IAccolades,
|
||||
IHubNpcCustomization,
|
||||
ILotusCustomization
|
||||
ILockedWeaponGroupDatabase
|
||||
} from "../../types/inventoryTypes/inventoryTypes";
|
||||
import { IOid } from "../../types/commonTypes";
|
||||
import {
|
||||
@ -111,7 +98,7 @@ import {
|
||||
IEquipmentClient
|
||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { EquipmentSelectionSchema, oidSchema } from "./loadoutModel";
|
||||
import { EquipmentSelectionSchema } from "./loadoutModel";
|
||||
|
||||
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
||||
|
||||
@ -303,55 +290,6 @@ upgradeSchema.set("toJSON", {
|
||||
}
|
||||
});
|
||||
|
||||
const crewMemberSkillSchema = new Schema<ICrewMemberSkill>(
|
||||
{
|
||||
Assigned: Number
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const crewMemberSkillEfficiencySchema = new Schema<ICrewMemberSkillEfficiency>(
|
||||
{
|
||||
PILOTING: crewMemberSkillSchema,
|
||||
GUNNERY: crewMemberSkillSchema,
|
||||
ENGINEERING: crewMemberSkillSchema,
|
||||
COMBAT: crewMemberSkillSchema,
|
||||
SURVIVABILITY: crewMemberSkillSchema
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const crewMemberSchema = new Schema<ICrewMemberDatabase>(
|
||||
{
|
||||
ItemType: { type: String, required: true },
|
||||
NemesisFingerprint: { type: BigInt, default: 0n },
|
||||
Seed: { type: BigInt, default: 0n },
|
||||
AssignedRole: Number,
|
||||
SkillEfficiency: crewMemberSkillEfficiencySchema,
|
||||
WeaponConfigIdx: Number,
|
||||
WeaponId: { type: Schema.Types.ObjectId, default: "000000000000000000000000" },
|
||||
XP: { type: Number, default: 0 },
|
||||
PowersuitType: { type: String, required: true },
|
||||
Configs: [ItemConfigSchema],
|
||||
SecondInCommand: { type: Boolean, default: false }
|
||||
},
|
||||
{ id: false }
|
||||
);
|
||||
|
||||
crewMemberSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
const db = obj as ICrewMemberDatabase;
|
||||
const client = obj as ICrewMemberClient;
|
||||
|
||||
client.WeaponId = toOid(db.WeaponId);
|
||||
client.ItemId = toOid(db._id);
|
||||
|
||||
delete obj._id;
|
||||
delete obj.__v;
|
||||
}
|
||||
});
|
||||
|
||||
const slotsBinSchema = new Schema<ISlots>(
|
||||
{
|
||||
Slots: Number,
|
||||
@ -391,7 +329,7 @@ MailboxSchema.set("toJSON", {
|
||||
|
||||
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||
{
|
||||
Seed: BigInt,
|
||||
Seed: Number,
|
||||
NumCompletions: { type: Number, default: 0 }
|
||||
},
|
||||
{
|
||||
@ -560,39 +498,7 @@ const seasonChallengeHistorySchema = new Schema<ISeasonChallenge>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const personalTechProjectSchema = new Schema<IPersonalTechProjectDatabase>({
|
||||
State: Number,
|
||||
ReqCredits: Number,
|
||||
ItemType: String,
|
||||
ProductCategory: String,
|
||||
CategoryItemId: Schema.Types.ObjectId,
|
||||
ReqItems: { type: [typeCountSchema], default: undefined },
|
||||
HasContributions: Boolean,
|
||||
CompletionDate: Date
|
||||
});
|
||||
|
||||
personalTechProjectSchema.virtual("ItemId").get(function () {
|
||||
return { $oid: this._id.toString() };
|
||||
});
|
||||
|
||||
personalTechProjectSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
|
||||
const db = ret as IPersonalTechProjectDatabase;
|
||||
const client = ret as IPersonalTechProjectClient;
|
||||
|
||||
if (db.CategoryItemId) {
|
||||
client.CategoryItemId = toOid(db.CategoryItemId);
|
||||
}
|
||||
if (db.CompletionDate) {
|
||||
client.CompletionDate = toMongoDate(db.CompletionDate);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: check whether this is complete
|
||||
const playerSkillsSchema = new Schema<IPlayerSkills>(
|
||||
{
|
||||
LPP_SPACE: { type: Number, default: 0 },
|
||||
@ -689,27 +595,6 @@ questKeysSchema.set("toJSON", {
|
||||
|
||||
export const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema).add({ Sockets: Number });
|
||||
|
||||
const invasionProgressSchema = new Schema<IInvasionProgressDatabase>(
|
||||
{
|
||||
invasionId: Schema.Types.ObjectId,
|
||||
Delta: Number,
|
||||
AttackerScore: Number,
|
||||
DefenderScore: Number
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
invasionProgressSchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
const db = obj as IInvasionProgressDatabase;
|
||||
const client = obj as IInvasionProgressClient;
|
||||
|
||||
client._id = toOid(db.invasionId);
|
||||
delete obj.invasionId;
|
||||
delete obj.__v;
|
||||
}
|
||||
});
|
||||
|
||||
const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
|
||||
{
|
||||
ItemType: String,
|
||||
@ -727,7 +612,6 @@ const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
|
||||
const weaponSkinsSchema = new Schema<IWeaponSkinDatabase>(
|
||||
{
|
||||
ItemType: String,
|
||||
Favorite: Boolean,
|
||||
IsNew: Boolean
|
||||
},
|
||||
{ id: false }
|
||||
@ -781,26 +665,6 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
// const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({
|
||||
// Persona: String
|
||||
// });
|
||||
|
||||
// Laxer schema for cleanupInventory
|
||||
const lotusCustomizationSchema = new Schema<ILotusCustomization>(
|
||||
{
|
||||
Skins: [String],
|
||||
pricol: colorSchema,
|
||||
attcol: Schema.Types.Mixed,
|
||||
sigcol: Schema.Types.Mixed,
|
||||
eyecol: Schema.Types.Mixed,
|
||||
facial: Schema.Types.Mixed,
|
||||
cloth: Schema.Types.Mixed,
|
||||
syancol: Schema.Types.Mixed,
|
||||
Persona: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const evolutionProgressSchema = new Schema<IEvolutionProgress>(
|
||||
{
|
||||
Progress: Number,
|
||||
@ -818,23 +682,25 @@ const endlessXpProgressSchema = new Schema<IEndlessXpProgress>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const crewShipWeaponEmplacementsSchema = new Schema<ICrewShipWeaponEmplacements>(
|
||||
const crewShipPilotWeaponSchema = new Schema<ICrewShipPilotWeapon>(
|
||||
{
|
||||
PRIMARY_A: EquipmentSelectionSchema,
|
||||
PRIMARY_B: EquipmentSelectionSchema,
|
||||
SECONDARY_A: EquipmentSelectionSchema,
|
||||
SECONDARY_B: EquipmentSelectionSchema
|
||||
SECONDARY_A: EquipmentSelectionSchema
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const crewShipPortGunsSchema = new Schema<ICrewShipPortGuns>(
|
||||
{
|
||||
PRIMARY_A: EquipmentSelectionSchema
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const crewShipWeaponSchema = new Schema<ICrewShipWeapon>(
|
||||
{
|
||||
PILOT: crewShipWeaponEmplacementsSchema,
|
||||
PORT_GUNS: crewShipWeaponEmplacementsSchema,
|
||||
STARBOARD_GUNS: crewShipWeaponEmplacementsSchema,
|
||||
ARTILLERY: crewShipWeaponEmplacementsSchema,
|
||||
SCANNER: crewShipWeaponEmplacementsSchema
|
||||
PILOT: crewShipPilotWeaponSchema,
|
||||
PORT_GUNS: crewShipPortGunsSchema
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -858,7 +724,7 @@ const crewShipCustomizationSchema = new Schema<ICrewShipCustomization>(
|
||||
const crewShipMemberSchema = new Schema<ICrewShipMemberDatabase>(
|
||||
{
|
||||
ItemId: { type: Schema.Types.ObjectId, required: false },
|
||||
NemesisFingerprint: { type: BigInt, required: false }
|
||||
NemesisFingerprint: { type: Number, required: false }
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -930,7 +796,7 @@ dialogueSchema.set("toJSON", {
|
||||
|
||||
const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
|
||||
{
|
||||
YearIteration: Number,
|
||||
YearIteration: { type: Number, required: true },
|
||||
Resets: Number,
|
||||
Dialogues: { type: [dialogueSchema], required: false }
|
||||
},
|
||||
@ -1014,7 +880,6 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
|
||||
RailjackImage: FlavourItemSchema,
|
||||
CrewMembers: crewShipMembersSchema,
|
||||
Details: detailsSchema,
|
||||
Favorite: Boolean,
|
||||
IsNew: Boolean
|
||||
},
|
||||
{ id: false }
|
||||
@ -1055,8 +920,6 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
|
||||
{
|
||||
ItemType: String,
|
||||
CompletionDate: Date,
|
||||
TargetItemId: String,
|
||||
TargetFingerprint: String,
|
||||
LongGuns: { type: [EquipmentSchema], default: undefined },
|
||||
Pistols: { type: [EquipmentSchema], default: undefined },
|
||||
Melee: { type: [EquipmentSchema], default: undefined },
|
||||
@ -1084,13 +947,6 @@ pendingRecipeSchema.set("toJSON", {
|
||||
}
|
||||
});
|
||||
|
||||
const accoladesSchema = new Schema<IAccolades>(
|
||||
{
|
||||
Heirloom: Boolean
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
|
||||
{
|
||||
Name: String,
|
||||
@ -1148,15 +1004,15 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
|
||||
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
||||
{
|
||||
Version: { type: Number, default: 19 },
|
||||
Iteration: { type: Number, required: true },
|
||||
Iteration: { type: Number, default: 2 },
|
||||
YearProgress: {
|
||||
Upgrades: { type: [String], default: [] }
|
||||
Upgrades: { type: [] }
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: { type: String, required: true },
|
||||
LastCompletedDayIdx: { type: Number, default: 0 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: 0 },
|
||||
ActivatedChallenges: { type: [String], default: [] }
|
||||
SeasonType: String,
|
||||
LastCompletedDayIdx: { type: Number, default: -1 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: -1 },
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
},
|
||||
{ _id: false }
|
||||
@ -1278,11 +1134,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
||||
PrevOwners: Number,
|
||||
SecondInCommand: Boolean,
|
||||
Weakened: Boolean,
|
||||
InfNodes: { type: [infNodeSchema], default: undefined },
|
||||
InfNodes: [infNodeSchema],
|
||||
HenchmenKilled: Number,
|
||||
HintProgress: Number,
|
||||
Hints: { type: [Number], default: undefined },
|
||||
GuessHistory: { type: [Number], default: undefined },
|
||||
Hints: [Number],
|
||||
GuessHistory: [Number],
|
||||
MissionCount: Number,
|
||||
LastEnc: Number
|
||||
},
|
||||
@ -1310,36 +1166,6 @@ const alignmentSchema = new Schema<IAlignment>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const lastSortieRewardSchema = new Schema<ILastSortieRewardDatabase>(
|
||||
{
|
||||
SortieId: Schema.Types.ObjectId,
|
||||
StoreItem: String,
|
||||
Manifest: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
lastSortieRewardSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
const db = obj as ILastSortieRewardDatabase;
|
||||
const client = obj as ILastSortieRewardClient;
|
||||
|
||||
client.SortieId = toOid(db.SortieId);
|
||||
|
||||
delete obj._id;
|
||||
delete obj.__v;
|
||||
}
|
||||
});
|
||||
|
||||
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>(
|
||||
{
|
||||
Tag: String,
|
||||
Atten: Number
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
|
||||
{
|
||||
s: Schema.Types.ObjectId,
|
||||
@ -1351,21 +1177,12 @@ const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const hubNpcCustomizationSchema = new Schema<IHubNpcCustomization>(
|
||||
{
|
||||
Colors: colorSchema,
|
||||
Pattern: String,
|
||||
Tag: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
{
|
||||
accountOwnerId: Schema.Types.ObjectId,
|
||||
SubscribedToEmails: { type: Number, default: 0 },
|
||||
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
|
||||
RewardSeed: BigInt,
|
||||
RewardSeed: Number,
|
||||
|
||||
//Credit
|
||||
RegularCredits: { type: Number, default: 0 },
|
||||
@ -1399,7 +1216,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//How many Gift do you have left*(gift spends the trade)
|
||||
GiftsRemaining: { type: Number, default: 8 },
|
||||
//Curent trade info Giving or Getting items
|
||||
//PendingTrades: [Schema.Types.Mixed],
|
||||
PendingTrades: [Schema.Types.Mixed],
|
||||
|
||||
//Syndicate currently being pledged to.
|
||||
SupportedSyndicate: String,
|
||||
@ -1449,7 +1266,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
KubrowPetEggs: [kubrowPetEggSchema],
|
||||
//Prints Cat(3 Prints)\Kubrow(2 Prints) Pets
|
||||
//KubrowPetPrints: [Schema.Types.Mixed],
|
||||
KubrowPetPrints: [Schema.Types.Mixed],
|
||||
|
||||
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc
|
||||
Consumables: [typeCountSchema],
|
||||
@ -1486,7 +1303,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
CrewShipSalvagedWeaponSkins: [upgradeSchema],
|
||||
|
||||
//RailJack Crew
|
||||
CrewMembers: [crewMemberSchema],
|
||||
CrewMembers: [Schema.Types.Mixed],
|
||||
|
||||
//Complete Mission\Quests
|
||||
Missions: [missionSchema],
|
||||
@ -1495,7 +1312,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//item like DojoKey or Boss missions key
|
||||
LevelKeys: [typeCountSchema],
|
||||
//Active quests
|
||||
//Quests: [Schema.Types.Mixed],
|
||||
Quests: [Schema.Types.Mixed],
|
||||
|
||||
//Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
|
||||
FlavourItems: [FlavourItemSchema],
|
||||
@ -1507,16 +1324,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//Mastery Rank next availability
|
||||
TrainingDate: { type: Date, default: new Date(0) },
|
||||
|
||||
//Accolades
|
||||
Staff: Boolean,
|
||||
Founder: Number,
|
||||
Guide: Number,
|
||||
Moderator: Boolean,
|
||||
Partner: Boolean,
|
||||
Accolades: accoladesSchema,
|
||||
//Not an accolade but unlocks an extra chat
|
||||
Counselor: Boolean,
|
||||
|
||||
//you saw last played Region when you opened the star map
|
||||
LastRegionPlayed: String,
|
||||
|
||||
@ -1534,7 +1341,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
TauntHistory: { type: [tauntSchema], default: undefined },
|
||||
|
||||
//noShow2FA,VisitPrimeVault etc
|
||||
//WebFlags: Schema.Types.Mixed,
|
||||
WebFlags: Schema.Types.Mixed,
|
||||
//Id CompletedAlerts
|
||||
CompletedAlerts: [String],
|
||||
|
||||
@ -1554,9 +1361,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB
|
||||
ActiveDojoColorResearch: String,
|
||||
|
||||
//SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
||||
SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
||||
|
||||
QualifyingInvasions: [invasionProgressSchema],
|
||||
QualifyingInvasions: [Schema.Types.Mixed],
|
||||
FactionScores: [Number],
|
||||
|
||||
// https://warframe.fandom.com/wiki/Specter_(Tenno)
|
||||
@ -1576,9 +1383,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//https://warframe.fandom.com/wiki/Sortie
|
||||
CompletedSorties: [String],
|
||||
LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
|
||||
LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
|
||||
SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined },
|
||||
LastSortieReward: [Schema.Types.Mixed],
|
||||
|
||||
// Resource Extractor Drones
|
||||
Drones: [droneSchema],
|
||||
@ -1589,10 +1394,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
|
||||
DiscoveredMarkers: [discoveredMarkerSchema],
|
||||
//Open location mission like "JobId" + "StageCompletions"
|
||||
//CompletedJobs: [Schema.Types.Mixed],
|
||||
CompletedJobs: [Schema.Types.Mixed],
|
||||
|
||||
//Game mission\ivent score example "Tag": "WaterFight", "Best": 170, "Count": 1258,
|
||||
//PersonalGoalProgress: [Schema.Types.Mixed],
|
||||
PersonalGoalProgress: [Schema.Types.Mixed],
|
||||
|
||||
//Setting interface Style
|
||||
ThemeStyle: String,
|
||||
@ -1610,7 +1415,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//https://warframe.fandom.com/wiki/Heist
|
||||
//ProfitTaker(1-4) Example:"LocationTag": "EudicoHeists", "Jobs":Mission name
|
||||
CompletedJobChains: { type: [completedJobChainsSchema], default: undefined },
|
||||
CompletedJobChains: [completedJobChainsSchema],
|
||||
//Night Wave Challenge
|
||||
SeasonChallengeHistory: [seasonChallengeHistorySchema],
|
||||
|
||||
@ -1622,27 +1427,27 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema,
|
||||
|
||||
//https://warframe.fandom.com/wiki/Invasion
|
||||
//InvasionChainProgress: [Schema.Types.Mixed],
|
||||
InvasionChainProgress: [Schema.Types.Mixed],
|
||||
|
||||
//CorpusLich or GrineerLich
|
||||
NemesisAbandonedRewards: { type: [String], default: [] },
|
||||
Nemesis: nemesisSchema,
|
||||
NemesisHistory: { type: [nemesisSchema], default: undefined },
|
||||
//LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
||||
NemesisHistory: [Schema.Types.Mixed],
|
||||
LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
||||
|
||||
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
|
||||
Settings: settingsSchema,
|
||||
|
||||
//Railjack craft
|
||||
//https://warframe.fandom.com/wiki/Rising_Tide
|
||||
PersonalTechProjects: { type: [personalTechProjectSchema], default: [] },
|
||||
PersonalTechProjects: [Schema.Types.Mixed],
|
||||
|
||||
//Modulars lvl and exp(Railjack|Duviri)
|
||||
//https://warframe.fandom.com/wiki/Intrinsics
|
||||
PlayerSkills: { type: playerSkillsSchema, default: {} },
|
||||
|
||||
//TradeBannedUntil data
|
||||
//TradeBannedUntil: Schema.Types.Mixed,
|
||||
TradeBannedUntil: Schema.Types.Mixed,
|
||||
|
||||
//https://warframe.fandom.com/wiki/Helminth
|
||||
InfestedFoundry: infestedFoundrySchema,
|
||||
@ -1651,7 +1456,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter.
|
||||
//https://warframe.fandom.com/wiki/Lotus#The_New_War
|
||||
LotusCustomization: { type: lotusCustomizationSchema, default: undefined },
|
||||
LotusCustomization: Schema.Types.Mixed,
|
||||
|
||||
//Progress+Rank+ItemType(ZarimanPumpShotgun)
|
||||
//https://warframe.fandom.com/wiki/Incarnon
|
||||
@ -1662,24 +1467,23 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//Unknown and system
|
||||
DuviriInfo: DuviriInfoSchema,
|
||||
LastInventorySync: Schema.Types.ObjectId,
|
||||
Mailbox: MailboxSchema,
|
||||
HandlerPoints: Number,
|
||||
ChallengesFixVersion: { type: Number, default: 6 },
|
||||
ChallengesFixVersion: Number,
|
||||
PlayedParkourTutorial: Boolean,
|
||||
//ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||
//RepVotes: [Schema.Types.Mixed],
|
||||
//LeagueTickets: [Schema.Types.Mixed],
|
||||
ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||
RepVotes: [Schema.Types.Mixed],
|
||||
LeagueTickets: [Schema.Types.Mixed],
|
||||
HasContributedToDojo: Boolean,
|
||||
HWIDProtectEnabled: Boolean,
|
||||
LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" },
|
||||
CurrentLoadOutIds: [oidSchema],
|
||||
CurrentLoadOutIds: [Schema.Types.Mixed],
|
||||
RandomUpgradesIdentified: Number,
|
||||
BountyScore: Number,
|
||||
//ChallengeInstanceStates: [Schema.Types.Mixed],
|
||||
ChallengeInstanceStates: [Schema.Types.Mixed],
|
||||
RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
|
||||
//Robotics: [Schema.Types.Mixed],
|
||||
//UsedDailyDeals: [Schema.Types.Mixed],
|
||||
Robotics: [Schema.Types.Mixed],
|
||||
UsedDailyDeals: [Schema.Types.Mixed],
|
||||
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
|
||||
HasResetAccount: { type: Boolean, default: false },
|
||||
|
||||
@ -1714,9 +1518,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
// G3 + Zanuka
|
||||
BrandedSuits: { type: [Schema.Types.ObjectId], default: undefined },
|
||||
LockedWeaponGroup: { type: lockedWeaponGroupSchema, default: undefined },
|
||||
|
||||
HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined }
|
||||
LockedWeaponGroup: { type: lockedWeaponGroupSchema, default: undefined }
|
||||
},
|
||||
{ timestamps: { createdAt: "Created", updatedAt: false } }
|
||||
);
|
||||
@ -1760,9 +1562,6 @@ inventorySchema.set("toJSON", {
|
||||
sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined
|
||||
};
|
||||
}
|
||||
if (inventoryDatabase.LastInventorySync) {
|
||||
inventoryResponse.LastInventorySync = toOid(inventoryDatabase.LastInventorySync);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1783,9 +1582,7 @@ export type InventoryDocumentProps = {
|
||||
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
|
||||
Drones: Types.DocumentArray<IDroneDatabase>;
|
||||
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||
CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||
PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
|
||||
CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
|
||||
CrewShipSalvagedWeaponsSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
|
@ -3,7 +3,7 @@ import { IEquipmentSelection } from "@/src/types/inventoryTypes/commonInventoryT
|
||||
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
||||
import { Document, Model, Schema, Types, model } from "mongoose";
|
||||
|
||||
export const oidSchema = new Schema<IOid>(
|
||||
const oidSchema = new Schema<IOid>(
|
||||
{
|
||||
$oid: String
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { IDatabaseAccountJson, IIgnore } from "@/src/types/loginTypes";
|
||||
import { IDatabaseAccountJson } from "@/src/types/loginTypes";
|
||||
import { model, Schema, SchemaOptions } from "mongoose";
|
||||
|
||||
const opts = {
|
||||
@ -37,13 +37,3 @@ databaseAccountSchema.set("toJSON", {
|
||||
});
|
||||
|
||||
export const Account = model<IDatabaseAccountJson>("Account", databaseAccountSchema);
|
||||
|
||||
const ignoreSchema = new Schema<IIgnore>({
|
||||
ignorer: Schema.Types.ObjectId,
|
||||
ignoree: Schema.Types.ObjectId
|
||||
});
|
||||
|
||||
ignoreSchema.index({ ignorer: 1 });
|
||||
ignoreSchema.index({ ignorer: 1, ignoree: 1 }, { unique: true });
|
||||
|
||||
export const Ignore = model<IIgnore>("Ignore", ignoreSchema);
|
||||
|
@ -1,17 +1,14 @@
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
|
||||
import {
|
||||
IApartment,
|
||||
IFavouriteLoadoutDatabase,
|
||||
IGardeningDatabase,
|
||||
IGardening,
|
||||
IPlacedDecosDatabase,
|
||||
IPictureFrameInfo,
|
||||
IRoom,
|
||||
ITailorShopDatabase,
|
||||
IApartmentDatabase,
|
||||
IPlanterDatabase,
|
||||
IPlantDatabase,
|
||||
IPlantClient
|
||||
ITailorShopDatabase
|
||||
} from "@/src/types/shipTypes";
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
@ -65,64 +62,19 @@ const roomSchema = new Schema<IRoom>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
||||
{
|
||||
Tag: String,
|
||||
LoadoutId: Schema.Types.ObjectId
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
favouriteLoadoutSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
||||
}
|
||||
const gardeningSchema = new Schema<IGardening>({
|
||||
Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
|
||||
});
|
||||
|
||||
const plantSchema = new Schema<IPlantDatabase>(
|
||||
{
|
||||
PlantType: String,
|
||||
EndTime: Date,
|
||||
PlotIndex: Number
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
plantSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
const client = obj as IPlantClient;
|
||||
const db = obj as IPlantDatabase;
|
||||
|
||||
client.EndTime = toMongoDate(db.EndTime);
|
||||
}
|
||||
});
|
||||
|
||||
const planterSchema = new Schema<IPlanterDatabase>(
|
||||
{
|
||||
Name: { type: String, required: true },
|
||||
Plants: { type: [plantSchema], default: [] }
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const gardeningSchema = new Schema<IGardeningDatabase>(
|
||||
{
|
||||
Planters: { type: [planterSchema], default: [] }
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const apartmentSchema = new Schema<IApartmentDatabase>(
|
||||
const apartmentSchema = new Schema<IApartment>(
|
||||
{
|
||||
Rooms: [roomSchema],
|
||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||
Gardening: gardeningSchema
|
||||
FavouriteLoadouts: [Schema.Types.Mixed],
|
||||
Gardening: gardeningSchema // TODO: ensure this is correct
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
const apartmentDefault: IApartmentDatabase = {
|
||||
const apartmentDefault: IApartment = {
|
||||
Rooms: [
|
||||
{ Name: "ElevatorLanding", MaxCapacity: 1600 },
|
||||
{ Name: "ApartmentRoomA", MaxCapacity: 1000 },
|
||||
@ -131,19 +83,13 @@ const apartmentDefault: IApartmentDatabase = {
|
||||
{ Name: "DuviriHallway", MaxCapacity: 1600 }
|
||||
],
|
||||
FavouriteLoadouts: [],
|
||||
Gardening: {
|
||||
Planters: []
|
||||
}
|
||||
Gardening: {}
|
||||
};
|
||||
|
||||
const orbiterSchema = new Schema<IOrbiter>(
|
||||
{
|
||||
Features: [String],
|
||||
Rooms: [roomSchema],
|
||||
VignetteFish: { type: [String], default: undefined },
|
||||
FavouriteLoadoutId: Schema.Types.ObjectId,
|
||||
Wallpaper: String,
|
||||
Vignette: String,
|
||||
ContentUrlSignature: { type: String, required: false },
|
||||
BootLocation: String
|
||||
},
|
||||
@ -161,6 +107,21 @@ const orbiterDefault: IOrbiter = {
|
||||
]
|
||||
};
|
||||
|
||||
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
||||
{
|
||||
Tag: String,
|
||||
LoadoutId: Schema.Types.ObjectId
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
favouriteLoadoutSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
||||
}
|
||||
});
|
||||
|
||||
const tailorShopSchema = new Schema<ITailorShopDatabase>(
|
||||
{
|
||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||
|
@ -4,7 +4,6 @@ import { abortDojoComponentController } from "@/src/controllers/api/abortDojoCom
|
||||
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
|
||||
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
|
||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
|
||||
import { addIgnoredUserController } from "@/src/controllers/api/addIgnoredUserController";
|
||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||
@ -19,7 +18,6 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
|
||||
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
|
||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
|
||||
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
|
||||
import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController";
|
||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
||||
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
||||
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
||||
@ -29,8 +27,6 @@ import { contributeToVaultController } from "@/src/controllers/api/contributeToV
|
||||
import { createAllianceController } from "@/src/controllers/api/createAllianceController";
|
||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
||||
import { creditsController } from "@/src/controllers/api/creditsController";
|
||||
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
|
||||
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
|
||||
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
|
||||
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
|
||||
import { declineAllianceInviteController } from "@/src/controllers/api/declineAllianceInviteController";
|
||||
@ -39,7 +35,7 @@ import { deleteSessionController } from "@/src/controllers/api/deleteSessionCont
|
||||
import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController";
|
||||
import { divvyAllianceVaultController } from "@/src/controllers/api/divvyAllianceVaultController";
|
||||
import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController";
|
||||
import { dojoController, setDojoURLController } from "@/src/controllers/api/dojoController";
|
||||
import { dojoController } from "@/src/controllers/api/dojoController";
|
||||
import { dronesController } from "@/src/controllers/api/dronesController";
|
||||
import { endlessXpController } from "@/src/controllers/api/endlessXpController";
|
||||
import { entratiLabConquestModeController } from "@/src/controllers/api/entratiLabConquestModeController";
|
||||
@ -48,7 +44,6 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
|
||||
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
|
||||
import { focusController } from "@/src/controllers/api/focusController";
|
||||
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
|
||||
import { gardeningController } from "@/src/controllers/api/gardeningController";
|
||||
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
|
||||
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
|
||||
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
|
||||
@ -66,8 +61,7 @@ import { giftingController } from "@/src/controllers/api/giftingController";
|
||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
|
||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
|
||||
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKeyRewardController";
|
||||
import { giveShipDecoAndLoreFragmentController } from "@/src/controllers/api/giveShipDecoAndLoreFragmentController";
|
||||
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
|
||||
import { giveStartingGearController } from "@/src/controllers/api/giveStartingGearController";
|
||||
import { guildTechController } from "@/src/controllers/api/guildTechController";
|
||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
|
||||
@ -91,7 +85,6 @@ import { modularWeaponSaleController } from "@/src/controllers/api/modularWeapon
|
||||
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
|
||||
import { nemesisController } from "@/src/controllers/api/nemesisController";
|
||||
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
|
||||
import { playedParkourTutorialController } from "@/src/controllers/api/playedParkourTutorialController";
|
||||
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
|
||||
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
|
||||
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
|
||||
@ -101,15 +94,13 @@ import { redeemPromoCodeController } from "@/src/controllers/api/redeemPromoCode
|
||||
import { releasePetController } from "@/src/controllers/api/releasePetController";
|
||||
import { removeFromAllianceController } from "@/src/controllers/api/removeFromAllianceController";
|
||||
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
|
||||
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
||||
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
||||
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadoutController";
|
||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
|
||||
import { saveSettingsController } from "@/src/controllers/api/saveSettingsController";
|
||||
import { saveVaultAutoContributeController } from "@/src/controllers/api/saveVaultAutoContributeController";
|
||||
import { sellController } from "@/src/controllers/api/sellController";
|
||||
import { sendMsgToInBoxController } from "@/src/controllers/api/sendMsgToInBoxController";
|
||||
import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
|
||||
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
|
||||
import { setAllianceGuildPermissionsController } from "@/src/controllers/api/setAllianceGuildPermissionsController";
|
||||
@ -119,11 +110,9 @@ import { setDojoComponentMessageController } from "@/src/controllers/api/setDojo
|
||||
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
|
||||
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
|
||||
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
|
||||
import { setHubNpcCustomizationsController } from "@/src/controllers/api/setHubNpcCustomizationsController";
|
||||
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
|
||||
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
|
||||
import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController";
|
||||
import { setShipVignetteController } from "@/src/controllers/api/setShipVignetteController";
|
||||
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
|
||||
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
|
||||
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
|
||||
@ -156,11 +145,9 @@ const apiRouter = express.Router();
|
||||
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
||||
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
||||
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
||||
apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
||||
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
||||
apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController);
|
||||
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
||||
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
||||
apiRouter.get("/credits.php", creditsController);
|
||||
@ -189,14 +176,12 @@ apiRouter.get("/logout.php", logoutController);
|
||||
apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
|
||||
apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
|
||||
apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
|
||||
apiRouter.get("/playedParkourTutorial.php", playedParkourTutorialController);
|
||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
||||
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
||||
apiRouter.get("/setBootLocation.php", setBootLocationController);
|
||||
apiRouter.get("/setDojoURL", setDojoURLController);
|
||||
apiRouter.get("/setGuildMotd.php", setGuildMotdController);
|
||||
apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController);
|
||||
apiRouter.get("/startLibraryDailyTask.php", startLibraryDailyTaskController);
|
||||
@ -209,7 +194,6 @@ apiRouter.get("/updateSession.php", updateSessionGetController);
|
||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
||||
apiRouter.post("/addIgnoredUser.php", addIgnoredUserController);
|
||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
|
||||
apiRouter.post("/addToGuild.php", addToGuildController);
|
||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
|
||||
@ -227,8 +211,6 @@ apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentContro
|
||||
apiRouter.post("/contributeToVault.php", contributeToVaultController);
|
||||
apiRouter.post("/createAlliance.php", createAllianceController);
|
||||
apiRouter.post("/createGuild.php", createGuildController);
|
||||
apiRouter.post("/crewMembers.php", crewMembersController);
|
||||
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
|
||||
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
|
||||
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
|
||||
apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController);
|
||||
@ -241,7 +223,6 @@ apiRouter.post("/findSessions.php", findSessionsController);
|
||||
apiRouter.post("/fishmonger.php", fishmongerController);
|
||||
apiRouter.post("/focus.php", focusController);
|
||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
|
||||
apiRouter.post("/gardening.php", gardeningController);
|
||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
|
||||
apiRouter.post("/getAlliance.php", getAllianceController);
|
||||
apiRouter.post("/getFriends.php", getFriendsController);
|
||||
@ -252,7 +233,6 @@ apiRouter.post("/gildWeapon.php", gildWeaponController);
|
||||
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
||||
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
||||
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
||||
apiRouter.post("/giveShipDecoAndLoreFragment.php", giveShipDecoAndLoreFragmentController);
|
||||
apiRouter.post("/giveStartingGear.php", giveStartingGearController);
|
||||
apiRouter.post("/guildTech.php", guildTechController);
|
||||
apiRouter.post("/hostSession.php", hostSessionController);
|
||||
@ -276,7 +256,6 @@ apiRouter.post("/purchase.php", purchaseController);
|
||||
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);
|
||||
apiRouter.post("/releasePet.php", releasePetController);
|
||||
apiRouter.post("/removeFromGuild.php", removeFromGuildController);
|
||||
apiRouter.post("/removeIgnoredUser.php", removeIgnoredUserController);
|
||||
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
|
||||
apiRouter.post("/retrievePetFromStasis.php", retrievePetFromStasisController);
|
||||
apiRouter.post("/saveDialogue.php", saveDialogueController);
|
||||
@ -284,17 +263,14 @@ apiRouter.post("/saveLoadout.php", saveLoadoutController);
|
||||
apiRouter.post("/saveSettings.php", saveSettingsController);
|
||||
apiRouter.post("/saveVaultAutoContribute.php", saveVaultAutoContributeController);
|
||||
apiRouter.post("/sell.php", sellController);
|
||||
apiRouter.post("/sendMsgToInBox.php", sendMsgToInBoxController);
|
||||
apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
|
||||
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
|
||||
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
|
||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
|
||||
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
|
||||
apiRouter.post("/setHubNpcCustomizations.php", setHubNpcCustomizationsController);
|
||||
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
|
||||
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
|
||||
apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController);
|
||||
apiRouter.post("/setShipVignette.php", setShipVignetteController);
|
||||
apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController);
|
||||
apiRouter.post("/shipDecorations.php", shipDecorationsController);
|
||||
apiRouter.post("/startCollectibleEntry.php", startCollectibleEntryController);
|
||||
|
@ -10,19 +10,19 @@ import { getAccountInfoController } from "@/src/controllers/custom/getAccountInf
|
||||
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
|
||||
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
|
||||
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
|
||||
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
|
||||
|
||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
|
||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
|
||||
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
|
||||
import { addItemsController } from "@/src/controllers/custom/addItemsController";
|
||||
import { addModularEquipmentController } from "@/src/controllers/custom/addModularEquipmentController";
|
||||
import { addXpController } from "@/src/controllers/custom/addXpController";
|
||||
import { gildEquipmentController } from "@/src/controllers/custom/gildEquipmentController";
|
||||
import { importController } from "@/src/controllers/custom/importController";
|
||||
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
|
||||
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
||||
|
||||
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
|
||||
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
|
||||
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
|
||||
|
||||
const customRouter = express.Router();
|
||||
|
||||
@ -36,16 +36,16 @@ customRouter.get("/getAccountInfo", getAccountInfoController);
|
||||
customRouter.get("/renameAccount", renameAccountController);
|
||||
customRouter.get("/ircDropped", ircDroppedController);
|
||||
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
||||
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
|
||||
|
||||
customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
customRouter.post("/addCurrency", addCurrencyController);
|
||||
customRouter.post("/addItems", addItemsController);
|
||||
customRouter.post("/addModularEquipment", addModularEquipmentController);
|
||||
customRouter.post("/addXp", addXpController);
|
||||
customRouter.post("/gildEquipment", gildEquipmentController);
|
||||
customRouter.post("/import", importController);
|
||||
customRouter.post("/manageQuests", manageQuestsController);
|
||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||
|
||||
customRouter.get("/config", getConfigDataController);
|
||||
customRouter.post("/config", updateConfigDataController);
|
||||
|
@ -24,29 +24,21 @@ interface IConfig {
|
||||
infiniteEndo?: boolean;
|
||||
infiniteRegalAya?: boolean;
|
||||
infiniteHelminthMaterials?: boolean;
|
||||
dontSubtractConsumables?: boolean;
|
||||
unlockAllShipFeatures?: boolean;
|
||||
unlockAllShipDecorations?: boolean;
|
||||
unlockAllFlavourItems?: boolean;
|
||||
unlockAllSkins?: boolean;
|
||||
unlockAllCapturaScenes?: boolean;
|
||||
unlockAllDecoRecipes?: boolean;
|
||||
universalPolarityEverywhere?: boolean;
|
||||
unlockDoubleCapacityPotatoesEverywhere?: boolean;
|
||||
unlockExilusEverywhere?: boolean;
|
||||
unlockArcanesEverywhere?: boolean;
|
||||
noDailyStandingLimits?: boolean;
|
||||
noDailyFocusLimit?: boolean;
|
||||
noArgonCrystalDecay?: boolean;
|
||||
noMasteryRankUpCooldown?: boolean;
|
||||
noVendorPurchaseLimits?: boolean;
|
||||
noDeathMarks?: boolean;
|
||||
noKimCooldowns?: boolean;
|
||||
instantResourceExtractorDrones?: boolean;
|
||||
noResourceExtractorDronesDamage?: boolean;
|
||||
skipClanKeyCrafting?: boolean;
|
||||
noDojoRoomBuildStage?: boolean;
|
||||
noDojoDecoBuildStage?: boolean;
|
||||
fastDojoRoomDestruction?: boolean;
|
||||
noDojoResearchCosts?: boolean;
|
||||
noDojoResearchTime?: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Request } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addLevelKeys, addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import {
|
||||
@ -30,8 +30,6 @@ import { Inbox } from "../models/inboxModel";
|
||||
import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "../types/purchaseTypes";
|
||||
import { parallelForeach } from "../utils/async-utils";
|
||||
import allDecoRecipes from "@/static/fixed_responses/allDecoRecipes.json";
|
||||
import { createMessage } from "./inboxService";
|
||||
|
||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -59,7 +57,6 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
|
||||
const members: IGuildMemberClient[] = [];
|
||||
let missingEntry = true;
|
||||
const dataFillInPromises: Promise<void>[] = [];
|
||||
for (const guildMember of guildMembers) {
|
||||
const member: IGuildMemberClient = {
|
||||
_id: toOid(guildMember.accountId),
|
||||
@ -71,12 +68,8 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
if (guildMember.accountId.equals(accountId)) {
|
||||
missingEntry = false;
|
||||
} else {
|
||||
dataFillInPromises.push(
|
||||
(async (): Promise<void> => {
|
||||
member.DisplayName = (await Account.findById(guildMember.accountId, "DisplayName"))!.DisplayName;
|
||||
})()
|
||||
);
|
||||
dataFillInPromises.push(fillInInventoryDataForGuildMember(member));
|
||||
await fillInInventoryDataForGuildMember(member);
|
||||
}
|
||||
members.push(member);
|
||||
}
|
||||
@ -95,8 +88,6 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(dataFillInPromises);
|
||||
|
||||
return {
|
||||
_id: toOid(guild._id),
|
||||
Name: guild.Name,
|
||||
@ -105,7 +96,6 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
Members: members,
|
||||
Ranks: guild.Ranks,
|
||||
Tier: guild.Tier,
|
||||
Emblem: guild.Emblem,
|
||||
Vault: getGuildVault(guild),
|
||||
ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
|
||||
Class: guild.Class,
|
||||
@ -124,17 +114,14 @@ export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
|
||||
DojoRefundMiscItems: guild.VaultMiscItems,
|
||||
DojoRefundPremiumCredits: guild.VaultPremiumCredits,
|
||||
ShipDecorations: guild.VaultShipDecorations,
|
||||
FusionTreasures: guild.VaultFusionTreasures,
|
||||
DecoRecipes: config.unlockAllDecoRecipes
|
||||
? allDecoRecipes.map(recipe => ({ ItemType: recipe, ItemCount: 1 }))
|
||||
: guild.VaultDecoRecipes
|
||||
FusionTreasures: guild.VaultFusionTreasures
|
||||
};
|
||||
};
|
||||
|
||||
export const getDojoClient = async (
|
||||
guild: TGuildDatabaseDocument,
|
||||
status: number,
|
||||
componentId?: Types.ObjectId | string
|
||||
componentId: Types.ObjectId | string | undefined = undefined
|
||||
): Promise<IDojoClient> => {
|
||||
const dojo: IDojoClient = {
|
||||
_id: { $oid: guild._id.toString() },
|
||||
@ -160,7 +147,6 @@ export const getDojoClient = async (
|
||||
if (!componentId || dojoComponent._id.equals(componentId)) {
|
||||
const clientComponent: IDojoComponentClient = {
|
||||
id: toOid(dojoComponent._id),
|
||||
SortId: toOid(dojoComponent.SortId ?? dojoComponent._id), // always providing a SortId so decos don't need repositioning to reparent
|
||||
pf: dojoComponent.pf,
|
||||
ppf: dojoComponent.ppf,
|
||||
Name: dojoComponent.Name,
|
||||
@ -223,7 +209,6 @@ export const getDojoClient = async (
|
||||
Type: deco.Type,
|
||||
Pos: deco.Pos,
|
||||
Rot: deco.Rot,
|
||||
Scale: deco.Scale,
|
||||
Name: deco.Name,
|
||||
Sockets: deco.Sockets,
|
||||
PictureFrameInfo: deco.PictureFrameInfo
|
||||
@ -505,7 +490,7 @@ export const hasGuildPermissionEx = (
|
||||
|
||||
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
|
||||
const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
|
||||
await parallelForeach(members, async member => {
|
||||
for (const member of members) {
|
||||
const inventory = await getInventory(member.accountId.toString(), "MiscItems");
|
||||
const index = inventory.MiscItems.findIndex(
|
||||
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
|
||||
@ -514,7 +499,7 @@ export const removePigmentsFromGuildMembers = async (guildId: string | Types.Obj
|
||||
inventory.MiscItems.splice(index, 1);
|
||||
await inventory.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const processGuildTechProjectContributionsUpdate = async (
|
||||
@ -554,7 +539,7 @@ export const setGuildTechLogState = (
|
||||
guild: TGuildDatabaseDocument,
|
||||
type: string,
|
||||
state: number,
|
||||
dateTime?: Date
|
||||
dateTime: Date | undefined = undefined
|
||||
): boolean => {
|
||||
guild.TechChanges ??= [];
|
||||
const entry = guild.TechChanges.find(x => x.details == type);
|
||||
@ -611,76 +596,6 @@ const setGuildTier = async (guild: TGuildDatabaseDocument, newTier: number): Pro
|
||||
await processGuildTechProjectContributionsUpdate(guild, project);
|
||||
}
|
||||
}
|
||||
if (guild.CeremonyContributors) {
|
||||
await checkClanAscensionHasRequiredContributors(guild);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkClanAscensionHasRequiredContributors = async (guild: TGuildDatabaseDocument): Promise<void> => {
|
||||
const requiredContributors = [1, 5, 15, 30, 50][guild.Tier - 1];
|
||||
// Once required contributor count is hit, the class is committed and there's 72 hours to claim endo.
|
||||
if (guild.CeremonyContributors!.length >= requiredContributors) {
|
||||
guild.Class = guild.CeremonyClass!;
|
||||
guild.CeremonyClass = undefined;
|
||||
guild.CeremonyResetDate = new Date(Date.now() + (config.fastClanAscension ? 5_000 : 72 * 3600_000));
|
||||
if (!config.fastClanAscension) {
|
||||
// Send message to all active guild members
|
||||
const members = await GuildMember.find({ guildId: guild._id, status: 0 }, "accountId");
|
||||
await parallelForeach(members, async member => {
|
||||
// somewhat unfaithful as on live the "msg" is not a loctag, but since we don't have the string, we'll let the client fill it in with "arg".
|
||||
await createMessage(member.accountId, [
|
||||
{
|
||||
sndr: guild.Name,
|
||||
msg: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgressDetails",
|
||||
arg: [
|
||||
{
|
||||
Key: "RESETDATE",
|
||||
Tag:
|
||||
guild.CeremonyResetDate!.getUTCMonth() +
|
||||
"/" +
|
||||
guild.CeremonyResetDate!.getUTCDate() +
|
||||
"/" +
|
||||
(guild.CeremonyResetDate!.getUTCFullYear() % 100) +
|
||||
" " +
|
||||
guild.CeremonyResetDate!.getUTCHours().toString().padStart(2, "0") +
|
||||
":" +
|
||||
guild.CeremonyResetDate!.getUTCMinutes().toString().padStart(2, "0")
|
||||
}
|
||||
],
|
||||
sub: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgress",
|
||||
icon: "/Lotus/Interface/Graphics/ClanTileImages/ClanEnterDojo.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const giveClanKey = (inventory: TInventoryDatabaseDocument, inventoryChanges?: IInventoryChanges): void => {
|
||||
if (config.skipClanKeyCrafting) {
|
||||
const levelKeyChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKey",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addLevelKeys(inventory, levelKeyChanges);
|
||||
if (inventoryChanges) {
|
||||
combineInventoryChanges(inventoryChanges, { LevelKeys: levelKeyChanges });
|
||||
}
|
||||
} else {
|
||||
const recipeChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addRecipes(inventory, recipeChanges);
|
||||
if (inventoryChanges) {
|
||||
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const removeDojoKeyItems = (inventory: TInventoryDatabaseDocument): IInventoryChanges => {
|
||||
|
@ -2,7 +2,6 @@ import { Types } from "mongoose";
|
||||
import {
|
||||
IEquipmentClient,
|
||||
IEquipmentDatabase,
|
||||
IItemConfig,
|
||||
IOperatorConfigClient,
|
||||
IOperatorConfigDatabase
|
||||
} from "../types/inventoryTypes/commonInventoryTypes";
|
||||
@ -38,7 +37,6 @@ import {
|
||||
} from "../types/inventoryTypes/inventoryTypes";
|
||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
||||
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
|
||||
import { slotNames } from "../types/purchaseTypes";
|
||||
|
||||
const convertDate = (value: IMongoDate): Date => {
|
||||
return new Date(parseInt(value.$date.$numberLong));
|
||||
@ -106,18 +104,18 @@ const replaceSlots = (db: ISlots, client: ISlots): void => {
|
||||
db.Slots = client.Slots;
|
||||
};
|
||||
|
||||
export const importCrewMemberId = (crewMemberId: ICrewShipMemberClient): ICrewShipMemberDatabase => {
|
||||
if (crewMemberId.ItemId) {
|
||||
return { ItemId: new Types.ObjectId(crewMemberId.ItemId.$oid) };
|
||||
}
|
||||
return { NemesisFingerprint: BigInt(crewMemberId.NemesisFingerprint ?? 0) };
|
||||
const convertCrewShipMember = (client: ICrewShipMemberClient): ICrewShipMemberDatabase => {
|
||||
return {
|
||||
...client,
|
||||
ItemId: client.ItemId ? new Types.ObjectId(client.ItemId.$oid) : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const convertCrewShipMembers = (client: ICrewShipMembersClient): ICrewShipMembersDatabase => {
|
||||
return {
|
||||
SLOT_A: client.SLOT_A ? importCrewMemberId(client.SLOT_A) : undefined,
|
||||
SLOT_B: client.SLOT_B ? importCrewMemberId(client.SLOT_B) : undefined,
|
||||
SLOT_C: client.SLOT_C ? importCrewMemberId(client.SLOT_C) : undefined
|
||||
SLOT_A: client.SLOT_A ? convertCrewShipMember(client.SLOT_A) : undefined,
|
||||
SLOT_B: client.SLOT_B ? convertCrewShipMember(client.SLOT_B) : undefined,
|
||||
SLOT_C: client.SLOT_C ? convertCrewShipMember(client.SLOT_C) : undefined
|
||||
};
|
||||
};
|
||||
|
||||
@ -170,25 +168,10 @@ const convertPendingRecipe = (client: IPendingRecipeClient): IPendingRecipeDatab
|
||||
const convertNemesis = (client: INemesisClient): INemesisDatabase => {
|
||||
return {
|
||||
...client,
|
||||
fp: BigInt(client.fp),
|
||||
d: convertDate(client.d)
|
||||
};
|
||||
};
|
||||
|
||||
// Empty objects from live may have been encoded as empty arrays because of PHP.
|
||||
const convertItemConfig = <T extends IItemConfig>(client: T): T => {
|
||||
return {
|
||||
...client,
|
||||
pricol: Array.isArray(client.pricol) ? {} : client.pricol,
|
||||
attcol: Array.isArray(client.attcol) ? {} : client.attcol,
|
||||
sigcol: Array.isArray(client.sigcol) ? {} : client.sigcol,
|
||||
eyecol: Array.isArray(client.eyecol) ? {} : client.eyecol,
|
||||
facial: Array.isArray(client.facial) ? {} : client.facial,
|
||||
cloth: Array.isArray(client.cloth) ? {} : client.cloth,
|
||||
syancol: Array.isArray(client.syancol) ? {} : client.syancol
|
||||
};
|
||||
};
|
||||
|
||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
|
||||
for (const key of equipmentKeys) {
|
||||
if (client[key] !== undefined) {
|
||||
@ -229,28 +212,35 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
||||
}
|
||||
}
|
||||
for (const key of slotNames) {
|
||||
for (const key of [
|
||||
"SuitBin",
|
||||
"WeaponBin",
|
||||
"SentinelBin",
|
||||
"SpaceSuitBin",
|
||||
"SpaceWeaponBin",
|
||||
"PvpBonusLoadoutBin",
|
||||
"PveBonusLoadoutBin",
|
||||
"RandomModBin",
|
||||
"MechBin",
|
||||
"CrewMemberBin",
|
||||
"OperatorAmpBin",
|
||||
"CrewShipSalvageBin"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
replaceSlots(db[key], client[key]);
|
||||
}
|
||||
}
|
||||
// boolean
|
||||
for (const key of [
|
||||
"UseAdultOperatorLoadout",
|
||||
"HasOwnedVoidProjectionsPreviously",
|
||||
"ReceivedStartingGear",
|
||||
"ArchwingEnabled",
|
||||
"PlayedParkourTutorial",
|
||||
"Staff",
|
||||
"Moderator",
|
||||
"Partner",
|
||||
"Counselor"
|
||||
"PlayedParkourTutorial"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
// number
|
||||
for (const key of [
|
||||
"PlayerLevel",
|
||||
"RegularCredits",
|
||||
@ -260,15 +250,12 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"PrimeTokens",
|
||||
"TradesRemaining",
|
||||
"GiftsRemaining",
|
||||
"ChallengesFixVersion",
|
||||
"Founder",
|
||||
"Guide"
|
||||
"ChallengesFixVersion"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
// string
|
||||
for (const key of [
|
||||
"ThemeStyle",
|
||||
"ThemeBackground",
|
||||
@ -283,7 +270,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
// string[]
|
||||
for (const key of [
|
||||
"EquippedGear",
|
||||
"EquippedEmotes",
|
||||
@ -367,7 +353,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
db.PlayerSkills = client.PlayerSkills;
|
||||
}
|
||||
if (client.LotusCustomization !== undefined) {
|
||||
db.LotusCustomization = convertItemConfig(client.LotusCustomization);
|
||||
db.LotusCustomization = client.LotusCustomization;
|
||||
}
|
||||
if (client.CollectibleSeries !== undefined) {
|
||||
db.CollectibleSeries = client.CollectibleSeries;
|
||||
@ -394,9 +380,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
});
|
||||
});
|
||||
}
|
||||
if (client.Accolades !== undefined) {
|
||||
db.Accolades = client.Accolades;
|
||||
}
|
||||
};
|
||||
|
||||
const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
||||
|
@ -18,17 +18,10 @@ import {
|
||||
IKubrowPetEggDatabase,
|
||||
IKubrowPetEggClient,
|
||||
ILibraryDailyTaskInfo,
|
||||
ICalendarProgress,
|
||||
IDroneClient,
|
||||
IUpgradeClient,
|
||||
TPartialStartingGear,
|
||||
ILoreFragmentScan,
|
||||
ICrewMemberClient,
|
||||
Status,
|
||||
IKubrowPetDetailsDatabase,
|
||||
ITraits,
|
||||
ICalendarProgress,
|
||||
INemesisWeaponTargetFingerprint,
|
||||
INemesisPetTargetFingerprint
|
||||
TPartialStartingGear
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||
@ -63,25 +56,15 @@ import {
|
||||
ExportWeapons,
|
||||
IDefaultUpgrade,
|
||||
IPowersuit,
|
||||
ISentinel,
|
||||
TStandingLimitBin
|
||||
} from "warframe-public-export-plus";
|
||||
import { createShip } from "./shipService";
|
||||
import {
|
||||
catbrowDetails,
|
||||
kubrowDetails,
|
||||
kubrowFurPatternsWeights,
|
||||
kubrowWeights,
|
||||
toOid
|
||||
} from "../helpers/inventoryHelpers";
|
||||
import { toOid } from "../helpers/inventoryHelpers";
|
||||
import { addQuestKey, completeQuest } from "@/src/services/questService";
|
||||
import { handleBundleAcqusition } from "./purchaseService";
|
||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
|
||||
import { getRandomElement, getRandomInt, SRng } from "./rngService";
|
||||
import { createMessage } from "./inboxService";
|
||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||
import { getWorldState } from "./worldStateService";
|
||||
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
|
||||
|
||||
export const createInventory = async (
|
||||
accountOwnerId: Types.ObjectId,
|
||||
@ -91,10 +74,13 @@ export const createInventory = async (
|
||||
const inventory = new Inventory({
|
||||
accountOwnerId: accountOwnerId,
|
||||
LoadOutPresets: defaultItemReferences.loadOutPresetId,
|
||||
Ships: [defaultItemReferences.ship]
|
||||
Ships: [defaultItemReferences.ship],
|
||||
PlayedParkourTutorial: config.skipTutorial,
|
||||
ReceivedStartingGear: config.skipTutorial
|
||||
});
|
||||
|
||||
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
|
||||
inventory.CalendarProgress = createCalendar();
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
inventory.DuviriInfo = {
|
||||
Seed: generateRewardSeed(),
|
||||
@ -103,7 +89,6 @@ export const createInventory = async (
|
||||
await addItem(inventory, "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords");
|
||||
|
||||
if (config.skipTutorial) {
|
||||
inventory.PlayedParkourTutorial = true;
|
||||
await addStartingGear(inventory);
|
||||
await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
|
||||
|
||||
@ -123,15 +108,10 @@ export const createInventory = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const generateRewardSeed = (): bigint => {
|
||||
const hiDword = getRandomInt(0, 0x7fffffff);
|
||||
const loDword = getRandomInt(0, 0xffffffff);
|
||||
let seed = (BigInt(hiDword) << 32n) | BigInt(loDword);
|
||||
if (Math.random() < 0.5) {
|
||||
seed *= -1n;
|
||||
seed -= 1n;
|
||||
}
|
||||
return seed;
|
||||
export const generateRewardSeed = (): number => {
|
||||
const min = -Number.MAX_SAFE_INTEGER;
|
||||
const max = Number.MAX_SAFE_INTEGER;
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
//TODO: RawUpgrades might need to return a LastAdded
|
||||
@ -146,7 +126,7 @@ const awakeningRewards = [
|
||||
|
||||
export const addStartingGear = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
startingGear?: TPartialStartingGear
|
||||
startingGear: TPartialStartingGear | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
|
||||
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
|
||||
@ -157,22 +137,23 @@ export const addStartingGear = async (
|
||||
|
||||
//TODO: properly merge weapon bin changes it is currently static here
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"DataKnives",
|
||||
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
|
||||
{ XP: 450_000, IsNew: false },
|
||||
inventoryChanges
|
||||
undefined,
|
||||
inventoryChanges,
|
||||
{ XP: 450_000 }
|
||||
);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"Scoops",
|
||||
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
|
||||
{ IsNew: false },
|
||||
undefined,
|
||||
inventoryChanges
|
||||
);
|
||||
|
||||
@ -196,9 +177,7 @@ export const addStartingGear = async (
|
||||
combineInventoryChanges(inventoryChanges, inventoryDelta);
|
||||
}
|
||||
|
||||
if (inventory.ReceivedStartingGear) {
|
||||
logger.warn(`account already had starting gear but asked for it again?!`);
|
||||
}
|
||||
inventory.PlayedParkourTutorial = true;
|
||||
inventory.ReceivedStartingGear = true;
|
||||
|
||||
return inventoryChanges;
|
||||
@ -215,15 +194,6 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
for (const key in delta) {
|
||||
if (!(key in InventoryChanges)) {
|
||||
InventoryChanges[key] = delta[key];
|
||||
} else if (key == "MiscItems") {
|
||||
for (const deltaItem of delta[key]!) {
|
||||
const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType);
|
||||
if (existing) {
|
||||
existing.ItemCount += deltaItem.ItemCount;
|
||||
} else {
|
||||
InventoryChanges[key]!.push(deltaItem);
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(delta[key])) {
|
||||
const left = InventoryChanges[key] as object[];
|
||||
const right: object[] = delta[key];
|
||||
@ -256,7 +226,7 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
|
||||
export const getInventory = async (
|
||||
accountOwnerId: string,
|
||||
projection?: string
|
||||
projection: string | undefined = undefined
|
||||
): Promise<TInventoryDatabaseDocument> => {
|
||||
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId }, projection);
|
||||
|
||||
@ -330,8 +300,7 @@ export const addItem = async (
|
||||
typeName: string,
|
||||
quantity: number = 1,
|
||||
premiumPurchase: boolean = false,
|
||||
seed?: bigint,
|
||||
targetFingerprint?: string
|
||||
seed?: bigint
|
||||
): Promise<IInventoryChanges> => {
|
||||
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
|
||||
if (typeName in ExportBundles) {
|
||||
@ -409,7 +378,7 @@ export const addItem = async (
|
||||
} else if (ExportResources[typeName].productCategory == "KubrowPetEggs") {
|
||||
const changes: IKubrowPetEggClient[] = [];
|
||||
if (quantity < 0 || quantity > 100) {
|
||||
throw new Error(`unexpected acquisition quantity of KubrowPetEggs: got ${quantity}, expected 0..100`);
|
||||
throw new Error(`unexpected acquisition quantity of KubrowPetEggs: ${quantity}`);
|
||||
}
|
||||
for (let i = 0; i != quantity; ++i) {
|
||||
const egg: IKubrowPetEggDatabase = {
|
||||
@ -435,32 +404,8 @@ export const addItem = async (
|
||||
const meta = ExportCustoms[typeName];
|
||||
let inventoryChanges: IInventoryChanges;
|
||||
if (meta.productCategory == "CrewShipWeaponSkins") {
|
||||
if (meta.subroutines || meta.randomisedUpgrades) {
|
||||
// House versions need to be identified to get stats so put them into raw salvage first.
|
||||
const rawSalvageChanges = [
|
||||
{
|
||||
ItemType: typeName,
|
||||
ItemCount: quantity
|
||||
}
|
||||
];
|
||||
addCrewShipRawSalvage(inventory, rawSalvageChanges);
|
||||
inventoryChanges = { CrewShipRawSalvage: rawSalvageChanges };
|
||||
inventoryChanges = addCrewShipWeaponSkin(inventory, typeName);
|
||||
} else {
|
||||
// Sigma versions can be added directly.
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of CrewShipWeaponSkin: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
inventoryChanges = {
|
||||
...addCrewShipWeaponSkin(inventory, typeName, undefined),
|
||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, premiumPurchase)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of WeaponSkins: got ${quantity}, expected 1`);
|
||||
}
|
||||
inventoryChanges = addSkin(inventory, typeName);
|
||||
}
|
||||
if (meta.additionalItems) {
|
||||
@ -534,13 +479,14 @@ export const addItem = async (
|
||||
]
|
||||
});
|
||||
}
|
||||
if (targetFingerprint) {
|
||||
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisWeaponTargetFingerprint;
|
||||
defaultOverwrites.UpgradeType = targetFingerprintObj.ItemType;
|
||||
defaultOverwrites.UpgradeFingerprint = JSON.stringify(targetFingerprintObj.UpgradeFingerprint);
|
||||
defaultOverwrites.ItemName = targetFingerprintObj.Name;
|
||||
}
|
||||
const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites);
|
||||
const inventoryChanges = addEquipment(
|
||||
inventory,
|
||||
weapon.productCategory,
|
||||
typeName,
|
||||
[],
|
||||
{},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (weapon.additionalItems) {
|
||||
for (const item of weapon.additionalItems) {
|
||||
combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1));
|
||||
@ -548,32 +494,7 @@ export const addItem = async (
|
||||
}
|
||||
return {
|
||||
...inventoryChanges,
|
||||
...occupySlot(
|
||||
inventory,
|
||||
productCategoryToInventoryBin(weapon.productCategory) ?? InventorySlot.WEAPONS,
|
||||
premiumPurchase
|
||||
)
|
||||
};
|
||||
} else if (targetFingerprint) {
|
||||
// Sister's Hound
|
||||
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisPetTargetFingerprint;
|
||||
const head = targetFingerprintObj.Parts[0];
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
ModularParts: targetFingerprintObj.Parts,
|
||||
ItemName: targetFingerprintObj.Name,
|
||||
Configs: applyDefaultUpgrades(inventory, ExportWeapons[head].defaultUpgrades)
|
||||
};
|
||||
const itemType = {
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
||||
}[head] as string;
|
||||
return {
|
||||
...addEquipment(inventory, "MoaPets", itemType, defaultOverwrites),
|
||||
...occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase)
|
||||
...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase)
|
||||
};
|
||||
} else {
|
||||
// Modular weapon parts
|
||||
@ -590,28 +511,11 @@ export const addItem = async (
|
||||
}
|
||||
}
|
||||
if (typeName in ExportRailjackWeapons) {
|
||||
const meta = ExportRailjackWeapons[typeName];
|
||||
if (meta.defaultUpgrades?.length) {
|
||||
// House versions need to be identified to get stats so put them into raw salvage first.
|
||||
const rawSalvageChanges = [
|
||||
{
|
||||
ItemType: typeName,
|
||||
ItemCount: quantity
|
||||
}
|
||||
];
|
||||
addCrewShipRawSalvage(inventory, rawSalvageChanges);
|
||||
return { CrewShipRawSalvage: rawSalvageChanges };
|
||||
} else {
|
||||
// Sigma versions can be added directly.
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of CrewShipWeapon: got ${quantity}, expected 1`);
|
||||
}
|
||||
return {
|
||||
...addEquipment(inventory, meta.productCategory, typeName),
|
||||
...addEquipment(inventory, ExportRailjackWeapons[typeName].productCategory, typeName),
|
||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, premiumPurchase)
|
||||
};
|
||||
}
|
||||
}
|
||||
if (typeName in ExportMisc.creditBundles) {
|
||||
const creditsTotal = ExportMisc.creditBundles[typeName] * quantity;
|
||||
inventory.RegularCredits += creditsTotal;
|
||||
@ -621,7 +525,7 @@ export const addItem = async (
|
||||
}
|
||||
if (typeName in ExportFusionBundles) {
|
||||
const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity;
|
||||
addFusionPoints(inventory, fusionPointsTotal);
|
||||
inventory.FusionPoints += fusionPointsTotal;
|
||||
return {
|
||||
FusionPoints: fusionPointsTotal
|
||||
};
|
||||
@ -641,18 +545,9 @@ export const addItem = async (
|
||||
}
|
||||
}
|
||||
if (typeName in ExportDrones) {
|
||||
// Can only get 1 at a time from crafting, but for convenience's sake, allow up 100 to via the WebUI.
|
||||
if (quantity < 0 || quantity > 100) {
|
||||
throw new Error(`unexpected acquisition quantity of Drones: got ${quantity}, expected 0..100`);
|
||||
}
|
||||
for (let i = 0; i != quantity; ++i) {
|
||||
return addDrone(inventory, typeName);
|
||||
}
|
||||
}
|
||||
if (typeName in ExportEmailItems) {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of EmailItems: got ${quantity}, expected 1`);
|
||||
}
|
||||
return await addEmailItem(inventory, typeName);
|
||||
}
|
||||
|
||||
@ -662,9 +557,12 @@ export const addItem = async (
|
||||
switch (typeName.substr(1).split("/")[2]) {
|
||||
default: {
|
||||
return {
|
||||
...(await addPowerSuit(inventory, typeName, {
|
||||
Features: premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
})),
|
||||
...(await addPowerSuit(
|
||||
inventory,
|
||||
typeName,
|
||||
{},
|
||||
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
)),
|
||||
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase)
|
||||
};
|
||||
}
|
||||
@ -761,20 +659,6 @@ export const addItem = async (
|
||||
return {
|
||||
MiscItems: miscItemChanges
|
||||
};
|
||||
} else if (
|
||||
typeName.substr(1).split("/")[3] == "CatbrowPet" ||
|
||||
typeName.substr(1).split("/")[3] == "KubrowPet"
|
||||
) {
|
||||
return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
|
||||
} else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
|
||||
if (!seed) {
|
||||
throw new Error(`Expected crew member to have a seed`);
|
||||
}
|
||||
seed |= 0x33b81en << 32n;
|
||||
return {
|
||||
...addCrewMember(inventory, typeName, seed),
|
||||
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
||||
};
|
||||
} else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
|
||||
return addCrewShipHarness(inventory, typeName);
|
||||
}
|
||||
@ -859,8 +743,7 @@ const addSentinel = (
|
||||
|
||||
const features = premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined;
|
||||
const sentinelIndex =
|
||||
inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features, IsNew: true }) -
|
||||
1;
|
||||
inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features }) - 1;
|
||||
inventoryChanges.Sentinels ??= [];
|
||||
inventoryChanges.Sentinels.push(inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>());
|
||||
|
||||
@ -884,8 +767,8 @@ const addSentinelWeapon = (
|
||||
export const addPowerSuit = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
powersuitName: string,
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features: number | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
const powersuit = ExportWarframes[powersuitName] as IPowersuit | undefined;
|
||||
const exalted = powersuit?.exalted ?? [];
|
||||
@ -899,20 +782,15 @@ export const addPowerSuit = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
const suit: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
{
|
||||
const suitIndex =
|
||||
inventory.Suits.push({
|
||||
ItemType: powersuitName,
|
||||
Configs: [],
|
||||
UpgradeVer: 101,
|
||||
XP: 0,
|
||||
Features: features,
|
||||
IsNew: true
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!suit.IsNew) {
|
||||
suit.IsNew = undefined;
|
||||
}
|
||||
const suitIndex = inventory.Suits.push(suit) - 1;
|
||||
}) - 1;
|
||||
inventoryChanges.Suits ??= [];
|
||||
inventoryChanges.Suits.push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
|
||||
return inventoryChanges;
|
||||
@ -922,7 +800,7 @@ export const addMechSuit = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
mechsuitName: string,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features?: number
|
||||
features: number | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
const powersuit = ExportWarframes[mechsuitName] as IPowersuit | undefined;
|
||||
const exalted = powersuit?.exalted ?? [];
|
||||
@ -974,7 +852,7 @@ export const addSpaceSuit = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
spacesuitName: string,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features?: number
|
||||
features: number | undefined = undefined
|
||||
): IInventoryChanges => {
|
||||
const suitIndex =
|
||||
inventory.SpaceSuits.push({
|
||||
@ -990,89 +868,6 @@ export const addSpaceSuit = (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const addKubrowPet = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
kubrowPetName: string,
|
||||
details: IKubrowPetDetailsDatabase | undefined,
|
||||
premiumPurchase: boolean,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
combineInventoryChanges(inventoryChanges, occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase));
|
||||
|
||||
const kubrowPet = ExportSentinels[kubrowPetName] as ISentinel | undefined;
|
||||
const exalted = kubrowPet?.exalted ?? [];
|
||||
for (const specialItem of exalted) {
|
||||
addSpecialItem(inventory, specialItem, inventoryChanges);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
|
||||
|
||||
if (!details) {
|
||||
let traits: ITraits;
|
||||
|
||||
if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
|
||||
traits = {
|
||||
BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
|
||||
SecondaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryVampire",
|
||||
TertiaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryVampire",
|
||||
AccentColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsVampire",
|
||||
EyeColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA",
|
||||
FurPattern: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternVampire",
|
||||
Personality: kubrowPetName,
|
||||
BodyType: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetVampireBodyType",
|
||||
Head: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadVampire",
|
||||
Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
|
||||
};
|
||||
} else {
|
||||
const isCatbrow = [
|
||||
"/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit"
|
||||
].includes(kubrowPetName);
|
||||
const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
|
||||
|
||||
traits = {
|
||||
BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
TertiaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
AccentColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
EyeColor: getRandomWeightedReward(traitsPool.EyeColors, kubrowWeights)!.type,
|
||||
FurPattern: getRandomWeightedReward(traitsPool.FurPatterns, kubrowFurPatternsWeights)!.type,
|
||||
Personality: kubrowPetName,
|
||||
BodyType: getRandomWeightedReward(traitsPool.BodyTypes, kubrowWeights)!.type,
|
||||
Head: isCatbrow ? getRandomWeightedReward(traitsPool.Heads, kubrowWeights)!.type : undefined,
|
||||
Tail: isCatbrow ? getRandomWeightedReward(traitsPool.Tails, kubrowWeights)!.type : undefined
|
||||
};
|
||||
}
|
||||
|
||||
details = {
|
||||
Name: "",
|
||||
IsPuppy: false,
|
||||
HasCollar: true,
|
||||
PrintsRemaining: 2,
|
||||
Status: Status.StatusStasis,
|
||||
HatchDate: new Date(Math.trunc(Date.now() / 86400000) * 86400000),
|
||||
IsMale: !!getRandomInt(0, 1),
|
||||
Size: getRandomInt(70, 100) / 100,
|
||||
DominantTraits: traits,
|
||||
RecessiveTraits: traits
|
||||
};
|
||||
}
|
||||
|
||||
const kubrowPetIndex =
|
||||
inventory.KubrowPets.push({
|
||||
ItemType: kubrowPetName,
|
||||
Configs: configs,
|
||||
XP: 0,
|
||||
Details: details,
|
||||
IsNew: true
|
||||
}) - 1;
|
||||
inventoryChanges.KubrowPets ??= [];
|
||||
inventoryChanges.KubrowPets.push(inventory.KubrowPets[kubrowPetIndex].toJSON<IEquipmentClient>());
|
||||
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const updateSlots = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
slotName: SlotNames,
|
||||
@ -1113,15 +908,6 @@ export const updateCurrency = (
|
||||
return currencyChanges;
|
||||
};
|
||||
|
||||
export const addFusionPoints = (inventory: TInventoryDatabaseDocument, add: number): number => {
|
||||
if (inventory.FusionPoints + add > 2147483647) {
|
||||
logger.warn(`capping FusionPoints balance at 2147483647`);
|
||||
add = 2147483647 - inventory.FusionPoints;
|
||||
}
|
||||
inventory.FusionPoints += add;
|
||||
return add;
|
||||
};
|
||||
|
||||
const standingLimitBinToInventoryKey: Record<
|
||||
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
|
||||
keyof IDailyAffiliations
|
||||
@ -1144,50 +930,23 @@ const standingLimitBinToInventoryKey: Record<
|
||||
|
||||
export const allDailyAffiliationKeys: (keyof IDailyAffiliations)[] = Object.values(standingLimitBinToInventoryKey);
|
||||
|
||||
const getStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin): number => {
|
||||
export const getStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin): number => {
|
||||
if (bin == "STANDING_LIMIT_BIN_NONE" || config.noDailyStandingLimits) {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
return inventory[standingLimitBinToInventoryKey[bin]];
|
||||
};
|
||||
|
||||
const updateStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin, subtrahend: number): void => {
|
||||
export const updateStandingLimit = (
|
||||
inventory: IDailyAffiliations,
|
||||
bin: TStandingLimitBin,
|
||||
subtrahend: number
|
||||
): void => {
|
||||
if (bin != "STANDING_LIMIT_BIN_NONE" && !config.noDailyStandingLimits) {
|
||||
inventory[standingLimitBinToInventoryKey[bin]] -= subtrahend;
|
||||
}
|
||||
};
|
||||
|
||||
export const addStanding = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
syndicateTag: string,
|
||||
gainedStanding: number,
|
||||
isMedallion: boolean = false
|
||||
): IAffiliationMods => {
|
||||
let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag);
|
||||
const syndicateMeta = ExportSyndicates[syndicateTag];
|
||||
|
||||
if (!syndicate) {
|
||||
syndicate =
|
||||
inventory.Affiliations[inventory.Affiliations.push({ Tag: syndicateTag, Standing: 0, Title: 0 }) - 1];
|
||||
}
|
||||
|
||||
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
|
||||
if (syndicate.Standing + gainedStanding > max) gainedStanding = max - syndicate.Standing;
|
||||
|
||||
if (!isMedallion || syndicateMeta.medallionsCappedByDailyLimit) {
|
||||
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
|
||||
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
|
||||
}
|
||||
updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
|
||||
}
|
||||
|
||||
syndicate.Standing += gainedStanding;
|
||||
return {
|
||||
Tag: syndicateTag,
|
||||
Standing: gainedStanding
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: AffiliationMods support (Nightwave).
|
||||
export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise<IUpdateNodeIntrosResponse> => {
|
||||
const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems");
|
||||
@ -1207,10 +966,6 @@ export const updateGeneric = async (data: IGenericUpdate, accountId: string): Pr
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
} else if (node == "BeatCaliberChicks") {
|
||||
await addEmailItem(inventory, "/Lotus/Types/Items/EmailItems/BeatCaliberChicksEmailItem", inventoryChanges);
|
||||
} else if (node == "ClearedFiveLoops") {
|
||||
await addEmailItem(inventory, "/Lotus/Types/Items/EmailItems/ClearedFiveLoopsEmailItem", inventoryChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1233,21 +988,20 @@ export const addEquipment = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
category: TEquipmentKey,
|
||||
type: string,
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
modularParts: string[] | undefined = undefined,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
defaultOverwrites: Partial<IEquipmentDatabase> | undefined = undefined
|
||||
): IInventoryChanges => {
|
||||
const equipment: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
const equipment = Object.assign(
|
||||
{
|
||||
ItemType: type,
|
||||
Configs: [],
|
||||
XP: 0,
|
||||
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons"
|
||||
ModularParts: modularParts,
|
||||
IsNew: true
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!equipment.IsNew) {
|
||||
equipment.IsNew = undefined;
|
||||
}
|
||||
const index = inventory[category].push(equipment) - 1;
|
||||
|
||||
inventoryChanges[category] ??= [];
|
||||
@ -1276,27 +1030,21 @@ export const addSkin = (
|
||||
typeName: string,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) {
|
||||
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
|
||||
} else {
|
||||
const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
inventoryChanges.WeaponSkins ??= [];
|
||||
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
|
||||
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
|
||||
);
|
||||
}
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const addCrewShipWeaponSkin = (
|
||||
const addCrewShipWeaponSkin = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
typeName: string,
|
||||
upgradeFingerprint: string | undefined,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
const index =
|
||||
inventory.CrewShipWeaponSkins.push({ ItemType: typeName, UpgradeFingerprint: upgradeFingerprint }) - 1;
|
||||
const index = inventory.CrewShipWeaponSkins.push({ ItemType: typeName }) - 1;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
inventoryChanges.CrewShipWeaponSkins ??= [];
|
||||
(inventoryChanges.CrewShipWeaponSkins as IUpgradeClient[]).push(
|
||||
@ -1305,22 +1053,6 @@ export const addCrewShipWeaponSkin = (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const addCrewShipSalvagedWeaponSkin = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
typeName: string,
|
||||
upgradeFingerprint: string | undefined,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
const index =
|
||||
inventory.CrewShipSalvagedWeaponSkins.push({ ItemType: typeName, UpgradeFingerprint: upgradeFingerprint }) - 1;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
inventoryChanges.CrewShipSalvagedWeaponSkins ??= [];
|
||||
(inventoryChanges.CrewShipSalvagedWeaponSkins as IUpgradeClient[]).push(
|
||||
inventory.CrewShipSalvagedWeaponSkins[index].toJSON<IUpgradeClient>()
|
||||
);
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
const addCrewShip = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
typeName: string,
|
||||
@ -1377,78 +1109,6 @@ const addDrone = (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
/*const getCrewMemberSkills = (seed: bigint, skillPointsToAssign: number): Record<string, number> => {
|
||||
const rng = new SRng(seed);
|
||||
|
||||
const skills = ["PILOTING", "GUNNERY", "ENGINEERING", "COMBAT", "SURVIVABILITY"];
|
||||
for (let i = 1; i != 5; ++i) {
|
||||
const swapIndex = rng.randomInt(0, i);
|
||||
if (swapIndex != i) {
|
||||
const tmp = skills[i];
|
||||
skills[i] = skills[swapIndex];
|
||||
skills[swapIndex] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
rng.randomFloat(); // unused afaict
|
||||
|
||||
const skillAssignments = [0, 0, 0, 0, 0];
|
||||
for (let skill = 0; skillPointsToAssign; skill = (skill + 1) % 5) {
|
||||
const maxIncrease = Math.min(5 - skillAssignments[skill], skillPointsToAssign);
|
||||
const increase = rng.randomInt(0, maxIncrease);
|
||||
skillAssignments[skill] += increase;
|
||||
skillPointsToAssign -= increase;
|
||||
}
|
||||
|
||||
skillAssignments.sort((a, b) => b - a);
|
||||
|
||||
const combined: Record<string, number> = {};
|
||||
for (let i = 0; i != 5; ++i) {
|
||||
combined[skills[i]] = skillAssignments[i];
|
||||
}
|
||||
return combined;
|
||||
};*/
|
||||
|
||||
const addCrewMember = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
itemType: string,
|
||||
seed: bigint,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
// SkillEfficiency is additional to the base stats, so we don't need to compute this
|
||||
//const skillPointsToAssign = itemType.endsWith("Strong") ? 12 : itemType.indexOf("Medium") != -1 ? 10 : 8;
|
||||
//const skills = getCrewMemberSkills(seed, skillPointsToAssign);
|
||||
|
||||
// Arbiters = male
|
||||
// CephalonSuda = female
|
||||
// NewLoka = female
|
||||
// Perrin = male
|
||||
// RedVeil = male
|
||||
// SteelMeridian = female
|
||||
const powersuitType =
|
||||
itemType.indexOf("Arbiters") != -1 || itemType.indexOf("Perrin") != -1 || itemType.indexOf("RedVeil") != -1
|
||||
? "/Lotus/Powersuits/NpcPowersuits/CrewMemberMaleSuit"
|
||||
: "/Lotus/Powersuits/NpcPowersuits/CrewMemberFemaleSuit";
|
||||
|
||||
const index =
|
||||
inventory.CrewMembers.push({
|
||||
ItemType: itemType,
|
||||
NemesisFingerprint: 0n,
|
||||
Seed: seed,
|
||||
SkillEfficiency: {
|
||||
PILOTING: { Assigned: 0 },
|
||||
GUNNERY: { Assigned: 0 },
|
||||
ENGINEERING: { Assigned: 0 },
|
||||
COMBAT: { Assigned: 0 },
|
||||
SURVIVABILITY: { Assigned: 0 }
|
||||
},
|
||||
PowersuitType: powersuitType
|
||||
}) - 1;
|
||||
inventoryChanges.CrewMembers ??= [];
|
||||
inventoryChanges.CrewMembers.push(inventory.CrewMembers[index].toJSON<ICrewMemberClient>());
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const addEmailItem = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
typeName: string,
|
||||
@ -1503,22 +1163,6 @@ export const addGearExpByCategory = (
|
||||
});
|
||||
};
|
||||
|
||||
export const addMiscItem = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
type: string,
|
||||
count: number,
|
||||
inventoryChanges: IInventoryChanges
|
||||
): void => {
|
||||
const miscItemChanges: IMiscItem[] = [
|
||||
{
|
||||
ItemType: type,
|
||||
ItemCount: count
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||
};
|
||||
|
||||
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => {
|
||||
const { MiscItems } = inventory;
|
||||
|
||||
@ -1660,20 +1304,7 @@ export const addFocusXpIncreases = (inventory: TInventoryDatabaseDocument, focus
|
||||
inventory.FocusXP.AP_POWER += focusXpPlus[FocusType.AP_POWER];
|
||||
inventory.FocusXP.AP_WARD += focusXpPlus[FocusType.AP_WARD];
|
||||
|
||||
if (!config.noDailyFocusLimit) {
|
||||
inventory.DailyFocus -= focusXpPlus.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
};
|
||||
|
||||
export const addLoreFragmentScans = (inventory: TInventoryDatabaseDocument, arr: ILoreFragmentScan[]): void => {
|
||||
arr.forEach(clientFragment => {
|
||||
const fragment = inventory.LoreFragmentScans.find(x => x.ItemType == clientFragment.ItemType);
|
||||
if (fragment) {
|
||||
fragment.Progress += clientFragment.Progress;
|
||||
} else {
|
||||
inventory.LoreFragmentScans.push(clientFragment);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addChallenges = (
|
||||
@ -1798,7 +1429,7 @@ export const addKeyChainItems = async (
|
||||
};
|
||||
|
||||
export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
|
||||
const enemyTypes = getRandomElement(libraryDailyTasks)!;
|
||||
const enemyTypes = getRandomElement(libraryDailyTasks);
|
||||
const enemyAvatar = ExportEnemies.avatars[enemyTypes[0]];
|
||||
const scansRequired = getRandomInt(2, 4);
|
||||
return {
|
||||
@ -1812,6 +1443,20 @@ export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
|
||||
};
|
||||
};
|
||||
|
||||
const createCalendar = (): ICalendarProgress => {
|
||||
return {
|
||||
Version: 19,
|
||||
Iteration: 2,
|
||||
YearProgress: { Upgrades: [] },
|
||||
SeasonProgress: {
|
||||
SeasonType: "CST_SPRING",
|
||||
LastCompletedDayIdx: -1,
|
||||
LastCompletedChallengeDayIdx: -1,
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => {
|
||||
inventory.Affiliations.push({
|
||||
Title: 1,
|
||||
@ -1828,151 +1473,3 @@ export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void
|
||||
Tag: "KahlSyndicate"
|
||||
});
|
||||
};
|
||||
|
||||
export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void => {
|
||||
let index = inventory.MiscItems.findIndex(x => x.ItemType == "");
|
||||
if (index != -1) {
|
||||
inventory.MiscItems.splice(index, 1);
|
||||
}
|
||||
|
||||
index = inventory.Affiliations.findIndex(x => x.Tag == "KahlSyndicate");
|
||||
if (index != -1 && !inventory.Affiliations[index].WeeklyMissions) {
|
||||
logger.debug(`KahlSyndicate seems broken, removing it and setting up again`);
|
||||
inventory.Affiliations.splice(index, 1);
|
||||
setupKahlSyndicate(inventory);
|
||||
}
|
||||
|
||||
const LibrarySyndicate = inventory.Affiliations.find(x => x.Tag == "LibrarySyndicate");
|
||||
if (LibrarySyndicate && LibrarySyndicate.FreeFavorsEarned) {
|
||||
logger.debug(`removing FreeFavorsEarned from LibrarySyndicate`);
|
||||
LibrarySyndicate.FreeFavorsEarned = undefined;
|
||||
}
|
||||
|
||||
if (inventory.LotusCustomization) {
|
||||
if (
|
||||
Array.isArray(inventory.LotusCustomization.attcol) ||
|
||||
Array.isArray(inventory.LotusCustomization.sigcol) ||
|
||||
Array.isArray(inventory.LotusCustomization.eyecol) ||
|
||||
Array.isArray(inventory.LotusCustomization.facial) ||
|
||||
Array.isArray(inventory.LotusCustomization.cloth) ||
|
||||
Array.isArray(inventory.LotusCustomization.syancol)
|
||||
) {
|
||||
logger.debug(`fixing empty objects represented as empty arrays in LotusCustomization`);
|
||||
inventory.LotusCustomization.attcol = {};
|
||||
inventory.LotusCustomization.sigcol = {};
|
||||
inventory.LotusCustomization.eyecol = {};
|
||||
inventory.LotusCustomization.facial = {};
|
||||
inventory.LotusCustomization.cloth = {};
|
||||
inventory.LotusCustomization.syancol = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
||||
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||
|
||||
if (!inventory.CalendarProgress) {
|
||||
inventory.CalendarProgress = {
|
||||
Version: 19,
|
||||
Iteration: currentSeason.YearIteration,
|
||||
YearProgress: {
|
||||
Upgrades: []
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: currentSeason.Season,
|
||||
LastCompletedDayIdx: 0,
|
||||
LastCompletedChallengeDayIdx: 0,
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const yearRolledOver = inventory.CalendarProgress.Iteration != currentSeason.YearIteration;
|
||||
if (yearRolledOver) {
|
||||
inventory.CalendarProgress.Iteration = currentSeason.YearIteration;
|
||||
inventory.CalendarProgress.YearProgress.Upgrades = [];
|
||||
}
|
||||
if (yearRolledOver || inventory.CalendarProgress.SeasonProgress.SeasonType != currentSeason.Season) {
|
||||
inventory.CalendarProgress.SeasonProgress.SeasonType = currentSeason.Season;
|
||||
inventory.CalendarProgress.SeasonProgress.LastCompletedDayIdx = -1;
|
||||
inventory.CalendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = -1;
|
||||
inventory.CalendarProgress.SeasonProgress.ActivatedChallenges = [];
|
||||
}
|
||||
|
||||
return inventory.CalendarProgress;
|
||||
};
|
||||
|
||||
export const giveNemesisWeaponRecipe = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
weaponType: string,
|
||||
nemesisName: string = "AGOR ROK",
|
||||
weaponLoc?: string,
|
||||
KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
|
||||
fp: bigint = generateRewardSeed()
|
||||
): void => {
|
||||
if (!weaponLoc) {
|
||||
weaponLoc = ExportWeapons[weaponType].name;
|
||||
}
|
||||
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == weaponType)![0];
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: recipeType,
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
inventory.PendingRecipes.push({
|
||||
CompletionDate: new Date(),
|
||||
ItemType: recipeType,
|
||||
TargetFingerprint: JSON.stringify({
|
||||
ItemType: "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod",
|
||||
UpgradeFingerprint: {
|
||||
compat: weaponType,
|
||||
buffs: [
|
||||
{
|
||||
Tag: getInnateDamageTag(KillingSuit),
|
||||
Value: getInnateDamageValue(fp)
|
||||
}
|
||||
]
|
||||
},
|
||||
Name: weaponLoc + "|" + nemesisName
|
||||
} satisfies INemesisWeaponTargetFingerprint)
|
||||
});
|
||||
};
|
||||
|
||||
export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
|
||||
const head = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
|
||||
])!;
|
||||
const body = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
|
||||
"/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];
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: recipeType,
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
inventory.PendingRecipes.push({
|
||||
CompletionDate: new Date(),
|
||||
ItemType: recipeType,
|
||||
TargetFingerprint: JSON.stringify({
|
||||
Parts: [head, body, legs, tail],
|
||||
Name: "/Lotus/Language/Pets/ZanukaPetName|" + nemesisName
|
||||
} satisfies INemesisPetTargetFingerprint)
|
||||
});
|
||||
};
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
dict_uk,
|
||||
dict_zh,
|
||||
ExportArcanes,
|
||||
ExportBoosters,
|
||||
ExportCustoms,
|
||||
ExportDrones,
|
||||
ExportGear,
|
||||
@ -186,15 +185,14 @@ export const getKeyChainMessage = ({ KeyChain, ChainStage }: IKeyChainRequest):
|
||||
throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
|
||||
}
|
||||
|
||||
let i = ChainStage;
|
||||
let chainStageMessage = chainStages[i].messageToSendWhenTriggered;
|
||||
while (!chainStageMessage) {
|
||||
if (++i >= chainStages.length) {
|
||||
break;
|
||||
}
|
||||
chainStageMessage = chainStages[i].messageToSendWhenTriggered;
|
||||
const keyChainStage = chainStages[ChainStage];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!keyChainStage) {
|
||||
throw new Error(`KeyChainStage ${ChainStage} not found`);
|
||||
}
|
||||
|
||||
const chainStageMessage = keyChainStage.messageToSendWhenTriggered;
|
||||
|
||||
if (!chainStageMessage) {
|
||||
throw new Error(
|
||||
`client requested key chain message in keychain ${KeyChain} at stage ${ChainStage} but they did not exist`
|
||||
@ -218,30 +216,15 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
|
||||
};
|
||||
|
||||
export const isStoreItem = (type: string): boolean => {
|
||||
return type.startsWith("/Lotus/StoreItems/") || type in ExportBoosters;
|
||||
return type.startsWith("/Lotus/StoreItems/");
|
||||
};
|
||||
|
||||
export const toStoreItem = (type: string): string => {
|
||||
if (type.startsWith("/Lotus/Types/StoreItems/Boosters/")) {
|
||||
const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == type);
|
||||
if (boosterEntry) {
|
||||
return boosterEntry[0];
|
||||
}
|
||||
throw new Error(`could not convert ${type} to a store item`);
|
||||
}
|
||||
return "/Lotus/StoreItems/" + type.substring("/Lotus/".length);
|
||||
};
|
||||
|
||||
export const fromStoreItem = (type: string): string => {
|
||||
if (type.startsWith("/Lotus/StoreItems/")) {
|
||||
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
|
||||
}
|
||||
|
||||
if (type in ExportBoosters) {
|
||||
return ExportBoosters[type].typeName;
|
||||
}
|
||||
|
||||
throw new Error(`${type} is not a store item`);
|
||||
};
|
||||
|
||||
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {
|
||||
|
@ -8,7 +8,7 @@ export const submitLeaderboardScore = async (
|
||||
ownerId: string,
|
||||
displayName: string,
|
||||
score: number,
|
||||
guildId: string | undefined
|
||||
guildId?: string
|
||||
): Promise<void> => {
|
||||
let expiry: Date;
|
||||
if (schedule == "daily") {
|
||||
@ -39,9 +39,9 @@ export const getLeaderboard = async (
|
||||
leaderboard: string,
|
||||
before: number,
|
||||
after: number,
|
||||
pivotId: string | undefined,
|
||||
guildId: string | undefined,
|
||||
guildTier: number | undefined
|
||||
pivotId?: string,
|
||||
guildId?: string,
|
||||
guildTier?: number
|
||||
): Promise<ILeaderboardEntryClient[]> => {
|
||||
const filter: { leaderboard: string; guildId?: string; guildTier?: number } = { leaderboard };
|
||||
if (guildId) {
|
||||
|
@ -77,6 +77,7 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
||||
const reward = rng.randomReward(randomRewards)!;
|
||||
//const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!;
|
||||
if (reward.RewardType == "RT_RANDOM_RECIPE") {
|
||||
// Not very faithful implementation but roughly the same idea
|
||||
const masteredItems = new Set();
|
||||
for (const entry of inventory.XPInfo) {
|
||||
masteredItems.add(entry.ItemType);
|
||||
@ -94,15 +95,15 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
||||
}
|
||||
const eligibleRecipes: string[] = [];
|
||||
for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) {
|
||||
if (!recipe.excludeFromMarket && unmasteredItems.has(recipe.resultType)) {
|
||||
if (unmasteredItems.has(recipe.resultType)) {
|
||||
eligibleRecipes.push(uniqueName);
|
||||
}
|
||||
}
|
||||
if (eligibleRecipes.length == 0) {
|
||||
// This account has all applicable warframes and weapons already mastered (filthy cheater), need a different reward.
|
||||
// This account has all warframes and weapons already mastered (filthy cheater), need a different reward.
|
||||
return getRandomLoginReward(rng, day, inventory);
|
||||
}
|
||||
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes)!);
|
||||
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes));
|
||||
}
|
||||
return {
|
||||
//_id: toOid(new Types.ObjectId()),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,9 @@
|
||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||
import { addItem, getInventory } from "@/src/services/inventoryService";
|
||||
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
|
||||
import { IGardeningDatabase } from "../types/shipTypes";
|
||||
import { getRandomElement } from "./rngService";
|
||||
|
||||
export const getPersonalRooms = async (
|
||||
accountId: string,
|
||||
projection?: string
|
||||
): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
|
||||
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
|
||||
|
||||
if (!personalRooms) {
|
||||
throw new Error(`personal rooms not found for account ${accountId}`);
|
||||
@ -30,64 +25,3 @@ export const updateShipFeature = async (accountId: string, shipFeature: string):
|
||||
await addItem(inventory, shipFeature, -1);
|
||||
await inventory.save();
|
||||
};
|
||||
|
||||
export const createGarden = (): IGardeningDatabase => {
|
||||
const plantTypes = [
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantA",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantB",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantC",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantD",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantE",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantF"
|
||||
];
|
||||
const endTime = new Date((Math.trunc(Date.now() / 1000) + 79200) * 1000); // Plants will take 22 hours to grow
|
||||
return {
|
||||
Planters: [
|
||||
{
|
||||
Name: "Garden0",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
Name: "Garden1",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
Name: "Garden2",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes)!,
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
@ -66,18 +66,6 @@ export const handlePurchase = async (
|
||||
if (!offer) {
|
||||
throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`);
|
||||
}
|
||||
if (offer.RegularPrice) {
|
||||
combineInventoryChanges(
|
||||
prePurchaseInventoryChanges,
|
||||
updateCurrency(inventory, offer.RegularPrice[0], false)
|
||||
);
|
||||
}
|
||||
if (offer.PremiumPrice) {
|
||||
combineInventoryChanges(
|
||||
prePurchaseInventoryChanges,
|
||||
updateCurrency(inventory, offer.PremiumPrice[0], true)
|
||||
);
|
||||
}
|
||||
if (offer.ItemPrices) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -153,8 +141,7 @@ export const handlePurchase = async (
|
||||
inventory,
|
||||
purchaseRequest.PurchaseParams.Quantity,
|
||||
undefined,
|
||||
false,
|
||||
purchaseRequest.PurchaseParams.UsePremium,
|
||||
undefined,
|
||||
seed
|
||||
);
|
||||
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
|
||||
@ -182,9 +169,6 @@ export const handlePurchase = async (
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.RegularPrice, false)
|
||||
);
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
|
||||
const invItem: IMiscItem = {
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
|
||||
@ -238,18 +222,12 @@ export const handlePurchase = async (
|
||||
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
|
||||
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
|
||||
if (offer) {
|
||||
if (typeof offer.credits == "number") {
|
||||
if (offer.credits) {
|
||||
combineInventoryChanges(
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.credits, false)
|
||||
);
|
||||
}
|
||||
if (typeof offer.platinum == "number") {
|
||||
combineInventoryChanges(
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.platinum, true)
|
||||
);
|
||||
}
|
||||
if (offer.itemPrices) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -260,9 +238,6 @@ export const handlePurchase = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
break;
|
||||
case 18: {
|
||||
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
|
||||
@ -356,7 +331,6 @@ export const handleStoreItemAcquisition = async (
|
||||
quantity: number = 1,
|
||||
durability: TRarity = "COMMON",
|
||||
ignorePurchaseQuantity: boolean = false,
|
||||
premiumPurchase: boolean = true,
|
||||
seed?: bigint
|
||||
): Promise<IPurchaseResponse> => {
|
||||
let purchaseResponse = {
|
||||
@ -378,20 +352,11 @@ export const handleStoreItemAcquisition = async (
|
||||
}
|
||||
switch (storeCategory) {
|
||||
default: {
|
||||
purchaseResponse = {
|
||||
InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
|
||||
};
|
||||
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
|
||||
break;
|
||||
}
|
||||
case "Types":
|
||||
purchaseResponse = await handleTypesPurchase(
|
||||
internalName,
|
||||
inventory,
|
||||
quantity,
|
||||
ignorePurchaseQuantity,
|
||||
premiumPurchase,
|
||||
seed
|
||||
);
|
||||
purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity, ignorePurchaseQuantity);
|
||||
break;
|
||||
case "Boosters":
|
||||
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
||||
@ -513,15 +478,13 @@ const handleTypesPurchase = async (
|
||||
typesName: string,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
quantity: number,
|
||||
ignorePurchaseQuantity: boolean,
|
||||
premiumPurchase: boolean = true,
|
||||
seed?: bigint
|
||||
ignorePurchaseQuantity: boolean
|
||||
): Promise<IPurchaseResponse> => {
|
||||
const typeCategory = getStoreItemTypesCategory(typesName);
|
||||
logger.debug(`type category ${typeCategory}`);
|
||||
switch (typeCategory) {
|
||||
default:
|
||||
return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
|
||||
return { InventoryChanges: await addItem(inventory, typesName, quantity) };
|
||||
case "BoosterPacks":
|
||||
return handleBoosterPackPurchase(typesName, inventory, quantity);
|
||||
case "SlotItems":
|
||||
|
@ -191,25 +191,6 @@ const getQuestCompletionItems = (questKey: string): ITypeCount[] | undefined =>
|
||||
return items;
|
||||
};
|
||||
|
||||
// Checks that `questKey` is in `requirements`, and if so, that all other quests in `requirements` are also already completed.
|
||||
const doesQuestCompletionFinishSet = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
questKey: string,
|
||||
requirements: string[]
|
||||
): boolean => {
|
||||
let holds = false;
|
||||
for (const requirement of requirements) {
|
||||
if (questKey == requirement) {
|
||||
holds = true;
|
||||
} else {
|
||||
if (!inventory.QuestKeys.find(x => x.ItemType == requirement)?.Completed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return holds;
|
||||
};
|
||||
|
||||
const handleQuestCompletion = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
questKey: string,
|
||||
@ -235,44 +216,6 @@ const handleQuestCompletion = async (
|
||||
setupKahlSyndicate(inventory);
|
||||
}
|
||||
|
||||
// Whispers in the Walls is unlocked once The New + Heart of Deimos are completed.
|
||||
if (
|
||||
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||
"/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain",
|
||||
"/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain"
|
||||
])
|
||||
) {
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Bosses/Loid",
|
||||
msg: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxBody",
|
||||
att: ["/Lotus/Types/Keys/EntratiLab/EntratiQuestKeyChain"],
|
||||
sub: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Entrati/Loid.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// The Hex (Quest) is unlocked once The Lotus Eaters + The Duviri Paradox are completed.
|
||||
if (
|
||||
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||
"/Lotus/Types/Keys/1999PrologueQuest/1999PrologueQuestKeyChain",
|
||||
"/Lotus/Types/Keys/DuviriQuest/DuviriQuestKeyChain"
|
||||
])
|
||||
) {
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/NewWar/P3M1ChooseMara",
|
||||
msg: "/Lotus/Language/1999Quest/1999QuestInboxBody",
|
||||
att: ["/Lotus/Types/Keys/1999Quest/1999QuestKeyChain"],
|
||||
sub: "/Lotus/Language/1999Quest/1999QuestInboxSubject",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Operator.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
const questCompletionItems = getQuestCompletionItems(questKey);
|
||||
logger.debug(`quest completion items`, questCompletionItems);
|
||||
if (questCompletionItems) {
|
||||
@ -289,7 +232,7 @@ export const giveKeyChainItem = async (
|
||||
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
||||
|
||||
if (isEmptyObject(inventoryChanges)) {
|
||||
logger.warn("inventory changes was empty after getting keychain items: should not happen");
|
||||
throw new Error("inventory changes was empty after getting keychain items: should not happen");
|
||||
}
|
||||
// items were added: update quest stage's i (item was given)
|
||||
updateQuestStage(inventory, keyChainInfo, { i: true });
|
||||
|
@ -6,7 +6,7 @@ export interface IRngResult {
|
||||
probability: number;
|
||||
}
|
||||
|
||||
export const getRandomElement = <T>(arr: T[]): T | undefined => {
|
||||
export const getRandomElement = <T>(arr: T[]): T => {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
};
|
||||
|
||||
@ -18,10 +18,7 @@ export const getRandomInt = (min: number, max: number): number => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
export const getRewardAtPercentage = <T extends { probability: number }>(
|
||||
pool: T[],
|
||||
percentage: number
|
||||
): T | undefined => {
|
||||
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
|
||||
if (pool.length == 0) return;
|
||||
|
||||
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
|
||||
@ -34,7 +31,7 @@ export const getRewardAtPercentage = <T extends { probability: number }>(
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return pool[pool.length - 1];
|
||||
throw new Error("What the fuck?");
|
||||
};
|
||||
|
||||
export const getRandomReward = <T extends { probability: number }>(pool: T[]): T | undefined => {
|
||||
@ -100,32 +97,18 @@ export class CRng {
|
||||
}
|
||||
|
||||
randomInt(min: number, max: number): number {
|
||||
const diff = max - min;
|
||||
if (diff != 0) {
|
||||
if (diff < 0) {
|
||||
throw new Error(`max must be greater than min`);
|
||||
}
|
||||
if (diff > 0x3fffffff) {
|
||||
throw new Error(`insufficient entropy`);
|
||||
}
|
||||
min += Math.floor(this.random() * (diff + 1));
|
||||
}
|
||||
return min;
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(this.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
randomElement<T>(arr: T[]): T | undefined {
|
||||
randomElement<T>(arr: T[]): T {
|
||||
return arr[Math.floor(this.random() * arr.length)];
|
||||
}
|
||||
|
||||
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||
return getRewardAtPercentage(pool, this.random());
|
||||
}
|
||||
|
||||
churnSeed(its: number): void {
|
||||
while (its--) {
|
||||
this.state = (this.state * 1103515245 + 12345) & 0x7fffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seeded RNG for cases where we need identical results to the game client. Based on work by Donald Knuth.
|
||||
@ -145,7 +128,7 @@ export class SRng {
|
||||
return min;
|
||||
}
|
||||
|
||||
randomElement<T>(arr: T[]): T | undefined {
|
||||
randomElement<T>(arr: T[]): T {
|
||||
return arr[this.randomInt(0, arr.length - 1)];
|
||||
}
|
||||
|
||||
@ -153,8 +136,4 @@ export class SRng {
|
||||
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
|
||||
return (Number(this.state >> 38n) & 0xffffff) * 0.000000059604645;
|
||||
}
|
||||
|
||||
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||
return getRewardAtPercentage(pool, this.randomFloat());
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ import { Types } from "mongoose";
|
||||
import { isEmptyObject } from "@/src/helpers/general";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
|
||||
import { importCrewMemberId } from "./importService";
|
||||
|
||||
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
||||
|
||||
@ -142,28 +140,8 @@ export const handleInventoryItemConfigChange = async (
|
||||
case "WeaponSkins": {
|
||||
const itemEntries = equipment as IItemEntry;
|
||||
for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
|
||||
if (itemId.startsWith("ca70ca70ca70ca70")) {
|
||||
logger.warn(
|
||||
`unlockAllSkins does not work with favoriting items because you don't actually own it`
|
||||
);
|
||||
} else {
|
||||
const inventoryItem = inventory.WeaponSkins.id(itemId);
|
||||
if (!inventoryItem) {
|
||||
throw new Error(`inventory item WeaponSkins not found with id ${itemId}`);
|
||||
inventory.WeaponSkins.id(itemId)!.IsNew = itemConfigEntries.IsNew;
|
||||
}
|
||||
if ("Favorite" in itemConfigEntries) {
|
||||
inventoryItem.Favorite = itemConfigEntries.Favorite;
|
||||
}
|
||||
if ("IsNew" in itemConfigEntries) {
|
||||
inventoryItem.IsNew = itemConfigEntries.IsNew;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "LotusCustomization": {
|
||||
logger.debug(`saved LotusCustomization`, equipmentChanges.LotusCustomization);
|
||||
inventory.LotusCustomization = equipmentChanges.LotusCustomization;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -181,36 +159,13 @@ export const handleInventoryItemConfigChange = async (
|
||||
}
|
||||
|
||||
for (const [configId, config] of Object.entries(itemConfigEntries)) {
|
||||
if (/^[0-9]+$/.test(configId)) {
|
||||
inventoryItem.Configs[parseInt(configId)] = config as IItemConfig;
|
||||
if (typeof config !== "boolean") {
|
||||
inventoryItem.Configs[parseInt(configId)] = config;
|
||||
}
|
||||
}
|
||||
if ("Favorite" in itemConfigEntries) {
|
||||
inventoryItem.Favorite = itemConfigEntries.Favorite;
|
||||
}
|
||||
if ("IsNew" in itemConfigEntries) {
|
||||
inventoryItem.IsNew = itemConfigEntries.IsNew;
|
||||
}
|
||||
|
||||
if ("ItemName" in itemConfigEntries) {
|
||||
inventoryItem.ItemName = itemConfigEntries.ItemName;
|
||||
}
|
||||
if ("RailjackImage" in itemConfigEntries) {
|
||||
inventoryItem.RailjackImage = itemConfigEntries.RailjackImage;
|
||||
}
|
||||
if ("Customization" in itemConfigEntries) {
|
||||
inventoryItem.Customization = itemConfigEntries.Customization;
|
||||
}
|
||||
if ("Weapon" in itemConfigEntries) {
|
||||
inventoryItem.Weapon = itemConfigEntries.Weapon;
|
||||
}
|
||||
if (itemConfigEntries.CrewMembers) {
|
||||
inventoryItem.CrewMembers = {
|
||||
SLOT_A: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_A ?? {}),
|
||||
SLOT_B: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_B ?? {}),
|
||||
SLOT_C: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_C ?? {})
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
|
@ -1,308 +1,143 @@
|
||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { repoDir } from "@/src/helpers/pathHelper";
|
||||
import { CRng, mixSeeds } from "@/src/services/rngService";
|
||||
import { IMongoDate } from "@/src/types/commonTypes";
|
||||
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
|
||||
import { ExportVendors, IRange } from "warframe-public-export-plus";
|
||||
import { IItemManifestPreprocessed, IRawVendorManifest, IVendorManifestPreprocessed } from "@/src/types/vendorTypes";
|
||||
import { JSONParse } from "json-with-bigint";
|
||||
import { ExportVendors } from "warframe-public-export-plus";
|
||||
|
||||
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
|
||||
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
|
||||
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
|
||||
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
|
||||
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
|
||||
import DeimosHivemindCommisionsManifestTokenVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestTokenVendor.json";
|
||||
import DeimosHivemindCommisionsManifestWeaponsmith from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestWeaponsmith.json";
|
||||
import DeimosHivemindTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosHivemindTokenVendorManifest.json";
|
||||
import DeimosPetVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosPetVendorManifest.json";
|
||||
import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosProspectorVendorManifest.json";
|
||||
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
|
||||
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
|
||||
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
|
||||
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
|
||||
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
|
||||
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
|
||||
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
|
||||
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
||||
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
|
||||
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
|
||||
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
||||
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.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";
|
||||
|
||||
const rawVendorManifests: IVendorManifest[] = [
|
||||
ArchimedeanVendorManifest,
|
||||
DeimosEntratiFragmentVendorProductsManifest,
|
||||
DeimosHivemindCommisionsManifestFishmonger,
|
||||
DeimosHivemindCommisionsManifestPetVendor,
|
||||
DeimosHivemindCommisionsManifestProspector,
|
||||
DeimosHivemindCommisionsManifestTokenVendor,
|
||||
DeimosHivemindCommisionsManifestWeaponsmith,
|
||||
DeimosHivemindTokenVendorManifest,
|
||||
DeimosPetVendorManifest,
|
||||
DeimosProspectorVendorManifest,
|
||||
DuviriAcrithisVendorManifest,
|
||||
EntratiLabsEntratiLabsCommisionsManifest,
|
||||
EntratiLabsEntratiLabVendorManifest,
|
||||
HubsIronwakeDondaVendorManifest, // uses preprocessing
|
||||
HubsRailjackCrewMemberVendorManifest,
|
||||
MaskSalesmanManifest,
|
||||
Nova1999ConquestShopManifest,
|
||||
OstronPetVendorManifest,
|
||||
OstronProspectorVendorManifest,
|
||||
RadioLegionIntermission12VendorManifest,
|
||||
SolarisDebtTokenVendorRepossessionsManifest,
|
||||
SolarisProspectorVendorManifest,
|
||||
Temple1999VendorManifest,
|
||||
TeshinHardModeVendorManifest, // uses preprocessing
|
||||
ZarimanCommisionsManifestArchimedean
|
||||
];
|
||||
|
||||
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
|
||||
cycleOffset?: number;
|
||||
cycleDuration: number;
|
||||
}
|
||||
|
||||
const generatableVendors: IGeneratableVendorInfo[] = [
|
||||
{
|
||||
_id: { $oid: "67dadc30e4b6e0e5979c8d84" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
|
||||
RandomSeedType: "VRST_WEAPON",
|
||||
RequiredGoalTag: "",
|
||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||
cycleOffset: 1740960000_000,
|
||||
cycleDuration: 4 * unixTimesInMs.day
|
||||
},
|
||||
{
|
||||
_id: { $oid: "60ad3b6ec96976e97d227e19" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/PerrinSequenceWeaponVendorManifest",
|
||||
RandomSeedType: "VRST_WEAPON",
|
||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||
cycleOffset: 1744934400_000,
|
||||
cycleDuration: 4 * unixTimesInMs.day
|
||||
},
|
||||
{
|
||||
_id: { $oid: "61ba123467e5d37975aeeb03" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
|
||||
RandomSeedType: "VRST_FLAVOUR_TEXT",
|
||||
cycleDuration: unixTimesInMs.week // TODO: Auto-detect this based on the items, so we don't need to specify it explicitly.
|
||||
}
|
||||
// {
|
||||
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
|
||||
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest"
|
||||
// }
|
||||
];
|
||||
|
||||
const getVendorOid = (typeName: string): string => {
|
||||
return "5be4a159b144f3cd" + catBreadHash(typeName).toString(16).padStart(8, "0");
|
||||
const getVendorManifestJson = (name: string): IRawVendorManifest => {
|
||||
return JSONParse(fs.readFileSync(path.join(repoDir, `static/fixed_responses/getVendorInfo/${name}.json`), "utf-8"));
|
||||
};
|
||||
|
||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
|
||||
const rawVendorManifests: IRawVendorManifest[] = [
|
||||
getVendorManifestJson("ArchimedeanVendorManifest"),
|
||||
getVendorManifestJson("DeimosEntratiFragmentVendorProductsManifest"),
|
||||
getVendorManifestJson("DeimosFishmongerVendorManifest"),
|
||||
getVendorManifestJson("DeimosHivemindCommisionsManifestFishmonger"),
|
||||
getVendorManifestJson("DeimosHivemindCommisionsManifestPetVendor"),
|
||||
getVendorManifestJson("DeimosHivemindCommisionsManifestProspector"),
|
||||
getVendorManifestJson("DeimosHivemindCommisionsManifestTokenVendor"),
|
||||
getVendorManifestJson("DeimosHivemindCommisionsManifestWeaponsmith"),
|
||||
getVendorManifestJson("DeimosHivemindTokenVendorManifest"),
|
||||
getVendorManifestJson("DeimosPetVendorManifest"),
|
||||
getVendorManifestJson("DeimosProspectorVendorManifest"),
|
||||
getVendorManifestJson("DuviriAcrithisVendorManifest"),
|
||||
getVendorManifestJson("EntratiLabsEntratiLabsCommisionsManifest"),
|
||||
getVendorManifestJson("EntratiLabsEntratiLabVendorManifest"),
|
||||
getVendorManifestJson("GuildAdvertisementVendorManifest"), // uses preprocessing
|
||||
getVendorManifestJson("HubsIronwakeDondaVendorManifest"), // uses preprocessing
|
||||
getVendorManifestJson("HubsPerrinSequenceWeaponVendorManifest"),
|
||||
getVendorManifestJson("HubsRailjackCrewMemberVendorManifest"),
|
||||
getVendorManifestJson("MaskSalesmanManifest"),
|
||||
getVendorManifestJson("Nova1999ConquestShopManifest"),
|
||||
getVendorManifestJson("OstronFishmongerVendorManifest"),
|
||||
getVendorManifestJson("OstronPetVendorManifest"),
|
||||
getVendorManifestJson("OstronProspectorVendorManifest"),
|
||||
getVendorManifestJson("RadioLegionIntermission12VendorManifest"),
|
||||
getVendorManifestJson("SolarisDebtTokenVendorManifest"),
|
||||
getVendorManifestJson("SolarisDebtTokenVendorRepossessionsManifest"),
|
||||
getVendorManifestJson("SolarisFishmongerVendorManifest"),
|
||||
getVendorManifestJson("SolarisProspectorVendorManifest"),
|
||||
getVendorManifestJson("TeshinHardModeVendorManifest"), // uses preprocessing
|
||||
getVendorManifestJson("ZarimanCommisionsManifestArchimedean")
|
||||
];
|
||||
|
||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPreprocessed | undefined => {
|
||||
for (const vendorManifest of rawVendorManifests) {
|
||||
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
||||
return preprocessVendorManifest(vendorManifest);
|
||||
}
|
||||
}
|
||||
for (const vendorInfo of generatableVendors) {
|
||||
if (vendorInfo.TypeName == typeName) {
|
||||
return generateVendorManifest(vendorInfo);
|
||||
}
|
||||
}
|
||||
if (typeName in ExportVendors) {
|
||||
return generateVendorManifest({
|
||||
_id: { $oid: getVendorOid(typeName) },
|
||||
TypeName: typeName,
|
||||
RandomSeedType: ExportVendors[typeName].randomSeedType,
|
||||
cycleDuration: unixTimesInMs.hour
|
||||
});
|
||||
if (typeName == "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest") {
|
||||
return generateCodaWeaponVendorManifest();
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
|
||||
export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed | undefined => {
|
||||
for (const vendorManifest of rawVendorManifests) {
|
||||
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
||||
return preprocessVendorManifest(vendorManifest);
|
||||
}
|
||||
}
|
||||
for (const vendorInfo of generatableVendors) {
|
||||
if (vendorInfo._id.$oid == oid) {
|
||||
return generateVendorManifest(vendorInfo);
|
||||
}
|
||||
}
|
||||
for (const [typeName, manifest] of Object.entries(ExportVendors)) {
|
||||
const typeNameOid = getVendorOid(typeName);
|
||||
if (typeNameOid == oid) {
|
||||
return generateVendorManifest({
|
||||
_id: { $oid: typeNameOid },
|
||||
TypeName: typeName,
|
||||
RandomSeedType: manifest.randomSeedType,
|
||||
cycleDuration: unixTimesInMs.hour
|
||||
});
|
||||
}
|
||||
if (oid == "67dadc30e4b6e0e5979c8d84") {
|
||||
return generateCodaWeaponVendorManifest();
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
|
||||
const preprocessVendorManifest = (originalManifest: IRawVendorManifest): IVendorManifestPreprocessed => {
|
||||
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);
|
||||
const iteration = refreshExpiry(offer.Expiry);
|
||||
if (offer.ItemPrices) {
|
||||
for (const price of offer.ItemPrices) {
|
||||
if (typeof price.ItemType != "string") {
|
||||
const itemSeed = parseInt(offer.Id.$oid.substring(16), 16);
|
||||
const rng = new CRng(mixSeeds(itemSeed, iteration));
|
||||
price.ItemType = rng.randomElement(price.ItemType);
|
||||
}
|
||||
return manifest;
|
||||
}
|
||||
return originalManifest;
|
||||
}
|
||||
}
|
||||
return manifest as IVendorManifestPreprocessed;
|
||||
}
|
||||
return originalManifest as IVendorManifestPreprocessed;
|
||||
};
|
||||
|
||||
const refreshExpiry = (expiry: IMongoDate): void => {
|
||||
const refreshExpiry = (expiry: IMongoDate): number => {
|
||||
const period = parseInt(expiry.$date.$numberLong);
|
||||
if (Date.now() >= period) {
|
||||
const epoch = 1734307200_000; // Monday (for weekly schedules)
|
||||
const epoch = 1734307200 * 1000; // 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();
|
||||
return iteration;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const toRange = (value: IRange | number): IRange => {
|
||||
if (typeof value == "number") {
|
||||
return { minValue: value, maxValue: value };
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const vendorInfoCache: Record<string, IVendorInfo> = {};
|
||||
|
||||
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
|
||||
if (!(vendorInfo.TypeName in vendorInfoCache)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
|
||||
vendorInfoCache[vendorInfo.TypeName] = {
|
||||
...clientVendorInfo,
|
||||
ItemManifest: [],
|
||||
Expiry: { $date: { $numberLong: "0" } }
|
||||
};
|
||||
}
|
||||
const processed = vendorInfoCache[vendorInfo.TypeName];
|
||||
if (Date.now() >= parseInt(processed.Expiry.$date.$numberLong)) {
|
||||
// Remove expired offers
|
||||
for (let i = 0; i != processed.ItemManifest.length; ) {
|
||||
if (Date.now() >= parseInt(processed.ItemManifest[i].Expiry.$date.$numberLong)) {
|
||||
processed.ItemManifest.splice(i, 1);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new offers
|
||||
const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
|
||||
const cycleOffset = vendorInfo.cycleOffset ?? 1734307200_000;
|
||||
const cycleDuration = vendorInfo.cycleDuration;
|
||||
const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
|
||||
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
|
||||
const manifest = ExportVendors[vendorInfo.TypeName];
|
||||
const offersToAdd = [];
|
||||
if (manifest.numItems && !manifest.isOneBinPerCycle) {
|
||||
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
|
||||
while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) {
|
||||
// TODO: Consider per-bin item limits
|
||||
// TODO: Consider item probability weightings
|
||||
offersToAdd.push(rng.randomElement(manifest.items)!);
|
||||
}
|
||||
} else {
|
||||
let binThisCycle;
|
||||
if (manifest.isOneBinPerCycle) {
|
||||
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
||||
}
|
||||
const generateCodaWeaponVendorManifest = (): IVendorManifestPreprocessed => {
|
||||
const EPOCH = 1740960000 * 1000;
|
||||
const DUR = 4 * 86400 * 1000;
|
||||
const cycle = Math.trunc((Date.now() - EPOCH) / DUR);
|
||||
const cycleStart = EPOCH + cycle * DUR;
|
||||
const cycleEnd = cycleStart + DUR;
|
||||
const binThisCycle = cycle % 2; // isOneBinPerCycle
|
||||
const items: IItemManifestPreprocessed[] = [];
|
||||
const manifest = ExportVendors["/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest"];
|
||||
const rng = new CRng(cycle);
|
||||
for (const rawItem of manifest.items) {
|
||||
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) {
|
||||
offersToAdd.push(rawItem);
|
||||
if (rawItem.bin != binThisCycle) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// For most vendors, the offers seem to roughly be in reverse order from the manifest. Coda weapons are an odd exception.
|
||||
if (!manifest.isOneBinPerCycle) {
|
||||
offersToAdd.reverse();
|
||||
}
|
||||
}
|
||||
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
|
||||
for (const rawItem of offersToAdd) {
|
||||
const durationHoursRange = toRange(rawItem.durationHours);
|
||||
const expiry =
|
||||
cycleStart +
|
||||
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
|
||||
const item: IItemManifest = {
|
||||
items.push({
|
||||
StoreItem: rawItem.storeItem,
|
||||
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||
ItemPrices: rawItem.itemPrices!.map(item => ({ ...item, ProductCategory: "MiscItems" })),
|
||||
Bin: "BIN_" + rawItem.bin,
|
||||
QuantityMultiplier: 1,
|
||||
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } },
|
||||
AllowMultipurchase: false,
|
||||
Id: {
|
||||
$oid:
|
||||
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
||||
vendorInfo._id.$oid.substring(8, 16) +
|
||||
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0") +
|
||||
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0")
|
||||
}
|
||||
};
|
||||
if (rawItem.numRandomItemPrices) {
|
||||
item.ItemPrices = [];
|
||||
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
|
||||
let itemPrice: { type: string; count: IRange };
|
||||
do {
|
||||
itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin])!;
|
||||
} while (item.ItemPrices.find(x => x.ItemType == itemPrice.type));
|
||||
item.ItemPrices.push({
|
||||
ItemType: itemPrice.type,
|
||||
ItemCount: rng.randomInt(itemPrice.count.minValue, itemPrice.count.maxValue),
|
||||
ProductCategory: "MiscItems"
|
||||
LocTagRandSeed: (BigInt(rng.randomInt(0, 0xffffffff)) << 32n) | BigInt(rng.randomInt(0, 0xffffffff)),
|
||||
Id: { $oid: "67e9da12793a120d" + rng.randomInt(0, 0xffffffff).toString(16).padStart(8, "0") }
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rawItem.credits) {
|
||||
const value =
|
||||
typeof rawItem.credits == "number"
|
||||
? rawItem.credits
|
||||
: rng.randomInt(
|
||||
rawItem.credits.minValue / rawItem.credits.step,
|
||||
rawItem.credits.maxValue / rawItem.credits.step
|
||||
) * rawItem.credits.step;
|
||||
item.RegularPrice = [value, value];
|
||||
}
|
||||
if (rawItem.platinum) {
|
||||
const value =
|
||||
typeof rawItem.platinum == "number"
|
||||
? rawItem.platinum
|
||||
: rng.randomInt(rawItem.platinum.minValue, rawItem.platinum.maxValue);
|
||||
item.PremiumPrice = [value, value];
|
||||
}
|
||||
if (vendorInfo.RandomSeedType) {
|
||||
item.LocTagRandSeed = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||
if (vendorInfo.RandomSeedType == "VRST_WEAPON") {
|
||||
const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||
item.LocTagRandSeed = (BigInt(highDword) << 32n) | (BigInt(item.LocTagRandSeed) & 0xffffffffn);
|
||||
}
|
||||
}
|
||||
processed.ItemManifest.push(item);
|
||||
}
|
||||
|
||||
// Update vendor expiry
|
||||
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
|
||||
for (const offer of processed.ItemManifest) {
|
||||
const offerExpiry = parseInt(offer.Expiry.$date.$numberLong);
|
||||
if (soonestOfferExpiry > offerExpiry) {
|
||||
soonestOfferExpiry = offerExpiry;
|
||||
}
|
||||
}
|
||||
processed.Expiry.$date.$numberLong = soonestOfferExpiry.toString();
|
||||
}
|
||||
return {
|
||||
VendorInfo: processed
|
||||
VendorInfo: {
|
||||
_id: { $oid: "67dadc30e4b6e0e5979c8d84" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
|
||||
ItemManifest: items,
|
||||
PropertyTextHash: "77093DD05A8561A022DEC9A4B9BB4A56",
|
||||
RandomSeedType: "VRST_WEAPON",
|
||||
RequiredGoalTag: "",
|
||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } }
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
|
||||
import {
|
||||
IEnemy,
|
||||
IStatsAdd,
|
||||
IStatsMax,
|
||||
IStatsSet,
|
||||
@ -136,21 +137,16 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
|
||||
case "HEADSHOT":
|
||||
case "KILL_ASSIST": {
|
||||
playerStats.Enemies ??= [];
|
||||
const enemyStatKey = (
|
||||
{
|
||||
const enemyStatKey = {
|
||||
KILL_ENEMY: "kills",
|
||||
EXECUTE_ENEMY: "executions",
|
||||
HEADSHOT: "headshots",
|
||||
KILL_ASSIST: "assists"
|
||||
} as const
|
||||
)[category];
|
||||
}[category] as "kills" | "executions" | "headshots" | "assists";
|
||||
|
||||
for (const [type, count] of Object.entries(data as IUploadEntry)) {
|
||||
let enemy = playerStats.Enemies.find(element => element.type === type);
|
||||
if (!enemy) {
|
||||
enemy = { type: type };
|
||||
playerStats.Enemies.push(enemy);
|
||||
}
|
||||
const enemy = playerStats.Enemies.find(element => element.type === type);
|
||||
if (enemy) {
|
||||
if (category === "KILL_ENEMY") {
|
||||
enemy.kills ??= 0;
|
||||
const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type];
|
||||
@ -165,6 +161,11 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
|
||||
enemy[enemyStatKey] ??= 0;
|
||||
enemy[enemyStatKey] += count;
|
||||
}
|
||||
} else {
|
||||
const newEnemy: IEnemy = { type: type };
|
||||
newEnemy[enemyStatKey] = count;
|
||||
playerStats.Enemies.push(newEnemy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -377,8 +378,7 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
|
||||
category,
|
||||
accountOwnerId,
|
||||
payload.displayName,
|
||||
data as number,
|
||||
payload.guildId
|
||||
data as number
|
||||
);
|
||||
break;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@ export interface IGuildClient {
|
||||
Members: IGuildMemberClient[];
|
||||
Ranks: IGuildRank[];
|
||||
Tier: number;
|
||||
Emblem?: boolean;
|
||||
Vault: IGuildVault;
|
||||
ActiveDojoColorResearch: string;
|
||||
Class: number;
|
||||
@ -45,7 +44,6 @@ export interface IGuildDatabase {
|
||||
VaultMiscItems?: IMiscItem[];
|
||||
VaultShipDecorations?: ITypeCount[];
|
||||
VaultFusionTreasures?: IFusionTreasure[];
|
||||
VaultDecoRecipes?: ITypeCount[];
|
||||
|
||||
TechProjects?: ITechProjectDatabase[];
|
||||
ActiveDojoColorResearch: string;
|
||||
@ -104,12 +102,12 @@ export interface IGuildMemberDatabase {
|
||||
ShipDecorationsContributed?: ITypeCount[];
|
||||
}
|
||||
|
||||
export interface IFriendInfo {
|
||||
interface IFriendInfo {
|
||||
_id: IOid;
|
||||
DisplayName?: string;
|
||||
PlatformNames?: string[];
|
||||
PlatformAccountId?: string;
|
||||
Status?: number;
|
||||
Status: number;
|
||||
ActiveAvatarImageType?: string;
|
||||
LastLogin?: IMongoDate;
|
||||
PlayerLevel?: number;
|
||||
@ -192,7 +190,6 @@ export interface IDojoComponentDatabase
|
||||
"id" | "SortId" | "pi" | "CompletionTime" | "DestructionTime" | "Decos" | "PaintBot"
|
||||
> {
|
||||
_id: Types.ObjectId;
|
||||
SortId?: Types.ObjectId;
|
||||
pi?: Types.ObjectId;
|
||||
CompletionTime?: Date;
|
||||
CompletionLogPending?: boolean;
|
||||
@ -207,7 +204,6 @@ export interface IDojoDecoClient {
|
||||
Type: string;
|
||||
Pos: number[];
|
||||
Rot: number[];
|
||||
Scale?: number;
|
||||
Name?: string; // for teleporters
|
||||
Sockets?: number;
|
||||
RegularCredits?: number;
|
||||
|
@ -142,7 +142,6 @@ export interface IEquipmentDatabase {
|
||||
RailjackImage?: IFlavourItem;
|
||||
CrewMembers?: ICrewShipMembersDatabase;
|
||||
Details?: IKubrowPetDetailsDatabase;
|
||||
Favorite?: boolean;
|
||||
IsNew?: boolean;
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
@ -43,16 +43,9 @@ export interface IInventoryDatabase
|
||||
| "RecentVendorPurchases"
|
||||
| "NextRefill"
|
||||
| "Nemesis"
|
||||
| "NemesisHistory"
|
||||
| "EntratiVaultCountResetDate"
|
||||
| "BrandedSuits"
|
||||
| "LockedWeaponGroup"
|
||||
| "PersonalTechProjects"
|
||||
| "LastSortieReward"
|
||||
| "LastLiteSortieReward"
|
||||
| "CrewMembers"
|
||||
| "QualifyingInvasions"
|
||||
| "LastInventorySync"
|
||||
| TEquipmentKey
|
||||
>,
|
||||
InventoryDatabaseEquipment {
|
||||
@ -81,16 +74,9 @@ export interface IInventoryDatabase
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
|
||||
NextRefill?: Date;
|
||||
Nemesis?: INemesisDatabase;
|
||||
NemesisHistory?: INemesisBaseDatabase[];
|
||||
EntratiVaultCountResetDate?: Date;
|
||||
BrandedSuits?: Types.ObjectId[];
|
||||
LockedWeaponGroup?: ILockedWeaponGroupDatabase;
|
||||
PersonalTechProjects: IPersonalTechProjectDatabase[];
|
||||
LastSortieReward?: ILastSortieRewardDatabase[];
|
||||
LastLiteSortieReward?: ILastSortieRewardDatabase[];
|
||||
CrewMembers: ICrewMemberDatabase[];
|
||||
QualifyingInvasions: IInvasionProgressDatabase[];
|
||||
LastInventorySync?: Types.ObjectId;
|
||||
}
|
||||
|
||||
export interface IQuestKeyDatabase {
|
||||
@ -138,7 +124,7 @@ export const equipmentKeys = [
|
||||
export type TEquipmentKey = (typeof equipmentKeys)[number];
|
||||
|
||||
export interface IDuviriInfo {
|
||||
Seed: bigint;
|
||||
Seed: number;
|
||||
NumCompletions: number;
|
||||
}
|
||||
|
||||
@ -171,11 +157,6 @@ export type TSolarMapRegion =
|
||||
|
||||
//TODO: perhaps split response and database into their own files
|
||||
|
||||
export enum LoadoutIndex {
|
||||
NORMAL = 0,
|
||||
DATAKNIFE = 7
|
||||
}
|
||||
|
||||
export interface IDailyAffiliations {
|
||||
DailyAffiliation: number;
|
||||
DailyAffiliationPvp: number;
|
||||
@ -206,7 +187,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
Mailbox?: IMailboxClient;
|
||||
SubscribedToEmails: number;
|
||||
Created: IMongoDate;
|
||||
RewardSeed: bigint;
|
||||
RewardSeed: number;
|
||||
RegularCredits: number;
|
||||
PremiumCredits: number;
|
||||
PremiumCreditsFree: number;
|
||||
@ -239,7 +220,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
ActiveQuest: string;
|
||||
FlavourItems: IFlavourItem[];
|
||||
LoadOutPresets: ILoadOutPresets;
|
||||
CurrentLoadOutIds: IOid[]; // we store it in the database using this representation as well :/
|
||||
CurrentLoadOutIds: IOid[]; //TODO: we store it in the database using this representation as well :/
|
||||
Missions: IMission[];
|
||||
RandomUpgradesIdentified?: number;
|
||||
LastRegionPlayed: TSolarMapRegion;
|
||||
@ -254,13 +235,14 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
Guide?: number;
|
||||
Moderator?: boolean;
|
||||
Partner?: boolean;
|
||||
Accolades?: IAccolades;
|
||||
Counselor?: boolean;
|
||||
Accolades?: {
|
||||
Heirloom?: boolean;
|
||||
};
|
||||
Upgrades: IUpgradeClient[];
|
||||
EquippedGear: string[];
|
||||
DeathMarks: string[];
|
||||
FusionTreasures: IFusionTreasure[];
|
||||
//WebFlags: IWebFlags;
|
||||
WebFlags: IWebFlags;
|
||||
CompletedAlerts: string[];
|
||||
Consumables: ITypeCount[];
|
||||
LevelKeys: ITypeCount[];
|
||||
@ -270,13 +252,13 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
KubrowPetEggs?: IKubrowPetEggClient[];
|
||||
LoreFragmentScans: ILoreFragmentScan[];
|
||||
EquippedEmotes: string[];
|
||||
//PendingTrades: IPendingTrade[];
|
||||
PendingTrades: IPendingTrade[];
|
||||
Boosters: IBooster[];
|
||||
ActiveDojoColorResearch: string;
|
||||
//SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
|
||||
SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
|
||||
SupportedSyndicate?: string;
|
||||
Affiliations: IAffiliation[];
|
||||
QualifyingInvasions: IInvasionProgressClient[];
|
||||
QualifyingInvasions: any[];
|
||||
FactionScores: number[];
|
||||
ArchwingEnabled?: boolean;
|
||||
PendingSpectreLoadouts?: ISpectreLoadout[];
|
||||
@ -287,65 +269,63 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
Wishlist: string[];
|
||||
Alignment?: IAlignment;
|
||||
CompletedSorties: string[];
|
||||
LastSortieReward?: ILastSortieRewardClient[];
|
||||
LastLiteSortieReward?: ILastSortieRewardClient[];
|
||||
SortieRewardAttenuation?: ISortieRewardAttenuation[];
|
||||
LastSortieReward: ILastSortieReward[];
|
||||
Drones: IDroneClient[];
|
||||
StepSequencers: IStepSequencer[];
|
||||
ActiveAvatarImageType: string;
|
||||
ShipDecorations: ITypeCount[];
|
||||
DiscoveredMarkers: IDiscoveredMarker[];
|
||||
//CompletedJobs: ICompletedJob[];
|
||||
CompletedJobs: ICompletedJob[];
|
||||
FocusAbility?: string;
|
||||
FocusUpgrades: IFocusUpgrade[];
|
||||
HasContributedToDojo?: boolean;
|
||||
HWIDProtectEnabled?: boolean;
|
||||
//KubrowPetPrints: IKubrowPetPrint[];
|
||||
KubrowPetPrints: IKubrowPetPrint[];
|
||||
AlignmentReplay?: IAlignment;
|
||||
//PersonalGoalProgress: IPersonalGoalProgress[];
|
||||
PersonalGoalProgress: IPersonalGoalProgress[];
|
||||
ThemeStyle: string;
|
||||
ThemeBackground: string;
|
||||
ThemeSounds: string;
|
||||
BountyScore: number;
|
||||
//ChallengeInstanceStates: IChallengeInstanceState[];
|
||||
ChallengeInstanceStates: IChallengeInstanceState[];
|
||||
LoginMilestoneRewards: string[];
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseClient[];
|
||||
NodeIntrosCompleted: string[];
|
||||
GuildId?: IOid;
|
||||
CompletedJobChains?: ICompletedJobChain[];
|
||||
CompletedJobChains: ICompletedJobChain[];
|
||||
SeasonChallengeHistory: ISeasonChallenge[];
|
||||
EquippedInstrument?: string;
|
||||
//InvasionChainProgress: IInvasionChainProgress[];
|
||||
InvasionChainProgress: IInvasionChainProgress[];
|
||||
Nemesis?: INemesisClient;
|
||||
NemesisHistory?: INemesisBaseClient[];
|
||||
//LastNemesisAllySpawnTime?: IMongoDate;
|
||||
NemesisHistory: INemesisBaseClient[];
|
||||
LastNemesisAllySpawnTime?: IMongoDate;
|
||||
Settings?: ISettings;
|
||||
PersonalTechProjects: IPersonalTechProjectClient[];
|
||||
PersonalTechProjects: IPersonalTechProject[];
|
||||
PlayerSkills: IPlayerSkills;
|
||||
CrewShipAmmo: ITypeCount[];
|
||||
CrewShipWeaponSkins: IUpgradeClient[];
|
||||
CrewShipSalvagedWeaponSkins: IUpgradeClient[];
|
||||
//TradeBannedUntil?: IMongoDate;
|
||||
TradeBannedUntil?: IMongoDate;
|
||||
PlayedParkourTutorial: boolean;
|
||||
SubscribedToEmailsPersonalized: number;
|
||||
InfestedFoundry?: IInfestedFoundryClient;
|
||||
BlessingCooldown?: IMongoDate;
|
||||
CrewShipRawSalvage: ITypeCount[];
|
||||
CrewMembers: ICrewMemberClient[];
|
||||
LotusCustomization?: ILotusCustomization;
|
||||
CrewMembers: ICrewMember[];
|
||||
LotusCustomization: ILotusCustomization;
|
||||
UseAdultOperatorLoadout?: boolean;
|
||||
NemesisAbandonedRewards: string[];
|
||||
LastInventorySync?: IOid;
|
||||
LastInventorySync: IOid;
|
||||
NextRefill?: IMongoDate;
|
||||
FoundToday?: IMiscItem[]; // for Argon Crystals
|
||||
CustomMarkers?: ICustomMarkers[];
|
||||
//ActiveLandscapeTraps: any[];
|
||||
ActiveLandscapeTraps: any[];
|
||||
EvolutionProgress?: IEvolutionProgress[];
|
||||
//RepVotes: any[];
|
||||
//LeagueTickets: any[];
|
||||
//Quests: any[];
|
||||
//Robotics: any[];
|
||||
//UsedDailyDeals: any[];
|
||||
RepVotes: any[];
|
||||
LeagueTickets: any[];
|
||||
Quests: any[];
|
||||
Robotics: any[];
|
||||
UsedDailyDeals: any[];
|
||||
LibraryPersonalTarget?: string;
|
||||
LibraryPersonalProgress: ILibraryPersonalProgress[];
|
||||
CollectibleSeries?: ICollectibleEntry[];
|
||||
@ -357,7 +337,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
DeathSquadable: boolean;
|
||||
EndlessXP?: IEndlessXpProgress[];
|
||||
DialogueHistory?: IDialogueHistoryClient;
|
||||
CalendarProgress?: ICalendarProgress;
|
||||
CalendarProgress: ICalendarProgress;
|
||||
SongChallenges?: ISongChallenge[];
|
||||
EntratiVaultCountLastPeriod?: number;
|
||||
EntratiVaultCountResetDate?: IMongoDate;
|
||||
@ -372,7 +352,6 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
EchoesHexConquestActiveStickers?: string[];
|
||||
BrandedSuits?: IOid[];
|
||||
LockedWeaponGroup?: ILockedWeaponGroupClient;
|
||||
HubNpcCustomizations?: IHubNpcCustomization[];
|
||||
}
|
||||
|
||||
export interface IAffiliation {
|
||||
@ -469,36 +448,32 @@ export interface ICompletedJob {
|
||||
StageCompletions: number[];
|
||||
}
|
||||
|
||||
export interface ICrewMemberSkill {
|
||||
Assigned: number;
|
||||
}
|
||||
|
||||
export interface ICrewMemberSkillEfficiency {
|
||||
PILOTING: ICrewMemberSkill;
|
||||
GUNNERY: ICrewMemberSkill;
|
||||
ENGINEERING: ICrewMemberSkill;
|
||||
COMBAT: ICrewMemberSkill;
|
||||
SURVIVABILITY: ICrewMemberSkill;
|
||||
}
|
||||
|
||||
export interface ICrewMemberClient {
|
||||
export interface ICrewMember {
|
||||
ItemType: string;
|
||||
NemesisFingerprint: bigint;
|
||||
Seed: bigint;
|
||||
AssignedRole?: number;
|
||||
SkillEfficiency: ICrewMemberSkillEfficiency;
|
||||
NemesisFingerprint: number;
|
||||
Seed: number;
|
||||
HireDate: IMongoDate;
|
||||
AssignedRole: number;
|
||||
SkillEfficiency: ISkillEfficiency;
|
||||
WeaponConfigIdx: number;
|
||||
WeaponId: IOid;
|
||||
XP: number;
|
||||
PowersuitType: string;
|
||||
Configs: IItemConfig[];
|
||||
SecondInCommand: boolean; // on call
|
||||
SecondInCommand: boolean;
|
||||
ItemId: IOid;
|
||||
}
|
||||
|
||||
export interface ICrewMemberDatabase extends Omit<ICrewMemberClient, "WeaponId" | "ItemId"> {
|
||||
WeaponId: Types.ObjectId;
|
||||
_id: Types.ObjectId;
|
||||
export interface ISkillEfficiency {
|
||||
PILOTING: ICombat;
|
||||
GUNNERY: ICombat;
|
||||
ENGINEERING: ICombat;
|
||||
COMBAT: ICombat;
|
||||
SURVIVABILITY: ICombat;
|
||||
}
|
||||
|
||||
export interface ICombat {
|
||||
Assigned: number;
|
||||
}
|
||||
|
||||
export enum InventorySlot {
|
||||
@ -544,12 +519,12 @@ export interface ICrewShipMembersDatabase {
|
||||
|
||||
export interface ICrewShipMemberClient {
|
||||
ItemId?: IOid;
|
||||
NemesisFingerprint?: number | bigint;
|
||||
NemesisFingerprint?: number;
|
||||
}
|
||||
|
||||
export interface ICrewShipMemberDatabase {
|
||||
ItemId?: Types.ObjectId;
|
||||
NemesisFingerprint?: bigint;
|
||||
NemesisFingerprint?: number;
|
||||
}
|
||||
|
||||
export interface ICrewShipCustomization {
|
||||
@ -574,18 +549,17 @@ export type IMiscItem = ITypeCount;
|
||||
|
||||
// inventory.CrewShips[0].Weapon
|
||||
export interface ICrewShipWeapon {
|
||||
PILOT?: ICrewShipWeaponEmplacements;
|
||||
PORT_GUNS?: ICrewShipWeaponEmplacements;
|
||||
STARBOARD_GUNS?: ICrewShipWeaponEmplacements;
|
||||
ARTILLERY?: ICrewShipWeaponEmplacements;
|
||||
SCANNER?: ICrewShipWeaponEmplacements;
|
||||
PILOT: ICrewShipPilotWeapon;
|
||||
PORT_GUNS: ICrewShipPortGuns;
|
||||
}
|
||||
|
||||
export interface ICrewShipWeaponEmplacements {
|
||||
PRIMARY_A?: IEquipmentSelection;
|
||||
PRIMARY_B?: IEquipmentSelection;
|
||||
SECONDARY_A?: IEquipmentSelection;
|
||||
SECONDARY_B?: IEquipmentSelection;
|
||||
export interface ICrewShipPilotWeapon {
|
||||
PRIMARY_A: IEquipmentSelection;
|
||||
SECONDARY_A: IEquipmentSelection;
|
||||
}
|
||||
|
||||
export interface ICrewShipPortGuns {
|
||||
PRIMARY_A: IEquipmentSelection;
|
||||
}
|
||||
|
||||
export interface IDiscoveredMarker {
|
||||
@ -681,17 +655,6 @@ export interface IInvasionChainProgress {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface IInvasionProgressClient {
|
||||
_id: IOid;
|
||||
Delta: number;
|
||||
AttackerScore: number;
|
||||
DefenderScore: number;
|
||||
}
|
||||
|
||||
export interface IInvasionProgressDatabase extends Omit<IInvasionProgressClient, "_id"> {
|
||||
invasionId: Types.ObjectId;
|
||||
}
|
||||
|
||||
export interface IKubrowPetEggClient {
|
||||
ItemType: string;
|
||||
ExpirationDate: IMongoDate; // seems to be set to 7 days ahead @ 0 UTC
|
||||
@ -753,21 +716,12 @@ export enum Status {
|
||||
StatusStasis = "STATUS_STASIS"
|
||||
}
|
||||
|
||||
export interface ILastSortieRewardClient {
|
||||
export interface ILastSortieReward {
|
||||
SortieId: IOid;
|
||||
StoreItem: string;
|
||||
Manifest: string;
|
||||
}
|
||||
|
||||
export interface ILastSortieRewardDatabase extends Omit<ILastSortieRewardClient, "SortieId"> {
|
||||
SortieId: Types.ObjectId;
|
||||
}
|
||||
|
||||
export interface ISortieRewardAttenuation {
|
||||
Tag: string;
|
||||
Atten: number;
|
||||
}
|
||||
|
||||
export interface ILibraryDailyTaskInfo {
|
||||
EnemyTypes: string[];
|
||||
EnemyLocTag: string;
|
||||
@ -849,7 +803,7 @@ export interface IMission extends IMissionDatabase {
|
||||
}
|
||||
|
||||
export interface INemesisBaseClient {
|
||||
fp: bigint | number;
|
||||
fp: bigint;
|
||||
manifest: string;
|
||||
KillingSuit: string;
|
||||
killingDamageType: number;
|
||||
@ -867,8 +821,7 @@ export interface INemesisBaseClient {
|
||||
Weakened: boolean;
|
||||
}
|
||||
|
||||
export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "fp" | "d"> {
|
||||
fp: bigint;
|
||||
export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "d"> {
|
||||
d: Date;
|
||||
}
|
||||
|
||||
@ -882,8 +835,7 @@ export interface INemesisClient extends INemesisBaseClient {
|
||||
LastEnc: number;
|
||||
}
|
||||
|
||||
export interface INemesisDatabase extends Omit<INemesisClient, "fp" | "d"> {
|
||||
fp: bigint;
|
||||
export interface INemesisDatabase extends Omit<INemesisClient, "d"> {
|
||||
d: Date;
|
||||
}
|
||||
|
||||
@ -906,8 +858,8 @@ export interface IPendingRecipeDatabase {
|
||||
ItemType: string;
|
||||
CompletionDate: Date;
|
||||
ItemId: IOid;
|
||||
TargetItemId?: string; // unsure what this is for
|
||||
TargetFingerprint?: string;
|
||||
TargetItemId?: string; // likely related to liches
|
||||
TargetFingerprint?: string; // likely related to liches
|
||||
LongGuns?: IEquipmentDatabase[];
|
||||
Pistols?: IEquipmentDatabase[];
|
||||
Melee?: IEquipmentDatabase[];
|
||||
@ -919,10 +871,6 @@ export interface IPendingRecipeClient
|
||||
CompletionDate: IMongoDate;
|
||||
}
|
||||
|
||||
export interface IAccolades {
|
||||
Heirloom?: boolean;
|
||||
}
|
||||
|
||||
export interface IPendingTrade {
|
||||
State: number;
|
||||
SelfReady: boolean;
|
||||
@ -951,21 +899,6 @@ export interface IInnateDamageFingerprint {
|
||||
buffs: IFingerprintStat[];
|
||||
}
|
||||
|
||||
export interface ICrewShipComponentFingerprint extends IInnateDamageFingerprint {
|
||||
SubroutineIndex?: number;
|
||||
}
|
||||
|
||||
export interface INemesisWeaponTargetFingerprint {
|
||||
ItemType: string;
|
||||
UpgradeFingerprint: IInnateDamageFingerprint;
|
||||
Name: string;
|
||||
}
|
||||
|
||||
export interface INemesisPetTargetFingerprint {
|
||||
Parts: string[];
|
||||
Name: string;
|
||||
}
|
||||
|
||||
export enum GettingSlotOrderInfo {
|
||||
Empty = "",
|
||||
LotusUpgradesModsRandomizedPlayerMeleeWeaponRandomModRare0 = "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare:0",
|
||||
@ -1002,22 +935,16 @@ export interface IPersonalGoalProgress {
|
||||
ReceivedClanReward1?: boolean;
|
||||
}
|
||||
|
||||
export interface IPersonalTechProjectDatabase {
|
||||
export interface IPersonalTechProject {
|
||||
State: number;
|
||||
ReqCredits: number;
|
||||
ItemType: string;
|
||||
ProductCategory?: string;
|
||||
CategoryItemId?: Types.ObjectId;
|
||||
ReqItems: ITypeCount[];
|
||||
HasContributions?: boolean;
|
||||
CompletionDate?: Date;
|
||||
}
|
||||
|
||||
export interface IPersonalTechProjectClient
|
||||
extends Omit<IPersonalTechProjectDatabase, "CategoryItemId" | "CompletionDate"> {
|
||||
CategoryItemId?: IOid;
|
||||
CompletionDate?: IMongoDate;
|
||||
ItemId: IOid;
|
||||
ProductCategory?: string;
|
||||
CategoryItemId?: IOid;
|
||||
HasContributions?: boolean;
|
||||
}
|
||||
|
||||
export interface IPlayerSkills {
|
||||
@ -1107,7 +1034,6 @@ export interface ITaunt {
|
||||
|
||||
export interface IWeaponSkinDatabase {
|
||||
ItemType: string;
|
||||
Favorite?: boolean;
|
||||
IsNew?: boolean;
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
@ -1147,13 +1073,13 @@ export interface IEndlessXpProgress {
|
||||
}
|
||||
|
||||
export interface IDialogueHistoryClient {
|
||||
YearIteration?: number;
|
||||
YearIteration: number;
|
||||
Resets?: number; // added in 38.5.0
|
||||
Dialogues?: IDialogueClient[];
|
||||
}
|
||||
|
||||
export interface IDialogueHistoryDatabase {
|
||||
YearIteration?: number;
|
||||
YearIteration: number;
|
||||
Resets?: number;
|
||||
Dialogues?: IDialogueDatabase[];
|
||||
}
|
||||
@ -1210,18 +1136,17 @@ export interface IMarker {
|
||||
z: number;
|
||||
showInHud: boolean;
|
||||
}
|
||||
|
||||
export interface ISeasonProgress {
|
||||
SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||
SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||
LastCompletedDayIdx: number;
|
||||
LastCompletedChallengeDayIdx: number;
|
||||
ActivatedChallenges: string[];
|
||||
ActivatedChallenges: unknown[];
|
||||
}
|
||||
|
||||
export interface ICalendarProgress {
|
||||
Version: number;
|
||||
Iteration: number;
|
||||
YearProgress: { Upgrades: string[] };
|
||||
YearProgress: { Upgrades: unknown[] };
|
||||
SeasonProgress: ISeasonProgress;
|
||||
}
|
||||
|
||||
@ -1247,9 +1172,3 @@ export interface ILockedWeaponGroupDatabase {
|
||||
}
|
||||
|
||||
export type TPartialStartingGear = Pick<IInventoryClient, "LongGuns" | "Suits" | "Pistols" | "Melee">;
|
||||
|
||||
export interface IHubNpcCustomization {
|
||||
Colors?: IColor;
|
||||
Pattern: string;
|
||||
Tag: string;
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export interface IAccountAndLoginResponseCommons {
|
||||
DisplayName: string;
|
||||
CountryCode: string;
|
||||
@ -58,8 +56,3 @@ export interface IGroup {
|
||||
experiment: string;
|
||||
experimentGroup: string;
|
||||
}
|
||||
|
||||
export interface IIgnore {
|
||||
ignorer: Types.ObjectId;
|
||||
ignoree: Types.ObjectId;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user