forked from OpenWF/SpaceNinjaServer
Compare commits
10 Commits
5a9415ae0c
...
ba349a11f1
| Author | SHA1 | Date | |
|---|---|---|---|
| ba349a11f1 | |||
| d027e7f26e | |||
| cd6ce61b80 | |||
| a5be29159f | |||
| f099b64ef4 | |||
| c4f348c252 | |||
| 0d388b4b0f | |||
| d64531f4b2 | |||
| 01b8f7acf3 | |||
| 8a7db2cd85 |
@ -16,12 +16,6 @@
|
||||
"unlockAllSkins": false,
|
||||
"fullyStockedVendors": false,
|
||||
"skipClanKeyCrafting": false,
|
||||
"noDojoRoomBuildStage": false,
|
||||
"noDojoDecoBuildStage": false,
|
||||
"fastDojoRoomDestruction": false,
|
||||
"noDojoResearchCosts": false,
|
||||
"noDojoResearchTime": false,
|
||||
"fastClanAscension": false,
|
||||
"spoofMasteryRank": -1,
|
||||
"relicRewardItemCountMultiplier": 1,
|
||||
"nightwaveStandingMultiplier": 1,
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -17,7 +17,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"undici": "^7.10.0",
|
||||
"warframe-public-export-plus": "^0.5.83",
|
||||
"warframe-public-export-plus": "^0.5.88",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
@ -5532,9 +5532,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/warframe-public-export-plus": {
|
||||
"version": "0.5.84",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.84.tgz",
|
||||
"integrity": "sha512-ZpI1Y5CgWDmCwM4/oQpv9u0GD6KFvsJ9f1vJVXYhm5VD9DdOJcFzXgXgg98HXJ5JHbO16ZGIj83117qdpd0RQA=="
|
||||
"version": "0.5.88",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.88.tgz",
|
||||
"integrity": "sha512-uX766+MYDY3pMncu/23Dp9VZvrUe8pdWRWMcxfUbXg29aYO2GqipimHaFtw+vfrY06YAE8nbFkCWhFL3oPDPGw=="
|
||||
},
|
||||
"node_modules/warframe-riven-info": {
|
||||
"version": "0.1.2",
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"undici": "^7.10.0",
|
||||
"warframe-public-export-plus": "^0.5.83",
|
||||
"warframe-public-export-plus": "^0.5.88",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
|
||||
@ -3,6 +3,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import type { IInventoryClient, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { addMods, getInventory } from "../../services/inventoryService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const artifactsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -57,6 +58,7 @@ export const artifactsController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
res.send(itemId);
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IArtifactsRequest {
|
||||
|
||||
@ -95,10 +95,7 @@ export const confirmGuildInvitationPostController: RequestHandler = async (req,
|
||||
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
||||
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(
|
||||
guildMember.accountId.toString(),
|
||||
"GuildId LevelKeys Recipes skipClanKeyCrafting"
|
||||
);
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
giveClanKey(inventory);
|
||||
await inventory.save();
|
||||
|
||||
@ -27,7 +27,7 @@ export const createGuildController: RequestHandler = async (req, res) => {
|
||||
rank: 0
|
||||
});
|
||||
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes skipClanKeyCrafting");
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
||||
inventory.GuildId = guild._id;
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
giveClanKey(inventory, inventoryChanges);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { addMiscItems, getInventory } from "../../services/inventoryService.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
@ -75,5 +75,5 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
InventoryChanges: inventoryChanges,
|
||||
AffiliationMods: affiliationMods
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -8,8 +8,8 @@ export const giveKeyChainTriggeredMessageController: RequestHandler = async (req
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
|
||||
|
||||
const inventory = await getInventory(accountId, "QuestKeys");
|
||||
await giveKeyChainMessage(inventory, accountId, keyChainInfo);
|
||||
const inventory = await getInventory(accountId, "QuestKeys accountOwnerId");
|
||||
await giveKeyChainMessage(inventory, keyChainInfo);
|
||||
await inventory.save();
|
||||
|
||||
res.send(1);
|
||||
|
||||
@ -28,7 +28,6 @@ import {
|
||||
import type { IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
|
||||
import { config } from "../../services/configService.ts";
|
||||
import type { ITechProjectClient } from "../../types/guildTypes.ts";
|
||||
import { GuildPermission } from "../../types/guildTypes.ts";
|
||||
import { GuildMember } from "../../models/guildModel.ts";
|
||||
@ -83,16 +82,16 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
||||
guild.TechProjects[
|
||||
guild.TechProjects.push({
|
||||
ItemType: data.RecipeType,
|
||||
ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
|
||||
ReqCredits: guild.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
|
||||
ReqItems: recipe.ingredients.map(x => ({
|
||||
ItemType: x.ItemType,
|
||||
ItemCount: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
|
||||
ItemCount: guild.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
|
||||
})),
|
||||
State: 0
|
||||
}) - 1
|
||||
];
|
||||
setGuildTechLogState(guild, techProject.ItemType, 5);
|
||||
if (config.noDojoResearchCosts) {
|
||||
if (guild.noDojoResearchCosts) {
|
||||
processFundedGuildTechProject(guild, techProject, recipe);
|
||||
} else {
|
||||
if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
|
||||
|
||||
@ -10,7 +10,7 @@ import type {
|
||||
IMiscItem
|
||||
} from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { ExportMisc } from "warframe-public-export-plus";
|
||||
import { ExportResources } from "warframe-public-export-plus";
|
||||
import { getRecipe } from "../../services/itemDataService.ts";
|
||||
import { toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import { logger } from "../../utils/logger.ts";
|
||||
@ -20,6 +20,7 @@ import {
|
||||
applyCheatsToInfestedFoundry,
|
||||
handleSubsumeCompletion
|
||||
} from "../../services/infestedFoundryService.ts";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
@ -145,7 +146,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
const currentUnixSeconds = Math.trunc(Date.now() / 1000);
|
||||
|
||||
for (const contribution of request.ResourceContributions) {
|
||||
const snack = ExportMisc.helminthSnacks[contribution.ItemType];
|
||||
const snack = ExportResources[contribution.ItemType].helminthSnack!;
|
||||
|
||||
// tally items for removal
|
||||
const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType);
|
||||
@ -363,6 +364,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
|
||||
);
|
||||
addRecipes(inventory, recipeChanges);
|
||||
await inventory.save();
|
||||
sendWsBroadcastToGame(account._id.toString(), { sync_inventory: true });
|
||||
}
|
||||
res.end();
|
||||
break;
|
||||
|
||||
@ -8,7 +8,7 @@ import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } f
|
||||
import type { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "../../types/loginTypes.ts";
|
||||
import { logger } from "../../utils/logger.ts";
|
||||
import { version_compare } from "../../helpers/inventoryHelpers.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { handleNonceInvalidation } from "../../services/wsService.ts";
|
||||
|
||||
export const loginController: RequestHandler = async (request, response) => {
|
||||
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
|
||||
@ -74,7 +74,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
account.LastLogin = new Date();
|
||||
await account.save();
|
||||
|
||||
sendWsBroadcastTo(account._id.toString(), { nonce_updated: true });
|
||||
handleNonceInvalidation(account._id.toString());
|
||||
|
||||
response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { Account } from "../../models/loginModel.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { handleNonceInvalidation } from "../../services/wsService.ts";
|
||||
|
||||
export const logoutController: RequestHandler = async (req, res) => {
|
||||
if (!req.query.accountId) {
|
||||
@ -21,7 +21,7 @@ export const logoutController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
);
|
||||
if (stat.modifiedCount) {
|
||||
sendWsBroadcastTo(req.query.accountId as string, { nonce_updated: true });
|
||||
handleNonceInvalidation(req.query.accountId as string);
|
||||
}
|
||||
|
||||
res.writeHead(200, {
|
||||
|
||||
@ -2,6 +2,7 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const maturePetController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -19,6 +20,7 @@ export const maturePetController: RequestHandler = async (req, res) => {
|
||||
: [details.DominantTraits.FurPattern, details.DominantTraits.FurPattern, details.DominantTraits.FurPattern],
|
||||
unmature: data.revert
|
||||
});
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IMaturePetRequest {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import {
|
||||
getInventory,
|
||||
@ -197,5 +197,5 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
MiscItems: miscItemChanges
|
||||
}
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory, updateCurrency } from "../../services/inventoryService.ts";
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
interface INameWeaponRequest {
|
||||
ItemName: string;
|
||||
@ -28,5 +28,5 @@ export const nameWeaponController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
InventoryChanges: currencyChanges
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -13,7 +13,6 @@ import { GuildPermission } from "../../types/guildTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { ExportDojoRecipes, ExportResources } from "warframe-public-export-plus";
|
||||
import { config } from "../../services/configService.ts";
|
||||
|
||||
export const placeDecoInComponentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -74,7 +73,7 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
}
|
||||
}
|
||||
if (deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco") {
|
||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0) || config.noDojoDecoBuildStage) {
|
||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0) || guild.noDojoDecoBuildStage) {
|
||||
deco.CompletionTime = new Date();
|
||||
if (meta) {
|
||||
processDojoBuildMaterialsGathered(guild, meta);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { config } from "../../services/configService.ts";
|
||||
import {
|
||||
getDojoClient,
|
||||
getGuildForRequestEx,
|
||||
@ -21,7 +20,7 @@ export const queueDojoComponentDestructionController: RequestHandler = async (re
|
||||
const componentId = req.query.componentId as string;
|
||||
|
||||
guild.DojoComponents.id(componentId)!.DestructionTime = new Date(
|
||||
(Math.trunc(Date.now() / 1000) + (config.fastDojoRoomDestruction ? 5 : 2 * 3600)) * 1000
|
||||
(Math.trunc(Date.now() / 1000) + (guild.fastDojoRoomDestruction ? 5 : 2 * 3600)) * 1000
|
||||
);
|
||||
|
||||
await guild.save();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { getInventory, updateCurrency } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
export const releasePetController: RequestHandler = async (req, res) => {
|
||||
@ -20,7 +20,7 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
||||
|
||||
await inventory.save();
|
||||
res.json({ inventoryChanges }); // Not a mistake; it's "inventoryChanges" here.
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IReleasePetRequest {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { getInventory, updateCurrency } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
@ -23,7 +23,7 @@ export const renamePetController: RequestHandler = async (req, res) => {
|
||||
...data,
|
||||
inventoryChanges: inventoryChanges
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IRenamePetRequest {
|
||||
|
||||
@ -17,7 +17,7 @@ import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
|
||||
import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts";
|
||||
import { sendWsBroadcastEx } from "../../services/wsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import { parseFusionTreasure } from "../../helpers/inventoryHelpers.ts";
|
||||
|
||||
export const sellController: RequestHandler = async (req, res) => {
|
||||
@ -307,7 +307,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
|
||||
});
|
||||
sendWsBroadcastEx({ update_inventory: true }, accountId, parseInt(String(req.query.wsid)));
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface ISellRequest {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const setSupportedSyndicateController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -15,4 +16,5 @@ export const setSupportedSyndicateController: RequestHandler = async (req, res)
|
||||
);
|
||||
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
} from "../../services/guildService.ts";
|
||||
import { Types } from "mongoose";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { config } from "../../services/configService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
|
||||
@ -57,7 +56,7 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => {
|
||||
DecoCapacity: room?.decoCapacity
|
||||
}) - 1
|
||||
];
|
||||
if (config.noDojoRoomBuildStage) {
|
||||
if (guild.noDojoRoomBuildStage) {
|
||||
component.CompletionTime = new Date(Date.now());
|
||||
if (room) {
|
||||
processDojoBuildMaterialsGathered(guild, room);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
@ -19,6 +20,7 @@ export const abilityOverrideController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IAbilityOverrideRequest {
|
||||
|
||||
@ -1,21 +1,42 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { addFusionPoints, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts";
|
||||
import { GuildPermission } from "../../types/guildTypes.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const addCurrencyController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IAddCurrencyRequest;
|
||||
const inventory = await getInventory(accountId, request.currency);
|
||||
let projection = request.currency as string;
|
||||
if (request.currency.startsWith("Vault")) projection = "GuildId";
|
||||
const inventory = await getInventory(accountId, projection);
|
||||
if (request.currency == "FusionPoints") {
|
||||
addFusionPoints(inventory, request.delta);
|
||||
} else if (request.currency == "VaultRegularCredits" || request.currency == "VaultPremiumCredits") {
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (await hasGuildPermission(guild, accountId, GuildPermission.Treasurer)) {
|
||||
guild[request.currency] ??= 0;
|
||||
guild[request.currency]! += request.delta;
|
||||
await guild.save();
|
||||
}
|
||||
} else {
|
||||
inventory[request.currency] += request.delta;
|
||||
}
|
||||
await inventory.save();
|
||||
if (!request.currency.startsWith("Vault")) {
|
||||
await inventory.save();
|
||||
broadcastInventoryUpdate(req);
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IAddCurrencyRequest {
|
||||
currency: "RegularCredits" | "PremiumCredits" | "FusionPoints" | "PrimeTokens";
|
||||
currency:
|
||||
| "RegularCredits"
|
||||
| "PremiumCredits"
|
||||
| "FusionPoints"
|
||||
| "PrimeTokens"
|
||||
| "VaultRegularCredits"
|
||||
| "VaultPremiumCredits";
|
||||
delta: number;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory, addItem } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const addItemsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -11,6 +12,7 @@ export const addItemsController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IAddItemRequest {
|
||||
|
||||
@ -2,6 +2,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory, addRecipes } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportRecipes } from "warframe-public-export-plus";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const addMissingHelminthBlueprintsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -21,4 +22,5 @@ export const addMissingHelminthBlueprintsController: RequestHandler = async (req
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const addMissingMaxRankModsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -41,4 +42,5 @@ export const addMissingMaxRankModsController: RequestHandler = async (req, res)
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
36
src/controllers/custom/addVaultDecoRecipeController.ts
Normal file
36
src/controllers/custom/addVaultDecoRecipeController.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { hasAccessToDojo, getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts";
|
||||
import { GuildPermission } from "../../types/guildTypes.ts";
|
||||
import type { ITypeCount } from "../../types/commonTypes.ts";
|
||||
|
||||
export const addVaultDecoRecipeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requests = req.body as ITypeCount[];
|
||||
const inventory = await getInventory(accountId, "LevelKeys GuildId");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Architect))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
guild.VaultDecoRecipes ??= [];
|
||||
for (const request of requests) {
|
||||
const index = guild.VaultDecoRecipes.findIndex(x => x.ItemType === request.ItemType);
|
||||
|
||||
if (index == -1) {
|
||||
guild.VaultDecoRecipes.push({
|
||||
ItemType: request.ItemType,
|
||||
ItemCount: request.ItemCount
|
||||
});
|
||||
} else {
|
||||
guild.VaultDecoRecipes[index].ItemCount += request.ItemCount;
|
||||
|
||||
if (guild.VaultDecoRecipes[index].ItemCount < 1) {
|
||||
guild.VaultDecoRecipes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
await guild.save();
|
||||
res.end();
|
||||
};
|
||||
@ -1,10 +1,11 @@
|
||||
import { applyClientEquipmentUpdates, getInventory } from "../../services/inventoryService.ts";
|
||||
import { getMaxLevelCap } from "../../services/itemDataService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { IOid } from "../../types/commonTypes.ts";
|
||||
import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportMisc } from "warframe-public-export-plus";
|
||||
|
||||
export const addXpController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -14,7 +15,7 @@ export const addXpController: RequestHandler = async (req, res) => {
|
||||
for (const clientItem of gear) {
|
||||
const dbItem = inventory[category as TEquipmentKey].id((clientItem.ItemId as IOid).$oid);
|
||||
if (dbItem) {
|
||||
if (dbItem.ItemType in ExportMisc.uniqueLevelCaps) {
|
||||
if (getMaxLevelCap(dbItem.ItemType) > 30) {
|
||||
if ((dbItem.Polarized ?? 0) < 5) {
|
||||
dbItem.Polarized = 5;
|
||||
}
|
||||
@ -25,6 +26,7 @@ export const addXpController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
type IAddXpRequest = {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
@ -20,6 +21,7 @@ export const changeModularPartsController: RequestHandler = async (req, res) =>
|
||||
await inventory.save();
|
||||
}
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IUpdateFingerPrintRequest {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
@ -31,4 +32,5 @@ export const editSuitInvigorationUpgradeController: RequestHandler = async (req,
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
16
src/controllers/custom/getAllianceController.ts
Normal file
16
src/controllers/custom/getAllianceController.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Alliance, Guild } from "../../models/guildModel.ts";
|
||||
import { getAllianceClient } from "../../services/guildService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
export const getAllianceController: RequestHandler = async (req, res) => {
|
||||
const guildId = req.query.guildId;
|
||||
if (guildId) {
|
||||
const guild = await Guild.findById(guildId, "Name Tier AllianceId");
|
||||
if (guild && guild.AllianceId) {
|
||||
const alliance = (await Alliance.findById(guild.AllianceId))!;
|
||||
res.json(await getAllianceClient(alliance, guild));
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
40
src/controllers/custom/getGuildController.ts
Normal file
40
src/controllers/custom/getGuildController.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { Guild, GuildMember } from "../../models/guildModel.ts";
|
||||
import { toMongoDate, toOid2 } from "../../helpers/inventoryHelpers.ts";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "../../services/friendService.ts";
|
||||
import type { IGuildMemberClient } from "../../types/guildTypes.ts";
|
||||
|
||||
export const getGuildController: RequestHandler = async (req, res) => {
|
||||
const guildId = req.query.guildId;
|
||||
if (guildId) {
|
||||
const guild = await Guild.findById(guildId);
|
||||
if (guild) {
|
||||
const guildMembers = await GuildMember.find({ guildId: guild._id });
|
||||
|
||||
const members: IGuildMemberClient[] = [];
|
||||
const dataFillInPromises: Promise<void>[] = [];
|
||||
for (const guildMember of guildMembers) {
|
||||
const member: IGuildMemberClient = {
|
||||
_id: toOid2(guildMember.accountId, undefined),
|
||||
Rank: guildMember.rank,
|
||||
Status: guildMember.status,
|
||||
Note: guildMember.RequestMsg,
|
||||
RequestExpiry: guildMember.RequestExpiry ? toMongoDate(guildMember.RequestExpiry) : undefined
|
||||
};
|
||||
dataFillInPromises.push(addAccountDataToFriendInfo(member));
|
||||
dataFillInPromises.push(addInventoryDataToFriendInfo(member));
|
||||
|
||||
members.push(member);
|
||||
}
|
||||
|
||||
await Promise.all(dataFillInPromises);
|
||||
|
||||
res.json({
|
||||
...guild.toObject(),
|
||||
Members: members
|
||||
});
|
||||
} else {
|
||||
res.status(400).end();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -7,10 +7,11 @@ import {
|
||||
ExportAvionics,
|
||||
ExportBoosters,
|
||||
ExportCustoms,
|
||||
ExportDojoRecipes,
|
||||
ExportDrones,
|
||||
ExportFactions,
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
ExportMisc,
|
||||
ExportRailjackWeapons,
|
||||
ExportRecipes,
|
||||
ExportRelics,
|
||||
@ -59,24 +60,26 @@ interface ItemLists {
|
||||
Boosters: ListedItem[];
|
||||
VarziaOffers: ListedItem[];
|
||||
Abilities: ListedItem[];
|
||||
TechProjects: ListedItem[];
|
||||
VaultDecoRecipes: ListedItem[];
|
||||
//circuitGameModes: ListedItem[];
|
||||
}
|
||||
|
||||
const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
||||
VPQ_BRONZE: "",
|
||||
VPQ_SILVER: " [Exceptional]",
|
||||
VPQ_GOLD: " [Flawless]",
|
||||
VPQ_PLATINUM: " [Radiant]"
|
||||
VPQ_SILVER: "/Lotus/Language/Relics/VoidProjectionQuality_Silver",
|
||||
VPQ_GOLD: "/Lotus/Language/Relics/VoidProjectionQuality_Gold",
|
||||
VPQ_PLATINUM: "/Lotus/Language/Relics/VoidProjectionQuality_Platinum"
|
||||
};
|
||||
|
||||
/*const toTitleCase = (str: string): string => {
|
||||
const toTitleCase = (str: string): string => {
|
||||
return str.replace(/[^\s-]+/g, word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase());
|
||||
};*/
|
||||
};
|
||||
|
||||
const getItemListsController: RequestHandler = (req, response) => {
|
||||
const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
|
||||
const res: ItemLists = {
|
||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||
uniqueLevelCaps: {},
|
||||
Suits: [],
|
||||
LongGuns: [],
|
||||
Melee: [],
|
||||
@ -97,7 +100,9 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
mods: [],
|
||||
Boosters: [],
|
||||
VarziaOffers: [],
|
||||
Abilities: []
|
||||
Abilities: [],
|
||||
TechProjects: [],
|
||||
VaultDecoRecipes: []
|
||||
/*circuitGameModes: [
|
||||
{
|
||||
uniqueName: "Survival",
|
||||
@ -141,6 +146,9 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(ability.name || uniqueName, lang)
|
||||
});
|
||||
});
|
||||
if (item.maxLevelCap) {
|
||||
res.uniqueLevelCaps[uniqueName] = item.maxLevelCap;
|
||||
}
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||
@ -190,6 +198,9 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
if (item.maxLevelCap) {
|
||||
res.uniqueLevelCaps[uniqueName] = item.maxLevelCap;
|
||||
}
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportResources)) {
|
||||
let name = getString(item.name, lang);
|
||||
@ -232,14 +243,19 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
}
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportRelics)) {
|
||||
const qualitySuffix =
|
||||
item.quality !== "VPQ_BRONZE"
|
||||
? ` [${toTitleCase(getString(relicQualitySuffixes[item.quality], lang))}]`
|
||||
: "";
|
||||
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name:
|
||||
getString("/Lotus/Language/Relics/VoidProjectionName", lang)
|
||||
.split("|ERA|")
|
||||
.join(item.era)
|
||||
.join(getString(`/Lotus/Language/Relics/Era_${item.era.toUpperCase()}`, lang))
|
||||
.split("|CATEGORY|")
|
||||
.join(item.category) + relicQualitySuffixes[item.quality]
|
||||
.join(item.category) + qualitySuffix
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportGear)) {
|
||||
@ -367,6 +383,66 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const uniqueName of Object.keys(ExportDojoRecipes.research)) {
|
||||
if (
|
||||
!["Zekti", "Vidar", "Lavan"].some(house => uniqueName.includes(house)) &&
|
||||
!uniqueName.startsWith("/Lotus/Types/Items/ShipFeatureItems/Railjack/")
|
||||
) {
|
||||
let resultType;
|
||||
if (uniqueName in ExportRecipes) {
|
||||
resultType = ExportRecipes[uniqueName].resultType;
|
||||
} else if (uniqueName in ExportDojoRecipes.fabrications) {
|
||||
resultType = ExportDojoRecipes.fabrications[uniqueName].resultType;
|
||||
} else if (uniqueName.startsWith("/Lotus/Types/Game/")) {
|
||||
resultType = uniqueName.replace("Blueprint", "");
|
||||
} else {
|
||||
resultType = uniqueName;
|
||||
}
|
||||
|
||||
let name = getString(getItemName(resultType) || resultType, lang);
|
||||
|
||||
if (uniqueName in ExportRecipes) {
|
||||
const recipeNum = ExportRecipes[uniqueName].num;
|
||||
if (recipeNum > 1) {
|
||||
name = `${name} X ${recipeNum}`;
|
||||
}
|
||||
}
|
||||
|
||||
res.TechProjects.push({
|
||||
uniqueName,
|
||||
name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const uniqueName of [
|
||||
...Object.entries(ExportDojoRecipes.decos)
|
||||
.filter(([_, data]) => data.requiredInVault)
|
||||
.map(([uniqueName]) => uniqueName),
|
||||
// not requiredInVault:
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/PlagueStarEventTrophyBronzeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/PlagueStarEventTrophyGoldRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/PlagueStarEventTrophySilverRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/PlagueStarEventTrophyTerracottaRecipe"
|
||||
// removed in 38.6.0:
|
||||
// "/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyBronzeRecipe",
|
||||
// "/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyCrystalRecipe",
|
||||
// "/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyGoldRecipe",
|
||||
// "/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophySilverRecipe",
|
||||
// "/Lotus/Levels/ClanDojo/ComponentPropRecipes/NaturalPlaceables/CoralChunkARecipe"
|
||||
]) {
|
||||
let name = getString(getItemName(uniqueName) || uniqueName, lang);
|
||||
if (uniqueName.startsWith("/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemma")) {
|
||||
const factionTag = uniqueName.includes("Corpus") ? "FC_CORPUS" : "FC_GRINEER";
|
||||
const faction = ExportFactions[factionTag].name;
|
||||
name += ` [${getString(faction, lang)}]`;
|
||||
}
|
||||
res.VaultDecoRecipes.push({
|
||||
uniqueName,
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
response.json(res);
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getLoadout } from "../../services/loadoutService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getPersonalRooms } from "../../services/personalRoomsService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
import type { IInventoryClient } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IGetShipResponse } from "../../types/personalRoomsTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
@ -32,6 +33,7 @@ export const importController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
interface IImportRequest {
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
import { logger } from "../../utils/logger.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportKeys } from "warframe-public-export-plus";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -101,8 +102,16 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
questKey.Completed = false;
|
||||
questKey.CompletionDate = undefined;
|
||||
}
|
||||
questKey.Progress.pop();
|
||||
const stage = questKey.Progress.length - 1;
|
||||
|
||||
const run = questKey.Progress[0]?.c ?? 0;
|
||||
const stage = questKey.Progress.map(p => p.c).lastIndexOf(run);
|
||||
|
||||
if (run > 0) {
|
||||
questKey.Progress[stage].c = run - 1;
|
||||
} else {
|
||||
questKey.Progress.pop();
|
||||
}
|
||||
|
||||
if (stage > 0) {
|
||||
await giveKeyChainStageTriggered(inventory, {
|
||||
KeyChain: questKey.ItemType,
|
||||
@ -122,28 +131,28 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
if (!questKey.Progress) break;
|
||||
|
||||
const currentStage = questKey.Progress.length;
|
||||
const run = questKey.Progress[0]?.c ?? 0;
|
||||
const currentStage = questKey.Progress.map(p => p.c).lastIndexOf(run);
|
||||
|
||||
if (currentStage + 1 == questManifest.chainStages?.length) {
|
||||
logger.debug(`Trying to complete last stage with nextStage, calling completeQuest instead`);
|
||||
await completeQuest(inventory, questKey.ItemType);
|
||||
} else {
|
||||
const progress = {
|
||||
c: 0,
|
||||
i: false,
|
||||
m: false,
|
||||
b: []
|
||||
};
|
||||
questKey.Progress.push(progress);
|
||||
if (run > 0) {
|
||||
questKey.Progress[currentStage + 1].c = run;
|
||||
} else {
|
||||
questKey.Progress.push({ c: run, i: false, m: false, b: [] });
|
||||
}
|
||||
|
||||
await giveKeyChainStageTriggered(inventory, {
|
||||
KeyChain: questKey.ItemType,
|
||||
ChainStage: currentStage
|
||||
ChainStage: currentStage + 1
|
||||
});
|
||||
|
||||
if (currentStage > 0) {
|
||||
await giveKeyChainMissionReward(inventory, {
|
||||
KeyChain: questKey.ItemType,
|
||||
ChainStage: currentStage - 1
|
||||
ChainStage: currentStage
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -157,4 +166,5 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
|
||||
await inventory.save();
|
||||
res.status(200).end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const popArchonCrystalUpgradeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -12,6 +13,7 @@ export const popArchonCrystalUpgradeController: RequestHandler = async (req, res
|
||||
);
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
return;
|
||||
}
|
||||
res.status(400).end();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const pushArchonCrystalUpgradeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -15,6 +16,7 @@ export const pushArchonCrystalUpgradeController: RequestHandler = async (req, re
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import type { IAccountCheats } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
@ -10,6 +11,9 @@ export const setAccountCheatController: RequestHandler = async (req, res) => {
|
||||
inventory[payload.key] = payload.value;
|
||||
await inventory.save();
|
||||
res.end();
|
||||
if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(payload.key) != -1) {
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true, sync_inventory: true });
|
||||
}
|
||||
};
|
||||
|
||||
interface ISetAccountCheatRequest {
|
||||
|
||||
@ -2,6 +2,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { ExportBoosters } from "warframe-public-export-plus";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
const I32_MAX = 0x7fffffff;
|
||||
|
||||
@ -42,4 +43,5 @@ export const setBoosterController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
|
||||
|
||||
export const setEvolutionProgressController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -25,6 +26,7 @@ export const setEvolutionProgressController: RequestHandler = async (req, res) =
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
type ISetEvolutionProgressRequest = {
|
||||
|
||||
29
src/controllers/custom/setGuildCheatController.ts
Normal file
29
src/controllers/custom/setGuildCheatController.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { GuildMember } from "../../models/guildModel.ts";
|
||||
import { getGuildForRequestEx, hasAccessToDojo } from "../../services/guildService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { IGuildCheats } from "../../types/guildTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
export const setGuildCheatController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = req.body as ISetGuildCheatRequest;
|
||||
const inventory = await getInventory(accountId, `${payload.key} GuildId LevelKeys`);
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const member = await GuildMember.findOne({ accountId: accountId, guildId: guild._id });
|
||||
|
||||
if (member) {
|
||||
if (!hasAccessToDojo(inventory) || member.rank > 1) {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
guild[payload.key] = payload.value;
|
||||
await guild.save();
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface ISetGuildCheatRequest {
|
||||
key: keyof IGuildCheats;
|
||||
value: boolean;
|
||||
}
|
||||
128
src/controllers/custom/techProjectController.ts
Normal file
128
src/controllers/custom/techProjectController.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import {
|
||||
hasAccessToDojo,
|
||||
getGuildForRequestEx,
|
||||
setGuildTechLogState,
|
||||
processFundedGuildTechProject,
|
||||
scaleRequiredCount,
|
||||
hasGuildPermission,
|
||||
addGuildMemberMiscItemContribution,
|
||||
processGuildTechProjectContributionsUpdate,
|
||||
processCompletedGuildTechProject
|
||||
} from "../../services/guildService.ts";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { GuildPermission } from "../../types/guildTypes.ts";
|
||||
import { GuildMember } from "../../models/guildModel.ts";
|
||||
|
||||
export const addTechProjectController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requests = req.body as ITechProjectRequest[];
|
||||
const inventory = await getInventory(accountId, "LevelKeys GuildId");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
guild.TechProjects ??= [];
|
||||
for (const request of requests) {
|
||||
const recipe = ExportDojoRecipes.research[request.ItemType];
|
||||
if (!guild.TechProjects.find(x => x.ItemType == request.ItemType)) {
|
||||
const techProject =
|
||||
guild.TechProjects[
|
||||
guild.TechProjects.push({
|
||||
ItemType: request.ItemType,
|
||||
ReqCredits: guild.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
|
||||
ReqItems: recipe.ingredients.map(x => ({
|
||||
ItemType: x.ItemType,
|
||||
ItemCount: guild.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
|
||||
})),
|
||||
State: 0
|
||||
}) - 1
|
||||
];
|
||||
setGuildTechLogState(guild, techProject.ItemType, 5);
|
||||
if (guild.noDojoResearchCosts) {
|
||||
processFundedGuildTechProject(guild, techProject, recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
await guild.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
export const removeTechProjectController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requests = req.body as ITechProjectRequest[];
|
||||
const inventory = await getInventory(accountId, "LevelKeys GuildId");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
guild.TechProjects ??= [];
|
||||
for (const request of requests) {
|
||||
const index = guild.TechProjects.findIndex(x => x.ItemType === request.ItemType);
|
||||
if (index !== -1) {
|
||||
guild.TechProjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
await guild.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
export const fundTechProjectController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requests = req.body as ITechProjectRequest[];
|
||||
const inventory = await getInventory(accountId, "LevelKeys GuildId");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const guildMember = (await GuildMember.findOne(
|
||||
{ accountId, guildId: guild._id },
|
||||
"RegularCreditsContributed MiscItemsContributed"
|
||||
))!;
|
||||
if (!hasAccessToDojo(inventory)) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
for (const request of requests) {
|
||||
const techProject = guild.TechProjects!.find(x => x.ItemType == request.ItemType)!;
|
||||
|
||||
guildMember.RegularCreditsContributed ??= 0;
|
||||
guildMember.RegularCreditsContributed += techProject.ReqCredits;
|
||||
techProject.ReqCredits = 0;
|
||||
|
||||
for (const reqItem of techProject.ReqItems) {
|
||||
addGuildMemberMiscItemContribution(guildMember, reqItem);
|
||||
reqItem.ItemCount = 0;
|
||||
}
|
||||
|
||||
await processGuildTechProjectContributionsUpdate(guild, techProject);
|
||||
}
|
||||
await Promise.all([guild.save(), guildMember.save()]);
|
||||
res.end();
|
||||
};
|
||||
|
||||
export const completeTechProjectsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const requests = req.body as ITechProjectRequest[];
|
||||
const inventory = await getInventory(accountId, "LevelKeys GuildId");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory)) {
|
||||
res.status(400).send("-1").end();
|
||||
return;
|
||||
}
|
||||
for (const request of requests) {
|
||||
const techProject = guild.TechProjects!.find(x => x.ItemType == request.ItemType)!;
|
||||
techProject.CompletionDate = new Date();
|
||||
|
||||
if (setGuildTechLogState(guild, techProject.ItemType, 4, techProject.CompletionDate)) {
|
||||
processCompletedGuildTechProject(guild, techProject.ItemType);
|
||||
}
|
||||
}
|
||||
await guild.save();
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface ITechProjectRequest {
|
||||
ItemType: string;
|
||||
}
|
||||
@ -2,19 +2,25 @@ import type { RequestHandler } from "express";
|
||||
import { ExportResources, ExportVirtuals } from "warframe-public-export-plus";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { addItem, getInventory } from "../../services/inventoryService.ts";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
export const unlockAllCapturaScenesController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
let needSync = false;
|
||||
for (const uniqueName of Object.keys(ExportResources)) {
|
||||
if (resourceInheritsFrom(uniqueName, "/Lotus/Types/Items/MiscItems/PhotoboothTile")) {
|
||||
await addItem(inventory, uniqueName, 1);
|
||||
needSync = true;
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
if (needSync) {
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
}
|
||||
};
|
||||
|
||||
const resourceInheritsFrom = (resourceName: string, targetName: string): boolean => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
export const unlockAllIntrinsicsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -16,4 +17,5 @@ export const unlockAllIntrinsicsController: RequestHandler = async (req, res) =>
|
||||
inventory.PlayerSkills.LPS_DRIFT_ENDURANCE = 10;
|
||||
await inventory.save();
|
||||
res.end();
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
const allEudicoHeistJobs = [
|
||||
"/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyOne",
|
||||
@ -21,4 +22,5 @@ export const unlockAllProfitTakerStagesController: RequestHandler = async (req,
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
export const unlockAllSimarisResearchEntriesController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -17,4 +18,5 @@ export const unlockAllSimarisResearchEntriesController: RequestHandler = async (
|
||||
].map(type => ({ TargetType: type, Scans: 10, Completed: true }));
|
||||
await inventory.save();
|
||||
res.end();
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { getInventory } from "../../services/inventoryService.ts";
|
||||
import type { WeaponTypeInternal } from "../../services/itemDataService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
import { sendWsBroadcastToGame } from "../../services/wsService.ts";
|
||||
|
||||
export const updateFingerprintController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -22,6 +23,7 @@ export const updateFingerprintController: RequestHandler = async (req, res) => {
|
||||
await inventory.save();
|
||||
}
|
||||
res.end();
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
};
|
||||
|
||||
interface IUpdateFingerPrintRequest {
|
||||
|
||||
@ -212,12 +212,12 @@ export const getInfNodes = (manifest: INemesisManifest, rank: number): IInfNode[
|
||||
value.systemIndex === systemIndex &&
|
||||
value.nodeType != 3 && // not hub
|
||||
value.nodeType != 7 && // not junction
|
||||
value.missionIndex && // must have a mission type and not assassination
|
||||
value.missionIndex != 28 && // not open world
|
||||
value.missionIndex != 32 && // not railjack
|
||||
value.missionIndex != 41 && // not saya's visions
|
||||
value.missionIndex != 42 && // not face off
|
||||
value.name.indexOf("1999NodeI") == -1 && // not stage defence
|
||||
value.missionType != "MT_ASSASSINATION" &&
|
||||
value.missionType != "MT_LANDSCAPE" &&
|
||||
value.missionType != "MT_RAILJACK" &&
|
||||
value.missionType != "MT_OFFERING" &&
|
||||
value.missionType != "MT_PVPVE" &&
|
||||
value.name.indexOf("1999NodeI") == -1 && // not stage defense
|
||||
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
|
||||
!isArchwingMission(value)
|
||||
) {
|
||||
|
||||
@ -201,6 +201,14 @@ goalProgressSchema.set("toJSON", {
|
||||
|
||||
const guildSchema = new Schema<IGuildDatabase>(
|
||||
{
|
||||
// SNS guild cheats
|
||||
noDojoRoomBuildStage: Boolean,
|
||||
noDojoDecoBuildStage: Boolean,
|
||||
fastDojoRoomDestruction: Boolean,
|
||||
noDojoResearchCosts: Boolean,
|
||||
noDojoResearchTime: Boolean,
|
||||
fastClanAscension: Boolean,
|
||||
|
||||
Name: { type: String, required: true, unique: true },
|
||||
MOTD: { type: String, default: "" },
|
||||
LongMOTD: { type: longMOTDSchema, default: undefined },
|
||||
|
||||
@ -7,6 +7,8 @@ import { popArchonCrystalUpgradeController } from "../controllers/custom/popArch
|
||||
import { deleteAccountController } from "../controllers/custom/deleteAccountController.ts";
|
||||
import { getNameController } from "../controllers/custom/getNameController.ts";
|
||||
import { getAccountInfoController } from "../controllers/custom/getAccountInfoController.ts";
|
||||
import { getGuildController } from "../controllers/custom/getGuildController.ts";
|
||||
import { getAllianceController } from "../controllers/custom/getAllianceController.ts";
|
||||
import { renameAccountController } from "../controllers/custom/renameAccountController.ts";
|
||||
import { ircDroppedController } from "../controllers/custom/ircDroppedController.ts";
|
||||
import { unlockAllIntrinsicsController } from "../controllers/custom/unlockAllIntrinsicsController.ts";
|
||||
@ -25,6 +27,13 @@ import { createAccountController } from "../controllers/custom/createAccountCont
|
||||
import { createMessageController } from "../controllers/custom/createMessageController.ts";
|
||||
import { addCurrencyController } from "../controllers/custom/addCurrencyController.ts";
|
||||
import { addItemsController } from "../controllers/custom/addItemsController.ts";
|
||||
import {
|
||||
addTechProjectController,
|
||||
completeTechProjectsController,
|
||||
fundTechProjectController,
|
||||
removeTechProjectController
|
||||
} from "../controllers/custom/techProjectController.ts";
|
||||
import { addVaultDecoRecipeController } from "../controllers/custom/addVaultDecoRecipeController.ts";
|
||||
import { addXpController } from "../controllers/custom/addXpController.ts";
|
||||
import { importController } from "../controllers/custom/importController.ts";
|
||||
import { manageQuestsController } from "../controllers/custom/manageQuestsController.ts";
|
||||
@ -34,6 +43,7 @@ import { updateFingerprintController } from "../controllers/custom/updateFingerp
|
||||
import { changeModularPartsController } from "../controllers/custom/changeModularPartsController.ts";
|
||||
import { editSuitInvigorationUpgradeController } from "../controllers/custom/editSuitInvigorationUpgradeController.ts";
|
||||
import { setAccountCheatController } from "../controllers/custom/setAccountCheatController.ts";
|
||||
import { setGuildCheatController } from "../controllers/custom/setGuildCheatController.ts";
|
||||
|
||||
import { getConfigController, setConfigController } from "../controllers/custom/configController.ts";
|
||||
|
||||
@ -46,6 +56,8 @@ customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController);
|
||||
customRouter.get("/deleteAccount", deleteAccountController);
|
||||
customRouter.get("/getName", getNameController);
|
||||
customRouter.get("/getAccountInfo", getAccountInfoController);
|
||||
customRouter.get("/getGuild", getGuildController);
|
||||
customRouter.get("/getAlliance", getAllianceController);
|
||||
customRouter.get("/renameAccount", renameAccountController);
|
||||
customRouter.get("/ircDropped", ircDroppedController);
|
||||
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
||||
@ -64,6 +76,11 @@ customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
customRouter.post("/addCurrency", addCurrencyController);
|
||||
customRouter.post("/addItems", addItemsController);
|
||||
customRouter.post("/addTechProject", addTechProjectController);
|
||||
customRouter.post("/removeTechProject", removeTechProjectController);
|
||||
customRouter.post("/addVaultDecoRecipe", addVaultDecoRecipeController);
|
||||
customRouter.post("/fundTechProject", fundTechProjectController);
|
||||
customRouter.post("/completeTechProject", completeTechProjectsController);
|
||||
customRouter.post("/addXp", addXpController);
|
||||
customRouter.post("/import", importController);
|
||||
customRouter.post("/manageQuests", manageQuestsController);
|
||||
@ -73,6 +90,7 @@ customRouter.post("/updateFingerprint", updateFingerprintController);
|
||||
customRouter.post("/changeModularParts", changeModularPartsController);
|
||||
customRouter.post("/editSuitInvigorationUpgrade", editSuitInvigorationUpgradeController);
|
||||
customRouter.post("/setAccountCheat", setAccountCheatController);
|
||||
customRouter.post("/setGuildCheat", setGuildCheatController);
|
||||
|
||||
customRouter.post("/getConfig", getConfigController);
|
||||
customRouter.post("/setConfig", setConfigController);
|
||||
|
||||
@ -42,6 +42,9 @@ webuiRouter.get("/webui/cheats", (_req, res) => {
|
||||
webuiRouter.get("/webui/import", (_req, res) => {
|
||||
res.sendFile(path.join(baseDir, "static/webui/index.html"));
|
||||
});
|
||||
webuiRouter.get("/webui/guildView", (_req, res) => {
|
||||
res.sendFile(path.join(baseDir, "static/webui/index.html"));
|
||||
});
|
||||
|
||||
// Serve static files
|
||||
webuiRouter.use("/webui", express.static(path.join(baseDir, "static/webui")));
|
||||
|
||||
@ -24,15 +24,8 @@ export interface IConfig {
|
||||
unlockAllShipDecorations?: boolean;
|
||||
unlockAllFlavourItems?: boolean;
|
||||
unlockAllSkins?: boolean;
|
||||
unlockAllDecoRecipes?: boolean;
|
||||
fullyStockedVendors?: boolean;
|
||||
skipClanKeyCrafting?: boolean;
|
||||
noDojoRoomBuildStage?: boolean;
|
||||
noDojoDecoBuildStage?: boolean;
|
||||
fastDojoRoomDestruction?: boolean;
|
||||
noDojoResearchCosts?: boolean;
|
||||
noDojoResearchTime?: boolean;
|
||||
fastClanAscension?: boolean;
|
||||
spoofMasteryRank?: number;
|
||||
relicRewardItemCountMultiplier?: number;
|
||||
nightwaveStandingMultiplier?: number;
|
||||
@ -128,7 +121,14 @@ export const configRemovedOptionsKeys = [
|
||||
"exceptionalRelicsAlwaysGiveBronzeReward",
|
||||
"flawlessRelicsAlwaysGiveSilverReward",
|
||||
"radiantRelicsAlwaysGiveGoldReward",
|
||||
"disableDailyTribute"
|
||||
"disableDailyTribute",
|
||||
"noDojoRoomBuildStage",
|
||||
"noDojoDecoBuildStage",
|
||||
"fastDojoRoomDestruction",
|
||||
"noDojoResearchCosts",
|
||||
"noDojoResearchTime",
|
||||
"fastClanAscension",
|
||||
"unlockAllDecoRecipes"
|
||||
];
|
||||
|
||||
export const configPath = path.join(repoDir, args.configPath ?? "config.json");
|
||||
|
||||
@ -33,7 +33,6 @@ import { Inbox } from "../models/inboxModel.ts";
|
||||
import type { IFusionTreasure } from "../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
|
||||
import { parallelForeach } from "../utils/async-utils.ts";
|
||||
import allDecoRecipes from "../../static/fixed_responses/allDecoRecipes.json" with { type: "json" };
|
||||
import { createMessage } from "./inboxService.ts";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "./friendService.ts";
|
||||
import type { ITypeCount } from "../types/commonTypes.ts";
|
||||
@ -136,9 +135,7 @@ export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
|
||||
DojoRefundPremiumCredits: guild.VaultPremiumCredits,
|
||||
ShipDecorations: guild.VaultShipDecorations,
|
||||
FusionTreasures: guild.VaultFusionTreasures,
|
||||
DecoRecipes: config.unlockAllDecoRecipes
|
||||
? allDecoRecipes.map(recipe => ({ ItemType: recipe, ItemCount: 1 }))
|
||||
: guild.VaultDecoRecipes
|
||||
DecoRecipes: guild.VaultDecoRecipes
|
||||
};
|
||||
};
|
||||
|
||||
@ -565,12 +562,12 @@ export const processFundedGuildTechProject = (
|
||||
recipe: IDojoResearch
|
||||
): void => {
|
||||
techProject.State = 1;
|
||||
techProject.CompletionDate = new Date(Date.now() + (config.noDojoResearchTime ? 0 : recipe.time) * 1000);
|
||||
techProject.CompletionDate = new Date(Date.now() + (guild.noDojoResearchTime ? 0 : recipe.time) * 1000);
|
||||
if (recipe.guildXpValue) {
|
||||
guild.XP += recipe.guildXpValue;
|
||||
}
|
||||
setGuildTechLogState(guild, techProject.ItemType, config.noDojoResearchTime ? 4 : 3, techProject.CompletionDate);
|
||||
if (config.noDojoResearchTime) {
|
||||
setGuildTechLogState(guild, techProject.ItemType, guild.noDojoResearchTime ? 4 : 3, techProject.CompletionDate);
|
||||
if (guild.noDojoResearchTime) {
|
||||
processCompletedGuildTechProject(guild, techProject.ItemType);
|
||||
}
|
||||
};
|
||||
@ -657,8 +654,8 @@ export const checkClanAscensionHasRequiredContributors = async (guild: TGuildDat
|
||||
if (guild.CeremonyContributors!.length >= requiredContributors) {
|
||||
guild.Class = guild.CeremonyClass!;
|
||||
guild.CeremonyClass = undefined;
|
||||
guild.CeremonyResetDate = new Date(Date.now() + (config.fastClanAscension ? 5_000 : 72 * 3600_000));
|
||||
if (!config.fastClanAscension) {
|
||||
guild.CeremonyResetDate = new Date(Date.now() + (guild.fastClanAscension ? 5_000 : 72 * 3600_000));
|
||||
if (!guild.fastClanAscension) {
|
||||
// Send message to all active guild members
|
||||
const members = await GuildMember.find({ guildId: guild._id, status: 0 }, "accountId");
|
||||
await parallelForeach(members, async member => {
|
||||
|
||||
@ -40,6 +40,7 @@ import {
|
||||
ExportBoosters,
|
||||
ExportBundles,
|
||||
ExportChallenges,
|
||||
ExportCreditBundles,
|
||||
ExportCustoms,
|
||||
ExportDrones,
|
||||
ExportEmailItems,
|
||||
@ -48,7 +49,6 @@ import {
|
||||
ExportFusionBundles,
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
ExportMisc,
|
||||
ExportRailjackWeapons,
|
||||
ExportRecipes,
|
||||
ExportResources,
|
||||
@ -631,8 +631,8 @@ export const addItem = async (
|
||||
};
|
||||
}
|
||||
}
|
||||
if (typeName in ExportMisc.creditBundles) {
|
||||
const creditsTotal = ExportMisc.creditBundles[typeName] * quantity;
|
||||
if (typeName in ExportCreditBundles) {
|
||||
const creditsTotal = ExportCreditBundles[typeName].credits * quantity;
|
||||
inventory.RegularCredits += creditsTotal;
|
||||
return {
|
||||
RegularCredits: creditsTotal
|
||||
@ -2160,7 +2160,7 @@ export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag,
|
||||
}
|
||||
};
|
||||
|
||||
export const addBooster = (ItemType: string, time: number, inventory: TInventoryDatabaseDocument): void => {
|
||||
export const addBooster = (ItemType: string, timeSecs: number, inventory: TInventoryDatabaseDocument): void => {
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
const { Boosters } = inventory;
|
||||
@ -2169,9 +2169,9 @@ export const addBooster = (ItemType: string, time: number, inventory: TInventory
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
const existingBooster = Boosters[itemIndex];
|
||||
existingBooster.ExpiryDate = Math.max(existingBooster.ExpiryDate, currentTime) + time;
|
||||
existingBooster.ExpiryDate = Math.max(existingBooster.ExpiryDate, currentTime) + timeSecs;
|
||||
} else {
|
||||
Boosters.push({ ItemType, ExpiryDate: currentTime + time });
|
||||
Boosters.push({ ItemType, ExpiryDate: currentTime + timeSecs });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -27,9 +27,11 @@ import {
|
||||
ExportBoosters,
|
||||
ExportBundles,
|
||||
ExportCustoms,
|
||||
ExportDojoRecipes,
|
||||
ExportDrones,
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
ExportRailjackWeapons,
|
||||
ExportRecipes,
|
||||
ExportResources,
|
||||
ExportSentinels,
|
||||
@ -149,6 +151,18 @@ export const getItemName = (uniqueName: string): string | undefined => {
|
||||
if (uniqueName in ExportWeapons) {
|
||||
return ExportWeapons[uniqueName].name;
|
||||
}
|
||||
if (uniqueName in ExportRailjackWeapons) {
|
||||
return ExportRailjackWeapons[uniqueName].name;
|
||||
}
|
||||
if (uniqueName in ExportDojoRecipes.colours) {
|
||||
return ExportDojoRecipes.colours[uniqueName].name;
|
||||
}
|
||||
if (uniqueName in ExportDojoRecipes.backdrops) {
|
||||
return ExportDojoRecipes.backdrops[uniqueName].name;
|
||||
}
|
||||
if (uniqueName in ExportDojoRecipes.decos) {
|
||||
return ExportDojoRecipes.decos[uniqueName].name;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -309,3 +323,13 @@ export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefin
|
||||
}
|
||||
return allDefaultUpgrades.length == 0 ? undefined : allDefaultUpgrades;
|
||||
};
|
||||
|
||||
export const getMaxLevelCap = (type: string): number => {
|
||||
if (type in ExportWarframes) {
|
||||
return ExportWarframes[type].maxLevelCap ?? 30;
|
||||
}
|
||||
if (type in ExportWeapons) {
|
||||
return ExportWeapons[type].maxLevelCap ?? 30;
|
||||
}
|
||||
return 30;
|
||||
};
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
import type { IMissionReward as IMissionRewardExternal, IRegion, IReward } from "warframe-public-export-plus";
|
||||
import type {
|
||||
IMissionReward as IMissionRewardExternal,
|
||||
IRegion,
|
||||
IReward,
|
||||
TMissionType
|
||||
} from "warframe-public-export-plus";
|
||||
import {
|
||||
ExportAnimals,
|
||||
ExportEnemies,
|
||||
ExportFusionBundles,
|
||||
ExportRegions,
|
||||
@ -56,7 +62,6 @@ import { createMessage } from "./inboxService.ts";
|
||||
import kuriaMessage50 from "../../static/fixed_responses/kuriaMessages/fiftyPercent.json" with { type: "json" };
|
||||
import kuriaMessage75 from "../../static/fixed_responses/kuriaMessages/seventyFivePercent.json" with { type: "json" };
|
||||
import kuriaMessage100 from "../../static/fixed_responses/kuriaMessages/oneHundredPercent.json" with { type: "json" };
|
||||
import conservationAnimals from "../../static/fixed_responses/conservationAnimals.json" with { type: "json" };
|
||||
import {
|
||||
generateNemesisProfile,
|
||||
getInfestedLichItemRewards,
|
||||
@ -87,11 +92,6 @@ import { handleGuildGoalProgress } from "./guildService.ts";
|
||||
import { importLoadOutConfig } from "./importService.ts";
|
||||
|
||||
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
|
||||
// Disruption missions just tell us (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2599)
|
||||
if (rewardInfo.rewardTierOverrides) {
|
||||
return rewardInfo.rewardTierOverrides;
|
||||
}
|
||||
|
||||
// For Spy missions, e.g. 3 vaults cracked = A, B, C
|
||||
if (rewardInfo.VaultsCracked) {
|
||||
const rotations: number[] = [];
|
||||
@ -102,19 +102,28 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
||||
}
|
||||
|
||||
const region = ExportRegions[rewardInfo.node] as IRegion | undefined;
|
||||
const missionIndex: number | undefined = region?.missionIndex;
|
||||
const missionType: TMissionType | undefined = region?.missionType;
|
||||
|
||||
// For Rescue missions
|
||||
if (missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
// Disruption uses 'rewardTierOverrides' to tell us (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2599)
|
||||
// Note that this may stick in lab conquest so we need to filter by mission type (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2768)
|
||||
if (missionType == "MT_ARTIFACT") {
|
||||
return rewardInfo.rewardTierOverrides ?? [];
|
||||
}
|
||||
|
||||
if (missionType == "MT_RESCUE" && rewardInfo.rewardTier !== undefined) {
|
||||
return [rewardInfo.rewardTier];
|
||||
}
|
||||
|
||||
// 'rewardQualifications' is unreliable for non-endless railjack missions (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2586, https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2612)
|
||||
// 'rewardQualifications' may stick from previous missions for non-endless missions done after them
|
||||
// - via railjack (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2586, https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2612)
|
||||
// - via lab conquest (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2768)
|
||||
switch (region?.missionName) {
|
||||
case "/Lotus/Language/Missions/MissionName_Railjack":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackVolatile":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackExterminate":
|
||||
case "/Lotus/Language/Missions/MissionName_RailjackAssassinate":
|
||||
case "/Lotus/Language/Missions/MissionName_Assassination":
|
||||
case "/Lotus/Language/Missions/MissionName_Exterminate":
|
||||
return [0];
|
||||
}
|
||||
|
||||
@ -123,7 +132,7 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
||||
// Empty or absent rewardQualifications should not give rewards when:
|
||||
// - Completing only 1 zone of (E)SO (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||
if (rotationCount == 0 && missionIndex != 30 && missionIndex != 32) {
|
||||
if (rotationCount == 0 && missionType != "MT_ENDLESS_EXTERMINATION" && missionType != "MT_RAILJACK") {
|
||||
return [0];
|
||||
}
|
||||
|
||||
@ -510,22 +519,23 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
case "CapturedAnimals": {
|
||||
for (const capturedAnimal of value) {
|
||||
const meta = conservationAnimals[capturedAnimal.AnimalType as keyof typeof conservationAnimals];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const meta = ExportAnimals[capturedAnimal.AnimalType]?.conservation;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (meta) {
|
||||
if (capturedAnimal.NumTags) {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: meta.tag,
|
||||
ItemType: meta.itemReward,
|
||||
ItemCount: capturedAnimal.NumTags
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (capturedAnimal.NumExtraRewards) {
|
||||
if ("extraReward" in meta) {
|
||||
if (meta.woundedAnimalReward) {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
ItemType: meta.extraReward,
|
||||
ItemType: meta.woundedAnimalReward,
|
||||
ItemCount: capturedAnimal.NumExtraRewards
|
||||
}
|
||||
]);
|
||||
@ -535,6 +545,12 @@ export const addMissionInventoryUpdates = async (
|
||||
);
|
||||
}
|
||||
}
|
||||
if (meta.standingReward) {
|
||||
const syndicateTag =
|
||||
inventoryUpdates.Missions!.Tag == "SolNode129" ? "SolarisSyndicate" : "CetusSyndicate";
|
||||
logger.debug(`adding ${meta.standingReward} standing to ${syndicateTag} for conservation`);
|
||||
addStanding(inventory, syndicateTag, meta.standingReward);
|
||||
}
|
||||
} else {
|
||||
logger.warn(`ignoring conservation of unknown AnimalType: ${capturedAnimal.AnimalType}`);
|
||||
}
|
||||
@ -1095,8 +1111,8 @@ const isEligibleForCreditReward = (rewardInfo: IRewardInfo, missions: IMission,
|
||||
}
|
||||
// The rest here might not be needed anymore, but just to be sure we don't give undue credits...
|
||||
return (
|
||||
node.missionIndex != 23 && // junction
|
||||
node.missionIndex != 28 && // open world
|
||||
node.missionType != "MT_JUNCTION" &&
|
||||
node.missionType != "MT_LANDSCAPE" &&
|
||||
missions.Tag != "SolNode761" && // the index
|
||||
missions.Tag != "SolNode762" && // the index
|
||||
missions.Tag != "SolNode763" && // the index
|
||||
@ -1792,14 +1808,14 @@ function getRandomMissionDrops(
|
||||
let rewardManifests: string[];
|
||||
if (RewardInfo.periodicMissionTag == "EliteAlert" || RewardInfo.periodicMissionTag == "EliteAlertB") {
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/EliteAlertMissionRewards/EliteAlertMissionRewards"];
|
||||
} else if (RewardInfo.invasionId && region.missionIndex == 0) {
|
||||
} else if (RewardInfo.invasionId && region.missionType == "MT_ASSASSINATION") {
|
||||
// Invasion assassination has Phorid has the boss who should drop Nyx parts
|
||||
// TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
|
||||
} else if (RewardInfo.sortieId) {
|
||||
// Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes.
|
||||
// Assassinations in non-lite sorties are an exception to this.
|
||||
if (region.missionIndex == 0) {
|
||||
if (region.missionType == "MT_ASSASSINATION") {
|
||||
const arr = RewardInfo.sortieId.split("_");
|
||||
let giveNodeReward = false;
|
||||
if (arr[1] != "Lite") {
|
||||
@ -2165,7 +2181,7 @@ function getRandomMissionDrops(
|
||||
const deck = ExportRewards["/Lotus/Types/Game/MissionDecks/NightmareModeRewards"];
|
||||
let rotation = 0;
|
||||
|
||||
if (region.missionIndex === 3 && RewardInfo.rewardTier) {
|
||||
if (region.missionType == "MT_RESCUE" && RewardInfo.rewardTier) {
|
||||
rotation = RewardInfo.rewardTier;
|
||||
} else if ([6, 7, 8, 10, 11].includes(region.systemIndex)) {
|
||||
rotation = 2;
|
||||
|
||||
@ -20,13 +20,12 @@ import type {
|
||||
import { PurchaseSource } from "../types/purchaseTypes.ts";
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import { getWorldState } from "./worldStateService.ts";
|
||||
import type { TRarity } from "warframe-public-export-plus";
|
||||
import {
|
||||
ExportBoosterPacks,
|
||||
ExportBoosters,
|
||||
ExportBundles,
|
||||
ExportCreditBundles,
|
||||
ExportGear,
|
||||
ExportMisc,
|
||||
ExportResources,
|
||||
ExportSyndicates,
|
||||
ExportVendors
|
||||
@ -419,7 +418,7 @@ export const handleBundleAcqusition = async (
|
||||
component.typeName,
|
||||
inventory,
|
||||
component.purchaseQuantity * quantity,
|
||||
component.durability,
|
||||
component.durabilityDays,
|
||||
true
|
||||
)
|
||||
).InventoryChanges
|
||||
@ -432,7 +431,7 @@ export const handleStoreItemAcquisition = async (
|
||||
storeItemName: string,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
quantity: number = 1,
|
||||
durability: TRarity = "COMMON",
|
||||
durabilityDays: number = 3,
|
||||
ignorePurchaseQuantity: boolean = false,
|
||||
premiumPurchase: boolean = true,
|
||||
seed?: bigint
|
||||
@ -482,7 +481,7 @@ export const handleStoreItemAcquisition = async (
|
||||
);
|
||||
break;
|
||||
case "Boosters":
|
||||
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
||||
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durabilityDays);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -632,8 +631,8 @@ const handleCreditBundlePurchase = async (
|
||||
typeName: string,
|
||||
inventory: TInventoryDatabaseDocument
|
||||
): Promise<IPurchaseResponse> => {
|
||||
if (typeName && typeName in ExportMisc.creditBundles) {
|
||||
const creditsAmount = ExportMisc.creditBundles[typeName];
|
||||
if (typeName && typeName in ExportCreditBundles) {
|
||||
const creditsAmount = ExportCreditBundles[typeName].credits;
|
||||
|
||||
inventory.RegularCredits += creditsAmount;
|
||||
await inventory.save();
|
||||
@ -672,7 +671,7 @@ const handleTypesPurchase = async (
|
||||
const handleBoostersPurchase = (
|
||||
boosterStoreName: string,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
durability: TRarity
|
||||
durabilityDays: number
|
||||
): { InventoryChanges: IInventoryChanges } => {
|
||||
if (!(boosterStoreName in ExportBoosters)) {
|
||||
logger.error(`unknown booster type: ${boosterStoreName}`);
|
||||
@ -680,7 +679,7 @@ const handleBoostersPurchase = (
|
||||
}
|
||||
|
||||
const ItemType = ExportBoosters[boosterStoreName].typeName;
|
||||
const ExpiryDate = ExportMisc.boosterDurations[durability];
|
||||
const ExpiryDate = durabilityDays * 86400;
|
||||
|
||||
addBooster(ItemType, ExpiryDate, inventory);
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import { addItem, addItems, addKeyChainItems, setupKahlSyndicate } from "./inven
|
||||
import { fromStoreItem, getKeyChainMessage, getLevelKeyRewards } from "./itemDataService.ts";
|
||||
import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import type { Types } from "mongoose";
|
||||
import { ExportKeys } from "warframe-public-export-plus";
|
||||
import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts";
|
||||
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
|
||||
@ -44,7 +43,12 @@ export const updateQuestKey = async (
|
||||
inventory.QuestKeys[questKeyIndex].CompletionDate = new Date();
|
||||
|
||||
const questKey = questKeyUpdate[0].ItemType;
|
||||
await handleQuestCompletion(inventory, questKey, inventoryChanges);
|
||||
await handleQuestCompletion(
|
||||
inventory,
|
||||
questKey,
|
||||
inventoryChanges,
|
||||
(questKeyUpdate[0].Progress?.[0]?.c ?? 0) > 0
|
||||
);
|
||||
}
|
||||
return inventoryChanges;
|
||||
};
|
||||
@ -52,7 +56,7 @@ export const updateQuestKey = async (
|
||||
export const updateQuestStage = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
{ KeyChain, ChainStage }: IKeyChainRequest,
|
||||
questStageUpdate: IQuestStage
|
||||
questStageUpdate: Partial<IQuestStage>
|
||||
): void => {
|
||||
const quest = inventory.QuestKeys.find(quest => quest.ItemType === KeyChain);
|
||||
|
||||
@ -68,14 +72,22 @@ export const updateQuestStage = (
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!questStage) {
|
||||
const questStageIndex = quest.Progress.push(questStageUpdate) - 1;
|
||||
const questStageIndex =
|
||||
quest.Progress.push({
|
||||
c: questStageUpdate.c ?? 0,
|
||||
i: questStageUpdate.i ?? false,
|
||||
m: questStageUpdate.m ?? false,
|
||||
b: questStageUpdate.b ?? []
|
||||
}) - 1;
|
||||
if (questStageIndex !== ChainStage) {
|
||||
throw new Error(`Quest stage index mismatch: ${questStageIndex} !== ${ChainStage}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(questStage, questStageUpdate);
|
||||
for (const [key, value] of Object.entries(questStageUpdate) as [keyof IQuestStage, number | boolean | any[]][]) {
|
||||
(questStage[key] as any) = value;
|
||||
}
|
||||
};
|
||||
|
||||
export const addQuestKey = (
|
||||
@ -112,58 +124,53 @@ export const completeQuest = async (inventory: TInventoryDatabaseDocument, quest
|
||||
}
|
||||
|
||||
const chainStageTotal = chainStages.length;
|
||||
let existingQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
|
||||
|
||||
const existingQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
|
||||
|
||||
const startingStage = Math.max((existingQuestKey?.Progress?.length ?? 0) - 1, 0);
|
||||
|
||||
if (existingQuestKey?.Completed) {
|
||||
return;
|
||||
}
|
||||
if (existingQuestKey) {
|
||||
existingQuestKey.Progress = existingQuestKey.Progress ?? [];
|
||||
|
||||
const existingProgressLength = existingQuestKey.Progress.length;
|
||||
|
||||
if (existingProgressLength < chainStageTotal) {
|
||||
const missingProgress: IQuestStage[] = Array.from(
|
||||
{ length: chainStageTotal - existingProgressLength },
|
||||
() =>
|
||||
({
|
||||
c: 0,
|
||||
i: false,
|
||||
m: false,
|
||||
b: []
|
||||
}) as IQuestStage
|
||||
);
|
||||
|
||||
existingQuestKey.Progress.push(...missingProgress);
|
||||
existingQuestKey.CompletionDate = new Date();
|
||||
existingQuestKey.Completed = true;
|
||||
}
|
||||
} else {
|
||||
if (!existingQuestKey) {
|
||||
const completedQuestKey: IQuestKeyDatabase = {
|
||||
ItemType: questKey,
|
||||
Completed: true,
|
||||
Completed: false,
|
||||
unlock: true,
|
||||
Progress: Array(chainStageTotal).fill({
|
||||
Progress: Array.from({ length: chainStageTotal }, () => ({
|
||||
c: 0,
|
||||
i: false,
|
||||
m: false,
|
||||
b: []
|
||||
} satisfies IQuestStage),
|
||||
CompletionDate: new Date()
|
||||
}))
|
||||
};
|
||||
addQuestKey(inventory, completedQuestKey);
|
||||
existingQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey)!;
|
||||
} else if (existingQuestKey.Completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = startingStage; i < chainStageTotal; i++) {
|
||||
await giveKeyChainStageTriggered(inventory, { KeyChain: questKey, ChainStage: i });
|
||||
existingQuestKey.Progress = existingQuestKey.Progress ?? [];
|
||||
|
||||
await giveKeyChainMissionReward(inventory, { KeyChain: questKey, ChainStage: i });
|
||||
const run = existingQuestKey.Progress[0]?.c ?? 0;
|
||||
|
||||
const existingProgressLength = existingQuestKey.Progress.length;
|
||||
if (existingProgressLength < chainStageTotal) {
|
||||
const missingProgress: IQuestStage[] = Array.from(
|
||||
{ length: chainStageTotal - existingProgressLength },
|
||||
() => ({ c: run, i: false, m: false, b: [] }) as IQuestStage
|
||||
);
|
||||
existingQuestKey.Progress.push(...missingProgress);
|
||||
}
|
||||
|
||||
await handleQuestCompletion(inventory, questKey);
|
||||
for (let i = 0; i < chainStageTotal; i++) {
|
||||
const stage = existingQuestKey.Progress[i];
|
||||
if (stage.c < run) {
|
||||
stage.c = run;
|
||||
await giveKeyChainStageTriggered(inventory, { KeyChain: questKey, ChainStage: i });
|
||||
await giveKeyChainMissionReward(inventory, { KeyChain: questKey, ChainStage: i });
|
||||
}
|
||||
}
|
||||
|
||||
if (existingQuestKey.Progress.every(p => p.c == run)) {
|
||||
existingQuestKey.Completed = true;
|
||||
existingQuestKey.CompletionDate = new Date();
|
||||
await handleQuestCompletion(inventory, questKey, undefined, run > 0);
|
||||
}
|
||||
};
|
||||
|
||||
const getQuestCompletionItems = (questKey: string): ITypeCount[] | undefined => {
|
||||
@ -214,28 +221,35 @@ const doesQuestCompletionFinishSet = (
|
||||
const handleQuestCompletion = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
questKey: string,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
isRerun: boolean = false
|
||||
): Promise<void> => {
|
||||
logger.debug(`completed quest ${questKey}`);
|
||||
|
||||
if (inventory.ActiveQuest == questKey) inventory.ActiveQuest = "";
|
||||
if (questKey == "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain") {
|
||||
const att = isRerun
|
||||
? []
|
||||
: [
|
||||
"/Lotus/Weapons/Tenno/Melee/Swords/StalkerTwo/StalkerTwoSmallSword",
|
||||
"/Lotus/Upgrades/Skins/Sigils/ScarSigil"
|
||||
];
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/G1Quests/SecondDreamFinishInboxMessage",
|
||||
att: [
|
||||
"/Lotus/Weapons/Tenno/Melee/Swords/StalkerTwo/StalkerTwoSmallSword",
|
||||
"/Lotus/Upgrades/Skins/Sigils/ScarSigil"
|
||||
],
|
||||
att,
|
||||
sub: "/Lotus/Language/G1Quests/SecondDreamFinishInboxTitle",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
highPriority: true
|
||||
}
|
||||
]);
|
||||
} else if (questKey == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain") {
|
||||
} else if (questKey == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain" && !isRerun) {
|
||||
setupKahlSyndicate(inventory);
|
||||
}
|
||||
|
||||
if (isRerun) return;
|
||||
|
||||
// Whispers in the Walls is unlocked once The New War + Heart of Deimos are completed.
|
||||
if (
|
||||
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||
@ -279,21 +293,24 @@ const handleQuestCompletion = async (
|
||||
if (questCompletionItems) {
|
||||
await addItems(inventory, questCompletionItems, inventoryChanges);
|
||||
}
|
||||
|
||||
if (inventory.ActiveQuest == questKey) inventory.ActiveQuest = "";
|
||||
};
|
||||
|
||||
export const giveKeyChainItem = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
keyChainInfo: IKeyChainRequest
|
||||
keyChainInfo: IKeyChainRequest,
|
||||
isRerun: boolean = false
|
||||
): Promise<IInventoryChanges> => {
|
||||
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
||||
let inventoryChanges: IInventoryChanges = {};
|
||||
|
||||
if (isEmptyObject(inventoryChanges)) {
|
||||
logger.warn("inventory changes was empty after getting keychain items: should not happen");
|
||||
if (!isRerun) {
|
||||
inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
||||
|
||||
if (isEmptyObject(inventoryChanges)) {
|
||||
logger.warn("inventory changes was empty after getting keychain items: should not happen");
|
||||
}
|
||||
// items were added: update quest stage's i (item was given)
|
||||
updateQuestStage(inventory, keyChainInfo, { i: true });
|
||||
}
|
||||
// items were added: update quest stage's i (item was given)
|
||||
updateQuestStage(inventory, keyChainInfo, { i: true });
|
||||
|
||||
return inventoryChanges;
|
||||
|
||||
@ -309,12 +326,17 @@ export const giveKeyChainItem = async (
|
||||
|
||||
export const giveKeyChainMessage = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
accountId: string | Types.ObjectId,
|
||||
keyChainInfo: IKeyChainRequest
|
||||
keyChainInfo: IKeyChainRequest,
|
||||
isRerun: boolean = false
|
||||
): Promise<void> => {
|
||||
const keyChainMessage = getKeyChainMessage(keyChainInfo);
|
||||
|
||||
await createMessage(accountId, [keyChainMessage]);
|
||||
if (!isRerun) {
|
||||
keyChainMessage.att = [];
|
||||
keyChainMessage.countedAtt = [];
|
||||
}
|
||||
|
||||
await createMessage(inventory.accountOwnerId, [keyChainMessage]);
|
||||
|
||||
updateQuestStage(inventory, keyChainInfo, { m: true });
|
||||
};
|
||||
@ -328,8 +350,10 @@ export const giveKeyChainMissionReward = async (
|
||||
|
||||
if (chainStages) {
|
||||
const missionName = chainStages[keyChainInfo.ChainStage].key;
|
||||
if (missionName) {
|
||||
const questKey = inventory.QuestKeys.find(q => q.ItemType === keyChainInfo.KeyChain);
|
||||
if (missionName && questKey) {
|
||||
const fixedLevelRewards = getLevelKeyRewards(missionName);
|
||||
const run = questKey.Progress?.[0]?.c ?? 0;
|
||||
if (fixedLevelRewards.levelKeyRewards) {
|
||||
const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
|
||||
inventory.RegularCredits += addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, missionRewards);
|
||||
@ -338,7 +362,7 @@ export const giveKeyChainMissionReward = async (
|
||||
await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
|
||||
}
|
||||
|
||||
updateQuestStage(inventory, keyChainInfo, { c: 0 });
|
||||
updateQuestStage(inventory, keyChainInfo, { c: run });
|
||||
} else if (fixedLevelRewards.levelKeyRewards2) {
|
||||
for (const reward of fixedLevelRewards.levelKeyRewards2) {
|
||||
if (reward.rewardType == "RT_CREDITS") {
|
||||
@ -352,7 +376,7 @@ export const giveKeyChainMissionReward = async (
|
||||
}
|
||||
}
|
||||
|
||||
updateQuestStage(inventory, keyChainInfo, { c: 0 });
|
||||
updateQuestStage(inventory, keyChainInfo, { c: run });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,14 +388,17 @@ export const giveKeyChainStageTriggered = async (
|
||||
): Promise<void> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const chainStages = ExportKeys[keyChainInfo.KeyChain]?.chainStages;
|
||||
const questKey = inventory.QuestKeys.find(qk => qk.ItemType === keyChainInfo.KeyChain);
|
||||
|
||||
if (chainStages && questKey) {
|
||||
const run = questKey.Progress?.[0]?.c ?? 0;
|
||||
|
||||
if (chainStages) {
|
||||
if (chainStages[keyChainInfo.ChainStage].itemsToGiveWhenTriggered.length > 0) {
|
||||
await giveKeyChainItem(inventory, keyChainInfo);
|
||||
await giveKeyChainItem(inventory, keyChainInfo, run > 0);
|
||||
}
|
||||
|
||||
if (chainStages[keyChainInfo.ChainStage].messageToSendWhenTriggered) {
|
||||
await giveKeyChainMessage(inventory, inventory.accountOwnerId, keyChainInfo);
|
||||
await giveKeyChainMessage(inventory, keyChainInfo, run > 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -13,8 +13,8 @@ import { buildConfig } from "./buildConfigService.ts";
|
||||
import { unixTimesInMs } from "../constants/timeConstants.ts";
|
||||
import { config } from "./configService.ts";
|
||||
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "./rngService.ts";
|
||||
import type { IMissionReward, IRegion } from "warframe-public-export-plus";
|
||||
import { eMissionType, ExportRegions, ExportSyndicates } from "warframe-public-export-plus";
|
||||
import type { IMissionReward, IRegion, TFaction } from "warframe-public-export-plus";
|
||||
import { ExportRegions, ExportSyndicates } from "warframe-public-export-plus";
|
||||
import type {
|
||||
ICalendarDay,
|
||||
ICalendarEvent,
|
||||
@ -87,11 +87,11 @@ const sortieFactionToSystemIndexes: Record<string, number[]> = {
|
||||
FC_OROKIN: [14]
|
||||
};
|
||||
|
||||
const sortieFactionToFactionIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0],
|
||||
FC_CORPUS: [1],
|
||||
FC_INFESTATION: [0, 1, 2],
|
||||
FC_OROKIN: [3]
|
||||
const sortieFactionToFactions: Record<string, TFaction[]> = {
|
||||
FC_GRINEER: ["FC_GRINEER"],
|
||||
FC_CORPUS: ["FC_CORPUS"],
|
||||
FC_INFESTATION: ["FC_GRINEER", "FC_CORPUS", "FC_INFESTATION"],
|
||||
FC_OROKIN: ["FC_OROKIN"]
|
||||
};
|
||||
|
||||
const sortieBossNode: Record<Exclude<TSortieBoss, "SORTIE_BOSS_CORRUPTED_VOR">, string> = {
|
||||
@ -271,7 +271,7 @@ export const getSortie = (day: number): ISortie => {
|
||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||
if (
|
||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
||||
sortieFactionToFactions[sortieBossToFaction[boss]].includes(value.faction!) &&
|
||||
key in sortieTilesets &&
|
||||
(key != "SolNode228" || sortieBossToFaction[boss] == "FC_GRINEER") // PoE does not work for non-infested enemies
|
||||
) {
|
||||
@ -339,10 +339,10 @@ export const getSortie = (day: number): ISortie => {
|
||||
modifiers.push("SORTIE_MODIFIER_HAZARD_RADIATION");
|
||||
}
|
||||
|
||||
if (ExportRegions[node].factionIndex == 0) {
|
||||
if (ExportRegions[node].faction == "FC_GRINEER") {
|
||||
// Grineer
|
||||
modifiers.push("SORTIE_MODIFIER_ARMOR");
|
||||
} else if (ExportRegions[node].factionIndex == 1) {
|
||||
} else if (ExportRegions[node].faction == "FC_CORPUS") {
|
||||
// Corpus
|
||||
modifiers.push("SORTIE_MODIFIER_SHIELDS");
|
||||
}
|
||||
@ -1306,7 +1306,7 @@ const createInvasion = (day: number, idx: number): IInvasion => {
|
||||
),
|
||||
Goal: 30000, // Value seems to range from 30000 to 98000 in intervals of 1000. Higher values are increasingly rare. I don't think this is relevant for the frontend besides dividing count by it.
|
||||
LocTag: isInfestationOutbreak
|
||||
? ExportRegions[node].missionIndex == 0
|
||||
? ExportRegions[node].missionType == "MT_ASSASSINATION"
|
||||
? "/Lotus/Language/Menu/InfestedInvasionBoss"
|
||||
: "/Lotus/Language/Menu/InfestedInvasionGeneric"
|
||||
: attacker == "FC_CORPUS"
|
||||
@ -3179,7 +3179,7 @@ export const populateFissures = async (worldState: IWorldState): Promise<void> =
|
||||
Activation: { $date: { $numberLong: "1000000000000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
Node: node,
|
||||
MissionType: eMissionType[meta.missionIndex].tag,
|
||||
MissionType: meta.missionType,
|
||||
Modifier: tier,
|
||||
Hard: config.worldState.allTheFissures == "hard"
|
||||
});
|
||||
@ -3199,7 +3199,7 @@ export const populateFissures = async (worldState: IWorldState): Promise<void> =
|
||||
: toMongoDate(fissure.Activation),
|
||||
Expiry: toMongoDate(fissure.Expiry),
|
||||
Node: fissure.Node,
|
||||
MissionType: eMissionType[meta.missionIndex].tag,
|
||||
MissionType: meta.missionType,
|
||||
Modifier: fissure.Modifier,
|
||||
Hard: fissure.Hard
|
||||
});
|
||||
@ -3246,13 +3246,12 @@ export const getLiteSortie = (week: number): ILiteSortie => {
|
||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||
if (
|
||||
value.systemIndex === systemIndex &&
|
||||
value.factionIndex !== undefined &&
|
||||
value.factionIndex < 2 &&
|
||||
(value.faction == "FC_GRINEER" || value.faction == "FC_CORPUS") &&
|
||||
!isArchwingMission(value) &&
|
||||
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
|
||||
value.missionIndex != 23 && // Exclude junctions
|
||||
value.missionIndex != 28 && // Exclude open worlds
|
||||
value.missionIndex != 32 // Exclude railjack
|
||||
value.missionType != "MT_ASSASSINATION" &&
|
||||
value.missionType != "MT_JUNCTION" &&
|
||||
value.missionType != "MT_LANDSCAPE" &&
|
||||
value.missionType != "MT_RAILJACK"
|
||||
) {
|
||||
nodes.push(key);
|
||||
}
|
||||
@ -3309,7 +3308,7 @@ export const isArchwingMission = (node: IRegion): boolean => {
|
||||
return true;
|
||||
}
|
||||
// SettlementNode10
|
||||
if (node.missionIndex == 25) {
|
||||
if (node.missionType == "MT_RACE") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import type http from "http";
|
||||
import type https from "https";
|
||||
import type { default as ws } from "ws";
|
||||
import type { WebSocket } from "ws";
|
||||
import { WebSocketServer } from "ws";
|
||||
import { Account } from "../models/loginModel.ts";
|
||||
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "./loginService.ts";
|
||||
import type { IDatabaseAccountJson } from "../types/loginTypes.ts";
|
||||
import type { HydratedDocument } from "mongoose";
|
||||
import { logError } from "../utils/logger.ts";
|
||||
import { logError, logger } from "../utils/logger.ts";
|
||||
import type { Request } from "express";
|
||||
|
||||
let wsServer: WebSocketServer | undefined;
|
||||
let wssServer: WebSocketServer | undefined;
|
||||
@ -44,9 +45,10 @@ export const stopWsServers = (promises: Promise<void>[]): void => {
|
||||
|
||||
let lastWsid: number = 0;
|
||||
|
||||
interface IWsCustomData extends ws {
|
||||
interface IWsCustomData extends WebSocket {
|
||||
id: number;
|
||||
accountId?: string;
|
||||
isGame?: boolean;
|
||||
}
|
||||
|
||||
interface IWsMsgFromClient {
|
||||
@ -55,11 +57,19 @@ interface IWsMsgFromClient {
|
||||
password: string;
|
||||
isRegister: boolean;
|
||||
};
|
||||
auth_game?: {
|
||||
accountId: string;
|
||||
nonce: number;
|
||||
};
|
||||
logout?: boolean;
|
||||
sync_inventory?: boolean;
|
||||
}
|
||||
|
||||
interface IWsMsgToClient {
|
||||
//wsid?: number;
|
||||
// common
|
||||
wsid?: number;
|
||||
|
||||
// to webui
|
||||
reload?: boolean;
|
||||
ports?: {
|
||||
http: number | undefined;
|
||||
@ -77,9 +87,13 @@ interface IWsMsgToClient {
|
||||
nonce_updated?: boolean;
|
||||
update_inventory?: boolean;
|
||||
logged_out?: boolean;
|
||||
have_game_ws?: boolean;
|
||||
|
||||
// to game
|
||||
sync_inventory?: boolean;
|
||||
}
|
||||
|
||||
const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
const wsOnConnect = (ws: WebSocket, req: http.IncomingMessage): void => {
|
||||
if (req.url == "/custom/selftest") {
|
||||
ws.send("SpaceNinjaServer");
|
||||
ws.close();
|
||||
@ -87,11 +101,12 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
}
|
||||
|
||||
(ws as IWsCustomData).id = ++lastWsid;
|
||||
ws.send(JSON.stringify({ wsid: lastWsid }));
|
||||
ws.send(JSON.stringify({ wsid: lastWsid } satisfies IWsMsgToClient));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on("message", async msg => {
|
||||
try {
|
||||
//console.log(String(msg));
|
||||
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||
if (data.auth) {
|
||||
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||
@ -124,7 +139,8 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
Nonce: account.Nonce
|
||||
}
|
||||
},
|
||||
have_game_ws: haveGameWs(account.id)
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
} else {
|
||||
@ -137,77 +153,152 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.auth_game) {
|
||||
(ws as IWsCustomData).isGame = true;
|
||||
if (data.auth_game.nonce) {
|
||||
const account: IDatabaseAccountJson | null = await Account.findOne({
|
||||
_id: data.auth_game.accountId,
|
||||
Nonce: data.auth_game.nonce
|
||||
});
|
||||
if (account) {
|
||||
(ws as IWsCustomData).accountId = account.id;
|
||||
logger.debug(`got bootstrapper connection for ${account.id}`);
|
||||
sendWsBroadcastToWebui({ have_game_ws: true }, account.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.logout) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
if (accountId) {
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.sync_inventory) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
if (accountId) {
|
||||
sendWsBroadcastToGame(accountId, { sync_inventory: true });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e as Error, `processing websocket message`);
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on("close", async () => {
|
||||
if ((ws as IWsCustomData).isGame && (ws as IWsCustomData).accountId) {
|
||||
logger.debug(`lost bootstrapper connection for ${(ws as IWsCustomData).accountId}`);
|
||||
sendWsBroadcastToWebui({ have_game_ws: false }, (ws as IWsCustomData).accountId);
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: (ws as IWsCustomData).accountId
|
||||
},
|
||||
{
|
||||
Dropped: true
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const forEachClient = (cb: (client: IWsCustomData) => void): void => {
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
cb(client as IWsCustomData);
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
cb(client as IWsCustomData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const haveGameWs = (accountId: string): boolean => {
|
||||
let ret = false;
|
||||
forEachClient(client => {
|
||||
if (client.isGame && client.accountId == accountId) {
|
||||
ret = true;
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const sendWsBroadcast = (data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
forEachClient(client => {
|
||||
client.send(msg);
|
||||
});
|
||||
};
|
||||
|
||||
export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
if ((client as IWsCustomData).accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
forEachClient(client => {
|
||||
if (client.accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
if ((client as IWsCustomData).accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const sendWsBroadcastToGame = (accountId: string, data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
if (client.isGame && client.accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
forEachClient(client => {
|
||||
if ((!accountId || client.accountId == accountId) && client.id != excludeWsid) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const sendWsBroadcastToWebui = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
if (!client.isGame && (!accountId || client.accountId == accountId) && client.id != excludeWsid) {
|
||||
client.send(msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const broadcastInventoryUpdate = (req: Request): void => {
|
||||
const accountId = req.query.accountId as string;
|
||||
if (req.query.wsid) {
|
||||
// for webui requests, let other tabs and the game know
|
||||
sendWsBroadcastEx(
|
||||
{ sync_inventory: true, update_inventory: true },
|
||||
accountId,
|
||||
parseInt(String(req.query.wsid))
|
||||
);
|
||||
} else {
|
||||
// for game requests, let all webui tabs know
|
||||
sendWsBroadcastToWebui({ update_inventory: true }, accountId, parseInt(String(req.query.wsid)));
|
||||
}
|
||||
};
|
||||
|
||||
export const handleNonceInvalidation = (accountId: string): void => {
|
||||
forEachClient(client => {
|
||||
if (client.accountId == accountId) {
|
||||
if (client.isGame) {
|
||||
client.accountId = undefined; // prevent processing of the close event
|
||||
client.close();
|
||||
} else {
|
||||
client.send(JSON.stringify({ nonce_updated: true, have_game_ws: false } satisfies IWsMsgToClient));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -31,8 +31,17 @@ export interface IGuildClient {
|
||||
|
||||
GoalProgress?: IGoalProgressClient[];
|
||||
}
|
||||
// Fields specific to SNS
|
||||
export interface IGuildCheats {
|
||||
noDojoRoomBuildStage?: boolean;
|
||||
noDojoDecoBuildStage?: boolean;
|
||||
fastDojoRoomDestruction?: boolean;
|
||||
noDojoResearchCosts?: boolean;
|
||||
noDojoResearchTime?: boolean;
|
||||
fastClanAscension?: boolean;
|
||||
}
|
||||
|
||||
export interface IGuildDatabase {
|
||||
export interface IGuildDatabase extends IGuildCheats {
|
||||
_id: Types.ObjectId;
|
||||
Name: string;
|
||||
MOTD: string;
|
||||
|
||||
@ -977,10 +977,10 @@ export interface IQuestKeyClient extends Omit<IQuestKeyDatabase, "CompletionDate
|
||||
}
|
||||
|
||||
export interface IQuestStage {
|
||||
c?: number;
|
||||
i?: boolean;
|
||||
m?: boolean;
|
||||
b?: any[];
|
||||
c: number;
|
||||
i: boolean;
|
||||
m: boolean;
|
||||
b: any[];
|
||||
}
|
||||
|
||||
export interface IRawUpgrade {
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
[
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AmbulasEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AmbulasEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AmbulasEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AmbulasEventTerracottaTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AridFearBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AridFearGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/AridFearSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/BreedingGroundsBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/BreedingGroundsGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/BreedingGroundsSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/CiceroCrisisBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/CiceroCrisisGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/CiceroCrisisSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DisruptionEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DisruptionEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DisruptionEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DisruptionEventTerracottaTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyBronzeARecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyGoldARecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyPlatinumARecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophySilverARecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventClayTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBaseTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventTerracottaTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EvacuationEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EvacuationEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EvacuationEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EvacuationEventTerracottaTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/EyesOfBlightTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/FalseProfitBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/FalseProfitClayTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/FalseProfitGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/FalseProfitSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/FusionMoaTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaCorpusBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaCorpusGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaCorpusSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaGrineerBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaGrineerGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/GradivusDilemmaGrineerSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/IcePlanetTrophyBronzeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/IcePlanetTrophyGoldRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/IcePlanetTrophySilverRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventBaseTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventPewterTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/JadeShadowsEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyBronzeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyGoldRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophySilverRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MechEventTrophyTerracottaRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MutalistIncursionBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MutalistIncursionGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/MutalistIncursionSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/OrokinMusicBoxRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/OrokinSabotageTrophyBronzeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/OrokinSabotageTrophyGoldRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/OrokinSabotageTrophySilverRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ProjectSinisterBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ProjectSinisterClayTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ProjectSinisterGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ProjectSinisterSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/RailjackResearchTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/RathuumBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/RathuumClayTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/RathuumGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/RathuumSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ShipyardsEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ShipyardsEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ShipyardsEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/SlingStoneTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/SpyDroneTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/SurvivalEventBronzeTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/SurvivalEventGoldTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/SurvivalEventSilverTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoConDojoGhostTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoConDojoMoonTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoConDojoMountainTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoConDojoShadowTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoConDojoStormTrophyRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyBronzeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyCrystalRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophyGoldRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophySilverRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/CorpusPlaceables/GasTurbineConeRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NaturalPlaceables/CoralChunkARecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoPlaceables/TnoBeaconEmitterRecipe",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronFemaleSitting",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronFemaleStanding",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronMaleStanding",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronMaleStandingTwo",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisForeman",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisHazard",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisStrikerOne",
|
||||
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisStrikerThree"
|
||||
]
|
||||
@ -1,491 +0,0 @@
|
||||
{
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/CommonBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/CommonFemaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/CommonMaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/RareBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/RareFemaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/RareMaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/UncommonBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/UncommonFemaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/BirdOfPrey/UncommonMaleBirdOfPreyAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagCondrocUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedCritter/BaseInfestedCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedCritter/CommonInfestedCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterCommon",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedCritterCommonRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedCritter/RareInfestedCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterRare",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedCritterRareRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedCritter/UncommonInfestedCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterUncommon",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedCritterUncommonRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedKDrive/GrottoInfKDriveAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedKdriveUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedKDrive/HighlandInfKDriveAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedKdriveRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedKDrive/SwampInfKDriveAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedKdriveCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMaggot/CommonInfestedMaggotAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMaggot/RareInfestedMaggotAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMaggot/UncommonInfestedMaggotAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMergoo/CommonInfestedMergooAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMergooCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMergoo/RareInfestedMergooAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMergooRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedMergoo/UncommonInfestedMergooAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMergooUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedNexifera/BaseInfestedNexiferaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedNexiferaCommon",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedNexifera/CommonInfestedNexiferaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedNexiferaCommon",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedNexifera/RareInfestedNexiferaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedNexiferaRare",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedNexifera/UncommonInfestedNexiferaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedNexiferaUncommon",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedPredator/BaseInfestedPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem",
|
||||
"extraReward": "/Lotus/Types/Items/Conservation/WoundedAnimalRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedPredator/CommonInfestedPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedPredatorCommonRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedPredator/RareInfestedPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorRare",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedPredatorRareRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedPredator/UncommonInfestedPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorUncommon",
|
||||
"extraReward": "/Lotus/Types/Items/Deimos/WoundedInfestedPredatorUncommonRewardItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedUndazoa/BaseUndazoaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedZongroCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedUndazoa/CommonUndazoaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedZongroCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedUndazoa/RareUndazoaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedZongroRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Deimos/InfestedUndazoa/UncommonUndazoaAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Deimos/AnimalTagInfestedZongroUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Duviri/Rabbit/BaseDuviriRabbitAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Duviri/Rabbit/TeshinRabbitAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Duviri/Rabbit/TeshinRabbitOnHandAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Duviri/Wolf/DuviriWolfAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/Duviri/Wolf/DuviriWolfConservationAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/CommonFemaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/CommonForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/CommonMaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/RareFemaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/RareForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/RareMaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/TutorialForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/UncommonFemaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/UncommonForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/ForestRodent/UncommonMaleForestRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagKuakaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/BaseLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/CommonFemaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/CommonLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/CommonMaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/CommonPupLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/RareFemaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/RareLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/RareMaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/RarePupLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/UncommonFemaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/UncommonLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/UncommonMaleLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/LegendaryKubrow/UncommonPupLegendaryKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagKubrodonUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/BaseOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/CommonFemaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/CommonMaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/CommonOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/CommonPupOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/RareFemaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/RareMaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/RareOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/RarePupOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/UncommonFemaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/UncommonMaleOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/UncommonOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OrokinKubrow/UncommonPupOrokinKubrowAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagStoverUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/CommonFemaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/CommonMaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/CommonOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/RareFemaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/RareMaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/RareOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/UncommonFemaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/UncommonMaleOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/OstronSeaBird/UncommonOstronSeaBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagMergooUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/BaseSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/CommonFemaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/CommonMaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/CommonPupSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/CommonSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/RareFemaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/RareMaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/RarePupSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/RareSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/UncommonFemaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/UncommonMaleSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/UncommonPupSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowArmadillo/UncommonSnowArmadilloAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagBolarolaUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/BaseSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/CommonFemaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/CommonMaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/CommonPupSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/CommonSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/RareFemaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/RareMaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/RarePupSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/RareSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/UncommonFemaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/UncommonMaleSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/UncommonPupSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowBird/UncommonSnowBirdAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagSawgawUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/BaseSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/CommonFemaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/CommonMaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/CommonPupSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/CommonSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/RareFemaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/RareMaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/RarePupSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/RareSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/UncommonFemaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/UncommonMaleSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/UncommonPupSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowCritter/UncommonSnowCritterAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagVirminkUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/BaseSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/CommonFemaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/CommonMaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/CommonPupSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/CommonSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/RareFemaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/RareMaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/RarePupSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/RareSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/UncommonFemaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/UncommonMaleSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/UncommonPupSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowPredator/UncommonSnowPredatorAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagHorrasqueUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/BaseSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/CommonFemaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/CommonMaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/CommonSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/RareFemaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/RareMaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/RareSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/UncommonFemaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/UncommonMaleSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/SnowRodent/UncommonSnowRodentAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Solaris/AnimalTagPobbersUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/BaseVampireKavatAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Conservation/ConservationTagItem"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/CommonVampireKavatAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/CommonVampireKavatCubAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/CommonVampireKavatFemaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/CommonVampireKavatMaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatCommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/RareVampireKavatAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/RareVampireKavatCubAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/RareVampireKavatFemaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/RareVampireKavatMaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatRare"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/UncommonVampireKavatAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/UncommonVampireKavatCubAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/UncommonVampireKavatFemaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatUncommon"
|
||||
},
|
||||
"/Lotus/Types/NeutralCreatures/Conservation/VampireKavat/UncommonVampireKavatMaleAvatar": {
|
||||
"tag": "/Lotus/Types/Items/Eidolon/AnimalTagVampireKavatUncommon"
|
||||
}
|
||||
}
|
||||
@ -71,6 +71,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/webui/import" data-bs-dismiss="offcanvas" data-bs-target="#sidebar" data-loc="navbar_import"></a>
|
||||
</li>
|
||||
<li class="nav-item d-none" id="nav-guildView">
|
||||
<a class="nav-link" href="/webui/guildView" data-bs-dismiss="offcanvas" data-bs-target="#sidebar" data-loc="navbar_guildView"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,7 +93,7 @@
|
||||
</form>
|
||||
</div>
|
||||
<div data-route="/webui/inventory" data-title="Inventory | OpenWF WebUI">
|
||||
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
|
||||
<p class="mb-3 inventory-update-note"></p>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-tabs">
|
||||
@ -480,6 +483,139 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="guild-route" data-route="/webui/guildView" data-title="Guild | OpenWF WebUI">
|
||||
<h3 id="guildView-loading" class="mb-0" data-loc="general_loading"></h3>
|
||||
<h3 id="guildView-title" class="mb-0"></h3>
|
||||
<p id="guildView-tier" class="text-body-secondary mb-0"></p>
|
||||
<p id="guildView-class" class="text-body-secondary mb-0"></p>
|
||||
<p id="guildView-alliance" class="text-body-secondary"></p>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header" data-loc="currency_RegularCredits"></h5>
|
||||
<div class="card-body">
|
||||
<p class="card-text" id="VaultRegularCredits-owned"></p>
|
||||
<form id="vaultRegularCredits-form" class="input-group d-none" onsubmit="doAddCurrency('VaultRegularCredits');return false;">
|
||||
<input class="form-control" id="VaultRegularCredits-delta" type="number" value="1000000" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header" data-loc="currency_PremiumCredits"></h5>
|
||||
<div class="card-body">
|
||||
<p class="card-text" id="VaultPremiumCredits-owned"></p>
|
||||
<form id="vaultPremiumCredits-form" class="input-group d-none" onsubmit="doAddCurrency('VaultPremiumCredits');return false;">
|
||||
<input class="form-control" id="VaultPremiumCredits-delta" type="number" value="100" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-6">
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="guildView_techProjects"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<form id="techProjects-form" class="input-group mb-3 d-none" onsubmit="addGuildTechProject();return false;">
|
||||
<input class="form-control" id="acquire-type-TechProjects" list="datalist-TechProjects" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="TechProjects-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="guildView_vaultDecoRecipes"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<form id="vaultDecoRecipes-form" class="input-group mb-3 d-none" onsubmit="addVaultDecoRecipe();return false;">
|
||||
<input class="form-control" id="acquire-type-VaultDecoRecipes" list="datalist-VaultDecoRecipes" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="VaultDecoRecipes-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-6">
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="guildView_members"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="Members-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="guildView_alliance"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="Alliance-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header" data-loc="general_bulkActions"></h5>
|
||||
<div class="card-body" id="guild-actions">
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingTechProjects);" data-loc="guildView_bulkAddTechProjects"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingVaultDecoRecipes);" data-loc="guildView_bulkAddVaultDecoRecipes"></button>
|
||||
</div>
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-success" onclick="debounce(fundAllTechProjects);" data-loc="guildView_bulkFundTechProjects"></button>
|
||||
<button class="btn btn-success" onclick="debounce(completeAllTechProjects);" data-loc="guildView_bulkCompleteTechProjects"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card mb-3">
|
||||
<h5 class="card-header" data-loc="guildView_cheats"></h5>
|
||||
<div class="card-body" id="guild-cheats">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoRoomBuildStage" />
|
||||
<label class="form-check-label" for="noDojoRoomBuildStage" data-loc="cheats_noDojoRoomBuildStage"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoDecoBuildStage" />
|
||||
<label class="form-check-label" for="noDojoDecoBuildStage" data-loc="cheats_noDojoDecoBuildStage"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoResearchCosts" />
|
||||
<label class="form-check-label" for="noDojoResearchCosts" data-loc="cheats_noDojoResearchCosts"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoResearchTime" />
|
||||
<label class="form-check-label" for="noDojoResearchTime" data-loc="cheats_noDojoResearchTime"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fastDojoRoomDestruction" />
|
||||
<label class="form-check-label" for="fastDojoRoomDestruction" data-loc="cheats_fastDojoRoomDestruction"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fastClanAscension" />
|
||||
<label class="form-check-label" for="fastClanAscension" data-loc="cheats_fastClanAscension"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="detailedView-route" data-route="/webui/detailedView" data-title="Inventory | OpenWF WebUI">
|
||||
<h3 id="detailedView-loading" class="mb-0" data-loc="general_loading"></h3>
|
||||
<h3 id="detailedView-title" class="mb-0"></h3>
|
||||
@ -579,7 +715,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div data-route="/webui/mods" data-title="Mods | OpenWF WebUI">
|
||||
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
|
||||
<p class="mb-3 inventory-update-note"></p>
|
||||
<div class="row g-3">
|
||||
<div class="col-xxl-6">
|
||||
<div class="card mb-3">
|
||||
@ -635,7 +771,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div data-route="/webui/quests" data-title="Quests | OpenWF WebUI">
|
||||
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
|
||||
<p class="mb-3 inventory-update-note"></p>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
@ -855,10 +991,6 @@
|
||||
<input class="form-check-input" type="checkbox" id="unlockAllSkins" />
|
||||
<label class="form-check-label" for="unlockAllSkins" data-loc="cheats_unlockAllSkins"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="unlockAllDecoRecipes" />
|
||||
<label class="form-check-label" for="unlockAllDecoRecipes" data-loc="cheats_unlockAllDecoRecipes"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fullyStockedVendors" />
|
||||
<label class="form-check-label" for="fullyStockedVendors" data-loc="cheats_fullyStockedVendors"></label>
|
||||
@ -867,30 +999,6 @@
|
||||
<input class="form-check-input" type="checkbox" id="skipClanKeyCrafting" />
|
||||
<label class="form-check-label" for="skipClanKeyCrafting" data-loc="cheats_skipClanKeyCrafting"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoRoomBuildStage" />
|
||||
<label class="form-check-label" for="noDojoRoomBuildStage" data-loc="cheats_noDojoRoomBuildStage"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoDecoBuildStage" />
|
||||
<label class="form-check-label" for="noDojoDecoBuildStage" data-loc="cheats_noDojoDecoBuildStage"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fastDojoRoomDestruction" />
|
||||
<label class="form-check-label" for="fastDojoRoomDestruction" data-loc="cheats_fastDojoRoomDestruction"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoResearchCosts" />
|
||||
<label class="form-check-label" for="noDojoResearchCosts" data-loc="cheats_noDojoResearchCosts"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoResearchTime" />
|
||||
<label class="form-check-label" for="noDojoResearchTime" data-loc="cheats_noDojoResearchTime"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fastClanAscension" />
|
||||
<label class="form-check-label" for="fastClanAscension" data-loc="cheats_fastClanAscension"></label>
|
||||
</div>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;">
|
||||
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
|
||||
<div class="input-group">
|
||||
@ -1277,6 +1385,8 @@
|
||||
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
|
||||
<datalist id="datalist-Boosters"></datalist>
|
||||
<datalist id="datalist-Abilities"></datalist>
|
||||
<datalist id="datalist-TechProjects"></datalist>
|
||||
<datalist id="datalist-VaultDecoRecipes"></datalist>
|
||||
<datalist id="datalist-circuitGameModes">
|
||||
<option>Survival</option>
|
||||
<option>VoidFlood</option>
|
||||
|
||||
@ -63,7 +63,7 @@ function openWebSocket() {
|
||||
}
|
||||
$(".displayname").text(data.DisplayName);
|
||||
window.accountId = data.id;
|
||||
window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce;
|
||||
window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce + "&wsid=" + wsid;
|
||||
if (window.dict) {
|
||||
updateLocElements();
|
||||
}
|
||||
@ -90,6 +90,14 @@ function openWebSocket() {
|
||||
if ("logged_out" in msg) {
|
||||
logout();
|
||||
}
|
||||
if ("have_game_ws" in msg) {
|
||||
window.have_game_ws = msg.have_game_ws;
|
||||
if (window.dict) {
|
||||
$(".inventory-update-note").text(
|
||||
loc(msg.have_game_ws ? "general_inventoryUpdateNoteGameWs" : "general_inventoryUpdateNote")
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
window.ws.onclose = function () {
|
||||
ws_is_open = false;
|
||||
@ -223,6 +231,9 @@ function updateLocElements() {
|
||||
document.querySelectorAll("[data-loc-replace]").forEach(elm => {
|
||||
elm.innerHTML = elm.innerHTML.replace("|VAL|", elm.getAttribute("data-loc-replace"));
|
||||
});
|
||||
$(".inventory-update-note").text(
|
||||
loc(window.have_game_ws ? "general_inventoryUpdateNoteGameWs" : "general_inventoryUpdateNote")
|
||||
);
|
||||
}
|
||||
|
||||
function setActiveLanguage(lang) {
|
||||
@ -256,6 +267,9 @@ function setLanguage(lang) {
|
||||
// Not in prelogin state?
|
||||
fetchItemList();
|
||||
updateInventory();
|
||||
if (single.getCurrentPath().startsWith("/webui/guildView")) {
|
||||
updateInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,6 +504,33 @@ function fetchItemList() {
|
||||
name: data.ModularParts.find(
|
||||
i => i.uniqueName === "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
|
||||
).name
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Creator": {
|
||||
name: loc("guildView_rank_creator")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Warlord": {
|
||||
name: loc("guildView_rank_warlord")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_General": {
|
||||
name: loc("guildView_rank_general")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Officer": {
|
||||
name: loc("guildView_rank_officer")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Leader": {
|
||||
name: loc("guildView_rank_leader")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Sage": {
|
||||
name: loc("guildView_rank_sage")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Soldier": {
|
||||
name: loc("guildView_rank_soldier")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Initiate": {
|
||||
name: loc("guildView_rank_initiate")
|
||||
},
|
||||
"/Lotus/Language/Game/Rank_Utility": {
|
||||
name: loc("guildView_rank_utility")
|
||||
}
|
||||
};
|
||||
for (const [type, items] of Object.entries(data)) {
|
||||
@ -623,6 +664,12 @@ function updateInventory() {
|
||||
req.done(data => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
window.didInitialInventoryUpdate = true;
|
||||
if (data.GuildId) {
|
||||
window.guildId = data.GuildId.$oid;
|
||||
document.getElementById("nav-guildView").classList.remove("d-none");
|
||||
} else {
|
||||
document.getElementById("nav-guildView").classList.add("d-none");
|
||||
}
|
||||
|
||||
const modularWeapons = [
|
||||
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
@ -935,14 +982,15 @@ function updateInventory() {
|
||||
if (!data.QuestKeys.some(x => x.ItemType == questKey.uniqueName)) {
|
||||
const datalist = document.getElementById("datalist-QuestKeys");
|
||||
if (!datalist.querySelector(`option[data-key="${questKey.uniqueName}"]`)) {
|
||||
readdQuestKey(itemMap, questKey.uniqueName);
|
||||
reAddToItemList(itemMap, "QuestKeys", questKey.uniqueName);
|
||||
}
|
||||
}
|
||||
});
|
||||
data.QuestKeys.forEach(item => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("data-item-type", item.ItemType);
|
||||
const stage = item.Progress?.length ?? 0;
|
||||
const run = item.Progress[0]?.c ?? 0;
|
||||
const stage = run == 0 ? item.Progress.length : item.Progress.map(p => p.c ?? 0).lastIndexOf(run);
|
||||
|
||||
const datalist = document.getElementById("datalist-QuestKeys");
|
||||
const optionToRemove = datalist.querySelector(`option[data-key="${item.ItemType}"]`);
|
||||
@ -960,6 +1008,10 @@ function updateInventory() {
|
||||
td.textContent += " | " + loc("code_completed");
|
||||
}
|
||||
|
||||
if (run > 0) {
|
||||
td.textContent += " | " + loc("code_replays") + ": " + (run + 1);
|
||||
}
|
||||
|
||||
if (data.ActiveQuest == item.ItemType) td.textContent += " | " + loc("code_active");
|
||||
tr.appendChild(td);
|
||||
}
|
||||
@ -1032,7 +1084,7 @@ function updateInventory() {
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
readdQuestKey(itemMap, item.ItemType);
|
||||
reAddToItemList(itemMap, "QuestKeys", item.ItemType);
|
||||
doQuestUpdate("deleteKey", item.ItemType);
|
||||
};
|
||||
a.title = loc("code_remove");
|
||||
@ -1502,6 +1554,315 @@ function updateInventory() {
|
||||
document.getElementById("Boosters-list").appendChild(tr);
|
||||
});
|
||||
|
||||
if (single.getCurrentPath().startsWith("/webui/guildView")) {
|
||||
const guildReq = $.get("/custom/getGuild?guildId=" + window.guildId);
|
||||
guildReq.done(guildData => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
document.getElementById("guildView-loading").classList.add("d-none");
|
||||
|
||||
document.getElementById("guildView-title").textContent = guildData.Name;
|
||||
document.getElementById("guildView-tier").textContent = loc("guildView_tierDisplay")
|
||||
.split("|TIER|")
|
||||
.join(loc(`guildView_tier${guildData.Tier}`));
|
||||
document.getElementById("guildView-class").textContent = loc("guildView_classDisplay")
|
||||
.split("|CLASS|")
|
||||
.join(guildData.Class);
|
||||
|
||||
["VaultRegularCredits", "VaultPremiumCredits"].forEach(currency => {
|
||||
document.getElementById(currency + "-owned").textContent = loc("guildView_currency_owned")
|
||||
.split("|COUNT|")
|
||||
.join((guildData[currency] ?? 0).toLocaleString());
|
||||
});
|
||||
|
||||
const userGuildMember = guildData.Members.find(m => m._id.$oid === window.accountId);
|
||||
let userGuildPermissions;
|
||||
if (userGuildMember) {
|
||||
userGuildPermissions = guildData.Ranks[userGuildMember.Rank].Permissions;
|
||||
// Ruler = 1, // Clan: Change hierarchy. Alliance (Creator only): Kick clans.
|
||||
// Advertiser = 8192,
|
||||
// Recruiter = 2, // Send invites (Clans & Alliances)
|
||||
// Regulator = 4, // Kick members
|
||||
// Promoter = 8, // Clan: Promote and demote members. Alliance (Creator only): Change clan permissions.
|
||||
// Architect = 16, // Create and destroy rooms
|
||||
// Host = 32, // No longer used in modern versions
|
||||
// Decorator = 1024, // Create and destroy decos
|
||||
// Treasurer = 64, // Clan: Contribute from vault and edit tax rate. Alliance: Divvy vault.
|
||||
// Tech = 128, // Queue research
|
||||
// ChatModerator = 512, // (Clans & Alliances)
|
||||
// Herald = 2048, // Change MOTD
|
||||
// Fabricator = 4096 // Replicate research
|
||||
if (userGuildPermissions & 128) {
|
||||
document.getElementById("techProjects-form").classList.remove("d-none");
|
||||
}
|
||||
if (userGuildPermissions & 16) {
|
||||
document.getElementById("vaultDecoRecipes-form").classList.remove("d-none");
|
||||
}
|
||||
if (userGuildPermissions & 64) {
|
||||
document.getElementById("vaultRegularCredits-form").classList.remove("d-none");
|
||||
document.getElementById("VaultRegularCredits-owned").classList.remove("mb-0");
|
||||
document.getElementById("vaultPremiumCredits-form").classList.remove("d-none");
|
||||
document.getElementById("VaultPremiumCredits-owned").classList.remove("mb-0");
|
||||
}
|
||||
if (userGuildMember.Rank <= 1) {
|
||||
document.querySelectorAll("#guild-actions button").forEach(btn => {
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const guildCheats = document.querySelectorAll("#guild-cheats input[id]");
|
||||
for (const elm of guildCheats) {
|
||||
elm.checked = !!guildData[elm.id];
|
||||
if (!userGuildMember || userGuildMember.Rank > 1) {
|
||||
elm.disabled = true;
|
||||
} else {
|
||||
elm.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("TechProjects-list").innerHTML = "";
|
||||
guildData.TechProjects ??= [];
|
||||
guildData.TechProjects.forEach(item => {
|
||||
const datalist = document.getElementById("datalist-TechProjects");
|
||||
const optionToRemove = datalist.querySelector(`option[data-key="${item.ItemType}"]`);
|
||||
if (optionToRemove) {
|
||||
datalist.removeChild(optionToRemove);
|
||||
}
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("data-item-type", item.ItemType);
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
|
||||
if (new Date(item.CompletionDate) < new Date()) {
|
||||
td.textContent += " | " + loc("code_completed");
|
||||
} else if (item.State == 1) {
|
||||
td.textContent += " | " + loc("code_funded");
|
||||
}
|
||||
tr.appendChild(td);
|
||||
}
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.classList = "text-end text-nowrap";
|
||||
|
||||
if (userGuildPermissions && userGuildPermissions & 128 && item.State != 1) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
fundGuildTechProject(item.ItemType);
|
||||
};
|
||||
a.title = loc("code_fund");
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M214.6 17.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 117.3 160 488c0 17.7 14.3 32 32 32s32-14.3 32-32l0-370.7 105.4 105.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (
|
||||
userGuildPermissions &&
|
||||
userGuildPermissions & 128 &&
|
||||
item.State == 1 &&
|
||||
new Date(item.CompletionDate) > new Date()
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
completeGuildTechProject(item.ItemType);
|
||||
};
|
||||
a.title = loc("code_complete");
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M371.7 43.1C360.1 32 343 28.9 328.3 35.2S304 56 304 72l0 136.3-172.3-165.1C120.1 32 103 28.9 88.3 35.2S64 56 64 72l0 368c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9L304 303.7 304 440c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9l192-184c7.9-7.5 12.3-18 12.3-28.9s-4.5-21.3-12.3-28.9l-192-184z"/></svg>`;
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (userGuildMember && userGuildMember.Rank <= 1) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
reAddToItemList(itemMap, "TechProjects", item.ItemType);
|
||||
removeGuildTechProject(item.ItemType);
|
||||
};
|
||||
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("TechProjects-list").appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById("VaultDecoRecipes-list").innerHTML = "";
|
||||
guildData.VaultDecoRecipes ??= [];
|
||||
guildData.VaultDecoRecipes.forEach(item => {
|
||||
const datalist = document.getElementById("datalist-VaultDecoRecipes");
|
||||
const optionToRemove = datalist.querySelector(`option[data-key="${item.ItemType}"]`);
|
||||
if (optionToRemove) {
|
||||
datalist.removeChild(optionToRemove);
|
||||
}
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("data-item-type", item.ItemType);
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
|
||||
tr.appendChild(td);
|
||||
}
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.classList = "text-end text-nowrap";
|
||||
|
||||
if (userGuildMember && userGuildMember.Rank <= 1) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
reAddToItemList(itemMap, "VaultDecoRecipes", item.ItemType);
|
||||
removeVaultDecoRecipe(item.ItemType);
|
||||
};
|
||||
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("VaultDecoRecipes-list").appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById("Members-list").innerHTML = "";
|
||||
guildData.Members.forEach(member => {
|
||||
const tr = document.createElement("tr");
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
const memberRank = guildData.Ranks[member.Rank];
|
||||
td.textContent = member.DisplayName;
|
||||
td.textContent += " | " + itemMap[memberRank.Name]?.name ?? memberRank.Name;
|
||||
if (member.Status != 0) {
|
||||
td.textContent += " | " + loc("guildView_pending");
|
||||
}
|
||||
tr.appendChild(td);
|
||||
}
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.classList = "text-end text-nowrap";
|
||||
|
||||
if (
|
||||
userGuildMember &&
|
||||
member.Rank < 8 &&
|
||||
member.Rank > userGuildMember.Rank &&
|
||||
userGuildPermissions &&
|
||||
userGuildPermissions & 8
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
changeGuildRank(guildId, member._id.$oid, member.Rank + 1);
|
||||
};
|
||||
a.title = loc("guildView_demote");
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>`;
|
||||
td.appendChild(a);
|
||||
}
|
||||
if (
|
||||
userGuildMember &&
|
||||
member.Rank > userGuildMember.Rank &&
|
||||
userGuildPermissions &&
|
||||
userGuildPermissions & 8
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
changeGuildRank(guildId, member._id.$oid, member.Rank - 1);
|
||||
};
|
||||
a.title = loc("guildView_promote");
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"/></svg>`;
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (
|
||||
(userGuildMember &&
|
||||
member.Rank > userGuildMember.Rank &&
|
||||
userGuildPermissions &&
|
||||
userGuildPermissions & 4) ||
|
||||
(userGuildMember && userGuildMember.Rank != 0 && userGuildMember._id == member._id)
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
kickFromGuild(member._id.$oid);
|
||||
};
|
||||
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("Members-list").appendChild(tr);
|
||||
});
|
||||
|
||||
if (guildData.AllianceId) {
|
||||
const allianceReq = $.get("/custom/getAlliance?guildId=" + guildId);
|
||||
allianceReq.done(allianceData => {
|
||||
document.getElementById("guildView-alliance").textContent =
|
||||
loc("guildView_alliance") + ": " + allianceData.Name;
|
||||
|
||||
let userAlliancePermisssions;
|
||||
if (userGuildMember && userGuildMember.Rank <= 1) {
|
||||
userAlliancePermisssions = allianceData.Clans.find(
|
||||
c => c._id.$oid === guildId
|
||||
).Permissions;
|
||||
}
|
||||
document.getElementById("Alliance-list").innerHTML = "";
|
||||
allianceData.Clans.forEach(clan => {
|
||||
const tr = document.createElement("tr");
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = clan.Name;
|
||||
if (clan.Pending) {
|
||||
td.textContent += " | " + loc("guildView_pending");
|
||||
}
|
||||
tr.appendChild(td);
|
||||
}
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.classList = "text-end text-nowrap";
|
||||
|
||||
if (
|
||||
!(clan.Permissions & 1) &&
|
||||
userAlliancePermisssions &&
|
||||
userAlliancePermisssions & 1
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
kickFromAlliance(clan._id.$oid);
|
||||
};
|
||||
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("Alliance-list").appendChild(tr);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
guildReq.fail(() => {
|
||||
single.loadRoute("/webui/inventory");
|
||||
});
|
||||
}
|
||||
|
||||
for (const elm of accountCheats) {
|
||||
elm.checked = !!data[elm.id];
|
||||
}
|
||||
@ -1509,6 +1870,74 @@ function updateInventory() {
|
||||
});
|
||||
}
|
||||
|
||||
function addVaultDecoRecipe() {
|
||||
const uniqueName = getKey(document.getElementById("acquire-type-VaultDecoRecipes"));
|
||||
if (!guildId) {
|
||||
return;
|
||||
}
|
||||
if (!uniqueName) {
|
||||
$("acquire-type-VaultDecoRecipes").addClass("is-invalid").focus();
|
||||
return;
|
||||
}
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addVaultDecoRecipe?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName,
|
||||
ItemCount: 1
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeGuildRank(guildId, targetId, rankChange) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get(
|
||||
"/api/changeGuildRank.php?" +
|
||||
window.authz +
|
||||
"&guildId=" +
|
||||
guildId +
|
||||
"&targetId=" +
|
||||
targetId +
|
||||
"&rankChange=" +
|
||||
rankChange
|
||||
);
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function kickFromGuild(accountId) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/api/removeFromGuild.php?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/octet-stream",
|
||||
data: JSON.stringify({
|
||||
userId: accountId
|
||||
})
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function kickFromAlliance(guildId) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/api/removeFromAlliance.php?" + window.authz + "&guildId=" + guildId);
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getKey(input) {
|
||||
return document
|
||||
.getElementById(input.getAttribute("list"))
|
||||
@ -1733,6 +2162,262 @@ function addMissingEquipment(categories) {
|
||||
}
|
||||
}
|
||||
|
||||
function addVaultDecoRecipe() {
|
||||
const uniqueName = getKey(document.getElementById("acquire-type-VaultDecoRecipes"));
|
||||
if (!uniqueName) {
|
||||
$("#acquire-type-VaultDecoRecipes").addClass("is-invalid").focus();
|
||||
return;
|
||||
}
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addVaultDecoRecipe?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName,
|
||||
ItemCount: 1
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
document.getElementById("acquire-type-VaultDecoRecipes").value = "";
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeVaultDecoRecipe(uniqueName) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addVaultDecoRecipe?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName,
|
||||
ItemCount: -1
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addGuildTechProject() {
|
||||
const uniqueName = getKey(document.getElementById("acquire-type-TechProjects"));
|
||||
if (!uniqueName) {
|
||||
$("#acquire-type-TechProjects").addClass("is-invalid").focus();
|
||||
return;
|
||||
}
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
document.getElementById("acquire-type-TechProjects").value = "";
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeGuildTechProject(uniqueName) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/removeTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function completeGuildTechProject(uniqueName) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/completeTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fundGuildTechProject(uniqueName) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/fundTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify([
|
||||
{
|
||||
ItemType: uniqueName
|
||||
}
|
||||
])
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchAddVaultDecoRecipesBatch(requests) {
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addVaultDecoRecipe?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addMissingVaultDecoRecipes() {
|
||||
const requests = [];
|
||||
|
||||
document.querySelectorAll("#datalist-VaultDecoRecipes" + " option").forEach(elm => {
|
||||
if (!document.querySelector("#VaultDecoRecipes-list [data-item-type='" + elm.getAttribute("data-key") + "']")) {
|
||||
requests.push({ ItemType: elm.getAttribute("data-key"), ItemCount: 1 });
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
requests.length != 0 &&
|
||||
window.confirm(loc("code_addDecoRecipesConfirm").split("|COUNT|").join(requests.length))
|
||||
) {
|
||||
return dispatchAddVaultDecoRecipesBatch(requests);
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchAddTechProjectsBatch(requests) {
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addMissingTechProjects() {
|
||||
const requests = [];
|
||||
|
||||
document.querySelectorAll("#datalist-TechProjects option").forEach(elm => {
|
||||
if (!document.querySelector("#TechProjects-list [data-item-type='" + elm.getAttribute("data-key") + "']")) {
|
||||
requests.push({ ItemType: elm.getAttribute("data-key") });
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
requests.length != 0 &&
|
||||
window.confirm(loc("code_addTechProjectsConfirm").split("|COUNT|").join(requests.length))
|
||||
) {
|
||||
return dispatchAddTechProjectsBatch(requests);
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchFundTechProjectsBatch(requests) {
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/fundTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fundAllTechProjects() {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/custom/getGuild?guildId=" + window.guildId);
|
||||
req.done(data => {
|
||||
const requests = [];
|
||||
data.TechProjects ??= [];
|
||||
data.TechProjects.forEach(techProject => {
|
||||
if (techProject.State != 1) {
|
||||
requests.push({
|
||||
ItemType: techProject.ItemType
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(requests).length > 0) {
|
||||
return dispatchFundTechProjectsBatch(requests);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchCompleteTechProjectsBatch(requests) {
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/completeTechProject?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function completeAllTechProjects() {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/custom/getGuild?guildId=" + window.guildId);
|
||||
req.done(data => {
|
||||
const requests = [];
|
||||
data.TechProjects ??= [];
|
||||
data.TechProjects.forEach(techProject => {
|
||||
if (techProject.State == 1 && new Date(techProject.CompletionDate) > new Date()) {
|
||||
requests.push({
|
||||
ItemType: techProject.ItemType
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(requests).length > 0) {
|
||||
return dispatchCompleteTechProjectsBatch(requests);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function addMissingHelminthRecipes() {
|
||||
await revalidateAuthz();
|
||||
await fetch("/custom/addMissingHelminthBlueprints?" + window.authz);
|
||||
@ -1913,7 +2598,7 @@ function disposeOfGear(category, oid) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -1935,7 +2620,7 @@ function disposeOfItems(category, type, count) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -2189,7 +2874,7 @@ for (const id of uiConfigs) {
|
||||
value = parseInt(value);
|
||||
}
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/custom/setConfig?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ [id]: value })
|
||||
});
|
||||
@ -2197,13 +2882,9 @@ for (const id of uiConfigs) {
|
||||
} else if (elm.type == "checkbox") {
|
||||
elm.onchange = function () {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/custom/setConfig?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ [id]: this.checked })
|
||||
}).then(() => {
|
||||
if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(id) != -1) {
|
||||
updateInventory();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -2224,7 +2905,7 @@ document.querySelectorAll(".config-form .input-group").forEach(grp => {
|
||||
|
||||
function doSaveConfigInt(id) {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/custom/setConfig?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
[id]: parseInt(document.getElementById(id).value)
|
||||
@ -2234,7 +2915,7 @@ function doSaveConfigInt(id) {
|
||||
|
||||
function doSaveConfigFloat(id) {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/custom/setConfig?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
[id]: parseFloat(document.getElementById(id).value)
|
||||
@ -2244,7 +2925,7 @@ function doSaveConfigFloat(id) {
|
||||
|
||||
function doSaveConfigStringArray(id) {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/custom/setConfig?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
[id]: document
|
||||
@ -2328,6 +3009,9 @@ function doUnlockAllFocusSchools() {
|
||||
toast(loc("code_focusAllUnlocked"));
|
||||
} else {
|
||||
toast(loc("code_focusUnlocked").split("|COUNT|").join(Object.keys(missingFocusUpgrades).length));
|
||||
if (ws_is_open) {
|
||||
window.ws.send(JSON.stringify({ sync_inventory: true }));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -2371,7 +3055,22 @@ document.querySelectorAll("#account-cheats input[type=checkbox]").forEach(elm =>
|
||||
elm.onchange = function () {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/setAccountCheat?" + window.authz /*+ "&wsid=" + wsid*/,
|
||||
url: "/custom/setAccountCheat?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
key: elm.id,
|
||||
value: elm.checked
|
||||
})
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
document.querySelectorAll("#guild-cheats input[type=checkbox]").forEach(elm => {
|
||||
elm.onchange = function () {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/setGuildCheat?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
key: elm.id,
|
||||
@ -2428,7 +3127,7 @@ function doRemoveUnrankedMods() {
|
||||
req.done(inventory => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify({
|
||||
SellCurrency: "SC_RegularCredits",
|
||||
@ -2472,6 +3171,32 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
|
||||
}
|
||||
});
|
||||
|
||||
single.getRoute("#guild-route").on("beforeload", function () {
|
||||
document.getElementById("guildView-loading").classList.remove("d-none");
|
||||
document.getElementById("guildView-title").textContent = "";
|
||||
document.getElementById("guildView-tier").textContent = "";
|
||||
document.getElementById("guildView-class").textContent = "";
|
||||
document.getElementById("vaultRegularCredits-form").classList.add("d-none");
|
||||
document.getElementById("vaultPremiumCredits-form").classList.add("d-none");
|
||||
document.getElementById("VaultRegularCredits-owned").classList.add("mb-0");
|
||||
document.getElementById("VaultPremiumCredits-owned").classList.add("mb-0");
|
||||
document.getElementById("TechProjects-list").innerHTML = "";
|
||||
document.getElementById("techProjects-form").classList.add("d-none");
|
||||
document.getElementById("acquire-type-TechProjects").value = "";
|
||||
document.getElementById("VaultDecoRecipes-list").innerHTML = "";
|
||||
document.getElementById("vaultDecoRecipes-form").classList.add("d-none");
|
||||
document.getElementById("acquire-type-VaultDecoRecipes").value = "";
|
||||
document.getElementById("Alliance-list").innerHTML = "";
|
||||
document.getElementById("guildView-alliance").textContent = "";
|
||||
document.getElementById("Members-list").innerHTML = "";
|
||||
document.querySelectorAll("#guild-actions button").forEach(btn => {
|
||||
btn.disabled = true;
|
||||
});
|
||||
if (window.didInitialInventoryUpdate) {
|
||||
updateInventory();
|
||||
}
|
||||
});
|
||||
|
||||
function doPushArchonCrystalUpgrade() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const uniqueName = getKey(document.querySelector("[list='datalist-archonCrystalUpgrades']"));
|
||||
@ -2535,7 +3260,7 @@ function doChangeSupportedSyndicate() {
|
||||
function doAddCurrency(currency) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/addCurrency?" + window.authz,
|
||||
url: "/custom/addCurrency?" + window.authz + "&guildId=" + window.guildId,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
currency,
|
||||
@ -2547,11 +3272,11 @@ function doAddCurrency(currency) {
|
||||
});
|
||||
}
|
||||
|
||||
function readdQuestKey(itemMap, itemType) {
|
||||
function reAddToItemList(itemMap, datalist, itemType) {
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", itemType);
|
||||
option.value = itemMap[itemType]?.name ?? itemType;
|
||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
||||
document.getElementById("datalist-" + datalist).appendChild(option);
|
||||
}
|
||||
|
||||
function doQuestUpdate(operation, itemType) {
|
||||
@ -2759,13 +3484,13 @@ async function doUnlockAllScans() {
|
||||
async function doUnlockAllShipFeatures() {
|
||||
await revalidateAuthz();
|
||||
await fetch("/custom/unlockAllShipFeatures?" + window.authz);
|
||||
toast(loc("cheats_unlockSuccInventory"));
|
||||
toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory"));
|
||||
}
|
||||
|
||||
async function doUnlockAllCapturaScenes() {
|
||||
await revalidateAuthz();
|
||||
await fetch("/custom/unlockAllCapturaScenes?" + window.authz);
|
||||
toast(loc("cheats_unlockSuccInventory"));
|
||||
toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory"));
|
||||
}
|
||||
|
||||
async function unlockAllMissions() {
|
||||
@ -2777,13 +3502,13 @@ async function unlockAllMissions() {
|
||||
async function unlockAllProfitTakerStages() {
|
||||
await revalidateAuthz();
|
||||
await fetch("/custom/unlockAllProfitTakerStages?" + window.authz);
|
||||
toast(loc("cheats_unlockSuccInventory"));
|
||||
toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory"));
|
||||
}
|
||||
|
||||
async function unlockAllSimarisResearchEntries() {
|
||||
await revalidateAuthz();
|
||||
await fetch("/custom/unlockAllSimarisResearchEntries?" + window.authz);
|
||||
toast(loc("cheats_unlockSuccInventory"));
|
||||
toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory"));
|
||||
}
|
||||
|
||||
const importSamples = {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// German translation by Animan8000
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `Hinweis: Um Änderungen im Spiel zu sehen, musst du dein Inventar neu synchronisieren, z. B. mit dem /sync Befehl des Bootstrappers, durch Besuch eines Dojo/Relais oder durch erneutes Einloggen.`,
|
||||
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `Hinzufügen`,
|
||||
general_setButton: `Festlegen`,
|
||||
general_none: `Keines`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `Neuen benutzerdefinierten Namen eingeben:`,
|
||||
code_remove: `Entfernen`,
|
||||
code_addItemsConfirm: `Bist du sicher, dass du |COUNT| Gegenstände zu deinem Account hinzufügen möchtest?`,
|
||||
code_addTechProjectsConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| research to your clan?`,
|
||||
code_addDecoRecipesConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| deco recipes to your clan?`,
|
||||
code_succRankUp: `Erfolgreich aufgestiegen.`,
|
||||
code_noEquipmentToRankUp: `Keine Ausstattung zum Rangaufstieg verfügbar.`,
|
||||
code_succAdded: `Erfolgreich hinzugefügt.`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Für den Kampf auswachsen lassen`,
|
||||
code_unmature: `Genetisches Altern zurücksetzen`,
|
||||
code_fund: `[UNTRANSLATED] Fund`,
|
||||
code_funded: `[UNTRANSLATED] Funded`,
|
||||
code_replays: `[UNTRANSLATED] Replays`,
|
||||
code_succChange: `Erfolgreich geändert.`,
|
||||
code_requiredInvigorationUpgrade: `Du musst sowohl ein offensives & defensives Upgrade auswählen.`,
|
||||
login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
|
||||
@ -74,6 +80,7 @@ dict = {
|
||||
navbar_renameAccount: `Account umbenennen`,
|
||||
navbar_deleteAccount: `Account löschen`,
|
||||
navbar_inventory: `Inventar`,
|
||||
navbar_guildView: `Clan`,
|
||||
navbar_mods: `Mods`,
|
||||
navbar_quests: `Quests`,
|
||||
navbar_cheats: `Cheats`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Alle <abbr title="Animationssets, Glyphen, Farbpaletten usw.">Sammlerstücke</abbr> freischalten`,
|
||||
cheats_unlockAllSkins: `Alle Skins freischalten`,
|
||||
cheats_unlockAllCapturaScenes: `Alle Photora-Szenen freischalten`,
|
||||
cheats_unlockAllDecoRecipes: `Alle Dojo-Deko-Baupläne freischalten`,
|
||||
cheats_universalPolarityEverywhere: `Universelle Polarität überall`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Orokin Reaktor & Beschleuniger überall`,
|
||||
cheats_unlockExilusEverywhere: `Exilus-Adapter überall`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `Dunkles Design`,
|
||||
theme_light: `Helles Design`,
|
||||
|
||||
guildView_cheats: `[UNTRANSLATED] Clan Cheats`,
|
||||
guildView_techProjects: `Forschung`,
|
||||
guildView_vaultDecoRecipes: `[UNTRANSLATED] Dojo Deco Recipes`,
|
||||
guildView_alliance: `Allianz`,
|
||||
guildView_members: `Mitglieder`,
|
||||
guildView_pending: `Ausstehend`,
|
||||
guildView_classDisplay: `Rang |CLASS|`,
|
||||
guildView_tierDisplay: `|TIER| Clan`,
|
||||
guildView_tier1: `Geist`,
|
||||
guildView_tier2: `Schatten`,
|
||||
guildView_tier3: `Sturm`,
|
||||
guildView_tier4: `Berg`,
|
||||
guildView_tier5: `Mond`,
|
||||
guildView_rank_creator: `Gründer Kriegsherr`,
|
||||
guildView_rank_general: `General`,
|
||||
guildView_rank_initiate: `Initiant`,
|
||||
guildView_rank_leader: `Anführer`,
|
||||
guildView_rank_officer: `Offizier`,
|
||||
guildView_rank_sage: `Weiser`,
|
||||
guildView_rank_soldier: `Soldat`,
|
||||
guildView_rank_utility: `Versorger`,
|
||||
guildView_rank_warlord: `Kriegsherr`,
|
||||
guildView_currency_owned: `[UNTRANSLATED] |COUNT| in Vault.`,
|
||||
guildView_bulkAddTechProjects: `[UNTRANSLATED] Add Missing Research`,
|
||||
guildView_bulkAddVaultDecoRecipes: `[UNTRANSLATED] Add Missing Dojo Deco Recipes`,
|
||||
guildView_bulkFundTechProjects: `[UNTRANSLATED] Fund All Research`,
|
||||
guildView_bulkCompleteTechProjects: `[UNTRANSLATED] Complete All Research`,
|
||||
guildView_promote: `Befördern`,
|
||||
guildView_demote: `Degradieren`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
dict = {
|
||||
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_inventoryUpdateNoteGameWs: `Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `Add`,
|
||||
general_setButton: `Set`,
|
||||
general_none: `None`,
|
||||
@ -30,6 +31,8 @@ dict = {
|
||||
code_renamePrompt: `Enter new custom name:`,
|
||||
code_remove: `Remove`,
|
||||
code_addItemsConfirm: `Are you sure you want to add |COUNT| items to your account?`,
|
||||
code_addTechProjectsConfirm: `Are you sure you want to add |COUNT| research to your clan?`,
|
||||
code_addDecoRecipesConfirm: `Are you sure you want to add |COUNT| deco recipes to your clan?`,
|
||||
code_succRankUp: `Successfully ranked up.`,
|
||||
code_noEquipmentToRankUp: `No equipment to rank up.`,
|
||||
code_succAdded: `Successfully added.`,
|
||||
@ -62,6 +65,9 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Mature for combat`,
|
||||
code_unmature: `Regress genetic aging`,
|
||||
code_fund: `Fund`,
|
||||
code_funded: `Funded`,
|
||||
code_replays: `Replays`,
|
||||
code_succChange: `Successfully changed.`,
|
||||
code_requiredInvigorationUpgrade: `You must select both an offensive & defensive upgrade.`,
|
||||
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
|
||||
@ -73,6 +79,7 @@ dict = {
|
||||
navbar_renameAccount: `Rename Account`,
|
||||
navbar_deleteAccount: `Delete Account`,
|
||||
navbar_inventory: `Inventory`,
|
||||
navbar_guildView: `Clan`,
|
||||
navbar_mods: `Mods`,
|
||||
navbar_quests: `Quests`,
|
||||
navbar_cheats: `Cheats`,
|
||||
@ -195,7 +202,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Unlock All <abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavor Items</abbr>`,
|
||||
cheats_unlockAllSkins: `Unlock All Skins`,
|
||||
cheats_unlockAllCapturaScenes: `Unlock All Captura Scenes`,
|
||||
cheats_unlockAllDecoRecipes: `Unlock All Dojo Deco Recipes`,
|
||||
cheats_universalPolarityEverywhere: `Universal Polarity Everywhere`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Potatoes Everywhere`,
|
||||
cheats_unlockExilusEverywhere: `Exilus Adapters Everywhere`,
|
||||
@ -386,5 +392,35 @@ dict = {
|
||||
theme_dark: `Dark Theme`,
|
||||
theme_light: `Light Theme`,
|
||||
|
||||
guildView_cheats: `Clan Cheats`,
|
||||
guildView_techProjects: `Research`,
|
||||
guildView_vaultDecoRecipes: `Dojo Deco Recipes`,
|
||||
guildView_alliance: `Alliance`,
|
||||
guildView_members: `Members`,
|
||||
guildView_pending: `Pending`,
|
||||
guildView_classDisplay: `Rank |CLASS|`,
|
||||
guildView_tierDisplay: `|TIER| Clan`,
|
||||
guildView_tier1: `Ghost`,
|
||||
guildView_tier2: `Shadow`,
|
||||
guildView_tier3: `Storm`,
|
||||
guildView_tier4: `Mountain`,
|
||||
guildView_tier5: `Moon`,
|
||||
guildView_rank_creator: `Founding Warlord`,
|
||||
guildView_rank_general: `General`,
|
||||
guildView_rank_initiate: `Initiate`,
|
||||
guildView_rank_leader: `Leader`,
|
||||
guildView_rank_officer: `Officer`,
|
||||
guildView_rank_sage: `Sage`,
|
||||
guildView_rank_soldier: `Soldier`,
|
||||
guildView_rank_utility: `Utility`,
|
||||
guildView_rank_warlord: `Warlord`,
|
||||
guildView_currency_owned: `|COUNT| in Vault.`,
|
||||
guildView_bulkAddTechProjects: `Add Missing Research`,
|
||||
guildView_bulkAddVaultDecoRecipes: `Add Missing Dojo Deco Recipes`,
|
||||
guildView_bulkFundTechProjects: `Fund All Research`,
|
||||
guildView_bulkCompleteTechProjects: `Complete All Research`,
|
||||
guildView_promote: `Promote`,
|
||||
guildView_demote: `Demote`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Spanish translation by hxedcl
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`,
|
||||
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `Agregar`,
|
||||
general_setButton: `Establecer`,
|
||||
general_none: `Ninguno`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `Escribe tu nuevo nombre personalizado:`,
|
||||
code_remove: `Quitar`,
|
||||
code_addItemsConfirm: `¿Estás seguro de que deseas agregar |COUNT| objetos a tu cuenta?`,
|
||||
code_addTechProjectsConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| research to your clan?`,
|
||||
code_addDecoRecipesConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| deco recipes to your clan?`,
|
||||
code_succRankUp: `Ascenso exitoso.`,
|
||||
code_noEquipmentToRankUp: `No hay equipo para ascender.`,
|
||||
code_succAdded: `Agregado exitosamente.`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `Pigmento`,
|
||||
code_mature: `Listo para el combate`,
|
||||
code_unmature: `Regresar el envejecimiento genético`,
|
||||
code_fund: `[UNTRANSLATED] Fund`,
|
||||
code_funded: `[UNTRANSLATED] Funded`,
|
||||
code_replays: `[UNTRANSLATED] Replays`,
|
||||
code_succChange: `Cambiado correctamente`,
|
||||
code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una defensiva.`,
|
||||
login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
|
||||
@ -74,6 +80,7 @@ dict = {
|
||||
navbar_renameAccount: `Renombrar cuenta`,
|
||||
navbar_deleteAccount: `Eliminar cuenta`,
|
||||
navbar_inventory: `Inventario`,
|
||||
navbar_guildView: `Clan`,
|
||||
navbar_mods: `Mods`,
|
||||
navbar_quests: `Misiones`,
|
||||
navbar_cheats: `Trucos`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Desbloquear todos los <abbr title="Conjuntos de animaciones, glifos, paletas, etc.">ítems estéticos</abbr>`,
|
||||
cheats_unlockAllSkins: `Desbloquear todas las skins`,
|
||||
cheats_unlockAllCapturaScenes: `Desbloquear todas las escenas de Captura`,
|
||||
cheats_unlockAllDecoRecipes: `Desbloquear todas las recetas decorativas del dojo`,
|
||||
cheats_universalPolarityEverywhere: `Polaridad universal en todas partes`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Patatas en todas partes`,
|
||||
cheats_unlockExilusEverywhere: `Adaptadores Exilus en todas partes`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `Tema Oscuro`,
|
||||
theme_light: `Tema Claro`,
|
||||
|
||||
guildView_cheats: `[UNTRANSLATED] Clan Cheats`,
|
||||
guildView_techProjects: `Investigación`,
|
||||
guildView_vaultDecoRecipes: `[UNTRANSLATED] Dojo Deco Recipes`,
|
||||
guildView_alliance: `Alianza`,
|
||||
guildView_members: `Miembros`,
|
||||
guildView_pending: `Pendiente`,
|
||||
guildView_classDisplay: `Rango |CLASS|`,
|
||||
guildView_tierDisplay: `Clan |TIER|`,
|
||||
guildView_tier1: `Fantasma`,
|
||||
guildView_tier2: `Sombra`,
|
||||
guildView_tier3: `Tormenta`,
|
||||
guildView_tier4: `Montaña`,
|
||||
guildView_tier5: `Luna`,
|
||||
guildView_rank_creator: `Señor de la guerra fundador`,
|
||||
guildView_rank_general: `General`,
|
||||
guildView_rank_initiate: `Iniciado`,
|
||||
guildView_rank_leader: `Líder`,
|
||||
guildView_rank_officer: `Oficial`,
|
||||
guildView_rank_sage: `Sabio`,
|
||||
guildView_rank_soldier: `Soldado`,
|
||||
guildView_rank_utility: `Utilitario`,
|
||||
guildView_rank_warlord: `Señor de la guerra`,
|
||||
guildView_currency_owned: `[UNTRANSLATED] |COUNT| in Vault.`,
|
||||
guildView_bulkAddTechProjects: `[UNTRANSLATED] Add Missing Research`,
|
||||
guildView_bulkAddVaultDecoRecipes: `[UNTRANSLATED] Add Missing Dojo Deco Recipes`,
|
||||
guildView_bulkFundTechProjects: `[UNTRANSLATED] Fund All Research`,
|
||||
guildView_bulkCompleteTechProjects: `[UNTRANSLATED] Complete All Research`,
|
||||
guildView_promote: `Promover`,
|
||||
guildView_demote: `Degradar`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// French translation by Vitruvio
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `Note : Pour voir les changements en jeu, l'inventaire doit être actualisé. Cela se fait en tapant /sync dans le tchat, en visitant un dojo/relais ou en se reconnectant.`,
|
||||
general_inventoryUpdateNoteGameWs: `Note : Rouvrir un menu est nécessaire pour voir les changements.`,
|
||||
general_addButton: `Ajouter`,
|
||||
general_setButton: `Définir`,
|
||||
general_none: `Aucun`,
|
||||
@ -10,7 +11,7 @@ dict = {
|
||||
code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`,
|
||||
code_regFail: `Enregistrement impossible. Compte existant?`,
|
||||
code_changeNameConfirm: `Nouveau nom du compte :`,
|
||||
code_changeNameRetry: `[UNTRANSLATED] |NAME| is already taken.`,
|
||||
code_changeNameRetry: `|NAME| est déjà pris.`,
|
||||
code_deleteAccountConfirm: `Supprimer |DISPLAYNAME| (|EMAIL|) ? Cette action est irreversible.`,
|
||||
code_archgun: `Archgun`,
|
||||
code_melee: `Melee`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `Nouveau nom :`,
|
||||
code_remove: `Retirer`,
|
||||
code_addItemsConfirm: `Ajouter |COUNT| items à l'inventaire ?`,
|
||||
code_addTechProjectsConfirm: `Ajouter |COUNT| recherches au clan ?`,
|
||||
code_addDecoRecipesConfirm: `Ajouter |COUNT| décorations au clan ?`,
|
||||
code_succRankUp: `Montée de niveau effectuée.`,
|
||||
code_noEquipmentToRankUp: `Aucun équipement à monter de niveau.`,
|
||||
code_succAdded: `Ajouté.`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Maturer pour le combat`,
|
||||
code_unmature: `Régrésser l'âge génétique`,
|
||||
code_fund: `Financer`,
|
||||
code_funded: `Complété`,
|
||||
code_replays: `[UNTRANSLATED] Replays`,
|
||||
code_succChange: `Changement effectué.`,
|
||||
code_requiredInvigorationUpgrade: `Augmentation offensive et défensive requises.`,
|
||||
login_description: `Connexion avec les informations de connexion OpenWF.`,
|
||||
@ -74,13 +80,14 @@ dict = {
|
||||
navbar_renameAccount: `Renommer le compte`,
|
||||
navbar_deleteAccount: `Supprimer le compte`,
|
||||
navbar_inventory: `Inventaire`,
|
||||
navbar_guildView: `Clan`,
|
||||
navbar_mods: `Mods`,
|
||||
navbar_quests: `Quêtes`,
|
||||
navbar_cheats: `Cheats`,
|
||||
navbar_import: `Importer`,
|
||||
inventory_addItems: `Ajouter des items`,
|
||||
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
|
||||
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
|
||||
inventory_addItemByItemType: `Brut`,
|
||||
inventory_addItemByItemType_warning: `Cette fonctionnalité comporte des risques. Il faudra rajouter les items manuellement si l'inventaire est compris.`,
|
||||
inventory_suits: `Warframes`,
|
||||
inventory_longGuns: `Armes principales`,
|
||||
inventory_pistols: `Armes secondaires`,
|
||||
@ -176,7 +183,7 @@ dict = {
|
||||
cheats_skipTutorial: `Passer le tutoriel`,
|
||||
cheats_skipAllDialogue: `Passer les dialogues`,
|
||||
cheats_unlockAllScans: `Débloquer tous les scans`,
|
||||
cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
|
||||
cheats_unlockSuccRelog: `Succès. Une reconnexion est requise pour appliquer les changements.`,
|
||||
cheats_unlockAllMissions: `Débloquer toutes les missions`,
|
||||
cheats_unlockAllMissions_ok: `Succès. Une actualisation de l'inventaire est nécessaire.`,
|
||||
cheats_infiniteCredits: `Crédits infinis`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Débloquer tous les <abbr title="Animations, Glyphes, Palettes, etc.">Flavor Items</abbr>`,
|
||||
cheats_unlockAllSkins: `Débloquer tous les skins`,
|
||||
cheats_unlockAllCapturaScenes: `Débloquer toutes les scènes captura`,
|
||||
cheats_unlockAllDecoRecipes: `Débloquer toutes les recherches dojo`,
|
||||
cheats_universalPolarityEverywhere: `Polarités universelles partout`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Réacteurs et Catalyseurs partout`,
|
||||
cheats_unlockExilusEverywhere: `Adaptateurs Exilus partout`,
|
||||
@ -213,7 +219,7 @@ dict = {
|
||||
cheats_baroFullyStocked: `Stock de Baro au max`,
|
||||
cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
|
||||
cheats_unlockAllProfitTakerStages: `Débloquer toutes les étapes du Preneur de Profit`,
|
||||
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
|
||||
cheats_unlockSuccInventory: `Succès. Une resynchronisation est nécessaire en tapant "/sync" dans le tchat, en visitant un relais ou en se reconnectant.`,
|
||||
cheats_instantFinishRivenChallenge: `Débloquer le challenge Riven instantanément`,
|
||||
cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
|
||||
cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
|
||||
@ -242,7 +248,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Allégeance`,
|
||||
cheats_changeButton: `Changer`,
|
||||
cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
|
||||
cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`,
|
||||
cheats_finishInvasionsInOneMission: `Compléter les invasions en une mission.`,
|
||||
|
||||
worldState: `Carte Solaire`,
|
||||
worldState_creditBoost: `Booster de Crédit`,
|
||||
@ -252,30 +258,30 @@ dict = {
|
||||
worldState_baroTennoConRelay: `Relais Baro TennoCon`,
|
||||
worldState_starDays: `Jours Stellaires`,
|
||||
worldState_galleonOfGhouls: `Galion des Goules`,
|
||||
worldState_anniversary: `[UNTRANSLATED] Warframe Anniversary`,
|
||||
worldState_useAnniversaryTagForOldGoals: `[UNTRANSLATED] Use <code>Tag</code> from Warframe Anniversary for old Events`,
|
||||
worldState_anniversary: `Anniversaire de Warframe`,
|
||||
worldState_useAnniversaryTagForOldGoals: `Utiliser <code>Tag</code> de l'Anniversaire de Warframe pour les anciens événements`,
|
||||
worldState_ghoulEmergence: `Purge des Goules`,
|
||||
worldState_plagueStar: `Fléau Céleste`,
|
||||
worldState_dogDays: `Bataille d'Eau`,
|
||||
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
|
||||
worldState_dogDaysRewards: `Récompenses de la Bataille d'Eau`,
|
||||
worldState_wolfHunt: `Chasse au Loup (2025)`,
|
||||
worldState_orphixVenom: `Venin Orphix`,
|
||||
worldState_longShadow: `La Propagation des Ombres`,
|
||||
worldState_hallowedFlame: `Flamme Hantée`,
|
||||
worldState_hallowedNightmares: `Cauchemars Hantés`,
|
||||
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
|
||||
worldState_hallowedNightmaresRewards: `Récompenses Flamme Hantée Cauchemar`,
|
||||
worldState_proxyRebellion: `Rébellion Proxy`,
|
||||
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
|
||||
worldState_proxyRebellionRewards: `Récompenses Rébellion Proxy`,
|
||||
worldState_bellyOfTheBeast: `Ventre de la Bête`,
|
||||
worldState_bellyOfTheBeastProgressOverride: `[UNTRANSLATED] Belly of the Beast Progress`,
|
||||
worldState_bellyOfTheBeastProgressOverride: `Progrès du Ventre de la Bête`,
|
||||
worldState_eightClaw: `Huitième Griffe`,
|
||||
worldState_eightClawProgressOverride: `[UNTRANSLATED] Eight Claw Progress`,
|
||||
worldState_eightClawProgressOverride: `Progrès de la Huitième Griffe`,
|
||||
worldState_thermiaFractures: `Crevasses Thermia`,
|
||||
worldState_thermiaFracturesProgressOverride: `[UNTRANSLATED] Thermia Fractures Progress`,
|
||||
worldState_from_year: `[UNTRANSLATED] from |VAL|`,
|
||||
worldState_pre_year: `[UNTRANSLATED] pre |VAL|`,
|
||||
worldState_week: `[UNTRANSLATED] Week |VAL|`,
|
||||
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
|
||||
worldState_thermiaFracturesProgressOverride: `Progrès des Fractures Thermia`,
|
||||
worldState_from_year: `de |VAL|`,
|
||||
worldState_pre_year: `pre-|VAL|`,
|
||||
worldState_week: `Semaine |VAL|`,
|
||||
worldState_incompatibleWith: `Incompatible avec :`,
|
||||
enabled: `Activé`,
|
||||
disabled: `Désactivé`,
|
||||
worldState_we1: `Weekend 1`,
|
||||
@ -319,8 +325,8 @@ dict = {
|
||||
worldState_varziaFullyStocked: `Stock de Varzia au max`,
|
||||
worldState_varziaOverride: `Rotation de Varzia`,
|
||||
|
||||
import_importNote: `[UNTRANSLATED] You can provide a full or partial <code>inventory.php</code> or <code>getShip.php</code> response (client representation) here.`,
|
||||
import_importNote2: `[UNTRANSLATED] All fields that are supported by the importer <b>will be overwritten</b> in your account.`,
|
||||
import_importNote: `Une réponse partielle ou complète de <code>inventory.php</code> ou <code>getShip.php</code> peut être incluse ici.`,
|
||||
import_importNote2: `Tous les champs sont supportés par l'outil d'import <b>serront écrasés</b> sur le compte.`,
|
||||
import_submit: `Soumettre`,
|
||||
import_samples: `Échantillons :`,
|
||||
import_samples_maxFocus: `Toutes les écoles de focus au rang max`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `Thème sombre`,
|
||||
theme_light: `Thème clair`,
|
||||
|
||||
guildView_cheats: `[UNTRANSLATED] Clan Cheats`,
|
||||
guildView_techProjects: `Recherche`,
|
||||
guildView_vaultDecoRecipes: `Schémas de décorations de dojo`,
|
||||
guildView_alliance: `Alliance`,
|
||||
guildView_members: `Members`,
|
||||
guildView_pending: `En Attente`,
|
||||
guildView_classDisplay: `Rang |CLASS|`,
|
||||
guildView_tierDisplay: `Clan |TIER|`,
|
||||
guildView_tier1: `Fantôme`,
|
||||
guildView_tier2: `Ombre`,
|
||||
guildView_tier3: `Tempête`,
|
||||
guildView_tier4: `Montagne`,
|
||||
guildView_tier5: `Lune`,
|
||||
guildView_rank_creator: `Seigneur de Guerre Fondateur`,
|
||||
guildView_rank_general: `Général`,
|
||||
guildView_rank_initiate: `Initié`,
|
||||
guildView_rank_leader: `Chef`,
|
||||
guildView_rank_officer: `Officier`,
|
||||
guildView_rank_sage: `Sage`,
|
||||
guildView_rank_soldier: `Soldat`,
|
||||
guildView_rank_utility: `Utilitaire`,
|
||||
guildView_rank_warlord: `Seigneur de guerre`,
|
||||
guildView_currency_owned: `|COUNT| dans le coffre.`,
|
||||
guildView_bulkAddTechProjects: `Ajouter les recherches manquantes`,
|
||||
guildView_bulkAddVaultDecoRecipes: `Ajouter les schémas de décorations de dojo manquantes`,
|
||||
guildView_bulkFundTechProjects: `Financer toutes les recherches`,
|
||||
guildView_bulkCompleteTechProjects: `Compléter toutes les recherches`,
|
||||
guildView_promote: `Promouvoir`,
|
||||
guildView_demote: `Rétrograder`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Russian translation by AMelonInsideLemon, LoseFace
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `Примечание: Чтобы увидеть изменения в игре, вам нужно повторно синхронизировать свой инвентарь, например, используя команду /sync загрузчика, посетив Додзё/Реле или перезагрузив игру.`,
|
||||
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `Добавить`,
|
||||
general_setButton: `Установить`,
|
||||
general_none: `Отсутствует`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `Введите новое имя:`,
|
||||
code_remove: `Удалить`,
|
||||
code_addItemsConfirm: `Вы уверены, что хотите добавить |COUNT| предметов на ваш аккаунт?`,
|
||||
code_addTechProjectsConfirm: `Вы уверены, что хотите добавить |COUNT| исследований в свой клан?`,
|
||||
code_addDecoRecipesConfirm: `Вы уверены, что хотите добавить |COUNT| рецептов декораций в свой клан?`,
|
||||
code_succRankUp: `Ранг успешно повышен.`,
|
||||
code_noEquipmentToRankUp: `Нет снаряжения для повышения ранга.`,
|
||||
code_succAdded: `Успешно добавлено.`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `Пигмент`,
|
||||
code_mature: `Подготовить к сражениям`,
|
||||
code_unmature: `Регрессия генетического старения`,
|
||||
code_fund: `Профинансировать`,
|
||||
code_funded: `Профинансировано`,
|
||||
code_replays: `Повторов`,
|
||||
code_succChange: `Успешно изменено.`,
|
||||
code_requiredInvigorationUpgrade: `Вы должны выбрать как атакующее, так и вспомогательное улучшение.`,
|
||||
login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
|
||||
@ -74,6 +80,7 @@ dict = {
|
||||
navbar_renameAccount: `Переименовать аккаунт`,
|
||||
navbar_deleteAccount: `Удалить аккаунт`,
|
||||
navbar_inventory: `Инвентарь`,
|
||||
navbar_guildView: `Клан`,
|
||||
navbar_mods: `Моды`,
|
||||
navbar_quests: `Квесты`,
|
||||
navbar_cheats: `Читы`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Разблокировать все <abbr title="Наборы анимаций, глифы, палитры и т. д.">уникальные предметы</abbr>`,
|
||||
cheats_unlockAllSkins: `Разблокировать все скины`,
|
||||
cheats_unlockAllCapturaScenes: `Разблокировать все сцены Каптуры`,
|
||||
cheats_unlockAllDecoRecipes: `Разблокировать все рецепты декораций Дoдзё`,
|
||||
cheats_universalPolarityEverywhere: `Универсальная полярность везде`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Реакторы/Катализаторы орокин везде`,
|
||||
cheats_unlockExilusEverywhere: `Адаптеры Эксилус везде`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `Темная тема`,
|
||||
theme_light: `Светлая тема`,
|
||||
|
||||
guildView_cheats: `Читы Клана`,
|
||||
guildView_techProjects: `Иследовения`,
|
||||
guildView_vaultDecoRecipes: `Рецепты декораций Додзё`,
|
||||
guildView_alliance: `Альянс`,
|
||||
guildView_members: `Товарищи`,
|
||||
guildView_pending: `Ожидание`,
|
||||
guildView_classDisplay: `Ранг |CLASS|`,
|
||||
guildView_tierDisplay: `|TIER| Клан`,
|
||||
guildView_tier1: `Призрачный`,
|
||||
guildView_tier2: `Теневой`,
|
||||
guildView_tier3: `Штормовой`,
|
||||
guildView_tier4: `Горный`,
|
||||
guildView_tier5: `Лунный`,
|
||||
guildView_rank_creator: `Основатель`,
|
||||
guildView_rank_general: `Генерал`,
|
||||
guildView_rank_initiate: `Неофит`,
|
||||
guildView_rank_leader: `Лидер`,
|
||||
guildView_rank_officer: `Офицер`,
|
||||
guildView_rank_sage: `Мудрец`,
|
||||
guildView_rank_soldier: `Солдат`,
|
||||
guildView_rank_utility: `Инженер`,
|
||||
guildView_rank_warlord: `Военачальник`,
|
||||
guildView_currency_owned: `В хранилище |COUNT|.`,
|
||||
guildView_bulkAddTechProjects: `Добавить отсутствующие Иследования`,
|
||||
guildView_bulkAddVaultDecoRecipes: `Добавить отсутствующие рецепты декораций Дoдзё`,
|
||||
guildView_bulkFundTechProjects: `Профинансировать все Иследования`,
|
||||
guildView_bulkCompleteTechProjects: `Завершить все Иследования`,
|
||||
guildView_promote: `Повысить`,
|
||||
guildView_demote: `Понизить`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Ukrainian translation by LoseFace
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `Пам'ятка: Щоб побачити зміни в грі, вам потрібно повторно синхронізувати своє спорядження, наприклад, використовуючи команду /sync завантажувача, відвідавши Доджьо/Реле або перезавантаживши гру.`,
|
||||
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `Добавити`,
|
||||
general_setButton: `Встановити`,
|
||||
general_none: `Відсутній`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `Введіть нове ім'я:`,
|
||||
code_remove: `Видалити`,
|
||||
code_addItemsConfirm: `Ви впевнені, що хочете додати |COUNT| предметів на ваш обліковий запис?`,
|
||||
code_addTechProjectsConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| research to your clan?`,
|
||||
code_addDecoRecipesConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| deco recipes to your clan?`,
|
||||
code_succRankUp: `Рівень успішно підвищено`,
|
||||
code_noEquipmentToRankUp: `Немає спорядження для підвищення рівня.`,
|
||||
code_succAdded: `Успішно додано.`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `Барвник`,
|
||||
code_mature: `Виростити для бою`,
|
||||
code_unmature: `Обернути старіння`,
|
||||
code_fund: `[UNTRANSLATED] Fund`,
|
||||
code_funded: `[UNTRANSLATED] Funded`,
|
||||
code_replays: `[UNTRANSLATED] Replays`,
|
||||
code_succChange: `Успішно змінено.`,
|
||||
code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`,
|
||||
login_description: `Увійдіть, використовуючи облікові дані OpenWF (ті ж, що й у грі при підключенні до цього серверу).`,
|
||||
@ -74,6 +80,7 @@ dict = {
|
||||
navbar_renameAccount: `Перейменувати обліковий запис`,
|
||||
navbar_deleteAccount: `Видалити обліковий запис`,
|
||||
navbar_inventory: `Спорядження`,
|
||||
navbar_guildView: `Клан`,
|
||||
navbar_mods: `Модифікатори`,
|
||||
navbar_quests: `Пригоди`,
|
||||
navbar_cheats: `Чити`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `Розблокувати всі <abbr title="Набори анімацій, гліфи, палітри і т. д.">унікальні предмети</abbr>`,
|
||||
cheats_unlockAllSkins: `Розблокувати всі скіни`,
|
||||
cheats_unlockAllCapturaScenes: `Розблокувати всі сцени Світлописця`,
|
||||
cheats_unlockAllDecoRecipes: `Розблокувати всі рецепти декорацій Доджьо`,
|
||||
cheats_universalPolarityEverywhere: `Будь-яка полярність скрізь`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `Орокінські Реактори/Каталізатори скрізь`,
|
||||
cheats_unlockExilusEverywhere: `Ексилотримач скрізь`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `Темна тема`,
|
||||
theme_light: `Світла тема`,
|
||||
|
||||
guildView_cheats: `[UNTRANSLATED] Clan Cheats`,
|
||||
guildView_techProjects: `Дослідження`,
|
||||
guildView_vaultDecoRecipes: `[UNTRANSLATED] Dojo Deco Recipes`,
|
||||
guildView_alliance: `Альянс`,
|
||||
guildView_members: `Учасники`,
|
||||
guildView_pending: `Очікування`,
|
||||
guildView_classDisplay: `Ранг: |CLASS|`,
|
||||
guildView_tierDisplay: `|TIER| Клан`,
|
||||
guildView_tier1: `Примарний`,
|
||||
guildView_tier2: `Тіньовий`,
|
||||
guildView_tier3: `Грозовий`,
|
||||
guildView_tier4: `Гірський`,
|
||||
guildView_tier5: `Місячний`,
|
||||
guildView_rank_creator: `Воєвода-засновник`,
|
||||
guildView_rank_general: `Генерал`,
|
||||
guildView_rank_initiate: `Рекрут`,
|
||||
guildView_rank_leader: `Лідер`,
|
||||
guildView_rank_officer: `Офіцер`,
|
||||
guildView_rank_sage: `Ветеран`,
|
||||
guildView_rank_soldier: `Солдат`,
|
||||
guildView_rank_utility: `Наймит`,
|
||||
guildView_rank_warlord: `Воєвода`,
|
||||
guildView_currency_owned: `[UNTRANSLATED] |COUNT| in Vault.`,
|
||||
guildView_bulkAddTechProjects: `[UNTRANSLATED] Add Missing Research`,
|
||||
guildView_bulkAddVaultDecoRecipes: `[UNTRANSLATED] Add Missing Dojo Deco Recipes`,
|
||||
guildView_bulkFundTechProjects: `[UNTRANSLATED] Fund All Research`,
|
||||
guildView_bulkCompleteTechProjects: `[UNTRANSLATED] Complete All Research`,
|
||||
guildView_promote: `Підвищити звання`,
|
||||
guildView_demote: `Понизити звання`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang, Corvus, & qingchun
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`,
|
||||
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
|
||||
general_addButton: `添加`,
|
||||
general_setButton: `设置`,
|
||||
general_none: `无`,
|
||||
@ -31,6 +32,8 @@ dict = {
|
||||
code_renamePrompt: `输入新的自定义名称:`,
|
||||
code_remove: `移除`,
|
||||
code_addItemsConfirm: `确定要向您的账户添加 |COUNT| 件物品吗?`,
|
||||
code_addTechProjectsConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| research to your clan?`,
|
||||
code_addDecoRecipesConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| deco recipes to your clan?`,
|
||||
code_succRankUp: `等级已提升`,
|
||||
code_noEquipmentToRankUp: `没有可升级的装备`,
|
||||
code_succAdded: `添加成功`,
|
||||
@ -63,6 +66,9 @@ dict = {
|
||||
code_pigment: `颜料`,
|
||||
code_mature: `成长并战备`,
|
||||
code_unmature: `逆转衰老基因`,
|
||||
code_fund: `[UNTRANSLATED] Fund`,
|
||||
code_funded: `[UNTRANSLATED] Funded`,
|
||||
code_replays: `[UNTRANSLATED] Replays`,
|
||||
code_succChange: `更改成功`,
|
||||
code_requiredInvigorationUpgrade: `您必须同时选择一个进攻型和一个功能型活化属性.`,
|
||||
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`,
|
||||
@ -74,6 +80,7 @@ dict = {
|
||||
navbar_renameAccount: `重命名账户`,
|
||||
navbar_deleteAccount: `删除账户`,
|
||||
navbar_inventory: `仓库`,
|
||||
navbar_guildView: `氏族`,
|
||||
navbar_mods: `Mods`,
|
||||
navbar_quests: `系列任务`,
|
||||
navbar_cheats: `作弊选项`,
|
||||
@ -196,7 +203,6 @@ dict = {
|
||||
cheats_unlockAllFlavourItems: `解锁所有<abbr title="动作表情、浮印、调色板等">装饰物品</abbr>`,
|
||||
cheats_unlockAllSkins: `解锁所有外观`,
|
||||
cheats_unlockAllCapturaScenes: `解锁所有Captura场景`,
|
||||
cheats_unlockAllDecoRecipes: `解锁所有道场配方`,
|
||||
cheats_universalPolarityEverywhere: `全局万用极性`,
|
||||
cheats_unlockDoubleCapacityPotatoesEverywhere: `全物品自带Orokin反应堆`,
|
||||
cheats_unlockExilusEverywhere: `全物品自带适配器`,
|
||||
@ -387,5 +393,35 @@ dict = {
|
||||
theme_dark: `暗色主题`,
|
||||
theme_light: `亮色主题`,
|
||||
|
||||
guildView_cheats: `[UNTRANSLATED] Clan Cheats`,
|
||||
guildView_techProjects: `研究`,
|
||||
guildView_vaultDecoRecipes: `[UNTRANSLATED] Dojo Deco Recipes`,
|
||||
guildView_alliance: `联盟`,
|
||||
guildView_members: `成员`,
|
||||
guildView_pending: `待处理`,
|
||||
guildView_classDisplay: `等級 |CLASS|`,
|
||||
guildView_tierDisplay: `|TIER| 氏族`,
|
||||
guildView_tier1: `幽灵`,
|
||||
guildView_tier2: `暗影`,
|
||||
guildView_tier3: `风暴`,
|
||||
guildView_tier4: `山脉`,
|
||||
guildView_tier5: `月亮`,
|
||||
guildView_rank_creator: `创始军阀`,
|
||||
guildView_rank_general: `将军`,
|
||||
guildView_rank_initiate: `新兵`,
|
||||
guildView_rank_leader: `首领`,
|
||||
guildView_rank_officer: `智者`,
|
||||
guildView_rank_sage: `贤者`,
|
||||
guildView_rank_soldier: `战士`,
|
||||
guildView_rank_utility: `实管`,
|
||||
guildView_rank_warlord: `军阀`,
|
||||
guildView_currency_owned: `[UNTRANSLATED] |COUNT| in Vault.`,
|
||||
guildView_bulkAddTechProjects: `[UNTRANSLATED] Add Missing Research`,
|
||||
guildView_bulkAddVaultDecoRecipes: `[UNTRANSLATED] Add Missing Dojo Deco Recipes`,
|
||||
guildView_bulkFundTechProjects: `[UNTRANSLATED] Fund All Research`,
|
||||
guildView_bulkCompleteTechProjects: `[UNTRANSLATED] Complete All Research`,
|
||||
guildView_promote: `升级`,
|
||||
guildView_demote: `降级`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user