Compare commits
20 Commits
8659500408
...
bs-0
| Author | SHA1 | Date | |
|---|---|---|---|
| a64ee889a2 | |||
| 6606e3b924 | |||
| 45609d999c | |||
| a3c1dcd1f1 | |||
| 3f3fe0b466 | |||
| 412ce82aae | |||
| 1b74fe4c54 | |||
| cca9d56d11 | |||
| a33d1199c7 | |||
| 4353f2915a | |||
| 4b43825b67 | |||
| 974636b5f5 | |||
| e07f3bb5d2 | |||
| cbd3750125 | |||
| 78373c134c | |||
| 2bdf8b80fb | |||
| 25d2b1c3ce | |||
| 34ba56862b | |||
| de77c06120 | |||
| b09ef0f21b |
151
LICENSE
151
LICENSE
@@ -1,5 +1,15 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
“Commons Clause” License Condition v1.0
|
||||
|
||||
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
|
||||
|
||||
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software.
|
||||
|
||||
For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
|
||||
|
||||
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@@ -7,17 +17,15 @@ GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -26,44 +34,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -72,7 +70,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -549,35 +547,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -635,40 +643,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"name": "wf-emulator",
|
||||
"name": "spaceninjaserver",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wf-emulator",
|
||||
"name": "spaceninjaserver",
|
||||
"version": "0.1.0",
|
||||
"license": "GNU",
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.0",
|
||||
"chokidar": "^4.0.3",
|
||||
@@ -552,6 +551,7 @@
|
||||
"integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.41.0",
|
||||
"@typescript-eslint/types": "8.41.0",
|
||||
@@ -1180,6 +1180,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -2057,6 +2058,7 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -2223,6 +2225,7 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -4362,6 +4365,7 @@
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -5203,6 +5207,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -5421,6 +5426,7 @@
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -5480,6 +5486,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"napi-postinstall": "^0.3.0"
|
||||
},
|
||||
@@ -5675,6 +5682,7 @@
|
||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
|
||||
"integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@colors/colors": "^1.6.0",
|
||||
"@dabh/diagnostics": "^2.0.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wf-emulator",
|
||||
"name": "spaceninjaserver",
|
||||
"version": "0.1.0",
|
||||
"description": "WF Emulator",
|
||||
"description": "SpaceNinjaServer",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps build/src/index.js",
|
||||
@@ -23,7 +23,6 @@
|
||||
"update-translations": "cd scripts && node update-translations.cjs",
|
||||
"fix": "npm run update-translations && npm run prettier"
|
||||
},
|
||||
"license": "GNU",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toOid } from "../../helpers/inventoryHelpers.ts";
|
||||
import { toOid2 } from "../../helpers/inventoryHelpers.ts";
|
||||
import {
|
||||
createVeiledRivenFingerprint,
|
||||
createUnveiledRivenFingerprint,
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
} from "../../helpers/rivenHelper.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { addMods, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { getRandomElement } from "../../services/rngService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportUpgrades } from "warframe-public-export-plus";
|
||||
|
||||
export const activateRandomModController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const inventory = await getInventory(accountId, "RawUpgrades Upgrades instantFinishRivenChallenge");
|
||||
const request = getJSONfromString<IActiveRandomModRequest>(String(req.body));
|
||||
addMods(inventory, [
|
||||
@@ -36,7 +37,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
|
||||
NewMod: {
|
||||
UpgradeFingerprint: fingerprint,
|
||||
ItemType: rivenType,
|
||||
ItemId: toOid(inventory.Upgrades[upgradeIndex]._id)
|
||||
ItemId: toOid2(inventory.Upgrades[upgradeIndex]._id, account.BuildLabel)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fromOid, toOid } from "../../helpers/inventoryHelpers.ts";
|
||||
import { fromOid, toOid2 } from "../../helpers/inventoryHelpers.ts";
|
||||
import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "../../helpers/rivenHelper.ts";
|
||||
import { addMiscItems, addMods, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { getRandomElement, getRandomWeightedReward, getRandomWeightedRewardUc } from "../../services/rngService.ts";
|
||||
import type { IUpgradeFromClient } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
@@ -9,12 +9,15 @@ import type { TRarity } from "warframe-public-export-plus";
|
||||
import { ExportBoosterPacks, ExportUpgrades } from "warframe-public-export-plus";
|
||||
|
||||
export const artifactTransmutationController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const inventory = await getInventory(accountId);
|
||||
const payload = JSON.parse(String(req.body)) as IArtifactTransmutationRequest;
|
||||
|
||||
inventory.RegularCredits -= payload.Cost;
|
||||
inventory.FusionPoints -= payload.FusionPointCost;
|
||||
if (payload.FusionPointCost) {
|
||||
inventory.FusionPoints -= payload.FusionPointCost;
|
||||
}
|
||||
|
||||
if (payload.RivenTransmute) {
|
||||
addMiscItems(inventory, [
|
||||
@@ -41,7 +44,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
res.json({
|
||||
NewMods: [
|
||||
{
|
||||
ItemId: toOid(inventory.Upgrades[upgradeIndex]._id),
|
||||
ItemId: toOid2(inventory.Upgrades[upgradeIndex]._id, account.BuildLabel),
|
||||
ItemType: rivenType,
|
||||
UpgradeFingerprint: fingerprint
|
||||
}
|
||||
@@ -56,9 +59,10 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
};
|
||||
let forcedPolarity: string | undefined;
|
||||
payload.Consumed.forEach(upgrade => {
|
||||
upgrade.ItemCount ??= 1;
|
||||
const meta = ExportUpgrades[upgrade.ItemType];
|
||||
counts[meta.rarity] += upgrade.ItemCount;
|
||||
if (fromOid(upgrade.ItemId) != "000000000000000000000000") {
|
||||
if (fromOid(upgrade.ItemId) != "" && fromOid(upgrade.ItemId) != "000000000000000000000000") {
|
||||
inventory.Upgrades.pull({ _id: fromOid(upgrade.ItemId) });
|
||||
} else {
|
||||
addMods(inventory, [
|
||||
@@ -133,7 +137,7 @@ interface IArtifactTransmutationRequest {
|
||||
LevelDiff: number;
|
||||
Consumed: IUpgradeFromClient[];
|
||||
Cost: number;
|
||||
FusionPointCost: number;
|
||||
FusionPointCost?: number;
|
||||
RivenTransmute?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +1,126 @@
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import type { IInventoryClient, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type {
|
||||
IInventoryClient,
|
||||
IUpgradeClient,
|
||||
IUpgradeFromClient
|
||||
} from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { addMods, getInventory } from "../../services/inventoryService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import { fromOid, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
|
||||
export const artifactsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const artifactsData = getJSONfromString<IArtifactsRequest>(String(req.body));
|
||||
|
||||
const { Upgrade, LevelDiff, Cost, FusionPointCost } = artifactsData;
|
||||
const { Upgrade, LevelDiff, Cost, FusionPointCost, Consumed, Fingerprint } = artifactsData;
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const { Upgrades } = inventory;
|
||||
const { ItemType, UpgradeFingerprint, ItemId } = Upgrade;
|
||||
|
||||
const safeUpgradeFingerprint = UpgradeFingerprint || '{"lvl":0}';
|
||||
const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number };
|
||||
parsedUpgradeFingerprint.lvl += LevelDiff;
|
||||
const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint);
|
||||
if (!account.BuildLabel || version_compare(account.BuildLabel, "2016.08.19.17.12") >= 0) {
|
||||
const safeUpgradeFingerprint = UpgradeFingerprint || '{"lvl":0}';
|
||||
const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number };
|
||||
parsedUpgradeFingerprint.lvl += LevelDiff;
|
||||
const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint);
|
||||
|
||||
let itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(ItemId.$oid));
|
||||
let itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(fromOid(ItemId)));
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint;
|
||||
if (itemIndex !== -1) {
|
||||
Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint;
|
||||
} else {
|
||||
itemIndex =
|
||||
Upgrades.push({
|
||||
UpgradeFingerprint: stringifiedUpgradeFingerprint,
|
||||
ItemType
|
||||
}) - 1;
|
||||
|
||||
addMods(inventory, [{ ItemType, ItemCount: -1 }]);
|
||||
}
|
||||
|
||||
if (!inventory.infiniteCredits) {
|
||||
inventory.RegularCredits -= Cost;
|
||||
}
|
||||
if (!inventory.infiniteEndo) {
|
||||
inventory.FusionPoints -= FusionPointCost;
|
||||
}
|
||||
|
||||
if (artifactsData.LegendaryFusion) {
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser",
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
const changedInventory = (await inventory.save()).toJSON<IInventoryClient>();
|
||||
const itemId =
|
||||
changedInventory.Upgrades[itemIndex].ItemId.$oid ?? changedInventory.Upgrades[itemIndex].ItemId.$id;
|
||||
|
||||
if (!itemId) {
|
||||
throw new Error("Item Id not found in upgradeMod");
|
||||
}
|
||||
|
||||
res.send(itemId);
|
||||
} else {
|
||||
itemIndex =
|
||||
Upgrades.push({
|
||||
UpgradeFingerprint: stringifiedUpgradeFingerprint,
|
||||
ItemType
|
||||
}) - 1;
|
||||
// Pre-U18.18.0 uses the old pre-Endo fusion system which uses a different UpgradeFingerprint format
|
||||
// that has to be converted and consumes upgrades in the fusion proccess
|
||||
const safeUpgradeFingerprint = `{"lvl":${Fingerprint?.substring(4, Fingerprint.lastIndexOf("|"))}}`;
|
||||
const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number };
|
||||
if (LevelDiff) {
|
||||
parsedUpgradeFingerprint.lvl += LevelDiff;
|
||||
}
|
||||
const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint);
|
||||
|
||||
addMods(inventory, [{ ItemType, ItemCount: -1 }]);
|
||||
}
|
||||
let itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(ItemId.$id));
|
||||
|
||||
if (!inventory.infiniteCredits) {
|
||||
inventory.RegularCredits -= Cost;
|
||||
}
|
||||
if (!inventory.infiniteEndo) {
|
||||
inventory.FusionPoints -= FusionPointCost;
|
||||
}
|
||||
if (itemIndex !== -1) {
|
||||
Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint;
|
||||
} else {
|
||||
itemIndex =
|
||||
Upgrades.push({
|
||||
UpgradeFingerprint: stringifiedUpgradeFingerprint,
|
||||
ItemType
|
||||
}) - 1;
|
||||
|
||||
if (artifactsData.LegendaryFusion) {
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser",
|
||||
ItemCount: -1
|
||||
addMods(inventory, [{ ItemType, ItemCount: -1 }]);
|
||||
}
|
||||
|
||||
const itemId = Upgrades[itemIndex]._id.toString();
|
||||
if (!itemId) {
|
||||
throw new Error("Item Id not found in upgradeMod");
|
||||
}
|
||||
|
||||
if (!inventory.infiniteCredits) {
|
||||
inventory.RegularCredits -= Cost;
|
||||
}
|
||||
if (Consumed && Consumed.length > 0) {
|
||||
for (const upgrade of Consumed) {
|
||||
// The client does not send the expected information about the mods, so we have to check if it's an Upgrade or RawUpgrade manually.
|
||||
if (Upgrades.id(fromOid(upgrade.ItemId))) {
|
||||
Upgrades.pull({ _id: upgrade.ItemId.$id });
|
||||
} else {
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: upgrade.ItemType,
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(itemId));
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
|
||||
res.send(itemId);
|
||||
}
|
||||
|
||||
const changedInventory = await inventory.save();
|
||||
const itemId = changedInventory.toJSON<IInventoryClient>().Upgrades[itemIndex].ItemId.$oid;
|
||||
|
||||
if (!itemId) {
|
||||
throw new Error("Item Id not found in upgradeMod");
|
||||
}
|
||||
|
||||
res.send(itemId);
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@@ -67,4 +130,6 @@ interface IArtifactsRequest {
|
||||
Cost: number;
|
||||
FusionPointCost: number;
|
||||
LegendaryFusion?: boolean;
|
||||
Fingerprint?: string;
|
||||
Consumed?: IUpgradeFromClient[];
|
||||
}
|
||||
|
||||
@@ -262,9 +262,9 @@ const claimCompletedRecipe = async (
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
umbraModA.ItemId.$oid,
|
||||
umbraModB.ItemId.$oid,
|
||||
umbraModC.ItemId.$oid
|
||||
fromOid(umbraModA.ItemId),
|
||||
fromOid(umbraModB.ItemId),
|
||||
fromOid(umbraModC.ItemId)
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -284,7 +284,18 @@ const claimCompletedRecipe = async (
|
||||
"/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
|
||||
{
|
||||
Configs: [
|
||||
{ Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] }
|
||||
{
|
||||
Upgrades: [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
fromOid(sacrificeModA.ItemId),
|
||||
fromOid(sacrificeModB.ItemId)
|
||||
]
|
||||
}
|
||||
],
|
||||
XP: 450_000,
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY
|
||||
|
||||
@@ -4,8 +4,9 @@ import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/in
|
||||
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
|
||||
import { config } from "../../services/configService.ts";
|
||||
import allDialogue from "../../../static/fixed_responses/allDialogue.json" with { type: "json" };
|
||||
import allPopups from "../../../static/fixed_responses/allPopups.json" with { type: "json" };
|
||||
import type { ILoadoutDatabase } from "../../types/saveLoadoutTypes.ts";
|
||||
import type { IInventoryClient, IShipInventory } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IInventoryClient, IShipInventory, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { equipmentKeys } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
@@ -29,7 +30,7 @@ import { getNemesisManifest } from "../../helpers/nemesisHelpers.ts";
|
||||
import { getPersonalRooms } from "../../services/personalRoomsService.ts";
|
||||
import type { IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
|
||||
import { Ship } from "../../models/shipModel.ts";
|
||||
import { toLegacyOid, toOid, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import { toLegacyOid, toOid, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import { Inbox } from "../../models/inboxModel.ts";
|
||||
import { unixTimesInMs } from "../../constants/timeConstants.ts";
|
||||
import { DailyDeal } from "../../models/worldStateModel.ts";
|
||||
@@ -326,6 +327,12 @@ export const getInventoryResponse = async (
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.skipAllPopups) {
|
||||
for (const str of allPopups) {
|
||||
addString(inventoryResponse.NodeIntrosCompleted, str);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.worldState?.baroTennoConRelay) {
|
||||
[
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2022EarlyAccess",
|
||||
@@ -447,28 +454,140 @@ export const getInventoryResponse = async (
|
||||
inventoryResponse.Nemesis = undefined;
|
||||
}
|
||||
|
||||
if (version_compare(buildLabel, "2018.02.22.14.34") < 0) {
|
||||
const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString());
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||
inventoryResponse.Ship = personalRooms.Ship;
|
||||
if (version_compare(buildLabel, "2019.03.07.20.21") < 0) {
|
||||
// Builds before U24.4.0 handle equipment features differently
|
||||
for (const category of equipmentKeys) {
|
||||
for (const item of inventoryResponse[category]) {
|
||||
if (item.Features && item.Features & EquipmentFeatures.DOUBLE_CAPACITY) {
|
||||
item.UnlockLevel = 1;
|
||||
}
|
||||
if (item.Features && item.Features & EquipmentFeatures.UTILITY_SLOT) {
|
||||
item.UtilityUnlocked = 1;
|
||||
}
|
||||
if (item.Features && item.Features & EquipmentFeatures.GILDED) {
|
||||
item.Gild = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version_compare(buildLabel, "2016.12.21.19.13") <= 0) {
|
||||
// U19.5 and below use $id instead of $oid
|
||||
for (const category of equipmentKeys) {
|
||||
for (const item of inventoryResponse[category]) {
|
||||
toLegacyOid(item.ItemId);
|
||||
if (version_compare(buildLabel, "2018.02.22.14.34") < 0) {
|
||||
const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString());
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||
inventoryResponse.Ship = personalRooms.Ship;
|
||||
|
||||
if (version_compare(buildLabel, "2016.12.21.19.13") <= 0) {
|
||||
// U19.5 and below use $id instead of $oid
|
||||
for (const category of equipmentKeys) {
|
||||
for (const item of inventoryResponse[category]) {
|
||||
toLegacyOid(item.ItemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const upgrade of inventoryResponse.Upgrades) {
|
||||
toLegacyOid(upgrade.ItemId);
|
||||
}
|
||||
if (inventoryResponse.BrandedSuits) {
|
||||
for (const id of inventoryResponse.BrandedSuits) {
|
||||
toLegacyOid(id);
|
||||
|
||||
if (version_compare(buildLabel, "2014.02.05.00.00") < 0) {
|
||||
// Pre-U12 builds store mods in an array called Cards, and have no concept of RawUpgrades
|
||||
inventoryResponse.Cards = [];
|
||||
for (const rawUpgrade of inventoryResponse.RawUpgrades) {
|
||||
const id = inventory.RawUpgrades.find(x => x.ItemType == rawUpgrade.ItemType)?._id;
|
||||
if (id) {
|
||||
for (let i = 0; i < rawUpgrade.ItemCount; i++) {
|
||||
const card = {
|
||||
ItemType: rawUpgrade.ItemType,
|
||||
ItemId: toOid2(id, buildLabel),
|
||||
Rank: 0,
|
||||
AmountRemaining: rawUpgrade.ItemCount
|
||||
} as IUpgradeClient;
|
||||
// Client doesn't see the mods unless they are in both Cards and Upgrades
|
||||
inventoryResponse.Cards.push(card);
|
||||
inventoryResponse.Upgrades.push(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inventoryResponse.RawUpgrades = [];
|
||||
|
||||
for (const category of equipmentKeys) {
|
||||
for (const item of inventoryResponse[category]) {
|
||||
for (const config of item.Configs) {
|
||||
if (config.Upgrades) {
|
||||
// Convert installed upgrades for U10-U11
|
||||
const convertedUpgrades: { $id: string }[] = [];
|
||||
config.Upgrades.forEach(upgrade => {
|
||||
const upgradeId = upgrade as string;
|
||||
convertedUpgrades.push({ $id: upgradeId });
|
||||
});
|
||||
config.Upgrades = convertedUpgrades;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const upgrade of inventoryResponse.Upgrades) {
|
||||
toLegacyOid(upgrade.ItemId);
|
||||
if (version_compare(buildLabel, "2016.08.19.17.12") < 0) {
|
||||
// Pre-U18.18 builds use a different UpgradeFingerprint format
|
||||
let rank: number = 0;
|
||||
if (upgrade.UpgradeFingerprint) {
|
||||
rank = Number.parseFloat(
|
||||
upgrade.UpgradeFingerprint.substring(
|
||||
upgrade.UpgradeFingerprint.indexOf(":") + 1,
|
||||
upgrade.UpgradeFingerprint.lastIndexOf("}")
|
||||
)
|
||||
);
|
||||
}
|
||||
upgrade.UpgradeFingerprint = `lvl=${rank}|`;
|
||||
if (version_compare(buildLabel, "2014.04.10.17.47") < 0) {
|
||||
// Pre-U10 builds
|
||||
if (
|
||||
!upgrade.AmountRemaining ||
|
||||
(upgrade.AmountRemaining && upgrade.AmountRemaining <= 0)
|
||||
) {
|
||||
upgrade.AmountRemaining = 1;
|
||||
}
|
||||
upgrade.Rank = rank;
|
||||
if (inventoryResponse.Cards) {
|
||||
inventoryResponse.Cards.push(upgrade);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version_compare(buildLabel, "2014.02.05.00.00") < 0) {
|
||||
// Convert installed mods for pre-U12 builds
|
||||
for (const category of equipmentKeys) {
|
||||
for (const item of inventoryResponse[category]) {
|
||||
for (const config of item.Configs) {
|
||||
if (config.Upgrades) {
|
||||
for (let i = 0; i < config.Upgrades.length; i++) {
|
||||
const id = config.Upgrades[i] as { $id: string | undefined };
|
||||
const invUpgrade = inventoryResponse.Upgrades.find(
|
||||
x => x.ItemId.$id == id.$id
|
||||
);
|
||||
if (invUpgrade) {
|
||||
if (id.$id?.startsWith("/Lotus")) {
|
||||
// Pre-U12 builds have no concept of RawUpgrades, have to convert the db entry to the closest id of an unranked copy
|
||||
id.$id = inventoryResponse.Upgrades.find(
|
||||
x => x.ItemType == id.$id
|
||||
)?.ItemId.$id;
|
||||
}
|
||||
// Pre-U10
|
||||
invUpgrade.ParentId = item.ItemId;
|
||||
invUpgrade.Slot = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inventoryResponse.BrandedSuits) {
|
||||
for (const id of inventoryResponse.BrandedSuits) {
|
||||
toLegacyOid(id);
|
||||
}
|
||||
}
|
||||
if (inventoryResponse.GuildId) {
|
||||
toLegacyOid(inventoryResponse.GuildId);
|
||||
}
|
||||
}
|
||||
if (inventoryResponse.GuildId) {
|
||||
toLegacyOid(inventoryResponse.GuildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,16 @@ const createLoginResponse = (
|
||||
if (version_compare(buildLabel, "2022.09.06.19.24") >= 0) {
|
||||
resp.CrossPlatformAllowed = account.CrossPlatformAllowed;
|
||||
resp.HUB = `${myUrlBase}/api/`;
|
||||
resp.MatchmakingBuildId = buildConfig.matchmakingBuildId;
|
||||
|
||||
// The MatchmakingBuildId is a 64-bit integer represented as a decimal string. On live, the value is seemingly random per build, but really any value that is different across builds should work.
|
||||
const [year, month, day, hour, minute] = buildLabel.split(".").map(x => parseInt(x));
|
||||
resp.MatchmakingBuildId = (
|
||||
year * 1_00_00_00_00 +
|
||||
month * 1_00_00_00 +
|
||||
day * 1_00_00 +
|
||||
hour * 1_00 +
|
||||
minute
|
||||
).toString();
|
||||
}
|
||||
if (version_compare(buildLabel, "2023.04.25.23.40") >= 0) {
|
||||
if (version_compare(buildLabel, "2025.08.26.09.49") >= 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fromDbOid, toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import { fromDbOid, fromOid, toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import type { IKnifeResponse } from "../../helpers/nemesisHelpers.ts";
|
||||
import {
|
||||
antivirusMods,
|
||||
@@ -21,7 +21,7 @@ import { Loadout } from "../../models/inventoryModels/loadoutModel.ts";
|
||||
import { addMods, freeUpSlot, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { SRng } from "../../services/rngService.ts";
|
||||
import type { IMongoDate, IOid } from "../../types/commonTypes.ts";
|
||||
import type { IMongoDate, IOid, IOidWithLegacySupport } from "../../types/commonTypes.ts";
|
||||
import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
|
||||
import type {
|
||||
IInnateDamageFingerprint,
|
||||
@@ -420,7 +420,7 @@ interface IKnife {
|
||||
const consumeModCharge = (
|
||||
response: IKnifeResponse,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
upgrade: { ItemId: IOid; ItemType: string },
|
||||
upgrade: { ItemId: IOidWithLegacySupport; ItemType: string },
|
||||
dataknifeUpgrades: string[]
|
||||
): void => {
|
||||
response.UpgradeIds ??= [];
|
||||
@@ -429,13 +429,13 @@ const consumeModCharge = (
|
||||
response.UpgradeNew ??= [];
|
||||
response.HasKnife = true;
|
||||
|
||||
if (upgrade.ItemId.$oid != "000000000000000000000000") {
|
||||
const dbUpgrade = inventory.Upgrades.id(upgrade.ItemId.$oid)!;
|
||||
if (fromOid(upgrade.ItemId) != "000000000000000000000000") {
|
||||
const dbUpgrade = inventory.Upgrades.id(fromOid(upgrade.ItemId))!;
|
||||
const fingerprint = JSON.parse(dbUpgrade.UpgradeFingerprint!) as { lvl: number };
|
||||
fingerprint.lvl += 1;
|
||||
dbUpgrade.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||
|
||||
response.UpgradeIds.push(upgrade.ItemId.$oid);
|
||||
response.UpgradeIds.push(fromOid(upgrade.ItemId));
|
||||
response.UpgradeTypes.push(upgrade.ItemType);
|
||||
response.UpgradeFingerprints.push(fingerprint);
|
||||
response.UpgradeNew.push(false);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import type { RivenFingerprint } from "../../helpers/rivenHelper.ts";
|
||||
import { createUnveiledRivenFingerprint, randomiseRivenStats } from "../../helpers/rivenHelper.ts";
|
||||
import { ExportUpgrades } from "warframe-public-export-plus";
|
||||
import type { IOid } from "../../types/commonTypes.ts";
|
||||
import type { IOidWithLegacySupport } from "../../types/commonTypes.ts";
|
||||
import { toObjectId, toOid2 } from "../../helpers/inventoryHelpers.ts";
|
||||
|
||||
export const rerollRandomModController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const request = getJSONfromString<RerollRandomModRequest>(String(req.body));
|
||||
if ("ItemIds" in request) {
|
||||
const inventory = await getInventory(accountId, "Upgrades MiscItems");
|
||||
@@ -40,7 +42,7 @@ export const rerollRandomModController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
changes.push({
|
||||
ItemId: { $oid: request.ItemIds[0] },
|
||||
ItemId: toOid2(toObjectId(request.ItemIds[0]), account.BuildLabel),
|
||||
UpgradeFingerprint: upgrade.UpgradeFingerprint,
|
||||
PendingRerollFingerprint: upgrade.PendingRerollFingerprint
|
||||
});
|
||||
@@ -76,7 +78,7 @@ interface AwDangitRequest {
|
||||
}
|
||||
|
||||
interface IChange {
|
||||
ItemId: IOid;
|
||||
ItemId: IOidWithLegacySupport;
|
||||
UpgradeFingerprint?: string;
|
||||
PendingRerollFingerprint?: string;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import type { ISaveLoadoutRequest } from "../../types/saveLoadoutTypes.ts";
|
||||
import { handleInventoryItemConfigChange } from "../../services/saveLoadoutService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
|
||||
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
|
||||
const body: ISaveLoadoutRequest = getJSONfromString<ISaveLoadoutRequest>(String(req.body));
|
||||
// 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);
|
||||
const newLoadoutId = await handleInventoryItemConfigChange(
|
||||
equipmentChanges,
|
||||
account._id.toString(),
|
||||
account.BuildLabel
|
||||
);
|
||||
|
||||
//send back new loadout id, if new loadout was added
|
||||
if (newLoadoutId) {
|
||||
|
||||
@@ -1,155 +1,288 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import type { IUpgradesRequest } from "../../types/requestTypes.ts";
|
||||
import { fromOid, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import type { IUpgradesRequest, IUpgradesRequestLegacy } from "../../types/requestTypes.ts";
|
||||
import type { ArtifactPolarity, IAbilityOverride } from "../../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import type { IInventoryClient, IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "../../services/inventoryService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { addMiscItems, addMods, addRecipes, getInventory, updateCurrency } from "../../services/inventoryService.ts";
|
||||
import { getRecipeByResult } from "../../services/itemDataService.ts";
|
||||
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
|
||||
import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "../../services/infestedFoundryService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import type { IEquipmentDatabase } from "../../types/equipmentTypes.ts";
|
||||
import { EquipmentFeatures } from "../../types/equipmentTypes.ts";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const upgradesController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
for (const operation of payload.Operations) {
|
||||
if (
|
||||
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" ||
|
||||
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?`);
|
||||
}
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: operation.UpgradeRequirement,
|
||||
ItemCount: -1
|
||||
} satisfies IMiscItem
|
||||
]);
|
||||
}
|
||||
|
||||
if (operation.OperationType == "UOT_ABILITY_OVERRIDE") {
|
||||
console.assert(payload.ItemCategory == "Suits");
|
||||
const suit = inventory.Suits.id(payload.ItemId.$oid)!;
|
||||
|
||||
let newAbilityOverride: IAbilityOverride | undefined;
|
||||
let totalPercentagePointsConsumed = 0;
|
||||
if (operation.UpgradeRequirement != "") {
|
||||
newAbilityOverride = {
|
||||
Ability: operation.UpgradeRequirement,
|
||||
Index: operation.PolarizeSlot
|
||||
};
|
||||
|
||||
const recipe = getRecipeByResult(operation.UpgradeRequirement)!;
|
||||
for (const ingredient of recipe.ingredients) {
|
||||
totalPercentagePointsConsumed += ingredient.ItemCount / 10;
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
|
||||
ingredient.ItemCount;
|
||||
if (account.BuildLabel && version_compare(account.BuildLabel, "2019.03.07.20.21") < 0) {
|
||||
// Builds before U24.4.0 have a different request format
|
||||
const payload = JSON.parse(String(req.body)) as IUpgradesRequestLegacy;
|
||||
const itemId = fromOid(payload.Weapon.ItemId);
|
||||
if (itemId) {
|
||||
if (payload.IsSwappingOperation === true) {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
for (let i = 0; i != payload.PolarityRemap.length; ++i) {
|
||||
// Can't really be selective here like the newer format, it pushes everything in a way that the comparison fails against...
|
||||
setSlotPolarity(item, i, payload.PolarityRemap[i].Value);
|
||||
}
|
||||
} else {
|
||||
if (payload.PolarizeReq) {
|
||||
switch (payload.PolarizeReq) {
|
||||
case "/Lotus/Types/Items/MiscItems/Forma":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaUmbra": {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
item.XP = 0;
|
||||
setSlotPolarity(item, payload.PolarizeSlot, payload.PolarizeValue);
|
||||
item.Polarized ??= 0;
|
||||
item.Polarized += 1;
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported polarize item: " + payload.PolarizeReq);
|
||||
}
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: payload.PolarizeReq,
|
||||
ItemCount: -1
|
||||
} satisfies IMiscItem
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of operation.PolarityRemap) {
|
||||
suit.Configs[entry.Slot] ??= {};
|
||||
suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride;
|
||||
}
|
||||
|
||||
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, totalPercentagePointsConsumed * 8);
|
||||
addRecipes(inventory, recipeChanges);
|
||||
|
||||
inventoryChanges.Recipes = recipeChanges;
|
||||
inventoryChanges.InfestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry;
|
||||
applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!);
|
||||
} else
|
||||
switch (operation.UpgradeRequirement) {
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinReactor":
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.DOUBLE_CAPACITY;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/UtilityUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponUtilityUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.UTILITY_SLOT;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/HeavyWeaponCatalyst": {
|
||||
console.assert(payload.ItemCategory == "SpaceGuns");
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.GRAVIMAG_INSTALLED;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponArchGunArcaneUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
if (operation.OperationType == "UOT_ARCANE_UNLOCK_1") {
|
||||
item.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT;
|
||||
} else {
|
||||
item.Features |= EquipmentFeatures.ARCANE_SLOT;
|
||||
if (payload.UtilityReq) {
|
||||
switch (payload.UtilityReq) {
|
||||
case "/Lotus/Types/Items/MiscItems/UtilityUnlocker": {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.UTILITY_SLOT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported utility item: " + payload.UtilityReq);
|
||||
}
|
||||
break;
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: payload.UtilityReq,
|
||||
ItemCount: -1
|
||||
} satisfies IMiscItem
|
||||
]);
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/ValenceAdapter": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.VALENCE_SWAP;
|
||||
break;
|
||||
if (payload.UpgradeReq) {
|
||||
switch (payload.UpgradeReq) {
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinReactor":
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.DOUBLE_CAPACITY;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported upgrade: " + payload.UpgradeReq);
|
||||
}
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: payload.UpgradeReq,
|
||||
ItemCount: -1
|
||||
} satisfies IMiscItem
|
||||
]);
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/Forma":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaUmbra":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaAura":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaStance": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.XP = 0;
|
||||
setSlotPolarity(item, operation.PolarizeSlot, operation.PolarizeValue);
|
||||
item.Polarized ??= 0;
|
||||
item.Polarized += 1;
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true }); // webui may need to to re-add "max rank" button
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle attaching/detaching mods in U7-U8
|
||||
if (payload.UpgradesToAttach && payload.UpgradesToAttach.length > 0) {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
if (!item.Configs[0]) {
|
||||
item.Configs.push({ Upgrades: ["", "", "", "", "", "", "", "", "", "", ""] });
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/ModSlotUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.ModSlotPurchases ??= 0;
|
||||
item.ModSlotPurchases += 1;
|
||||
break;
|
||||
if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) {
|
||||
item.Configs[0].Upgrades.length = 11;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.CustomizationSlotPurchases ??= 0;
|
||||
item.CustomizationSlotPurchases += 1;
|
||||
break;
|
||||
}
|
||||
case "": {
|
||||
console.assert(operation.OperationType == "UOT_SWAP_POLARITY");
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
for (let i = 0; i != operation.PolarityRemap.length; ++i) {
|
||||
if (operation.PolarityRemap[i].Slot != i) {
|
||||
setSlotPolarity(item, i, operation.PolarityRemap[i].Value);
|
||||
payload.UpgradesToAttach.forEach(upgrade => {
|
||||
if (item.Configs[0].Upgrades && upgrade.ItemId.$id && upgrade.Slot) {
|
||||
const arr = item.Configs[0].Upgrades;
|
||||
if (arr.indexOf(upgrade.ItemId.$id) != -1) {
|
||||
// Handle swapping mod to a different slot
|
||||
arr[arr.indexOf(upgrade.ItemId.$id)] = "";
|
||||
}
|
||||
// We need to convert RawUpgrade into Upgrade once it's attached
|
||||
const rawUpgrade = inventory.RawUpgrades.id(upgrade.ItemId.$id);
|
||||
if (rawUpgrade) {
|
||||
const newId = new Types.ObjectId().toString();
|
||||
arr[upgrade.Slot - 1] = newId;
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: upgrade.ItemType,
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
inventory.Upgrades.push({
|
||||
UpgradeFingerprint: `{"lvl":0}`,
|
||||
ItemType: upgrade.ItemType,
|
||||
_id: newId
|
||||
});
|
||||
} else {
|
||||
arr[upgrade.Slot - 1] = upgrade.ItemId.$id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported upgrade: " + operation.UpgradeRequirement);
|
||||
});
|
||||
}
|
||||
if (payload.UpgradesToDetach && payload.UpgradesToDetach.length > 0) {
|
||||
const item = inventory[payload.Category].id(itemId)!;
|
||||
if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) {
|
||||
item.Configs[0].Upgrades.length = 11;
|
||||
}
|
||||
payload.UpgradesToDetach.forEach(upgrade => {
|
||||
if (item.Configs[0].Upgrades && upgrade.ItemId.$id) {
|
||||
const arr = item.Configs[0].Upgrades;
|
||||
arr[arr.indexOf(upgrade.ItemId.$id)] = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
|
||||
for (const operation of payload.Operations) {
|
||||
if (
|
||||
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" ||
|
||||
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?`);
|
||||
}
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: operation.UpgradeRequirement,
|
||||
ItemCount: -1
|
||||
} satisfies IMiscItem
|
||||
]);
|
||||
}
|
||||
|
||||
if (operation.OperationType == "UOT_ABILITY_OVERRIDE") {
|
||||
console.assert(payload.ItemCategory == "Suits");
|
||||
const suit = inventory.Suits.id(payload.ItemId.$oid)!;
|
||||
|
||||
let newAbilityOverride: IAbilityOverride | undefined;
|
||||
let totalPercentagePointsConsumed = 0;
|
||||
if (operation.UpgradeRequirement != "") {
|
||||
newAbilityOverride = {
|
||||
Ability: operation.UpgradeRequirement,
|
||||
Index: operation.PolarizeSlot
|
||||
};
|
||||
|
||||
const recipe = getRecipeByResult(operation.UpgradeRequirement)!;
|
||||
for (const ingredient of recipe.ingredients) {
|
||||
totalPercentagePointsConsumed += ingredient.ItemCount / 10;
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
|
||||
ingredient.ItemCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of operation.PolarityRemap) {
|
||||
suit.Configs[entry.Slot] ??= {};
|
||||
suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride;
|
||||
}
|
||||
|
||||
const recipeChanges = addInfestedFoundryXP(
|
||||
inventory.InfestedFoundry!,
|
||||
totalPercentagePointsConsumed * 8
|
||||
);
|
||||
addRecipes(inventory, recipeChanges);
|
||||
|
||||
inventoryChanges.Recipes = recipeChanges;
|
||||
inventoryChanges.InfestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry;
|
||||
applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!);
|
||||
} else
|
||||
switch (operation.UpgradeRequirement) {
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinReactor":
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.DOUBLE_CAPACITY;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/UtilityUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponUtilityUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.UTILITY_SLOT;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/HeavyWeaponCatalyst": {
|
||||
console.assert(payload.ItemCategory == "SpaceGuns");
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.GRAVIMAG_INSTALLED;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker":
|
||||
case "/Lotus/Types/Items/MiscItems/WeaponArchGunArcaneUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
if (operation.OperationType == "UOT_ARCANE_UNLOCK_1") {
|
||||
item.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT;
|
||||
} else {
|
||||
item.Features |= EquipmentFeatures.ARCANE_SLOT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/ValenceAdapter": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.Features ??= 0;
|
||||
item.Features |= EquipmentFeatures.VALENCE_SWAP;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/Forma":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaUmbra":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaAura":
|
||||
case "/Lotus/Types/Items/MiscItems/FormaStance": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.XP = 0;
|
||||
setSlotPolarity(item, operation.PolarizeSlot, operation.PolarizeValue);
|
||||
item.Polarized ??= 0;
|
||||
item.Polarized += 1;
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true }); // webui may need to to re-add "max rank" button
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/ModSlotUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.ModSlotPurchases ??= 0;
|
||||
item.ModSlotPurchases += 1;
|
||||
break;
|
||||
}
|
||||
case "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker": {
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
item.CustomizationSlotPurchases ??= 0;
|
||||
item.CustomizationSlotPurchases += 1;
|
||||
break;
|
||||
}
|
||||
case "": {
|
||||
console.assert(operation.OperationType == "UOT_SWAP_POLARITY");
|
||||
const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
|
||||
for (let i = 0; i != operation.PolarityRemap.length; ++i) {
|
||||
if (operation.PolarityRemap[i].Slot != i) {
|
||||
setSlotPolarity(item, i, operation.PolarityRemap[i].Value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported upgrade: " + operation.UpgradeRequirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({ InventoryChanges: inventoryChanges });
|
||||
|
||||
@@ -13,33 +13,46 @@ export const importController: RequestHandler = async (req, res) => {
|
||||
const request = req.body as IImportRequest;
|
||||
|
||||
let anyKnownKey = false;
|
||||
try {
|
||||
const inventory = await getInventory(accountId);
|
||||
importInventory(inventory, request.inventory);
|
||||
if (inventory.isModified()) {
|
||||
anyKnownKey = true;
|
||||
await inventory.save();
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
if (importInventory(inventory, request.inventory)) {
|
||||
anyKnownKey = true;
|
||||
await inventory.save();
|
||||
if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) {
|
||||
const loadout = await getLoadout(accountId);
|
||||
importLoadOutPresets(loadout, request.inventory.LoadOutPresets);
|
||||
if (loadout.isModified()) {
|
||||
anyKnownKey = true;
|
||||
await loadout.save();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
request.inventory.Ship?.Rooms || // very old accounts may have Ship with { Features: [ ... ] }
|
||||
"Apartment" in request.inventory ||
|
||||
"TailorShop" in request.inventory
|
||||
) {
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
importPersonalRooms(personalRooms, request.inventory);
|
||||
if (personalRooms.isModified()) {
|
||||
anyKnownKey = true;
|
||||
await personalRooms.save();
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyKnownKey) {
|
||||
res.send("noKnownKey").end();
|
||||
}
|
||||
|
||||
broadcastInventoryUpdate(req);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
res.send((e as Error).message);
|
||||
}
|
||||
|
||||
if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) {
|
||||
anyKnownKey = true;
|
||||
const loadout = await getLoadout(accountId);
|
||||
importLoadOutPresets(loadout, request.inventory.LoadOutPresets);
|
||||
await loadout.save();
|
||||
}
|
||||
|
||||
if (
|
||||
request.inventory.Ship?.Rooms || // very old accounts may have Ship with { Features: [ ... ] }
|
||||
"Apartment" in request.inventory ||
|
||||
"TailorShop" in request.inventory
|
||||
) {
|
||||
anyKnownKey = true;
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
importPersonalRooms(personalRooms, request.inventory);
|
||||
await personalRooms.save();
|
||||
}
|
||||
|
||||
res.json(anyKnownKey);
|
||||
broadcastInventoryUpdate(req);
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IImportRequest {
|
||||
|
||||
@@ -20,6 +20,10 @@ export const version_compare = (a: string, b: string): number => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const toObjectId = (s: string): Types.ObjectId => {
|
||||
return new Types.ObjectId(s);
|
||||
};
|
||||
|
||||
export const toOid = (objectId: Types.ObjectId): IOid => {
|
||||
return { $oid: objectId.toString() };
|
||||
};
|
||||
|
||||
@@ -1060,7 +1060,6 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
|
||||
InfestationDays: Number,
|
||||
InfestationType: String,
|
||||
ModularParts: { type: [String], default: undefined },
|
||||
UnlockLevel: Number,
|
||||
Expiry: Date,
|
||||
SkillTree: String,
|
||||
OffensiveUpgrade: String,
|
||||
@@ -1448,6 +1447,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
// SNS account cheats
|
||||
skipAllDialogue: Boolean,
|
||||
skipAllPopups: Boolean,
|
||||
dontSubtractPurchaseCreditCost: Boolean,
|
||||
dontSubtractPurchasePlatinumCost: Boolean,
|
||||
dontSubtractPurchaseItemCost: Boolean,
|
||||
@@ -1492,6 +1492,8 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
relicRewardItemCountMultiplier: { type: Number, default: 1 },
|
||||
nightwaveStandingMultiplier: { type: Number, default: 1 },
|
||||
|
||||
Created: Date,
|
||||
|
||||
SubscribedToEmails: { type: Number, default: 0 },
|
||||
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
|
||||
RewardSeed: BigInt,
|
||||
|
||||
@@ -9,6 +9,7 @@ statsRouter.get("/view.php", viewController);
|
||||
statsRouter.get("/profileStats.php", viewController);
|
||||
statsRouter.get("/leaderboard.php", leaderboardGetController);
|
||||
statsRouter.post("/upload.php", uploadController);
|
||||
statsRouter.post("/view.php", viewController);
|
||||
statsRouter.post("/leaderboardWeekly.php", leaderboardPostController);
|
||||
statsRouter.post("/leaderboardArchived.php", leaderboardPostController);
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@ import { repoDir } from "../helpers/pathHelper.ts";
|
||||
interface IBuildConfig {
|
||||
version: string;
|
||||
buildLabel: string;
|
||||
matchmakingBuildId: string;
|
||||
}
|
||||
|
||||
export const buildConfig: IBuildConfig = {
|
||||
version: "",
|
||||
buildLabel: "",
|
||||
matchmakingBuildId: ""
|
||||
buildLabel: ""
|
||||
};
|
||||
|
||||
const buildConfigPath = path.join(repoDir, "static/data/buildConfig.json");
|
||||
|
||||
@@ -254,21 +254,17 @@ const convertItemConfig = <T extends IItemConfig>(client: T): T => {
|
||||
};
|
||||
};
|
||||
|
||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): boolean => {
|
||||
let anyKnownKey = false;
|
||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
|
||||
for (const key of equipmentKeys) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IEquipmentDatabase>(db[key], client[key].map(convertEquipment));
|
||||
}
|
||||
}
|
||||
if (client.WeaponSkins !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IWeaponSkinDatabase>(db.WeaponSkins, client.WeaponSkins.map(convertWeaponSkin));
|
||||
}
|
||||
for (const key of ["Upgrades", "CrewShipSalvagedWeaponSkins", "CrewShipWeaponSkins"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IUpgradeDatabase>(db[key], client[key].map(convertUpgrade));
|
||||
}
|
||||
}
|
||||
@@ -284,7 +280,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"CrewShipRawSalvage"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key].splice(0, db[key].length);
|
||||
client[key].forEach(x => {
|
||||
db[key].push({
|
||||
@@ -296,13 +291,11 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
}
|
||||
for (const key of ["AdultOperatorLoadOuts", "OperatorLoadOuts", "KahlLoadOuts"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
||||
}
|
||||
}
|
||||
for (const key of slotNames) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceSlots(db[key], client[key]);
|
||||
}
|
||||
}
|
||||
@@ -319,7 +312,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"Counselor"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -346,7 +338,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EchoesHexConquestCacheScoreMission"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -362,7 +353,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"ActiveAvatarImageType"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -379,7 +369,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EchoesHexConquestActiveStickers"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -393,133 +382,103 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EntratiVaultCountResetDate"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = fromMongoDate(client[key]);
|
||||
}
|
||||
}
|
||||
// IRewardAtten[]
|
||||
for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.XPInfo !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.XPInfo = client.XPInfo;
|
||||
}
|
||||
if (client.CurrentLoadOutIds !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CurrentLoadOutIds = client.CurrentLoadOutIds;
|
||||
}
|
||||
if (client.Affiliations !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Affiliations = client.Affiliations;
|
||||
}
|
||||
if (client.FusionTreasures !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FusionTreasures = client.FusionTreasures;
|
||||
}
|
||||
if (client.FocusUpgrades !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FocusUpgrades = client.FocusUpgrades;
|
||||
}
|
||||
if (client.EvolutionProgress !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.EvolutionProgress = client.EvolutionProgress;
|
||||
}
|
||||
if (client.InfestedFoundry !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.InfestedFoundry = convertInfestedFoundry(client.InfestedFoundry);
|
||||
}
|
||||
if (client.DialogueHistory !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.DialogueHistory = convertDialogueHistory(client.DialogueHistory);
|
||||
}
|
||||
if (client.CustomMarkers !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CustomMarkers = client.CustomMarkers;
|
||||
}
|
||||
if (client.ChallengeProgress !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.ChallengeProgress = client.ChallengeProgress;
|
||||
}
|
||||
if (client.QuestKeys !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IQuestKeyDatabase>(db.QuestKeys, client.QuestKeys.map(convertQuestKey));
|
||||
}
|
||||
if (client.LastRegionPlayed !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LastRegionPlayed = client.LastRegionPlayed;
|
||||
}
|
||||
if (client.PendingRecipes !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IPendingRecipeDatabase>(db.PendingRecipes, client.PendingRecipes.map(convertPendingRecipe));
|
||||
}
|
||||
if (client.TauntHistory !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.TauntHistory = client.TauntHistory;
|
||||
}
|
||||
if (client.LoreFragmentScans !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LoreFragmentScans = client.LoreFragmentScans;
|
||||
}
|
||||
for (const key of ["PendingSpectreLoadouts", "SpectreLoadouts"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.FocusXP !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FocusXP = client.FocusXP;
|
||||
}
|
||||
for (const key of ["Alignment", "AlignmentReplay"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.StepSequencers !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.StepSequencers = client.StepSequencers;
|
||||
}
|
||||
if (client.CompletedJobChains !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CompletedJobChains = client.CompletedJobChains;
|
||||
}
|
||||
if (client.Nemesis !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Nemesis = convertNemesis(client.Nemesis);
|
||||
}
|
||||
if (client.PlayerSkills !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.PlayerSkills = client.PlayerSkills;
|
||||
}
|
||||
if (client.LotusCustomization !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LotusCustomization = convertItemConfig(client.LotusCustomization);
|
||||
}
|
||||
if (client.CollectibleSeries !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CollectibleSeries = client.CollectibleSeries;
|
||||
}
|
||||
for (const key of ["LibraryAvailableDailyTaskInfo", "LibraryActiveDailyTaskInfo"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.SongChallenges !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.SongChallenges = client.SongChallenges;
|
||||
}
|
||||
if (client.Missions !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Missions = client.Missions;
|
||||
}
|
||||
if (client.FlavourItems !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FlavourItems.splice(0, db.FlavourItems.length);
|
||||
client.FlavourItems.forEach(x => {
|
||||
db.FlavourItems.push({
|
||||
@@ -528,14 +487,11 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
});
|
||||
}
|
||||
if (client.Accolades !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Accolades = client.Accolades;
|
||||
}
|
||||
if (client.Boosters !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IBooster>(db.Boosters, client.Boosters);
|
||||
}
|
||||
return anyKnownKey;
|
||||
};
|
||||
|
||||
export const importLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
||||
|
||||
@@ -33,7 +33,7 @@ import type { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/generic
|
||||
import type { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes.ts";
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import { convertInboxMessage, fromStoreItem, getKeyChainItems } from "./itemDataService.ts";
|
||||
import type { IFlavourItem, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import type { IFlavourItem, IItemConfig, IItemConfigDatabase } from "../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import type { IDefaultUpgrade, IPowersuit, ISentinel, TStandingLimitBin } from "warframe-public-export-plus";
|
||||
import {
|
||||
ExportArcanes,
|
||||
@@ -93,9 +93,11 @@ import type {
|
||||
import { EquipmentFeatures, Status } from "../types/equipmentTypes.ts";
|
||||
import type { ITypeCount } from "../types/commonTypes.ts";
|
||||
import { skinLookupTable } from "../helpers/skinLookupTable.ts";
|
||||
import type { TLoadoutDatabaseDocument } from "../models/inventoryModels/loadoutModel.ts";
|
||||
|
||||
export const createInventory = async (
|
||||
accountOwnerId: Types.ObjectId,
|
||||
loadout: TLoadoutDatabaseDocument,
|
||||
defaultItemReferences: { loadOutPresetId: Types.ObjectId; ship: Types.ObjectId }
|
||||
): Promise<void> => {
|
||||
try {
|
||||
@@ -115,10 +117,29 @@ export const createInventory = async (
|
||||
|
||||
if (config.skipTutorial) {
|
||||
inventory.PlayedParkourTutorial = true;
|
||||
await addStartingGear(inventory);
|
||||
const startingGear = await addStartingGear(inventory);
|
||||
await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain", undefined);
|
||||
await completeQuest(inventory, "/Lotus/Types/Keys/ModQuest/ModQuestKeyChain", undefined);
|
||||
|
||||
loadout.NORMAL.push({
|
||||
s: {
|
||||
ItemId: new Types.ObjectId(fromOid(startingGear.Suits![0].ItemId))
|
||||
},
|
||||
l: {
|
||||
ItemId: new Types.ObjectId(fromOid(startingGear.LongGuns![0].ItemId))
|
||||
},
|
||||
p: {
|
||||
ItemId: new Types.ObjectId(fromOid(startingGear.Pistols![0].ItemId))
|
||||
},
|
||||
m: {
|
||||
ItemId: new Types.ObjectId(fromOid(startingGear.Melee![0].ItemId))
|
||||
},
|
||||
a: {
|
||||
ItemId: new Types.ObjectId(fromOid(startingGear.SpecialItems![0].ItemId))
|
||||
}
|
||||
});
|
||||
await loadout.save();
|
||||
|
||||
const completedMissions = ["SolNode27", "SolNode89", "SolNode63", "SolNode85", "SolNode15", "SolNode79"];
|
||||
|
||||
inventory.Missions.push(
|
||||
@@ -377,6 +398,9 @@ export const addItem = async (
|
||||
FusionTreasures: fusionTreasureChanges
|
||||
};
|
||||
} else if (ExportResources[typeName].productCategory == "Ships") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Ships: got ${quantity}, expected 1`);
|
||||
}
|
||||
const oid = await createShip(inventory.accountOwnerId, typeName);
|
||||
inventory.Ships.push(oid);
|
||||
return {
|
||||
@@ -388,6 +412,9 @@ export const addItem = async (
|
||||
]
|
||||
};
|
||||
} else if (ExportResources[typeName].productCategory == "CrewShips") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of CrewShips: got ${quantity}, expected 1`);
|
||||
}
|
||||
return {
|
||||
...(await addCrewShip(inventory, typeName)),
|
||||
// fix to unlock railjack modding, item bellow supposed to be obtained from archwing quest
|
||||
@@ -522,6 +549,11 @@ export const addItem = async (
|
||||
if (typeName in ExportWeapons) {
|
||||
const weapon = ExportWeapons[typeName];
|
||||
if (weapon.totalDamage != 0) {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of ${weapon.productCategory}: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {};
|
||||
if (premiumPurchase) {
|
||||
defaultOverwrites.Features = EquipmentFeatures.DOUBLE_CAPACITY;
|
||||
@@ -578,6 +610,9 @@ export const addItem = async (
|
||||
};
|
||||
} else if (targetFingerprint) {
|
||||
// Sister's Hound
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of MoaPets: got ${quantity}, expected 1`);
|
||||
}
|
||||
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisPetTargetFingerprint;
|
||||
const head = targetFingerprintObj.Parts[0];
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
@@ -653,6 +688,9 @@ export const addItem = async (
|
||||
const key = ExportKeys[typeName];
|
||||
|
||||
if (key.chainStages) {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of QuestKeys: got ${quantity}, expected 1`);
|
||||
}
|
||||
const key = addQuestKey(inventory, { ItemType: typeName });
|
||||
if (!key) return {};
|
||||
return { QuestKeys: [key] };
|
||||
@@ -695,6 +733,9 @@ export const addItem = async (
|
||||
if (typeName.endsWith("AugmentCard")) break;
|
||||
switch (typeName.substring(1).split("/")[2]) {
|
||||
default: {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Suits: got ${quantity}, expected 1`);
|
||||
}
|
||||
return {
|
||||
...(await addPowerSuit(inventory, typeName, {
|
||||
Features: premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
@@ -703,6 +744,9 @@ export const addItem = async (
|
||||
};
|
||||
}
|
||||
case "Archwing": {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of SpaceSuits: got ${quantity}, expected 1`);
|
||||
}
|
||||
inventory.ArchwingEnabled = true;
|
||||
return {
|
||||
...addSpaceSuit(
|
||||
@@ -715,6 +759,9 @@ export const addItem = async (
|
||||
};
|
||||
}
|
||||
case "EntratiMech": {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of MechSuits: got ${quantity}, expected 1`);
|
||||
}
|
||||
return {
|
||||
...(await addMechSuit(
|
||||
inventory,
|
||||
@@ -747,16 +794,18 @@ export const addItem = async (
|
||||
|
||||
case "Boons":
|
||||
// Can purchase /Lotus/Upgrades/Boons/DuviriVendorBoonItem from Acrithis, doesn't need to be added to inventory.
|
||||
logger.debug(`acquisition of ${typeName} is not committed to inventory`);
|
||||
return {};
|
||||
|
||||
case "Stickers":
|
||||
{
|
||||
const entry = inventory.RawUpgrades.find(x => x.ItemType == typeName);
|
||||
if (entry && entry.ItemCount >= 10) {
|
||||
logger.debug(`adding ${quantity} pix chip(s) instead of ${typeName}`);
|
||||
const miscItemChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
|
||||
ItemCount: 1
|
||||
ItemCount: quantity
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
@@ -779,6 +828,9 @@ export const addItem = async (
|
||||
break;
|
||||
|
||||
case "Skins": {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Skins: got ${quantity}, expected 1`);
|
||||
}
|
||||
return addSkin(inventory, typeName);
|
||||
}
|
||||
}
|
||||
@@ -787,6 +839,9 @@ export const addItem = async (
|
||||
case "Types":
|
||||
switch (typeName.substring(1).split("/")[2]) {
|
||||
case "Sentinels": {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Sentinels: got ${quantity}, expected 1`);
|
||||
}
|
||||
return addSentinel(inventory, typeName, premiumPurchase);
|
||||
}
|
||||
case "Game": {
|
||||
@@ -813,9 +868,19 @@ export const addItem = async (
|
||||
typeName != "/Lotus/Types/Game/KubrowPet/BlankTraitPrint" &&
|
||||
typeName != "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
|
||||
) {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of KubrowPet: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
|
||||
}
|
||||
} else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of CrewMember: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
if (!seed) {
|
||||
throw new Error(`Expected crew member to have a seed`);
|
||||
}
|
||||
@@ -825,12 +890,22 @@ export const addItem = async (
|
||||
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
||||
};
|
||||
} else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of CrewShipHarness: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
return addCrewShipHarness(inventory, typeName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Items": {
|
||||
if (typeName.substring(1).split("/")[3] == "Emotes") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of FlavourItems: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
return addCustomization(inventory, typeName);
|
||||
}
|
||||
break;
|
||||
@@ -840,6 +915,9 @@ export const addItem = async (
|
||||
logger.warn("refusing to add Horse because account already has one");
|
||||
return {};
|
||||
}
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Horses: got ${quantity}, expected 1`);
|
||||
}
|
||||
const horseIndex = inventory.Horses.push({ ItemType: typeName });
|
||||
return {
|
||||
Horses: [inventory.Horses[horseIndex - 1].toJSON<IEquipmentClient>()]
|
||||
@@ -847,11 +925,19 @@ export const addItem = async (
|
||||
}
|
||||
case "Vehicles":
|
||||
if (typeName == "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(`unexpected acquisition quantity of Vehicles: got ${quantity}, expected 1`);
|
||||
}
|
||||
return addMotorcycle(inventory, typeName);
|
||||
}
|
||||
break;
|
||||
case "Lore":
|
||||
if (typeName == "/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentRewards") {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of LoreFragmentScans: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
const fragmentType = getRandomElement([
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentA",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentB",
|
||||
@@ -885,6 +971,11 @@ export const addItem = async (
|
||||
case "Pistols":
|
||||
case "LongGuns":
|
||||
case "Melee": {
|
||||
if (quantity != 1) {
|
||||
throw new Error(
|
||||
`unexpected acquisition quantity of ${productCategory}: got ${quantity}, expected 1`
|
||||
);
|
||||
}
|
||||
const inventoryChanges = addEquipment(inventory, productCategory, typeName);
|
||||
return {
|
||||
...inventoryChanges,
|
||||
@@ -922,9 +1013,9 @@ export const addItems = async (
|
||||
export const applyDefaultUpgrades = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
defaultUpgrades: IDefaultUpgrade[] | undefined
|
||||
): IItemConfig[] => {
|
||||
): IItemConfigDatabase[] => {
|
||||
const modsToGive: IRawUpgrade[] = [];
|
||||
const configs: IItemConfig[] = [];
|
||||
const configs: IItemConfigDatabase[] = [];
|
||||
if (defaultUpgrades) {
|
||||
const upgrades = [];
|
||||
for (const defaultUpgrade of defaultUpgrades) {
|
||||
@@ -1500,14 +1591,13 @@ export const addSkin = (
|
||||
typeName: string,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
if (typeName == "/Lotus/Upgrades/Skins/Clan/BountyHunterBadgeItem") {
|
||||
logger.debug(`stratos emblem, increasing bounty score`);
|
||||
inventory.BountyScore ??= 0;
|
||||
inventory.BountyScore += 1;
|
||||
}
|
||||
if (inventory.WeaponSkins.some(x => x.ItemType == typeName)) {
|
||||
if (typeName == "/Lotus/Upgrades/Skins/Clan/BountyHunterBadgeItem") {
|
||||
logger.debug(`account already owns stratos emblem, increasing bounty score instead`);
|
||||
inventory.BountyScore ??= 0;
|
||||
inventory.BountyScore += 1;
|
||||
} else {
|
||||
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
|
||||
}
|
||||
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
|
||||
} else {
|
||||
const index =
|
||||
inventory.WeaponSkins.push({
|
||||
@@ -1850,7 +1940,9 @@ export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray:
|
||||
if (MiscItems[itemIndex].ItemCount == 0) {
|
||||
MiscItems.splice(itemIndex, 1);
|
||||
} else if (MiscItems[itemIndex].ItemCount < 0) {
|
||||
throw new Error(`inventory.MiscItems has a negative count for ${ItemType} after subtracting ${ItemCount}`);
|
||||
throw new Error(
|
||||
`Cannot remove ${ItemCount * -1}x ${ItemType} from MiscItems, would be left with ${MiscItems[itemIndex].ItemCount}`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1873,7 +1965,7 @@ const applyArrayChanges = (
|
||||
arr.splice(itemIndex, 1);
|
||||
} else if (arr[itemIndex].ItemCount < 0) {
|
||||
throw new Error(
|
||||
`inventory.${key} has a negative count for ${change.ItemType} after subtracting ${change.ItemCount}`
|
||||
`Cannot remove ${change.ItemCount * -1}x ${change.ItemType} from ${key}, would be left with ${arr[itemIndex].ItemCount}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1922,7 +2014,7 @@ export const addMods = (inventory: TInventoryDatabaseDocument, itemsArray: IRawU
|
||||
RawUpgrades.splice(itemIndex, 1);
|
||||
} else if (RawUpgrades[itemIndex].ItemCount < 0) {
|
||||
throw new Error(
|
||||
`inventory.RawUpgrades has a negative count for ${ItemType} after subtracting ${ItemCount}`
|
||||
`Cannot remove ${ItemCount * -1}x ${ItemType} from RawUpgrades, would be left with ${RawUpgrades[itemIndex].ItemCount}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1939,7 +2031,7 @@ export const addFusionTreasures = (inventory: TInventoryDatabaseDocument, itemsA
|
||||
FusionTreasures.splice(itemIndex, 1);
|
||||
} else if (FusionTreasures[itemIndex].ItemCount <= 0) {
|
||||
throw new Error(
|
||||
`inventory.FusionTreasures has a negative count for ${ItemType} after subtracting ${ItemCount}`
|
||||
`Cannot remove ${ItemCount * -1}x ${ItemType} from FusionTreasures, would be left with ${FusionTreasures[itemIndex].ItemCount}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createInventory } from "./inventoryService.ts";
|
||||
import type { IDatabaseAccountJson, IDatabaseAccountRequiredFields } from "../types/loginTypes.ts";
|
||||
import { createShip } from "./shipService.ts";
|
||||
import type { Document, Types } from "mongoose";
|
||||
import { Loadout } from "../models/inventoryModels/loadoutModel.ts";
|
||||
import { Loadout, type TLoadoutDatabaseDocument } from "../models/inventoryModels/loadoutModel.ts";
|
||||
import { PersonalRooms } from "../models/personalRoomsModel.ts";
|
||||
import type { Request } from "express";
|
||||
import { config } from "./configService.ts";
|
||||
@@ -39,10 +39,10 @@ export const createAccount = async (accountData: IDatabaseAccountRequiredFields)
|
||||
const account = new Account(accountData);
|
||||
try {
|
||||
await account.save();
|
||||
const loadoutId = await createLoadout(account._id);
|
||||
const loadout = await createLoadout(account._id);
|
||||
const shipId = await createShip(account._id);
|
||||
await createPersonalRooms(account._id, shipId);
|
||||
await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
|
||||
await createInventory(account._id, loadout, { loadOutPresetId: loadout._id, ship: shipId });
|
||||
await createStats(account._id.toString());
|
||||
return account.toJSON();
|
||||
} catch (error) {
|
||||
@@ -53,10 +53,10 @@ export const createAccount = async (accountData: IDatabaseAccountRequiredFields)
|
||||
}
|
||||
};
|
||||
|
||||
export const createLoadout = async (accountId: Types.ObjectId): Promise<Types.ObjectId> => {
|
||||
export const createLoadout = async (accountId: Types.ObjectId): Promise<TLoadoutDatabaseDocument> => {
|
||||
const loadout = new Loadout({ loadoutOwnerId: accountId });
|
||||
const savedLoadout = await loadout.save();
|
||||
return savedLoadout._id;
|
||||
return savedLoadout;
|
||||
};
|
||||
|
||||
export const createPersonalRooms = async (accountId: Types.ObjectId, shipId: Types.ObjectId): Promise<void> => {
|
||||
|
||||
@@ -84,7 +84,7 @@ import {
|
||||
import { config } from "./configService.ts";
|
||||
import libraryDailyTasks from "../../static/fixed_responses/libraryDailyTasks.json" with { type: "json" };
|
||||
import type { IGoal, ISyndicateMissionInfo } from "../types/worldStateTypes.ts";
|
||||
import { fromOid } from "../helpers/inventoryHelpers.ts";
|
||||
import { fromOid, version_compare } from "../helpers/inventoryHelpers.ts";
|
||||
import type { TAccountDocument } from "./loginService.ts";
|
||||
import type { ITypeCount } from "../types/commonTypes.ts";
|
||||
import type { IEquipmentClient } from "../types/equipmentTypes.ts";
|
||||
@@ -480,9 +480,32 @@ export const addMissionInventoryUpdates = async (
|
||||
case "Upgrades":
|
||||
value.forEach(clientUpgrade => {
|
||||
const id = fromOid(clientUpgrade.ItemId);
|
||||
if (id == "") {
|
||||
// Really old builds (tested U7-U8) do not have the UpgradeFingerprint set for unranked mod drops
|
||||
clientUpgrade.UpgradeFingerprint ??= "lvl=0|";
|
||||
// U11 and below also don't initialize ItemCount since RawUpgrade doesn't exist in them
|
||||
clientUpgrade.ItemCount ??= 1;
|
||||
if (account.BuildLabel && version_compare(account.BuildLabel, "2016.08.19.17.12") < 0) {
|
||||
// Acquired Mods have a different UpgradeFingerprint format in pre-U18.18.0 builds, this converts them to the format the database expects
|
||||
clientUpgrade.UpgradeFingerprint = `{"lvl":${clientUpgrade.UpgradeFingerprint.substring(
|
||||
clientUpgrade.UpgradeFingerprint.indexOf("=") + 1,
|
||||
clientUpgrade.UpgradeFingerprint.lastIndexOf("|")
|
||||
)}}`;
|
||||
}
|
||||
// Handle Fusion Core drops
|
||||
const parsedFingerprint = JSON.parse(clientUpgrade.UpgradeFingerprint) as { lvl: number };
|
||||
if (parsedFingerprint.lvl != 0) {
|
||||
inventory.Upgrades.push({
|
||||
ItemType: clientUpgrade.ItemType,
|
||||
UpgradeFingerprint: clientUpgrade.UpgradeFingerprint
|
||||
});
|
||||
} else if (id == "") {
|
||||
// U19 does not provide RawUpgrades and instead interleaves them with riven progress here
|
||||
addMods(inventory, [clientUpgrade]);
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: clientUpgrade.ItemType,
|
||||
ItemCount: clientUpgrade.ItemCount
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
const upgrade = inventory.Upgrades.id(id)!;
|
||||
upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
|
||||
|
||||
@@ -554,6 +554,9 @@ const handleBoosterPackPurchase = async (
|
||||
BoosterPackItems: "",
|
||||
InventoryChanges: {}
|
||||
};
|
||||
if (quantity < 1) {
|
||||
throw new Error(`invalid quantity for booster pack purchase: ${quantity}`);
|
||||
}
|
||||
if (quantity > 100) {
|
||||
throw new Error(
|
||||
"attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
|
||||
|
||||
@@ -15,6 +15,7 @@ import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/i
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import { ExportKeys, ExportRecipes } from "warframe-public-export-plus";
|
||||
import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts";
|
||||
import { fromOid } from "../helpers/inventoryHelpers.ts";
|
||||
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
|
||||
import questCompletionItems from "../../static/fixed_responses/questCompletionRewards.json" with { type: "json" };
|
||||
import type { ITypeCount } from "../types/commonTypes.ts";
|
||||
@@ -609,21 +610,18 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument,
|
||||
}
|
||||
|
||||
case "/Lotus/Types/Recipes/WarframeRecipes/ChromaBlueprint": {
|
||||
await addItems(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconABlueprint",
|
||||
ItemCount: -1
|
||||
},
|
||||
{
|
||||
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconBBlueprint",
|
||||
ItemCount: -1
|
||||
},
|
||||
{
|
||||
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconCBlueprint",
|
||||
ItemCount: -1
|
||||
const itemsToRemove = [
|
||||
"/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconABlueprint",
|
||||
"/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconBBlueprint",
|
||||
"/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconCBlueprint"
|
||||
];
|
||||
for (const itemToRemove of itemsToRemove) {
|
||||
try {
|
||||
await addItem(inventory, itemToRemove, -1, undefined, undefined, undefined, true);
|
||||
} catch (e) {
|
||||
logger.debug(`removeRequiredItems: Couldn't remove ${itemToRemove}: ${(e as Error).message}`);
|
||||
}
|
||||
]);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -636,20 +634,20 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument,
|
||||
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
|
||||
await addItem(inventory, recipe.resultType);
|
||||
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
|
||||
await addItems(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartA",
|
||||
ItemCount: -1
|
||||
},
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartB",
|
||||
ItemCount: -1
|
||||
},
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartC",
|
||||
ItemCount: -1
|
||||
const itemsToRemove = [
|
||||
"/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartA",
|
||||
"/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartB",
|
||||
"/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartC"
|
||||
];
|
||||
for (const itemToRemove of itemsToRemove) {
|
||||
try {
|
||||
await addItem(inventory, itemToRemove, -1, undefined, undefined, undefined, true);
|
||||
} catch (e) {
|
||||
logger.debug(
|
||||
`removeRequiredItems: Couldn't remove ${itemToRemove}: ${(e as Error).message}`
|
||||
);
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -761,9 +759,9 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
umbraModA.ItemId.$oid,
|
||||
umbraModB.ItemId.$oid,
|
||||
umbraModC.ItemId.$oid
|
||||
fromOid(umbraModA.ItemId),
|
||||
fromOid(umbraModB.ItemId),
|
||||
fromOid(umbraModC.ItemId)
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -778,7 +776,16 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument,
|
||||
addEquipment(inventory, "Melee", "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana", {
|
||||
Configs: [
|
||||
{
|
||||
Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid]
|
||||
Upgrades: [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
fromOid(sacrificeModA.ItemId),
|
||||
fromOid(sacrificeModB.ItemId)
|
||||
]
|
||||
}
|
||||
],
|
||||
XP: 450_000,
|
||||
|
||||
@@ -6,14 +6,15 @@ import type {
|
||||
ISaveLoadoutRequestNoUpgradeVer
|
||||
} from "../types/saveLoadoutTypes.ts";
|
||||
import { Loadout } from "../models/inventoryModels/loadoutModel.ts";
|
||||
import { getInventory } from "./inventoryService.ts";
|
||||
import { addMods, getInventory } from "./inventoryService.ts";
|
||||
import type { IOid } from "../types/commonTypes.ts";
|
||||
import { Types } from "mongoose";
|
||||
import { isEmptyObject } from "../helpers/general.ts";
|
||||
import { version_compare } from "../helpers/inventoryHelpers.ts";
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import type { TEquipmentKey } from "../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { equipmentKeys } from "../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IItemConfig } from "../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import type { IItemConfig, IItemConfigDatabase } from "../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import { importCrewShipMembers, importCrewShipWeapon, importLoadOutConfig } from "./importService.ts";
|
||||
|
||||
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
||||
@@ -26,7 +27,8 @@ itemconfig has multiple config ids
|
||||
*/
|
||||
export const handleInventoryItemConfigChange = async (
|
||||
equipmentChanges: ISaveLoadoutRequestNoUpgradeVer,
|
||||
accountId: string
|
||||
accountId: string,
|
||||
buildLabel: string | undefined
|
||||
): Promise<string | void> => {
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
@@ -196,7 +198,36 @@ export const handleInventoryItemConfigChange = async (
|
||||
|
||||
for (const [configId, config] of Object.entries(itemConfigEntries)) {
|
||||
if (/^[0-9]+$/.test(configId)) {
|
||||
inventoryItem.Configs[parseInt(configId)] = config as IItemConfig;
|
||||
const c = config as IItemConfig;
|
||||
if (buildLabel && version_compare(buildLabel, "2014.04.10.17.47") < 0) {
|
||||
if (c.Upgrades) {
|
||||
// U10-U11 store mods in the item config as $id instead of a string, need to convert that here
|
||||
const convertedUpgrades: string[] = [];
|
||||
c.Upgrades.forEach(upgrade => {
|
||||
const upgradeId = upgrade as { $id: string };
|
||||
const rawUpgrade = inventory.RawUpgrades.id(upgradeId.$id);
|
||||
if (rawUpgrade) {
|
||||
const newId = new Types.ObjectId();
|
||||
convertedUpgrades.push(newId.toString());
|
||||
addMods(inventory, [
|
||||
{
|
||||
ItemType: rawUpgrade.ItemType,
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
inventory.Upgrades.push({
|
||||
UpgradeFingerprint: `{"lvl":0}`,
|
||||
ItemType: rawUpgrade.ItemType,
|
||||
_id: newId
|
||||
});
|
||||
} else {
|
||||
convertedUpgrades.push(upgradeId.$id);
|
||||
}
|
||||
});
|
||||
c.Upgrades = convertedUpgrades;
|
||||
}
|
||||
}
|
||||
inventoryItem.Configs[parseInt(configId)] = c as IItemConfigDatabase;
|
||||
}
|
||||
}
|
||||
if ("Favorite" in itemConfigEntries) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
ICrewShipCustomization,
|
||||
IFlavourItem,
|
||||
IItemConfig,
|
||||
IItemConfigDatabase,
|
||||
IPolarity
|
||||
} from "./inventoryTypes/commonInventoryTypes.ts";
|
||||
|
||||
@@ -33,7 +34,7 @@ export enum EquipmentFeatures {
|
||||
export interface IEquipmentDatabase {
|
||||
ItemType: string;
|
||||
ItemName?: string;
|
||||
Configs: IItemConfig[];
|
||||
Configs: IItemConfigDatabase[];
|
||||
UpgradeVer?: number;
|
||||
XP?: number;
|
||||
Features?: number;
|
||||
@@ -48,7 +49,6 @@ export interface IEquipmentDatabase {
|
||||
InfestationDays?: number;
|
||||
InfestationType?: string;
|
||||
ModularParts?: string[];
|
||||
UnlockLevel?: number;
|
||||
Expiry?: Date;
|
||||
SkillTree?: string;
|
||||
OffensiveUpgrade?: string;
|
||||
@@ -69,9 +69,18 @@ export interface IEquipmentDatabase {
|
||||
export interface IEquipmentClient
|
||||
extends Omit<
|
||||
IEquipmentDatabase,
|
||||
"_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "UmbraDate" | "Weapon" | "CrewMembers" | "Details"
|
||||
| "_id"
|
||||
| "Configs"
|
||||
| "InfestationDate"
|
||||
| "Expiry"
|
||||
| "UpgradesExpiry"
|
||||
| "UmbraDate"
|
||||
| "Weapon"
|
||||
| "CrewMembers"
|
||||
| "Details"
|
||||
> {
|
||||
ItemId: IOidWithLegacySupport;
|
||||
Configs: IItemConfig[];
|
||||
InfestationDate?: IMongoDate;
|
||||
Expiry?: IMongoDate;
|
||||
UpgradesExpiry?: IMongoDate;
|
||||
@@ -79,6 +88,10 @@ export interface IEquipmentClient
|
||||
Weapon?: ICrewShipWeaponClient;
|
||||
CrewMembers?: ICrewShipMembersClient;
|
||||
Details?: IKubrowPetDetailsClient;
|
||||
// For Pre-U24.4.0 builds
|
||||
UnlockLevel?: number;
|
||||
UtilityUnlocked?: number;
|
||||
Gild?: boolean;
|
||||
}
|
||||
|
||||
export interface IArchonCrystalUpgrade {
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface IItemConfig {
|
||||
facial?: IColor;
|
||||
syancol?: IColor;
|
||||
cloth?: IColor;
|
||||
Upgrades?: string[];
|
||||
Upgrades?: string[] | { $id: string }[];
|
||||
Name?: string;
|
||||
OperatorAmp?: IOid;
|
||||
Songs?: ISong[];
|
||||
@@ -56,13 +56,17 @@ export interface IItemConfig {
|
||||
ugly?: boolean;
|
||||
}
|
||||
|
||||
export interface IItemConfigDatabase extends Omit<IItemConfig, "Upgrades"> {
|
||||
Upgrades?: string[];
|
||||
}
|
||||
|
||||
export interface ISong {
|
||||
m?: string;
|
||||
b?: string;
|
||||
p?: string;
|
||||
s: string;
|
||||
}
|
||||
export interface IOperatorConfigDatabase extends IItemConfig {
|
||||
export interface IOperatorConfigDatabase extends IItemConfigDatabase {
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export type InventoryDatabaseEquipment = {
|
||||
// Fields specific to SNS
|
||||
export interface IAccountCheats {
|
||||
skipAllDialogue?: boolean;
|
||||
skipAllPopups?: boolean;
|
||||
dontSubtractPurchaseCreditCost?: boolean;
|
||||
dontSubtractPurchasePlatinumCost?: boolean;
|
||||
dontSubtractPurchaseItemCost?: boolean;
|
||||
@@ -317,6 +318,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
Accolades?: IAccolades;
|
||||
Counselor?: boolean;
|
||||
Upgrades: IUpgradeClient[];
|
||||
Cards?: IUpgradeClient[]; // U8
|
||||
EquippedGear: string[];
|
||||
DeathMarks: string[];
|
||||
FusionTreasures: IFusionTreasure[];
|
||||
@@ -591,10 +593,16 @@ export interface IUpgradeClient {
|
||||
ItemType: string;
|
||||
UpgradeFingerprint?: string;
|
||||
PendingRerollFingerprint?: string;
|
||||
ItemId: IOid;
|
||||
ItemId: IOidWithLegacySupport;
|
||||
// Stuff for U7-U8
|
||||
ParentId?: IOidWithLegacySupport;
|
||||
Slot?: number;
|
||||
AmountRemaining?: number;
|
||||
Rank?: number;
|
||||
}
|
||||
|
||||
export interface IUpgradeDatabase extends Omit<IUpgradeClient, "ItemId"> {
|
||||
export interface IUpgradeDatabase
|
||||
extends Omit<IUpgradeClient, "ItemId" | "ParentId" | "Slot" | "AmountRemaining" | "Rank"> {
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
@@ -602,9 +610,9 @@ export interface IUpgradeFromClient {
|
||||
ItemType: string;
|
||||
ItemId: IOidWithLegacySupport;
|
||||
FromSKU?: boolean;
|
||||
UpgradeFingerprint: string;
|
||||
UpgradeFingerprint?: string;
|
||||
PendingRerollFingerprint: string;
|
||||
ItemCount: number;
|
||||
ItemCount?: number;
|
||||
LastAdded: IOidWithLegacySupport;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IOid, ITypeCount } from "./commonTypes.ts";
|
||||
import type { IOid, IOidWithLegacySupport, ITypeCount } from "./commonTypes.ts";
|
||||
import type { ArtifactPolarity, IPolarity } from "./inventoryTypes/commonInventoryTypes.ts";
|
||||
import type {
|
||||
IBooster,
|
||||
@@ -21,7 +21,8 @@ import type {
|
||||
IInvasionProgressClient,
|
||||
IWeaponSkinClient,
|
||||
IKubrowPetEggClient,
|
||||
INemesisClient
|
||||
INemesisClient,
|
||||
IUpgradeClient
|
||||
} from "./inventoryTypes/inventoryTypes.ts";
|
||||
import type { IGroup } from "./loginTypes.ts";
|
||||
import type { ILoadOutPresets } from "./saveLoadoutTypes.ts";
|
||||
@@ -227,6 +228,24 @@ export interface IUpgradesRequest {
|
||||
UpgradeVersion: number;
|
||||
Operations: IUpgradeOperation[];
|
||||
}
|
||||
export interface IUpgradesRequestLegacy {
|
||||
Category: TEquipmentKey;
|
||||
Weapon: { ItemType: string; ItemId: IOidWithLegacySupport };
|
||||
UpgradeVer: number;
|
||||
UnlockLevel: number;
|
||||
Polarized: number;
|
||||
UtilityUnlocked: number;
|
||||
FocusLens?: string;
|
||||
UpgradeReq?: string;
|
||||
UtilityReq?: string;
|
||||
IsSwappingOperation: boolean;
|
||||
PolarizeReq?: string;
|
||||
PolarizeSlot: number;
|
||||
PolarizeValue: ArtifactPolarity;
|
||||
PolarityRemap: IPolarity[];
|
||||
UpgradesToAttach?: IUpgradeClient[];
|
||||
UpgradesToDetach?: IUpgradeClient[];
|
||||
}
|
||||
export interface IUpgradeOperation {
|
||||
OperationType: string;
|
||||
UpgradeRequirement: string; // uniqueName of item being consumed
|
||||
|
||||
@@ -31,7 +31,7 @@ export interface ISession {
|
||||
export interface IFindSessionRequest {
|
||||
id?: string;
|
||||
originalSessionId?: string;
|
||||
buildId?: number;
|
||||
buildId?: number | bigint;
|
||||
gameModeId?: number;
|
||||
regionId?: number;
|
||||
maxEloDifference?: number;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
[
|
||||
"ArcaneWallConsole",
|
||||
"CetusHub4",
|
||||
"SolarisUnitedHub1",
|
||||
"/Lotus/Language/SolarisVenus/FishmongerName",
|
||||
"/Lotus/Language/SolarisVenus/ProspectorName",
|
||||
@@ -12,6 +14,7 @@
|
||||
"SaturnWolf3",
|
||||
"SaturnWolf4",
|
||||
"SaturnWolf5",
|
||||
"SyndicateFirstPledge",
|
||||
"ConclaveSyndicate",
|
||||
"ArbitersSyndicate",
|
||||
"LibrarySyndicate",
|
||||
@@ -117,6 +120,7 @@
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/Fibonacci",
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/BirdThree",
|
||||
"/Lotus/Language/Zariman/Quinn",
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/Tagfer",
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/TagferFirstRank1",
|
||||
"VoidVaultIntro",
|
||||
"PurchasePlatformLockedNotificationSeen",
|
||||
@@ -137,6 +141,7 @@
|
||||
"EntratiLabConquestHardModeUnlocked",
|
||||
"/Lotus/Language/Npcs/KonzuPostNewWar",
|
||||
"/Lotus/Language/SolarisVenus/EudicoPostNewWar",
|
||||
"/Lotus/Language/FiveFates/KoumeiStatueHubName",
|
||||
"/Lotus/Language/NokkoColony/NokkoVendorName",
|
||||
"NokkoVisions_FirstVisit"
|
||||
]
|
||||
|
||||
27
static/fixed_responses/allPopups.json
Normal file
27
static/fixed_responses/allPopups.json
Normal file
@@ -0,0 +1,27 @@
|
||||
[
|
||||
"RailjackPlexusTutorial",
|
||||
"RailjackIntrinsicsTutorial",
|
||||
"RailjackDryDockTutorial",
|
||||
"RailjackStarchartTutorial",
|
||||
"NPE_poponce_ayatans",
|
||||
"NPE_poponce_sellmodinfo",
|
||||
"NPE_poponce_transmuteinfo",
|
||||
"MarketOpened",
|
||||
"PrimeTokensTutorial",
|
||||
"WelcomeScreen_Undermind",
|
||||
"WelcomeScreen_Jade",
|
||||
"WelcomeScreen_Isleweaver",
|
||||
"WelcomeScreen_Techrot",
|
||||
"WelcomeScreen_1999",
|
||||
"WelcomeScreen_Koumei",
|
||||
"WelcomeScreen_Dante",
|
||||
"WelcomeScreen_Whispers",
|
||||
"WelcomeScreen_Dagath",
|
||||
"WelcomeScreen_Kullervo",
|
||||
"EpisodeIntro_RadioLegionIntermission14Syndicate",
|
||||
"EpisodeIntro_RadioLegionIntermission13Syndicate",
|
||||
"EpisodeIntro_RadioLegionIntermission12Syndicate",
|
||||
"EpisodeIntro_RadioLegionIntermission11Syndicate",
|
||||
"EpisodeIntro_RadioLegionIntermission10Syndicate",
|
||||
"EpisodeIntro_RadioLegionIntermission9Syndicate"
|
||||
]
|
||||
@@ -960,6 +960,10 @@
|
||||
<input class="form-check-input" type="checkbox" id="skipAllDialogue" />
|
||||
<label class="form-check-label" for="skipAllDialogue" data-loc="cheats_skipAllDialogue"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="skipAllPopups" />
|
||||
<label class="form-check-label" for="skipAllPopups" data-loc="cheats_skipAllPopups"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="dontSubtractPurchaseCreditCost" />
|
||||
<label class="form-check-label" for="dontSubtractPurchaseCreditCost" data-loc="cheats_dontSubtractPurchaseCreditCost"></label>
|
||||
@@ -1558,6 +1562,7 @@
|
||||
<p class="mt-3 mb-1" data-loc="import_samples"></p>
|
||||
<ul>
|
||||
<li><a href="#" onclick="event.preventDefault();setImportSample('maxFocus');" data-loc="import_samples_maxFocus"></a></li>
|
||||
<li><a href="#" onclick="event.preventDefault();setImportSample('accolades');" data-loc="import_samples_accolades"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -426,9 +426,25 @@ function fetchItemList() {
|
||||
};
|
||||
|
||||
// Add mods missing in data sources
|
||||
data.mods.push({
|
||||
uniqueName: "/Lotus/Upgrades/Mods/Fusers/CommonModFuser",
|
||||
name: loc("code_fusionCoreCommon"),
|
||||
fusionLimit: 3
|
||||
});
|
||||
data.mods.push({
|
||||
uniqueName: "/Lotus/Upgrades/Mods/Fusers/UncommonModFuser",
|
||||
name: loc("code_fusionCoreUncommon"),
|
||||
fusionLimit: 5
|
||||
});
|
||||
data.mods.push({
|
||||
uniqueName: "/Lotus/Upgrades/Mods/Fusers/RareModFuser",
|
||||
name: loc("code_fusionCoreRare"),
|
||||
fusionLimit: 5
|
||||
});
|
||||
data.mods.push({
|
||||
uniqueName: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser",
|
||||
name: loc("code_legendaryCore")
|
||||
name: loc("code_legendaryCore"),
|
||||
fusionLimit: 0
|
||||
});
|
||||
data.mods.push({
|
||||
uniqueName: "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod",
|
||||
@@ -1507,7 +1523,9 @@ function updateInventory() {
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
|
||||
td.innerHTML += " <span title='" + loc("code_rank") + "'>★ 0/" + maxRank + "</span>";
|
||||
if (maxRank > 0) {
|
||||
td.innerHTML += " <span title='" + loc("code_rank") + "'>★ 0/" + maxRank + "</span>";
|
||||
}
|
||||
if (item.ItemCount > 1) {
|
||||
td.innerHTML +=
|
||||
" <span title='" + loc("code_count") + "'>🗍 " + parseInt(item.ItemCount) + "</span>";
|
||||
@@ -3463,6 +3481,9 @@ function doAddAllMods() {
|
||||
for (const child of document.getElementById("datalist-mods").children) {
|
||||
modsAll.add(child.getAttribute("data-key"));
|
||||
}
|
||||
modsAll.delete("/Lotus/Upgrades/Mods/Fusers/CommonModFuser");
|
||||
modsAll.delete("/Lotus/Upgrades/Mods/Fusers/UncommonModFuser");
|
||||
modsAll.delete("/Lotus/Upgrades/Mods/Fusers/RareModFuser");
|
||||
modsAll.delete("/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser");
|
||||
|
||||
revalidateAuthz().then(() => {
|
||||
@@ -3625,8 +3646,12 @@ function doImport() {
|
||||
data: JSON.stringify({
|
||||
inventory: JSON.parse($("#import-inventory").val())
|
||||
})
|
||||
}).then(function (anyKnownKey) {
|
||||
toast(loc(anyKnownKey ? "code_succImport" : "code_nothingToDo"));
|
||||
}).then(function (err) {
|
||||
if (err) {
|
||||
toast(err == "noKnownKey" ? loc("code_nothingToDo") : err);
|
||||
} else {
|
||||
toast(loc("code_succImport"));
|
||||
}
|
||||
updateInventory();
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -4126,6 +4151,22 @@ const importSamples = {
|
||||
Level: 3
|
||||
}
|
||||
]
|
||||
},
|
||||
accolades: {
|
||||
Staff: false,
|
||||
Founder: 4,
|
||||
Guide: 2,
|
||||
Moderator: true,
|
||||
Partner: true,
|
||||
Created: {
|
||||
$date: {
|
||||
$numberLong: "1356998400000"
|
||||
}
|
||||
},
|
||||
Accolades: {
|
||||
Heirloom: true
|
||||
},
|
||||
Counselor: true
|
||||
}
|
||||
};
|
||||
function setImportSample(key) {
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `Anfangsverstärker`,
|
||||
code_amp: `Verstärker`,
|
||||
code_kDrive: `K-Drive`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Legendärer Kern`,
|
||||
code_traumaticPeculiar: `Kuriose Mod: Traumatisch`,
|
||||
code_starter: `|MOD| (Defekt)`,
|
||||
@@ -80,7 +83,7 @@ dict = {
|
||||
code_drifterFaceName: `Drifter-Gesicht: |INDEX|`,
|
||||
code_operatorFaceName: `Operator-Gesicht: |INDEX|`,
|
||||
code_reviveBooster: `Wiederbelebungsbooster`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `Erfolgreich geändert.`,
|
||||
code_requiredInvigorationUpgrade: `Du musst sowohl ein Offensiv- als auch ein Support-Upgrade auswählen.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `Server`,
|
||||
cheats_skipTutorial: `Tutorial überspringen`,
|
||||
cheats_skipAllDialogue: `Alle Dialoge überspringen`,
|
||||
cheats_skipAllPopups: `Alle Popups überspringen`,
|
||||
cheats_unlockAllScans: `Alle Scans freischalten`,
|
||||
cheats_unlockSuccRelog: `Erfolgreich. Bitte beachte, dass du dich neu anmelden musst, damit der Client dies aktualisiert.`,
|
||||
cheats_unlockAllMissions: `Alle Missionen freischalten`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `Absenden`,
|
||||
import_samples: `Beispiele:`,
|
||||
import_samples_maxFocus: `Alle Fokus-Schulen maximiert`,
|
||||
import_samples_accolades: `Auszeichnungen & Council Chat Zugang`,
|
||||
|
||||
upgrade_Equilibrium: `+|VAL|% Energie bei Gesundheitskugeln, +|VAL|% Gesundheit bei Energiekugeln`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% Krit. Nahkampfschaden`,
|
||||
|
||||
@@ -22,6 +22,9 @@ dict = {
|
||||
code_moteAmp: `Mote Amp`,
|
||||
code_amp: `Amp`,
|
||||
code_kDrive: `K-Drive`,
|
||||
code_fusionCoreCommon: `Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Legendary Core`,
|
||||
code_traumaticPeculiar: `Traumatic Peculiar`,
|
||||
code_starter: `|MOD| (Flawed)`,
|
||||
@@ -79,7 +82,7 @@ dict = {
|
||||
code_drifterFaceName: `Drifter Visage |INDEX|`,
|
||||
code_operatorFaceName: `Operator Visage |INDEX|`,
|
||||
code_reviveBooster: `Revive Booster`,
|
||||
code_reviveBoosterDesc: `Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `Successfully changed.`,
|
||||
code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`,
|
||||
code_feature_1: `Orokin Reactor`,
|
||||
@@ -211,6 +214,7 @@ dict = {
|
||||
cheats_server: `Server`,
|
||||
cheats_skipTutorial: `Skip Tutorial`,
|
||||
cheats_skipAllDialogue: `Skip All Dialogue`,
|
||||
cheats_skipAllPopups: `Skip All Popups`,
|
||||
cheats_unlockAllScans: `Unlock All Scans`,
|
||||
cheats_unlockSuccRelog: `Success. Please note that you'll need to relog for the client to refresh this.`,
|
||||
cheats_unlockAllMissions: `Unlock All Missions`,
|
||||
@@ -369,6 +373,7 @@ dict = {
|
||||
import_submit: `Submit`,
|
||||
import_samples: `Samples:`,
|
||||
import_samples_maxFocus: `All Focus Schools Maxed Out`,
|
||||
import_samples_accolades: `Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `+|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% Melee Critical Damage`,
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `Amp Mota`,
|
||||
code_amp: `Amp`,
|
||||
code_kDrive: `K-Drive`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Núcleo legendario`,
|
||||
code_traumaticPeculiar: `Traumatismo peculiar`,
|
||||
code_starter: `|MOD| (Defectuoso)`,
|
||||
@@ -80,7 +83,7 @@ dict = {
|
||||
code_drifterFaceName: `Rostro del Viajero |INDEX|`,
|
||||
code_operatorFaceName: `Rostro del operador |INDEX|`,
|
||||
code_reviveBooster: `Potenciador de reanimaciones`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `Cambiado correctamente`,
|
||||
code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `Servidor`,
|
||||
cheats_skipTutorial: `Omitir tutorial`,
|
||||
cheats_skipAllDialogue: `Omitir todos los diálogos`,
|
||||
cheats_skipAllPopups: `[UNTRANSLATED] Skip All Popups`,
|
||||
cheats_unlockAllScans: `Desbloquear todos los escaneos`,
|
||||
cheats_unlockSuccRelog: `Éxito. Ten en cuenta que deberás volver a iniciar sesión para que el cliente se actualice.`,
|
||||
cheats_unlockAllMissions: `Desbloquear todas las misiones`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `Enviar`,
|
||||
import_samples: `Muestras:`,
|
||||
import_samples_maxFocus: `Todas las escuelas de enfoque al máximo`,
|
||||
import_samples_accolades: `[UNTRANSLATED] Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `+|VAL|% de Energía al recoger salud, +|VAL|% de Salud al recoger energía`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% de daño crítico cuerpo a cuerpo`,
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `Amplificateur Faible`,
|
||||
code_amp: `Amplificateur`,
|
||||
code_kDrive: `K-Drive`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Coeur Légendaire`,
|
||||
code_traumaticPeculiar: `Traumatisme Atypique`,
|
||||
code_starter: `|MOD| (Défectueux)`,
|
||||
@@ -80,7 +83,7 @@ dict = {
|
||||
code_drifterFaceName: `Visage du Voyageur |INDEX|`,
|
||||
code_operatorFaceName: `Visage de l'Opérateur |INDEX|`,
|
||||
code_reviveBooster: `Booster de Réanimation`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `Changement effectué.`,
|
||||
code_requiredInvigorationUpgrade: `Invigoration offensive et défensive requises.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `Serveur`,
|
||||
cheats_skipTutorial: `Passer le tutoriel`,
|
||||
cheats_skipAllDialogue: `Passer les dialogues`,
|
||||
cheats_skipAllPopups: `[UNTRANSLATED] Skip All Popups`,
|
||||
cheats_unlockAllScans: `Débloquer tous les scans`,
|
||||
cheats_unlockSuccRelog: `Succès. Une reconnexion est requise pour appliquer les changements.`,
|
||||
cheats_unlockAllMissions: `Débloquer toutes les missions`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `Soumettre`,
|
||||
import_samples: `Échantillons :`,
|
||||
import_samples_maxFocus: `Toutes les écoles de focus au rang max`,
|
||||
import_samples_accolades: `[UNTRANSLATED] Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `Ramasser de la santé donne +|VAL|% d'énergie supplémentaire. Ramasser de l'énergie donne +|VAL|% de santé supplémentaire.`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% de dégâts critique en mêlée`,
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `Пылинка`,
|
||||
code_amp: `Усилитель`,
|
||||
code_kDrive: `К-Драйв`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Легендарное ядро`,
|
||||
code_traumaticPeculiar: `Травмирующая Странность`,
|
||||
code_starter: `|MOD| (Повреждённый)`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `Сервер`,
|
||||
cheats_skipTutorial: `Пропустить обучение`,
|
||||
cheats_skipAllDialogue: `Пропустить все диалоги`,
|
||||
cheats_skipAllPopups: `[UNTRANSLATED] Skip All Popups`,
|
||||
cheats_unlockAllScans: `Разблокировать все сканирования`,
|
||||
cheats_unlockSuccRelog: `Успех. Вам необходимо повторно войти в игру, чтобы клиент обновил эту информацию.`,
|
||||
cheats_unlockAllMissions: `Разблокировать все миссии`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `Отправить`,
|
||||
import_samples: `Пример:`,
|
||||
import_samples_maxFocus: `Все школы Фокуса макс. уровня`,
|
||||
import_samples_accolades: `[UNTRANSLATED] Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `Подбор сфер здоровья даёт +|VAL|% энергии. Подбор сфер энергии даёт +|VAL|% здоровья.`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% к крит. урону в ближнем бою.`,
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `Порошинка`,
|
||||
code_amp: `Підсилювач`,
|
||||
code_kDrive: `К-Драйв`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `Легендарне ядро`,
|
||||
code_traumaticPeculiar: `Особливе травмування`,
|
||||
code_starter: `|MOD| (Пошкоджений)`,
|
||||
@@ -80,7 +83,7 @@ dict = {
|
||||
code_drifterFaceName: `Зовнішність мандрівника: |INDEX|`,
|
||||
code_operatorFaceName: `Зовнішність оператора: |INDEX|`,
|
||||
code_reviveBooster: `Збільшувач зцілення`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `Успішно змінено.`,
|
||||
code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `Сервер`,
|
||||
cheats_skipTutorial: `Пропустити навчання`,
|
||||
cheats_skipAllDialogue: `Пропустити всі діалоги`,
|
||||
cheats_skipAllPopups: `[UNTRANSLATED] Skip All Popups`,
|
||||
cheats_unlockAllScans: `Розблокувати всі сканування`,
|
||||
cheats_unlockSuccRelog: `Успіх. Вам потрібно буде повторно увійти в гру, щоб клієнт оновив цю інформацію.`,
|
||||
cheats_unlockAllMissions: `Розблокувати всі місії`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `Відправити`,
|
||||
import_samples: `Приклад:`,
|
||||
import_samples_maxFocus: `Всі школи Фокусу макс. рівня`,
|
||||
import_samples_accolades: `[UNTRANSLATED] Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `Згустки здоров'я дають +|VAL|% енергії, згустки енергії дають +|VAL|% здоров'я.`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% до критичної шкоди від холодної зброї.`,
|
||||
|
||||
@@ -23,6 +23,9 @@ dict = {
|
||||
code_moteAmp: `微尘增幅器`,
|
||||
code_amp: `增幅器`,
|
||||
code_kDrive: `K式悬浮板`,
|
||||
code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`,
|
||||
code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`,
|
||||
code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`,
|
||||
code_legendaryCore: `传奇核心`,
|
||||
code_traumaticPeculiar: `创伤怪奇`,
|
||||
code_starter: `|MOD| (有瑕疵的)`,
|
||||
@@ -80,7 +83,7 @@ dict = {
|
||||
code_drifterFaceName: `漂泊者面部 |INDEX|`,
|
||||
code_operatorFaceName: `指挥官面部 |INDEX|`,
|
||||
code_reviveBooster: `复活加速器`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Set revive count to 4. Disable self-revive restriction on Archon Hunt missions.`,
|
||||
code_reviveBoosterDesc: `[UNTRANSLATED] Sets revive count to 4, which allows self-revive in Archon Hunts.`,
|
||||
code_succChange: `更改成功`,
|
||||
code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
@@ -212,6 +215,7 @@ dict = {
|
||||
cheats_server: `服务器`,
|
||||
cheats_skipTutorial: `跳过教程`,
|
||||
cheats_skipAllDialogue: `跳过所有对话`,
|
||||
cheats_skipAllPopups: `[UNTRANSLATED] Skip All Popups`,
|
||||
cheats_unlockAllScans: `解锁所有扫描`,
|
||||
cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please note that you'll need to relog for the client to refresh this.`,
|
||||
cheats_unlockAllMissions: `解锁所有星图`,
|
||||
@@ -370,6 +374,7 @@ dict = {
|
||||
import_submit: `提交`,
|
||||
import_samples: `示例:`,
|
||||
import_samples_maxFocus: `所有专精学派完全精通`,
|
||||
import_samples_accolades: `[UNTRANSLATED] Accolades & Council Chat Access`,
|
||||
|
||||
upgrade_Equilibrium: `拾取生命球+|VAL|%能量,拾取能量球+|VAL|%生命`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|%近战暴击伤害`,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
nounusedlocals,
|
||||
Reference in New Issue
Block a user