forked from OpenWF/SpaceNinjaServer
Compare commits
143 Commits
config-wri
...
main
Author | SHA1 | Date | |
---|---|---|---|
62a6042c9c | |||
e8d4d84d6e | |||
62881aaa36 | |||
df316e3a7a | |||
264e9cfc98 | |||
5d5554a80e | |||
da14a4081b | |||
b0b68f474a | |||
ab214df1a8 | |||
9f8105d7f1 | |||
c47a29ec96 | |||
6d727c50f4 | |||
bf04755c36 | |||
e345fc35b6 | |||
f5335704b4 | |||
79c5f7a67a | |||
e97b107853 | |||
7bc5065251 | |||
3194a693b3 | |||
261dbd5fdf | |||
fd2ec696a0 | |||
9cc0c76ef5 | |||
2a4488d1dd | |||
2e1326cde8 | |||
70be467cbf | |||
fac3ec01c6 | |||
ebdca760e6 | |||
51c0ddda38 | |||
9129bdb5fc | |||
a4922d4c35 | |||
679752633a | |||
67b5890f39 | |||
5d54e79e5d | |||
4606f28a58 | |||
a2d383ee3c | |||
834b7a8196 | |||
4a2d863c9c | |||
9f0cd91105 | |||
ebfef52fb1 | |||
dd7bacd22e | |||
c00967931e | |||
b15a635e11 | |||
7e618539fa | |||
a29398fae6 | |||
601091f1c0 | |||
f561884f2c | |||
6e1cb0c9f9 | |||
9286627668 | |||
f94f2005d3 | |||
9901b7af54 | |||
2fa846f465 | |||
541ec3d702 | |||
0a28eab65d | |||
8e639a16bd | |||
522924a823 | |||
48e3f324e2 | |||
8f77c722cb | |||
e7287933b5 | |||
b21bca7a6d | |||
d30d450311 | |||
b62e326920 | |||
8b4bc114f6 | |||
564aa06762 | |||
2e84f71af8 | |||
ddfa98e0b2 | |||
bb3c3e01b0 | |||
695dcf98e0 | |||
509f7f0d9b | |||
aada031a80 | |||
a2a441ecb0 | |||
c0a0463a68 | |||
2307a40833 | |||
304af514e2 | |||
ddf3cd49b5 | |||
41e3f0136f | |||
c0ca9d9398 | |||
0f6b55beed | |||
f8550e9afe | |||
b53c4d9125 | |||
922b65cfab | |||
2f642df20a | |||
62314e89c7 | |||
56aa3e3331 | |||
c3f486488f | |||
49c353d895 | |||
90ab560620 | |||
b0e80fcfa8 | |||
2c62fb3c3c | |||
5b215733aa | |||
39866b9a2b | |||
fad1ee9314 | |||
64b43fcccf | |||
e407262cf8 | |||
00e57c43df | |||
2ab9f39507 | |||
b60723ef54 | |||
b3bf291d10 | |||
db86e2d265 | |||
f6cb8414c1 | |||
ba3df4bdbc | |||
8feb3a5b3c | |||
66f3d65d77 | |||
b18f06087b | |||
987b5b98ff | |||
fbbd9076cf | |||
838818543c | |||
a16e2716f1 | |||
f4c7ce582b | |||
c0187f9446 | |||
f796f9a851 | |||
e18b8e09ea | |||
0d8044b87c | |||
a109ea6c5d | |||
7eb95c995c | |||
dc8f32d4d8 | |||
ba70ba88dd | |||
08d4a03c50 | |||
45feff682b | |||
65be1083ce | |||
07e7c9e897 | |||
dcb26471c9 | |||
5a75d88385 | |||
a35572e306 | |||
c46c43f143 | |||
98ed2b5ee4 | |||
7aa1b12306 | |||
b410f6b554 | |||
1dffcf979f | |||
c86bba017b | |||
2c499cec3d | |||
d6145561fd | |||
1545cdb8ce | |||
80b5e2df7f | |||
76e61129bf | |||
ea3e299861 | |||
3d8c1d036a | |||
773f96ebbc | |||
2a80307c26 | |||
a40ff27fea | |||
c9a4359714 | |||
280ed8bef1 | |||
9c89e907b1 | |||
b54fd96098 |
@ -21,7 +21,7 @@
|
||||
"@typescript-eslint/no-unsafe-argument": "error",
|
||||
"@typescript-eslint/no-unsafe-call": "error",
|
||||
"@typescript-eslint/no-unsafe-assignment": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-loss-of-precision": "error",
|
||||
"@typescript-eslint/no-unnecessary-condition": "error",
|
||||
"@typescript-eslint/no-base-to-string": "off",
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
with:
|
||||
node-version: ">=20.6.0"
|
||||
- run: npm ci
|
||||
- run: cp config.json.example config.json
|
||||
- run: cp config-vanilla.json config.json
|
||||
- run: npm run verify
|
||||
- run: npm run lint:ci
|
||||
- run: npm run prettier
|
||||
|
@ -2,3 +2,4 @@ src/routes/api.ts
|
||||
static/webui/libs/
|
||||
*.html
|
||||
*.md
|
||||
config-vanilla.json
|
||||
|
@ -7,5 +7,6 @@ WORKDIR /app
|
||||
|
||||
RUN npm i --omit=dev
|
||||
RUN npm run build
|
||||
RUN date '+%d %B %Y' > BUILD_DATE
|
||||
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
@ -10,7 +10,7 @@ To get an idea of what functionality you can expect to be missing [have a look t
|
||||
|
||||
## config.json
|
||||
|
||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config.json.example](config.json.example), which has most cheats disabled.
|
||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config-vanilla.json](config-vanilla.json), which has most cheats disabled.
|
||||
|
||||
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
|
||||
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
|
||||
|
@ -13,11 +13,6 @@
|
||||
"skipTutorial": false,
|
||||
"skipAllDialogue": false,
|
||||
"unlockAllScans": false,
|
||||
"infiniteCredits": false,
|
||||
"infinitePlatinum": false,
|
||||
"infiniteEndo": false,
|
||||
"infiniteRegalAya": false,
|
||||
"infiniteHelminthMaterials": false,
|
||||
"claimingBlueprintRefundsIngredients": false,
|
||||
"dontSubtractPurchaseCreditCost": false,
|
||||
"dontSubtractPurchasePlatinumCost": false,
|
||||
@ -70,8 +65,24 @@
|
||||
"creditBoost": false,
|
||||
"affinityBoost": false,
|
||||
"resourceBoost": false,
|
||||
"starDays": true,
|
||||
"tennoLiveRelay": false,
|
||||
"wolfHunt": false,
|
||||
"longShadow": false,
|
||||
"hallowedFlame": false,
|
||||
"hallowedNightmares": false,
|
||||
"hallowedNightmaresRewardsOverride": 0,
|
||||
"proxyRebellion": false,
|
||||
"proxyRebellionRewardsOverride": 0,
|
||||
"galleonOfGhouls": 0,
|
||||
"ghoulEmergenceOverride": null,
|
||||
"plagueStarOverride": null,
|
||||
"starDaysOverride": null,
|
||||
"dogDaysOverride": null,
|
||||
"dogDaysRewardsOverride": null,
|
||||
"bellyOfTheBeast": false,
|
||||
"bellyOfTheBeastProgressOverride": 0,
|
||||
"eightClaw": false,
|
||||
"eightClawProgressOverride": 0,
|
||||
"eidolonOverride": "",
|
||||
"vallisOverride": "",
|
||||
"duviriOverride": "",
|
@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
if [ ! -f conf/config.json ]; then
|
||||
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config.json.example > /app/conf/config.json
|
||||
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config-vanilla.json > /app/conf/config.json
|
||||
fi
|
||||
|
||||
exec npm run start -- --configPath conf/config.json
|
||||
|
660
package-lock.json
generated
660
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
"dev": "node scripts/dev.js",
|
||||
"dev:bun": "bun scripts/dev.js",
|
||||
"verify": "tsgo --noEmit",
|
||||
"verify:tsc": "tsc --noEmit",
|
||||
"bun-run": "bun src/index.ts",
|
||||
"lint": "eslint --ext .ts .",
|
||||
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
|
||||
@ -39,7 +40,7 @@
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"undici": "^7.10.0",
|
||||
"warframe-public-export-plus": "^0.5.77",
|
||||
"warframe-public-export-plus": "^0.5.81",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
|
22
src/controllers/api/apartmentController.ts
Normal file
22
src/controllers/api/apartmentController.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const apartmentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRooms = await getPersonalRooms(accountId, "Apartment");
|
||||
const response: IApartmentResponse = {};
|
||||
if (req.query.backdrop !== undefined) {
|
||||
response.NewBackdropItem = personalRooms.Apartment.VideoWallBackdrop = req.query.backdrop as string;
|
||||
}
|
||||
if (req.query.soundscape !== undefined) {
|
||||
response.NewSoundscapeItem = personalRooms.Apartment.Soundscape = req.query.soundscape as string;
|
||||
}
|
||||
await personalRooms.save();
|
||||
res.json(response);
|
||||
};
|
||||
|
||||
interface IApartmentResponse {
|
||||
NewBackdropItem?: string;
|
||||
NewSoundscapeItem?: string;
|
||||
}
|
@ -3,7 +3,6 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { IInventoryClient, IUpgradeClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { addMods, getInventory } from "@/src/services/inventoryService";
|
||||
import { config } from "@/src/services/configService";
|
||||
|
||||
export const artifactsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -34,10 +33,10 @@ export const artifactsController: RequestHandler = async (req, res) => {
|
||||
addMods(inventory, [{ ItemType, ItemCount: -1 }]);
|
||||
}
|
||||
|
||||
if (!config.infiniteCredits) {
|
||||
if (!inventory.infiniteCredits) {
|
||||
inventory.RegularCredits -= Cost;
|
||||
}
|
||||
if (!config.infiniteEndo) {
|
||||
if (!inventory.infiniteEndo) {
|
||||
inventory.FusionPoints -= FusionPointCost;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@ import {
|
||||
addRecipes,
|
||||
occupySlot,
|
||||
combineInventoryChanges,
|
||||
addKubrowPetPrint
|
||||
addKubrowPetPrint,
|
||||
addPowerSuit,
|
||||
addEquipment
|
||||
} from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { InventorySlot, IPendingRecipeDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -22,7 +24,7 @@ import { toOid2 } from "@/src/helpers/inventoryHelpers";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IRecipe } from "warframe-public-export-plus";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { IEquipmentClient, Status } from "@/src/types/equipmentTypes";
|
||||
import { EquipmentFeatures, IEquipmentClient, Status } from "@/src/types/equipmentTypes";
|
||||
|
||||
interface IClaimCompletedRecipeRequest {
|
||||
RecipeIds: IOid[];
|
||||
@ -100,7 +102,10 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
const secondsElapsed = Math.trunc(Date.now() / 1000) - start;
|
||||
const progress = secondsElapsed / recipe.buildTime;
|
||||
logger.debug(`rushing recipe at ${Math.trunc(progress * 100)}% completion`);
|
||||
const cost = Math.round(recipe.skipBuildTimePrice * (1 - (progress - 0.5)));
|
||||
const cost =
|
||||
progress > 0.5
|
||||
? Math.round(recipe.skipBuildTimePrice * (1 - (progress - 0.5)))
|
||||
: recipe.skipBuildTimePrice;
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...updateCurrency(inventory, cost, true)
|
||||
@ -124,17 +129,122 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
|
||||
addKubrowPetPrint(inventory, pet, InventoryChanges);
|
||||
} else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...(await addItem(
|
||||
if (recipe.resultType == "/Lotus/Powersuits/Excalibur/ExcaliburUmbra") {
|
||||
// Quite the special case here...
|
||||
// We don't just get Umbra, but also Skiajati and Umbra Mods. Both items are max rank, potatoed, and with the mods are pre-installed.
|
||||
// Source: https://wiki.warframe.com/w/The_Sacrifice, https://wiki.warframe.com/w/Excalibur/Umbra, https://wiki.warframe.com/w/Skiajati
|
||||
|
||||
const umbraModA = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const umbraModB = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const umbraModC = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const sacrificeModA = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const sacrificeModB = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
InventoryChanges.Upgrades ??= [];
|
||||
InventoryChanges.Upgrades.push(umbraModA, umbraModB, umbraModC, sacrificeModA, sacrificeModB);
|
||||
|
||||
await addPowerSuit(
|
||||
inventory,
|
||||
recipe.resultType,
|
||||
recipe.num,
|
||||
false,
|
||||
undefined,
|
||||
pendingRecipe.TargetFingerprint
|
||||
))
|
||||
};
|
||||
"/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
|
||||
{
|
||||
Configs: [
|
||||
{
|
||||
Upgrades: [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
umbraModA.ItemId.$oid,
|
||||
umbraModB.ItemId.$oid,
|
||||
umbraModC.ItemId.$oid
|
||||
]
|
||||
}
|
||||
],
|
||||
XP: 900_000,
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY
|
||||
},
|
||||
InventoryChanges
|
||||
);
|
||||
inventory.XPInfo.push({
|
||||
ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
|
||||
XP: 900_000
|
||||
});
|
||||
|
||||
addEquipment(
|
||||
inventory,
|
||||
"Melee",
|
||||
"/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
|
||||
{
|
||||
Configs: [
|
||||
{ Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] }
|
||||
],
|
||||
XP: 450_000,
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY
|
||||
},
|
||||
InventoryChanges
|
||||
);
|
||||
inventory.XPInfo.push({
|
||||
ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
|
||||
XP: 450_000
|
||||
});
|
||||
} else {
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...(await addItem(
|
||||
inventory,
|
||||
recipe.resultType,
|
||||
recipe.num,
|
||||
false,
|
||||
undefined,
|
||||
pendingRecipe.TargetFingerprint
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
if (
|
||||
config.claimingBlueprintRefundsIngredients &&
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { checkCalendarChallengeCompletion, getCalendarProgress, getInventory } from "@/src/services/inventoryService";
|
||||
import { checkCalendarAutoAdvance, getCalendarProgress, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { getWorldState } from "@/src/services/worldStateService";
|
||||
@ -28,7 +28,7 @@ export const completeCalendarEventController: RequestHandler = async (req, res)
|
||||
}
|
||||
}
|
||||
calendarProgress.SeasonProgress.LastCompletedDayIdx = dayIndex;
|
||||
checkCalendarChallengeCompletion(calendarProgress, currentSeason);
|
||||
checkCalendarAutoAdvance(inventory, currentSeason);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
|
@ -4,8 +4,7 @@ import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inven
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { createUnveiledRivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||
import { ExportUpgrades } from "warframe-public-export-plus";
|
||||
import { IVeiledRivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||
|
||||
export const completeRandomModChallengeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -27,10 +26,11 @@ export const completeRandomModChallengeController: RequestHandler = async (req,
|
||||
inventoryChanges.MiscItems = miscItemChanges;
|
||||
}
|
||||
|
||||
// Update riven fingerprint to a randomised unveiled state
|
||||
// Complete the riven challenge
|
||||
const upgrade = inventory.Upgrades.id(request.ItemId)!;
|
||||
const meta = ExportUpgrades[upgrade.ItemType];
|
||||
upgrade.UpgradeFingerprint = JSON.stringify(createUnveiledRivenFingerprint(meta));
|
||||
const fp = JSON.parse(upgrade.UpgradeFingerprint!) as IVeiledRivenFingerprint;
|
||||
fp.challenge.Progress = fp.challenge.Required;
|
||||
upgrade.UpgradeFingerprint = JSON.stringify(fp);
|
||||
|
||||
await inventory.save();
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
@ -9,7 +8,7 @@ export const creditsController: RequestHandler = async (req, res) => {
|
||||
getAccountIdForRequest(req),
|
||||
getInventory(
|
||||
req.query.accountId as string,
|
||||
"RegularCredits TradesRemaining PremiumCreditsFree PremiumCredits"
|
||||
"RegularCredits TradesRemaining PremiumCreditsFree PremiumCredits infiniteCredits infinitePlatinum"
|
||||
)
|
||||
])
|
||||
)[1];
|
||||
@ -21,10 +20,10 @@ export const creditsController: RequestHandler = async (req, res) => {
|
||||
PremiumCredits: inventory.PremiumCredits
|
||||
};
|
||||
|
||||
if (config.infiniteCredits) {
|
||||
if (inventory.infiniteCredits) {
|
||||
response.RegularCredits = 999999999;
|
||||
}
|
||||
if (config.infinitePlatinum) {
|
||||
if (inventory.infinitePlatinum) {
|
||||
response.PremiumCreditsFree = 0;
|
||||
response.PremiumCredits = 999999999;
|
||||
}
|
||||
|
@ -88,7 +88,6 @@ export const crewShipFusionController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
superiorItem.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
inventoryChanges[category] = [superiorItem.toJSON() as any];
|
||||
|
||||
await inventory.save();
|
||||
|
@ -3,11 +3,13 @@ import {
|
||||
getGuildForRequestEx,
|
||||
hasAccessToDojo,
|
||||
hasGuildPermission,
|
||||
refundDojoDeco,
|
||||
removeDojoDeco
|
||||
} from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const destroyDojoDecoController: RequestHandler = async (req, res) => {
|
||||
@ -18,9 +20,20 @@ export const destroyDojoDecoController: RequestHandler = async (req, res) => {
|
||||
res.json({ DojoRequestStatus: -1 });
|
||||
return;
|
||||
}
|
||||
const request = JSON.parse(String(req.body)) as IDestroyDojoDecoRequest;
|
||||
|
||||
removeDojoDeco(guild, request.ComponentId, request.DecoId);
|
||||
const request = JSON.parse(String(req.body)) as IDestroyDojoDecoRequest | IClearObstacleCourseRequest;
|
||||
if ("DecoType" in request) {
|
||||
removeDojoDeco(guild, request.ComponentId, request.DecoId);
|
||||
} else if (request.Act == "cObst") {
|
||||
const component = guild.DojoComponents.id(request.ComponentId)!;
|
||||
if (component.Decos) {
|
||||
for (const deco of component.Decos) {
|
||||
refundDojoDeco(guild, component, deco);
|
||||
}
|
||||
component.Decos.splice(0, component.Decos.length);
|
||||
}
|
||||
} else {
|
||||
logger.error(`unhandled destroyDojoDeco request`, request);
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, request.ComponentId));
|
||||
@ -31,3 +44,8 @@ interface IDestroyDojoDecoRequest {
|
||||
ComponentId: string;
|
||||
DecoId: string;
|
||||
}
|
||||
|
||||
interface IClearObstacleCourseRequest {
|
||||
ComponentId: string;
|
||||
Act: "cObst" | "maybesomethingelsewedontknowabout";
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export const dronesController: RequestHandler = async (req, res) => {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
drone.ResourceCount = 1;
|
||||
drone.ResourceCount = droneMeta.binCapacity * droneMeta.capacityMultipliers[resource.Rarity];
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({});
|
||||
|
@ -2,22 +2,14 @@ import { RequestHandler } from "express";
|
||||
import { ExportResources } from "warframe-public-export-plus";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addFusionTreasures, addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||
import { IFusionTreasure, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
interface IFusionTreasureRequest {
|
||||
oldTreasureName: string;
|
||||
newTreasureName: string;
|
||||
}
|
||||
|
||||
const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
|
||||
const arr = name.split("_");
|
||||
return {
|
||||
ItemType: arr[0],
|
||||
Sockets: parseInt(arr[1], 16),
|
||||
ItemCount: count
|
||||
};
|
||||
};
|
||||
|
||||
export const fusionTreasuresController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
|
25
src/controllers/api/getGuildEventScore.ts
Normal file
25
src/controllers/api/getGuildEventScore.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountForRequest } from "@/src/services/loginService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { Guild } from "@/src/models/guildModel";
|
||||
|
||||
export const getGuildEventScoreController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId");
|
||||
const guild = await Guild.findById(inventory.GuildId);
|
||||
const goalId = req.query.goalId as string;
|
||||
if (guild && guild.GoalProgress && goalId) {
|
||||
const goal = guild.GoalProgress.find(x => x.goalId.toString() == goalId);
|
||||
if (goal) {
|
||||
return res.json({
|
||||
Tier: guild.Tier,
|
||||
GoalProgress: {
|
||||
Count: goal.Count,
|
||||
Tag: goal.Tag,
|
||||
_id: { $oid: goal.goalId }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.json({});
|
||||
};
|
62
src/controllers/api/getPastWeeklyChallengesController.ts
Normal file
62
src/controllers/api/getPastWeeklyChallengesController.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { EPOCH, getSeasonChallengePools, getWorldState, pushWeeklyActs } from "@/src/services/worldStateService";
|
||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { ISeasonChallenge } from "@/src/types/worldStateTypes";
|
||||
import { ExportChallenges } from "warframe-public-export-plus";
|
||||
|
||||
export const getPastWeeklyChallengesController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "SeasonChallengeHistory ChallengeProgress");
|
||||
const worldState = getWorldState(undefined);
|
||||
|
||||
if (worldState.SeasonInfo) {
|
||||
const pools = getSeasonChallengePools(worldState.SeasonInfo.AffiliationTag);
|
||||
const nightwaveStartTimestamp = Number(worldState.SeasonInfo.Activation.$date.$numberLong);
|
||||
const nightwaveSeason = worldState.SeasonInfo.Season;
|
||||
const timeMs = worldState.Time * 1000;
|
||||
const completedChallengesIds = new Set<string>();
|
||||
|
||||
inventory.SeasonChallengeHistory.forEach(challengeHistory => {
|
||||
const entryNightwaveSeason = parseInt(challengeHistory.id.slice(0, 4), 10) - 1;
|
||||
if (nightwaveSeason == entryNightwaveSeason) {
|
||||
const meta = Object.entries(ExportChallenges).find(
|
||||
([key]) => key.split("/").pop() === challengeHistory.challenge
|
||||
);
|
||||
if (meta) {
|
||||
const [, challengeMeta] = meta;
|
||||
const challengeProgress = inventory.ChallengeProgress.find(
|
||||
c => c.Name === challengeHistory.challenge
|
||||
);
|
||||
|
||||
if (challengeProgress && challengeProgress.Progress >= (challengeMeta.requiredCount ?? 1)) {
|
||||
completedChallengesIds.add(challengeHistory.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PastWeeklyChallenges: ISeasonChallenge[] = [];
|
||||
|
||||
let week = Math.trunc((timeMs - EPOCH) / unixTimesInMs.week) - 1;
|
||||
|
||||
while (EPOCH + week * unixTimesInMs.week >= nightwaveStartTimestamp && PastWeeklyChallenges.length < 3) {
|
||||
const tempActs: ISeasonChallenge[] = [];
|
||||
pushWeeklyActs(tempActs, pools, week, nightwaveStartTimestamp, nightwaveSeason);
|
||||
|
||||
for (const act of tempActs) {
|
||||
if (!completedChallengesIds.has(act._id.$oid) && PastWeeklyChallenges.length < 3) {
|
||||
if (act.Challenge.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")) {
|
||||
act.Permanent = true;
|
||||
}
|
||||
PastWeeklyChallenges.push(act);
|
||||
}
|
||||
}
|
||||
|
||||
week--;
|
||||
}
|
||||
|
||||
res.json({ PastWeeklyChallenges: PastWeeklyChallenges });
|
||||
}
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -73,4 +74,5 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
InventoryChanges: inventoryChanges,
|
||||
AffiliationMods: affiliationMods
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ export const hubBlessingController: RequestHandler = async (req, res) => {
|
||||
if (req.query.mode == "send") {
|
||||
const inventory = await getInventory(accountId, "BlessingCooldown Boosters");
|
||||
inventory.BlessingCooldown = new Date(Date.now() + 86400000);
|
||||
addBooster(boosterType, 3 * 3600, inventory);
|
||||
addBooster(boosterType, 3 * 3600, inventory); // unfaithful, but current HUB server does not handle broadcasting, so this way users can bless themselves.
|
||||
await inventory.save();
|
||||
|
||||
let token = "";
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
addItems,
|
||||
combineInventoryChanges,
|
||||
getEffectiveAvatarImageType,
|
||||
getInventory
|
||||
getInventory,
|
||||
updateCurrency
|
||||
} from "@/src/services/inventoryService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { ExportFlavour } from "warframe-public-export-plus";
|
||||
@ -100,6 +101,9 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.RegularCredits) {
|
||||
updateCurrency(inventory, -message.RegularCredits, false, inventoryChanges);
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({ InventoryChanges: inventoryChanges });
|
||||
} else if (latestClientMessageId) {
|
||||
|
@ -15,7 +15,6 @@ import { getRecipe } from "@/src/services/itemDataService";
|
||||
import { toMongoDate, version_compare } from "@/src/helpers/inventoryHelpers";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { colorToShard } from "@/src/helpers/shardHelper";
|
||||
import { config } from "@/src/services/configService";
|
||||
import {
|
||||
addInfestedFoundryXP,
|
||||
applyCheatsToInfestedFoundry,
|
||||
@ -73,7 +72,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
|
||||
// consume resources
|
||||
if (!config.infiniteHelminthMaterials) {
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
let type: string;
|
||||
let count: number;
|
||||
if (account.BuildLabel && version_compare(account.BuildLabel, "2025.05.20.10.18") < 0) {
|
||||
@ -99,7 +98,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
await inventory.save();
|
||||
|
||||
const infestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry!;
|
||||
applyCheatsToInfestedFoundry(infestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, infestedFoundry);
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
MiscItems: miscItemChanges,
|
||||
@ -129,13 +128,14 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
case "c": {
|
||||
// consume items
|
||||
|
||||
if (config.infiniteHelminthMaterials) {
|
||||
const inventory = await getInventory(account._id.toString());
|
||||
|
||||
if (inventory.infiniteHelminthMaterials) {
|
||||
res.status(400).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const request = getJSONfromString<IHelminthFeedRequest>(String(req.body));
|
||||
const inventory = await getInventory(account._id.toString());
|
||||
inventory.InfestedFoundry ??= {};
|
||||
inventory.InfestedFoundry.Resources ??= [];
|
||||
|
||||
@ -240,7 +240,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
await inventory.save();
|
||||
const infestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry!;
|
||||
applyCheatsToInfestedFoundry(infestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, infestedFoundry);
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
InfestedFoundry: infestedFoundry
|
||||
@ -254,7 +254,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
const request = getJSONfromString<IHelminthSubsumeRequest>(String(req.body));
|
||||
const inventory = await getInventory(account._id.toString());
|
||||
const recipe = getRecipe(request.Recipe)!;
|
||||
if (!config.infiniteHelminthMaterials) {
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
for (const ingredient of recipe.secretIngredients!) {
|
||||
const resource = inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType);
|
||||
if (resource) {
|
||||
@ -280,7 +280,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
freeUpSlot(inventory, InventorySlot.SUITS);
|
||||
await inventory.save();
|
||||
const infestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry!;
|
||||
applyCheatsToInfestedFoundry(infestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, infestedFoundry);
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
Recipes: recipeChanges,
|
||||
@ -307,7 +307,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
const recipeChanges = handleSubsumeCompletion(inventory);
|
||||
await inventory.save();
|
||||
const infestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry!;
|
||||
applyCheatsToInfestedFoundry(infestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, infestedFoundry);
|
||||
res.json({
|
||||
InventoryChanges: {
|
||||
...currencyChanges,
|
||||
@ -328,7 +328,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
suit.UpgradesExpiry = upgradesExpiry;
|
||||
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00);
|
||||
addRecipes(inventory, recipeChanges);
|
||||
if (!config.infiniteHelminthMaterials) {
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
for (let i = 0; i != request.ResourceTypes.length; ++i) {
|
||||
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == request.ResourceTypes[i])!.Count -=
|
||||
request.ResourceCosts[i];
|
||||
@ -338,7 +338,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
inventory.InfestedFoundry!.InvigorationsApplied += 1;
|
||||
await inventory.save();
|
||||
const infestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry!;
|
||||
applyCheatsToInfestedFoundry(infestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, infestedFoundry);
|
||||
res.json({
|
||||
SuitId: request.SuitId,
|
||||
OffensiveUpgrade: request.OffensiveUpgradeType,
|
||||
|
@ -6,12 +6,21 @@ import allDialogue from "@/static/fixed_responses/allDialogue.json";
|
||||
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
||||
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
|
||||
import {
|
||||
eFaction,
|
||||
ExportCustoms,
|
||||
ExportFlavour,
|
||||
ExportResources,
|
||||
ExportVirtuals,
|
||||
ICountedItem
|
||||
} from "warframe-public-export-plus";
|
||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
||||
import {
|
||||
addEmailItem,
|
||||
addItem,
|
||||
addMiscItems,
|
||||
allDailyAffiliationKeys,
|
||||
checkCalendarAutoAdvance,
|
||||
cleanupInventory,
|
||||
createLibraryDailyTask,
|
||||
getCalendarProgress
|
||||
@ -29,6 +38,8 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { DailyDeal } from "@/src/models/worldStateModel";
|
||||
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
|
||||
import { generateRewardSeed } from "@/src/services/rngService";
|
||||
import { getInvasionByOid, getWorldState } from "@/src/services/worldStateService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
|
||||
export const inventoryController: RequestHandler = async (request, response) => {
|
||||
const account = await getAccountForRequest(request);
|
||||
@ -111,58 +122,61 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.CalendarProgress) {
|
||||
const previousYearIteration = inventory.CalendarProgress.Iteration;
|
||||
getCalendarProgress(inventory); // handle year rollover; the client expects to receive an inventory with an up-to-date CalendarProgress
|
||||
// TODO: Setup CalendarProgress as part of 1999 mission completion?
|
||||
|
||||
// also handle sending of kiss cinematic at year rollover
|
||||
if (
|
||||
inventory.CalendarProgress.Iteration != previousYearIteration &&
|
||||
inventory.DialogueHistory &&
|
||||
inventory.DialogueHistory.Dialogues
|
||||
) {
|
||||
let kalymos = false;
|
||||
for (const { dialogueName, kissEmail } of [
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/ArthurKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/EleanorKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/LettieKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/AmirKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/AoiKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/QuincyKissEmailItem"
|
||||
const previousYearIteration = inventory.CalendarProgress?.Iteration;
|
||||
|
||||
// We need to do the following to ensure the in-game calendar does not break:
|
||||
getCalendarProgress(inventory); // Keep the CalendarProgress up-to-date (at least for the current year iteration) (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2364)
|
||||
checkCalendarAutoAdvance(inventory, getWorldState().KnownCalendarSeasons[0]); // Skip birthday events for characters if we do not have them unlocked yet (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2424)
|
||||
|
||||
// also handle sending of kiss cinematic at year rollover
|
||||
if (
|
||||
inventory.CalendarProgress!.Iteration != previousYearIteration &&
|
||||
inventory.DialogueHistory &&
|
||||
inventory.DialogueHistory.Dialogues
|
||||
) {
|
||||
let kalymos = false;
|
||||
for (const { dialogueName, kissEmail } of [
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/ArthurKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/EleanorKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/LettieKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/AmirKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/AoiKissEmailItem"
|
||||
},
|
||||
{
|
||||
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue",
|
||||
kissEmail: "/Lotus/Types/Items/EmailItems/QuincyKissEmailItem"
|
||||
}
|
||||
]) {
|
||||
const dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == dialogueName);
|
||||
if (dialogue) {
|
||||
if (dialogue.Rank == 7) {
|
||||
await addEmailItem(inventory, kissEmail);
|
||||
kalymos = false;
|
||||
break;
|
||||
}
|
||||
]) {
|
||||
const dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == dialogueName);
|
||||
if (dialogue) {
|
||||
if (dialogue.Rank == 7) {
|
||||
await addEmailItem(inventory, kissEmail);
|
||||
kalymos = false;
|
||||
break;
|
||||
}
|
||||
if (dialogue.Rank == 6) {
|
||||
kalymos = true;
|
||||
}
|
||||
if (dialogue.Rank == 6) {
|
||||
kalymos = true;
|
||||
}
|
||||
}
|
||||
if (kalymos) {
|
||||
await addEmailItem(inventory, "/Lotus/Types/Items/EmailItems/KalymosKissEmailItem");
|
||||
}
|
||||
}
|
||||
if (kalymos) {
|
||||
await addEmailItem(inventory, "/Lotus/Types/Items/EmailItems/KalymosKissEmailItem");
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,6 +195,63 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
//await inventory.save();
|
||||
}
|
||||
|
||||
for (let i = 0; i != inventory.QualifyingInvasions.length; ) {
|
||||
const qi = inventory.QualifyingInvasions[i];
|
||||
const invasion = getInvasionByOid(qi.invasionId.toString());
|
||||
if (!invasion) {
|
||||
logger.debug(`removing QualifyingInvasions entry for unknown invasion: ${qi.invasionId.toString()}`);
|
||||
inventory.QualifyingInvasions.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
if (invasion.Completed) {
|
||||
let factionSidedWith: string | undefined;
|
||||
let battlePay: ICountedItem[] | undefined;
|
||||
if (qi.AttackerScore >= 3) {
|
||||
factionSidedWith = invasion.Faction;
|
||||
battlePay = invasion.AttackerReward.countedItems;
|
||||
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
|
||||
} else if (qi.DefenderScore >= 3) {
|
||||
factionSidedWith = invasion.DefenderFaction;
|
||||
battlePay = invasion.DefenderReward.countedItems;
|
||||
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
|
||||
}
|
||||
if (factionSidedWith) {
|
||||
if (battlePay) {
|
||||
// Decoupling rewards from the inbox message because it may delete itself without being read
|
||||
for (const item of battlePay) {
|
||||
await addItem(inventory, item.ItemType, item.ItemCount);
|
||||
}
|
||||
await createMessage(account._id, [
|
||||
{
|
||||
sndr: eFaction.find(x => x.tag == factionSidedWith)?.name ?? factionSidedWith, // TOVERIFY
|
||||
msg: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageBody`,
|
||||
sub: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageSubject`,
|
||||
countedAtt: battlePay,
|
||||
attVisualOnly: true,
|
||||
icon:
|
||||
factionSidedWith == "FC_GRINEER"
|
||||
? "/Lotus/Interface/Icons/Npcs/EliteRifleLancerAvatar.png" // Source: https://www.reddit.com/r/Warframe/comments/1aj4usx/battle_pay_worth_10_plat/, https://www.youtube.com/watch?v=XhNZ6ai6BOY
|
||||
: "/Lotus/Interface/Icons/Npcs/CrewmanNormal.png", // My best source for this is https://www.youtube.com/watch?v=rxrCCFm73XE around 1:37
|
||||
// TOVERIFY: highPriority?
|
||||
endDate: new Date(Date.now() + 86400_000) // TOVERIFY: This type of inbox message seems to automatically delete itself. We'll just delete it after 24 hours, but it's not clear if this is correct.
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (invasion.Faction != "FC_INFESTATION") {
|
||||
// Sided with grineer -> opposed corpus -> send zanuka (harvester)
|
||||
// Sided with corpus -> opposed grineer -> send g3 (death squad)
|
||||
inventory[factionSidedWith != "FC_GRINEER" ? "DeathSquadable" : "Harvestable"] = true;
|
||||
// TOVERIFY: Should this happen earlier?
|
||||
// TOVERIFY: Should this send an (ephemeral) email?
|
||||
}
|
||||
}
|
||||
logger.debug(`removing QualifyingInvasions entry for completed invasion: ${qi.invasionId.toString()}`);
|
||||
inventory.QualifyingInvasions.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (inventory.LastInventorySync) {
|
||||
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
|
||||
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
|
||||
@ -224,17 +295,17 @@ export const getInventoryResponse = async (
|
||||
};
|
||||
}
|
||||
|
||||
if (config.infiniteCredits) {
|
||||
if (inventory.infiniteCredits) {
|
||||
inventoryResponse.RegularCredits = 999999999;
|
||||
}
|
||||
if (config.infinitePlatinum) {
|
||||
if (inventory.infinitePlatinum) {
|
||||
inventoryResponse.PremiumCreditsFree = 0;
|
||||
inventoryResponse.PremiumCredits = 999999999;
|
||||
}
|
||||
if (config.infiniteEndo) {
|
||||
if (inventory.infiniteEndo) {
|
||||
inventoryResponse.FusionPoints = 999999999;
|
||||
}
|
||||
if (config.infiniteRegalAya) {
|
||||
if (inventory.infiniteRegalAya) {
|
||||
inventoryResponse.PrimeTokens = 999999999;
|
||||
}
|
||||
|
||||
@ -264,6 +335,17 @@ export const getInventoryResponse = async (
|
||||
for (const uniqueName in ExportFlavour) {
|
||||
inventoryResponse.FlavourItems.push({ ItemType: uniqueName });
|
||||
}
|
||||
} else if (config.worldState?.baroTennoConRelay) {
|
||||
[
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2022EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2023EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2024EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2025EarlyAccess"
|
||||
].forEach(uniqueName => {
|
||||
if (!inventoryResponse.FlavourItems.some(x => x.ItemType == uniqueName)) {
|
||||
inventoryResponse.FlavourItems.push({ ItemType: uniqueName });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (config.unlockAllSkins) {
|
||||
@ -368,7 +450,7 @@ export const getInventoryResponse = async (
|
||||
}
|
||||
|
||||
if (inventoryResponse.InfestedFoundry) {
|
||||
applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
|
||||
applyCheatsToInfestedFoundry(inventory, inventoryResponse.InfestedFoundry);
|
||||
}
|
||||
|
||||
// Set 2FA enabled so trading post can be used
|
||||
|
@ -2,7 +2,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory, updateCurrency, updateSlots } from "@/src/services/inventoryService";
|
||||
import { RequestHandler } from "express";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { exhaustive } from "@/src/utils/ts-utils";
|
||||
|
||||
/*
|
||||
loadout slots are additionally purchased slots only
|
||||
@ -22,13 +22,44 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const body = JSON.parse(req.body as string) as IInventorySlotsRequest;
|
||||
|
||||
if (body.Bin != InventorySlot.SUITS && body.Bin != InventorySlot.PVE_LOADOUTS) {
|
||||
logger.warn(`unexpected slot purchase of type ${body.Bin}, account may be overcharged`);
|
||||
let price;
|
||||
let amount;
|
||||
switch (body.Bin) {
|
||||
case InventorySlot.SUITS:
|
||||
case InventorySlot.MECHSUITS:
|
||||
case InventorySlot.PVE_LOADOUTS:
|
||||
case InventorySlot.CREWMEMBERS:
|
||||
price = 20;
|
||||
amount = 1;
|
||||
break;
|
||||
|
||||
case InventorySlot.SPACESUITS:
|
||||
price = 12;
|
||||
amount = 1;
|
||||
break;
|
||||
|
||||
case InventorySlot.WEAPONS:
|
||||
case InventorySlot.SPACEWEAPONS:
|
||||
case InventorySlot.SENTINELS:
|
||||
case InventorySlot.RJ_COMPONENT_AND_ARMAMENTS:
|
||||
case InventorySlot.AMPS:
|
||||
price = 12;
|
||||
amount = 2;
|
||||
break;
|
||||
|
||||
case InventorySlot.RIVENS:
|
||||
price = 60;
|
||||
amount = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
exhaustive(body.Bin);
|
||||
throw new Error(`unexpected slot purchase of type ${body.Bin as string}`);
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const currencyChanges = updateCurrency(inventory, 20, true);
|
||||
updateSlots(inventory, body.Bin, 1, 1);
|
||||
const currencyChanges = updateCurrency(inventory, price, true);
|
||||
updateSlots(inventory, body.Bin, amount, amount);
|
||||
await inventory.save();
|
||||
|
||||
res.json({ InventoryChanges: currencyChanges });
|
||||
|
@ -130,7 +130,7 @@ const createLoginResponse = (
|
||||
resp.Groups = [];
|
||||
}
|
||||
if (version_compare(buildLabel, "2021.04.13.19.58") >= 0) {
|
||||
resp.DTLS = 99;
|
||||
resp.DTLS = 0; // bit 0 enables DTLS. if enabled, additional bits can be set, e.g. bit 2 to enable logging. on live, the value is 99.
|
||||
}
|
||||
if (version_compare(buildLabel, "2022.04.29.12.53") >= 0) {
|
||||
resp.ClientType = account.ClientType;
|
||||
|
@ -6,7 +6,11 @@ import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/mi
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventoryResponse } from "@/src/controllers/api/inventoryController";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||
import {
|
||||
IMissionInventoryUpdateResponse,
|
||||
IMissionInventoryUpdateResponseBackToDryDock,
|
||||
IMissionInventoryUpdateResponseRailjackInterstitial
|
||||
} from "@/src/types/missionTypes";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { generateRewardSeed } from "@/src/services/rngService";
|
||||
|
||||
@ -95,11 +99,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
}
|
||||
await inventory.save();
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel);
|
||||
|
||||
//TODO: figure out when to send inventory. it is needed for many cases.
|
||||
res.json({
|
||||
InventoryJson: JSON.stringify(inventoryResponse),
|
||||
const deltas: IMissionInventoryUpdateResponseRailjackInterstitial = {
|
||||
InventoryChanges: inventoryChanges,
|
||||
MissionRewards,
|
||||
...credits,
|
||||
@ -108,7 +110,25 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
SyndicateXPItemReward,
|
||||
AffiliationMods,
|
||||
ConquestCompletedMissionsCount
|
||||
} satisfies IMissionInventoryUpdateResponse);
|
||||
};
|
||||
if (missionReport.RJ) {
|
||||
logger.debug(`railjack interstitial request, sending only deltas`, deltas);
|
||||
res.json(deltas);
|
||||
} else if (missionReport.RewardInfo) {
|
||||
logger.debug(`classic mission completion, sending everything`);
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel);
|
||||
res.json({
|
||||
InventoryJson: JSON.stringify(inventoryResponse),
|
||||
...deltas
|
||||
} satisfies IMissionInventoryUpdateResponse);
|
||||
} else {
|
||||
logger.debug(`no reward info, assuming this wasn't a mission completion and we should just sync inventory`);
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel);
|
||||
res.json({
|
||||
InventoryJson: JSON.stringify(inventoryResponse)
|
||||
} satisfies IMissionInventoryUpdateResponseBackToDryDock);
|
||||
}
|
||||
|
||||
sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import {
|
||||
getInventory,
|
||||
@ -194,4 +195,5 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
MiscItems: miscItemChanges
|
||||
}
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
} from "@/src/services/inventoryService";
|
||||
import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
|
||||
@ -68,6 +69,7 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
} else {
|
||||
throw new Error(`unknown modularWeaponSale op: ${String(req.query.op)}`);
|
||||
}
|
||||
|
@ -1,25 +1,52 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { addConsumables, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IPlayerSkills } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const playerSkillsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "PlayerSkills");
|
||||
const inventory = await getInventory(accountId, "PlayerSkills Consumables");
|
||||
const request = getJSONfromString<IPlayerSkillsRequest>(String(req.body));
|
||||
|
||||
const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
|
||||
const cost = (request.Pool == "LPP_DRIFTER" ? drifterCosts[oldRank] : 1 << oldRank) * 1000;
|
||||
inventory.PlayerSkills[request.Pool as keyof IPlayerSkills] -= cost;
|
||||
inventory.PlayerSkills[request.Skill as keyof IPlayerSkills]++;
|
||||
await inventory.save();
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (request.Skill == "LPS_COMMAND") {
|
||||
if (inventory.PlayerSkills.LPS_COMMAND == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/Consumable/CrewmateBall",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
}
|
||||
} else if (request.Skill == "LPS_DRIFT_RIDING") {
|
||||
if (inventory.PlayerSkills.LPS_DRIFT_RIDING == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/ErsatzSummon",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
Pool: request.Pool,
|
||||
PoolInc: -cost,
|
||||
Skill: request.Skill,
|
||||
Rank: oldRank + 1
|
||||
Rank: oldRank + 1,
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ export const projectionManagerController: RequestHandler = async (req, res) => {
|
||||
const [era, category, currentQuality] = parseProjection(request.projectionType);
|
||||
const upgradeCost = config.dontSubtractVoidTraces
|
||||
? 0
|
||||
: (request.qualityTag - qualityKeywordToNumber[currentQuality]) * 25;
|
||||
: qualityNumberToCost[request.qualityTag] - qualityNumberToCost[qualityKeywordToNumber[currentQuality]];
|
||||
const newProjectionType = findProjection(era, category, qualityNumberToKeyword[request.qualityTag]);
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -49,6 +49,7 @@ const qualityKeywordToNumber: Record<VoidProjectionQuality, number> = {
|
||||
VPQ_GOLD: 2,
|
||||
VPQ_PLATINUM: 3
|
||||
};
|
||||
const qualityNumberToCost = [0, 25, 50, 100];
|
||||
|
||||
// e.g. "/Lotus/Types/Game/Projections/T2VoidProjectionProteaPrimeDBronze" -> ["Lith", "W5", "VPQ_BRONZE"]
|
||||
const parseProjection = (typeName: string): [string, string, VoidProjectionQuality] => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const releasePetController: RequestHandler = async (req, res) => {
|
||||
@ -19,6 +20,7 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
||||
|
||||
await inventory.save();
|
||||
res.json({ inventoryChanges }); // Not a mistake; it's "inventoryChanges" here.
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
||||
interface IReleasePetRequest {
|
||||
|
5
src/controllers/api/resetQuestProgressController.ts
Normal file
5
src/controllers/api/resetQuestProgressController.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const resetQuestProgressController: RequestHandler = (_req, res) => {
|
||||
res.send("1").end();
|
||||
};
|
@ -9,13 +9,16 @@ import {
|
||||
freeUpSlot,
|
||||
combineInventoryChanges,
|
||||
addCrewShipRawSalvage,
|
||||
addFusionPoints
|
||||
addFusionPoints,
|
||||
addCrewShipFusionPoints,
|
||||
addFusionTreasures
|
||||
} from "@/src/services/inventoryService";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { sendWsBroadcastEx } from "@/src/services/wsService";
|
||||
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
export const sellController: RequestHandler = async (req, res) => {
|
||||
const payload = JSON.parse(String(req.body)) as ISellRequest;
|
||||
@ -26,6 +29,8 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
requiredFields.add("RegularCredits");
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
requiredFields.add("FusionPoints");
|
||||
} else if (payload.SellCurrency == "SC_CrewShipFusionPoints") {
|
||||
requiredFields.add("CrewShipFusionPoints");
|
||||
} else {
|
||||
requiredFields.add("MiscItems");
|
||||
}
|
||||
@ -79,6 +84,8 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
inventory.RegularCredits += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
addFusionPoints(inventory, payload.SellPrice);
|
||||
} else if (payload.SellCurrency == "SC_CrewShipFusionPoints") {
|
||||
addCrewShipFusionPoints(inventory, payload.SellPrice);
|
||||
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -290,12 +297,17 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
]);
|
||||
});
|
||||
}
|
||||
if (payload.Items.FusionTreasures) {
|
||||
payload.Items.FusionTreasures.forEach(sellItem => {
|
||||
addFusionTreasures(inventory, [parseFusionTreasure(sellItem.String, sellItem.Count * -1)]);
|
||||
});
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
sendWsBroadcastEx({ update_inventory: true }, accountId, parseInt(String(req.query.wsid)));
|
||||
};
|
||||
|
||||
interface ISellRequest {
|
||||
@ -322,6 +334,7 @@ interface ISellRequest {
|
||||
CrewMembers?: ISellItem[];
|
||||
CrewShipWeapons?: ISellItem[];
|
||||
CrewShipWeaponSkins?: ISellItem[];
|
||||
FusionTreasures?: ISellItem[];
|
||||
};
|
||||
SellPrice: number;
|
||||
SellCurrency:
|
||||
@ -330,7 +343,8 @@ interface ISellRequest {
|
||||
| "SC_FusionPoints"
|
||||
| "SC_DistillPoints"
|
||||
| "SC_CrewShipFusionPoints"
|
||||
| "SC_Resources";
|
||||
| "SC_Resources"
|
||||
| "somethingelsewemightnotknowabout";
|
||||
buildLabel: string;
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,19 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IPictureFrameInfo, ISetPlacedDecoInfoRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { ISetPlacedDecoInfoRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { handleSetPlacedDecoInfo } from "@/src/services/shipCustomizationsService";
|
||||
|
||||
export const setPlacedDecoInfoController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = JSON.parse(req.body as string) as ISetPlacedDecoInfoRequest;
|
||||
//console.log(JSON.stringify(payload, null, 2));
|
||||
await handleSetPlacedDecoInfo(accountId, payload);
|
||||
res.json({
|
||||
DecoId: payload.DecoId,
|
||||
IsPicture: true,
|
||||
PictureFrameInfo: payload.PictureFrameInfo,
|
||||
BootLocation: payload.BootLocation
|
||||
...payload,
|
||||
IsPicture: !!payload.PictureFrameInfo
|
||||
} satisfies ISetPlacedDecoInfoResponse);
|
||||
};
|
||||
|
||||
interface ISetPlacedDecoInfoResponse {
|
||||
DecoId: string;
|
||||
interface ISetPlacedDecoInfoResponse extends ISetPlacedDecoInfoRequest {
|
||||
IsPicture: boolean;
|
||||
PictureFrameInfo?: IPictureFrameInfo;
|
||||
BootLocation?: string;
|
||||
}
|
||||
|
@ -1,20 +1,17 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IShipDecorationsRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IShipDecorationsRequest, IResetShipDecorationsRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { handleSetShipDecorations } from "@/src/services/shipCustomizationsService";
|
||||
import { handleResetShipDecorations, handleSetShipDecorations } from "@/src/services/shipCustomizationsService";
|
||||
|
||||
export const shipDecorationsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const shipDecorationsRequest = JSON.parse(req.body as string) as IShipDecorationsRequest;
|
||||
|
||||
try {
|
||||
if (req.query.reset == "1") {
|
||||
const request = JSON.parse(req.body as string) as IResetShipDecorationsRequest;
|
||||
const response = await handleResetShipDecorations(accountId, request);
|
||||
res.send(response);
|
||||
} else {
|
||||
const shipDecorationsRequest = JSON.parse(req.body as string) as IShipDecorationsRequest;
|
||||
const placedDecoration = await handleSetShipDecorations(accountId, shipDecorationsRequest);
|
||||
res.send(placedDecoration);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
logger.error(`error in shipDecorationsController: ${error.message}`);
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ import { logger } from "@/src/utils/logger";
|
||||
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
|
||||
const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
|
||||
const account = await getAccountForRequest(req);
|
||||
logger.debug(`challenge report:`, challenges);
|
||||
|
||||
const inventory = await getInventory(
|
||||
account._id.toString(),
|
||||
@ -17,7 +18,7 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
|
||||
);
|
||||
let affiliationMods: IAffiliationMods[] = [];
|
||||
if (challenges.ChallengeProgress) {
|
||||
affiliationMods = addChallenges(
|
||||
affiliationMods = await addChallenges(
|
||||
account,
|
||||
inventory,
|
||||
challenges.ChallengeProgress,
|
||||
|
@ -7,7 +7,6 @@ import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/se
|
||||
import { getRecipeByResult } from "@/src/services/itemDataService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "@/src/services/infestedFoundryService";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { EquipmentFeatures, IEquipmentDatabase } from "@/src/types/equipmentTypes";
|
||||
|
||||
@ -52,7 +51,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
|
||||
const recipe = getRecipeByResult(operation.UpgradeRequirement)!;
|
||||
for (const ingredient of recipe.ingredients) {
|
||||
totalPercentagePointsConsumed += ingredient.ItemCount / 10;
|
||||
if (!config.infiniteHelminthMaterials) {
|
||||
if (!inventory.infiniteHelminthMaterials) {
|
||||
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
|
||||
ingredient.ItemCount;
|
||||
}
|
||||
@ -69,7 +68,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
|
||||
|
||||
inventoryChanges.Recipes = recipeChanges;
|
||||
inventoryChanges.InfestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry;
|
||||
applyCheatsToInfestedFoundry(inventoryChanges.InfestedFoundry!);
|
||||
applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!);
|
||||
} else
|
||||
switch (operation.UpgradeRequirement) {
|
||||
case "/Lotus/Types/Items/MiscItems/OrokinReactor":
|
||||
|
33
src/controllers/custom/abilityOverrideController.ts
Normal file
33
src/controllers/custom/abilityOverrideController.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const abilityOverrideController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IAbilityOverrideRequest;
|
||||
if (request.category === "Suits") {
|
||||
const inventory = await getInventory(accountId, request.category);
|
||||
const item = inventory[request.category].id(request.oid);
|
||||
if (item) {
|
||||
if (request.action == "set") {
|
||||
item.Configs[request.configIndex].AbilityOverride = request.AbilityOverride;
|
||||
} else {
|
||||
item.Configs[request.configIndex].AbilityOverride = undefined;
|
||||
}
|
||||
await inventory.save();
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IAbilityOverrideRequest {
|
||||
category: TEquipmentKey;
|
||||
oid: string;
|
||||
action: "set" | "remove";
|
||||
configIndex: number;
|
||||
AbilityOverride: {
|
||||
Ability: string;
|
||||
Index: number;
|
||||
};
|
||||
}
|
65
src/controllers/custom/changeModularPartsController.ts
Normal file
65
src/controllers/custom/changeModularPartsController.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const changeModularPartsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IUpdateFingerPrintRequest;
|
||||
const inventory = await getInventory(accountId, request.category);
|
||||
const item = inventory[request.category].id(request.oid);
|
||||
if (item) {
|
||||
item.ModularParts = request.modularParts;
|
||||
|
||||
request.modularParts.forEach(part => {
|
||||
const categoryMap = mapping[part];
|
||||
if (categoryMap && categoryMap[request.category]) {
|
||||
item.ItemType = categoryMap[request.category]!;
|
||||
}
|
||||
});
|
||||
await inventory.save();
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IUpdateFingerPrintRequest {
|
||||
category: TEquipmentKey;
|
||||
oid: string;
|
||||
modularParts: string[];
|
||||
}
|
||||
|
||||
const mapping: Partial<Record<string, Partial<Record<TEquipmentKey, string>>>> = {
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelAPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||
},
|
||||
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelEgg/InfModularBarrelEggPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelCPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelDPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||
},
|
||||
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelBeam/InfModularBarrelBeamPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
||||
}
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { config, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||
import { saveConfig } from "@/src/services/configWriterService";
|
||||
import { sendWsBroadcastExcept } from "@/src/services/wsService";
|
||||
import { sendWsBroadcastEx } from "@/src/services/wsService";
|
||||
|
||||
export const getConfigController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
@ -25,7 +25,8 @@ export const setConfigController: RequestHandler = async (req, res) => {
|
||||
const [obj, idx] = configIdToIndexable(id);
|
||||
obj[idx] = value;
|
||||
}
|
||||
sendWsBroadcastExcept(parseInt(String(req.query.wsid)), { config_reloaded: true });
|
||||
sendWsBroadcastEx({ config_reloaded: true }, undefined, parseInt(String(req.query.wsid)));
|
||||
syncConfigWithDatabase();
|
||||
await saveConfig();
|
||||
res.end();
|
||||
} else {
|
||||
@ -37,6 +38,8 @@ const configIdToIndexable = (id: string): [Record<string, boolean | string | num
|
||||
let obj = config as unknown as Record<string, never>;
|
||||
const arr = id.split(".");
|
||||
while (arr.length > 1) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
obj[arr[0]] ??= {} as never;
|
||||
obj = obj[arr[0]];
|
||||
arr.splice(0, 1);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { GuildMember } from "@/src/models/guildModel";
|
||||
import { Leaderboard } from "@/src/models/leaderboardModel";
|
||||
import { deleteGuild } from "@/src/services/guildService";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
|
||||
export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -36,5 +37,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
Ship.deleteMany({ ShipOwnerId: accountId }),
|
||||
Stats.deleteOne({ accountOwnerId: accountId })
|
||||
]);
|
||||
|
||||
sendWsBroadcastTo(accountId, { logged_out: true });
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
export const editSuitInvigorationUpgradeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const { oid, data } = req.body as {
|
||||
oid: string;
|
||||
data?: {
|
||||
DefensiveUpgrade: string;
|
||||
OffensiveUpgrade: string;
|
||||
UpgradesExpiry?: number;
|
||||
};
|
||||
};
|
||||
const inventory = await getInventory(accountId);
|
||||
const suit = inventory.Suits.id(oid)!;
|
||||
if (data) {
|
||||
suit.DefensiveUpgrade = data.DefensiveUpgrade;
|
||||
suit.OffensiveUpgrade = data.OffensiveUpgrade;
|
||||
if (data.UpgradesExpiry) {
|
||||
suit.UpgradesExpiry = new Date(data.UpgradesExpiry);
|
||||
} else {
|
||||
suit.UpgradesExpiry = new Date(Date.now() + DEFAULT_UPGRADE_EXPIRY_MS);
|
||||
}
|
||||
} else {
|
||||
suit.DefensiveUpgrade = undefined;
|
||||
suit.OffensiveUpgrade = undefined;
|
||||
suit.UpgradesExpiry = undefined;
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getDict, getItemName, getString } from "@/src/services/itemDataService";
|
||||
import {
|
||||
ExportAbilities,
|
||||
ExportArcanes,
|
||||
ExportAvionics,
|
||||
ExportBoosters,
|
||||
@ -57,6 +58,7 @@ interface ItemLists {
|
||||
mods: ListedItem[];
|
||||
Boosters: ListedItem[];
|
||||
VarziaOffers: ListedItem[];
|
||||
Abilities: ListedItem[];
|
||||
//circuitGameModes: ListedItem[];
|
||||
}
|
||||
|
||||
@ -94,7 +96,8 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
EvolutionProgress: [],
|
||||
mods: [],
|
||||
Boosters: [],
|
||||
VarziaOffers: []
|
||||
VarziaOffers: [],
|
||||
Abilities: []
|
||||
/*circuitGameModes: [
|
||||
{
|
||||
uniqueName: "Survival",
|
||||
@ -132,6 +135,12 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(item.name, lang),
|
||||
exalted: item.exalted
|
||||
});
|
||||
item.abilities.forEach(ability => {
|
||||
res.Abilities.push({
|
||||
uniqueName: ability.uniqueName,
|
||||
name: getString(ability.name || uniqueName, lang)
|
||||
});
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||
@ -144,18 +153,21 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
|
||||
if (item.partType) {
|
||||
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||
res.ModularParts.push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
partType: item.partType
|
||||
});
|
||||
}
|
||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
if (!uniqueName.split("/")[7]?.startsWith("PvPVariant")) {
|
||||
// not a pvp variant
|
||||
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||
res.ModularParts.push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
partType: item.partType
|
||||
});
|
||||
}
|
||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (item.totalDamage !== 0) {
|
||||
if (
|
||||
@ -348,6 +360,13 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const [uniqueName, ability] of Object.entries(ExportAbilities)) {
|
||||
res.Abilities.push({
|
||||
uniqueName,
|
||||
name: getString(ability.name || uniqueName, lang)
|
||||
});
|
||||
}
|
||||
|
||||
response.json(res);
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { importInventory, importLoadOutPresets } from "@/src/services/importService";
|
||||
import { importInventory, importLoadOutPresets, importPersonalRooms } from "@/src/services/importService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getLoadout } from "@/src/services/loadoutService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IInventoryClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGetShipResponse } from "@/src/types/personalRoomsTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const importController: RequestHandler = async (req, res) => {
|
||||
@ -13,15 +15,21 @@ export const importController: RequestHandler = async (req, res) => {
|
||||
importInventory(inventory, request.inventory);
|
||||
await inventory.save();
|
||||
|
||||
if (request.inventory.LoadOutPresets) {
|
||||
if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) {
|
||||
const loadout = await getLoadout(accountId);
|
||||
importLoadOutPresets(loadout, request.inventory.LoadOutPresets);
|
||||
await loadout.save();
|
||||
}
|
||||
|
||||
if ("Ship" in request.inventory || "Apartment" in request.inventory || "TailorShop" in request.inventory) {
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
importPersonalRooms(personalRooms, request.inventory);
|
||||
await personalRooms.save();
|
||||
}
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IImportRequest {
|
||||
inventory: Partial<IInventoryClient>;
|
||||
inventory: Partial<IInventoryClient> | Partial<IGetShipResponse>;
|
||||
}
|
||||
|
18
src/controllers/custom/setAccountCheatController.ts
Normal file
18
src/controllers/custom/setAccountCheatController.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IAccountCheats } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setAccountCheatController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = req.body as ISetAccountCheatRequest;
|
||||
const inventory = await getInventory(accountId, payload.key);
|
||||
inventory[payload.key] = payload.value;
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface ISetAccountCheatRequest {
|
||||
key: keyof IAccountCheats;
|
||||
value: boolean;
|
||||
}
|
@ -141,7 +141,7 @@ export const getProfileViewingDataGetController: RequestHandler = async (req, re
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
combinedStats[arrayName].push(entry as any);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { IMongoDate, IOid, IOidWithLegacySupport } from "@/src/types/commonTypes";
|
||||
import { Types } from "mongoose";
|
||||
import { TRarity } from "warframe-public-export-plus";
|
||||
import { IFusionTreasure } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export const version_compare = (a: string, b: string): number => {
|
||||
const a_digits = a
|
||||
@ -51,6 +52,20 @@ export const fromMongoDate = (date: IMongoDate): Date => {
|
||||
return new Date(parseInt(date.$date.$numberLong));
|
||||
};
|
||||
|
||||
export const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
|
||||
const arr = name.split("_");
|
||||
return {
|
||||
ItemType: arr[0],
|
||||
Sockets: parseInt(arr[1], 16),
|
||||
ItemCount: count
|
||||
};
|
||||
};
|
||||
|
||||
export type TTraitsPool = Record<
|
||||
"Colors" | "EyeColors" | "FurPatterns" | "BodyTypes" | "Heads" | "Tails",
|
||||
{ type: string; rarity: TRarity }[]
|
||||
>;
|
||||
|
||||
export const kubrowWeights: Record<TRarity, number> = {
|
||||
COMMON: 6,
|
||||
UNCOMMON: 4,
|
||||
@ -65,126 +80,126 @@ export const kubrowFurPatternsWeights: Record<TRarity, number> = {
|
||||
LEGENDARY: 1
|
||||
};
|
||||
|
||||
export const catbrowDetails = {
|
||||
export const catbrowDetails: TTraitsPool = {
|
||||
Colors: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseB", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseC", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseD", rarity: "COMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA", rarity: "COMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseB", rarity: "COMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseC", rarity: "COMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseD", rarity: "COMMON" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryA", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryC", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryD", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryA", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryB", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryC", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryD", rarity: "UNCOMMON" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryA", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryB", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryD", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryA", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryB", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryC", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryD", rarity: "RARE" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsD", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsD", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
EyeColors: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesI", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesJ", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesK", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesL", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesM", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesN", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesD", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesE", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesF", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesG", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesH", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesI", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesJ", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesK", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesL", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesM", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesN", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
FurPatterns: [{ type: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternA", rarity: "COMMON" as TRarity }],
|
||||
FurPatterns: [{ type: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternA", rarity: "COMMON" }],
|
||||
|
||||
BodyTypes: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
Heads: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadD", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadD", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
Tails: [
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailE", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailD", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailE", rarity: "LEGENDARY" }
|
||||
]
|
||||
};
|
||||
|
||||
export const kubrowDetails = {
|
||||
export const kubrowDetails: TTraitsPool = {
|
||||
Colors: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneA", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneC", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneD", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneE", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneF", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneG", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneH", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneA", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneB", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneC", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneD", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneE", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneF", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneG", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneH", rarity: "UNCOMMON" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidA", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidB", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidD", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidE", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidF", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidG", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidH", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidA", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidB", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidC", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidD", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidE", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidF", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidG", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidH", rarity: "RARE" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantH", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantD", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantE", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantF", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantG", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantH", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
EyeColors: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesI", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesA", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesB", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesC", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesD", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesE", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesF", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesG", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesH", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesI", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
FurPatterns: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternB", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternA", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternB", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternA", rarity: "UNCOMMON" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternC", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternD", rarity: "RARE" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternC", rarity: "RARE" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternD", rarity: "RARE" },
|
||||
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternE", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternF", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternE", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternF", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
BodyTypes: [
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetHeavyBodyType", rarity: "LEGENDARY" as TRarity },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetThinBodyType", rarity: "LEGENDARY" as TRarity }
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetRegularBodyType", rarity: "UNCOMMON" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetHeavyBodyType", rarity: "LEGENDARY" },
|
||||
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetThinBodyType", rarity: "LEGENDARY" }
|
||||
],
|
||||
|
||||
Heads: [],
|
||||
|
@ -22,8 +22,11 @@ export interface INemesisManifest {
|
||||
ephemeraTypes?: Record<TInnateDamageTag, string>;
|
||||
firstKillReward: string;
|
||||
firstConvertReward: string;
|
||||
messageTitle: string;
|
||||
messageBody: string;
|
||||
killMessageSubject: string;
|
||||
killMessageBody: string;
|
||||
convertMessageSubject: string;
|
||||
convertMessageBody: string;
|
||||
convertMessageIcon: string;
|
||||
minBuild: string;
|
||||
}
|
||||
|
||||
@ -57,8 +60,11 @@ class KuvaLichManifest implements INemesisManifest {
|
||||
};
|
||||
firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Clan/LichKillerBadgeItem";
|
||||
firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/KuvaLichSigil";
|
||||
messageTitle = "/Lotus/Language/Inbox/VanquishKuvaMsgTitle";
|
||||
messageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
|
||||
killMessageSubject = "/Lotus/Language/Inbox/VanquishKuvaMsgTitle";
|
||||
killMessageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
|
||||
convertMessageSubject = "/Lotus/Language/Kingpins/InboxKuvaConvertedSubject";
|
||||
convertMessageBody = "/Lotus/Language/Kingpins/InboxKuvaConvertedBody";
|
||||
convertMessageIcon = "/Lotus/Interface/Graphics/WorldStatePanel/Grineer.png";
|
||||
minBuild = "2019.10.31.22.42"; // 26.0.0
|
||||
}
|
||||
|
||||
@ -131,8 +137,11 @@ class LawyerManifest implements INemesisManifest {
|
||||
};
|
||||
firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Clan/CorpusLichBadgeItem";
|
||||
firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/CorpusLichSigil";
|
||||
messageTitle = "/Lotus/Language/Inbox/VanquishLawyerMsgTitle";
|
||||
messageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
|
||||
killMessageSubject = "/Lotus/Language/Inbox/VanquishLawyerMsgTitle";
|
||||
killMessageBody = "/Lotus/Language/Inbox/VanquishLichMsgBody";
|
||||
convertMessageSubject = "/Lotus/Language/Kingpins/InboxSisterConvertedSubject";
|
||||
convertMessageBody = "/Lotus/Language/Kingpins/InboxSisterConvertedBody";
|
||||
convertMessageIcon = "/Lotus/Interface/Graphics/WorldStatePanel/Corpus.png";
|
||||
minBuild = "2021.07.05.17.03"; // 30.5.0
|
||||
}
|
||||
|
||||
@ -166,8 +175,11 @@ class InfestedLichManfest implements INemesisManifest {
|
||||
ephemeraChance = 0;
|
||||
firstKillReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichVanquishedSigil";
|
||||
firstConvertReward = "/Lotus/StoreItems/Upgrades/Skins/Sigils/InfLichConvertedSigil";
|
||||
messageTitle = "/Lotus/Language/Inbox/VanquishBandMsgTitle";
|
||||
messageBody = "/Lotus/Language/Inbox/VanquishBandMsgBody";
|
||||
killMessageSubject = "/Lotus/Language/Inbox/VanquishBandMsgTitle";
|
||||
killMessageBody = "/Lotus/Language/Inbox/VanquishBandMsgBody";
|
||||
convertMessageSubject = "/Lotus/Language/Kingpins/InboxBandConvertedSubject";
|
||||
convertMessageBody = "/Lotus/Language/Kingpins/InboxBandConvertedBody";
|
||||
convertMessageIcon = "/Lotus/Interface/Graphics/WorldStatePanel/Infested.png";
|
||||
minBuild = "2025.03.18.09.51"; // 38.5.0
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,17 @@
|
||||
import { slotPurchaseNameToSlotName } from "@/src/services/purchaseService";
|
||||
import { SlotPurchaseName } from "@/src/types/purchaseTypes";
|
||||
import { SlotPurchase, SlotPurchaseName } from "@/src/types/purchaseTypes";
|
||||
|
||||
export const slotPurchaseNameToSlotName: SlotPurchase = {
|
||||
SuitSlotItem: { name: "SuitBin", purchaseQuantity: 1 },
|
||||
TwoSentinelSlotItem: { name: "SentinelBin", purchaseQuantity: 2 },
|
||||
TwoWeaponSlotItem: { name: "WeaponBin", purchaseQuantity: 2 },
|
||||
SpaceSuitSlotItem: { name: "SpaceSuitBin", purchaseQuantity: 1 },
|
||||
TwoSpaceWeaponSlotItem: { name: "SpaceWeaponBin", purchaseQuantity: 2 },
|
||||
MechSlotItem: { name: "MechBin", purchaseQuantity: 1 },
|
||||
TwoOperatorWeaponSlotItem: { name: "OperatorAmpBin", purchaseQuantity: 2 },
|
||||
RandomModSlotItem: { name: "RandomModBin", purchaseQuantity: 3 },
|
||||
TwoCrewShipSalvageSlotItem: { name: "CrewShipSalvageBin", purchaseQuantity: 2 },
|
||||
CrewMemberSlotItem: { name: "CrewMemberBin", purchaseQuantity: 1 }
|
||||
};
|
||||
|
||||
export const isSlotPurchaseName = (slotPurchaseName: string): slotPurchaseName is SlotPurchaseName => {
|
||||
return slotPurchaseName in slotPurchaseNameToSlotName;
|
||||
|
@ -23,10 +23,16 @@ export const crackRelic = async (
|
||||
weights = { COMMON: 0, UNCOMMON: 0, RARE: 1, LEGENDARY: 0 };
|
||||
}
|
||||
logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
|
||||
const reward = getRandomWeightedReward(
|
||||
let reward = getRandomWeightedReward(
|
||||
ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
|
||||
weights
|
||||
)!;
|
||||
if (config.relicRewardItemCountMultiplier !== undefined && (config.relicRewardItemCountMultiplier ?? 1) != 1) {
|
||||
reward = {
|
||||
...reward,
|
||||
itemCount: reward.itemCount * config.relicRewardItemCountMultiplier
|
||||
};
|
||||
}
|
||||
logger.debug(`relic rolled`, reward);
|
||||
participant.Reward = reward.type;
|
||||
|
||||
@ -43,13 +49,7 @@ export const crackRelic = async (
|
||||
// Give reward
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
(
|
||||
await handleStoreItemAcquisition(
|
||||
reward.type,
|
||||
inventory,
|
||||
reward.itemCount * (config.relicRewardItemCountMultiplier ?? 1)
|
||||
)
|
||||
).InventoryChanges
|
||||
(await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount)).InventoryChanges
|
||||
);
|
||||
|
||||
return reward;
|
||||
|
18
src/index.ts
18
src/index.ts
@ -1,5 +1,5 @@
|
||||
// First, init config.
|
||||
import { config, configPath, loadConfig } from "@/src/services/configService";
|
||||
import { config, configPath, loadConfig, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import fs from "fs";
|
||||
try {
|
||||
loadConfig();
|
||||
@ -7,7 +7,7 @@ try {
|
||||
if (fs.existsSync("config.json")) {
|
||||
console.log("Failed to load " + configPath + ": " + (e as Error).message);
|
||||
} else {
|
||||
console.log("Failed to load " + configPath + ". You can copy config.json.example to create your config file.");
|
||||
console.log("Failed to load " + configPath + ". You can copy config-vanilla.json to create your config file.");
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@ -18,17 +18,23 @@ logger.info("Starting up...");
|
||||
|
||||
// Proceed with normal startup: bring up config watcher service, validate config, connect to MongoDB, and finally start listening for HTTP.
|
||||
import mongoose from "mongoose";
|
||||
import path from "path";
|
||||
import { JSONStringify } from "json-with-bigint";
|
||||
import { startWebServer } from "@/src/services/webService";
|
||||
|
||||
import { syncConfigWithDatabase, validateConfig } from "@/src/services/configWatcherService";
|
||||
import { validateConfig } from "@/src/services/configWatcherService";
|
||||
import { updateWorldStateCollections } from "@/src/services/worldStateService";
|
||||
import { repoDir } from "@/src/helpers/pathHelper";
|
||||
|
||||
// Patch JSON.stringify to work flawlessly with Bigints.
|
||||
JSON.stringify = JSONStringify;
|
||||
JSON.stringify = JSONStringify; // Patch JSON.stringify to work flawlessly with Bigints.
|
||||
|
||||
validateConfig();
|
||||
|
||||
fs.readFile(path.join(repoDir, "BUILD_DATE"), "utf-8", (err, data) => {
|
||||
if (!err) {
|
||||
logger.info(`Docker image was built on ${data.trim()}`);
|
||||
}
|
||||
});
|
||||
|
||||
mongoose
|
||||
.connect(config.mongodbUrl)
|
||||
.then(() => {
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { logError } from "@/src/utils/logger";
|
||||
|
||||
export const errorHandler = (err: Error, req: Request, res: Response, _next: NextFunction): void => {
|
||||
if (err.message == "Invalid accountId-nonce pair") {
|
||||
res.status(400).send("Log-in expired");
|
||||
} else if (err.stack) {
|
||||
const stackArr = err.stack.split("\n");
|
||||
stackArr[0] += ` while processing ${req.path} request`;
|
||||
logger.error(stackArr.join("\n"));
|
||||
res.status(500).end();
|
||||
} else {
|
||||
logger.error(`uncaught error while processing ${req.path} request: ${err.message}`);
|
||||
logError(err, `processing ${req.path} request`);
|
||||
res.status(500).end();
|
||||
}
|
||||
};
|
||||
|
@ -19,6 +19,8 @@ import {
|
||||
import { Document, Model, model, Schema, Types } from "mongoose";
|
||||
import { fusionTreasuresSchema, typeCountSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { pictureFrameInfoSchema } from "@/src/models/personalRoomsModel";
|
||||
import { IGoalProgressClient, IGoalProgressDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
|
||||
Type: String,
|
||||
@ -174,6 +176,28 @@ const guildLogEntryNumberSchema = new Schema<IGuildLogEntryNumber>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const goalProgressSchema = new Schema<IGoalProgressDatabase>(
|
||||
{
|
||||
Count: Number,
|
||||
Tag: String,
|
||||
goalId: Types.ObjectId
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
goalProgressSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IGoalProgressDatabase;
|
||||
const client = obj as IGoalProgressClient;
|
||||
|
||||
client._id = toOid(db.goalId);
|
||||
|
||||
delete obj.goalId;
|
||||
delete obj.__v;
|
||||
}
|
||||
});
|
||||
|
||||
const guildSchema = new Schema<IGuildDatabase>(
|
||||
{
|
||||
Name: { type: String, required: true, unique: true },
|
||||
@ -206,7 +230,8 @@ const guildSchema = new Schema<IGuildDatabase>(
|
||||
RoomChanges: { type: [guildLogRoomChangeSchema], default: undefined },
|
||||
TechChanges: { type: [guildLogEntryContributableSchema], default: undefined },
|
||||
RosterActivity: { type: [guildLogEntryRosterSchema], default: undefined },
|
||||
ClassChanges: { type: [guildLogEntryNumberSchema], default: undefined }
|
||||
ClassChanges: { type: [guildLogEntryNumberSchema], default: undefined },
|
||||
GoalProgress: { type: [goalProgressSchema], default: undefined }
|
||||
},
|
||||
{ id: false }
|
||||
);
|
||||
|
@ -4,8 +4,12 @@ import { typeCountSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IMongoDate, IOid, ITypeCount } from "@/src/types/commonTypes";
|
||||
|
||||
export interface IMessageClient
|
||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"> {
|
||||
extends Omit<
|
||||
IMessageDatabase,
|
||||
"_id" | "globaUpgradeId" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"
|
||||
> {
|
||||
_id?: IOid;
|
||||
globaUpgradeId?: IOid; // [sic]
|
||||
date: IMongoDate;
|
||||
startDate?: IMongoDate;
|
||||
endDate?: IMongoDate;
|
||||
@ -14,6 +18,7 @@ export interface IMessageClient
|
||||
|
||||
export interface IMessageDatabase extends IMessage {
|
||||
ownerId: Types.ObjectId;
|
||||
globaUpgradeId?: Types.ObjectId; // [sic]
|
||||
date: Date; //created at
|
||||
attVisualOnly?: boolean;
|
||||
_id: Types.ObjectId;
|
||||
@ -42,6 +47,7 @@ export interface IMessage {
|
||||
acceptAction?: string;
|
||||
declineAction?: string;
|
||||
hasAccountAction?: boolean;
|
||||
RegularCredits?: number;
|
||||
}
|
||||
|
||||
export interface Arg {
|
||||
@ -101,6 +107,7 @@ const giftSchema = new Schema<IGift>(
|
||||
const messageSchema = new Schema<IMessageDatabase>(
|
||||
{
|
||||
ownerId: Schema.Types.ObjectId,
|
||||
globaUpgradeId: Schema.Types.ObjectId,
|
||||
sndr: String,
|
||||
msg: String,
|
||||
cinematic: String,
|
||||
@ -133,7 +140,8 @@ const messageSchema = new Schema<IMessageDatabase>(
|
||||
contextInfo: String,
|
||||
acceptAction: String,
|
||||
declineAction: String,
|
||||
hasAccountAction: Boolean
|
||||
hasAccountAction: Boolean,
|
||||
RegularCredits: Number
|
||||
},
|
||||
{ id: false }
|
||||
);
|
||||
@ -144,7 +152,7 @@ messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
|
||||
|
||||
messageSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const messageDatabase = returnedObject as IMessageDatabase;
|
||||
const messageClient = returnedObject as IMessageClient;
|
||||
|
||||
@ -154,6 +162,10 @@ messageSchema.set("toJSON", {
|
||||
delete returnedObject.attVisualOnly;
|
||||
delete returnedObject.expiry;
|
||||
|
||||
if (messageDatabase.globaUpgradeId) {
|
||||
messageClient.globaUpgradeId = toOid(messageDatabase.globaUpgradeId);
|
||||
}
|
||||
|
||||
messageClient.date = toMongoDate(messageDatabase.date);
|
||||
|
||||
if (messageDatabase.startDate && messageDatabase.endDate) {
|
||||
|
@ -85,8 +85,8 @@ import {
|
||||
IAccolades,
|
||||
IHubNpcCustomization,
|
||||
IEndlessXpReward,
|
||||
IPersonalGoalProgressDatabase,
|
||||
IPersonalGoalProgressClient,
|
||||
IGoalProgressDatabase,
|
||||
IGoalProgressClient,
|
||||
IKubrowPetPrintClient,
|
||||
IKubrowPetPrintDatabase
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -121,7 +121,7 @@ import {
|
||||
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
||||
|
||||
typeCountSchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
if (obj.ItemCount > 2147483647) {
|
||||
obj.ItemCount = 2147483647;
|
||||
} else if (obj.ItemCount < -2147483648) {
|
||||
@ -189,7 +189,7 @@ operatorConfigSchema.virtual("ItemId").get(function () {
|
||||
|
||||
operatorConfigSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -226,7 +226,7 @@ const ItemConfigSchema = new Schema<IItemConfig>(
|
||||
);
|
||||
|
||||
ItemConfigSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
});
|
||||
@ -261,7 +261,7 @@ RawUpgrades.virtual("LastAdded").get(function () {
|
||||
|
||||
RawUpgrades.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -282,7 +282,7 @@ upgradeSchema.virtual("ItemId").get(function () {
|
||||
|
||||
upgradeSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -325,7 +325,7 @@ const crewMemberSchema = new Schema<ICrewMemberDatabase>(
|
||||
|
||||
crewMemberSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ICrewMemberDatabase;
|
||||
const client = obj as ICrewMemberClient;
|
||||
|
||||
@ -353,7 +353,7 @@ const FlavourItemSchema = new Schema(
|
||||
);
|
||||
|
||||
FlavourItemSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -367,7 +367,7 @@ FlavourItemSchema.set("toJSON", {
|
||||
);
|
||||
|
||||
MailboxSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const mailboxDatabase = returnedObject as HydratedDocument<IMailboxDatabase, { __v?: number }>;
|
||||
delete mailboxDatabase.__v;
|
||||
(returnedObject as IMailboxClient).LastInboxId = toOid(mailboxDatabase.LastInboxId);
|
||||
@ -386,7 +386,7 @@ const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||
);
|
||||
|
||||
DuviriInfoSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
});
|
||||
@ -416,7 +416,7 @@ const droneSchema = new Schema<IDroneDatabase>(
|
||||
);
|
||||
droneSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, obj) {
|
||||
transform(_document, obj: Record<string, any>) {
|
||||
const client = obj as IDroneClient;
|
||||
const db = obj as IDroneDatabase;
|
||||
|
||||
@ -445,7 +445,7 @@ const discoveredMarkerSchema = new Schema<IDiscoveredMarker>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const personalGoalProgressSchema = new Schema<IPersonalGoalProgressDatabase>(
|
||||
const personalGoalProgressSchema = new Schema<IGoalProgressDatabase>(
|
||||
{
|
||||
Best: Number,
|
||||
Count: Number,
|
||||
@ -457,9 +457,9 @@ const personalGoalProgressSchema = new Schema<IPersonalGoalProgressDatabase>(
|
||||
|
||||
personalGoalProgressSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
const db = obj as IPersonalGoalProgressDatabase;
|
||||
const client = obj as IPersonalGoalProgressClient;
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IGoalProgressDatabase;
|
||||
const client = obj as IGoalProgressClient;
|
||||
|
||||
client._id = toOid(db.goalId);
|
||||
|
||||
@ -502,7 +502,7 @@ StepSequencersSchema.virtual("ItemId").get(function () {
|
||||
|
||||
StepSequencersSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -516,7 +516,7 @@ const kubrowPetEggSchema = new Schema<IKubrowPetEggDatabase>(
|
||||
);
|
||||
kubrowPetEggSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, obj) {
|
||||
transform(_document, obj: Record<string, any>) {
|
||||
const client = obj as IKubrowPetEggClient;
|
||||
const db = obj as IKubrowPetEggDatabase;
|
||||
|
||||
@ -586,7 +586,7 @@ personalTechProjectSchema.virtual("ItemId").get(function () {
|
||||
|
||||
personalTechProjectSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
|
||||
@ -687,7 +687,7 @@ const questKeysSchema = new Schema<IQuestKeyDatabase>(
|
||||
);
|
||||
|
||||
questKeysSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const questKeysDatabase = ret as IQuestKeyDatabase;
|
||||
|
||||
if (questKeysDatabase.CompletionDate) {
|
||||
@ -709,7 +709,7 @@ const invasionProgressSchema = new Schema<IInvasionProgressDatabase>(
|
||||
);
|
||||
|
||||
invasionProgressSchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IInvasionProgressDatabase;
|
||||
const client = obj as IInvasionProgressClient;
|
||||
|
||||
@ -748,7 +748,7 @@ weaponSkinsSchema.virtual("ItemId").get(function () {
|
||||
|
||||
weaponSkinsSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
}
|
||||
@ -772,7 +772,7 @@ const periodicMissionCompletionsSchema = new Schema<IPeriodicMissionCompletionDa
|
||||
);
|
||||
|
||||
periodicMissionCompletionsSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const periodicMissionCompletionDatabase = ret as IPeriodicMissionCompletionDatabase;
|
||||
|
||||
(periodicMissionCompletionDatabase as unknown as IPeriodicMissionCompletionResponse).date = toMongoDate(
|
||||
@ -849,7 +849,7 @@ const endlessXpProgressSchema = new Schema<IEndlessXpProgressDatabase>(
|
||||
);
|
||||
|
||||
endlessXpProgressSchema.set("toJSON", {
|
||||
transform(_doc, ret) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const db = ret as IEndlessXpProgressDatabase;
|
||||
const client = ret as IEndlessXpProgressClient;
|
||||
|
||||
@ -898,7 +898,7 @@ const crewShipMemberSchema = new Schema<ICrewShipMemberDatabase>(
|
||||
);
|
||||
crewShipMemberSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ICrewShipMemberDatabase;
|
||||
const client = obj as ICrewShipMemberClient;
|
||||
if (db.ItemId) {
|
||||
@ -951,7 +951,7 @@ const dialogueSchema = new Schema<IDialogueDatabase>(
|
||||
);
|
||||
dialogueSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const db = ret as IDialogueDatabase;
|
||||
const client = ret as IDialogueClient;
|
||||
|
||||
@ -997,7 +997,7 @@ const kubrowPetPrintSchema = new Schema<IKubrowPetPrintDatabase>({
|
||||
});
|
||||
kubrowPetPrintSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IKubrowPetPrintDatabase;
|
||||
const client = obj as IKubrowPetPrintClient;
|
||||
|
||||
@ -1025,7 +1025,7 @@ const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
|
||||
);
|
||||
|
||||
detailsSchema.set("toJSON", {
|
||||
transform(_doc, returnedObject) {
|
||||
transform(_doc, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
|
||||
const db = returnedObject as IKubrowPetDetailsDatabase;
|
||||
@ -1081,7 +1081,7 @@ EquipmentSchema.virtual("ItemId").get(function () {
|
||||
|
||||
EquipmentSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
|
||||
@ -1132,7 +1132,7 @@ pendingRecipeSchema.virtual("ItemId").get(function () {
|
||||
|
||||
pendingRecipeSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.LongGuns;
|
||||
@ -1170,7 +1170,7 @@ const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
|
||||
);
|
||||
|
||||
infestedFoundrySchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
if (ret.AbilityOverrideUnlockCooldown) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
ret.AbilityOverrideUnlockCooldown = toMongoDate(ret.AbilityOverrideUnlockCooldown);
|
||||
@ -1216,8 +1216,8 @@ const calenderProgressSchema = new Schema<ICalendarProgress>(
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: { type: String, required: true },
|
||||
LastCompletedDayIdx: { type: Number, default: 0 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: 0 },
|
||||
LastCompletedDayIdx: { type: Number, default: -1 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: -1 },
|
||||
ActivatedChallenges: { type: [String], default: [] }
|
||||
}
|
||||
},
|
||||
@ -1243,7 +1243,7 @@ const vendorPurchaseHistoryEntrySchema = new Schema<IVendorPurchaseHistoryEntryD
|
||||
);
|
||||
|
||||
vendorPurchaseHistoryEntrySchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IVendorPurchaseHistoryEntryDatabase;
|
||||
const client = obj as IVendorPurchaseHistoryEntryClient;
|
||||
client.Expiry = toMongoDate(db.Expiry);
|
||||
@ -1286,7 +1286,7 @@ const pendingCouponSchema = new Schema<IPendingCouponDatabase>(
|
||||
);
|
||||
|
||||
pendingCouponSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
(ret as IPendingCouponClient).Expiry = toMongoDate((ret as IPendingCouponDatabase).Expiry);
|
||||
}
|
||||
});
|
||||
@ -1353,7 +1353,7 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
||||
|
||||
nemesisSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as INemesisDatabase;
|
||||
const client = obj as INemesisClient;
|
||||
|
||||
@ -1383,7 +1383,7 @@ const lastSortieRewardSchema = new Schema<ILastSortieRewardDatabase>(
|
||||
|
||||
lastSortieRewardSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ILastSortieRewardDatabase;
|
||||
const client = obj as ILastSortieRewardClient;
|
||||
|
||||
@ -1425,6 +1425,14 @@ const hubNpcCustomizationSchema = new Schema<IHubNpcCustomization>(
|
||||
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
{
|
||||
accountOwnerId: Schema.Types.ObjectId,
|
||||
|
||||
// SNS account cheats
|
||||
infiniteCredits: Boolean,
|
||||
infinitePlatinum: Boolean,
|
||||
infiniteEndo: Boolean,
|
||||
infiniteRegalAya: Boolean,
|
||||
infiniteHelminthMaterials: Boolean,
|
||||
|
||||
SubscribedToEmails: { type: Number, default: 0 },
|
||||
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
|
||||
RewardSeed: BigInt,
|
||||
@ -1437,6 +1445,8 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
PremiumCreditsFree: { type: Number, default: 0 },
|
||||
//Endo
|
||||
FusionPoints: { type: Number, default: 0 },
|
||||
//Dirac
|
||||
CrewShipFusionPoints: { type: Number, default: 0 },
|
||||
//Regal Aya
|
||||
PrimeTokens: { type: Number, default: 0 },
|
||||
|
||||
@ -1790,7 +1800,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
);
|
||||
|
||||
inventorySchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.accountOwnerId;
|
||||
|
@ -25,7 +25,7 @@ export const EquipmentSelectionSchema = new Schema<IEquipmentSelection>(
|
||||
}
|
||||
);
|
||||
|
||||
const loadoutConfigSchema = new Schema<ILoadoutConfigDatabase>(
|
||||
export const loadoutConfigSchema = new Schema<ILoadoutConfigDatabase>(
|
||||
{
|
||||
FocusSchool: String,
|
||||
PresetIcon: String,
|
||||
@ -49,7 +49,7 @@ loadoutConfigSchema.virtual("ItemId").get(function () {
|
||||
|
||||
loadoutConfigSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
}
|
||||
@ -62,6 +62,7 @@ export const loadoutSchema = new Schema<ILoadoutDatabase, loadoutModelType>({
|
||||
NORMAL_PVP: [loadoutConfigSchema],
|
||||
LUNARO: [loadoutConfigSchema],
|
||||
OPERATOR: [loadoutConfigSchema],
|
||||
GEAR: [loadoutConfigSchema],
|
||||
KDRIVE: [loadoutConfigSchema],
|
||||
DATAKNIFE: [loadoutConfigSchema],
|
||||
MECH: [loadoutConfigSchema],
|
||||
@ -71,7 +72,7 @@ export const loadoutSchema = new Schema<ILoadoutDatabase, loadoutModelType>({
|
||||
});
|
||||
|
||||
loadoutSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
delete ret.loadoutOwnerId;
|
||||
@ -88,6 +89,7 @@ type loadoutDocumentProps = {
|
||||
NORMAL_PVP: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
LUNARO: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
OPERATOR: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
GEAR: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
KDRIVE: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
DATAKNIFE: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
MECH: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
|
@ -32,7 +32,7 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
|
||||
);
|
||||
|
||||
databaseAccountSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import {
|
||||
IApartmentDatabase,
|
||||
ICustomizationInfoDatabase,
|
||||
IFavouriteLoadoutDatabase,
|
||||
IGardeningDatabase,
|
||||
IOrbiterClient,
|
||||
@ -11,12 +12,13 @@ import {
|
||||
IPlantClient,
|
||||
IPlantDatabase,
|
||||
IPlanterDatabase,
|
||||
IRoom,
|
||||
IRoomDatabase,
|
||||
ITailorShopDatabase,
|
||||
PersonalRoomsModelType
|
||||
} from "@/src/types/personalRoomsTypes";
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import { colorSchema, shipCustomizationSchema } from "@/src/models/commonModel";
|
||||
import { loadoutConfigSchema } from "@/src/models/inventoryModels/loadoutModel";
|
||||
|
||||
export const pictureFrameInfoSchema = new Schema<IPictureFrameInfo>(
|
||||
{
|
||||
@ -34,7 +36,20 @@ export const pictureFrameInfoSchema = new Schema<IPictureFrameInfo>(
|
||||
TextColorB: Number,
|
||||
TextOrientation: Number
|
||||
},
|
||||
{ id: false, _id: false }
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
export const customizationInfoSchema = new Schema<ICustomizationInfoDatabase>(
|
||||
{
|
||||
Anim: String,
|
||||
AnimPose: Number,
|
||||
LoadOutPreset: loadoutConfigSchema,
|
||||
VehiclePreset: loadoutConfigSchema,
|
||||
EquippedWeapon: String,
|
||||
AvatarType: String,
|
||||
LoadOutType: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const placedDecosSchema = new Schema<IPlacedDecosDatabase>(
|
||||
@ -44,7 +59,9 @@ const placedDecosSchema = new Schema<IPlacedDecosDatabase>(
|
||||
Rot: [Number],
|
||||
Scale: Number,
|
||||
Sockets: Number,
|
||||
PictureFrameInfo: { type: pictureFrameInfoSchema, default: undefined }
|
||||
PictureFrameInfo: { type: pictureFrameInfoSchema, default: undefined },
|
||||
CustomizationInfo: { type: customizationInfoSchema, default: undefined },
|
||||
AnimPoseItem: String
|
||||
},
|
||||
{ id: false }
|
||||
);
|
||||
@ -55,12 +72,12 @@ placedDecosSchema.virtual("id").get(function (this: IPlacedDecosDatabase) {
|
||||
|
||||
placedDecosSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
}
|
||||
});
|
||||
|
||||
const roomSchema = new Schema<IRoom>(
|
||||
const roomSchema = new Schema<IRoomDatabase>(
|
||||
{
|
||||
Name: String,
|
||||
MaxCapacity: Number,
|
||||
@ -78,7 +95,7 @@ const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
||||
);
|
||||
favouriteLoadoutSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
||||
}
|
||||
@ -95,7 +112,7 @@ const plantSchema = new Schema<IPlantDatabase>(
|
||||
|
||||
plantSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const client = obj as IPlantClient;
|
||||
const db = obj as IPlantDatabase;
|
||||
|
||||
@ -122,7 +139,9 @@ const apartmentSchema = new Schema<IApartmentDatabase>(
|
||||
{
|
||||
Rooms: [roomSchema],
|
||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||
Gardening: gardeningSchema
|
||||
Gardening: gardeningSchema,
|
||||
VideoWallBackdrop: String,
|
||||
Soundscape: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -156,7 +175,7 @@ const orbiterSchema = new Schema<IOrbiterDatabase>(
|
||||
);
|
||||
orbiterSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IOrbiterDatabase;
|
||||
const client = obj as IOrbiterClient;
|
||||
|
||||
|
@ -22,7 +22,7 @@ shipSchema.virtual("ItemId").get(function () {
|
||||
|
||||
shipSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const shipResponse = returnedObject as IShipInventory;
|
||||
const shipDatabase = returnedObject as IShipDatabase;
|
||||
delete returnedObject._id;
|
||||
|
@ -101,7 +101,7 @@ const statsSchema = new Schema<IStatsDatabase>({
|
||||
});
|
||||
|
||||
statsSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.accountOwnerId;
|
||||
|
@ -10,6 +10,7 @@ import { addPendingFriendController } from "@/src/controllers/api/addPendingFrie
|
||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
||||
import { adoptPetController } from "@/src/controllers/api/adoptPetController";
|
||||
import { apartmentController } from "@/src/controllers/api/apartmentController";
|
||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||
import { archonFusionController } from "@/src/controllers/api/archonFusionController";
|
||||
import { artifactsController } from "@/src/controllers/api/artifactsController";
|
||||
@ -61,10 +62,12 @@ import { getFriendsController } from "@/src/controllers/api/getFriendsController
|
||||
import { getGuildContributionsController } from "@/src/controllers/api/getGuildContributionsController";
|
||||
import { getGuildController } from "@/src/controllers/api/getGuildController";
|
||||
import { getGuildDojoController } from "@/src/controllers/api/getGuildDojoController";
|
||||
import { getGuildEventScoreController } from "@/src/controllers/api/getGuildEventScore";
|
||||
import { getGuildLogController } from "@/src/controllers/api/getGuildLogController";
|
||||
import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsersController";
|
||||
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
|
||||
import { getProfileViewingDataPostController } from "@/src/controllers/dynamic/getProfileViewingDataController";
|
||||
import { getPastWeeklyChallengesController } from "@/src/controllers/api/getPastWeeklyChallengesController";
|
||||
import { getShipController } from "@/src/controllers/api/getShipController";
|
||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
|
||||
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
|
||||
@ -112,6 +115,7 @@ import { removeFromGuildController } from "@/src/controllers/api/removeFromGuild
|
||||
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
||||
import { renamePetController } from "@/src/controllers/api/renamePetController";
|
||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
||||
import { resetQuestProgressController } from "@/src/controllers/api/resetQuestProgressController";
|
||||
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
||||
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadoutController";
|
||||
@ -167,6 +171,7 @@ const apiRouter = express.Router();
|
||||
// get
|
||||
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
||||
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
||||
apiRouter.get("/apartment.php", apartmentController);
|
||||
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
||||
apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||
@ -188,10 +193,12 @@ apiRouter.get("/getFriends.php", getFriendsController);
|
||||
apiRouter.get("/getGuild.php", getGuildController);
|
||||
apiRouter.get("/getGuildContributions.php", getGuildContributionsController);
|
||||
apiRouter.get("/getGuildDojo.php", getGuildDojoController);
|
||||
apiRouter.get("/getGuildEventScore.php", getGuildEventScoreController);
|
||||
apiRouter.get("/getGuildLog.php", getGuildLogController);
|
||||
apiRouter.get("/getIgnoredUsers.php", getIgnoredUsersController);
|
||||
apiRouter.get("/getMessages.php", inboxController); // unsure if this is correct, but needed for U17
|
||||
apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController);
|
||||
apiRouter.get("/getPastWeeklyChallenges.php", getPastWeeklyChallengesController)
|
||||
apiRouter.get("/getShip.php", getShipController);
|
||||
apiRouter.get("/getShipDecos.php", (_req, res) => { res.end(); }); // needed to log in on U22.8
|
||||
apiRouter.get("/getVendorInfo.php", getVendorInfoController);
|
||||
@ -209,6 +216,7 @@ apiRouter.get("/questControl.php", questControlController);
|
||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
||||
apiRouter.get("/removeFriend.php", removeFriendGetController);
|
||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
||||
apiRouter.get("/resetQuestProgress.php", resetQuestProgressController);
|
||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
||||
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
||||
|
@ -15,6 +15,7 @@ import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webu
|
||||
import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
|
||||
import { addMissingHelminthBlueprintsController } from "@/src/controllers/custom/addMissingHelminthBlueprintsController";
|
||||
|
||||
import { abilityOverrideController } from "@/src/controllers/custom/abilityOverrideController";
|
||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
|
||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
|
||||
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
|
||||
@ -25,6 +26,9 @@ import { manageQuestsController } from "@/src/controllers/custom/manageQuestsCon
|
||||
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
||||
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
|
||||
import { updateFingerprintController } from "@/src/controllers/custom/updateFingerprintController";
|
||||
import { changeModularPartsController } from "@/src/controllers/custom/changeModularPartsController";
|
||||
import { editSuitInvigorationUpgradeController } from "@/src/controllers/custom/editSuitInvigorationUpgradeController";
|
||||
import { setAccountCheatController } from "@/src/controllers/custom/setAccountCheatController";
|
||||
|
||||
import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
|
||||
|
||||
@ -45,6 +49,7 @@ customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
|
||||
customRouter.get("/completeAllMissions", completeAllMissionsController);
|
||||
customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
|
||||
|
||||
customRouter.post("/abilityOverride", abilityOverrideController);
|
||||
customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
customRouter.post("/addCurrency", addCurrencyController);
|
||||
@ -55,6 +60,9 @@ customRouter.post("/manageQuests", manageQuestsController);
|
||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||
customRouter.post("/setBooster", setBoosterController);
|
||||
customRouter.post("/updateFingerprint", updateFingerprintController);
|
||||
customRouter.post("/changeModularParts", changeModularPartsController);
|
||||
customRouter.post("/editSuitInvigorationUpgrade", editSuitInvigorationUpgradeController);
|
||||
customRouter.post("/setAccountCheat", setAccountCheatController);
|
||||
|
||||
customRouter.post("/getConfig", getConfigController);
|
||||
customRouter.post("/setConfig", setConfigController);
|
||||
|
@ -2,6 +2,7 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
import { repoDir } from "@/src/helpers/pathHelper";
|
||||
import { args } from "@/src/helpers/commandLineArguments";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
|
||||
export interface IConfig {
|
||||
mongodbUrl: string;
|
||||
@ -19,11 +20,6 @@ export interface IConfig {
|
||||
skipTutorial?: boolean;
|
||||
skipAllDialogue?: boolean;
|
||||
unlockAllScans?: boolean;
|
||||
infiniteCredits?: boolean;
|
||||
infinitePlatinum?: boolean;
|
||||
infiniteEndo?: boolean;
|
||||
infiniteRegalAya?: boolean;
|
||||
infiniteHelminthMaterials?: boolean;
|
||||
claimingBlueprintRefundsIngredients?: boolean;
|
||||
dontSubtractPurchaseCreditCost?: boolean;
|
||||
dontSubtractPurchasePlatinumCost?: boolean;
|
||||
@ -80,8 +76,25 @@ export interface IConfig {
|
||||
creditBoost?: boolean;
|
||||
affinityBoost?: boolean;
|
||||
resourceBoost?: boolean;
|
||||
starDays?: boolean;
|
||||
tennoLiveRelay?: boolean;
|
||||
baroTennoConRelay?: boolean;
|
||||
wolfHunt?: boolean;
|
||||
longShadow?: boolean;
|
||||
hallowedFlame?: boolean;
|
||||
hallowedNightmares?: boolean;
|
||||
hallowedNightmaresRewardsOverride?: number;
|
||||
proxyRebellion?: boolean;
|
||||
proxyRebellionRewardsOverride?: number;
|
||||
galleonOfGhouls?: number;
|
||||
ghoulEmergenceOverride?: boolean;
|
||||
plagueStarOverride?: boolean;
|
||||
starDaysOverride?: boolean;
|
||||
dogDaysOverride?: boolean;
|
||||
dogDaysRewardsOverride?: number;
|
||||
bellyOfTheBeast?: boolean;
|
||||
bellyOfTheBeastProgressOverride?: number;
|
||||
eightClaw?: boolean;
|
||||
eightClawProgressOverride?: number;
|
||||
eidolonOverride?: string;
|
||||
vallisOverride?: string;
|
||||
duviriOverride?: string;
|
||||
@ -113,9 +126,26 @@ export const loadConfig = (): void => {
|
||||
|
||||
// Set all values to undefined now so if the new config.json omits some fields that were previously present, it's correct in-memory.
|
||||
for (const key of Object.keys(config)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(config as any)[key] = undefined;
|
||||
}
|
||||
|
||||
Object.assign(config, newConfig);
|
||||
};
|
||||
|
||||
export const syncConfigWithDatabase = (): void => {
|
||||
// Event messages are deleted after endDate. Since we don't use beginDate/endDate and instead have config toggles, we need to delete the messages once those bools are false.
|
||||
// Also, for some reason, I can't just do `Inbox.deleteMany(...)`; - it needs this whole circus.
|
||||
if (!config.worldState?.creditBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666672" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.affinityBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666673" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.resourceBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666674" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.galleonOfGhouls) {
|
||||
void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {});
|
||||
}
|
||||
};
|
||||
|
@ -1,10 +1,9 @@
|
||||
import chokidar from "chokidar";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { config, configPath, loadConfig } from "@/src/services/configService";
|
||||
import { config, configPath, loadConfig, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import { saveConfig, shouldReloadConfig } from "@/src/services/configWriterService";
|
||||
import { getWebPorts, startWebServer, stopWebServer } from "@/src/services/webService";
|
||||
import { sendWsBroadcast } from "@/src/services/wsService";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
import varzia from "@/static/fixed_responses/worldState/varzia.json";
|
||||
|
||||
chokidar.watch(configPath).on("change", () => {
|
||||
@ -68,10 +67,3 @@ export const validateConfig = (): void => {
|
||||
void saveConfig();
|
||||
}
|
||||
};
|
||||
|
||||
export const syncConfigWithDatabase = (): void => {
|
||||
// Event messages are deleted after endDate. Since we don't use beginDate/endDate and instead have config toggles, we need to delete the messages once those bools are false.
|
||||
if (!config.worldState?.galleonOfGhouls) {
|
||||
void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {}); // For some reason, I can't just do `Inbox.deleteMany(...)`; it needs this whole circus.
|
||||
}
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
IDojoComponentDatabase,
|
||||
IDojoContributable,
|
||||
IDojoDecoClient,
|
||||
IDojoDecoDatabase,
|
||||
IGuildClient,
|
||||
IGuildMemberClient,
|
||||
IGuildMemberDatabase,
|
||||
@ -114,7 +115,14 @@ export const getGuildClient = async (
|
||||
NumContributors: guild.CeremonyContributors?.length ?? 0,
|
||||
CeremonyResetDate: guild.CeremonyResetDate ? toMongoDate(guild.CeremonyResetDate) : undefined,
|
||||
AutoContributeFromVault: guild.AutoContributeFromVault,
|
||||
AllianceId: guild.AllianceId ? toOid2(guild.AllianceId, account.BuildLabel) : undefined
|
||||
AllianceId: guild.AllianceId ? toOid2(guild.AllianceId, account.BuildLabel) : undefined,
|
||||
GoalProgress: guild.GoalProgress
|
||||
? guild.GoalProgress.map(gp => ({
|
||||
Count: gp.Count,
|
||||
Tag: gp.Tag,
|
||||
_id: { $oid: gp.goalId.toString() }
|
||||
}))
|
||||
: undefined
|
||||
};
|
||||
};
|
||||
|
||||
@ -309,7 +317,7 @@ export const removeDojoRoom = async (
|
||||
guild.DojoEnergy -= meta.energy;
|
||||
}
|
||||
moveResourcesToVault(guild, component);
|
||||
component.Decos?.forEach(deco => moveResourcesToVault(guild, deco));
|
||||
component.Decos?.forEach(deco => refundDojoDeco(guild, component, deco));
|
||||
|
||||
if (guild.RoomChanges) {
|
||||
const index = guild.RoomChanges.findIndex(x => x.componentId.equals(component._id));
|
||||
@ -344,6 +352,14 @@ export const removeDojoDeco = (
|
||||
component.Decos!.findIndex(x => x._id.equals(decoId)),
|
||||
1
|
||||
)[0];
|
||||
refundDojoDeco(guild, component, deco);
|
||||
};
|
||||
|
||||
export const refundDojoDeco = (
|
||||
guild: TGuildDatabaseDocument,
|
||||
component: IDojoComponentDatabase,
|
||||
deco: IDojoDecoDatabase
|
||||
): void => {
|
||||
const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type);
|
||||
if (meta) {
|
||||
if (meta.capacityCost) {
|
||||
@ -369,7 +385,7 @@ export const removeDojoDeco = (
|
||||
]);
|
||||
}
|
||||
}
|
||||
moveResourcesToVault(guild, deco);
|
||||
moveResourcesToVault(guild, deco); // Refund resources spent on construction
|
||||
};
|
||||
|
||||
const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoContributable): void => {
|
||||
@ -800,3 +816,85 @@ export const getAllianceClient = async (
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const handleGuildGoalProgress = async (
|
||||
guild: TGuildDatabaseDocument,
|
||||
upload: { Count: number; Tag: string; goalId: Types.ObjectId }
|
||||
): Promise<void> => {
|
||||
guild.GoalProgress ??= [];
|
||||
const goalProgress = guild.GoalProgress.find(x => x.goalId.equals(upload.goalId));
|
||||
if (!goalProgress) {
|
||||
guild.GoalProgress.push({
|
||||
Count: upload.Count,
|
||||
Tag: upload.Tag,
|
||||
goalId: upload.goalId
|
||||
});
|
||||
}
|
||||
const totalCount = (goalProgress?.Count ?? 0) + upload.Count;
|
||||
const guildRewards = goalGuildRewardByTag[upload.Tag].rewards;
|
||||
const tierGoals = goalGuildRewardByTag[upload.Tag].guildGoals[guild.Tier - 1];
|
||||
const rewards = [];
|
||||
if (tierGoals.length && guildRewards.length) {
|
||||
for (let i = 0; i < tierGoals.length; i++) {
|
||||
if (
|
||||
tierGoals[i] &&
|
||||
tierGoals[i] <= totalCount &&
|
||||
(!goalProgress || goalProgress.Count < tierGoals[i]) &&
|
||||
guildRewards[i]
|
||||
) {
|
||||
rewards.push(guildRewards[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (rewards.length) {
|
||||
logger.debug(`guild goal rewards`, rewards);
|
||||
guild.VaultDecoRecipes ??= [];
|
||||
rewards.forEach(type => {
|
||||
guild.VaultDecoRecipes!.push({
|
||||
ItemType: type,
|
||||
ItemCount: 1
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (goalProgress) {
|
||||
goalProgress.Count += upload.Count;
|
||||
}
|
||||
await guild.save();
|
||||
};
|
||||
|
||||
export const goalGuildRewardByTag: Record<string, { guildGoals: number[][]; rewards: string[] }> = {
|
||||
JadeShadowsEvent: {
|
||||
guildGoals: [
|
||||
// I don't know what ClanGoal means
|
||||
[15, 30, 45, 60],
|
||||
[45, 90, 135, 180],
|
||||
[150, 300, 450, 600],
|
||||
[450, 900, 1350, 1800],
|
||||
[1500, 3000, 4500, 6000]
|
||||
],
|
||||
rewards: [
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventPewterTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventGoldTrophyRecipe"
|
||||
]
|
||||
},
|
||||
DuviriMurmurEvent: {
|
||||
guildGoals: [
|
||||
// I don't know what ClanGoal means
|
||||
[260, 519, 779, 1038],
|
||||
[779, 1557, 2336, 3114],
|
||||
[2595, 5190, 7785, 10380],
|
||||
[7785, 15570, 23355, 31140],
|
||||
[29950, 51900, 77850, 103800]
|
||||
],
|
||||
rewards: [
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventClayTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventGoldTrophyRecipe"
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -44,6 +44,28 @@ import {
|
||||
IKubrowPetDetailsClient,
|
||||
IKubrowPetDetailsDatabase
|
||||
} from "@/src/types/equipmentTypes";
|
||||
import {
|
||||
IApartmentClient,
|
||||
IApartmentDatabase,
|
||||
ICustomizationInfoClient,
|
||||
ICustomizationInfoDatabase,
|
||||
IFavouriteLoadout,
|
||||
IFavouriteLoadoutDatabase,
|
||||
IGetShipResponse,
|
||||
IOrbiterClient,
|
||||
IOrbiterDatabase,
|
||||
IPersonalRoomsDatabase,
|
||||
IPlacedDecosClient,
|
||||
IPlacedDecosDatabase,
|
||||
IPlantClient,
|
||||
IPlantDatabase,
|
||||
IPlanterClient,
|
||||
IPlanterDatabase,
|
||||
IRoomClient,
|
||||
IRoomDatabase,
|
||||
ITailorShop,
|
||||
ITailorShopDatabase
|
||||
} from "@/src/types/personalRoomsTypes";
|
||||
|
||||
const convertDate = (value: IMongoDate): Date => {
|
||||
return new Date(parseInt(value.$date.$numberLong));
|
||||
@ -422,9 +444,91 @@ export const importLoadOutPresets = (db: ILoadoutDatabase, client: ILoadOutPrese
|
||||
db.NORMAL_PVP = client.NORMAL_PVP.map(convertLoadOutConfig);
|
||||
db.LUNARO = client.LUNARO.map(convertLoadOutConfig);
|
||||
db.OPERATOR = client.OPERATOR.map(convertLoadOutConfig);
|
||||
db.GEAR = client.GEAR.map(convertLoadOutConfig);
|
||||
db.KDRIVE = client.KDRIVE.map(convertLoadOutConfig);
|
||||
db.DATAKNIFE = client.DATAKNIFE.map(convertLoadOutConfig);
|
||||
db.MECH = client.MECH.map(convertLoadOutConfig);
|
||||
db.OPERATOR_ADULT = client.OPERATOR_ADULT.map(convertLoadOutConfig);
|
||||
db.DRIFTER = client.DRIFTER.map(convertLoadOutConfig);
|
||||
};
|
||||
|
||||
export const convertCustomizationInfo = (client: ICustomizationInfoClient): ICustomizationInfoDatabase => {
|
||||
return {
|
||||
...client,
|
||||
LoadOutPreset: client.LoadOutPreset ? convertLoadOutConfig(client.LoadOutPreset) : undefined,
|
||||
VehiclePreset: client.VehiclePreset ? convertLoadOutConfig(client.VehiclePreset) : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const convertDeco = (client: IPlacedDecosClient): IPlacedDecosDatabase => {
|
||||
const { id, ...rest } = client;
|
||||
return {
|
||||
...rest,
|
||||
CustomizationInfo: client.CustomizationInfo ? convertCustomizationInfo(client.CustomizationInfo) : undefined,
|
||||
_id: new Types.ObjectId(id.$oid)
|
||||
};
|
||||
};
|
||||
|
||||
const convertRoom = (client: IRoomClient): IRoomDatabase => {
|
||||
return {
|
||||
...client,
|
||||
PlacedDecos: client.PlacedDecos ? client.PlacedDecos.map(convertDeco) : []
|
||||
};
|
||||
};
|
||||
|
||||
const convertShip = (client: IOrbiterClient): IOrbiterDatabase => {
|
||||
return {
|
||||
...client,
|
||||
ShipInterior: {
|
||||
...client.ShipInterior,
|
||||
Colors: Array.isArray(client.ShipInterior.Colors) ? {} : client.ShipInterior.Colors
|
||||
},
|
||||
Rooms: client.Rooms.map(convertRoom),
|
||||
FavouriteLoadoutId: client.FavouriteLoadoutId ? new Types.ObjectId(client.FavouriteLoadoutId.$oid) : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const convertPlant = (client: IPlantClient): IPlantDatabase => {
|
||||
return {
|
||||
...client,
|
||||
EndTime: convertDate(client.EndTime)
|
||||
};
|
||||
};
|
||||
|
||||
const convertPlanter = (client: IPlanterClient): IPlanterDatabase => {
|
||||
return {
|
||||
...client,
|
||||
Plants: client.Plants.map(convertPlant)
|
||||
};
|
||||
};
|
||||
|
||||
const convertFavouriteLoadout = (client: IFavouriteLoadout): IFavouriteLoadoutDatabase => {
|
||||
return {
|
||||
...client,
|
||||
LoadoutId: new Types.ObjectId(client.LoadoutId.$oid)
|
||||
};
|
||||
};
|
||||
|
||||
const convertApartment = (client: IApartmentClient): IApartmentDatabase => {
|
||||
return {
|
||||
...client,
|
||||
Rooms: client.Rooms.map(convertRoom),
|
||||
Gardening: { Planters: client.Gardening.Planters.map(convertPlanter) },
|
||||
FavouriteLoadouts: client.FavouriteLoadouts ? client.FavouriteLoadouts.map(convertFavouriteLoadout) : []
|
||||
};
|
||||
};
|
||||
|
||||
const convertTailorShop = (client: ITailorShop): ITailorShopDatabase => {
|
||||
return {
|
||||
...client,
|
||||
Rooms: client.Rooms.map(convertRoom),
|
||||
Colors: Array.isArray(client.Colors) ? {} : client.Colors,
|
||||
FavouriteLoadouts: client.FavouriteLoadouts ? client.FavouriteLoadouts.map(convertFavouriteLoadout) : []
|
||||
};
|
||||
};
|
||||
|
||||
export const importPersonalRooms = (db: IPersonalRoomsDatabase, client: Partial<IGetShipResponse>): void => {
|
||||
if (client.Ship !== undefined) db.Ship = convertShip(client.Ship);
|
||||
if (client.Apartment !== undefined) db.Apartment = convertApartment(client.Apartment);
|
||||
if (client.TailorShop !== undefined) db.TailorShop = convertTailorShop(client.TailorShop);
|
||||
};
|
||||
|
@ -35,7 +35,7 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
|
||||
const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
|
||||
const baroStart = baroIndex * (unixTimesInMs.day * 14) + 910800000;
|
||||
const baroActualStart = baroStart + unixTimesInMs.day * (config.baroAlwaysAvailable ? 0 : 12);
|
||||
if (account.LatestEventMessageDate.getTime() < baroActualStart) {
|
||||
if (Date.now() >= baroActualStart && account.LatestEventMessageDate.getTime() < baroActualStart) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/G1Quests/VoidTraderName",
|
||||
sub: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceTitle",
|
||||
@ -55,20 +55,77 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
|
||||
}
|
||||
|
||||
// BUG: Deleting the inbox message manually means it'll just be automatically re-created. This is because we don't use startDate/endDate for these config-toggled events.
|
||||
if (config.worldState?.galleonOfGhouls) {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
|
||||
sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
|
||||
msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
|
||||
transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
|
||||
att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
|
||||
startDate: new Date(),
|
||||
goalTag: "GalleonRobbery"
|
||||
});
|
||||
}
|
||||
const promises = [];
|
||||
if (config.worldState?.creditBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666672" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666672"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleCreditsName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleCreditsDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.affinityBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666673" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666673"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleAffinityName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleAffinityDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.resourceBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666674" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666674"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleResourceName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleResourceDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.galleonOfGhouls) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
|
||||
sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
|
||||
msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
|
||||
transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
|
||||
att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
|
||||
startDate: new Date(),
|
||||
goalTag: "GalleonRobbery"
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
if (newEventMessages.length === 0) {
|
||||
return;
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { ExportRecipes } from "warframe-public-export-plus";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IInfestedFoundryClient, IInfestedFoundryDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import {
|
||||
IAccountCheats,
|
||||
IInfestedFoundryClient,
|
||||
IInfestedFoundryDatabase
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { addRecipes } from "@/src/services/inventoryService";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { ITypeCount } from "@/src/types/commonTypes";
|
||||
|
||||
export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => {
|
||||
@ -97,8 +100,8 @@ export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument):
|
||||
return recipeChanges;
|
||||
};
|
||||
|
||||
export const applyCheatsToInfestedFoundry = (infestedFoundry: IInfestedFoundryClient): void => {
|
||||
if (config.infiniteHelminthMaterials) {
|
||||
export const applyCheatsToInfestedFoundry = (cheats: IAccountCheats, infestedFoundry: IInfestedFoundryClient): void => {
|
||||
if (cheats.infiniteHelminthMaterials) {
|
||||
infestedFoundry.Resources = [
|
||||
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthCalx", Count: 1000 },
|
||||
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBiotics", Count: 1000 },
|
||||
|
@ -25,7 +25,8 @@ import {
|
||||
INemesisWeaponTargetFingerprint,
|
||||
INemesisPetTargetFingerprint,
|
||||
IDialogueDatabase,
|
||||
IKubrowPetPrintClient
|
||||
IKubrowPetPrintClient,
|
||||
equipmentKeys
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "@/src/types/genericUpdate";
|
||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||
@ -67,7 +68,8 @@ import {
|
||||
kubrowDetails,
|
||||
kubrowFurPatternsWeights,
|
||||
kubrowWeights,
|
||||
toOid
|
||||
toOid,
|
||||
TTraitsPool
|
||||
} from "@/src/helpers/inventoryHelpers";
|
||||
import { addQuestKey, completeQuest } from "@/src/services/questService";
|
||||
import { handleBundleAcqusition } from "@/src/services/purchaseService";
|
||||
@ -481,11 +483,14 @@ export const addItem = async (
|
||||
if (quantity != 1) {
|
||||
logger.warn(`adding 1 of ${typeName} ${targetFingerprint} even tho quantity ${quantity} was requested`);
|
||||
}
|
||||
inventory.Upgrades.push({
|
||||
ItemType: typeName,
|
||||
UpgradeFingerprint: targetFingerprint
|
||||
});
|
||||
return {}; // there's not exactly a common "InventoryChanges" format for these
|
||||
const upgrade =
|
||||
inventory.Upgrades[
|
||||
inventory.Upgrades.push({
|
||||
ItemType: typeName,
|
||||
UpgradeFingerprint: targetFingerprint
|
||||
}) - 1
|
||||
];
|
||||
return { Upgrades: [upgrade.toJSON<IUpgradeClient>()] };
|
||||
}
|
||||
const changes = [
|
||||
{
|
||||
@ -811,7 +816,7 @@ export const addItem = async (
|
||||
if (!seed) {
|
||||
throw new Error(`Expected crew member to have a seed`);
|
||||
}
|
||||
seed |= 0x33b81en << 32n;
|
||||
seed |= BigInt(Math.trunc(inventory.Created.getTime() / 1000) & 0xffffff) << 32n;
|
||||
return {
|
||||
...addCrewMember(inventory, typeName, seed),
|
||||
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
||||
@ -842,6 +847,32 @@ export const addItem = async (
|
||||
return addMotorcycle(inventory, typeName);
|
||||
}
|
||||
break;
|
||||
case "Lore":
|
||||
if (typeName == "/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentRewards") {
|
||||
const fragmentType = getRandomElement([
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentA",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentB",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentC",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentD",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentE",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentF",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentG",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentH",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentI",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentJ",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentK",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentL",
|
||||
"/Lotus/Types/Lore/Fragments/GrineerGhoulFragments/GhoulFragmentM"
|
||||
])!;
|
||||
addLoreFragmentScans(inventory, [
|
||||
{
|
||||
Progress: 1,
|
||||
Region: "",
|
||||
ItemType: fragmentType
|
||||
}
|
||||
]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1048,6 +1079,21 @@ export const addSpaceSuit = (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
const createRandomTraits = (kubrowPetName: string, traitsPool: TTraitsPool): ITraits => {
|
||||
return {
|
||||
BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
TertiaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
AccentColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
EyeColor: getRandomWeightedReward(traitsPool.EyeColors, kubrowWeights)!.type,
|
||||
FurPattern: getRandomWeightedReward(traitsPool.FurPatterns, kubrowFurPatternsWeights)!.type,
|
||||
Personality: kubrowPetName,
|
||||
BodyType: getRandomWeightedReward(traitsPool.BodyTypes, kubrowWeights)!.type,
|
||||
Head: traitsPool.Heads.length ? getRandomWeightedReward(traitsPool.Heads, kubrowWeights)!.type : undefined,
|
||||
Tail: traitsPool.Tails.length ? getRandomWeightedReward(traitsPool.Tails, kubrowWeights)!.type : undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const addKubrowPet = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
kubrowPetName: string,
|
||||
@ -1064,7 +1110,6 @@ export const addKubrowPet = (
|
||||
addSpecialItem(inventory, specialItem, inventoryChanges);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
|
||||
|
||||
if (!details) {
|
||||
@ -1074,9 +1119,10 @@ export const addKubrowPet = (
|
||||
"/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit"
|
||||
].includes(kubrowPetName);
|
||||
|
||||
let traits: ITraits;
|
||||
const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
|
||||
let dominantTraits: ITraits;
|
||||
if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
|
||||
traits = {
|
||||
dominantTraits = {
|
||||
BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
|
||||
SecondaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryVampire",
|
||||
TertiaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryVampire",
|
||||
@ -1089,19 +1135,35 @@ export const addKubrowPet = (
|
||||
Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
|
||||
};
|
||||
} else {
|
||||
const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
|
||||
traits = {
|
||||
BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
TertiaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
AccentColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
|
||||
EyeColor: getRandomWeightedReward(traitsPool.EyeColors, kubrowWeights)!.type,
|
||||
FurPattern: getRandomWeightedReward(traitsPool.FurPatterns, kubrowFurPatternsWeights)!.type,
|
||||
Personality: kubrowPetName,
|
||||
BodyType: getRandomWeightedReward(traitsPool.BodyTypes, kubrowWeights)!.type,
|
||||
Head: isCatbrow ? getRandomWeightedReward(traitsPool.Heads, kubrowWeights)!.type : undefined,
|
||||
Tail: isCatbrow ? getRandomWeightedReward(traitsPool.Tails, kubrowWeights)!.type : undefined
|
||||
};
|
||||
dominantTraits = createRandomTraits(kubrowPetName, traitsPool);
|
||||
if (kubrowPetName == "/Lotus/Types/Game/KubrowPet/ChargerKubrowPetPowerSuit") {
|
||||
dominantTraits.BodyType = "/Lotus/Types/Game/KubrowPet/BodyTypes/ChargerKubrowPetBodyType";
|
||||
dominantTraits.FurPattern = "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternInfested";
|
||||
}
|
||||
}
|
||||
|
||||
const recessiveTraits: ITraits = createRandomTraits(
|
||||
getRandomElement(
|
||||
isCatbrow
|
||||
? [
|
||||
"/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit"
|
||||
]
|
||||
: [
|
||||
"/Lotus/Types/Game/KubrowPet/AdventurerKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/KubrowPet/FurtiveKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/KubrowPet/GuardKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/KubrowPet/HunterKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Game/KubrowPet/RetrieverKubrowPetPowerSuit"
|
||||
]
|
||||
)!,
|
||||
traitsPool
|
||||
);
|
||||
for (const key of Object.keys(recessiveTraits) as (keyof ITraits)[]) {
|
||||
// My heurstic approximation is a 20% chance for a dominant trait to be copied into the recessive traits. TODO: A more scientific statistical analysis maybe?
|
||||
if (Math.random() < 0.2) {
|
||||
recessiveTraits[key] = dominantTraits[key]!;
|
||||
}
|
||||
}
|
||||
|
||||
details = {
|
||||
@ -1113,8 +1175,8 @@ export const addKubrowPet = (
|
||||
HatchDate: premiumPurchase ? new Date() : new Date(Date.now() + 10 * unixTimesInMs.hour), // On live, this seems to be somewhat randomised so that the pet hatches 9~11 hours after start.
|
||||
IsMale: !!getRandomInt(0, 1),
|
||||
Size: getRandomInt(70, 100) / 100,
|
||||
DominantTraits: traits,
|
||||
RecessiveTraits: traits
|
||||
DominantTraits: dominantTraits,
|
||||
RecessiveTraits: recessiveTraits
|
||||
};
|
||||
}
|
||||
|
||||
@ -1165,8 +1227,8 @@ export const updateSlots = (
|
||||
}
|
||||
};
|
||||
|
||||
const isCurrencyTracked = (usePremium: boolean): boolean => {
|
||||
return usePremium ? !config.infinitePlatinum : !config.infiniteCredits;
|
||||
const isCurrencyTracked = (inventory: TInventoryDatabaseDocument, usePremium: boolean): boolean => {
|
||||
return usePremium ? !inventory.infinitePlatinum : !inventory.infiniteCredits;
|
||||
};
|
||||
|
||||
export const updateCurrency = (
|
||||
@ -1175,7 +1237,7 @@ export const updateCurrency = (
|
||||
usePremium: boolean,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
if (price != 0 && isCurrencyTracked(usePremium)) {
|
||||
if (price != 0 && isCurrencyTracked(inventory, usePremium)) {
|
||||
if (usePremium) {
|
||||
if (inventory.PremiumCreditsFree > 0) {
|
||||
const premiumCreditsFreeDelta = Math.min(price, inventory.PremiumCreditsFree) * -1;
|
||||
@ -1206,6 +1268,15 @@ export const addFusionPoints = (inventory: TInventoryDatabaseDocument, add: numb
|
||||
return add;
|
||||
};
|
||||
|
||||
export const addCrewShipFusionPoints = (inventory: TInventoryDatabaseDocument, add: number): number => {
|
||||
if (inventory.CrewShipFusionPoints + add > 2147483647) {
|
||||
logger.warn(`capping CrewShipFusionPoints balance at 2147483647`);
|
||||
add = 2147483647 - inventory.CrewShipFusionPoints;
|
||||
}
|
||||
inventory.CrewShipFusionPoints += add;
|
||||
return add;
|
||||
};
|
||||
|
||||
const standingLimitBinToInventoryKey: Record<
|
||||
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
|
||||
keyof IDailyAffiliations
|
||||
@ -1297,7 +1368,7 @@ export const addStanding = (
|
||||
|
||||
// TODO: AffiliationMods support (Nightwave).
|
||||
export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise<IUpdateNodeIntrosResponse> => {
|
||||
const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems");
|
||||
const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems ShipDecorations");
|
||||
|
||||
// Make it an array for easier parsing.
|
||||
if (typeof data.NodeIntrosCompleted === "string") {
|
||||
@ -1306,7 +1377,15 @@ export const updateGeneric = async (data: IGenericUpdate, accountId: string): Pr
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
for (const node of data.NodeIntrosCompleted) {
|
||||
if (node == "KayaFirstVisitPack") {
|
||||
if (node == "TC2025") {
|
||||
inventoryChanges.ShipDecorations = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/ShipDecos/TauGrineerLancerBobbleHead",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addShipDecorations(inventory, inventoryChanges.ShipDecorations);
|
||||
} else if (node == "KayaFirstVisitPack") {
|
||||
inventoryChanges.MiscItems = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/1999FixedStickersPack",
|
||||
@ -1600,6 +1679,15 @@ export const addEmailItem = async (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
const xpEarningParts: readonly string[] = [
|
||||
"LWPT_BLADE",
|
||||
"LWPT_GUN_BARREL",
|
||||
"LWPT_AMP_OCULUS",
|
||||
"LWPT_MOA_HEAD",
|
||||
"LWPT_ZANUKA_HEAD",
|
||||
"LWPT_HB_DECK"
|
||||
];
|
||||
|
||||
export const applyClientEquipmentUpdates = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
gearArray: IEquipmentClient[],
|
||||
@ -1618,15 +1706,34 @@ export const applyClientEquipmentUpdates = (
|
||||
item.XP ??= 0;
|
||||
item.XP += XP;
|
||||
|
||||
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == item.ItemType);
|
||||
if (xpinfoIndex !== -1) {
|
||||
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
||||
xpinfo.XP += XP;
|
||||
} else {
|
||||
inventory.XPInfo.push({
|
||||
ItemType: item.ItemType,
|
||||
XP: XP
|
||||
});
|
||||
if (
|
||||
categoryName != "SpecialItems" ||
|
||||
item.ItemType == "/Lotus/Powersuits/Khora/Kavat/KhoraKavatPowerSuit" ||
|
||||
item.ItemType == "/Lotus/Powersuits/Khora/Kavat/KhoraPrimeKavatPowerSuit"
|
||||
) {
|
||||
let xpItemType = item.ItemType;
|
||||
if (item.ModularParts) {
|
||||
for (const part of item.ModularParts) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const partType = ExportWeapons[part]?.partType;
|
||||
if (partType !== undefined && xpEarningParts.indexOf(partType) != -1) {
|
||||
xpItemType = part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
logger.debug(`adding xp to ${xpItemType} for modular item ${fromOid(ItemId)} (${item.ItemType})`);
|
||||
}
|
||||
|
||||
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == xpItemType);
|
||||
if (xpinfoIndex !== -1) {
|
||||
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
||||
xpinfo.XP += XP;
|
||||
} else {
|
||||
inventory.XPInfo.push({
|
||||
ItemType: xpItemType,
|
||||
XP: XP
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1831,25 +1938,90 @@ export const addLoreFragmentScans = (inventory: TInventoryDatabaseDocument, arr:
|
||||
});
|
||||
};
|
||||
|
||||
export const addChallenges = (
|
||||
const challengeRewardsInboxMessages: Record<string, IMessageCreationTemplate> = {
|
||||
SentEvoEphemeraRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Effects/NarmerEvolvingEphemeraB"]
|
||||
},
|
||||
SentEvoEphemeraRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Effects/NarmerEvolvingEphemeraC"]
|
||||
},
|
||||
SentEvoSyandanaRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Scarves/NarmerEvolvingSyandanaBCape"]
|
||||
},
|
||||
SentEvoSyandanaRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Scarves/NarmerEvolvingSyandanaCCape"]
|
||||
},
|
||||
SentEvoSekharaRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSekharaUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSekharaUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Clan/ZarimanEvolvingSekharaBadgeItemB"]
|
||||
},
|
||||
SentEvoSekharaRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSekharaUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSekharaUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Clan/ZarimanEvolvingSekharaBadgeItemC"]
|
||||
}
|
||||
};
|
||||
|
||||
export const addChallenges = async (
|
||||
account: TAccountDocument,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
ChallengeProgress: IChallengeProgress[],
|
||||
SeasonChallengeCompletions: ISeasonChallenge[] | undefined
|
||||
): IAffiliationMods[] => {
|
||||
ChallengeProgress.forEach(({ Name, Progress }) => {
|
||||
const itemIndex = inventory.ChallengeProgress.findIndex(i => i.Name === Name);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
inventory.ChallengeProgress[itemIndex].Progress = Progress;
|
||||
): Promise<IAffiliationMods[]> => {
|
||||
for (const { Name, Progress, Completed } of ChallengeProgress) {
|
||||
let dbChallenge = inventory.ChallengeProgress.find(x => x.Name == Name);
|
||||
if (dbChallenge) {
|
||||
dbChallenge.Progress = Progress;
|
||||
} else {
|
||||
inventory.ChallengeProgress.push({ Name, Progress });
|
||||
dbChallenge = { Name, Progress };
|
||||
inventory.ChallengeProgress.push(dbChallenge);
|
||||
}
|
||||
|
||||
if (Name.startsWith("Calendar")) {
|
||||
addString(getCalendarProgress(inventory).SeasonProgress.ActivatedChallenges, Name);
|
||||
}
|
||||
});
|
||||
|
||||
if ((Completed?.length ?? 0) > (dbChallenge.Completed?.length ?? 0)) {
|
||||
dbChallenge.Completed ??= [];
|
||||
for (const completion of Completed!) {
|
||||
if (dbChallenge.Completed.indexOf(completion) == -1) {
|
||||
dbChallenge.Completed.push(completion);
|
||||
if (completion == "challengeRewards") {
|
||||
if (Name in challengeRewardsInboxMessages) {
|
||||
await createMessage(account._id, [challengeRewardsInboxMessages[Name]]);
|
||||
// Would love to somehow let the client know about inbox or inventory changes, but there doesn't seem to anything for updateChallengeProgress.
|
||||
continue;
|
||||
}
|
||||
logger.warn(`ignoring unknown challenge completion`, { challenge: Name, completion });
|
||||
dbChallenge.Completed = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dbChallenge.Completed = Completed;
|
||||
}
|
||||
}
|
||||
|
||||
const affiliationMods: IAffiliationMods[] = [];
|
||||
if (SeasonChallengeCompletions) {
|
||||
@ -1897,7 +2069,7 @@ export const addCalendarProgress = (inventory: TInventoryDatabaseDocument, value
|
||||
calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = currentSeason.Days.findIndex(
|
||||
day => day.events.length != 0 && day.events[0].challenge == value[value.length - 1].challenge
|
||||
);
|
||||
checkCalendarChallengeCompletion(calendarProgress, currentSeason);
|
||||
checkCalendarAutoAdvance(inventory, currentSeason);
|
||||
};
|
||||
|
||||
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes, Tier }: IMission): void => {
|
||||
@ -2045,6 +2217,21 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
||||
inventory.LotusCustomization.syancol = {};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let numFixed = 0;
|
||||
for (const equipmentKey of equipmentKeys) {
|
||||
for (const item of inventory[equipmentKey]) {
|
||||
if (item.ModularParts?.length === 0) {
|
||||
item.ModularParts = undefined;
|
||||
++numFixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numFixed != 0) {
|
||||
logger.debug(`removed ModularParts from ${numFixed} non-modular items`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||
@ -2082,8 +2269,8 @@ export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICal
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: currentSeason.Season,
|
||||
LastCompletedDayIdx: 0,
|
||||
LastCompletedChallengeDayIdx: 0,
|
||||
LastCompletedDayIdx: -1,
|
||||
LastCompletedChallengeDayIdx: -1,
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
};
|
||||
@ -2104,16 +2291,44 @@ export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICal
|
||||
return inventory.CalendarProgress;
|
||||
};
|
||||
|
||||
export const checkCalendarChallengeCompletion = (
|
||||
calendarProgress: ICalendarProgress,
|
||||
export const checkCalendarAutoAdvance = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
currentSeason: ICalendarSeason
|
||||
): void => {
|
||||
const dayIndex = calendarProgress.SeasonProgress.LastCompletedDayIdx + 1;
|
||||
if (calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx >= dayIndex) {
|
||||
const calendarProgress = inventory.CalendarProgress!;
|
||||
for (
|
||||
let dayIndex = calendarProgress.SeasonProgress.LastCompletedDayIdx + 1;
|
||||
dayIndex != currentSeason.Days.length;
|
||||
++dayIndex
|
||||
) {
|
||||
const day = currentSeason.Days[dayIndex];
|
||||
if (day.events.length != 0 && day.events[0].type == "CET_CHALLENGE") {
|
||||
if (day.events.length == 0) {
|
||||
// birthday
|
||||
if (day.day == 1) {
|
||||
// kaya
|
||||
if ((inventory.Affiliations.find(x => x.Tag == "HexSyndicate")?.Title || 0) >= 4) {
|
||||
break;
|
||||
}
|
||||
logger.debug(`cannot talk to kaya, skipping birthday`);
|
||||
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||
} else if (day.day == 74 || day.day == 355) {
|
||||
// minerva, velimir
|
||||
if ((inventory.Affiliations.find(x => x.Tag == "HexSyndicate")?.Title || 0) >= 5) {
|
||||
break;
|
||||
}
|
||||
logger.debug(`cannot talk to minerva/velimir, skipping birthday`);
|
||||
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (day.events[0].type == "CET_CHALLENGE") {
|
||||
if (calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx < dayIndex) {
|
||||
break;
|
||||
}
|
||||
//logger.debug(`already completed the challenge, skipping ahead`);
|
||||
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -32,7 +32,7 @@ export const getUsernameFromEmail = async (email: string): Promise<string> => {
|
||||
name = nameFromEmail + suffix;
|
||||
} while (await isNameTaken(name));
|
||||
}
|
||||
return nameFromEmail;
|
||||
return name;
|
||||
};
|
||||
|
||||
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
|
||||
|
@ -50,7 +50,7 @@ import { getEntriesUnsafe } from "@/src/utils/ts-utils";
|
||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { IMissionCredits, IMissionReward } from "@/src/types/missionTypes";
|
||||
import { crackRelic } from "@/src/helpers/relicHelper";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { createMessage, IMessageCreationTemplate } from "@/src/services/inboxService";
|
||||
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
|
||||
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
|
||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
|
||||
@ -76,13 +76,20 @@ import {
|
||||
} from "@/src/services/worldStateService";
|
||||
import { config } from "@/src/services/configService";
|
||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||
import { ISyndicateMissionInfo } from "@/src/types/worldStateTypes";
|
||||
import { IGoal, ISyndicateMissionInfo } from "@/src/types/worldStateTypes";
|
||||
import { fromOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { TAccountDocument } from "@/src/services/loginService";
|
||||
import { ITypeCount } from "@/src/types/commonTypes";
|
||||
import { IEquipmentClient } from "@/src/types/equipmentTypes";
|
||||
import { Guild } from "@/src/models/guildModel";
|
||||
import { handleGuildGoalProgress } from "@/src/services/guildService";
|
||||
|
||||
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
|
||||
// Disruption missions just tell us (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2599)
|
||||
if (rewardInfo.rewardTierOverrides) {
|
||||
return rewardInfo.rewardTierOverrides;
|
||||
}
|
||||
|
||||
// For Spy missions, e.g. 3 vaults cracked = A, B, C
|
||||
if (rewardInfo.VaultsCracked) {
|
||||
const rotations: number[] = [];
|
||||
@ -92,14 +99,23 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
||||
return rotations;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const missionIndex: number | undefined = ExportRegions[rewardInfo.node]?.missionIndex;
|
||||
const region = ExportRegions[rewardInfo.node] as IRegion | undefined;
|
||||
const missionIndex: number | undefined = region?.missionIndex;
|
||||
|
||||
// For Rescue missions
|
||||
if (missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
return [rewardInfo.rewardTier];
|
||||
}
|
||||
|
||||
// 'rewardQualifications' is unreliable for non-endless railjack missions (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2586, https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2612)
|
||||
switch (region?.missionName) {
|
||||
case "/Lotus/Language/Missions/MissionName_Railjack":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackVolatile":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackExterminate":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackAssassinate":
|
||||
return [0];
|
||||
}
|
||||
|
||||
const rotationCount = rewardInfo.rewardQualifications?.length || 0;
|
||||
|
||||
// Empty or absent rewardQualifications should not give rewards when:
|
||||
@ -292,7 +308,7 @@ export const addMissionInventoryUpdates = async (
|
||||
addRecipes(inventory, value);
|
||||
break;
|
||||
case "ChallengeProgress":
|
||||
addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
|
||||
await addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
|
||||
break;
|
||||
case "FusionTreasures":
|
||||
addFusionTreasures(inventory, value);
|
||||
@ -558,6 +574,7 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
]);
|
||||
}
|
||||
inventory.DeathSquadable = false;
|
||||
break;
|
||||
}
|
||||
case "LockedWeaponGroup": {
|
||||
@ -576,7 +593,7 @@ export const addMissionInventoryUpdates = async (
|
||||
break;
|
||||
}
|
||||
case "IncHarvester": {
|
||||
inventory.Harvestable = true;
|
||||
// Unsure what to do with this
|
||||
break;
|
||||
}
|
||||
case "CurrentLoadOutIds": {
|
||||
@ -613,37 +630,103 @@ export const addMissionInventoryUpdates = async (
|
||||
if (goal && goal.Personal) {
|
||||
inventory.PersonalGoalProgress ??= [];
|
||||
const goalProgress = inventory.PersonalGoalProgress.find(x => x.goalId.equals(goal._id.$oid));
|
||||
if (goalProgress) {
|
||||
goalProgress.Best = Math.max(goalProgress.Best, uploadProgress.Best);
|
||||
goalProgress.Count += uploadProgress.Count;
|
||||
} else {
|
||||
if (!goalProgress) {
|
||||
inventory.PersonalGoalProgress.push({
|
||||
Best: uploadProgress.Best,
|
||||
Count: uploadProgress.Count,
|
||||
Tag: goal.Tag,
|
||||
goalId: new Types.ObjectId(goal._id.$oid)
|
||||
});
|
||||
}
|
||||
|
||||
const currentNode = inventoryUpdates.RewardInfo!.node;
|
||||
let currentMissionKey;
|
||||
if (currentNode == goal.Node) {
|
||||
currentMissionKey = goal.MissionKeyName;
|
||||
} else if (goal.ConcurrentNodes && goal.ConcurrentMissionKeyNames) {
|
||||
for (let i = 0; i < goal.ConcurrentNodes.length; i++) {
|
||||
if (currentNode == goal.ConcurrentNodes[i]) {
|
||||
currentMissionKey = goal.ConcurrentMissionKeyNames[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentMissionKey && currentMissionKey in goalMessagesByKey) {
|
||||
const totalCount = (goalProgress?.Count ?? 0) + uploadProgress.Count;
|
||||
let reward;
|
||||
|
||||
if (goal.InterimGoals && goal.InterimRewards) {
|
||||
for (let i = 0; i < goal.InterimGoals.length; i++) {
|
||||
if (
|
||||
goal.InterimGoals[i] &&
|
||||
goal.InterimGoals[i] <= totalCount &&
|
||||
(!goalProgress || goalProgress.Count < goal.InterimGoals[i]) &&
|
||||
goal.InterimRewards[i]
|
||||
) {
|
||||
reward = goal.InterimRewards[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
goal.Reward &&
|
||||
goal.Reward.items &&
|
||||
goal.MissionKeyName &&
|
||||
goal.MissionKeyName in goalMessagesByKey
|
||||
!reward &&
|
||||
goal.Goal &&
|
||||
goal.Goal <= totalCount &&
|
||||
(!goalProgress || goalProgress.Count < goal.Goal) &&
|
||||
goal.Reward
|
||||
) {
|
||||
// Send reward via inbox
|
||||
const info = goalMessagesByKey[goal.MissionKeyName];
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
reward = goal.Reward;
|
||||
}
|
||||
if (
|
||||
!reward &&
|
||||
goal.BonusGoal &&
|
||||
goal.BonusGoal <= totalCount &&
|
||||
(!goalProgress || goalProgress.Count < goal.BonusGoal) &&
|
||||
goal.BonusReward
|
||||
) {
|
||||
reward = goal.BonusReward;
|
||||
}
|
||||
if (reward) {
|
||||
if (currentMissionKey in goalMessagesByKey) {
|
||||
// Send reward via inbox
|
||||
const info = goalMessagesByKey[currentMissionKey];
|
||||
const message: IMessageCreationTemplate = {
|
||||
sndr: info.sndr,
|
||||
msg: info.msg,
|
||||
att: goal.Reward.items.map(x => (isStoreItem(x) ? fromStoreItem(x) : x)),
|
||||
sub: info.sub,
|
||||
icon: info.icon,
|
||||
highPriority: true
|
||||
};
|
||||
|
||||
if (reward.items) {
|
||||
message.att = reward.items.map(x => (isStoreItem(x) ? fromStoreItem(x) : x));
|
||||
}
|
||||
]);
|
||||
if (reward.countedItems) {
|
||||
message.countedAtt = reward.countedItems;
|
||||
}
|
||||
if (reward.credits) {
|
||||
message.RegularCredits = reward.credits;
|
||||
}
|
||||
|
||||
await createMessage(inventory.accountOwnerId, [message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (goalProgress) {
|
||||
goalProgress.Best = Math.max(goalProgress.Best!, uploadProgress.Best);
|
||||
goalProgress.Count += uploadProgress.Count;
|
||||
}
|
||||
}
|
||||
if (goal && goal.ClanGoal && inventory.GuildId) {
|
||||
const guild = await Guild.findById(inventory.GuildId, "GoalProgress Tier VaultDecoRecipes");
|
||||
if (guild) {
|
||||
await handleGuildGoalProgress(guild, {
|
||||
Count: uploadProgress.Count,
|
||||
Tag: goal.Tag,
|
||||
goalId: new Types.ObjectId(goal._id.$oid)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -770,26 +853,24 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
}
|
||||
|
||||
if (value.killed) {
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: manifest.messageBody,
|
||||
arg: [
|
||||
{
|
||||
Key: "LICH_NAME",
|
||||
Tag: value.nemesisName
|
||||
}
|
||||
],
|
||||
att: att,
|
||||
countedAtt: countedAtt,
|
||||
attVisualOnly: true,
|
||||
sub: manifest.messageTitle,
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: value.killed ? "/Lotus/Language/Bosses/Ordis" : value.nemesisName,
|
||||
msg: value.killed ? manifest.killMessageBody : manifest.convertMessageBody,
|
||||
arg: [
|
||||
{
|
||||
Key: "LICH_NAME",
|
||||
Tag: value.nemesisName
|
||||
}
|
||||
],
|
||||
att: att,
|
||||
countedAtt: countedAtt,
|
||||
attVisualOnly: true,
|
||||
sub: value.killed ? manifest.killMessageSubject : manifest.convertMessageSubject,
|
||||
icon: value.killed ? "/Lotus/Interface/Icons/Npcs/Ordis.png" : manifest.convertMessageIcon,
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
|
||||
inventory.Nemesis = undefined;
|
||||
}
|
||||
@ -971,7 +1052,8 @@ export const addMissionRewards = async (
|
||||
Missions: missions,
|
||||
RegularCredits: creditDrops,
|
||||
VoidTearParticipantsCurrWave: voidTearWave,
|
||||
StrippedItems: strippedItems
|
||||
StrippedItems: strippedItems,
|
||||
AffiliationChanges: AffiliationMods
|
||||
}: IMissionInventoryUpdateRequest,
|
||||
firstCompletion: boolean
|
||||
): Promise<AddMissionRewardsReturnType> => {
|
||||
@ -991,7 +1073,6 @@ export const addMissionRewards = async (
|
||||
);
|
||||
logger.debug("random mission drops:", MissionRewards);
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const AffiliationMods: IAffiliationMods[] = [];
|
||||
let SyndicateXPItemReward;
|
||||
let ConquestCompletedMissionsCount;
|
||||
|
||||
@ -1000,8 +1081,16 @@ export const addMissionRewards = async (
|
||||
|
||||
if (rewardInfo.goalId) {
|
||||
const goal = getWorldState().Goals.find(x => x._id.$oid == rewardInfo.goalId);
|
||||
if (goal?.MissionKeyName) {
|
||||
levelKeyName = goal.MissionKeyName;
|
||||
if (goal) {
|
||||
if (rewardInfo.node == goal.Node && goal.MissionKeyName) levelKeyName = goal.MissionKeyName;
|
||||
if (goal.ConcurrentNodes && goal.ConcurrentMissionKeyNames) {
|
||||
for (let i = 0; i < goal.ConcurrentNodes.length && i < goal.ConcurrentMissionKeyNames.length; i++) {
|
||||
if (rewardInfo.node == goal.ConcurrentNodes[i]) {
|
||||
levelKeyName = goal.ConcurrentMissionKeyNames[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1220,6 +1309,27 @@ export const addMissionRewards = async (
|
||||
logger.error(`unknown droptable ${si.DropTable} for DROP_BLUEPRINT`);
|
||||
}
|
||||
}
|
||||
// e.g. H-09 Apex Turret Sumdali
|
||||
if (si.DROP_MISC_ITEM) {
|
||||
const resourceDroptable = droptables.find(x => x.type == "resource");
|
||||
if (resourceDroptable) {
|
||||
for (let i = 0; i != si.DROP_MISC_ITEM.length; ++i) {
|
||||
const reward = getRandomReward(resourceDroptable.items)!;
|
||||
logger.debug(`stripped droptable (resources pool) rolled`, reward);
|
||||
if (Object.keys(await addItem(inventory, reward.type)).length == 0) {
|
||||
logger.debug(`item already owned, skipping`);
|
||||
} else {
|
||||
MissionRewards.push({
|
||||
StoreItem: toStoreItem(reward.type),
|
||||
ItemCount: 1,
|
||||
FromEnemyCache: true // to show "identified"
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error(`unknown droptable ${si.DropTable} for DROP_BLUEPRINT`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,6 +1363,8 @@ export const addMissionRewards = async (
|
||||
}
|
||||
}
|
||||
|
||||
AffiliationMods ??= [];
|
||||
|
||||
if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [jobType, unkIndex, hubNode, syndicateMissionId] = rewardInfo.jobId.split("_");
|
||||
@ -1260,9 +1372,29 @@ export const addMissionRewards = async (
|
||||
if (syndicateMissionId) {
|
||||
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||
}
|
||||
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||
let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
|
||||
m => m._id.$oid === syndicateMissionId
|
||||
);
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
|
||||
].some(prefix => jobType.startsWith(prefix))
|
||||
) {
|
||||
const { Goals } = getWorldState(undefined);
|
||||
syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
|
||||
if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
|
||||
}
|
||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
|
||||
].some(prefix => jobType.startsWith(prefix))
|
||||
) {
|
||||
currentJob = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
|
||||
}
|
||||
if (syndicateEntry.Tag === "EntratiSyndicate") {
|
||||
if (
|
||||
[
|
||||
@ -1282,9 +1414,7 @@ export const addMissionRewards = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
let medallionAmount = Math.floor(
|
||||
Math.min(rewardInfo.JobStage, currentJob.xpAmounts.length - 1) / (rewardInfo.Q ? 0.8 : 1)
|
||||
);
|
||||
let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
|
||||
if (
|
||||
["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
|
||||
ending => jobType.endsWith(ending)
|
||||
@ -1303,35 +1433,45 @@ export const addMissionRewards = async (
|
||||
ItemCount: medallionAmount
|
||||
});
|
||||
SyndicateXPItemReward = medallionAmount;
|
||||
logger.debug(
|
||||
`Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
|
||||
);
|
||||
} else {
|
||||
if (rewardInfo.JobTier! >= 0) {
|
||||
const specialCase = [
|
||||
{ endings: ["Heists/HeistProfitTakerBountyOne"], stage: 2, amount: 1000 },
|
||||
{ endings: ["Hunts/AllTeralystsHunt"], stage: 2, amount: 5000 },
|
||||
{
|
||||
endings: [
|
||||
"Hunts/TeralystHunt",
|
||||
"Heists/HeistProfitTakerBountyTwo",
|
||||
"Heists/HeistProfitTakerBountyThree",
|
||||
"Heists/HeistProfitTakerBountyFour",
|
||||
"Heists/HeistExploiterBountyOne"
|
||||
],
|
||||
amount: 1000
|
||||
}
|
||||
];
|
||||
const specialCaseReward = specialCase.find(
|
||||
rule =>
|
||||
rule.endings.some(e => jobType.endsWith(e)) &&
|
||||
(rule.stage === undefined || rewardInfo.JobStage === rule.stage)
|
||||
);
|
||||
|
||||
if (specialCaseReward) {
|
||||
addStanding(inventory, syndicateEntry.Tag, specialCaseReward.amount, AffiliationMods);
|
||||
} else {
|
||||
addStanding(
|
||||
inventory,
|
||||
syndicateEntry.Tag,
|
||||
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
|
||||
AffiliationMods
|
||||
);
|
||||
} else {
|
||||
if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
|
||||
addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
|
||||
}
|
||||
if (jobType.endsWith("Hunts/AllTeralystsHunt") && rewardInfo.JobStage === 2) {
|
||||
addStanding(inventory, syndicateEntry.Tag, 5000, AffiliationMods);
|
||||
}
|
||||
if (
|
||||
[
|
||||
"Hunts/TeralystHunt",
|
||||
"Heists/HeistProfitTakerBountyTwo",
|
||||
"Heists/HeistProfitTakerBountyThree",
|
||||
"Heists/HeistProfitTakerBountyFour",
|
||||
"Heists/HeistExploiterBountyOne"
|
||||
].some(ending => jobType.endsWith(ending))
|
||||
) {
|
||||
addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
|
||||
addStanding(inventory, "CetusSyndicate", Math.floor(200 / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
|
||||
}
|
||||
}
|
||||
|
||||
if (rewardInfo.challengeMissionId) {
|
||||
@ -1348,6 +1488,7 @@ export const addMissionRewards = async (
|
||||
ItemCount: medallionAmount
|
||||
});
|
||||
SyndicateXPItemReward = medallionAmount;
|
||||
logger.debug(`Giving ${medallionAmount} medallions for the ${tier} tier bounty`);
|
||||
} else {
|
||||
let standingAmount = (tier + 1) * 1000;
|
||||
if (tier > 5) standingAmount = 7500; // InfestedLichBounty
|
||||
@ -1636,7 +1777,10 @@ function getRandomMissionDrops(
|
||||
rewardManifests = [
|
||||
"/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriMurmurFinalSteelChestRewards"
|
||||
];
|
||||
} else if (RewardInfo.T == 70) {
|
||||
} else if (
|
||||
RewardInfo.T == 70 ||
|
||||
RewardInfo.T == 6 // https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2526
|
||||
) {
|
||||
// Orowyrm chest, gives 10 Pathos Clamps, or 15 on Steel Path.
|
||||
drops.push({
|
||||
StoreItem: "/Lotus/StoreItems/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
|
||||
@ -1658,7 +1802,19 @@ function getRandomMissionDrops(
|
||||
if (syndicateMissionId) {
|
||||
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||
}
|
||||
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||
let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
|
||||
m => m._id.$oid === syndicateMissionId
|
||||
);
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
|
||||
].some(prefix => jobType.startsWith(prefix))
|
||||
) {
|
||||
const { Goals } = getWorldState(undefined);
|
||||
syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
|
||||
if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
|
||||
}
|
||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
|
||||
|
||||
@ -1743,6 +1899,14 @@ function getRandomMissionDrops(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
|
||||
"/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
|
||||
].some(prefix => jobType.startsWith(prefix))
|
||||
) {
|
||||
job = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
|
||||
}
|
||||
rewardManifests = [job.rewards];
|
||||
if (job.xpAmounts.length > 1) {
|
||||
const curentStage = RewardInfo.JobStage! + 1;
|
||||
@ -1770,6 +1934,11 @@ function getRandomMissionDrops(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableARewards"];
|
||||
rotations = [3];
|
||||
if (RewardInfo.Q) rotations.push(3);
|
||||
}
|
||||
}
|
||||
} else if (RewardInfo.challengeMissionId) {
|
||||
const rewardTables: Record<string, string[]> = {
|
||||
@ -1866,6 +2035,36 @@ function getRandomMissionDrops(
|
||||
}
|
||||
});
|
||||
|
||||
// Railjack Abandoned Cache Rewards, Rotation A (Mandatory Objectives)
|
||||
if (RewardInfo.POICompletions) {
|
||||
if (region.cacheRewardManifest) {
|
||||
const deck = ExportRewards[region.cacheRewardManifest];
|
||||
for (let cache = 0; cache != RewardInfo.POICompletions; ++cache) {
|
||||
const drop = getRandomRewardByChance(deck[0]);
|
||||
if (drop) {
|
||||
drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount, FromEnemyCache: true });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error(`POI completed, but there was no cache reward manifest at ${RewardInfo.node}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Railjack Abandoned Cache Rewards, Rotation B (Optional Objectives)
|
||||
if (RewardInfo.LootDungeonCompletions) {
|
||||
if (region.cacheRewardManifest) {
|
||||
const deck = ExportRewards[region.cacheRewardManifest];
|
||||
for (let cache = 0; cache != RewardInfo.LootDungeonCompletions; ++cache) {
|
||||
const drop = getRandomRewardByChance(deck[1]);
|
||||
if (drop) {
|
||||
drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount, FromEnemyCache: true });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error(`Loot dungeon completed, but there was no cache reward manifest at ${RewardInfo.node}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (region.cacheRewardManifest && RewardInfo.EnemyCachesFound) {
|
||||
const deck = ExportRewards[region.cacheRewardManifest];
|
||||
for (let rotation = 0; rotation != RewardInfo.EnemyCachesFound; ++rotation) {
|
||||
@ -2079,5 +2278,143 @@ const goalMessagesByKey: Record<string, { sndr: string; msg: string; sub: string
|
||||
msg: "/Lotus/Language/Messages/GalleonRobbery2025RewardMsgC",
|
||||
sub: "/Lotus/Language/Messages/GalleonRobbery2025MissionTitleC",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyWaterFightA": {
|
||||
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
|
||||
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgA",
|
||||
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectA",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyWaterFightB": {
|
||||
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
|
||||
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgB",
|
||||
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectB",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyWaterFightC": {
|
||||
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
|
||||
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgC",
|
||||
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectC",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyWaterFightD": {
|
||||
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
|
||||
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgD",
|
||||
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectD",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/WolfTacAlertReduxA": {
|
||||
sndr: "/Lotus/Language/Bosses/NoraNight",
|
||||
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
|
||||
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/WolfTacAlertReduxB": {
|
||||
sndr: "/Lotus/Language/Bosses/NoraNight",
|
||||
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
|
||||
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/WolfTacAlertReduxD": {
|
||||
sndr: "/Lotus/Language/Bosses/NoraNight",
|
||||
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
|
||||
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/WolfTacAlertReduxC": {
|
||||
sndr: "/Lotus/Language/Bosses/NoraNight",
|
||||
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
|
||||
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/LanternEndlessEventKeyA": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/LanternEndlessEventKeyB": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/LanternEndlessEventKeyD": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/LanternEndlessEventKeyC": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyHalloween": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBonusBody",
|
||||
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBonusTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyHalloweenBonus": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBody",
|
||||
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyHalloweenTimeAttack": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBody",
|
||||
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionOne": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionTwo": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionThree": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionFour": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/GenericTacAlertBadgeRewardMsgDesc",
|
||||
sub: "/Lotus/Language/G1Quests/GenericTacAlertBadgeRewardMsgTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchEasy": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/ProjectNightwatchRewardMsgA",
|
||||
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionOneTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatch": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionTwoTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchHard": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionThreeTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
},
|
||||
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchBonus": {
|
||||
sndr: "/Lotus/Language/Bosses/Lotus",
|
||||
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
|
||||
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionFourTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
|
||||
import { parseSlotPurchaseName, slotPurchaseNameToSlotName } from "@/src/helpers/purchaseHelpers";
|
||||
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
|
||||
import {
|
||||
addBooster,
|
||||
@ -14,7 +14,6 @@ import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import {
|
||||
IPurchaseRequest,
|
||||
IPurchaseResponse,
|
||||
SlotPurchase,
|
||||
IInventoryChanges,
|
||||
PurchaseSource,
|
||||
IPurchaseParams
|
||||
@ -37,6 +36,9 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
|
||||
import { fromStoreItem, toStoreItem } from "@/src/services/itemDataService";
|
||||
import { DailyDeal } from "@/src/models/worldStateModel";
|
||||
import { fromMongoDate, toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { Guild } from "@/src/models/guildModel";
|
||||
import { handleGuildGoalProgress } from "@/src/services/guildService";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const getStoreItemCategory = (storeItem: string): string => {
|
||||
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
|
||||
@ -138,6 +140,22 @@ export const handlePurchase = async (
|
||||
updateCurrency(inventory, offer.PremiumPrice[0], true, prePurchaseInventoryChanges);
|
||||
}
|
||||
}
|
||||
if (
|
||||
inventory.GuildId &&
|
||||
offer.ItemPrices &&
|
||||
manifest.VendorInfo.TypeName ==
|
||||
"/Lotus/Types/Game/VendorManifests/Events/DuviriMurmurInvasionVendorManifest"
|
||||
) {
|
||||
const guild = await Guild.findById(inventory.GuildId, "GoalProgress Tier VaultDecoRecipes");
|
||||
const goal = getWorldState().Goals.find(x => x.Tag == "DuviriMurmurEvent");
|
||||
if (guild && goal) {
|
||||
await handleGuildGoalProgress(guild, {
|
||||
Count: offer.ItemPrices[0].ItemCount * purchaseRequest.PurchaseParams.Quantity,
|
||||
Tag: goal.Tag,
|
||||
goalId: new Types.ObjectId(goal._id.$oid)
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!config.dontSubtractPurchaseItemCost) {
|
||||
if (offer.ItemPrices) {
|
||||
handleItemPrices(
|
||||
@ -328,7 +346,7 @@ export const handlePurchase = async (
|
||||
purchaseResponse.InventoryChanges.MiscItems ??= [];
|
||||
purchaseResponse.InventoryChanges.MiscItems.push(invItem);
|
||||
}
|
||||
} else if (!config.infiniteRegalAya) {
|
||||
} else if (!inventory.infiniteRegalAya) {
|
||||
inventory.PrimeTokens -= offer.PrimePrice! * purchaseRequest.PurchaseParams.Quantity;
|
||||
|
||||
purchaseResponse.InventoryChanges.PrimeTokens ??= 0;
|
||||
@ -472,19 +490,6 @@ export const handleStoreItemAcquisition = async (
|
||||
return purchaseResponse;
|
||||
};
|
||||
|
||||
export const slotPurchaseNameToSlotName: SlotPurchase = {
|
||||
SuitSlotItem: { name: "SuitBin", purchaseQuantity: 1 },
|
||||
TwoSentinelSlotItem: { name: "SentinelBin", purchaseQuantity: 2 },
|
||||
TwoWeaponSlotItem: { name: "WeaponBin", purchaseQuantity: 2 },
|
||||
SpaceSuitSlotItem: { name: "SpaceSuitBin", purchaseQuantity: 1 },
|
||||
TwoSpaceWeaponSlotItem: { name: "SpaceWeaponBin", purchaseQuantity: 2 },
|
||||
MechSlotItem: { name: "MechBin", purchaseQuantity: 1 },
|
||||
TwoOperatorWeaponSlotItem: { name: "OperatorAmpBin", purchaseQuantity: 2 },
|
||||
RandomModSlotItem: { name: "RandomModBin", purchaseQuantity: 3 },
|
||||
TwoCrewShipSalvageSlotItem: { name: "CrewShipSalvageBin", purchaseQuantity: 2 },
|
||||
CrewMemberSlotItem: { name: "CrewMemberBin", purchaseQuantity: 1 }
|
||||
};
|
||||
|
||||
// // extra = everything above the base +2 slots (depending on slot type)
|
||||
// // new slot above base = extra + 1 and slots +1
|
||||
// // new frame = slots -1
|
||||
@ -581,7 +586,7 @@ const handleBoosterPackPurchase = async (
|
||||
purchaseResponse.InventoryChanges,
|
||||
await addItem(inventory, specialItemReward.Item)
|
||||
);
|
||||
// TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
|
||||
atten.Atten = 0;
|
||||
} else {
|
||||
atten.Atten += specialItemReward.PityIncreaseRate!;
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ const handleQuestCompletion = async (
|
||||
setupKahlSyndicate(inventory);
|
||||
}
|
||||
|
||||
// Whispers in the Walls is unlocked once The New + Heart of Deimos are completed.
|
||||
// Whispers in the Walls is unlocked once The New War + Heart of Deimos are completed.
|
||||
if (
|
||||
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||
"/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain",
|
||||
|
@ -151,4 +151,57 @@ export class SRng {
|
||||
arr[lastIdx] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
shuffledArray<T>(inarr: readonly T[]): T[] {
|
||||
const arr = [...inarr];
|
||||
this.shuffleArray(arr);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
export const sequentiallyUniqueRandomElement = <T>(
|
||||
deck: readonly T[],
|
||||
idx: number,
|
||||
lookbehind: number,
|
||||
seed: number = 0
|
||||
): T | undefined => {
|
||||
// This algorithm may modify a shuffle up to index `lookbehind + 1`. It assumes that the last `lookbehind` cards are not adjusted.
|
||||
if (lookbehind + 1 >= deck.length - lookbehind) {
|
||||
throw new Error(
|
||||
`this algorithm cannot guarantee ${lookbehind} unique cards in a row with a deck of size ${deck.length}`
|
||||
);
|
||||
}
|
||||
|
||||
const iteration = Math.trunc(idx / deck.length);
|
||||
const card = idx % deck.length;
|
||||
const currentShuffle = new SRng(mixSeeds(new SRng(iteration).randomInt(0, 100_000), seed)).shuffledArray(deck);
|
||||
if (card < currentShuffle.length - lookbehind) {
|
||||
// We are indexing before the end of the deck, so adjustments may be needed to achieve uniqueness.
|
||||
const window: T[] = [];
|
||||
{
|
||||
const previousShuffle = new SRng(
|
||||
mixSeeds(new SRng(iteration - 1).randomInt(0, 100_000), seed)
|
||||
).shuffledArray(deck);
|
||||
for (let i = previousShuffle.length - lookbehind; i != previousShuffle.length; ++i) {
|
||||
window.push(previousShuffle[i]);
|
||||
}
|
||||
}
|
||||
// From this point on, `window.length == lookbehind` should hold.
|
||||
for (let i = 0; i != lookbehind; ++i) {
|
||||
if (window.indexOf(currentShuffle[i]) != -1) {
|
||||
for (let j = i; ; ++j) {
|
||||
// `j < currentShuffle.length - lookbehind` should hold.
|
||||
if (window.indexOf(currentShuffle[j]) == -1) {
|
||||
const tmp = currentShuffle[j];
|
||||
currentShuffle[j] = currentShuffle[i];
|
||||
currentShuffle[i] = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
window.splice(0, 1);
|
||||
window.push(currentShuffle[i]);
|
||||
}
|
||||
}
|
||||
return currentShuffle[card];
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getShip } from "@/src/services/shipService";
|
||||
import {
|
||||
IResetShipDecorationsRequest,
|
||||
IResetShipDecorationsResponse,
|
||||
ISetPlacedDecoInfoRequest,
|
||||
ISetShipCustomizationsRequest,
|
||||
IShipDecorationsRequest,
|
||||
@ -17,6 +19,7 @@ import { Guild } from "@/src/models/guildModel";
|
||||
import { hasGuildPermission } from "@/src/services/guildService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { ExportResources } from "warframe-public-export-plus";
|
||||
import { convertCustomizationInfo } from "@/src/services/importService";
|
||||
|
||||
export const setShipCustomizations = async (
|
||||
accountId: string,
|
||||
@ -52,12 +55,7 @@ export const handleSetShipDecorations = async (
|
||||
): Promise<IShipDecorationsResponse> => {
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
|
||||
const rooms =
|
||||
placedDecoration.BootLocation == "SHOP"
|
||||
? personalRooms.TailorShop.Rooms
|
||||
: placedDecoration.IsApartment
|
||||
? personalRooms.Apartment.Rooms
|
||||
: personalRooms.Ship.Rooms;
|
||||
const rooms = getRoomsForBootLocation(personalRooms, placedDecoration);
|
||||
|
||||
const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
|
||||
|
||||
@ -159,7 +157,6 @@ export const handleSetShipDecorations = async (
|
||||
|
||||
if (!config.unlockAllShipDecorations) {
|
||||
const inventory = await getInventory(accountId);
|
||||
const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == placedDecoration.Type)![0];
|
||||
if (placedDecoration.Sockets !== undefined) {
|
||||
addFusionTreasures(inventory, [{ ItemType: itemType, Sockets: placedDecoration.Sockets, ItemCount: -1 }]);
|
||||
} else {
|
||||
@ -192,17 +189,62 @@ export const handleSetShipDecorations = async (
|
||||
|
||||
const getRoomsForBootLocation = (
|
||||
personalRooms: TPersonalRoomsDatabaseDocument,
|
||||
bootLocation: TBootLocation | undefined
|
||||
request: { BootLocation?: TBootLocation; IsApartment?: boolean }
|
||||
): RoomsType[] => {
|
||||
if (bootLocation == "SHOP") {
|
||||
if (request.BootLocation == "SHOP") {
|
||||
return personalRooms.TailorShop.Rooms;
|
||||
}
|
||||
if (bootLocation == "APARTMENT") {
|
||||
if (request.BootLocation == "APARTMENT" || request.IsApartment) {
|
||||
return personalRooms.Apartment.Rooms;
|
||||
}
|
||||
return personalRooms.Ship.Rooms;
|
||||
};
|
||||
|
||||
export const handleResetShipDecorations = async (
|
||||
accountId: string,
|
||||
request: IResetShipDecorationsRequest
|
||||
): Promise<IResetShipDecorationsResponse> => {
|
||||
const [personalRooms, inventory] = await Promise.all([getPersonalRooms(accountId), getInventory(accountId)]);
|
||||
const room = getRoomsForBootLocation(personalRooms, request).find(room => room.Name === request.Room);
|
||||
if (!room) {
|
||||
throw new Error(`unknown room: ${request.Room}`);
|
||||
}
|
||||
|
||||
for (const deco of room.PlacedDecos) {
|
||||
const entry = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type);
|
||||
if (!entry) {
|
||||
throw new Error(`unknown deco type: ${deco.Type}`);
|
||||
}
|
||||
const [itemType, meta] = entry;
|
||||
if (meta.capacityCost === undefined) {
|
||||
throw new Error(`unknown deco type: ${deco.Type}`);
|
||||
}
|
||||
|
||||
// refund item
|
||||
if (!config.unlockAllShipDecorations) {
|
||||
if (deco.Sockets !== undefined) {
|
||||
addFusionTreasures(inventory, [{ ItemType: itemType, Sockets: deco.Sockets, ItemCount: 1 }]);
|
||||
} else {
|
||||
addShipDecorations(inventory, [{ ItemType: itemType, ItemCount: 1 }]);
|
||||
}
|
||||
}
|
||||
|
||||
// refund capacity
|
||||
room.MaxCapacity += meta.capacityCost;
|
||||
}
|
||||
|
||||
// empty room
|
||||
room.PlacedDecos.splice(0, room.PlacedDecos.length);
|
||||
|
||||
await Promise.all([personalRooms.save(), inventory.save()]);
|
||||
|
||||
return {
|
||||
ResetRoom: request.Room,
|
||||
ClaimedDecos: [], // Not sure what this is for; the client already implies that the decos were returned to inventory.
|
||||
NewCapacity: room.MaxCapacity
|
||||
};
|
||||
};
|
||||
|
||||
export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => {
|
||||
if (req.GuildId && req.ComponentId) {
|
||||
const guild = (await Guild.findById(req.GuildId))!;
|
||||
@ -217,7 +259,7 @@ export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlaced
|
||||
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
|
||||
const room = getRoomsForBootLocation(personalRooms, req.BootLocation).find(room => room.Name === req.Room);
|
||||
const room = getRoomsForBootLocation(personalRooms, req).find(room => room.Name === req.Room);
|
||||
if (!room) {
|
||||
throw new Error(`unknown room: ${req.Room}`);
|
||||
}
|
||||
@ -228,6 +270,8 @@ export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlaced
|
||||
}
|
||||
|
||||
placedDeco.PictureFrameInfo = req.PictureFrameInfo;
|
||||
placedDeco.CustomizationInfo = req.CustomizationInfo ? convertCustomizationInfo(req.CustomizationInfo) : undefined;
|
||||
placedDeco.AnimPoseItem = req.AnimPoseItem;
|
||||
|
||||
await personalRooms.save();
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ import { Account } from "@/src/models/loginModel";
|
||||
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "@/src/services/loginService";
|
||||
import { IDatabaseAccountJson } from "@/src/types/loginTypes";
|
||||
import { HydratedDocument } from "mongoose";
|
||||
import { logError } from "@/src/utils/logger";
|
||||
|
||||
let wsServer: ws.Server | undefined;
|
||||
let wssServer: ws.Server | undefined;
|
||||
@ -43,7 +44,7 @@ export const stopWsServers = (promises: Promise<void>[]): void => {
|
||||
let lastWsid: number = 0;
|
||||
|
||||
interface IWsCustomData extends ws {
|
||||
id?: number;
|
||||
id: number;
|
||||
accountId?: string;
|
||||
}
|
||||
|
||||
@ -88,63 +89,67 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on("message", async msg => {
|
||||
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||
if (data.auth) {
|
||||
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||
if (account) {
|
||||
if (isCorrectPassword(data.auth.password, account.password)) {
|
||||
if (!account.Nonce) {
|
||||
account.ClientType = "webui";
|
||||
account.Nonce = createNonce();
|
||||
await (account as HydratedDocument<IDatabaseAccountJson>).save();
|
||||
try {
|
||||
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||
if (data.auth) {
|
||||
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||
if (account) {
|
||||
if (isCorrectPassword(data.auth.password, account.password)) {
|
||||
if (!account.Nonce) {
|
||||
account.ClientType = "webui";
|
||||
account.Nonce = createNonce();
|
||||
await (account as HydratedDocument<IDatabaseAccountJson>).save();
|
||||
}
|
||||
} else {
|
||||
account = null;
|
||||
}
|
||||
} else if (data.auth.isRegister) {
|
||||
const name = await getUsernameFromEmail(data.auth.email);
|
||||
account = await createAccount({
|
||||
email: data.auth.email,
|
||||
password: data.auth.password,
|
||||
ClientType: "webui",
|
||||
LastLogin: new Date(),
|
||||
DisplayName: name,
|
||||
Nonce: createNonce()
|
||||
});
|
||||
}
|
||||
if (account) {
|
||||
(ws as IWsCustomData).accountId = account.id;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_succ: {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
Nonce: account.Nonce
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
} else {
|
||||
account = null;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_fail: {
|
||||
isRegister: data.auth.isRegister
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
}
|
||||
} else if (data.auth.isRegister) {
|
||||
const name = await getUsernameFromEmail(data.auth.email);
|
||||
account = await createAccount({
|
||||
email: data.auth.email,
|
||||
password: data.auth.password,
|
||||
ClientType: "webui",
|
||||
LastLogin: new Date(),
|
||||
DisplayName: name,
|
||||
Nonce: createNonce()
|
||||
});
|
||||
}
|
||||
if (account) {
|
||||
(ws as IWsCustomData).accountId = account.id;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_succ: {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
Nonce: account.Nonce
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
} else {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_fail: {
|
||||
isRegister: data.auth.isRegister
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
if (data.logout) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.logout) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e as Error, `processing websocket message`);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -181,18 +186,24 @@ export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void
|
||||
}
|
||||
};
|
||||
|
||||
export const sendWsBroadcastExcept = (wsid: number | undefined, data: IWsMsgToClient): void => {
|
||||
export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
if ((client as IWsCustomData).id != wsid) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
if ((client as IWsCustomData).id != wsid) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { Types } from "mongoose";
|
||||
import { IOid, IMongoDate, IOidWithLegacySupport, ITypeCount } from "@/src/types/commonTypes";
|
||||
import { IFusionTreasure, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import {
|
||||
IFusionTreasure,
|
||||
IMiscItem,
|
||||
IGoalProgressDatabase,
|
||||
IGoalProgressClient
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IPictureFrameInfo } from "@/src/types/personalRoomsTypes";
|
||||
import { IFriendInfo } from "@/src/types/friendTypes";
|
||||
|
||||
@ -23,6 +28,8 @@ export interface IGuildClient {
|
||||
CrossPlatformEnabled?: boolean;
|
||||
AutoContributeFromVault?: boolean;
|
||||
AllianceId?: IOidWithLegacySupport;
|
||||
|
||||
GoalProgress?: IGoalProgressClient[];
|
||||
}
|
||||
|
||||
export interface IGuildDatabase {
|
||||
@ -63,6 +70,8 @@ export interface IGuildDatabase {
|
||||
TechChanges?: IGuildLogEntryContributable[];
|
||||
RosterActivity?: IGuildLogEntryRoster[];
|
||||
ClassChanges?: IGuildLogEntryNumber[];
|
||||
|
||||
GoalProgress?: IGoalProgressDatabase[];
|
||||
}
|
||||
|
||||
export interface ILongMOTD {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Types } from "mongoose";
|
||||
import { IOid, IMongoDate, IOidWithLegacySupport, ITypeCount } from "@/src/types/commonTypes";
|
||||
import {
|
||||
@ -20,6 +19,15 @@ export type InventoryDatabaseEquipment = {
|
||||
[_ in TEquipmentKey]: IEquipmentDatabase[];
|
||||
};
|
||||
|
||||
// Fields specific to SNS
|
||||
export interface IAccountCheats {
|
||||
infiniteCredits?: boolean;
|
||||
infinitePlatinum?: boolean;
|
||||
infiniteEndo?: boolean;
|
||||
infiniteRegalAya?: boolean;
|
||||
infiniteHelminthMaterials?: boolean;
|
||||
}
|
||||
|
||||
export interface IInventoryDatabase
|
||||
extends Omit<
|
||||
IInventoryClient,
|
||||
@ -62,7 +70,8 @@ export interface IInventoryDatabase
|
||||
| "PersonalGoalProgress"
|
||||
| TEquipmentKey
|
||||
>,
|
||||
InventoryDatabaseEquipment {
|
||||
InventoryDatabaseEquipment,
|
||||
IAccountCheats {
|
||||
accountOwnerId: Types.ObjectId;
|
||||
Created: Date;
|
||||
TrainingDate: Date;
|
||||
@ -100,7 +109,7 @@ export interface IInventoryDatabase
|
||||
QualifyingInvasions: IInvasionProgressDatabase[];
|
||||
LastInventorySync?: Types.ObjectId;
|
||||
EndlessXP?: IEndlessXpProgressDatabase[];
|
||||
PersonalGoalProgress?: IPersonalGoalProgressDatabase[];
|
||||
PersonalGoalProgress?: IGoalProgressDatabase[];
|
||||
}
|
||||
|
||||
export interface IQuestKeyDatabase {
|
||||
@ -216,6 +225,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
PremiumCredits: number;
|
||||
PremiumCreditsFree: number;
|
||||
FusionPoints: number;
|
||||
CrewShipFusionPoints: number; //Dirac (pre-rework Railjack)
|
||||
PrimeTokens: number;
|
||||
SuitBin: ISlots;
|
||||
WeaponBin: ISlots;
|
||||
@ -308,7 +318,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
HWIDProtectEnabled?: boolean;
|
||||
KubrowPetPrints: IKubrowPetPrintClient[];
|
||||
AlignmentReplay?: IAlignment;
|
||||
PersonalGoalProgress?: IPersonalGoalProgressClient[];
|
||||
PersonalGoalProgress?: IGoalProgressClient[];
|
||||
ThemeStyle: string;
|
||||
ThemeBackground: string;
|
||||
ThemeSounds: string;
|
||||
@ -520,7 +530,8 @@ export enum InventorySlot {
|
||||
SENTINELS = "SentinelBin",
|
||||
AMPS = "OperatorAmpBin",
|
||||
RJ_COMPONENT_AND_ARMAMENTS = "CrewShipSalvageBin",
|
||||
CREWMEMBERS = "CrewMemberBin"
|
||||
CREWMEMBERS = "CrewMemberBin",
|
||||
RIVENS = "RandomModBin"
|
||||
}
|
||||
|
||||
export interface ISlots {
|
||||
@ -719,7 +730,7 @@ export enum UpgradeType {
|
||||
|
||||
export interface ILoreFragmentScan {
|
||||
Progress: number;
|
||||
Region?: string;
|
||||
Region: string;
|
||||
ItemType: string;
|
||||
}
|
||||
|
||||
@ -884,8 +895,8 @@ export interface IPeriodicMissionCompletionResponse extends Omit<IPeriodicMissio
|
||||
date: IMongoDate;
|
||||
}
|
||||
|
||||
export interface IPersonalGoalProgressClient {
|
||||
Best: number;
|
||||
export interface IGoalProgressClient {
|
||||
Best?: number;
|
||||
Count: number;
|
||||
Tag: string;
|
||||
_id: IOid;
|
||||
@ -893,7 +904,7 @@ export interface IPersonalGoalProgressClient {
|
||||
//ReceivedClanReward1?: boolean;
|
||||
}
|
||||
|
||||
export interface IPersonalGoalProgressDatabase extends Omit<IPersonalGoalProgressClient, "_id"> {
|
||||
export interface IGoalProgressDatabase extends Omit<IGoalProgressClient, "_id"> {
|
||||
goalId: Types.ObjectId;
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,19 @@ export interface IMissionCredits {
|
||||
DailyMissionBonus?: boolean;
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateResponse extends Partial<IMissionCredits> {
|
||||
export interface IMissionInventoryUpdateResponseRailjackInterstitial extends Partial<IMissionCredits> {
|
||||
ConquestCompletedMissionsCount?: number;
|
||||
InventoryJson?: string;
|
||||
MissionRewards?: IMissionReward[];
|
||||
InventoryChanges?: IInventoryChanges;
|
||||
FusionPoints?: number;
|
||||
SyndicateXPItemReward?: number;
|
||||
AffiliationMods?: IAffiliationMods[];
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateResponse extends IMissionInventoryUpdateResponseRailjackInterstitial {
|
||||
InventoryJson?: string;
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateResponseBackToDryDock {
|
||||
InventoryJson: string;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IColor, IShipAttachments, IShipCustomization } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { Document, Model, Types } from "mongoose";
|
||||
import { ILoadoutClient } from "@/src/types/saveLoadoutTypes";
|
||||
import { ILoadoutClient, ILoadoutConfigClient, ILoadoutConfigDatabase } from "@/src/types/saveLoadoutTypes";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
|
||||
export interface IGetShipResponse {
|
||||
@ -17,7 +17,7 @@ export interface IOrbiterClient {
|
||||
Features: string[];
|
||||
ShipId: IOid;
|
||||
ShipInterior: IShipCustomization;
|
||||
Rooms: IRoom[];
|
||||
Rooms: IRoomClient[];
|
||||
VignetteFish?: string[];
|
||||
FavouriteLoadoutId?: IOid;
|
||||
Wallpaper?: string;
|
||||
@ -28,7 +28,7 @@ export interface IOrbiterClient {
|
||||
|
||||
export interface IOrbiterDatabase {
|
||||
Features: string[];
|
||||
Rooms: IRoom[];
|
||||
Rooms: IRoomDatabase[];
|
||||
ShipInterior?: IShipCustomization;
|
||||
VignetteFish?: string[];
|
||||
FavouriteLoadoutId?: Types.ObjectId;
|
||||
@ -53,12 +53,18 @@ export interface IPersonalRoomsDatabase {
|
||||
TailorShop: ITailorShopDatabase;
|
||||
}
|
||||
|
||||
export interface IRoom {
|
||||
export interface IRoomDatabase {
|
||||
Name: string;
|
||||
MaxCapacity: number;
|
||||
PlacedDecos?: IPlacedDecosDatabase[];
|
||||
}
|
||||
|
||||
export interface IRoomClient {
|
||||
Name: string;
|
||||
MaxCapacity: number;
|
||||
PlacedDecos?: IPlacedDecosClient[];
|
||||
}
|
||||
|
||||
export interface IPlantClient {
|
||||
PlantType: string;
|
||||
EndTime: IMongoDate;
|
||||
@ -89,14 +95,18 @@ export interface IGardeningDatabase {
|
||||
|
||||
export interface IApartmentClient {
|
||||
Gardening: IGardeningClient;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadout[];
|
||||
Rooms: IRoomClient[];
|
||||
FavouriteLoadouts?: IFavouriteLoadout[];
|
||||
VideoWallBackdrop?: string;
|
||||
Soundscape?: string;
|
||||
}
|
||||
|
||||
export interface IApartmentDatabase {
|
||||
Gardening: IGardeningDatabase;
|
||||
Rooms: IRoom[];
|
||||
Rooms: IRoomDatabase[];
|
||||
FavouriteLoadouts: IFavouriteLoadoutDatabase[];
|
||||
VideoWallBackdrop?: string;
|
||||
Soundscape?: string;
|
||||
}
|
||||
|
||||
export interface IPlacedDecosDatabase {
|
||||
@ -106,11 +116,14 @@ export interface IPlacedDecosDatabase {
|
||||
Scale?: number;
|
||||
Sockets?: number;
|
||||
PictureFrameInfo?: IPictureFrameInfo;
|
||||
CustomizationInfo?: ICustomizationInfoDatabase;
|
||||
AnimPoseItem?: string;
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
export interface IPlacedDecosClient extends Omit<IPlacedDecosDatabase, "_id"> {
|
||||
export interface IPlacedDecosClient extends Omit<IPlacedDecosDatabase, "_id" | "CustomizationInfo"> {
|
||||
id: IOid;
|
||||
CustomizationInfo?: ICustomizationInfoClient;
|
||||
}
|
||||
|
||||
export interface ISetShipCustomizationsRequest {
|
||||
@ -150,12 +163,25 @@ export interface IShipDecorationsResponse {
|
||||
NewRoom?: string;
|
||||
}
|
||||
|
||||
export interface IResetShipDecorationsRequest {
|
||||
Room: string;
|
||||
BootLocation?: TBootLocation;
|
||||
}
|
||||
|
||||
export interface IResetShipDecorationsResponse {
|
||||
ResetRoom: string;
|
||||
ClaimedDecos: [];
|
||||
NewCapacity: number;
|
||||
}
|
||||
|
||||
export interface ISetPlacedDecoInfoRequest {
|
||||
DecoType: string;
|
||||
DecoType?: string;
|
||||
DecoId: string;
|
||||
Room: string;
|
||||
PictureFrameInfo: IPictureFrameInfo;
|
||||
PictureFrameInfo: IPictureFrameInfo; // IsPicture
|
||||
CustomizationInfo?: ICustomizationInfoClient; // !IsPicture
|
||||
BootLocation?: TBootLocation;
|
||||
AnimPoseItem?: string; // !IsPicture
|
||||
ComponentId?: string;
|
||||
GuildId?: string;
|
||||
}
|
||||
@ -176,6 +202,21 @@ export interface IPictureFrameInfo {
|
||||
TextOrientation: number;
|
||||
}
|
||||
|
||||
export interface ICustomizationInfoClient {
|
||||
Anim?: string;
|
||||
AnimPose?: number;
|
||||
LoadOutPreset?: ILoadoutConfigClient;
|
||||
VehiclePreset?: ILoadoutConfigClient;
|
||||
EquippedWeapon?: "SUIT_SLOT" | "LONG_GUN_SLOT" | "PISTOL_SLOT";
|
||||
AvatarType?: string;
|
||||
LoadOutType?: string; // "LOT_NORMAL"
|
||||
}
|
||||
|
||||
export interface ICustomizationInfoDatabase extends Omit<ICustomizationInfoClient, "LoadOutPreset" | "VehiclePreset"> {
|
||||
LoadOutPreset?: ILoadoutConfigDatabase;
|
||||
VehiclePreset?: ILoadoutConfigDatabase;
|
||||
}
|
||||
|
||||
export interface IFavouriteLoadout {
|
||||
Tag: string;
|
||||
LoadoutId: IOid;
|
||||
@ -191,11 +232,12 @@ export interface ITailorShopDatabase {
|
||||
Colors?: IColor;
|
||||
CustomJson?: string;
|
||||
LevelDecosVisible?: boolean;
|
||||
Rooms: IRoom[];
|
||||
Rooms: IRoomDatabase[];
|
||||
}
|
||||
|
||||
export interface ITailorShop extends Omit<ITailorShopDatabase, "FavouriteLoadouts"> {
|
||||
FavouriteLoadouts: IFavouriteLoadout[];
|
||||
export interface ITailorShop extends Omit<ITailorShopDatabase, "Rooms" | "FavouriteLoadouts"> {
|
||||
Rooms: IRoomClient[];
|
||||
FavouriteLoadouts?: IFavouriteLoadout[];
|
||||
}
|
||||
|
||||
export type RoomsType = { Name: string; MaxCapacity: number; PlacedDecos: Types.DocumentArray<IPlacedDecosDatabase> };
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
IRecentVendorPurchaseClient,
|
||||
TEquipmentKey,
|
||||
ICrewMemberClient,
|
||||
IKubrowPetPrintClient
|
||||
IKubrowPetPrintClient,
|
||||
IUpgradeClient
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export enum PurchaseSource {
|
||||
@ -73,6 +74,7 @@ export type IInventoryChanges = {
|
||||
InfestedFoundry?: IInfestedFoundryClient;
|
||||
Drones?: IDroneClient[];
|
||||
MiscItems?: IMiscItem[];
|
||||
ShipDecorations?: ITypeCount[];
|
||||
EmailItems?: ITypeCount[];
|
||||
CrewShipRawSalvage?: ITypeCount[];
|
||||
Nemesis?: Partial<INemesisClient>;
|
||||
@ -80,6 +82,7 @@ export type IInventoryChanges = {
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
|
||||
CrewMembers?: ICrewMemberClient[];
|
||||
KubrowPetPrints?: IKubrowPetPrintClient[];
|
||||
Upgrades?: IUpgradeClient[]; // TOVERIFY
|
||||
} & Record<
|
||||
Exclude<
|
||||
string,
|
||||
|
@ -117,6 +117,7 @@ export type IMissionInventoryUpdateRequest = {
|
||||
DropTable: string;
|
||||
DROP_MOD?: number[];
|
||||
DROP_BLUEPRINT?: number[];
|
||||
DROP_MISC_ITEM?: number[];
|
||||
}[];
|
||||
DeathMarks?: string[];
|
||||
Nemesis?: number;
|
||||
@ -148,6 +149,7 @@ export type IMissionInventoryUpdateRequest = {
|
||||
MultiProgress: unknown[];
|
||||
}[];
|
||||
InvasionProgress?: IInvasionProgressClient[];
|
||||
RJ?: boolean;
|
||||
ConquestMissionsCompleted?: number;
|
||||
duviriSuitSelection?: string;
|
||||
duviriPistolSelection?: string;
|
||||
@ -184,7 +186,10 @@ export interface IRewardInfo {
|
||||
NemesisHintProgress?: number;
|
||||
EOM_AFK?: number;
|
||||
rewardQualifications?: string; // did a Survival for 5 minutes and this was "1"
|
||||
rewardTierOverrides?: number[]; // Disruption
|
||||
PurgatoryRewardQualifications?: string;
|
||||
POICompletions?: number;
|
||||
LootDungeonCompletions?: number;
|
||||
rewardSeed?: number | bigint;
|
||||
periodicMissionTag?: string;
|
||||
T?: number; // Duviri
|
||||
|
@ -79,6 +79,7 @@ export interface ILoadoutDatabase {
|
||||
NORMAL_PVP: ILoadoutConfigDatabase[];
|
||||
LUNARO: ILoadoutConfigDatabase[];
|
||||
OPERATOR: ILoadoutConfigDatabase[];
|
||||
GEAR: ILoadoutConfigDatabase[];
|
||||
KDRIVE: ILoadoutConfigDatabase[];
|
||||
DATAKNIFE: ILoadoutConfigDatabase[];
|
||||
MECH: ILoadoutConfigDatabase[];
|
||||
|
@ -5,13 +5,16 @@ export interface IWorldState {
|
||||
Version: number; // for goals
|
||||
BuildLabel: string;
|
||||
Time: number;
|
||||
InGameMarket: IInGameMarket;
|
||||
Goals: IGoal[];
|
||||
Alerts: [];
|
||||
Sorties: ISortie[];
|
||||
LiteSorties: ILiteSortie[];
|
||||
SyndicateMissions: ISyndicateMissionInfo[];
|
||||
ActiveMissions: IFissure[];
|
||||
FlashSales: IFlashSale[];
|
||||
GlobalUpgrades: IGlobalUpgrade[];
|
||||
Invasions: IInvasion[];
|
||||
NodeOverrides: INodeOverride[];
|
||||
VoidTraders: IVoidTrader[];
|
||||
PrimeVaultTraders: IPrimeVaultTrader[];
|
||||
@ -36,19 +39,73 @@ export interface IGoal {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Count: number;
|
||||
Goal: number;
|
||||
Success: number;
|
||||
Personal: boolean;
|
||||
Bounty?: boolean;
|
||||
ClampNodeScores?: boolean;
|
||||
|
||||
Count?: number;
|
||||
HealthPct?: number;
|
||||
|
||||
Icon: string;
|
||||
Desc: string;
|
||||
ToolTip?: string;
|
||||
Icon: string;
|
||||
Faction?: string;
|
||||
|
||||
Goal?: number;
|
||||
InterimGoals?: number[];
|
||||
BonusGoal?: number;
|
||||
ClanGoal?: number[];
|
||||
|
||||
Success?: number;
|
||||
Personal?: boolean;
|
||||
Community?: boolean;
|
||||
Best?: boolean; // Fist one on Event Tab
|
||||
Bounty?: boolean; // Tactical Alert
|
||||
ClampNodeScores?: boolean;
|
||||
|
||||
Transmission?: string;
|
||||
InstructionalItem?: string;
|
||||
ItemType?: string;
|
||||
|
||||
Tag: string;
|
||||
Node: string;
|
||||
PrereqGoalTags?: string[];
|
||||
|
||||
Node?: string;
|
||||
VictimNode?: string;
|
||||
|
||||
ConcurrentMissionKeyNames?: string[];
|
||||
ConcurrentNodeReqs?: number[];
|
||||
ConcurrentNodes?: string[];
|
||||
RegionIdx?: number;
|
||||
Regions?: number[];
|
||||
MissionKeyName?: string;
|
||||
|
||||
Reward?: IMissionReward;
|
||||
InterimRewards?: IMissionReward[];
|
||||
BonusReward?: IMissionReward;
|
||||
|
||||
JobAffiliationTag?: string;
|
||||
Jobs?: ISyndicateJob[];
|
||||
PreviousJobs?: ISyndicateJob[];
|
||||
JobCurrentVersion?: IOid;
|
||||
JobPreviousVersion?: IOid;
|
||||
|
||||
ScoreVar?: string;
|
||||
ScoreMaxTag?: string;
|
||||
ScoreLocTag?: string;
|
||||
|
||||
NightLevel?: string;
|
||||
}
|
||||
|
||||
export interface ISyndicateJob {
|
||||
jobType?: string;
|
||||
rewards: string;
|
||||
masteryReq?: number;
|
||||
minEnemyLevel: number;
|
||||
maxEnemyLevel: number;
|
||||
xpAmounts: number[];
|
||||
endless?: boolean;
|
||||
locationTag?: string;
|
||||
isVault?: boolean;
|
||||
requiredItems?: string[];
|
||||
useRequiredItemsAsMiscItemFee?: boolean;
|
||||
}
|
||||
|
||||
export interface ISyndicateMissionInfo {
|
||||
@ -58,17 +115,7 @@ export interface ISyndicateMissionInfo {
|
||||
Tag: string;
|
||||
Seed: number;
|
||||
Nodes: string[];
|
||||
Jobs?: {
|
||||
jobType?: string;
|
||||
rewards: string;
|
||||
masteryReq: number;
|
||||
minEnemyLevel: number;
|
||||
maxEnemyLevel: number;
|
||||
xpAmounts: number[];
|
||||
endless?: boolean;
|
||||
locationTag?: string;
|
||||
isVault?: boolean;
|
||||
}[];
|
||||
Jobs?: ISyndicateJob[];
|
||||
}
|
||||
|
||||
export interface IGlobalUpgrade {
|
||||
@ -82,6 +129,28 @@ export interface IGlobalUpgrade {
|
||||
LocalizeDescTag: string;
|
||||
}
|
||||
|
||||
export interface IInvasion {
|
||||
_id: IOid;
|
||||
Faction: string;
|
||||
DefenderFaction: string;
|
||||
Node: string;
|
||||
Count: number;
|
||||
Goal: number;
|
||||
LocTag: string;
|
||||
Completed: boolean;
|
||||
ChainID: IOid;
|
||||
AttackerReward: IMissionReward;
|
||||
AttackerMissionInfo: IInvasionMissionInfo;
|
||||
DefenderReward: IMissionReward;
|
||||
DefenderMissionInfo: IInvasionMissionInfo;
|
||||
Activation: IMongoDate;
|
||||
}
|
||||
|
||||
export interface IInvasionMissionInfo {
|
||||
seed: number;
|
||||
faction: string;
|
||||
}
|
||||
|
||||
export interface IFissure {
|
||||
_id: IOid;
|
||||
Region: number;
|
||||
@ -242,6 +311,7 @@ export interface IEndlessXpChoice {
|
||||
export interface ISeasonChallenge {
|
||||
_id: IOid;
|
||||
Daily?: boolean;
|
||||
Permanent?: boolean; // only for getPastWeeklyChallenges response
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Challenge: string;
|
||||
@ -280,6 +350,37 @@ export type TCircuitGameMode =
|
||||
| "Assassination"
|
||||
| "Alchemy";
|
||||
|
||||
export interface IFlashSale {
|
||||
TypeName: string;
|
||||
ShowInMarket: boolean;
|
||||
HideFromMarket: boolean;
|
||||
SupporterPack: boolean;
|
||||
Discount: number;
|
||||
BogoBuy: number;
|
||||
BogoGet: number;
|
||||
PremiumOverride: number;
|
||||
RegularOverride: number;
|
||||
ProductExpiryOverride?: IMongoDate;
|
||||
StartDate: IMongoDate;
|
||||
EndDate: IMongoDate;
|
||||
}
|
||||
|
||||
export interface IInGameMarket {
|
||||
LandingPage: ILandingPage;
|
||||
}
|
||||
|
||||
export interface ILandingPage {
|
||||
Categories: IGameMarketCategory[];
|
||||
}
|
||||
|
||||
export interface IGameMarketCategory {
|
||||
CategoryName: string;
|
||||
Name: string;
|
||||
Icon: string;
|
||||
AddToMenu?: boolean;
|
||||
Items?: string[];
|
||||
}
|
||||
|
||||
export interface ITmp {
|
||||
cavabegin: string;
|
||||
PurchasePlatformLockEnabled: boolean; // Seems unused
|
||||
|
@ -108,3 +108,13 @@ errorLog.on("new", filename => logger.info(`Using error log file: ${filename}`))
|
||||
combinedLog.on("new", filename => logger.info(`Using combined log file: ${filename}`));
|
||||
errorLog.on("rotate", filename => logger.info(`Rotated error log file: ${filename}`));
|
||||
combinedLog.on("rotate", filename => logger.info(`Rotated combined log file: ${filename}`));
|
||||
|
||||
export const logError = (err: Error, context: string): void => {
|
||||
if (err.stack) {
|
||||
const stackArr = err.stack.split("\n");
|
||||
stackArr[0] += ` while ${context}`;
|
||||
logger.error(stackArr.join("\n"));
|
||||
} else {
|
||||
logger.error(`uncaught error while ${context}: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
@ -3,3 +3,5 @@ type Entries<T, K extends keyof T = keyof T> = (K extends unknown ? [K, T[K]] :
|
||||
export function getEntriesUnsafe<T extends object>(object: T): Entries<T> {
|
||||
return Object.entries(object) as Entries<T>;
|
||||
}
|
||||
|
||||
export const exhaustive = (_: never): void => {};
|
||||
|
@ -135,5 +135,6 @@
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/HumanLoidLoved",
|
||||
"ConquestSetupIntro",
|
||||
"EntratiLabConquestHardModeUnlocked",
|
||||
"/Lotus/Language/Npcs/KonzuPostNewWar"
|
||||
"/Lotus/Language/Npcs/KonzuPostNewWar",
|
||||
"/Lotus/Language/SolarisVenus/EudicoPostNewWar"
|
||||
]
|
||||
|
@ -16,6 +16,10 @@
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Keys/1999PrologueQuest/1999PrologueQuestKeyChain",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/EmailItems/TennokaiEmailItem",
|
||||
"ItemCount": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -304,7 +304,6 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/AvatarImageGlyphCookieKubrow", "PrimePrice": 80, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/LisetScarf", "PrimePrice": 600, "RegularPrice": 400000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/SuitCustomizations/ColourPickerTwitchBItemA", "PrimePrice": 220, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/BaroKavatBadgeItem", "PrimePrice": 50, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/BaroKavatSigil", "PrimePrice": 55, "RegularPrice": 45000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/WraithTurbinesScarf", "PrimePrice": 400, "RegularPrice": 500000 },
|
||||
@ -363,7 +362,6 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileInarosTomb", "PrimePrice": 325, "RegularPrice": 175000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/BaroFireWorksCrate", "PrimePrice": 50, "RegularPrice": 100000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileOrokinExtraction", "PrimePrice": 325, "RegularPrice": 175000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBait", "PrimePrice": 200, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBaitB", "PrimePrice": 200, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationB", "PrimePrice": 100, "RegularPrice": 100000 },
|
||||
@ -401,7 +399,39 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationC", "PrimePrice": 100, "RegularPrice": 100000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/PedistalPrime", "PrimePrice": 0, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/Emotes/BaroEmote", "PrimePrice": 0, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 }
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageAvaClemCommunityGlyph", "PrimePrice": 20, "RegularPrice": 33333 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TennoconConcert2025Display", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/SummerGameFestPoster", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/RathuumEventPoster", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionCorpus", "PrimePrice": 70, "RegularPrice": 55000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionEntrati", "PrimePrice": 99, "RegularPrice": 1900 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionScaldra", "PrimePrice": 93, "RegularPrice": 1906 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionTechrot", "PrimePrice": 98, "RegularPrice": 1901 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Warframes/VorunaActionGlyph", "PrimePrice": 75, "RegularPrice": 60000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageVoidAngelBaro", "PrimePrice": 80, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/1999DrippySigil", "PrimePrice": 50, "RegularPrice": 45000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Weapons/Rapier/CrpRapierSkin", "PrimePrice": 375, "RegularPrice": 400000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Events/OgrisOldSchool", "PrimePrice": 350, "RegularPrice": 325000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrismaLotusFlamesSigil", "PrimePrice": 55, "RegularPrice": 60000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/TenthAnniversaryLoginSongItem", "PrimePrice": 145, "RegularPrice": 165000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/AbyssofDagathSongItem", "PrimePrice": 150, "RegularPrice": 155000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/ZarimanLoginSongItem", "PrimePrice": 160, "RegularPrice": 180000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DanteUnboundLoginSongItem", "PrimePrice": 150, "RegularPrice": 150000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/EmpyreanSongItem", "PrimePrice": 160, "RegularPrice": 155000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DeimosLoginSongItem", "PrimePrice": 155, "RegularPrice": 160000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/JadeShadowsLoginSongItem", "PrimePrice": 150, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/WhispersInTheWallLoginSongItem", "PrimePrice": 165, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/CorpusRailjackLoginSongItem", "PrimePrice": 150, "RegularPrice": 165000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/LotusEatersSongItem", "PrimePrice": 165, "RegularPrice": 150000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/KuvaLichLoginSongItem", "PrimePrice": 140, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyBaro", "PrimePrice": 100, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyInaros", "PrimePrice": 120, "RegularPrice": 90000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 }
|
||||
],
|
||||
"allIfAny": [
|
||||
[
|
||||
|
114
static/fixed_responses/worldState/invasionNodes.json
Normal file
114
static/fixed_responses/worldState/invasionNodes.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"FC_CORPUS": [
|
||||
"SettlementNode1",
|
||||
"SettlementNode2",
|
||||
"SettlementNode3",
|
||||
"SettlementNode11",
|
||||
"SettlementNode12",
|
||||
"SettlementNode14",
|
||||
"SettlementNode15",
|
||||
"SettlementNode20",
|
||||
"SolNode1",
|
||||
"SolNode2",
|
||||
"SolNode4",
|
||||
"SolNode6",
|
||||
"SolNode10",
|
||||
"SolNode17",
|
||||
"SolNode21",
|
||||
"SolNode22",
|
||||
"SolNode23",
|
||||
"SolNode25",
|
||||
"SolNode38",
|
||||
"SolNode43",
|
||||
"SolNode48",
|
||||
"SolNode49",
|
||||
"SolNode51",
|
||||
"SolNode53",
|
||||
"SolNode56",
|
||||
"SolNode57",
|
||||
"SolNode61",
|
||||
"SolNode62",
|
||||
"SolNode65",
|
||||
"SolNode66",
|
||||
"SolNode72",
|
||||
"SolNode73",
|
||||
"SolNode74",
|
||||
"SolNode76",
|
||||
"SolNode78",
|
||||
"SolNode81",
|
||||
"SolNode84",
|
||||
"SolNode88",
|
||||
"SolNode97",
|
||||
"SolNode100",
|
||||
"SolNode101",
|
||||
"SolNode102",
|
||||
"SolNode104",
|
||||
"SolNode107",
|
||||
"SolNode109",
|
||||
"SolNode118",
|
||||
"SolNode121",
|
||||
"SolNode123",
|
||||
"SolNode125",
|
||||
"SolNode126",
|
||||
"SolNode127",
|
||||
"SolNode128",
|
||||
"SolNode203",
|
||||
"SolNode205",
|
||||
"SolNode209",
|
||||
"SolNode210",
|
||||
"SolNode211",
|
||||
"SolNode212",
|
||||
"SolNode214",
|
||||
"SolNode216",
|
||||
"SolNode217",
|
||||
"SolNode220"
|
||||
],
|
||||
"FC_GRINEER": [
|
||||
"SolNode11",
|
||||
"SolNode16",
|
||||
"SolNode18",
|
||||
"SolNode19",
|
||||
"SolNode20",
|
||||
"SolNode30",
|
||||
"SolNode31",
|
||||
"SolNode32",
|
||||
"SolNode36",
|
||||
"SolNode41",
|
||||
"SolNode42",
|
||||
"SolNode45",
|
||||
"SolNode46",
|
||||
"SolNode50",
|
||||
"SolNode58",
|
||||
"SolNode67",
|
||||
"SolNode68",
|
||||
"SolNode70",
|
||||
"SolNode82",
|
||||
"SolNode93",
|
||||
"SolNode96",
|
||||
"SolNode99",
|
||||
"SolNode106",
|
||||
"SolNode113",
|
||||
"SolNode131",
|
||||
"SolNode132",
|
||||
"SolNode135",
|
||||
"SolNode137",
|
||||
"SolNode138",
|
||||
"SolNode139",
|
||||
"SolNode140",
|
||||
"SolNode141",
|
||||
"SolNode144",
|
||||
"SolNode146",
|
||||
"SolNode147",
|
||||
"SolNode149",
|
||||
"SolNode177",
|
||||
"SolNode181",
|
||||
"SolNode184",
|
||||
"SolNode185",
|
||||
"SolNode187",
|
||||
"SolNode188",
|
||||
"SolNode189",
|
||||
"SolNode191",
|
||||
"SolNode195",
|
||||
"SolNode196"
|
||||
]
|
||||
}
|
190
static/fixed_responses/worldState/invasionRewards.json
Normal file
190
static/fixed_responses/worldState/invasionRewards.json
Normal file
@ -0,0 +1,190 @@
|
||||
{
|
||||
"FC_GRINEER": {
|
||||
"COMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/Research/ChemComponent",
|
||||
"ItemCount": 3
|
||||
}
|
||||
],
|
||||
"UNCOMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/KarakWraithBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithBarrel",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithStock",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/StrunWraithBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithBarrel",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithStock",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/LatronWraithBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithBarrel",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithStock",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/TwinVipersWraithBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithBarrel",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithLink",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/GrineerCombatKnifeSortieBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHilt",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeBlade",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHeatsink",
|
||||
"ItemCount": 1
|
||||
}
|
||||
],
|
||||
"RARE": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
|
||||
"ItemCount": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"FC_CORPUS": {
|
||||
"COMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
|
||||
"ItemCount": 3
|
||||
}
|
||||
],
|
||||
"UNCOMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/DeraVandalBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalBarrel",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalStock",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/SnipetronVandalBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalStock",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalReceiver",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalBarrel",
|
||||
"ItemCount": 1
|
||||
}
|
||||
],
|
||||
"RARE": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
|
||||
"ItemCount": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"FC_INFESTATION": {
|
||||
"COMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
|
||||
"ItemCount": 1
|
||||
}
|
||||
],
|
||||
"UNCOMMON": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
|
||||
"ItemCount": 2
|
||||
}
|
||||
],
|
||||
"RARE": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/MiscItems/InfestedAladCoordinate",
|
||||
"ItemCount": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
290
static/fixed_responses/worldState/pvpChallenges.json
Normal file
290
static/fixed_responses/worldState/pvpChallenges.json
Normal file
@ -0,0 +1,290 @@
|
||||
{
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeFlagCaptureEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_CAPTURETHEFLAG"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeFlagCaptureMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_CAPTURETHEFLAG"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeFlagReturnEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_CAPTURETHEFLAG"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsComboEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsComboMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsHeadShotsEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsHeadShotsMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsMeleeEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsMeleeMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsMeleeHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsMultiMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPaybackEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPayback_MEDIUM": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPowerEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPowerMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPowerHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPrimaryEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPrimaryMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPrimaryHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsSecondaryEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsSecondaryMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsSecondaryHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreak_MEDIUM": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakDominationEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakDomination_MEDIUM": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakDominationHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakStoppedEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakStopped_MEDIUM": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakHARD": {
|
||||
"ScriptParamValue": 2,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsTargetInAirEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsTargetInAirMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsTargetInAirHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsWhileSlidingEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsWhileSlidingMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsWhileSlidingHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 3000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeMatchCompleteEASY": {
|
||||
"ScriptParamValue": 1,
|
||||
"PVPModeAllowed": ["PVPMODE_CAPTURETHEFLAG", "PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeMatchCompleteMEDIUM": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_CAPTURETHEFLAG", "PVPMODE_DEATHMATCH", "PVPMODE_TEAMDEATHMATCH"],
|
||||
"SyndicateXP": 1500
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballCatchesEASY": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballCatchesMEDIUM": {
|
||||
"ScriptParamValue": 10,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballCatchesHARD": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballChecksEASY": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballChecksMEDIUM": {
|
||||
"ScriptParamValue": 10,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballChecksHARD": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballGoalsEASY": {
|
||||
"ScriptParamValue": 2,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballGoalsMEDIUM": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballGoalsHARD": {
|
||||
"ScriptParamValue": 4,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballInterceptionsEASY": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballInterceptionsMEDIUM": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballInterceptionsHARD": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballPassesEASY": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballPassesMEDIUM": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballPassesHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000,
|
||||
"DuringSingleMatch": true
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballStealsEASY": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 1000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballStealsMEDIUM": {
|
||||
"ScriptParamValue": 6,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 3000
|
||||
},
|
||||
"/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballStealsHARD": {
|
||||
"ScriptParamValue": 3,
|
||||
"PVPModeAllowed": ["PVPMODE_SPEEDBALL"],
|
||||
"SyndicateXP": 6000
|
||||
}
|
||||
}
|
@ -117,46 +117,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"Invasions": [
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "67c8ec8b3d0d86b236c1c18f"
|
||||
},
|
||||
"Faction": "FC_INFESTATION",
|
||||
"DefenderFaction": "FC_CORPUS",
|
||||
"Node": "SolNode53",
|
||||
"Count": -28558,
|
||||
"Goal": 30000,
|
||||
"LocTag": "/Lotus/Language/Menu/InfestedInvasionBoss",
|
||||
"Completed": false,
|
||||
"ChainID": {
|
||||
"$oid": "67c8b6a2bde0dfd0f7c1c18d"
|
||||
},
|
||||
"AttackerReward": [],
|
||||
"AttackerMissionInfo": {
|
||||
"seed": 488863,
|
||||
"faction": "FC_CORPUS"
|
||||
},
|
||||
"DefenderReward": {
|
||||
"countedItems": [
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
|
||||
"ItemCount": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
"DefenderMissionInfo": {
|
||||
"seed": 127653,
|
||||
"faction": "FC_INFESTATION",
|
||||
"missionReward": []
|
||||
},
|
||||
"Activation": {
|
||||
"$date": {
|
||||
"$numberLong": "1741221003031"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"SyndicateMissions": [
|
||||
{
|
||||
"_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
|
||||
@ -349,142 +309,8 @@
|
||||
],
|
||||
"PrimeAccessAvailability": { "State": "PRIME1" },
|
||||
"PrimeVaultAvailabilities": [false, false, false, false, false],
|
||||
"PrimeTokenAvailability": false,
|
||||
"PrimeTokenAvailability": true,
|
||||
"LibraryInfo": { "LastCompletedTargetType": "/Lotus/Types/Game/Library/Targets/Research7Target" },
|
||||
"PVPChallengeInstances": [
|
||||
{
|
||||
"_id": { "$oid": "6635562d036ce37f7f98e264" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeGameModeComplete",
|
||||
"startDate": { "$date": { "$numberLong": "1714771501460" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 20 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_ALL",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_WEEKLY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6635562d036ce37f7f98e263" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeGameModeWins",
|
||||
"startDate": { "$date": { "$numberLong": "1714771501460" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 6 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_ALL",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_WEEKLY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6635562d036ce37f7f98e265" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeOtherChallengeCompleteANY",
|
||||
"startDate": { "$date": { "$numberLong": "1714771501460" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 10 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_ALL",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_WEEKLY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6635562d036ce37f7f98e266" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeWeeklyStandardSet",
|
||||
"startDate": { "$date": { "$numberLong": "1714771501460" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 0 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_NONE",
|
||||
"subChallenges": [{ "$oid": "6635562d036ce37f7f98e263" }, { "$oid": "6635562d036ce37f7f98e264" }, { "$oid": "6635562d036ce37f7f98e265" }],
|
||||
"Category": "PVPChallengeTypeCategory_WEEKLY_ROOT"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75fee" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeFlagReturnEASY",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 1 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_CAPTURETHEFLAG",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75fed" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeMatchCompleteMEDIUM",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 4 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_CAPTURETHEFLAG",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75ff2" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeMatchCompleteEASY",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 1 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_DEATHMATCH",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75ff1" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsPayback_MEDIUM",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 3 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_DEATHMATCH",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75fef" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsStreakDominationEASY",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 1 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_TEAMDEATHMATCH",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75ff0" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeKillsWhileInAirHARD",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 3 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_TEAMDEATHMATCH",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75ff3" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballCatchesMEDIUM",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 10 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_SPEEDBALL",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
},
|
||||
{
|
||||
"_id": { "$oid": "6639ca6967c1192987d75ff4" },
|
||||
"challengeTypeRefID": "/Lotus/PVPChallengeTypes/PVPTimedChallengeSpeedballInterceptionsEASY",
|
||||
"startDate": { "$date": { "$numberLong": "1715063401824" } },
|
||||
"endDate": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"params": [{ "n": "ScriptParamValue", "v": 3 }],
|
||||
"isGenerated": true,
|
||||
"PVPMode": "PVPMODE_SPEEDBALL",
|
||||
"subChallenges": [],
|
||||
"Category": "PVPChallengeTypeCategory_DAILY"
|
||||
}
|
||||
],
|
||||
"PersistentEnemies": [],
|
||||
"PVPAlternativeModes": [],
|
||||
"PVPActiveTournaments": [],
|
||||
|
@ -388,11 +388,11 @@
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="inventory_hoverboards"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<form class="input-group mb-3" onsubmit="doAcquireModularEquipment('HoverBoards');return false;">
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_DECK" list="datalist-ModularParts-HB_DECK" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_ENGINE" list="datalist-ModularParts-HB_ENGINE" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_FRONT" list="datalist-ModularParts-HB_FRONT" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_JET" list="datalist-ModularParts-HB_JET" />
|
||||
<form class="input-group mb-3" onsubmit="doAcquireModularEquipment('Hoverboards');return false;">
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_DECK" list="datalist-ModularParts-HB_DECK" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_ENGINE" list="datalist-ModularParts-HB_ENGINE" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_FRONT" list="datalist-ModularParts-HB_FRONT" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_JET" list="datalist-ModularParts-HB_JET" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
@ -436,29 +436,37 @@
|
||||
<h5 class="card-header" data-loc="general_bulkActions"></h5>
|
||||
<div class="card-body">
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Suits']);" data-loc="inventory_bulkAddSuits"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkAddWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceSuits']);" data-loc="inventory_bulkAddSpaceSuits"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEvolutionProgress();" data-loc="inventory_bulkAddEvolutionProgress"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Suits']);" data-loc="inventory_bulkAddSuits"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkAddWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SpaceSuits']);" data-loc="inventory_bulkAddSpaceSuits"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEvolutionProgress);" data-loc="inventory_bulkAddEvolutionProgress"></button>
|
||||
</div>
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);" data-loc="inventory_bulkRankUpSuits"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkRankUpWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceSuits']);" data-loc="inventory_bulkRankUpSpaceSuits"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEvolutions();" data-loc="inventory_bulkRankUpEvolutionProgress"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Suits']);" data-loc="inventory_bulkRankUpSuits"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkRankUpWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SpaceSuits']);" data-loc="inventory_bulkRankUpSpaceSuits"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEvolutions);" data-loc="inventory_bulkRankUpEvolutionProgress"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="detailedView-route" data-route="/webui/detailedView" data-title="Inventory | OpenWF WebUI">
|
||||
<h3 class="mb-0"></h3>
|
||||
<h3 id="detailedView-loading" class="mb-0" data-loc="general_loading"></h3>
|
||||
<h3 id="detailedView-title" class="mb-0"></h3>
|
||||
<p class="text-body-secondary"></p>
|
||||
<div id="loadout-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_loadoutLabel"></h5>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs" id="loadoutTabs"></ul>
|
||||
<div class="tab-content mt-3" id="loadoutTabsContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="archonShards-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_archonShardsLabel"></h5>
|
||||
<div class="card-body">
|
||||
@ -477,6 +485,62 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="edit-suit-invigorations-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_suitInvigorationLabel"></h5>
|
||||
<div class="card-body">
|
||||
<form onsubmit="submitSuitInvigorationUpgrade(event)">
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-offensive" class="form-label" data-loc="invigorations_offensiveLabel"></label>
|
||||
<select class="form-select" id="dv-invigoration-offensive">
|
||||
<option value="" data-loc="general_none"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerStrength" data-loc="invigorations_offensive_AbilityStrength"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerRange" data-loc="invigorations_offensive_AbilityRange"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerDuration" data-loc="invigorations_offensive_AbilityDuration"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeDamage" data-loc="invigorations_offensive_MeleeDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPrimaryDamage" data-loc="invigorations_offensive_PrimaryDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationSecondaryDamage" data-loc="invigorations_offensive_SecondaryDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPrimaryCritChance" data-loc="invigorations_offensive_PrimaryCritChance"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationSecondaryCritChance" data-loc="invigorations_offensive_SecondaryCritChance"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeCritChance" data-loc="invigorations_offensive_MeleeCritChance"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-defensive" class="form-label" data-loc="invigorations_defensiveLabel"></label>
|
||||
<select class="form-select" id="dv-invigoration-defensive">
|
||||
<option value="" data-loc="general_none"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationPowerEfficiency" data-loc="invigorations_utility_AbilityEfficiency"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationMovementSpeed" data-loc="invigorations_utility_SprintSpeed"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationParkourSpeed" data-loc="invigorations_utility_ParkourVelocity"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationHealth" data-loc="invigorations_utility_HealthMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergy" data-loc="invigorations_utility_EnergyMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationStatusResistance" data-loc="invigorations_utility_StatusImmune"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationReloadSpeed" data-loc="invigorations_utility_ReloadSpeed"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationHealthRegen" data-loc="invigorations_utility_HealthRegen"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationArmor" data-loc="invigorations_utility_ArmorMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationJumps" data-loc="invigorations_utility_Jumps"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergyRegen" data-loc="invigorations_utility_EnergyRegen"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-expiry" class="form-label" data-loc="invigorations_expiryLabel"></label>
|
||||
<input type="datetime-local" class="form-control" id="dv-invigoration-expiry" />
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary" data-loc="general_setButton"></button>
|
||||
<button type="button" class="btn btn-danger" onclick="clearSuitInvigorationUpgrades()" data-loc="code_remove"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modularParts-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_modularPartsLabel"></h5>
|
||||
<div class="card-body">
|
||||
<form id="modularParts-form" class="input-group mb-3" onsubmit="handleModularPartsChange(event)"></form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="valenceBonus-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_valenceBonusLabel"></h5>
|
||||
<div class="card-body">
|
||||
@ -485,7 +549,7 @@
|
||||
<select class="form-control" id="valenceBonus-innateDamage"></select>
|
||||
<input type="number" id="valenceBonus-procent" min="25" max="60" step="0.1" class="form-control" style="max-width:100px" />
|
||||
<button class="btn btn-primary" type="submit" value="set" data-loc="general_setButton"></button>
|
||||
<button class="btn btn-danger" type="submit" value="remove" data-loc="general_removeButton"></button>
|
||||
<button class="btn btn-danger" type="submit" value="remove" data-loc="code_remove"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -527,8 +591,8 @@
|
||||
<form class="input-group mb-3" onsubmit="doAcquireMod();return false;">
|
||||
<input class="form-control" id="mod-count" type="number" value="1"/>
|
||||
<input class="form-control w-50" id="mod-to-acquire" list="datalist-mods" />
|
||||
<button class="btn btn-success" onclick="window.maxed=true" type="submit" data-loc="mods_addMax"></button>
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
<button class="btn btn-success" onclick="window.maxed=true" data-loc="mods_addMax"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="mods-list"></tbody>
|
||||
@ -599,26 +663,6 @@
|
||||
<input class="form-check-input" type="checkbox" id="unlockAllScans" />
|
||||
<label class="form-check-label" for="unlockAllScans" data-loc="cheats_unlockAllScans"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteCredits" />
|
||||
<label class="form-check-label" for="infiniteCredits" data-loc="cheats_infiniteCredits"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infinitePlatinum" />
|
||||
<label class="form-check-label" for="infinitePlatinum" data-loc="cheats_infinitePlatinum"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteEndo" />
|
||||
<label class="form-check-label" for="infiniteEndo" data-loc="cheats_infiniteEndo"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteRegalAya" />
|
||||
<label class="form-check-label" for="infiniteRegalAya" data-loc="cheats_infiniteRegalAya"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
|
||||
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="claimingBlueprintRefundsIngredients" />
|
||||
<label class="form-check-label" for="claimingBlueprintRefundsIngredients" data-loc="cheats_claimingBlueprintRefundsIngredients"></label>
|
||||
@ -803,21 +847,21 @@
|
||||
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" data-default="-1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigInt('relicRewardItemCountMultiplier'); return false;">
|
||||
<label class="form-label" for="relicRewardItemCountMultiplier" data-loc="cheats_relicRewardItemCountMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="relicRewardItemCountMultiplier" type="number" min="1" max="1000000" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;">
|
||||
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -827,9 +871,30 @@
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<h5 class="card-header" data-loc="cheats_account"></h5>
|
||||
<div class="card-body">
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<div class="card-body" id="account-cheats">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteCredits" />
|
||||
<label class="form-check-label" for="infiniteCredits" data-loc="cheats_infiniteCredits"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infinitePlatinum" />
|
||||
<label class="form-check-label" for="infinitePlatinum" data-loc="cheats_infinitePlatinum"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteEndo" />
|
||||
<label class="form-check-label" for="infiniteEndo" data-loc="cheats_infiniteEndo"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteRegalAya" />
|
||||
<label class="form-check-label" for="infiniteRegalAya" data-loc="cheats_infiniteRegalAya"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
|
||||
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
|
||||
</div>
|
||||
<div class="mt-2 mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(markAllAsRead);" data-loc="cheats_markAllAsRead"></button>
|
||||
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
|
||||
<button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
|
||||
@ -861,22 +926,156 @@
|
||||
<label class="form-check-label" for="worldState.resourceBoost" data-loc="worldState_resourceBoost"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.starDays" />
|
||||
<label class="form-check-label" for="worldState.starDays" data-loc="worldState_starDays"></label>
|
||||
<input class="form-check-input" type="checkbox" id="worldState.tennoLiveRelay" />
|
||||
<label class="form-check-label" for="worldState.tennoLiveRelay" data-loc="worldState_tennoLiveRelay"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.baroTennoConRelay" />
|
||||
<label class="form-check-label" for="worldState.baroTennoConRelay" data-loc="worldState_baroTennoConRelay"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.varziaFullyStocked" />
|
||||
<label class="form-check-label" for="worldState.varziaFullyStocked" data-loc="worldState_varziaFullyStocked"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.wolfHunt" />
|
||||
<label class="form-check-label" for="worldState.wolfHunt" data-loc="worldState_wolfHunt"></label>
|
||||
<abbr data-loc-inc="worldState_galleonOfGhouls"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.longShadow" />
|
||||
<label class="form-check-label" for="worldState.longShadow" data-loc="worldState_longShadow"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.hallowedFlame" />
|
||||
<label class="form-check-label" for="worldState.hallowedFlame" data-loc="worldState_hallowedFlame"></label>
|
||||
<abbr data-loc-inc="worldState_hallowedNightmares|worldState_dogDays"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.hallowedNightmares" data-loc="worldState_hallowedNightmares"></label>
|
||||
<abbr data-loc-inc="worldState_hallowedFlame|worldState_dogDays"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
|
||||
<select class="form-control" id="worldState.hallowedNightmares" data-default="false">
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.hallowedNightmaresRewardsOverride" data-loc="worldState_hallowedNightmaresRewards"></label>
|
||||
<select class="form-control" id="worldState.hallowedNightmaresRewardsOverride" data-default="0">
|
||||
<option value="0" data-loc="worldState_from_year" data-loc-year="2018"></option>
|
||||
<option value="1" data-loc="worldState_from_year" data-loc-year="2016"></option>
|
||||
<option value="2" data-loc="worldState_from_year" data-loc-year="2015"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.proxyRebellion" data-loc="worldState_proxyRebellion"></label>
|
||||
<select class="form-control" id="worldState.proxyRebellion" data-default="false">
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.proxyRebellionRewardsOverride" data-loc="worldState_proxyRebellionRewards"></label>
|
||||
<select class="form-control" id="worldState.proxyRebellionRewardsOverride" data-default="0">
|
||||
<option value="0" data-loc="worldState_from_year" data-loc-year="2019"></option>
|
||||
<option value="1" data-loc="worldState_from_year" data-loc-year="2018"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.galleonOfGhouls" data-loc="worldState_galleonOfGhouls"></label>
|
||||
<select class="form-control" id="worldState.galleonOfGhouls" data-default="">
|
||||
<abbr data-loc-inc="worldState_wolfHunt"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
|
||||
<select class="form-control" id="worldState.galleonOfGhouls" data-default="0">
|
||||
<option value="0" data-loc="disabled"></option>
|
||||
<option value="1" data-loc="worldState_we1"></option>
|
||||
<option value="2" data-loc="worldState_we2"></option>
|
||||
<option value="3" data-loc="worldState_we3"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.ghoulEmergenceOverride" data-loc="worldState_ghoulEmergence"></label>
|
||||
<select class="form-control" id="worldState.ghoulEmergenceOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.plagueStarOverride" data-loc="worldState_plagueStar"></label>
|
||||
<select class="form-control" id="worldState.plagueStarOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.starDaysOverride" data-loc="worldState_starDays"></label>
|
||||
<select class="form-control" id="worldState.starDaysOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.dogDaysOverride" data-loc="worldState_dogDays"></label>
|
||||
<abbr data-loc-inc="worldState_hallowedFlame|worldState_hallowedNightmares"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
|
||||
<select class="form-control" id="worldState.dogDaysOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.dogDaysRewardsOverride" data-loc="worldState_dogDaysRewards"></label>
|
||||
<select class="form-control" id="worldState.dogDaysRewardsOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="3" data-loc="worldState_from_year" data-loc-year="2025"></option>
|
||||
<option value="2" data-loc="worldState_from_year" data-loc-year="2024"></option>
|
||||
<option value="1" data-loc="worldState_from_year" data-loc-year="2023"></option>
|
||||
<option value="0" data-loc="worldState_pre_year" data-loc-year="2023"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.bellyOfTheBeast" data-loc="worldState_bellyOfTheBeast"></label>
|
||||
<select class="form-control" id="worldState.bellyOfTheBeast" data-default="false">
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<form class="form-group" onsubmit="doSaveConfigInt('worldState.bellyOfTheBeastProgressOverride'); return false;">
|
||||
<label class="form-label" for="worldState.bellyOfTheBeastProgressOverride" data-loc="worldState_bellyOfTheBeastProgressOverride"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.bellyOfTheBeastProgressOverride" class="form-control" type="number" min="0" max="100" data-default="0" />
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.eightClaw" data-loc="worldState_eightClaw"></label>
|
||||
<select class="form-control" id="worldState.eightClaw" data-default="false">
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<form class="form-group" onsubmit="doSaveConfigInt('worldState.eightClawProgressOverride'); return false;">
|
||||
<label class="form-label" for="worldState.eightClawProgressOverride" data-loc="worldState_eightClawProgressOverride"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.eightClawProgressOverride" class="form-control" type="number" min="0" max="100" data-default="0" />
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.eidolonOverride" data-loc="worldState_eidolonOverride"></label>
|
||||
<select class="form-control" id="worldState.eidolonOverride" data-default="">
|
||||
@ -942,14 +1141,14 @@
|
||||
<label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_theCircuitOverride"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.circuitGameModes" type="text" class="form-control tags-input" list="datalist-circuitGameModes" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;">
|
||||
<label class="form-label" for="worldState.darvoStockMultiplier" data-loc="worldState_darvoStockMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -958,7 +1157,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div data-route="/webui/import" data-title="Import | OpenWF WebUI">
|
||||
<p data-loc="import_importNote"></p>
|
||||
<p>
|
||||
<span data-loc="import_importNote"></span>
|
||||
<span data-loc="import_importNote2"></span>
|
||||
</p>
|
||||
<textarea class="form-control" id="import-inventory" style="height: calc(100vh - 300px)"></textarea>
|
||||
<button class="btn btn-primary mt-3" onclick="doImport();" data-loc="import_submit"></button>
|
||||
<p class="mt-3 mb-1" data-loc="import_samples"></p>
|
||||
@ -1015,6 +1217,7 @@
|
||||
<datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
|
||||
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
|
||||
<datalist id="datalist-Boosters"></datalist>
|
||||
<datalist id="datalist-Abilities"></datalist>
|
||||
<datalist id="datalist-circuitGameModes">
|
||||
<option>Survival</option>
|
||||
<option>VoidFlood</option>
|
||||
|
@ -18,7 +18,7 @@ const sendAuth = isRegister => {
|
||||
window.ws.send(
|
||||
JSON.stringify({
|
||||
auth: {
|
||||
email: localStorage.getItem("email"),
|
||||
email: localStorage.getItem("email").toLowerCase(),
|
||||
password: wp.encSync(localStorage.getItem("password")),
|
||||
isRegister
|
||||
}
|
||||
@ -28,7 +28,8 @@ const sendAuth = isRegister => {
|
||||
};
|
||||
|
||||
function openWebSocket() {
|
||||
window.ws = new WebSocket("/custom/ws");
|
||||
const wsProto = location.protocol === "https:" ? "wss://" : "ws://";
|
||||
window.ws = new WebSocket(wsProto + location.host + "/custom/ws");
|
||||
window.ws.onopen = () => {
|
||||
ws_is_open = true;
|
||||
sendAuth(false);
|
||||
@ -118,9 +119,16 @@ function doLogin() {
|
||||
window.registerSubmit = false;
|
||||
}
|
||||
|
||||
async function revalidateAuthz() {
|
||||
await getWebSocket();
|
||||
// We have a websocket connection, so authz should be good.
|
||||
function revalidateAuthz() {
|
||||
return new Promise(resolve => {
|
||||
let interval;
|
||||
interval = setInterval(() => {
|
||||
if (ws_is_open && !auth_pending) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
@ -194,6 +202,17 @@ function updateLocElements() {
|
||||
document.querySelectorAll("[data-loc-placeholder]").forEach(elm => {
|
||||
elm.placeholder = loc(elm.getAttribute("data-loc-placeholder"));
|
||||
});
|
||||
document.querySelectorAll("[data-loc-inc]").forEach(elm => {
|
||||
const incWith = elm
|
||||
.getAttribute("data-loc-inc")
|
||||
.split("|")
|
||||
.map(key => loc(key))
|
||||
.join(", ");
|
||||
elm.title = `${loc("worldState_incompatibleWith")} ${incWith}`;
|
||||
});
|
||||
document.querySelectorAll("[data-loc-year]").forEach(elm => {
|
||||
elm.innerHTML = elm.innerHTML.replace("|YEAR|", elm.getAttribute("data-loc-year"));
|
||||
});
|
||||
}
|
||||
|
||||
function setActiveLanguage(lang) {
|
||||
@ -204,7 +223,7 @@ function setActiveLanguage(lang) {
|
||||
document.querySelector("[data-lang=" + lang + "]").classList.add("active");
|
||||
|
||||
window.dictPromise = new Promise(resolve => {
|
||||
const webui_lang = ["en", "ru", "fr", "de", "zh", "es"].indexOf(lang) == -1 ? "en" : lang;
|
||||
const webui_lang = ["en", "ru", "fr", "de", "zh", "es", "uk"].indexOf(lang) == -1 ? "en" : lang;
|
||||
let script = document.getElementById("translations");
|
||||
if (script) document.documentElement.removeChild(script);
|
||||
|
||||
@ -273,6 +292,8 @@ function fetchItemList() {
|
||||
window.itemListPromise = new Promise(resolve => {
|
||||
const req = $.get("/custom/getItemLists?lang=" + window.lang);
|
||||
req.done(async data => {
|
||||
window.allQuestKeys = data.QuestKeys;
|
||||
|
||||
await dictPromise;
|
||||
|
||||
document.querySelectorAll('[id^="datalist-"]').forEach(datalist => {
|
||||
@ -280,7 +301,8 @@ function fetchItemList() {
|
||||
});
|
||||
|
||||
const syndicateNone = document.createElement("option");
|
||||
syndicateNone.textContent = loc("cheats_none");
|
||||
syndicateNone.value = "";
|
||||
syndicateNone.textContent = loc("general_none");
|
||||
document.getElementById("changeSyndicate").innerHTML = "";
|
||||
document.getElementById("changeSyndicate").appendChild(syndicateNone);
|
||||
|
||||
@ -301,8 +323,8 @@ function fetchItemList() {
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityDurationMythic": loc("upgrade_WarframeAbilityDuration").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrength": loc("upgrade_WarframeAbilityStrength").split("|VAL|").join("10"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrengthMythic": loc("upgrade_WarframeAbilityStrength").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMax": loc("upgrade_WarframeArmourMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMaxMythic": loc("upgrade_WarframeArmourMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMax": loc("upgrade_WarframeArmorMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMaxMythic": loc("upgrade_WarframeArmorMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProc": loc("upgrade_WarframeBlastProc").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProcMythic": loc("upgrade_WarframeBlastProc").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCastingSpeed": loc("upgrade_WarframeCastingSpeed").split("|VAL|").join("25"),
|
||||
@ -331,8 +353,8 @@ function fetchItemList() {
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeParkourVelocityMythic": loc("upgrade_WarframeParkourVelocity").split("|VAL|").join("22.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoost": loc("upgrade_WarframeRadiationDamageBoost").split("|VAL|").join("10"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoostMythic": loc("upgrade_WarframeRadiationDamageBoost").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegen": loc("upgrade_WarframeRegen").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegenMythic": loc("upgrade_WarframeRegen").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegen": loc("upgrade_WarframeHealthRegen").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegenMythic": loc("upgrade_WarframeHealthRegen").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMax": loc("upgrade_WarframeShieldMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMaxMythic": loc("upgrade_WarframeShieldMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeStartingEnergy": loc("upgrade_WarframeStartingEnergy").split("|VAL|").join("30"),
|
||||
@ -486,6 +508,9 @@ function fetchItemList() {
|
||||
uniqueLevelCaps = items;
|
||||
} else if (type == "Syndicates") {
|
||||
items.forEach(item => {
|
||||
if (item.uniqueName === "ConclaveSyndicate") {
|
||||
return;
|
||||
}
|
||||
if (item.uniqueName.startsWith("RadioLegion")) {
|
||||
item.name += " (" + item.uniqueName + ")";
|
||||
}
|
||||
@ -580,6 +605,8 @@ function fetchItemList() {
|
||||
}
|
||||
fetchItemList();
|
||||
|
||||
const accountCheats = document.querySelectorAll("#account-cheats input[id]");
|
||||
|
||||
// Assumes that caller revalidates authz
|
||||
function updateInventory() {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
@ -728,7 +755,10 @@ function updateInventory() {
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
|
||||
if (
|
||||
["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) ||
|
||||
modularWeapons.includes(item.ItemType)
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
|
||||
@ -859,15 +889,11 @@ function updateInventory() {
|
||||
|
||||
const datalistEvolutionProgress = document.querySelectorAll("#datalist-EvolutionProgress option");
|
||||
const formEvolutionProgress = document.querySelector('form[onsubmit*="doAcquireEvolution()"]');
|
||||
const giveAllQEvolutionProgress = document.querySelector(
|
||||
'button[onclick*="addMissingEvolutionProgress()"]'
|
||||
);
|
||||
|
||||
if (datalistEvolutionProgress.length === 0) {
|
||||
formEvolutionProgress.classList.add("disabled");
|
||||
formEvolutionProgress.querySelector("input").disabled = true;
|
||||
formEvolutionProgress.querySelector("button").disabled = true;
|
||||
giveAllQEvolutionProgress.disabled = true;
|
||||
}
|
||||
|
||||
if (data.CrewShipHarnesses?.length) {
|
||||
@ -879,6 +905,14 @@ function updateInventory() {
|
||||
|
||||
// Populate quests route
|
||||
document.getElementById("QuestKeys-list").innerHTML = "";
|
||||
window.allQuestKeys.forEach(questKey => {
|
||||
if (!data.QuestKeys.some(x => x.ItemType == questKey.uniqueName)) {
|
||||
const datalist = document.getElementById("datalist-QuestKeys");
|
||||
if (!datalist.querySelector(`option[data-key="${questKey.uniqueName}"]`)) {
|
||||
readdQuestKey(itemMap, questKey.uniqueName);
|
||||
}
|
||||
}
|
||||
});
|
||||
data.QuestKeys.forEach(item => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("data-item-type", item.ItemType);
|
||||
@ -972,10 +1006,7 @@ function updateInventory() {
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", item.ItemType);
|
||||
option.value = itemMap[item.ItemType]?.name ?? item.ItemType;
|
||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
||||
readdQuestKey(itemMap, item.ItemType);
|
||||
doQuestUpdate("deleteKey", item.ItemType);
|
||||
};
|
||||
a.title = loc("code_remove");
|
||||
@ -1010,13 +1041,19 @@ function updateInventory() {
|
||||
if (item.ItemType.substr(0, 32) == "/Lotus/Upgrades/Mods/Randomized/") {
|
||||
const rivenType = item.ItemType.substr(32);
|
||||
const fingerprint = JSON.parse(item.UpgradeFingerprint);
|
||||
if (fingerprint.buffs) {
|
||||
if ("buffs" in fingerprint) {
|
||||
// Riven has been revealed?
|
||||
const tr = document.createElement("tr");
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = itemMap[fingerprint.compat]?.name ?? fingerprint.compat;
|
||||
td.textContent += " " + RivenParser.parseRiven(rivenType, fingerprint, 1).name;
|
||||
td.textContent += " ";
|
||||
try {
|
||||
td.textContent += RivenParser.parseRiven(rivenType, fingerprint, 1).name;
|
||||
} catch (e) {
|
||||
console.warn("malformed riven", { rivenType, fingerprint });
|
||||
td.textContent += " [Malformed Riven]";
|
||||
}
|
||||
td.innerHTML +=
|
||||
" <span title='" +
|
||||
loc("code_buffsNumber") +
|
||||
@ -1166,14 +1203,15 @@ function updateInventory() {
|
||||
const item = data[category].find(x => x.ItemId.$oid == oid);
|
||||
|
||||
if (item) {
|
||||
document.getElementById("detailedView-loading").classList.add("d-none");
|
||||
|
||||
if (item.ItemName) {
|
||||
$("#detailedView-route h3").text(item.ItemName);
|
||||
$("#detailedView-title").text(item.ItemName);
|
||||
$("#detailedView-route .text-body-secondary").text(
|
||||
itemMap[item.ItemType]?.name ?? item.ItemType
|
||||
);
|
||||
} else {
|
||||
$("#detailedView-route h3").text(itemMap[item.ItemType]?.name ?? item.ItemType);
|
||||
$("#detailedView-route .text-body-secondary").text("");
|
||||
$("#detailedView-title").text(itemMap[item.ItemType]?.name ?? item.ItemType);
|
||||
}
|
||||
|
||||
if (category == "Suits") {
|
||||
@ -1213,6 +1251,127 @@ function updateInventory() {
|
||||
}
|
||||
document.getElementById("crystals-list").appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById("edit-suit-invigorations-card").classList.remove("d-none");
|
||||
const { OffensiveUpgrade, DefensiveUpgrade, UpgradesExpiry } =
|
||||
suitInvigorationUpgradeData(item);
|
||||
document.getElementById("dv-invigoration-offensive").value = OffensiveUpgrade;
|
||||
document.getElementById("dv-invigoration-defensive").value = DefensiveUpgrade;
|
||||
document.getElementById("dv-invigoration-expiry").value = UpgradesExpiry;
|
||||
|
||||
{
|
||||
document.getElementById("loadout-card").classList.remove("d-none");
|
||||
const maxModConfigNum = Math.min(2 + (item.ModSlotPurchases ?? 0), 5);
|
||||
|
||||
const configs = item.Configs ?? [];
|
||||
|
||||
const loadoutTabs = document.getElementById("loadoutTabs");
|
||||
const loadoutTabsContent = document.getElementById("loadoutTabsContent");
|
||||
loadoutTabs.innerHTML = "";
|
||||
loadoutTabsContent.innerHTML = "";
|
||||
for (let i = 0; i <= maxModConfigNum; i++) {
|
||||
const config = configs[i] ?? {};
|
||||
|
||||
{
|
||||
const li = document.createElement("li");
|
||||
li.classList.add("nav-item");
|
||||
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("nav-link");
|
||||
if (i === 0) button.classList.add("active");
|
||||
button.id = `config${i}-tab`;
|
||||
button.setAttribute("data-bs-toggle", "tab");
|
||||
button.setAttribute("data-bs-target", `#config${i}`);
|
||||
button.innerHTML = config.Name?.trim() || String.fromCharCode(65 + i);
|
||||
|
||||
li.appendChild(button);
|
||||
loadoutTabs.appendChild(li);
|
||||
}
|
||||
|
||||
{
|
||||
const tabDiv = document.createElement("div");
|
||||
tabDiv.classList = "tab-pane";
|
||||
if (i === 0) tabDiv.classList.add("show", "active");
|
||||
|
||||
tabDiv.id = `config${i}`;
|
||||
|
||||
{
|
||||
const abilityOverrideForm = document.createElement("form");
|
||||
abilityOverrideForm.classList = "form-group mt-2";
|
||||
abilityOverrideForm.setAttribute(
|
||||
"onsubmit",
|
||||
`handleAbilityOverride(event, ${i});return false;`
|
||||
);
|
||||
|
||||
const abilityOverrideFormLabel = document.createElement("label");
|
||||
abilityOverrideFormLabel.setAttribute("data-loc", "abilityOverride_label");
|
||||
abilityOverrideFormLabel.innerHTML = loc("abilityOverride_label");
|
||||
abilityOverrideFormLabel.classList = "form-label";
|
||||
abilityOverrideFormLabel.setAttribute(
|
||||
"for",
|
||||
`abilityOverride-ability-config-${i}`
|
||||
);
|
||||
abilityOverrideForm.appendChild(abilityOverrideFormLabel);
|
||||
|
||||
const abilityOverrideInputGroup = document.createElement("div");
|
||||
abilityOverrideInputGroup.classList = "input-group";
|
||||
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
|
||||
|
||||
const abilityOverrideInput = document.createElement("input");
|
||||
abilityOverrideInput.id = `abilityOverride-ability-config-${i}`;
|
||||
abilityOverrideInput.classList = "form-control";
|
||||
abilityOverrideInput.setAttribute("list", "datalist-Abilities");
|
||||
if (config.AbilityOverride) {
|
||||
const datalist = document.getElementById("datalist-Abilities");
|
||||
const options = Array.from(datalist.options);
|
||||
abilityOverrideInput.value = options.find(
|
||||
option =>
|
||||
config.AbilityOverride.Ability == option.getAttribute("data-key")
|
||||
).value;
|
||||
}
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideInput);
|
||||
|
||||
const abilityOverrideOnSlot = document.createElement("span");
|
||||
abilityOverrideOnSlot.classList = "input-group-text";
|
||||
abilityOverrideOnSlot.setAttribute("data-loc", "abilityOverride_onSlot");
|
||||
abilityOverrideOnSlot.innerHTML = loc("abilityOverride_onSlot");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideOnSlot);
|
||||
|
||||
const abilityOverrideSecondInput = document.createElement("input");
|
||||
abilityOverrideSecondInput.id = `abilityOverride-ability-index-config-${i}`;
|
||||
abilityOverrideSecondInput.classList = "form-control";
|
||||
abilityOverrideSecondInput.setAttribute("type", "number");
|
||||
abilityOverrideSecondInput.setAttribute("min", "0");
|
||||
abilityOverrideSecondInput.setAttribute("max", "3");
|
||||
if (config.AbilityOverride)
|
||||
abilityOverrideSecondInput.value = config.AbilityOverride.Index;
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideSecondInput);
|
||||
|
||||
const abilityOverrideSetButton = document.createElement("button");
|
||||
abilityOverrideSetButton.classList = "btn btn-primary";
|
||||
abilityOverrideSetButton.setAttribute("type", "submit");
|
||||
abilityOverrideSetButton.setAttribute("value", "set");
|
||||
abilityOverrideSetButton.setAttribute("data-loc", "general_setButton");
|
||||
abilityOverrideSetButton.innerHTML = loc("general_setButton");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideSetButton);
|
||||
|
||||
const abilityOverrideRemoveButton = document.createElement("button");
|
||||
abilityOverrideRemoveButton.classList = "btn btn-danger";
|
||||
abilityOverrideRemoveButton.setAttribute("type", "submit");
|
||||
abilityOverrideRemoveButton.setAttribute("value", "remove");
|
||||
abilityOverrideRemoveButton.setAttribute("data-loc", "code_remove");
|
||||
abilityOverrideRemoveButton.innerHTML = loc("code_remove");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideRemoveButton);
|
||||
|
||||
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
|
||||
|
||||
tabDiv.appendChild(abilityOverrideForm);
|
||||
}
|
||||
|
||||
loadoutTabsContent.appendChild(tabDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
|
||||
document.getElementById("valenceBonus-card").classList.remove("d-none");
|
||||
document.getElementById("valenceBonus-innateDamage").value = "";
|
||||
@ -1225,6 +1384,35 @@ function updateInventory() {
|
||||
document.getElementById("valenceBonus-procent").value = Math.round(buffValue * 1000) / 10;
|
||||
}
|
||||
}
|
||||
if (modularWeapons.includes(item.ItemType)) {
|
||||
document.getElementById("modularParts-card").classList.remove("d-none");
|
||||
const form = document.getElementById("modularParts-form");
|
||||
form.innerHTML = "";
|
||||
const requiredParts = getRequiredParts(category, item.ItemType);
|
||||
|
||||
requiredParts.forEach(modularPart => {
|
||||
const input = document.createElement("input");
|
||||
input.classList.add("form-control");
|
||||
input.id = "detailedView-modularPart-" + modularPart;
|
||||
input.setAttribute("list", "datalist-ModularParts-" + modularPart);
|
||||
|
||||
const datalist = document.getElementById("datalist-ModularParts-" + modularPart);
|
||||
const options = Array.from(datalist.options);
|
||||
|
||||
input.value =
|
||||
options.find(option => item.ModularParts.includes(option.getAttribute("data-key")))
|
||||
?.value || "";
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
const changeButton = document.createElement("button");
|
||||
changeButton.classList.add("btn");
|
||||
changeButton.classList.add("btn-primary");
|
||||
changeButton.type = "submit";
|
||||
changeButton.setAttribute("data-loc", "cheats_changeButton");
|
||||
changeButton.innerHTML = loc("cheats_changeButton");
|
||||
form.appendChild(changeButton);
|
||||
}
|
||||
} else {
|
||||
single.loadRoute("/webui/inventory");
|
||||
}
|
||||
@ -1287,6 +1475,10 @@ function updateInventory() {
|
||||
}
|
||||
document.getElementById("Boosters-list").appendChild(tr);
|
||||
});
|
||||
|
||||
for (const elm of accountCheats) {
|
||||
elm.checked = !!data[elm.id];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1324,47 +1516,41 @@ function doAcquireEquipment(category) {
|
||||
});
|
||||
}
|
||||
|
||||
function doAcquireModularEquipment(category, WeaponType) {
|
||||
let requiredParts;
|
||||
let Parts = [];
|
||||
function getRequiredParts(category, WeaponType) {
|
||||
switch (category) {
|
||||
case "HoverBoards":
|
||||
WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
||||
requiredParts = ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
||||
break;
|
||||
case "Hoverboards":
|
||||
return ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
||||
|
||||
case "OperatorAmps":
|
||||
requiredParts = ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
||||
break;
|
||||
return ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
||||
|
||||
case "Melee":
|
||||
requiredParts = ["BLADE", "HILT", "HILT_WEIGHT"];
|
||||
break;
|
||||
return ["BLADE", "HILT", "HILT_WEIGHT"];
|
||||
|
||||
case "LongGuns":
|
||||
requiredParts = ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
||||
break;
|
||||
return ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
||||
|
||||
case "Pistols":
|
||||
requiredParts = ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
||||
break;
|
||||
return ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
||||
|
||||
case "MoaPets":
|
||||
if (WeaponType == "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
|
||||
requiredParts = ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"];
|
||||
} else {
|
||||
requiredParts = ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
||||
}
|
||||
break;
|
||||
case "KubrowPets":
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
||||
].includes(WeaponType)
|
||||
) {
|
||||
requiredParts = ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"];
|
||||
} else {
|
||||
requiredParts = ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
||||
}
|
||||
break;
|
||||
return WeaponType === "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit"
|
||||
? ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"]
|
||||
: ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
||||
|
||||
case "KubrowPets": {
|
||||
return WeaponType.endsWith("InfestedCatbrowPetPowerSuit")
|
||||
? ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"]
|
||||
: ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doAcquireModularEquipment(category, WeaponType) {
|
||||
if (category === "Hoverboards") WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
||||
const requiredParts = getRequiredParts(category, WeaponType);
|
||||
let Parts = [];
|
||||
|
||||
requiredParts.forEach(part => {
|
||||
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
|
||||
if (partName) {
|
||||
@ -1481,19 +1667,22 @@ function doAcquireEvolution() {
|
||||
setEvolutionProgress([{ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 }]);
|
||||
}
|
||||
|
||||
$("input[list]").on("input", function () {
|
||||
$(document).on("input", "input[list]", function () {
|
||||
$(this).removeClass("is-invalid");
|
||||
});
|
||||
|
||||
function dispatchAddItemsRequestsBatch(requests) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addItems?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addItems?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1514,7 +1703,7 @@ function addMissingEquipment(categories) {
|
||||
});
|
||||
});
|
||||
if (requests.length != 0 && window.confirm(loc("code_addItemsConfirm").split("|COUNT|").join(requests.length))) {
|
||||
dispatchAddItemsRequestsBatch(requests);
|
||||
return dispatchAddItemsRequestsBatch(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1530,7 +1719,7 @@ function addMissingEvolutionProgress() {
|
||||
requests.push({ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 });
|
||||
});
|
||||
if (requests.length != 0 && window.confirm(loc("code_addItemsConfirm").split("|COUNT|").join(requests.length))) {
|
||||
setEvolutionProgress(requests);
|
||||
return setEvolutionProgress(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1698,7 +1887,7 @@ function disposeOfGear(category, oid) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -1720,7 +1909,7 @@ function disposeOfItems(category, type, count) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -1757,14 +1946,17 @@ function maturePet(oid, revert) {
|
||||
}
|
||||
|
||||
function setEvolutionProgress(requests) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/setEvolutionProgress?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/setEvolutionProgress?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1923,6 +2115,8 @@ function doAcquireModMax() {
|
||||
alert("doAcquireModMax: " + uniqueName);
|
||||
}
|
||||
|
||||
// Cheats route
|
||||
|
||||
const uiConfigs = [...$(".config-form input[id], .config-form select[id]")].map(x => x.id);
|
||||
|
||||
for (const id of uiConfigs) {
|
||||
@ -1930,7 +2124,13 @@ for (const id of uiConfigs) {
|
||||
if (elm.tagName == "SELECT") {
|
||||
elm.onchange = function () {
|
||||
let value = this.value;
|
||||
if (!isNaN(parseInt(value))) {
|
||||
if (value == "true") {
|
||||
value = true;
|
||||
} else if (value == "false") {
|
||||
value = false;
|
||||
} else if (value == "null") {
|
||||
value = null;
|
||||
} else if (!isNaN(parseInt(value))) {
|
||||
value = parseInt(value);
|
||||
}
|
||||
$.post({
|
||||
@ -1954,6 +2154,19 @@ for (const id of uiConfigs) {
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll(".config-form .input-group").forEach(grp => {
|
||||
const input = grp.querySelector("input");
|
||||
const btn = grp.querySelector("button");
|
||||
input.oninput = input.onchange = function () {
|
||||
btn.classList.remove("btn-secondary");
|
||||
btn.classList.add("btn-primary");
|
||||
};
|
||||
btn.onclick = function () {
|
||||
btn.classList.remove("btn-primary");
|
||||
btn.classList.add("btn-secondary");
|
||||
};
|
||||
});
|
||||
|
||||
function doSaveConfigInt(id) {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
@ -1988,8 +2201,6 @@ function doSaveConfigStringArray(id) {
|
||||
});
|
||||
}
|
||||
|
||||
// Cheats route
|
||||
|
||||
single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||
let interval;
|
||||
interval = setInterval(() => {
|
||||
@ -2010,7 +2221,7 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||
if (elm.type == "checkbox") {
|
||||
elm.checked = value;
|
||||
} else if (elm.classList.contains("tags-input")) {
|
||||
elm.value = value.join(", ");
|
||||
elm.value = (value ?? []).join(", ");
|
||||
elm.oninput();
|
||||
} else {
|
||||
elm.value = value ?? elm.getAttribute("data-default");
|
||||
@ -2019,6 +2230,10 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||
})
|
||||
.fail(res => {
|
||||
if (res.responseText == "Log-in expired") {
|
||||
if (ws_is_open && !auth_pending) {
|
||||
console.warn("Credentials invalidated but the server didn't let us know");
|
||||
sendAuth();
|
||||
}
|
||||
revalidateAuthz().then(() => {
|
||||
if (single.getCurrentPath() == "/webui/cheats") {
|
||||
single.loadRoute("/webui/cheats");
|
||||
@ -2097,6 +2312,23 @@ function doIntrinsicsUnlockAll() {
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll("#account-cheats input[type=checkbox]").forEach(elm => {
|
||||
elm.onchange = function () {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/setAccountCheat?" + window.authz /*+ "&wsid=" + wsid*/,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
key: elm.id,
|
||||
value: elm.checked
|
||||
})
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
// Mods route
|
||||
|
||||
function doAddAllMods() {
|
||||
let modsAll = new Set();
|
||||
for (const child of document.getElementById("datalist-mods").children) {
|
||||
@ -2141,7 +2373,7 @@ function doRemoveUnrankedMods() {
|
||||
req.done(inventory => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify({
|
||||
SellCurrency: "SC_RegularCredits",
|
||||
@ -2171,8 +2403,14 @@ function doAddMissingMaxRankMods() {
|
||||
// DetailedView Route
|
||||
|
||||
single.getRoute("#detailedView-route").on("beforeload", function () {
|
||||
this.element.querySelector("h3").textContent = "Loading...";
|
||||
document.getElementById("detailedView-loading").classList.remove("d-none");
|
||||
document.getElementById("detailedView-title").textContent = "";
|
||||
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
|
||||
document.getElementById("loadout-card").classList.add("d-none");
|
||||
document.getElementById("archonShards-card").classList.add("d-none");
|
||||
document.getElementById("edit-suit-invigorations-card").classList.add("d-none");
|
||||
document.getElementById("modularParts-card").classList.add("d-none");
|
||||
document.getElementById("modularParts-form").innerHTML = "";
|
||||
document.getElementById("valenceBonus-card").classList.add("d-none");
|
||||
if (window.didInitialInventoryUpdate) {
|
||||
updateInventory();
|
||||
@ -2254,6 +2492,13 @@ function doAddCurrency(currency) {
|
||||
});
|
||||
}
|
||||
|
||||
function readdQuestKey(itemMap, itemType) {
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", itemType);
|
||||
option.value = itemMap[itemType]?.name ?? itemType;
|
||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
||||
}
|
||||
|
||||
function doQuestUpdate(operation, itemType) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
@ -2323,22 +2568,10 @@ function handleModularSelection(category) {
|
||||
modularFieldsZanuka.style.display = "none";
|
||||
}
|
||||
} else if (inventoryCategory === "KubrowPets") {
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
||||
].includes(key)
|
||||
) {
|
||||
if (key.endsWith("InfestedCatbrowPetPowerSuit")) {
|
||||
modularFieldsCatbrow.style.display = "";
|
||||
modularFieldsKubrow.style.display = "none";
|
||||
} else if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
|
||||
].includes(key)
|
||||
) {
|
||||
} else if (key.endsWith("PredatorKubrowPetPowerSuit")) {
|
||||
modularFieldsCatbrow.style.display = "none";
|
||||
modularFieldsKubrow.style.display = "";
|
||||
} else {
|
||||
@ -2439,9 +2672,17 @@ function formatDatetime(fmt, date) {
|
||||
const calls_in_flight = new Set();
|
||||
|
||||
async function debounce(func, ...args) {
|
||||
calls_in_flight.add(func);
|
||||
await func(...args);
|
||||
calls_in_flight.delete(func);
|
||||
if (!func.name) {
|
||||
throw new Error(`cannot debounce anonymous functions`);
|
||||
}
|
||||
const callid = JSON.stringify({ func: func.name, args });
|
||||
if (!calls_in_flight.has(callid)) {
|
||||
calls_in_flight.add(callid);
|
||||
await func(...args);
|
||||
calls_in_flight.delete(callid);
|
||||
} else {
|
||||
console.log("debouncing", callid);
|
||||
}
|
||||
}
|
||||
|
||||
async function doMaxPlexus() {
|
||||
@ -2764,3 +3005,146 @@ document.querySelectorAll("#sidebar .nav-link").forEach(function (elm) {
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
});
|
||||
|
||||
async function markAllAsRead() {
|
||||
await revalidateAuthz();
|
||||
const { Inbox } = await fetch("/api/inbox.php?" + window.authz).then(x => x.json());
|
||||
let any = false;
|
||||
for (const msg of Inbox) {
|
||||
if (!msg.r) {
|
||||
await fetch("/api/inbox.php?" + window.authz + "&messageId=" + msg.messageId.$oid);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
toast(loc(any ? "code_succRelog" : "code_nothingToDo"));
|
||||
}
|
||||
|
||||
function handleModularPartsChange(event) {
|
||||
event.preventDefault();
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const form = document.getElementById("modularParts-form");
|
||||
const inputs = form.querySelectorAll("input");
|
||||
const modularParts = [];
|
||||
inputs.forEach(input => {
|
||||
const key = getKey(input);
|
||||
if (!key) {
|
||||
input.classList.add("is-invalid");
|
||||
} else {
|
||||
modularParts.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
if (inputs.length == modularParts.length) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/changeModularParts?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
category: urlParams.get("productCategory"),
|
||||
oid: urlParams.get("itemId"),
|
||||
modularParts
|
||||
})
|
||||
}).then(function () {
|
||||
toast(loc("code_succChange"));
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
function suitInvigorationUpgradeData(suitData) {
|
||||
let expiryDate = "";
|
||||
if (suitData.UpgradesExpiry) {
|
||||
if (suitData.UpgradesExpiry.$date) {
|
||||
expiryDate = new Date(parseInt(suitData.UpgradesExpiry.$date.$numberLong));
|
||||
} else if (typeof suitData.UpgradesExpiry === "number") {
|
||||
expiryDate = new Date(suitData.UpgradesExpiry);
|
||||
} else if (suitData.UpgradesExpiry instanceof Date) {
|
||||
expiryDate = suitData.UpgradesExpiry;
|
||||
}
|
||||
if (expiryDate && !isNaN(expiryDate.getTime())) {
|
||||
const year = expiryDate.getFullYear();
|
||||
const month = String(expiryDate.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(expiryDate.getDate()).padStart(2, "0");
|
||||
const hours = String(expiryDate.getHours()).padStart(2, "0");
|
||||
const minutes = String(expiryDate.getMinutes()).padStart(2, "0");
|
||||
expiryDate = `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
} else {
|
||||
expiryDate = "";
|
||||
}
|
||||
}
|
||||
return {
|
||||
oid: suitData.ItemId.$oid,
|
||||
OffensiveUpgrade: suitData.OffensiveUpgrade || "",
|
||||
DefensiveUpgrade: suitData.DefensiveUpgrade || "",
|
||||
UpgradesExpiry: expiryDate
|
||||
};
|
||||
}
|
||||
|
||||
function submitSuitInvigorationUpgrade(event) {
|
||||
event.preventDefault();
|
||||
const oid = new URLSearchParams(window.location.search).get("itemId");
|
||||
const offensiveUpgrade = document.getElementById("dv-invigoration-offensive").value;
|
||||
const defensiveUpgrade = document.getElementById("dv-invigoration-defensive").value;
|
||||
const expiry = document.getElementById("dv-invigoration-expiry").value;
|
||||
|
||||
if (!offensiveUpgrade || !defensiveUpgrade) {
|
||||
alert(loc("code_requiredInvigorationUpgrade"));
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
OffensiveUpgrade: offensiveUpgrade,
|
||||
DefensiveUpgrade: defensiveUpgrade
|
||||
};
|
||||
|
||||
if (expiry) {
|
||||
data.UpgradesExpiry = new Date(expiry).getTime();
|
||||
}
|
||||
|
||||
editSuitInvigorationUpgrade(oid, data);
|
||||
}
|
||||
|
||||
function clearSuitInvigorationUpgrades() {
|
||||
editSuitInvigorationUpgrade(new URLSearchParams(window.location.search).get("itemId"), null);
|
||||
}
|
||||
|
||||
async function editSuitInvigorationUpgrade(oid, data) {
|
||||
/* data?: {
|
||||
DefensiveUpgrade: string;
|
||||
OffensiveUpgrade: string;
|
||||
UpgradesExpiry?: number;
|
||||
}*/
|
||||
$.post({
|
||||
url: "/custom/editSuitInvigorationUpgrade?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ oid, data })
|
||||
}).done(function () {
|
||||
updateInventory();
|
||||
});
|
||||
}
|
||||
|
||||
function handleAbilityOverride(event, configIndex) {
|
||||
event.preventDefault();
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const action = event.submitter.value;
|
||||
const Ability = getKey(document.getElementById(`abilityOverride-ability-config-${configIndex}`));
|
||||
const Index = document.getElementById(`abilityOverride-ability-index-config-${configIndex}`).value;
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/abilityOverride?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
category: urlParams.get("productCategory"),
|
||||
oid: urlParams.get("itemId"),
|
||||
configIndex,
|
||||
action,
|
||||
AbilityOverride: {
|
||||
Ability,
|
||||
Index
|
||||
}
|
||||
})
|
||||
}).done(function () {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -11,7 +11,8 @@
|
||||
margin-left: 7rem;
|
||||
}
|
||||
|
||||
body.logged-in:has([data-lang="de"].active) #main-content {
|
||||
body.logged-in:has([data-lang="de"].active) #main-content,
|
||||
body.logged-in:has([data-lang="uk"].active) #main-content {
|
||||
margin-left: 8rem;
|
||||
}
|
||||
|
||||
@ -28,9 +29,12 @@ body:not(.logged-in) .user-dropdown {
|
||||
display: none;
|
||||
}
|
||||
|
||||
td.text-end > a > svg {
|
||||
/* font awesome icons */
|
||||
svg {
|
||||
fill: currentColor;
|
||||
height: 1em;
|
||||
}
|
||||
td.text-end > a > svg {
|
||||
margin-left: 0.5em;
|
||||
margin-bottom: 4px; /* to centre the icon */
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user