Compare commits

...

8 Commits

Author SHA1 Message Date
fd6b6ec9ab feat(webui): Valence Bonus
Some checks failed
Build / build (pull_request) Failing after 39s
Closes #1181
2025-06-28 14:49:38 -07:00
58bdb2d2ec chore(webui): improving inconsistent, long string (#2344)
All checks were successful
Build Docker image / docker-arm64 (push) Successful in 59s
Build / build (push) Successful in 54s
Build Docker image / docker-amd64 (push) Successful in 1m18s
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: #2344
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-06-28 10:28:09 -07:00
c4c622d82b feat: baro's void surplus (#2334)
All checks were successful
Build Docker image / docker-arm64 (push) Successful in 1m0s
Build / build (push) Successful in 59s
Build Docker image / docker-amd64 (push) Successful in 1m24s
Closes #2284

Reviewed-on: #2334
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-28 09:50:40 -07:00
44a129ab0b feat: create genetic imprint (#2337)
All checks were successful
Build / build (push) Successful in 54s
Build Docker image / docker-arm64 (push) Successful in 1m3s
Build Docker image / docker-amd64 (push) Successful in 1m18s
Re #2212

Reviewed-on: #2337
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-28 09:48:12 -07:00
5a7caa5ba9 feat: disableDailyTribute config (#2338)
Some checks failed
Build / build (push) Has been cancelled
Build Docker image / docker-arm64 (push) Has been cancelled
Build Docker image / docker-amd64 (push) Has been cancelled
Reviewed-on: #2338
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-28 09:47:39 -07:00
a9c5e30994 chore: deal with visiting navigation not resyncing inventory (#2340)
Some checks failed
Build / build (push) Successful in 57s
Build Docker image / docker-amd64 (push) Successful in 51s
Build Docker image / docker-arm64 (push) Has been cancelled
Closes #2339

Reviewed-on: #2340
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-28 09:46:45 -07:00
f0547cb9e6 chore(webui): update Chinese translation (#2341)
Some checks failed
Build Docker image / docker-arm64 (push) Successful in 1m1s
Build / build (push) Successful in 53s
Build Docker image / docker-amd64 (push) Has been cancelled
Reviewed-on: #2341
Co-authored-by: Corvus <corvus@noreply.localhost>
Co-committed-by: Corvus <corvus@noreply.localhost>
2025-06-28 09:45:45 -07:00
ef3d3b92c7 feat: darvo deal (#2261)
All checks were successful
Build / build (push) Successful in 49s
Build Docker image / docker-arm64 (push) Successful in 1m1s
Build Docker image / docker-amd64 (push) Successful in 1m12s
Closes #2260

Reviewed-on: #2261
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-28 09:39:41 -07:00
35 changed files with 959 additions and 234 deletions

View File

@ -11,17 +11,17 @@
"node": true "node": true
}, },
"rules": { "rules": {
"@typescript-eslint/explicit-function-return-type": "warn", "@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/restrict-template-expressions": "warn", "@typescript-eslint/restrict-template-expressions": "error",
"@typescript-eslint/restrict-plus-operands": "warn", "@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/no-unsafe-member-access": "warn", "@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "caughtErrors": "none" }], "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "caughtErrors": "none" }],
"@typescript-eslint/no-unsafe-argument": "error", "@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-call": "warn", "@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-assignment": "warn", "@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-explicit-any": "error",
"no-loss-of-precision": "warn", "no-loss-of-precision": "error",
"@typescript-eslint/no-unnecessary-condition": "warn", "@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-base-to-string": "off", "@typescript-eslint/no-base-to-string": "off",
"no-case-declarations": "error", "no-case-declarations": "error",
"prettier/prettier": "error", "prettier/prettier": "error",

View File

@ -58,6 +58,7 @@
"fastClanAscension": false, "fastClanAscension": false,
"missionsCanGiveAllRelics": false, "missionsCanGiveAllRelics": false,
"unlockAllSimarisResearchEntries": false, "unlockAllSimarisResearchEntries": false,
"disableDailyTribute": false,
"spoofMasteryRank": -1, "spoofMasteryRank": -1,
"nightwaveStandingMultiplier": 1, "nightwaveStandingMultiplier": 1,
"unfaithfulBugFixes": { "unfaithfulBugFixes": {
@ -75,7 +76,8 @@
"duviriOverride": "", "duviriOverride": "",
"nightwaveOverride": "", "nightwaveOverride": "",
"allTheFissures": "", "allTheFissures": "",
"circuitGameModes": null "circuitGameModes": null,
"darvoStockMultiplier": 1
}, },
"dev": { "dev": {
"keepVendorsExpired": false "keepVendorsExpired": false

View File

@ -13,7 +13,8 @@ import {
addItem, addItem,
addRecipes, addRecipes,
occupySlot, occupySlot,
combineInventoryChanges combineInventoryChanges,
addKubrowPetPrint
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
@ -119,6 +120,9 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
} }
} }
pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis; pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis;
} else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
addKubrowPetPrint(inventory, pet, InventoryChanges);
} else if (recipe.secretIngredientAction != "SIA_UNBRAND") { } else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
InventoryChanges = { InventoryChanges = {
...InventoryChanges, ...InventoryChanges,

View File

@ -1,8 +1,10 @@
import { DailyDeal } from "@/src/models/worldStateModel";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const getDailyDealStockLevelsController: RequestHandler = (req, res) => { export const getDailyDealStockLevelsController: RequestHandler = async (req, res) => {
const dailyDeal = (await DailyDeal.findOne({ StoreItem: req.query.productName }, "AmountSold"))!;
res.json({ res.json({
StoreItem: req.query.productName, StoreItem: req.query.productName,
AmountSold: 0 AmountSold: dailyDeal.AmountSold
}); });
}; };

View File

@ -9,15 +9,26 @@ import {
updateCurrency updateCurrency
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService"; import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService"; import { handleDailyDealPurchase, handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IOid } from "@/src/types/commonTypes"; import { IOid } from "@/src/types/commonTypes";
import { IInventoryChanges, IPurchaseParams, PurchaseSource } from "@/src/types/purchaseTypes"; import { IPurchaseParams, IPurchaseResponse, PurchaseSource } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { ExportBundles, ExportFlavour } from "warframe-public-export-plus"; import { ExportBundles, ExportFlavour } from "warframe-public-export-plus";
const checkPurchaseParams = (params: IPurchaseParams): boolean => {
switch (params.Source) {
case PurchaseSource.Market:
return params.UsePremium;
case PurchaseSource.DailyDeal:
return true;
}
return false;
};
export const giftingController: RequestHandler = async (req, res) => { export const giftingController: RequestHandler = async (req, res) => {
const data = getJSONfromString<IGiftingRequest>(String(req.body)); const data = getJSONfromString<IGiftingRequest>(String(req.body));
if (data.PurchaseParams.Source != PurchaseSource.Market || !data.PurchaseParams.UsePremium) { if (!checkPurchaseParams(data.PurchaseParams)) {
throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`); throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
} }
@ -58,16 +69,19 @@ export const giftingController: RequestHandler = async (req, res) => {
} }
senderInventory.GiftsRemaining -= 1; senderInventory.GiftsRemaining -= 1;
const inventoryChanges: IInventoryChanges = updateCurrency( const response: IPurchaseResponse = {
senderInventory, InventoryChanges: {}
data.PurchaseParams.ExpectedPrice, };
true if (data.PurchaseParams.Source == PurchaseSource.DailyDeal) {
); await handleDailyDealPurchase(senderInventory, data.PurchaseParams, response);
} else {
updateCurrency(senderInventory, data.PurchaseParams.ExpectedPrice, true, response.InventoryChanges);
}
if (data.PurchaseParams.StoreItem in ExportBundles) { if (data.PurchaseParams.StoreItem in ExportBundles) {
const bundle = ExportBundles[data.PurchaseParams.StoreItem]; const bundle = ExportBundles[data.PurchaseParams.StoreItem];
if (bundle.giftingBonus) { if (bundle.giftingBonus) {
combineInventoryChanges( combineInventoryChanges(
inventoryChanges, response.InventoryChanges,
(await handleStoreItemAcquisition(bundle.giftingBonus, senderInventory)).InventoryChanges (await handleStoreItemAcquisition(bundle.giftingBonus, senderInventory)).InventoryChanges
); );
} }
@ -99,9 +113,7 @@ export const giftingController: RequestHandler = async (req, res) => {
} }
]); ]);
res.json({ res.json(response);
InventoryChanges: inventoryChanges
});
}; };
interface IGiftingRequest { interface IGiftingRequest {

View File

@ -24,6 +24,8 @@ import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
import { Ship } from "@/src/models/shipModel"; import { Ship } from "@/src/models/shipModel";
import { toLegacyOid, toOid, version_compare } from "@/src/helpers/inventoryHelpers"; import { toLegacyOid, toOid, version_compare } from "@/src/helpers/inventoryHelpers";
import { Inbox } from "@/src/models/inboxModel"; import { Inbox } from "@/src/models/inboxModel";
import { unixTimesInMs } from "@/src/constants/timeConstants";
import { DailyDeal } from "@/src/models/worldStateModel";
export const inventoryController: RequestHandler = async (request, response) => { export const inventoryController: RequestHandler = async (request, response) => {
const account = await getAccountForRequest(request); const account = await getAccountForRequest(request);
@ -37,6 +39,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
// Handle daily reset // Handle daily reset
if (!inventory.NextRefill || Date.now() >= inventory.NextRefill.getTime()) { if (!inventory.NextRefill || Date.now() >= inventory.NextRefill.getTime()) {
const today = Math.trunc(Date.now() / 86400000);
for (const key of allDailyAffiliationKeys) { for (const key of allDailyAffiliationKeys) {
inventory[key] = 16000 + inventory.PlayerLevel * 500; inventory[key] = 16000 + inventory.PlayerLevel * 500;
} }
@ -47,12 +51,12 @@ export const inventoryController: RequestHandler = async (request, response) =>
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask(); inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
if (inventory.NextRefill) { if (inventory.NextRefill) {
const lastLoginDay = Math.trunc(inventory.NextRefill.getTime() / 86400000) - 1;
const daysPassed = today - lastLoginDay;
if (config.noArgonCrystalDecay) { if (config.noArgonCrystalDecay) {
inventory.FoundToday = undefined; inventory.FoundToday = undefined;
} else { } else {
const lastLoginDay = Math.trunc(inventory.NextRefill.getTime() / 86400000) - 1;
const today = Math.trunc(Date.now() / 86400000);
const daysPassed = today - lastLoginDay;
for (let i = 0; i != daysPassed; ++i) { for (let i = 0; i != daysPassed; ++i) {
const numArgonCrystals = const numArgonCrystals =
inventory.MiscItems.find(x => x.ItemType == "/Lotus/Types/Items/MiscItems/ArgonCrystal") inventory.MiscItems.find(x => x.ItemType == "/Lotus/Types/Items/MiscItems/ArgonCrystal")
@ -84,11 +88,29 @@ export const inventoryController: RequestHandler = async (request, response) =>
inventory.FoundToday = undefined; inventory.FoundToday = undefined;
} }
} }
if (inventory.UsedDailyDeals.length != 0) {
if (daysPassed == 1) {
const todayAt0Utc = today * 86400000;
const darvoIndex = Math.trunc((todayAt0Utc - 25200000) / (26 * unixTimesInMs.hour));
const darvoStart = darvoIndex * (26 * unixTimesInMs.hour) + 25200000;
const darvoOid =
((darvoStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "adc51a72f7324d95";
const deal = await DailyDeal.findById(darvoOid);
if (deal) {
inventory.UsedDailyDeals = inventory.UsedDailyDeals.filter(x => x == deal.StoreItem); // keep only the deal that came into this new day with us
} else {
inventory.UsedDailyDeals = [];
}
} else {
inventory.UsedDailyDeals = [];
}
}
} }
cleanupInventory(inventory); cleanupInventory(inventory);
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000); inventory.NextRefill = new Date((today + 1) * 86400000); // tomorrow at 0 UTC
//await inventory.save(); //await inventory.save();
} }
@ -291,9 +313,6 @@ export const getInventoryResponse = async (
applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry); applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
} }
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
inventoryResponse.LastInventorySync = undefined;
// Set 2FA enabled so trading post can be used // Set 2FA enabled so trading post can be used
inventoryResponse.HWIDProtectEnabled = true; inventoryResponse.HWIDProtectEnabled = true;

View File

@ -8,6 +8,7 @@ import {
setAccountGotLoginRewardToday setAccountGotLoginRewardToday
} from "@/src/services/loginRewardService"; } from "@/src/services/loginRewardService";
import { getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { config } from "@/src/services/configService";
export const loginRewardsController: RequestHandler = async (req, res) => { export const loginRewardsController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req); const account = await getAccountForRequest(req);
@ -15,7 +16,7 @@ export const loginRewardsController: RequestHandler = async (req, res) => {
const isMilestoneDay = account.LoginDays == 5 || account.LoginDays % 50 == 0; const isMilestoneDay = account.LoginDays == 5 || account.LoginDays % 50 == 0;
const nextMilestoneDay = account.LoginDays < 5 ? 5 : (Math.trunc(account.LoginDays / 50) + 1) * 50; const nextMilestoneDay = account.LoginDays < 5 ? 5 : (Math.trunc(account.LoginDays / 50) + 1) * 50;
if (today == account.LastLoginRewardDate) { if (today == account.LastLoginRewardDate || config.disableDailyTribute) {
res.json({ res.json({
DailyTributeInfo: { DailyTributeInfo: {
IsMilestoneDay: isMilestoneDay, IsMilestoneDay: isMilestoneDay,

View File

@ -11,6 +11,7 @@ export const purchaseController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const response = await handlePurchase(purchaseRequest, inventory); const response = await handlePurchase(purchaseRequest, inventory);
await inventory.save(); await inventory.save();
//console.log(JSON.stringify(response, null, 2));
res.json(response); res.json(response);
sendWsBroadcastTo(accountId, { update_inventory: true }); sendWsBroadcastTo(accountId, { update_inventory: true });
}; };

View File

@ -45,9 +45,9 @@ export const startRecipeController: RequestHandler = async (req, res) => {
for (let i = 0; i != recipe.ingredients.length; ++i) { for (let i = 0; i != recipe.ingredients.length; ++i) {
if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") { if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
if (recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") { if (recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") {
const index = inventory.KubrowPetEggs!.findIndex(x => x._id.equals(startRecipeRequest.Ids[i])); const index = inventory.KubrowPetEggs.findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
if (index != -1) { if (index != -1) {
inventory.KubrowPetEggs!.splice(index, 1); inventory.KubrowPetEggs.splice(index, 1);
} }
} else { } else {
const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory; const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
@ -72,6 +72,10 @@ export const startRecipeController: RequestHandler = async (req, res) => {
if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") { if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
inventoryChanges = addKubrowPet(inventory, getRandomElement(recipe.secretIngredients!)!.ItemType); inventoryChanges = addKubrowPet(inventory, getRandomElement(recipe.secretIngredients!)!.ItemType);
pr.KubrowPet = new Types.ObjectId(fromOid(inventoryChanges.KubrowPets![0].ItemId)); pr.KubrowPet = new Types.ObjectId(fromOid(inventoryChanges.KubrowPets![0].ItemId));
} else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
pr.KubrowPet = new Types.ObjectId(startRecipeRequest.Ids[recipe.ingredients.length]);
const pet = inventory.KubrowPets.id(pr.KubrowPet)!;
pet.Details!.PrintsRemaining -= 1;
} else if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") { } else if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
const spectreLoadout: ISpectreLoadout = { const spectreLoadout: ISpectreLoadout = {
ItemType: recipe.resultType, ItemType: recipe.resultType,

View File

@ -0,0 +1,41 @@
import { getInventory } from "@/src/services/inventoryService";
import { WeaponTypeInternal } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
export const updateFingerprintController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const request = req.body as IUpdateFingerPrintRequest;
const inventory = await getInventory(accountId, request.category);
if (inventory) {
const item = inventory[request.category].id(request.oid);
if (item) {
if (request.action == "set" && request.upgradeFingerprint.buffs[0].Tag) {
const newUpgradeFingerprint = request.upgradeFingerprint;
if (!newUpgradeFingerprint.compact) newUpgradeFingerprint.compact = item.ItemType;
item.UpgradeType = request.upgradeType;
item.UpgradeFingerprint = JSON.stringify(newUpgradeFingerprint);
} else if (request.action == "remove") {
item.UpgradeFingerprint = undefined;
item.UpgradeType = undefined;
}
await inventory.save();
}
}
res.end();
};
interface IUpdateFingerPrintRequest {
category: WeaponTypeInternal;
oid: string;
action: "set" | "remove";
upgradeType: string;
upgradeFingerprint: {
compact?: string;
buffs: {
Tag: string;
Value: number;
}[];
};
}

View File

@ -1,15 +1,19 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getWorldState, populateFissures } from "@/src/services/worldStateService"; import { getWorldState, populateDailyDeal, populateFissures } from "@/src/services/worldStateService";
import { version_compare } from "@/src/helpers/inventoryHelpers"; import { version_compare } from "@/src/helpers/inventoryHelpers";
export const worldStateController: RequestHandler = async (req, res) => { export const worldStateController: RequestHandler = async (req, res) => {
const buildLabel = req.query.buildLabel as string | undefined; const buildLabel = req.query.buildLabel as string | undefined;
const worldState = getWorldState(buildLabel); const worldState = getWorldState(buildLabel);
const populatePromises = [populateDailyDeal(worldState)];
// Omitting void fissures for versions prior to Dante Unbound to avoid script errors. // Omitting void fissures for versions prior to Dante Unbound to avoid script errors.
if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) { if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) {
await populateFissures(worldState); populatePromises.push(populateFissures(worldState));
} }
await Promise.all(populatePromises);
res.json(worldState); res.json(worldState);
}; };

View File

@ -91,7 +91,7 @@ import {
ICrewMemberSkillEfficiency, ICrewMemberSkillEfficiency,
ICrewMemberDatabase, ICrewMemberDatabase,
ICrewMemberClient, ICrewMemberClient,
ISortieRewardAttenuation, IRewardAttenuation,
IInvasionProgressDatabase, IInvasionProgressDatabase,
IInvasionProgressClient, IInvasionProgressClient,
IAccolades, IAccolades,
@ -99,7 +99,9 @@ import {
ILotusCustomization, ILotusCustomization,
IEndlessXpReward, IEndlessXpReward,
IPersonalGoalProgressDatabase, IPersonalGoalProgressDatabase,
IPersonalGoalProgressClient IPersonalGoalProgressClient,
IKubrowPetPrintClient,
IKubrowPetPrintDatabase
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -1008,6 +1010,27 @@ const traitsSchema = new Schema<ITraits>(
{ _id: false } { _id: false }
); );
const kubrowPetPrintSchema = new Schema<IKubrowPetPrintDatabase>({
ItemType: String,
Name: String,
IsMale: Boolean,
Size: Number,
DominantTraits: traitsSchema,
RecessiveTraits: traitsSchema
});
kubrowPetPrintSchema.set("toJSON", {
virtuals: true,
transform(_doc, obj) {
const db = obj as IKubrowPetPrintDatabase;
const client = obj as IKubrowPetPrintClient;
client.ItemId = toOid(db._id);
delete obj._id;
delete obj.__v;
}
});
const detailsSchema = new Schema<IKubrowPetDetailsDatabase>( const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
{ {
Name: String, Name: String,
@ -1394,10 +1417,10 @@ lastSortieRewardSchema.set("toJSON", {
} }
}); });
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>( const rewardAttenutationSchema = new Schema<IRewardAttenuation>(
{ {
Tag: String, Tag: { type: String, required: true },
Atten: Number Atten: { type: Number, required: true }
}, },
{ _id: false } { _id: false }
); );
@ -1511,7 +1534,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
KubrowPetEggs: [kubrowPetEggSchema], KubrowPetEggs: [kubrowPetEggSchema],
//Prints Cat(3 Prints)\Kubrow(2 Prints) Pets //Prints Cat(3 Prints)\Kubrow(2 Prints) Pets
//KubrowPetPrints: [Schema.Types.Mixed], KubrowPetPrints: [kubrowPetPrintSchema],
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc //Item for EquippedGear example:Scaner,LoadoutTechSummon etc
Consumables: [typeCountSchema], Consumables: [typeCountSchema],
@ -1625,6 +1648,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
PendingSpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined }, PendingSpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
SpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined }, SpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
//Darvo Deal
UsedDailyDeals: [String],
//New Quest Email //New Quest Email
EmailItems: [typeCountSchema], EmailItems: [typeCountSchema],
@ -1640,7 +1666,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
CompletedSorties: [String], CompletedSorties: [String],
LastSortieReward: { type: [lastSortieRewardSchema], default: undefined }, LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined }, LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined }, SortieRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined },
// Resource Extractor Drones // Resource Extractor Drones
Drones: [droneSchema], Drones: [droneSchema],
@ -1741,7 +1767,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//ChallengeInstanceStates: [Schema.Types.Mixed], //ChallengeInstanceStates: [Schema.Types.Mixed],
RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined }, RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
//Robotics: [Schema.Types.Mixed], //Robotics: [Schema.Types.Mixed],
//UsedDailyDeals: [Schema.Types.Mixed],
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined }, CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
HasResetAccount: { type: Boolean, default: false }, HasResetAccount: { type: Boolean, default: false },
@ -1780,7 +1805,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined }, HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined },
ClaimedJunctionChallengeRewards: { type: [String], default: undefined } ClaimedJunctionChallengeRewards: { type: [String], default: undefined },
SpecialItemRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined }
}, },
{ timestamps: { createdAt: "Created", updatedAt: false } } { timestamps: { createdAt: "Created", updatedAt: false } }
); );
@ -1850,6 +1877,7 @@ export type InventoryDocumentProps = {
CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>; CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>; PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
CrewMembers: Types.DocumentArray<ICrewMemberDatabase>; CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
KubrowPetPrints: Types.DocumentArray<IKubrowPetPrintDatabase>;
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> }; } & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
// eslint-disable-next-line @typescript-eslint/no-empty-object-type // eslint-disable-next-line @typescript-eslint/no-empty-object-type

View File

@ -1,4 +1,4 @@
import { IFissureDatabase } from "@/src/types/worldStateTypes"; import { IDailyDealDatabase, IFissureDatabase } from "@/src/types/worldStateTypes";
import { model, Schema } from "mongoose"; import { model, Schema } from "mongoose";
const fissureSchema = new Schema<IFissureDatabase>({ const fissureSchema = new Schema<IFissureDatabase>({
@ -12,3 +12,19 @@ const fissureSchema = new Schema<IFissureDatabase>({
fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries. fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema); export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema);
const dailyDealSchema = new Schema<IDailyDealDatabase>({
StoreItem: { type: String, required: true },
Activation: { type: Date, required: true },
Expiry: { type: Date, required: true },
Discount: { type: Number, required: true },
OriginalPrice: { type: Number, required: true },
SalePrice: { type: Number, required: true },
AmountTotal: { type: Number, required: true },
AmountSold: { type: Number, required: true }
});
dailyDealSchema.index({ StoreItem: 1 }, { unique: true });
dailyDealSchema.index({ Expiry: 1 }, { expireAfterSeconds: 86400 });
export const DailyDeal = model<IDailyDealDatabase>("DailyDeal", dailyDealSchema);

View File

@ -24,6 +24,7 @@ import { importController } from "@/src/controllers/custom/importController";
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController"; import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController"; import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
import { setBoosterController } from "@/src/controllers/custom/setBoosterController"; import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
import { updateFingerprintController } from "@/src/controllers/custom/updateFingerprintController";
import { getConfigController, setConfigController } from "@/src/controllers/custom/configController"; import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
@ -53,6 +54,7 @@ customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController); customRouter.post("/manageQuests", manageQuestsController);
customRouter.post("/setEvolutionProgress", setEvolutionProgressController); customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
customRouter.post("/setBooster", setBoosterController); customRouter.post("/setBooster", setBoosterController);
customRouter.post("/updateFingerprint", updateFingerprintController);
customRouter.post("/getConfig", getConfigController); customRouter.post("/getConfig", getConfigController);
customRouter.post("/setConfig", setConfigController); customRouter.post("/setConfig", setConfigController);

View File

@ -24,7 +24,7 @@ webuiRouter.use("/webui", (req, res, next) => {
webuiRouter.get("/webui/inventory", (_req, res) => { webuiRouter.get("/webui/inventory", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(baseDir, "static/webui/index.html"));
}); });
webuiRouter.get(/webui\/powersuit\/(.+)/, (_req, res) => { webuiRouter.get("/webui/detailedView", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(baseDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/mods", (_req, res) => { webuiRouter.get("/webui/mods", (_req, res) => {

View File

@ -65,6 +65,7 @@ export interface IConfig {
fastClanAscension?: boolean; fastClanAscension?: boolean;
missionsCanGiveAllRelics?: boolean; missionsCanGiveAllRelics?: boolean;
unlockAllSimarisResearchEntries?: boolean; unlockAllSimarisResearchEntries?: boolean;
disableDailyTribute?: boolean;
spoofMasteryRank?: number; spoofMasteryRank?: number;
nightwaveStandingMultiplier?: number; nightwaveStandingMultiplier?: number;
unfaithfulBugFixes?: { unfaithfulBugFixes?: {
@ -83,6 +84,7 @@ export interface IConfig {
nightwaveOverride?: string; nightwaveOverride?: string;
allTheFissures?: string; allTheFissures?: string;
circuitGameModes?: string[]; circuitGameModes?: string[];
darvoStockMultiplier?: number;
}; };
dev?: { dev?: {
keepVendorsExpired?: boolean; keepVendorsExpired?: boolean;

View File

@ -296,6 +296,12 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
db[key] = client[key]; db[key] = client[key];
} }
} }
// IRewardAtten[]
for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) {
if (client[key] !== undefined) {
db[key] = client[key];
}
}
if (client.XPInfo !== undefined) { if (client.XPInfo !== undefined) {
db.XPInfo = client.XPInfo; db.XPInfo = client.XPInfo;
} }

View File

@ -29,7 +29,8 @@ import {
ICalendarProgress, ICalendarProgress,
INemesisWeaponTargetFingerprint, INemesisWeaponTargetFingerprint,
INemesisPetTargetFingerprint, INemesisPetTargetFingerprint,
IDialogueDatabase IDialogueDatabase,
IKubrowPetPrintClient
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@ -43,6 +44,7 @@ import {
} from "../types/inventoryTypes/commonInventoryTypes"; } from "../types/inventoryTypes/commonInventoryTypes";
import { import {
ExportArcanes, ExportArcanes,
ExportBoosters,
ExportBundles, ExportBundles,
ExportChallenges, ExportChallenges,
ExportCustoms, ExportCustoms,
@ -424,7 +426,6 @@ export const addItem = async (
ItemType: "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg", ItemType: "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg",
_id: new Types.ObjectId() _id: new Types.ObjectId()
}; };
inventory.KubrowPetEggs ??= [];
inventory.KubrowPetEggs.push(egg); inventory.KubrowPetEggs.push(egg);
changes.push({ changes.push({
ItemType: egg.ItemType, ItemType: egg.ItemType,
@ -671,6 +672,17 @@ export const addItem = async (
return await addEmailItem(inventory, typeName); return await addEmailItem(inventory, typeName);
} }
// Boosters are an odd case. They're only added like this via Baro's Void Surplus afaik.
{
const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == typeName);
if (boosterEntry) {
addBooster(typeName, quantity, inventory);
return {
Boosters: [{ ItemType: typeName, ExpiryDate: quantity }]
};
}
}
// Path-based duck typing // Path-based duck typing
switch (typeName.substr(1).split("/")[1]) { switch (typeName.substr(1).split("/")[1]) {
case "Powersuits": case "Powersuits":
@ -784,7 +796,11 @@ export const addItem = async (
typeName.substr(1).split("/")[3] == "CatbrowPet" || typeName.substr(1).split("/")[3] == "CatbrowPet" ||
typeName.substr(1).split("/")[3] == "KubrowPet" typeName.substr(1).split("/")[3] == "KubrowPet"
) { ) {
if (typeName != "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") { if (
typeName != "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem" &&
typeName != "/Lotus/Types/Game/KubrowPet/BlankTraitPrint" &&
typeName != "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
) {
return addKubrowPet(inventory, typeName, undefined, premiumPurchase); return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
} }
} else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) { } else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
@ -1048,8 +1064,13 @@ export const addKubrowPet = (
const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades); const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
if (!details) { if (!details) {
let traits: ITraits; const isCatbrow = [
"/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit",
"/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
"/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit"
].includes(kubrowPetName);
let traits: ITraits;
if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") { if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
traits = { traits = {
BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire", BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
@ -1064,12 +1085,7 @@ export const addKubrowPet = (
Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire" Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
}; };
} else { } else {
const isCatbrow = [
"/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
"/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit"
].includes(kubrowPetName);
const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails; const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
traits = { traits = {
BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type, BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type, SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
@ -1088,7 +1104,7 @@ export const addKubrowPet = (
Name: "", Name: "",
IsPuppy: !premiumPurchase, IsPuppy: !premiumPurchase,
HasCollar: true, HasCollar: true,
PrintsRemaining: 3, PrintsRemaining: isCatbrow ? 3 : 2,
Status: premiumPurchase ? Status.StatusStasis : Status.StatusIncubating, Status: premiumPurchase ? Status.StatusStasis : Status.StatusIncubating,
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. 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), IsMale: !!getRandomInt(0, 1),
@ -1112,6 +1128,26 @@ export const addKubrowPet = (
return inventoryChanges; return inventoryChanges;
}; };
export const addKubrowPetPrint = (
inventory: TInventoryDatabaseDocument,
pet: IEquipmentDatabase,
inventoryChanges: IInventoryChanges
): void => {
inventoryChanges.KubrowPetPrints ??= [];
inventoryChanges.KubrowPetPrints.push(
inventory.KubrowPetPrints[
inventory.KubrowPetPrints.push({
ItemType: "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint",
Name: pet.Details!.Name,
IsMale: pet.Details!.IsMale,
Size: pet.Details!.Size,
DominantTraits: pet.Details!.DominantTraits,
RecessiveTraits: pet.Details!.RecessiveTraits
}) - 1
].toJSON<IKubrowPetPrintClient>()
);
};
export const updateSlots = ( export const updateSlots = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
slotName: SlotNames, slotName: SlotNames,
@ -1330,7 +1366,7 @@ export const addCustomization = (
customizationName: string, customizationName: string,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
if (!inventory.FlavourItems.find(x => x.ItemType == customizationName)) { if (!inventory.FlavourItems.some(x => x.ItemType == customizationName)) {
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1; const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.FlavourItems ??= []; inventoryChanges.FlavourItems ??= [];
@ -1346,7 +1382,7 @@ export const addSkin = (
typeName: string, typeName: string,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) { if (inventory.WeaponSkins.some(x => x.ItemType == typeName)) {
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`); logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
} else { } else {
const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1; const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;

View File

@ -525,7 +525,6 @@ export const addMissionInventoryUpdates = async (
} }
case "KubrowPetEggs": { case "KubrowPetEggs": {
for (const egg of value) { for (const egg of value) {
inventory.KubrowPetEggs ??= [];
inventory.KubrowPetEggs.push({ inventory.KubrowPetEggs.push({
ItemType: egg.ItemType, ItemType: egg.ItemType,
_id: new Types.ObjectId() _id: new Types.ObjectId()

View File

@ -8,7 +8,7 @@ import {
updateCurrency, updateCurrency,
updateSlots updateSlots
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { getRandomWeightedRewardUc } from "@/src/services/rngService"; import { getRandomReward, getRandomWeightedRewardUc } from "@/src/services/rngService";
import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService"; import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { import {
@ -16,7 +16,8 @@ import {
IPurchaseResponse, IPurchaseResponse,
SlotPurchase, SlotPurchase,
IInventoryChanges, IInventoryChanges,
PurchaseSource PurchaseSource,
IPurchaseParams
} from "@/src/types/purchaseTypes"; } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { getWorldState } from "./worldStateService"; import { getWorldState } from "./worldStateService";
@ -35,6 +36,8 @@ import {
import { config } from "./configService"; import { config } from "./configService";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
import { fromStoreItem, toStoreItem } from "./itemDataService"; import { fromStoreItem, toStoreItem } from "./itemDataService";
import { DailyDeal } from "../models/worldStateModel";
import { fromMongoDate, toMongoDate } from "../helpers/inventoryHelpers";
export const getStoreItemCategory = (storeItem: string): string => { export const getStoreItemCategory = (storeItem: string): string => {
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/"); const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@ -51,6 +54,58 @@ export const getStoreItemTypesCategory = (typesItem: string): string => {
return typeElements[1]; return typeElements[1];
}; };
const tallyVendorPurchase = (
inventory: TInventoryDatabaseDocument,
inventoryChanges: IInventoryChanges,
VendorType: string,
ItemId: string,
numPurchased: number,
Expiry: Date
): void => {
if (!config.noVendorPurchaseLimits) {
inventory.RecentVendorPurchases ??= [];
let vendorPurchases = inventory.RecentVendorPurchases.find(x => x.VendorType == VendorType);
if (!vendorPurchases) {
vendorPurchases =
inventory.RecentVendorPurchases[
inventory.RecentVendorPurchases.push({
VendorType: VendorType,
PurchaseHistory: []
}) - 1
];
}
let historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId);
if (historyEntry) {
if (Date.now() >= historyEntry.Expiry.getTime()) {
historyEntry.NumPurchased = numPurchased;
historyEntry.Expiry = Expiry;
} else {
historyEntry.NumPurchased += numPurchased;
}
} else {
historyEntry =
vendorPurchases.PurchaseHistory[
vendorPurchases.PurchaseHistory.push({
ItemId: ItemId,
NumPurchased: numPurchased,
Expiry: Expiry
}) - 1
];
}
inventoryChanges.NewVendorPurchase = {
VendorType: VendorType,
PurchaseHistory: [
{
ItemId: ItemId,
NumPurchased: historyEntry.NumPurchased,
Expiry: toMongoDate(Expiry)
}
]
};
inventoryChanges.RecentVendorPurchases = inventoryChanges.NewVendorPurchase;
}
};
export const handlePurchase = async ( export const handlePurchase = async (
purchaseRequest: IPurchaseRequest, purchaseRequest: IPurchaseRequest,
inventory: TInventoryDatabaseDocument inventory: TInventoryDatabaseDocument
@ -97,20 +152,7 @@ export const handlePurchase = async (
if (offer.LocTagRandSeed !== undefined) { if (offer.LocTagRandSeed !== undefined) {
seed = BigInt(offer.LocTagRandSeed); seed = BigInt(offer.LocTagRandSeed);
} }
if (!config.noVendorPurchaseLimits && ItemId) { if (ItemId) {
inventory.RecentVendorPurchases ??= [];
let vendorPurchases = inventory.RecentVendorPurchases.find(
x => x.VendorType == manifest!.VendorInfo.TypeName
);
if (!vendorPurchases) {
vendorPurchases =
inventory.RecentVendorPurchases[
inventory.RecentVendorPurchases.push({
VendorType: manifest.VendorInfo.TypeName,
PurchaseHistory: []
}) - 1
];
}
let expiry = parseInt(offer.Expiry.$date.$numberLong); let expiry = parseInt(offer.Expiry.$date.$numberLong);
if (purchaseRequest.PurchaseParams.IsWeekly) { if (purchaseRequest.PurchaseParams.IsWeekly) {
const EPOCH = 1734307200 * 1000; // Monday const EPOCH = 1734307200 * 1000; // Monday
@ -118,34 +160,14 @@ export const handlePurchase = async (
const weekStart = EPOCH + week * 604800000; const weekStart = EPOCH + week * 604800000;
expiry = weekStart + 604800000; expiry = weekStart + 604800000;
} }
const historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId); tallyVendorPurchase(
let numPurchased = purchaseRequest.PurchaseParams.Quantity; inventory,
if (historyEntry) { prePurchaseInventoryChanges,
if (Date.now() >= historyEntry.Expiry.getTime()) { manifest.VendorInfo.TypeName,
historyEntry.NumPurchased = numPurchased; ItemId,
historyEntry.Expiry = new Date(expiry); purchaseRequest.PurchaseParams.Quantity,
} else { new Date(expiry)
numPurchased += historyEntry.NumPurchased; );
historyEntry.NumPurchased += purchaseRequest.PurchaseParams.Quantity;
}
} else {
vendorPurchases.PurchaseHistory.push({
ItemId: ItemId,
NumPurchased: purchaseRequest.PurchaseParams.Quantity,
Expiry: new Date(expiry)
});
}
prePurchaseInventoryChanges.NewVendorPurchase = {
VendorType: manifest.VendorInfo.TypeName,
PurchaseHistory: [
{
ItemId: ItemId,
NumPurchased: numPurchased,
Expiry: { $date: { $numberLong: expiry.toString() } }
}
]
};
prePurchaseInventoryChanges.RecentVendorPurchases = prePurchaseInventoryChanges.NewVendorPurchase;
} }
purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier; purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
} else { } else {
@ -191,7 +213,7 @@ export const handlePurchase = async (
throw new Error(`vendor purchase should not have an expected price`); throw new Error(`vendor purchase should not have an expected price`);
} }
if (!config.dontSubtractPurchaseItemCost) { if (offer.PrimePrice && !config.dontSubtractPurchaseItemCost) {
const invItem: IMiscItem = { const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks", ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1 ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
@ -200,6 +222,17 @@ export const handlePurchase = async (
purchaseResponse.InventoryChanges.MiscItems ??= []; purchaseResponse.InventoryChanges.MiscItems ??= [];
purchaseResponse.InventoryChanges.MiscItems.push(invItem); purchaseResponse.InventoryChanges.MiscItems.push(invItem);
} }
if (offer.Limit) {
tallyVendorPurchase(
inventory,
purchaseResponse.InventoryChanges,
"VoidTrader",
offer.ItemType,
purchaseRequest.PurchaseParams.Quantity,
fromMongoDate(worldState.VoidTraders[0].Expiry)
);
}
} }
break; break;
} }
@ -240,6 +273,12 @@ export const handlePurchase = async (
} }
} }
break; break;
case PurchaseSource.DailyDeal:
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
throw new Error(`daily deal purchase should not have an expected price`);
}
await handleDailyDealPurchase(inventory, purchaseRequest.PurchaseParams, purchaseResponse);
break;
case PurchaseSource.Vendor: case PurchaseSource.Vendor:
if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) { if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) {
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!]; const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
@ -328,6 +367,25 @@ const handleItemPrices = (
} }
}; };
export const handleDailyDealPurchase = async (
inventory: TInventoryDatabaseDocument,
purchaseParams: IPurchaseParams,
purchaseResponse: IPurchaseResponse
): Promise<void> => {
const dailyDeal = (await DailyDeal.findOne({ StoreItem: purchaseParams.StoreItem }))!;
dailyDeal.AmountSold += 1;
await dailyDeal.save();
if (!config.dontSubtractPurchasePlatinumCost) {
updateCurrency(inventory, dailyDeal.SalePrice, true, purchaseResponse.InventoryChanges);
}
if (!config.noVendorPurchaseLimits) {
inventory.UsedDailyDeals.push(purchaseParams.StoreItem);
purchaseResponse.DailyDealUsed = purchaseParams.StoreItem;
}
};
export const handleBundleAcqusition = async ( export const handleBundleAcqusition = async (
storeItemName: string, storeItemName: string,
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
@ -482,12 +540,57 @@ const handleBoosterPackPurchase = async (
"attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server." "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
); );
} }
const specialItemReward = pack.components.find(x => x.PityIncreaseRate);
for (let i = 0; i != quantity; ++i) { for (let i = 0; i != quantity; ++i) {
const disallowedItems = new Set(); if (specialItemReward) {
for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) { {
const weights = pack.rarityWeightsPerRoll[roll]; const normalComponents = [];
const result = getRandomWeightedRewardUc(pack.components, weights); for (const comp of pack.components) {
if (result) { if (!comp.PityIncreaseRate) {
const { Probability, ...rest } = comp;
normalComponents.push({
...rest,
probability: Probability!
});
}
}
const result = getRandomReward(normalComponents)!;
logger.debug(`booster pack rolled`, result);
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges(
purchaseResponse.InventoryChanges,
await addItem(inventory, result.Item, result.Amount)
);
}
if (!inventory.WeaponSkins.some(x => x.ItemType == specialItemReward.Item)) {
inventory.SpecialItemRewardAttenuation ??= [];
let atten = inventory.SpecialItemRewardAttenuation.find(x => x.Tag == specialItemReward.Item);
if (!atten) {
atten =
inventory.SpecialItemRewardAttenuation[
inventory.SpecialItemRewardAttenuation.push({
Tag: specialItemReward.Item,
Atten: specialItemReward.Probability!
}) - 1
];
}
if (Math.random() < atten.Atten) {
purchaseResponse.BoosterPackItems += toStoreItem(specialItemReward.Item) + ',{"lvl":0};';
combineInventoryChanges(
purchaseResponse.InventoryChanges,
await addItem(inventory, specialItemReward.Item)
);
// TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
} else {
atten.Atten += specialItemReward.PityIncreaseRate!;
}
}
} else {
const disallowedItems = new Set();
for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
const weights = pack.rarityWeightsPerRoll[roll];
const result = getRandomWeightedRewardUc(pack.components, weights)!;
logger.debug(`booster pack rolled`, result); logger.debug(`booster pack rolled`, result);
if (disallowedItems.has(result.Item)) { if (disallowedItems.has(result.Item)) {
logger.debug(`oops, can't use that one; trying again`); logger.debug(`oops, can't use that one; trying again`);
@ -497,9 +600,12 @@ const handleBoosterPackPurchase = async (
disallowedItems.add(result.Item); disallowedItems.add(result.Item);
} }
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};'; purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1)); combineInventoryChanges(
purchaseResponse.InventoryChanges,
await addItem(inventory, result.Item, result.Amount)
);
++roll;
} }
++roll;
} }
} }
return purchaseResponse; return purchaseResponse;

View File

@ -4,6 +4,7 @@ import fissureMissions from "@/static/fixed_responses/worldState/fissureMissions
import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json"; import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json"; import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json"; import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
import { buildConfig } from "@/src/services/buildConfigService"; import { buildConfig } from "@/src/services/buildConfigService";
import { unixTimesInMs } from "@/src/constants/timeConstants"; import { unixTimesInMs } from "@/src/constants/timeConstants";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
@ -27,7 +28,7 @@ import {
} from "../types/worldStateTypes"; } from "../types/worldStateTypes";
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers"; import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { Fissure } from "../models/worldStateModel"; import { DailyDeal, Fissure } from "../models/worldStateModel";
const sortieBosses = [ const sortieBosses = [
"SORTIE_BOSS_HYENA", "SORTIE_BOSS_HYENA",
@ -1122,6 +1123,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
GlobalUpgrades: [], GlobalUpgrades: [],
VoidTraders: [], VoidTraders: [],
VoidStorms: [], VoidStorms: [],
DailyDeals: [],
EndlessXpChoices: [], EndlessXpChoices: [],
KnownCalendarSeasons: [], KnownCalendarSeasons: [],
...staticWorldState, ...staticWorldState,
@ -1561,6 +1563,24 @@ export const populateFissures = async (worldState: IWorldState): Promise<void> =
} }
}; };
export const populateDailyDeal = async (worldState: IWorldState): Promise<void> => {
const dailyDeals = await DailyDeal.find({});
for (const dailyDeal of dailyDeals) {
if (dailyDeal.Expiry.getTime() > Date.now()) {
worldState.DailyDeals.push({
StoreItem: dailyDeal.StoreItem,
Activation: toMongoDate(dailyDeal.Activation),
Expiry: toMongoDate(dailyDeal.Expiry),
Discount: dailyDeal.Discount,
OriginalPrice: dailyDeal.OriginalPrice,
SalePrice: dailyDeal.SalePrice,
AmountTotal: Math.round(dailyDeal.AmountTotal * (config.worldState?.darvoStockMultiplier ?? 1)),
AmountSold: dailyDeal.AmountSold
});
}
}
};
export const idToBountyCycle = (id: string): number => { export const idToBountyCycle = (id: string): number => {
return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000); return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000);
}; };
@ -1689,7 +1709,7 @@ const nightwaveTagToSeason: Record<string, number> = {
RadioLegionSyndicate: 0 // The Wolf of Saturn Six RadioLegionSyndicate: 0 // The Wolf of Saturn Six
}; };
export const updateWorldStateCollections = async (): Promise<void> => { const updateFissures = async (): Promise<void> => {
const fissures = await Fissure.find(); const fissures = await Fissure.find();
const activeNodes = new Set<string>(); const activeNodes = new Set<string>();
@ -1742,3 +1762,38 @@ export const updateWorldStateCollections = async (): Promise<void> => {
} }
} }
}; };
const updateDailyDeal = async (): Promise<void> => {
let darvoIndex = Math.trunc((Date.now() - 25200000) / (26 * unixTimesInMs.hour));
let darvoEnd;
do {
const darvoStart = darvoIndex * (26 * unixTimesInMs.hour) + 25200000;
darvoEnd = darvoStart + 26 * unixTimesInMs.hour;
const darvoOid = ((darvoStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "adc51a72f7324d95";
if (!(await DailyDeal.findById(darvoOid))) {
const seed = new SRng(darvoIndex).randomInt(0, 100_000);
const rng = new SRng(seed);
let deal;
do {
deal = rng.randomReward(darvoDeals)!; // Using an actual sampling collected over roughly a year because I can't extrapolate an algorithm from it with enough certainty.
//const [storeItem, meta] = rng.randomElement(Object.entries(darvoDeals))!;
//const discount = Math.min(rng.randomInt(1, 9) * 10, (meta as { MaxDiscount?: number }).MaxDiscount ?? 1);
} while (await DailyDeal.exists({ StoreItem: deal.StoreItem }));
await DailyDeal.insertOne({
_id: darvoOid,
StoreItem: deal.StoreItem,
Activation: new Date(darvoStart),
Expiry: new Date(darvoEnd),
Discount: deal.Discount,
OriginalPrice: deal.OriginalPrice,
SalePrice: deal.SalePrice, //Math.trunc(deal.OriginalPrice * (1 - discount))
AmountTotal: deal.AmountTotal,
AmountSold: 0
});
}
} while (darvoEnd < Date.now() + 6 * unixTimesInMs.minute && ++darvoIndex);
};
export const updateWorldStateCollections = async (): Promise<void> => {
await Promise.all([updateFissures(), updateDailyDeal()]);
};

View File

@ -40,6 +40,7 @@ export interface IInventoryDatabase
| "InfestedFoundry" | "InfestedFoundry"
| "DialogueHistory" | "DialogueHistory"
| "KubrowPetEggs" | "KubrowPetEggs"
| "KubrowPetPrints"
| "PendingCoupon" | "PendingCoupon"
| "Drones" | "Drones"
| "RecentVendorPurchases" | "RecentVendorPurchases"
@ -79,7 +80,8 @@ export interface IInventoryDatabase
KahlLoadOuts: IOperatorConfigDatabase[]; KahlLoadOuts: IOperatorConfigDatabase[];
InfestedFoundry?: IInfestedFoundryDatabase; InfestedFoundry?: IInfestedFoundryDatabase;
DialogueHistory?: IDialogueHistoryDatabase; DialogueHistory?: IDialogueHistoryDatabase;
KubrowPetEggs?: IKubrowPetEggDatabase[]; KubrowPetEggs: IKubrowPetEggDatabase[];
KubrowPetPrints: IKubrowPetPrintDatabase[];
PendingCoupon?: IPendingCouponDatabase; PendingCoupon?: IPendingCouponDatabase;
Drones: IDroneDatabase[]; Drones: IDroneDatabase[];
RecentVendorPurchases?: IRecentVendorPurchaseDatabase[]; RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
@ -287,6 +289,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
ArchwingEnabled?: boolean; ArchwingEnabled?: boolean;
PendingSpectreLoadouts?: ISpectreLoadout[]; PendingSpectreLoadouts?: ISpectreLoadout[];
SpectreLoadouts?: ISpectreLoadout[]; SpectreLoadouts?: ISpectreLoadout[];
UsedDailyDeals: string[];
EmailItems: ITypeCount[]; EmailItems: ITypeCount[];
CompletedSyndicates: string[]; CompletedSyndicates: string[];
FocusXP?: IFocusXP; FocusXP?: IFocusXP;
@ -295,7 +298,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
CompletedSorties: string[]; CompletedSorties: string[];
LastSortieReward?: ILastSortieRewardClient[]; LastSortieReward?: ILastSortieRewardClient[];
LastLiteSortieReward?: ILastSortieRewardClient[]; LastLiteSortieReward?: ILastSortieRewardClient[];
SortieRewardAttenuation?: ISortieRewardAttenuation[]; SortieRewardAttenuation?: IRewardAttenuation[];
Drones: IDroneClient[]; Drones: IDroneClient[];
StepSequencers: IStepSequencer[]; StepSequencers: IStepSequencer[];
ActiveAvatarImageType?: string; ActiveAvatarImageType?: string;
@ -306,7 +309,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
FocusUpgrades: IFocusUpgrade[]; FocusUpgrades: IFocusUpgrade[];
HasContributedToDojo?: boolean; HasContributedToDojo?: boolean;
HWIDProtectEnabled?: boolean; HWIDProtectEnabled?: boolean;
//KubrowPetPrints: IKubrowPetPrint[]; KubrowPetPrints: IKubrowPetPrintClient[];
AlignmentReplay?: IAlignment; AlignmentReplay?: IAlignment;
PersonalGoalProgress?: IPersonalGoalProgressClient[]; PersonalGoalProgress?: IPersonalGoalProgressClient[];
ThemeStyle: string; ThemeStyle: string;
@ -351,7 +354,6 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
//LeagueTickets: any[]; //LeagueTickets: any[];
//Quests: any[]; //Quests: any[];
//Robotics: any[]; //Robotics: any[];
//UsedDailyDeals: any[];
LibraryPersonalTarget?: string; LibraryPersonalTarget?: string;
LibraryPersonalProgress: ILibraryPersonalProgress[]; LibraryPersonalProgress: ILibraryPersonalProgress[];
CollectibleSeries?: ICollectibleEntry[]; CollectibleSeries?: ICollectibleEntry[];
@ -381,6 +383,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
HubNpcCustomizations?: IHubNpcCustomization[]; HubNpcCustomizations?: IHubNpcCustomization[];
Ship?: IOrbiter; // U22 and below, response only Ship?: IOrbiter; // U22 and below, response only
ClaimedJunctionChallengeRewards?: string[]; // U39 ClaimedJunctionChallengeRewards?: string[]; // U39
SpecialItemRewardAttenuation?: IRewardAttenuation[]; // Baro's Void Surplus
} }
export interface IAffiliation { export interface IAffiliation {
@ -722,8 +725,8 @@ export interface IKubrowPetEggDatabase {
_id: Types.ObjectId; _id: Types.ObjectId;
} }
export interface IKubrowPetPrint { export interface IKubrowPetPrintClient {
ItemType: KubrowPetPrintItemType; ItemType: "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint";
Name: string; Name: string;
IsMale: boolean; IsMale: boolean;
Size: number; // seems to be 0.7 to 1.0 Size: number; // seems to be 0.7 to 1.0
@ -733,6 +736,10 @@ export interface IKubrowPetPrint {
InheritedModularParts?: any[]; InheritedModularParts?: any[];
} }
export interface IKubrowPetPrintDatabase extends Omit<IKubrowPetPrintClient, "ItemId" | "InheritedModularParts"> {
_id: Types.ObjectId;
}
export interface ITraits { export interface ITraits {
BaseColor: string; BaseColor: string;
SecondaryColor: string; SecondaryColor: string;
@ -746,15 +753,11 @@ export interface ITraits {
Tail?: string; Tail?: string;
} }
export enum KubrowPetPrintItemType {
LotusTypesGameKubrowPetImprintedTraitPrint = "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
}
export interface IKubrowPetDetailsDatabase { export interface IKubrowPetDetailsDatabase {
Name?: string; Name?: string;
IsPuppy?: boolean; IsPuppy?: boolean;
HasCollar: boolean; HasCollar: boolean;
PrintsRemaining?: number; PrintsRemaining: number;
Status: Status; Status: Status;
HatchDate?: Date; HatchDate?: Date;
DominantTraits: ITraits; DominantTraits: ITraits;
@ -783,7 +786,7 @@ export interface ILastSortieRewardDatabase extends Omit<ILastSortieRewardClient,
SortieId: Types.ObjectId; SortieId: Types.ObjectId;
} }
export interface ISortieRewardAttenuation { export interface IRewardAttenuation {
Tag: string; Tag: string;
Atten: number; Atten: number;
} }

View File

@ -7,7 +7,8 @@ import {
ITypeCount, ITypeCount,
IRecentVendorPurchaseClient, IRecentVendorPurchaseClient,
TEquipmentKey, TEquipmentKey,
ICrewMemberClient ICrewMemberClient,
IKubrowPetPrintClient
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
export enum PurchaseSource { export enum PurchaseSource {
@ -78,6 +79,7 @@ export type IInventoryChanges = {
NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0 NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0 RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
CrewMembers?: ICrewMemberClient[]; CrewMembers?: ICrewMemberClient[];
KubrowPetPrints?: IKubrowPetPrintClient[];
} & Record< } & Record<
Exclude< Exclude<
string, string,
@ -105,6 +107,7 @@ export interface IPurchaseResponse {
Standing?: IAffiliationMods[]; Standing?: IAffiliationMods[];
FreeFavorsUsed?: IAffiliationMods[]; FreeFavorsUsed?: IAffiliationMods[];
BoosterPackItems?: string; BoosterPackItems?: string;
DailyDealUsed?: string;
} }
export type IBinChanges = { export type IBinChanges = {

View File

@ -15,6 +15,7 @@ export interface IWorldState {
NodeOverrides: INodeOverride[]; NodeOverrides: INodeOverride[];
VoidTraders: IVoidTrader[]; VoidTraders: IVoidTrader[];
VoidStorms: IVoidStorm[]; VoidStorms: IVoidStorm[];
DailyDeals: IDailyDeal[];
PVPChallengeInstances: IPVPChallengeInstance[]; PVPChallengeInstances: IPVPChallengeInstance[];
EndlessXpChoices: IEndlessXpChoice[]; EndlessXpChoices: IEndlessXpChoice[];
SeasonInfo?: { SeasonInfo?: {
@ -159,6 +160,7 @@ export interface IVoidTraderOffer {
ItemType: string; ItemType: string;
PrimePrice: number; PrimePrice: number;
RegularPrice: number; RegularPrice: number;
Limit?: number;
} }
export interface IVoidStorm { export interface IVoidStorm {
@ -169,6 +171,28 @@ export interface IVoidStorm {
ActiveMissionTier: string; ActiveMissionTier: string;
} }
export interface IDailyDeal {
StoreItem: string;
Activation: IMongoDate;
Expiry: IMongoDate;
Discount: number;
OriginalPrice: number;
SalePrice: number;
AmountTotal: number;
AmountSold: number;
}
export interface IDailyDealDatabase {
StoreItem: string;
Activation: Date;
Expiry: Date;
Discount: number;
OriginalPrice: number;
SalePrice: number;
AmountTotal: number;
AmountSold: number;
}
export interface IPVPChallengeInstance { export interface IPVPChallengeInstance {
_id: IOid; _id: IOid;
challengeTypeRefID: string; challengeTypeRefID: string;

View File

@ -1,7 +1,8 @@
{ {
"evergreen": [ "evergreen": [
{ "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 }, { "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 } { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
{ "ItemType": "/Lotus/StoreItems/Types/BoosterPacks/BaroTreasureBox", "PrimePrice": 0, "RegularPrice": 50000, "Limit": 1 }
], ],
"armorSets": [ "armorSets": [
[ [

View File

@ -0,0 +1,158 @@
[
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Archwing/DemolitionJetPack/DemolitionJetPack", "Discount": 60, "OriginalPrice": 275, "SalePrice": 110, "AmountTotal": 300, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Bard/Bard", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Ember/Ember", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Ember/Ember", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 20, "OriginalPrice": 200, "SalePrice": 160, "AmountTotal": 200, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 30, "OriginalPrice": 200, "SalePrice": 140, "AmountTotal": 200, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 40, "OriginalPrice": 200, "SalePrice": 120, "AmountTotal": 200, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 50, "OriginalPrice": 200, "SalePrice": 100, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Sandman/Sandman", "Discount": 20, "OriginalPrice": 225, "SalePrice": 180, "AmountTotal": 100, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Sandman/Sandman", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Trapper/Trapper", "Discount": 40, "OriginalPrice": 300, "SalePrice": 180, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Powersuits/Trapper/Trapper", "Discount": 50, "OriginalPrice": 300, "SalePrice": 150, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Types/Game/CatbrowPet/CatbrowGeneticSignature", "Discount": 20, "OriginalPrice": 5, "SalePrice": 4, "AmountTotal": 500, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Game/CatbrowPet/CatbrowGeneticSignature", "Discount": 30, "OriginalPrice": 5, "SalePrice": 3, "AmountTotal": 415, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Game/KubrowPet/Eggs/KubrowEgg", "Discount": 50, "OriginalPrice": 10, "SalePrice": 5, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Game/KubrowPet/Eggs/KubrowEgg", "Discount": 60, "OriginalPrice": 10, "SalePrice": 4, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma", "Discount": 30, "OriginalPrice": 20, "SalePrice": 14, "AmountTotal": 150, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma", "Discount": 45, "OriginalPrice": 20, "SalePrice": 11, "AmountTotal": 150, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/OrokinReactor", "Discount": 40, "OriginalPrice": 20, "SalePrice": 12, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 40, "OriginalPrice": 10, "SalePrice": 6, "AmountTotal": 100, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 40, "OriginalPrice": 10, "SalePrice": 6, "AmountTotal": 100, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 20, "OriginalPrice": 40, "SalePrice": 32, "AmountTotal": 125, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/PowerLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 20, "OriginalPrice": 40, "SalePrice": 32, "AmountTotal": 125, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 30, "OriginalPrice": 40, "SalePrice": 28, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/WardLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 20, "OriginalPrice": 235, "SalePrice": 188, "AmountTotal": 300, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 30, "OriginalPrice": 235, "SalePrice": 164, "AmountTotal": 250, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 50, "OriginalPrice": 235, "SalePrice": 117, "AmountTotal": 150, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 60, "OriginalPrice": 235, "SalePrice": 94, "AmountTotal": 100, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 100, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 30, "OriginalPrice": 125, "SalePrice": 87, "AmountTotal": 90, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 60, "OriginalPrice": 125, "SalePrice": 50, "AmountTotal": 65, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 90, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 50, "OriginalPrice": 175, "SalePrice": 87, "AmountTotal": 80, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 60, "OriginalPrice": 175, "SalePrice": 70, "AmountTotal": 70, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 70, "OriginalPrice": 175, "SalePrice": 52, "AmountTotal": 60, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 10, "OriginalPrice": 75, "SalePrice": 67, "AmountTotal": 100, "probability": 6 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 20, "OriginalPrice": 75, "SalePrice": 60, "AmountTotal": 100, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 30, "OriginalPrice": 75, "SalePrice": 52, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 500, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 500, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 500, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 20, "OriginalPrice": 150, "SalePrice": 120, "AmountTotal": 300, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 250, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 150, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 200, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 175, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 150, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 125, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 70, "OriginalPrice": 225, "SalePrice": 67, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 10, "OriginalPrice": 150, "SalePrice": 135, "AmountTotal": 300, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 20, "OriginalPrice": 150, "SalePrice": 120, "AmountTotal": 270, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 240, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 40, "OriginalPrice": 150, "SalePrice": 90, "AmountTotal": 205, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 60, "OriginalPrice": 150, "SalePrice": 60, "AmountTotal": 145, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 80, "OriginalPrice": 150, "SalePrice": 30, "AmountTotal": 80, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 20, "OriginalPrice": 225, "SalePrice": 180, "AmountTotal": 200, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 165, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 150, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 30, "OriginalPrice": 75, "SalePrice": 52, "AmountTotal": 350, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 40, "OriginalPrice": 75, "SalePrice": 45, "AmountTotal": 300, "probability": 7 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 50, "OriginalPrice": 75, "SalePrice": 37, "AmountTotal": 250, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 60, "OriginalPrice": 75, "SalePrice": 30, "AmountTotal": 200, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/DualShortSword/DualHeatSwords", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/DualShortSword/DualHeatSwords", "Discount": 70, "OriginalPrice": 175, "SalePrice": 52, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Fist/Fist", "Discount": 10, "OriginalPrice": 125, "SalePrice": 112, "AmountTotal": 500, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Fist/Fist", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 250, "probability": 6 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 100, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 30, "OriginalPrice": 125, "SalePrice": 87, "AmountTotal": 125, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 40, "OriginalPrice": 125, "SalePrice": 75, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 300, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 40, "OriginalPrice": 150, "SalePrice": 90, "AmountTotal": 250, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 200, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 20, "OriginalPrice": 165, "SalePrice": 132, "AmountTotal": 300, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 30, "OriginalPrice": 165, "SalePrice": 115, "AmountTotal": 250, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 40, "OriginalPrice": 165, "SalePrice": 99, "AmountTotal": 200, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 50, "OriginalPrice": 165, "SalePrice": 82, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 60, "OriginalPrice": 165, "SalePrice": 66, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 300, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 60, "OriginalPrice": 150, "SalePrice": 60, "AmountTotal": 265, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 70, "OriginalPrice": 150, "SalePrice": 45, "AmountTotal": 225, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 90, "OriginalPrice": 150, "SalePrice": 15, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Scythe/EtherScytheWeapon", "Discount": 40, "OriginalPrice": 230, "SalePrice": 138, "AmountTotal": 250, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Scythe/EtherScytheWeapon", "Discount": 60, "OriginalPrice": 230, "SalePrice": 92, "AmountTotal": 150, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 20, "OriginalPrice": 175, "SalePrice": 140, "AmountTotal": 100, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 100, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 90, "OriginalPrice": 175, "SalePrice": 17, "AmountTotal": 100, "probability": 1 },
{
"StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
"Discount": 30,
"OriginalPrice": 150,
"SalePrice": 105,
"AmountTotal": 100,
"probability": 1
},
{
"StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
"Discount": 70,
"OriginalPrice": 150,
"SalePrice": 45,
"AmountTotal": 100,
"probability": 1
},
{
"StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
"Discount": 90,
"OriginalPrice": 150,
"SalePrice": 15,
"AmountTotal": 100,
"probability": 1
},
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 300, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 250, "probability": 6 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 50, "OriginalPrice": 175, "SalePrice": 87, "AmountTotal": 200, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 60, "OriginalPrice": 175, "SalePrice": 70, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 300, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 100, "probability": 5 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 200, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 150, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 50, "OriginalPrice": 190, "SalePrice": 95, "AmountTotal": 50, "probability": 4 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 300, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 250, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 200, "probability": 2 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 50, "OriginalPrice": 190, "SalePrice": 95, "AmountTotal": 150, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 60, "OriginalPrice": 190, "SalePrice": 76, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 10, "OriginalPrice": 250, "SalePrice": 225, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 30, "OriginalPrice": 250, "SalePrice": 175, "AmountTotal": 100, "probability": 3 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 50, "OriginalPrice": 250, "SalePrice": 125, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Shotgun/QuadShotgun", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Shotgun/QuadShotgun", "Discount": 70, "OriginalPrice": 225, "SalePrice": 67, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 10, "OriginalPrice": 175, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 20, "OriginalPrice": 175, "SalePrice": 140, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 1 },
{ "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 100, "probability": 2 }
]

View File

@ -510,18 +510,6 @@
"PrimeAccessAvailability": { "State": "PRIME1" }, "PrimeAccessAvailability": { "State": "PRIME1" },
"PrimeVaultAvailabilities": [false, false, false, false, false], "PrimeVaultAvailabilities": [false, false, false, false, false],
"PrimeTokenAvailability": true, "PrimeTokenAvailability": true,
"DailyDeals": [
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Focus/PowerLensGreater",
"Activation": { "$date": { "$numberLong": "1715058000000" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Discount": 50,
"OriginalPrice": 40,
"SalePrice": 20,
"AmountTotal": 50,
"AmountSold": 0
}
],
"LibraryInfo": { "LastCompletedTargetType": "/Lotus/Types/Game/Library/Targets/Research7Target" }, "LibraryInfo": { "LastCompletedTargetType": "/Lotus/Types/Game/Library/Targets/Research7Target" },
"PVPChallengeInstances": [ "PVPChallengeInstances": [
{ {

View File

@ -455,15 +455,15 @@
</div> </div>
</div> </div>
</div> </div>
<div id="powersuit-route" data-route="~ /webui/powersuit/(.+)" data-title="Inventory | OpenWF WebUI"> <div id="detailedView-route" data-route="/webui/detailedView" data-title="Inventory | OpenWF WebUI">
<h3 class="mb-0"></h3> <h3 class="mb-0"></h3>
<p class="text-body-secondary"></p> <p class="text-body-secondary"></p>
<div class="card"> <div id="archonShards-card" class="card mb-3" style="display: none;">
<h5 class="card-header" data-loc="powersuit_archonShardsLabel"></h5> <h5 class="card-header" data-loc="detailedView_archonShardsLabel"></h5>
<div class="card-body"> <div class="card-body">
<p> <p>
<span data-loc="powersuit_archonShardsDescription"></span> <span data-loc="detailedView_archonShardsDescription"></span>
<span data-loc="powersuit_archonShardsDescription2"></span> <span data-loc="detailedView_archonShardsDescription2"></span>
</p> </p>
<form class="input-group mb-3" onsubmit="doPushArchonCrystalUpgrade();return false;"> <form class="input-group mb-3" onsubmit="doPushArchonCrystalUpgrade();return false;">
<input type="number" id="archon-crystal-add-count" min="1" max="10000" value="1" class="form-control" style="max-width:100px" /> <input type="number" id="archon-crystal-add-count" min="1" max="10000" value="1" class="form-control" style="max-width:100px" />
@ -476,6 +476,18 @@
</table> </table>
</div> </div>
</div> </div>
<div id="valenceBonus-card" class="card mb-3" style="display: none;">
<h5 class="card-header" data-loc="detailedView_valenceBonusLabel"></h5>
<div class="card-body">
<p data-loc="detailedView_valenceBonusDescription"></p>
<form class="input-group mb-3" onsubmit="handleValenceBonusChange(event)">
<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>
</form>
</div>
</div>
</div> </div>
<div data-route="/webui/mods" data-title="Mods | OpenWF WebUI"> <div data-route="/webui/mods" data-title="Mods | OpenWF WebUI">
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p> <p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
@ -768,6 +780,10 @@
<input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" /> <input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" />
<label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label> <label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="disableDailyTribute" />
<label class="form-check-label" for="disableDailyTribute" data-loc="cheats_disableDailyTribute"></label>
</div>
<form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;"> <form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label> <label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<div class="input-group"> <div class="input-group">
@ -899,6 +915,13 @@
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button> <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div> </div>
</form> </form>
<form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;">
<label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_darvoStockMultiplier"></label>
<div class="input-group">
<input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" placeholder="1" />
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>

View File

@ -230,6 +230,8 @@ function setLanguage(lang) {
} }
} }
const urlParams = new URLSearchParams(window.location.search);
const webUiModularWeapons = [ const webUiModularWeapons = [
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon", "/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon", "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon",
@ -272,6 +274,8 @@ function fetchItemList() {
document.getElementById("changeSyndicate").innerHTML = ""; document.getElementById("changeSyndicate").innerHTML = "";
document.getElementById("changeSyndicate").appendChild(syndicateNone); document.getElementById("changeSyndicate").appendChild(syndicateNone);
document.getElementById("valenceBonus-innateDamage").innerHTML = "";
// prettier-ignore // prettier-ignore
data.archonCrystalUpgrades = { data.archonCrystalUpgrades = {
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeEquilibrium": loc("upgrade_Equilibrium").split("|VAL|").join("20"), "/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeEquilibrium": loc("upgrade_Equilibrium").split("|VAL|").join("20"),
@ -354,6 +358,16 @@ function fetchItemList() {
}; };
window.archonCrystalUpgrades = data.archonCrystalUpgrades; window.archonCrystalUpgrades = data.archonCrystalUpgrades;
data.innateDamages = {
InnateElectricityDamage: loc("damageType_Electricity"),
InnateFreezeDamage: loc("damageType_Freeze"),
InnateHeatDamage: loc("damageType_Fire"),
InnateImpactDamage: loc("damageType_Impact"),
InnateMagDamage: loc("damageType_Magnetic"),
InnateRadDamage: loc("damageType_Radiation"),
InnateToxinDamage: loc("damageType_Poison")
};
// Add mods mising in data sources // Add mods mising in data sources
data.mods.push({ data.mods.push({
uniqueName: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser", uniqueName: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser",
@ -438,6 +452,13 @@ function fetchItemList() {
option.value = name; option.value = name;
document.getElementById("datalist-" + type).appendChild(option); document.getElementById("datalist-" + type).appendChild(option);
}); });
} else if (type == "innateDamages") {
Object.entries(items).forEach(([uniqueName, name]) => {
const option = document.createElement("option");
option.value = uniqueName;
option.textContent = name;
document.getElementById("valenceBonus-innateDamage").appendChild(option);
});
} else if (type == "uniqueLevelCaps") { } else if (type == "uniqueLevelCaps") {
uniqueLevelCaps = items; uniqueLevelCaps = items;
} else if (type == "Syndicates") { } else if (type == "Syndicates") {
@ -647,6 +668,12 @@ function updateInventory() {
} }
} }
if (["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
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>`;
td.appendChild(a);
}
if (item.XP < maxXP || anyExaltedMissingXP) { if (item.XP < maxXP || anyExaltedMissingXP) {
const a = document.createElement("a"); const a = document.createElement("a");
a.href = "#"; a.href = "#";
@ -709,13 +736,6 @@ function updateInventory() {
a.title = loc("code_unmature"); a.title = loc("code_unmature");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 64A64 64 0 1 0 128 64a64 64 0 1 0 128 0zM152.9 169.3c-23.7-8.4-44.5-24.3-58.8-45.8L74.6 94.2C64.8 79.5 45 75.6 30.2 85.4s-18.7 29.7-8.9 44.4L40.9 159c18.1 27.1 42.8 48.4 71.1 62.4L112 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96 32 0 0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-258.4c29.1-14.2 54.4-36.2 72.7-64.2l18.2-27.9c9.6-14.8 5.4-34.6-9.4-44.3s-34.6-5.5-44.3 9.4L291 122.4c-21.8 33.4-58.9 53.6-98.8 53.6c-12.6 0-24.9-2-36.6-5.8c-.9-.3-1.8-.7-2.7-.9z"/></svg>`; a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 64A64 64 0 1 0 128 64a64 64 0 1 0 128 0zM152.9 169.3c-23.7-8.4-44.5-24.3-58.8-45.8L74.6 94.2C64.8 79.5 45 75.6 30.2 85.4s-18.7 29.7-8.9 44.4L40.9 159c18.1 27.1 42.8 48.4 71.1 62.4L112 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96 32 0 0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-258.4c29.1-14.2 54.4-36.2 72.7-64.2l18.2-27.9c9.6-14.8 5.4-34.6-9.4-44.3s-34.6-5.5-44.3 9.4L291 122.4c-21.8 33.4-58.9 53.6-98.8 53.6c-12.6 0-24.9-2-36.6-5.8c-.9-.3-1.8-.7-2.7-.9z"/></svg>`;
} }
td.appendChild(a);
}
if (category == "Suits") {
const a = document.createElement("a");
a.href = "/webui/powersuit/" + 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>`;
td.appendChild(a); td.appendChild(a);
} }
{ {
@ -1114,53 +1134,72 @@ function updateInventory() {
} }
}); });
// Populate powersuit route // Populate detailedView route
if (single.getCurrentPath().substr(0, 17) == "/webui/powersuit/") { if (single.getCurrentPath().substr(0, 19) == "/webui/detailedView") {
const oid = single.getCurrentPath().substr(17); const oid = urlParams.get("itemId");
const item = data.Suits.find(x => x.ItemId.$oid == oid); const category = urlParams.get("productCategory");
const item = data[category].find(x => x.ItemId.$oid == oid);
if (item) { if (item) {
if (item.ItemName) { if (item.ItemName) {
$("#powersuit-route h3").text(item.ItemName); $("#detailedView-route h3").text(item.ItemName);
$("#powersuit-route .text-body-secondary").text(itemMap[item.ItemType]?.name ?? item.ItemType); $("#detailedView-route .text-body-secondary").text(
itemMap[item.ItemType]?.name ?? item.ItemType
);
} else { } else {
$("#powersuit-route h3").text(itemMap[item.ItemType]?.name ?? item.ItemType); $("#detailedView-route h3").text(itemMap[item.ItemType]?.name ?? item.ItemType);
$("#powersuit-route .text-body-secondary").text(""); $("#detailedView-route .text-body-secondary").text("");
} }
const uniqueUpgrades = {}; if (category == "Suits") {
(item.ArchonCrystalUpgrades ?? []).forEach(upgrade => { document.getElementById("archonShards-card").style.display = "";
if (upgrade && upgrade.UpgradeType) {
uniqueUpgrades[upgrade.UpgradeType] ??= 0;
uniqueUpgrades[upgrade.UpgradeType] += 1;
}
});
document.getElementById("crystals-list").innerHTML = ""; const uniqueUpgrades = {};
Object.entries(uniqueUpgrades).forEach(([upgradeType, count]) => { (item.ArchonCrystalUpgrades ?? []).forEach(upgrade => {
const tr = document.createElement("tr"); if (upgrade && upgrade.UpgradeType) {
{ uniqueUpgrades[upgrade.UpgradeType] ??= 0;
const td = document.createElement("td"); uniqueUpgrades[upgrade.UpgradeType] += 1;
td.textContent = count + "x " + (archonCrystalUpgrades[upgradeType] ?? upgradeType);
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
{
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
doPopArchonCrystalUpgrade(upgradeType);
};
a.title = loc("code_remove");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
td.appendChild(a);
} }
tr.appendChild(td); });
document.getElementById("crystals-list").innerHTML = "";
Object.entries(uniqueUpgrades).forEach(([upgradeType, count]) => {
const tr = document.createElement("tr");
{
const td = document.createElement("td");
td.textContent = count + "x " + (archonCrystalUpgrades[upgradeType] ?? upgradeType);
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
{
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
doPopArchonCrystalUpgrade(upgradeType);
};
a.title = loc("code_remove");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
td.appendChild(a);
}
tr.appendChild(td);
}
document.getElementById("crystals-list").appendChild(tr);
});
} else if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
document.getElementById("valenceBonus-card").style.display = "";
document.getElementById("valenceBonus-innateDamage").value = "";
document.getElementById("valenceBonus-procent").value = 25;
if (item.UpgradeFingerprint) {
const buff = JSON.parse(item.UpgradeFingerprint).buffs[0];
const buffValue = fromUpdradeFingerPrintVaule(buff.Value, 0.25);
document.getElementById("valenceBonus-innateDamage").value = buff.Tag ?? "";
document.getElementById("valenceBonus-procent").value = Math.round(buffValue * 1000) / 10;
} }
document.getElementById("crystals-list").appendChild(tr); }
});
} else { } else {
single.loadRoute("/webui/inventory"); single.loadRoute("/webui/inventory");
} }
@ -1892,6 +1931,16 @@ function doSaveConfigInt(id) {
}); });
} }
function doSaveConfigFloat(id) {
$.post({
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
contentType: "application/json",
data: JSON.stringify({
[id]: parseFloat(document.getElementById(id).value)
})
});
}
function doSaveConfigStringArray(id) { function doSaveConfigStringArray(id) {
$.post({ $.post({
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
@ -1928,8 +1977,6 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
if (x != null) { if (x != null) {
if (x.type == "checkbox") { if (x.type == "checkbox") {
x.checked = value; x.checked = value;
} else if (x.type == "number") {
x.setAttribute("value", value);
} else if (x.classList.contains("tags-input")) { } else if (x.classList.contains("tags-input")) {
x.value = value.join(", "); x.value = value.join(", ");
x.oninput(); x.oninput();
@ -2090,10 +2137,12 @@ function doAddMissingMaxRankMods() {
}); });
} }
// Powersuit Route // DetailedView Route
single.getRoute("#powersuit-route").on("beforeload", function () { single.getRoute("#detailedView-route").on("beforeload", function () {
this.element.querySelector("h3").textContent = "Loading..."; this.element.querySelector("h3").textContent = "Loading...";
document.getElementById("archonShards-card").style.display = "none";
document.getElementById("valenceBonus-card").style.display = "none";
if (window.didInitialInventoryUpdate) { if (window.didInitialInventoryUpdate) {
updateInventory(); updateInventory();
} }
@ -2110,7 +2159,7 @@ function doPushArchonCrystalUpgrade() {
"/custom/pushArchonCrystalUpgrade?" + "/custom/pushArchonCrystalUpgrade?" +
window.authz + window.authz +
"&oid=" + "&oid=" +
single.getCurrentPath().substr(17) + urlParams.get("itemId") +
"&type=" + "&type=" +
uniqueName + uniqueName +
"&count=" + "&count=" +
@ -2125,12 +2174,7 @@ function doPushArchonCrystalUpgrade() {
function doPopArchonCrystalUpgrade(type) { function doPopArchonCrystalUpgrade(type) {
revalidateAuthz().then(() => { revalidateAuthz().then(() => {
$.get( $.get(
"/custom/popArchonCrystalUpgrade?" + "/custom/popArchonCrystalUpgrade?" + window.authz + "&oid=" + urlParams.get("itemId") + "&type=" + type
window.authz +
"&oid=" +
single.getCurrentPath().substr(17) +
"&type=" +
type
).done(function () { ).done(function () {
updateInventory(); updateInventory();
}); });
@ -2641,3 +2685,42 @@ document.querySelectorAll(".tags-input").forEach(input => {
}; };
input.oninput(); input.oninput();
}); });
function fromUpdradeFingerPrintVaule(raw, min) {
const range = 0.6 - min;
return min + (raw * range) / 0x3fffffff;
}
function toUpdradeFingerPrintVaule(value, min) {
const range = 0.6 - min;
return Math.trunc(((value - min) * 0x3fffffff) / range);
}
function handleValenceBonusChange(event) {
event.preventDefault();
const action = event.submitter.value;
const Tag = document.getElementById("valenceBonus-innateDamage").value;
const Value = toUpdradeFingerPrintVaule(document.getElementById("valenceBonus-procent").value / 100, 0.25);
revalidateAuthz().then(() => {
$.post({
url: "/custom/updateFingerprint?" + window.authz,
contentType: "application/json",
data: JSON.stringify({
category: urlParams.get("productCategory"),
oid: urlParams.get("itemId"),
action,
upgradeType: "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod",
upgradeFingerprint: {
buffs: [
{
Tag,
Value
}
]
}
})
}).done(function () {
updateInventory();
});
});
}

View File

@ -1,8 +1,11 @@
// German translation by Animan8000 // German translation by Animan8000
dict = { dict = {
general_inventoryUpdateNote: `Hinweis: Änderungen, die hier vorgenommen werden, werden erst im Spiel angewendet, sobald das Inventar synchronisiert wird. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`, general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `Hinzufügen`, general_addButton: `Hinzufügen`,
general_setButton: `[UNTRANSLATED] Set`,
general_removeButton: `[UNTRANSLATED] Remove`,
general_bulkActions: `Massenaktionen`, general_bulkActions: `Massenaktionen`,
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`, code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`, code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
code_changeNameConfirm: `In welchen Namen möchtest du deinen Account umbenennen?`, code_changeNameConfirm: `In welchen Namen möchtest du deinen Account umbenennen?`,
@ -112,9 +115,13 @@ dict = {
currency_FusionPoints: `Endo`, currency_FusionPoints: `Endo`,
currency_PrimeTokens: `Reines Aya`, currency_PrimeTokens: `Reines Aya`,
currency_owned: `Du hast |COUNT|.`, currency_owned: `Du hast |COUNT|.`,
powersuit_archonShardsLabel: `Archon-Scherben-Slots`,
powersuit_archonShardsDescription: `Du kannst diese unbegrenzten Slots nutzen, um eine Vielzahl von Verbesserungen anzuwenden.`, detailedView_archonShardsLabel: `Archon-Scherben-Slots`,
powersuit_archonShardsDescription2: `Hinweis: Jede Archon-Scherbe benötigt beim Laden etwas Zeit, um angewendet zu werden.`, detailedView_archonShardsDescription: `Du kannst diese unbegrenzten Slots nutzen, um eine Vielzahl von Verbesserungen anzuwenden.`,
detailedView_archonShardsDescription2: `Hinweis: Jede Archon-Scherbe benötigt beim Laden etwas Zeit, um angewendet zu werden.`,
detailedView_valenceBonusLabel: `Valenz-Bonus`,
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can add or remove the Valence Bonus from your weapon.`,
mods_addRiven: `Riven hinzufügen`, mods_addRiven: `Riven hinzufügen`,
mods_fingerprint: `Fingerabdruck`, mods_fingerprint: `Fingerabdruck`,
mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`, mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,
@ -175,6 +182,7 @@ dict = {
cheats_fastClanAscension: `Schneller Clan-Aufstieg`, cheats_fastClanAscension: `Schneller Clan-Aufstieg`,
cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`, cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`, cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`,
cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
cheats_save: `[UNTRANSLATED] Save`, cheats_save: `[UNTRANSLATED] Save`,
@ -231,6 +239,7 @@ dict = {
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
import_importNote: `Du kannst hier eine vollständige oder teilweise Inventarantwort (Client-Darstellung) einfügen. Alle Felder, die vom Importer unterstützt werden, <b>werden in deinem Account überschrieben</b>.`, import_importNote: `Du kannst hier eine vollständige oder teilweise Inventarantwort (Client-Darstellung) einfügen. Alle Felder, die vom Importer unterstützt werden, <b>werden in deinem Account überschrieben</b>.`,
import_submit: `Absenden`, import_submit: `Absenden`,
@ -255,7 +264,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`, upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`,
upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`, upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`,
upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`, upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`,
upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health per enemy killed with Blast Damage (Max |VAL2| Health)`, upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`, upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`,
upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`, upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`,
upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`, upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
@ -288,5 +297,13 @@ dict = {
upgrade_SwiftExecute: `[UNTRANSLATED] Speed of Mercy Kills increased by 50%`, upgrade_SwiftExecute: `[UNTRANSLATED] Speed of Mercy Kills increased by 50%`,
upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after hacking`, upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after hacking`,
damageType_Electricity: `Elektrizität`,
damageType_Fire: `Hitze`,
damageType_Freeze: `Kälte`,
damageType_Impact: `Einschlag`,
damageType_Magnetic: `Magnetismus`,
damageType_Poison: `Gift`,
damageType_Radiation: `Strahlung`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };

View File

@ -1,7 +1,10 @@
dict = { dict = {
general_inventoryUpdateNote: `Note: Changes made here will only be applied in-game when the game syncs the inventory. Visiting the navigation should be the easiest way to trigger that.`, general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `Add`, general_addButton: `Add`,
general_setButton: `Set`,
general_removeButton: `Remove`,
general_bulkActions: `Bulk Actions`, general_bulkActions: `Bulk Actions`,
code_loginFail: `Login failed. Double-check the email and password.`, code_loginFail: `Login failed. Double-check the email and password.`,
code_regFail: `Registration failed. Account already exists?`, code_regFail: `Registration failed. Account already exists?`,
code_changeNameConfirm: `What would you like to change your account name to?`, code_changeNameConfirm: `What would you like to change your account name to?`,
@ -111,9 +114,13 @@ dict = {
currency_FusionPoints: `Endo`, currency_FusionPoints: `Endo`,
currency_PrimeTokens: `Regal Aya`, currency_PrimeTokens: `Regal Aya`,
currency_owned: `You have |COUNT|.`, currency_owned: `You have |COUNT|.`,
powersuit_archonShardsLabel: `Archon Shard Slots`,
powersuit_archonShardsDescription: `You can use these unlimited slots to apply a wide range of upgrades.`, detailedView_archonShardsLabel: `Archon Shard Slots`,
powersuit_archonShardsDescription2: `Note that each archon shard takes some time to be applied when loading in.`, detailedView_archonShardsDescription: `You can use these unlimited slots to apply a wide range of upgrades.`,
detailedView_archonShardsDescription2: `Note that each archon shard takes some time to be applied when loading in.`,
detailedView_valenceBonusLabel: `Valence Bonus`,
detailedView_valenceBonusDescription: `You can add or remove the Valence Bonus from your weapon.`,
mods_addRiven: `Add Riven`, mods_addRiven: `Add Riven`,
mods_fingerprint: `Fingerprint`, mods_fingerprint: `Fingerprint`,
mods_fingerprintHelp: `Need help with the fingerprint?`, mods_fingerprintHelp: `Need help with the fingerprint?`,
@ -174,6 +181,7 @@ dict = {
cheats_fastClanAscension: `Fast Clan Ascension`, cheats_fastClanAscension: `Fast Clan Ascension`,
cheats_missionsCanGiveAllRelics: `Missions Can Give All Relics`, cheats_missionsCanGiveAllRelics: `Missions Can Give All Relics`,
cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `Disable Daily Tribute`,
cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`, cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`,
cheats_save: `Save`, cheats_save: `Save`,
@ -230,6 +238,7 @@ dict = {
worldState_allAtOnceNormal: `All At Once, Normal`, worldState_allAtOnceNormal: `All At Once, Normal`,
worldState_allAtOnceSteelPath: `All At Once, Steel Path`, worldState_allAtOnceSteelPath: `All At Once, Steel Path`,
worldState_theCircuitOverride: `The Circuit Override`, worldState_theCircuitOverride: `The Circuit Override`,
worldState_darvoStockMultiplier: `Darvo Stock Multiplier`,
import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`, import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`,
import_submit: `Submit`, import_submit: `Submit`,
@ -254,7 +263,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% Energy Orb Effectiveness`, upgrade_WarframeGlobeEffectEnergy: `+|VAL|% Energy Orb Effectiveness`,
upgrade_WarframeGlobeEffectHealth: `+|VAL|% Health Orb Effectiveness`, upgrade_WarframeGlobeEffectHealth: `+|VAL|% Health Orb Effectiveness`,
upgrade_WarframeHealthMax: `+|VAL| Health`, upgrade_WarframeHealthMax: `+|VAL| Health`,
upgrade_WarframeHPBoostFromImpact: `+|VAL1| Health per enemy killed with Blast Damage (Max |VAL2| Health)`, upgrade_WarframeHPBoostFromImpact: `+|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `+|VAL|% Parkour Velocity`, upgrade_WarframeParkourVelocity: `+|VAL|% Parkour Velocity`,
upgrade_WarframeRadiationDamageBoost: `+|VAL|% Ability Damage on enemies affected by Radiation Status`, upgrade_WarframeRadiationDamageBoost: `+|VAL|% Ability Damage on enemies affected by Radiation Status`,
upgrade_WarframeRegen: `+|VAL| Health Regen/s`, upgrade_WarframeRegen: `+|VAL| Health Regen/s`,
@ -287,5 +296,13 @@ dict = {
upgrade_SwiftExecute: `Speed of Mercy Kills increased by 50%`, upgrade_SwiftExecute: `Speed of Mercy Kills increased by 50%`,
upgrade_OnHackInvis: `Invisible for 15 seconds after hacking`, upgrade_OnHackInvis: `Invisible for 15 seconds after hacking`,
damageType_Electricity: `Electricity`,
damageType_Fire: `Heat`,
damageType_Freeze: `Cold`,
damageType_Impact: `Impact`,
damageType_Magnetic: `Magnetic`,
damageType_Poison: `Toxin`,
damageType_Radiation: `Radiation`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };

View File

@ -1,8 +1,11 @@
// Spanish translation by hxedcl // Spanish translation by hxedcl
dict = { dict = {
general_inventoryUpdateNote: `Nota: Los cambios realizados aquí se reflejarán en el juego cuando este sincronice el inventario. Usar la navegación debería ser la forma más sencilla de activar esto.`, general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `Agregar`, general_addButton: `Agregar`,
general_setButton: `[UNTRANSLATED] Set`,
general_removeButton: `[UNTRANSLATED] Remove`,
general_bulkActions: `Acciones masivas`, general_bulkActions: `Acciones masivas`,
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`, code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`, code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`, code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`,
@ -112,9 +115,13 @@ dict = {
currency_FusionPoints: `Endo`, currency_FusionPoints: `Endo`,
currency_PrimeTokens: `Aya Real`, currency_PrimeTokens: `Aya Real`,
currency_owned: `Tienes |COUNT|.`, currency_owned: `Tienes |COUNT|.`,
powersuit_archonShardsLabel: `Ranuras de Fragmento de Archón`,
powersuit_archonShardsDescription: `Puedes usar estas ranuras ilimitadas para aplicar una amplia variedad de mejoras`, detailedView_archonShardsLabel: `Ranuras de Fragmento de Archón`,
powersuit_archonShardsDescription2: `Ten en cuenta que cada fragmento de archón tarda un poco en aplicarse al cargar`, detailedView_archonShardsDescription: `Puedes usar estas ranuras ilimitadas para aplicar una amplia variedad de mejoras`,
detailedView_archonShardsDescription2: `Ten en cuenta que cada fragmento de archón tarda un poco en aplicarse al cargar`,
detailedView_valenceBonusLabel: `Bônus de Valência`,
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can add or remove the Valence Bonus from your weapon.`,
mods_addRiven: `Agregar Agrietado`, mods_addRiven: `Agregar Agrietado`,
mods_fingerprint: `Huella digital`, mods_fingerprint: `Huella digital`,
mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`, mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,
@ -175,6 +182,7 @@ dict = {
cheats_fastClanAscension: `Ascenso rápido del clan`, cheats_fastClanAscension: `Ascenso rápido del clan`,
cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`, cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`,
cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`, cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`, cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`, cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
cheats_save: `Guardar`, cheats_save: `Guardar`,
@ -231,6 +239,7 @@ dict = {
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`, import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`,
import_submit: `Enviar`, import_submit: `Enviar`,
@ -255,7 +264,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% de efectividad de orbes de energía`, upgrade_WarframeGlobeEffectEnergy: `+|VAL|% de efectividad de orbes de energía`,
upgrade_WarframeGlobeEffectHealth: `+|VAL|% de efectividad de orbes de salud`, upgrade_WarframeGlobeEffectHealth: `+|VAL|% de efectividad de orbes de salud`,
upgrade_WarframeHealthMax: `+|VAL| de salud máxima`, upgrade_WarframeHealthMax: `+|VAL| de salud máxima`,
upgrade_WarframeHPBoostFromImpact: `+|VAL1| de salud por enemigo eliminado con daño explosivo (máximo |VAL2| de salud)`, upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `+|VAL|% de velocidad de parkour`, upgrade_WarframeParkourVelocity: `+|VAL|% de velocidad de parkour`,
upgrade_WarframeRadiationDamageBoost: `+|VAL|% de daño de habilidades a enemigos con estado radiactivo`, upgrade_WarframeRadiationDamageBoost: `+|VAL|% de daño de habilidades a enemigos con estado radiactivo`,
upgrade_WarframeRegen: `+|VAL| de regeneración de salud por segundo`, upgrade_WarframeRegen: `+|VAL| de regeneración de salud por segundo`,
@ -288,5 +297,13 @@ dict = {
upgrade_SwiftExecute: `Velocidad de ejecuciones aumentada en un 50%`, upgrade_SwiftExecute: `Velocidad de ejecuciones aumentada en un 50%`,
upgrade_OnHackInvis: `Invisible durante 15 segundos después de hackear`, upgrade_OnHackInvis: `Invisible durante 15 segundos después de hackear`,
damageType_Electricity: `Eletricidade`,
damageType_Fire: `Ígneo`,
damageType_Freeze: `Glacial`,
damageType_Impact: `Colisivo`,
damageType_Magnetic: `Magnético`,
damageType_Poison: `Tóxico`,
damageType_Radiation: `Radioativo`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };

View File

@ -1,8 +1,11 @@
// French translation by Vitruvio // French translation by Vitruvio
dict = { dict = {
general_inventoryUpdateNote: `Note : Les changements effectués ici seront appliqués lors de la syncrhonisation. Visiter la navigation appliquera les changements apportés à l'inventaire.`, general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `Ajouter`, general_addButton: `Ajouter`,
general_setButton: `[UNTRANSLATED] Set`,
general_removeButton: `[UNTRANSLATED] Remove`,
general_bulkActions: `Action groupée`, general_bulkActions: `Action groupée`,
code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`, code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`,
code_regFail: `Enregistrement impossible. Compte existant?`, code_regFail: `Enregistrement impossible. Compte existant?`,
code_changeNameConfirm: `Nouveau nom du compte :`, code_changeNameConfirm: `Nouveau nom du compte :`,
@ -112,9 +115,13 @@ dict = {
currency_FusionPoints: `Endo`, currency_FusionPoints: `Endo`,
currency_PrimeTokens: `Aya Raffiné`, currency_PrimeTokens: `Aya Raffiné`,
currency_owned: `|COUNT| possédés.`, currency_owned: `|COUNT| possédés.`,
powersuit_archonShardsLabel: `Emplacements de fragments d'Archonte`,
powersuit_archonShardsDescription: `Slots illimités pour appliquer plusieurs améliorations`, detailedView_archonShardsLabel: `Emplacements de fragments d'Archonte`,
powersuit_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`, detailedView_archonShardsDescription: `Slots illimités pour appliquer plusieurs améliorations`,
detailedView_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`,
detailedView_valenceBonusLabel: `Bonus de Valence`,
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can add or remove the Valence Bonus from your weapon.`,
mods_addRiven: `Ajouter un riven`, mods_addRiven: `Ajouter un riven`,
mods_fingerprint: `Empreinte`, mods_fingerprint: `Empreinte`,
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`, mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
@ -175,6 +182,7 @@ dict = {
cheats_fastClanAscension: `Ascension de clan rapide`, cheats_fastClanAscension: `Ascension de clan rapide`,
cheats_missionsCanGiveAllRelics: `Les missions donnent toutes les reliques`, cheats_missionsCanGiveAllRelics: `Les missions donnent toutes les reliques`,
cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`, cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`, cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`, cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`,
cheats_save: `Sauvegarder`, cheats_save: `Sauvegarder`,
@ -231,6 +239,7 @@ dict = {
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
import_importNote: `Import manuel. Toutes les modifcations supportées par l'inventaire <b>écraseront celles présentes dans la base de données</b>.`, import_importNote: `Import manuel. Toutes les modifcations supportées par l'inventaire <b>écraseront celles présentes dans la base de données</b>.`,
import_submit: `Soumettre`, import_submit: `Soumettre`,
@ -255,7 +264,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% d'efficacité d'orbe d'énergie`, upgrade_WarframeGlobeEffectEnergy: `+|VAL|% d'efficacité d'orbe d'énergie`,
upgrade_WarframeGlobeEffectHealth: `+|VAL|% d'efficacité d'orbe de santé`, upgrade_WarframeGlobeEffectHealth: `+|VAL|% d'efficacité d'orbe de santé`,
upgrade_WarframeHealthMax: `+|VAL| de santé`, upgrade_WarframeHealthMax: `+|VAL| de santé`,
upgrade_WarframeHPBoostFromImpact: `+|VAL1| de santé par ennemi tué avec des dégâts explosifs (Max |VAL2| de santé)`, upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `+|VAL|% de vélocité de parkour`, upgrade_WarframeParkourVelocity: `+|VAL|% de vélocité de parkour`,
upgrade_WarframeRadiationDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut radiation`, upgrade_WarframeRadiationDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut radiation`,
upgrade_WarframeRegen: `+|VAL| régénération de santé/s`, upgrade_WarframeRegen: `+|VAL| régénération de santé/s`,
@ -288,5 +297,13 @@ dict = {
upgrade_SwiftExecute: `Vitesse des miséricordes augmentée de 50%`, upgrade_SwiftExecute: `Vitesse des miséricordes augmentée de 50%`,
upgrade_OnHackInvis: `Invisible pendant 15 secondes après un piratage`, upgrade_OnHackInvis: `Invisible pendant 15 secondes après un piratage`,
damageType_Electricity: `Électrique`,
damageType_Fire: `Feu`,
damageType_Freeze: `Glace`,
damageType_Impact: `Impact`,
damageType_Magnetic: `Magnétique`,
damageType_Poison: `Poison`,
damageType_Radiation: `Radiations`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };

View File

@ -1,8 +1,11 @@
// Russian translation by AMelonInsideLemon // Russian translation by AMelonInsideLemon
dict = { dict = {
general_inventoryUpdateNote: `Примечание: изменения, внесенные здесь, отобразятся в игре только после повторной загрузки вашего инвентаря. Посещение навигации — самый простой способ этого добиться.`, general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `Добавить`, general_addButton: `Добавить`,
general_setButton: `Установить`,
general_removeButton: `Удалить`,
general_bulkActions: `Массовые действия`, general_bulkActions: `Массовые действия`,
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`, code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`, code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
code_changeNameConfirm: `Какое имя вы хотите установить для своей учетной записи?`, code_changeNameConfirm: `Какое имя вы хотите установить для своей учетной записи?`,
@ -112,9 +115,13 @@ dict = {
currency_FusionPoints: `Эндо`, currency_FusionPoints: `Эндо`,
currency_PrimeTokens: `Королевские Айя`, currency_PrimeTokens: `Королевские Айя`,
currency_owned: `У тебя |COUNT|.`, currency_owned: `У тебя |COUNT|.`,
powersuit_archonShardsLabel: `Ячейки осколков архонта`,
powersuit_archonShardsDescription: `Вы можете использовать эти неограниченные ячейки для установки множества улучшений.`, detailedView_archonShardsLabel: `Ячейки осколков архонта`,
powersuit_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`, detailedView_archonShardsDescription: `Вы можете использовать эти неограниченные ячейки для установки множества улучшений.`,
detailedView_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`,
detailedView_valenceBonusLabel: `Бонус Валентности`,
detailedView_valenceBonusDescription: `Вы можете добавить или убрать бонус валентности с вашего оружия.`,
mods_addRiven: `Добавить Мод Разлома`, mods_addRiven: `Добавить Мод Разлома`,
mods_fingerprint: `Отпечаток`, mods_fingerprint: `Отпечаток`,
mods_fingerprintHelp: `Нужна помощь с отпечатком?`, mods_fingerprintHelp: `Нужна помощь с отпечатком?`,
@ -175,6 +182,7 @@ dict = {
cheats_fastClanAscension: `Мгновенное Вознесение Клана`, cheats_fastClanAscension: `Мгновенное Вознесение Клана`,
cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`, cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`, cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`,
cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
cheats_save: `[UNTRANSLATED] Save`, cheats_save: `[UNTRANSLATED] Save`,
@ -231,6 +239,7 @@ dict = {
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`, import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`,
import_submit: `Отправить`, import_submit: `Отправить`,
@ -255,7 +264,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`, upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`,
upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`, upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`,
upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`, upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`,
upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health per enemy killed with Blast Damage (Max |VAL2| Health)`, upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`, upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`,
upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`, upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`,
upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`, upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
@ -288,5 +297,13 @@ dict = {
upgrade_SwiftExecute: `[UNTRANSLATED] Speed of Mercy Kills increased by 50%`, upgrade_SwiftExecute: `[UNTRANSLATED] Speed of Mercy Kills increased by 50%`,
upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after hacking`, upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after hacking`,
damageType_Electricity: `Электричество`,
damageType_Fire: `Огонь`,
damageType_Freeze: `Холод`,
damageType_Impact: `Удар`,
damageType_Magnetic: `Магнит`,
damageType_Poison: `Токсин`,
damageType_Radiation: `Радиация`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };

View File

@ -1,8 +1,11 @@
// Chinese translation by meb154, bishan178 & Corvus // Chinese translation by meb154, bishan178 & Corvus
dict = { dict = {
general_inventoryUpdateNote: `注意:此处所做的更改只有在游戏同步仓库后才会生效。您可以通过访问星图来触发仓库更新。`, general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
general_addButton: `添加`, general_addButton: `添加`,
general_setButton: `[UNTRANSLATED] Set`,
general_removeButton: `[UNTRANSLATED] Remove`,
general_bulkActions: `批量操作`, general_bulkActions: `批量操作`,
code_loginFail: `登录失败。请检查邮箱和密码。`, code_loginFail: `登录失败。请检查邮箱和密码。`,
code_regFail: `注册失败。账号已存在。`, code_regFail: `注册失败。账号已存在。`,
code_changeNameConfirm: `您想将账户名称更改为什么?`, code_changeNameConfirm: `您想将账户名称更改为什么?`,
@ -112,9 +115,13 @@ dict = {
currency_FusionPoints: `内融核心`, currency_FusionPoints: `内融核心`,
currency_PrimeTokens: `御品阿耶`, currency_PrimeTokens: `御品阿耶`,
currency_owned: `当前拥有 |COUNT|。`, currency_owned: `当前拥有 |COUNT|。`,
powersuit_archonShardsLabel: `执刑官源力石槽位`,
powersuit_archonShardsDescription: `您可以使用这些无限插槽应用各种强化效果`, detailedView_archonShardsLabel: `执刑官源力石槽位`,
powersuit_archonShardsDescription2: `请注意, 在加载时, 每个执政官源力石都需要一定的时间来生效。`, detailedView_archonShardsDescription: `您可以使用这些无限插槽应用各种强化效果`,
detailedView_archonShardsDescription2: `请注意, 在加载时, 每个执政官源力石都需要一定的时间来生效。`,
detailedView_valenceBonusLabel: `效价加成`,
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can add or remove the Valence Bonus from your weapon.`,
mods_addRiven: `添加裂罅MOD`, mods_addRiven: `添加裂罅MOD`,
mods_fingerprint: `印记`, mods_fingerprint: `印记`,
mods_fingerprintHelp: `需要印记相关的帮助?`, mods_fingerprintHelp: `需要印记相关的帮助?`,
@ -175,6 +182,7 @@ dict = {
cheats_fastClanAscension: `快速升级氏族`, cheats_fastClanAscension: `快速升级氏族`,
cheats_missionsCanGiveAllRelics: `任务可获取所有遗物`, cheats_missionsCanGiveAllRelics: `任务可获取所有遗物`,
cheats_unlockAllSimarisResearchEntries: `解锁所有Simaris研究条目`, cheats_unlockAllSimarisResearchEntries: `解锁所有Simaris研究条目`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`, cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`,
cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`, cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`,
cheats_save: `保存`, cheats_save: `保存`,
@ -230,7 +238,8 @@ dict = {
normal: `正常`, normal: `正常`,
worldState_allAtOnceNormal: `全部开启(普通)`, worldState_allAtOnceNormal: `全部开启(普通)`,
worldState_allAtOnceSteelPath: `全部开启(钢铁之路)`, worldState_allAtOnceSteelPath: `全部开启(钢铁之路)`,
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, worldState_theCircuitOverride: `无尽回廊任务循环配置:`,
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
import_importNote: `您可以在此处提供完整或部分库存响应(客户端表示)。支持的所有字段<b>将被覆盖</b>到您的账户中。`, import_importNote: `您可以在此处提供完整或部分库存响应(客户端表示)。支持的所有字段<b>将被覆盖</b>到您的账户中。`,
import_submit: `提交`, import_submit: `提交`,
@ -255,7 +264,7 @@ dict = {
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% 能量球效果`, upgrade_WarframeGlobeEffectEnergy: `+|VAL|% 能量球效果`,
upgrade_WarframeGlobeEffectHealth: `+|VAL|% 生命球效果`, upgrade_WarframeGlobeEffectHealth: `+|VAL|% 生命球效果`,
upgrade_WarframeHealthMax: `+|VAL| 生命`, upgrade_WarframeHealthMax: `+|VAL| 生命`,
upgrade_WarframeHPBoostFromImpact: `每个被爆炸伤害击杀的敌人,补充 |VAL1|生命 (最大 |VAL2| 生命)`, upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
upgrade_WarframeParkourVelocity: `+|VAL|% 跑酷速度`, upgrade_WarframeParkourVelocity: `+|VAL|% 跑酷速度`,
upgrade_WarframeRadiationDamageBoost: `对受辐射状态影响的敌人 +|VAL|% 技能伤害`, upgrade_WarframeRadiationDamageBoost: `对受辐射状态影响的敌人 +|VAL|% 技能伤害`,
upgrade_WarframeRegen: `+|VAL| 生命再生/s`, upgrade_WarframeRegen: `+|VAL| 生命再生/s`,
@ -288,5 +297,13 @@ dict = {
upgrade_SwiftExecute: `怜悯之击速度提升50%`, upgrade_SwiftExecute: `怜悯之击速度提升50%`,
upgrade_OnHackInvis: `入侵后隐身15秒`, upgrade_OnHackInvis: `入侵后隐身15秒`,
damageType_Electricity: `电击`,
damageType_Fire: `火焰`,
damageType_Freeze: `冰冻`,
damageType_Impact: `冲击`,
damageType_Magnetic: `磁力`,
damageType_Poison: `毒素`,
damageType_Radiation: `辐射`,
prettier_sucks_ass: `` prettier_sucks_ass: ``
}; };