This commit is contained in:
@@ -3,7 +3,22 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
|
||||
|
||||
export const addFriendImageController: RequestHandler = async (req, res) => {
|
||||
export const addFriendImageGetController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
await Inventory.updateOne(
|
||||
{
|
||||
accountOwnerId: accountId
|
||||
},
|
||||
{
|
||||
ActiveAvatarImageType: String(req.query.avatarImageType)
|
||||
}
|
||||
);
|
||||
|
||||
res.json({});
|
||||
};
|
||||
|
||||
export const addFriendImagePostController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const json = getJSONfromString<IUpdateGlyphRequest>(String(req.body));
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { toOid } from "../../helpers/inventoryHelpers.ts";
|
||||
import { toOid2 } from "../../helpers/inventoryHelpers.ts";
|
||||
import { Friendship } from "../../models/friendModel.ts";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "../../services/friendService.ts";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import type { IFriendInfo } from "../../types/friendTypes.ts";
|
||||
import type { Request, RequestHandler, Response } from "express";
|
||||
|
||||
// POST with {} instead of GET as of 38.5.0
|
||||
export const getFriendsController: RequestHandler = async (req: Request, res: Response) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = account._id.toString();
|
||||
const response: IGetFriendsResponse = {
|
||||
Current: [],
|
||||
IncomingFriendRequests: [],
|
||||
@@ -20,14 +21,14 @@ export const getFriendsController: RequestHandler = async (req: Request, res: Re
|
||||
for (const externalFriendship of externalFriendships) {
|
||||
if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
|
||||
response.IncomingFriendRequests.push({
|
||||
_id: toOid(externalFriendship.owner),
|
||||
_id: toOid2(externalFriendship.owner, account.BuildLabel),
|
||||
Note: externalFriendship.Note
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const internalFriendship of internalFriendships) {
|
||||
const friendInfo: IFriendInfo = {
|
||||
_id: toOid(internalFriendship.friend)
|
||||
_id: toOid2(internalFriendship.friend, account.BuildLabel)
|
||||
};
|
||||
if (externalFriendships.find(x => x.owner.equals(internalFriendship.friend))) {
|
||||
response.Current.push(friendInfo);
|
||||
|
||||
@@ -34,10 +34,9 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
const weapon = inventory[data.Category][weaponIndex];
|
||||
weapon.Features ??= 0;
|
||||
weapon.Features |= EquipmentFeatures.GILDED;
|
||||
if (data.Recipe != "webui") {
|
||||
weapon.ItemName = data.ItemName;
|
||||
weapon.XP = 0;
|
||||
}
|
||||
weapon.ItemName = data.ItemName;
|
||||
weapon.XP = 0;
|
||||
|
||||
if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
|
||||
weapon.Polarity = [
|
||||
{
|
||||
@@ -52,22 +51,20 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
|
||||
const affiliationMods = [];
|
||||
|
||||
if (data.Recipe != "webui") {
|
||||
const recipe = ExportRecipes[data.Recipe];
|
||||
inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
|
||||
ItemType: ingredient.ItemType,
|
||||
ItemCount: ingredient.ItemCount * -1
|
||||
}));
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
const recipe = ExportRecipes[data.Recipe];
|
||||
inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
|
||||
ItemType: ingredient.ItemType,
|
||||
ItemCount: ingredient.ItemCount * -1
|
||||
}));
|
||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||
|
||||
if (recipe.syndicateStandingChange) {
|
||||
const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
|
||||
affiliation.Standing += recipe.syndicateStandingChange.value;
|
||||
affiliationMods.push({
|
||||
Tag: recipe.syndicateStandingChange.tag,
|
||||
Standing: recipe.syndicateStandingChange.value
|
||||
});
|
||||
}
|
||||
if (recipe.syndicateStandingChange) {
|
||||
const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
|
||||
affiliation.Standing += recipe.syndicateStandingChange.value;
|
||||
affiliationMods.push({
|
||||
Tag: recipe.syndicateStandingChange.tag,
|
||||
Standing: recipe.syndicateStandingChange.value
|
||||
});
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { TPartialStartingGear } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
export const giveStartingGearController: RequestHandler = async (req, res) => {
|
||||
export const giveStartingGearPostController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const startingGear = getJSONfromString<TPartialStartingGear>(String(req.body));
|
||||
const inventory = await getInventory(accountId);
|
||||
@@ -14,3 +14,28 @@ export const giveStartingGearController: RequestHandler = async (req, res) => {
|
||||
|
||||
res.send(inventoryChanges);
|
||||
};
|
||||
|
||||
export const giveStartingGearGetController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
const inventoryChanges = await addStartingGear(inventory, {
|
||||
Suits: [
|
||||
{
|
||||
ItemType: String(req.query.warframeName),
|
||||
ItemId: { $oid: "0" },
|
||||
Configs: []
|
||||
}
|
||||
],
|
||||
LongGuns: [
|
||||
{
|
||||
ItemType: "/Lotus/Weapons/Tenno/Rifle/StartingRifle",
|
||||
ItemId: { $oid: "0" },
|
||||
Configs: []
|
||||
}
|
||||
]
|
||||
});
|
||||
await inventory.save();
|
||||
|
||||
res.send(inventoryChanges); // Not sure if this is even needed
|
||||
};
|
||||
|
||||
@@ -271,7 +271,11 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
await inventory.save();
|
||||
|
||||
response.json(
|
||||
await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query, account.BuildLabel)
|
||||
await getInventoryResponse(
|
||||
inventory,
|
||||
"xpBasedLevelCapDisabled" in request.query,
|
||||
"ignoreBuildLabel" in request.query ? undefined : account.BuildLabel
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -410,6 +414,7 @@ export const getInventoryResponse = async (
|
||||
for (const equipment of inventoryResponse[key]) {
|
||||
equipment.Features ??= 0;
|
||||
equipment.Features |= EquipmentFeatures.ARCANE_SLOT;
|
||||
equipment.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,12 +114,13 @@ const createLoginResponse = (
|
||||
Nonce: account.Nonce,
|
||||
BuildLabel: buildLabel
|
||||
};
|
||||
if (version_compare(buildLabel, "2014.10.24.08.24") >= 0) {
|
||||
// U15 and up
|
||||
if (version_compare(buildLabel, "2014.04.10.17.47") >= 0) {
|
||||
// U13 and up
|
||||
resp.CountryCode = account.CountryCode;
|
||||
} else {
|
||||
// U8
|
||||
resp.NatHash = "0";
|
||||
// U12 and down
|
||||
resp.NatHash =
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
resp.SteamId = "0";
|
||||
}
|
||||
if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
|
||||
|
||||
@@ -17,14 +17,13 @@ interface ITrainingResultsResponse {
|
||||
InventoryChanges: IInventoryChanges;
|
||||
}
|
||||
|
||||
const trainingResultController: RequestHandler = async (req, res): Promise<void> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
const trainingResults = getJSONfromString<ITrainingResultsRequest>(String(req.body));
|
||||
|
||||
const handleTrainingProgress = async (
|
||||
accountId: string,
|
||||
numLevelsGained: number
|
||||
): Promise<ITrainingResultsResponse> => {
|
||||
const inventory = await getInventory(accountId, "TrainingDate PlayerLevel TradesRemaining noMasteryRankUpCooldown");
|
||||
|
||||
if (trainingResults.numLevelsGained == 1) {
|
||||
if (numLevelsGained === 1) {
|
||||
let time = Date.now();
|
||||
if (!inventory.noMasteryRankUpCooldown) {
|
||||
time += unixTimesInMs.hour * 23;
|
||||
@@ -67,13 +66,27 @@ const trainingResultController: RequestHandler = async (req, res): Promise<void>
|
||||
|
||||
const changedinventory = await inventory.save();
|
||||
|
||||
res.json({
|
||||
return {
|
||||
NewTrainingDate: {
|
||||
$date: { $numberLong: changedinventory.TrainingDate.getTime().toString() }
|
||||
},
|
||||
NewLevel: trainingResults.numLevelsGained == 1 ? changedinventory.PlayerLevel : inventory.PlayerLevel,
|
||||
NewLevel: numLevelsGained == 1 ? changedinventory.PlayerLevel : inventory.PlayerLevel,
|
||||
InventoryChanges: {}
|
||||
} satisfies ITrainingResultsResponse);
|
||||
};
|
||||
};
|
||||
|
||||
export { trainingResultController };
|
||||
export const trainingResultPostController: RequestHandler = async (req, res): Promise<void> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const { numLevelsGained } = getJSONfromString<ITrainingResultsRequest>(String(req.body));
|
||||
|
||||
const response = await handleTrainingProgress(accountId, numLevelsGained);
|
||||
res.json(response satisfies ITrainingResultsResponse);
|
||||
};
|
||||
|
||||
export const trainingResultGetController: RequestHandler = async (req, res): Promise<void> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const numLevelsGained = Number(req.query.numLevelsGained ?? 0);
|
||||
|
||||
const response = await handleTrainingProgress(accountId, numLevelsGained);
|
||||
res.json(response satisfies ITrainingResultsResponse);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { RequestHandler } from "express";
|
||||
import { config, syncConfigWithDatabase } from "../../services/configService.ts";
|
||||
import { getAccountForRequest, isAdministrator } from "../../services/loginService.ts";
|
||||
import { saveConfig } from "../../services/configWriterService.ts";
|
||||
import { sendWsBroadcastEx } from "../../services/wsService.ts";
|
||||
import { sendWsBroadcastEx, sendWsBroadcast } from "../../services/wsService.ts";
|
||||
|
||||
export const getConfigController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
@@ -21,11 +21,14 @@ export const getConfigController: RequestHandler = async (req, res) => {
|
||||
export const setConfigController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
if (isAdministrator(account)) {
|
||||
let isWorldStateUpdate = false;
|
||||
for (const [id, value] of Object.entries(req.body as Record<string, boolean | string | number>)) {
|
||||
if (id.startsWith("worldState")) isWorldStateUpdate = true;
|
||||
const [obj, idx] = configIdToIndexable(id);
|
||||
obj[idx] = value;
|
||||
}
|
||||
sendWsBroadcastEx({ config_reloaded: true }, undefined, parseInt(String(req.query.wsid)));
|
||||
if (isWorldStateUpdate) sendWsBroadcast({ sync_world_state: true });
|
||||
syncConfigWithDatabase();
|
||||
await saveConfig();
|
||||
res.end();
|
||||
|
||||
34
src/controllers/custom/equipmentFeaturesController.ts
Normal file
34
src/controllers/custom/equipmentFeaturesController.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { EquipmentFeatures } from "../../types/equipmentTypes.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
|
||||
export const equipmentFeaturesController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const category = req.query.Category as TEquipmentKey;
|
||||
const inventory = await getInventory(
|
||||
accountId,
|
||||
`${category} unlockDoubleCapacityPotatoesEverywhere unlockExilusEverywhere unlockArcanesEverywhere`
|
||||
);
|
||||
const bit = Number(req.query.bit) as EquipmentFeatures;
|
||||
if (
|
||||
(inventory.unlockDoubleCapacityPotatoesEverywhere && bit === EquipmentFeatures.DOUBLE_CAPACITY) ||
|
||||
(inventory.unlockExilusEverywhere && bit === EquipmentFeatures.UTILITY_SLOT) ||
|
||||
(inventory.unlockArcanesEverywhere &&
|
||||
(bit === EquipmentFeatures.ARCANE_SLOT || bit === EquipmentFeatures.SECOND_ARCANE_SLOT))
|
||||
) {
|
||||
res.status(400).end();
|
||||
}
|
||||
const item = inventory[category].id(req.query.ItemId as string);
|
||||
if (item) {
|
||||
item.Features ??= 0;
|
||||
item.Features ^= bit;
|
||||
await inventory.save();
|
||||
sendWsBroadcastTo(accountId, { sync_inventory: true });
|
||||
res.status(200).end();
|
||||
} else {
|
||||
res.status(400).end();
|
||||
}
|
||||
};
|
||||
@@ -12,11 +12,16 @@ export const importController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IImportRequest;
|
||||
|
||||
let anyKnownKey = false;
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
importInventory(inventory, request.inventory);
|
||||
await inventory.save();
|
||||
if (importInventory(inventory, request.inventory)) {
|
||||
anyKnownKey = true;
|
||||
await inventory.save();
|
||||
}
|
||||
|
||||
if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) {
|
||||
anyKnownKey = true;
|
||||
const loadout = await getLoadout(accountId);
|
||||
importLoadOutPresets(loadout, request.inventory.LoadOutPresets);
|
||||
await loadout.save();
|
||||
@@ -27,12 +32,13 @@ export const importController: RequestHandler = async (req, res) => {
|
||||
"Apartment" in request.inventory ||
|
||||
"TailorShop" in request.inventory
|
||||
) {
|
||||
anyKnownKey = true;
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
importPersonalRooms(personalRooms, request.inventory);
|
||||
await personalRooms.save();
|
||||
}
|
||||
|
||||
res.end();
|
||||
res.json(anyKnownKey);
|
||||
broadcastInventoryUpdate(req);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { getStats } from "../../services/statsService.ts";
|
||||
import type { IStatsClient } from "../../types/statTypes.ts";
|
||||
|
||||
const viewController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const accountId = String(req.query.id ?? req.query.lookupId);
|
||||
const inventory = await getInventory(accountId, "XPInfo");
|
||||
const playerStats = await getStats(accountId);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { abortDojoComponentController } from "../controllers/api/abortDojoCompon
|
||||
import { abortDojoComponentDestructionController } from "../controllers/api/abortDojoComponentDestructionController.ts";
|
||||
import { activateRandomModController } from "../controllers/api/activateRandomModController.ts";
|
||||
import { addFriendController } from "../controllers/api/addFriendController.ts";
|
||||
import { addFriendImageController } from "../controllers/api/addFriendImageController.ts";
|
||||
import { addFriendImageGetController, addFriendImagePostController } from "../controllers/api/addFriendImageController.ts";
|
||||
import { addIgnoredUserController } from "../controllers/api/addIgnoredUserController.ts";
|
||||
import { addPendingFriendController } from "../controllers/api/addPendingFriendController.ts";
|
||||
import { addToAllianceController } from "../controllers/api/addToAllianceController.ts";
|
||||
@@ -80,7 +80,7 @@ import { giveKeyChainTriggeredItemsController } from "../controllers/api/giveKey
|
||||
import { giveKeyChainTriggeredMessageController } from "../controllers/api/giveKeyChainTriggeredMessageController.ts";
|
||||
import { giveQuestKeyRewardController } from "../controllers/api/giveQuestKeyRewardController.ts";
|
||||
import { giveShipDecoAndLoreFragmentController } from "../controllers/api/giveShipDecoAndLoreFragmentController.ts";
|
||||
import { giveStartingGearController } from "../controllers/api/giveStartingGearController.ts";
|
||||
import { giveStartingGearGetController, giveStartingGearPostController } from "../controllers/api/giveStartingGearController.ts";
|
||||
import { guildTechController } from "../controllers/api/guildTechController.ts";
|
||||
import { hostSessionController } from "../controllers/api/hostSessionController.ts";
|
||||
import { hubBlessingController } from "../controllers/api/hubBlessingController.ts";
|
||||
@@ -158,7 +158,7 @@ import { syndicateSacrificeController } from "../controllers/api/syndicateSacrif
|
||||
import { syndicateStandingBonusController } from "../controllers/api/syndicateStandingBonusController.ts";
|
||||
import { tauntHistoryController } from "../controllers/api/tauntHistoryController.ts";
|
||||
import { tradingController } from "../controllers/api/tradingController.ts";
|
||||
import { trainingResultController } from "../controllers/api/trainingResultController.ts";
|
||||
import { trainingResultGetController, trainingResultPostController } from "../controllers/api/trainingResultController.ts";
|
||||
import { umbraController } from "../controllers/api/umbraController.ts";
|
||||
import { unlockShipFeatureController } from "../controllers/api/unlockShipFeatureController.ts";
|
||||
import { updateAlignmentController } from "../controllers/api/updateAlignmentController.ts";
|
||||
@@ -178,6 +178,7 @@ const apiRouter = express.Router();
|
||||
// get
|
||||
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
||||
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
||||
apiRouter.get("/addFriendImage.php", addFriendImageGetController) // U17 and below
|
||||
apiRouter.get("/apartment.php", apartmentController);
|
||||
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
||||
apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||
@@ -208,6 +209,7 @@ apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController);
|
||||
apiRouter.get("/getPastWeeklyChallenges.php", getPastWeeklyChallengesController)
|
||||
apiRouter.get("/getShip.php", getShipController);
|
||||
apiRouter.get("/getShipDecos.php", (_req, res) => { res.end(); }); // needed to log in on U22.8
|
||||
apiRouter.get("/giveStartingGear.php", giveStartingGearGetController);
|
||||
apiRouter.get("/getVendorInfo.php", getVendorInfoController);
|
||||
apiRouter.get("/hub", hubController);
|
||||
apiRouter.get("/hubInstances", hubInstancesController);
|
||||
@@ -235,6 +237,7 @@ apiRouter.get("/startLibraryDailyTask.php", startLibraryDailyTaskController);
|
||||
apiRouter.get("/startLibraryPersonalTarget.php", startLibraryPersonalTargetController);
|
||||
apiRouter.get("/surveys.php", surveysController);
|
||||
apiRouter.get("/trading.php", tradingController);
|
||||
apiRouter.get("/trainingResult.php", trainingResultGetController);
|
||||
apiRouter.get("/updateSession.php", updateSessionGetController);
|
||||
apiRouter.get("/upgradeOperator.php", upgradeOperatorController);
|
||||
apiRouter.get("/worldState.php", worldStateController); // U8
|
||||
@@ -243,7 +246,7 @@ apiRouter.get("/worldState.php", worldStateController); // U8
|
||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
||||
apiRouter.post("/addFriend.php", addFriendController);
|
||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
||||
apiRouter.post("/addFriendImage.php", addFriendImagePostController);
|
||||
apiRouter.post("/addIgnoredUser.php", addIgnoredUserController);
|
||||
apiRouter.post("/addPendingFriend.php", addPendingFriendController);
|
||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
|
||||
@@ -297,7 +300,7 @@ apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsCont
|
||||
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
||||
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
||||
apiRouter.post("/giveShipDecoAndLoreFragment.php", giveShipDecoAndLoreFragmentController);
|
||||
apiRouter.post("/giveStartingGear.php", giveStartingGearController);
|
||||
apiRouter.post("/giveStartingGear.php", giveStartingGearPostController);
|
||||
apiRouter.post("/guildTech.php", guildTechController);
|
||||
apiRouter.post("/hostSession.php", hostSessionController);
|
||||
apiRouter.post("/hubBlessing.php", hubBlessingController);
|
||||
@@ -356,7 +359,7 @@ apiRouter.post("/stepSequencers.php", stepSequencersController);
|
||||
apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController);
|
||||
apiRouter.post("/syndicateStandingBonus.php", syndicateStandingBonusController);
|
||||
apiRouter.post("/tauntHistory.php", tauntHistoryController);
|
||||
apiRouter.post("/trainingResult.php", trainingResultController);
|
||||
apiRouter.post("/trainingResult.php", trainingResultPostController);
|
||||
apiRouter.post("/umbra.php", umbraController);
|
||||
apiRouter.post("/unlockShipFeature.php", unlockShipFeatureController);
|
||||
apiRouter.post("/updateAlignment.php", updateAlignmentController);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import express from "express";
|
||||
|
||||
import { tunablesController } from "../controllers/custom/tunablesController.ts";
|
||||
import { equipmentFeaturesController } from "../controllers/custom/equipmentFeaturesController.ts";
|
||||
import { getItemListsController } from "../controllers/custom/getItemListsController.ts";
|
||||
import { pushArchonCrystalUpgradeController } from "../controllers/custom/pushArchonCrystalUpgradeController.ts";
|
||||
import { popArchonCrystalUpgradeController } from "../controllers/custom/popArchonCrystalUpgradeController.ts";
|
||||
@@ -53,6 +54,7 @@ import { getConfigController, setConfigController } from "../controllers/custom/
|
||||
const customRouter = express.Router();
|
||||
|
||||
customRouter.get("/tunables.json", tunablesController);
|
||||
customRouter.get("/equipmentFeatures", equipmentFeaturesController);
|
||||
customRouter.get("/getItemLists", getItemListsController);
|
||||
customRouter.get("/pushArchonCrystalUpgrade", pushArchonCrystalUpgradeController);
|
||||
customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { leaderboardController } from "../controllers/stats/leaderboardControlle
|
||||
const statsRouter = express.Router();
|
||||
|
||||
statsRouter.get("/view.php", viewController);
|
||||
statsRouter.get("/profileStats.php", viewController);
|
||||
statsRouter.post("/upload.php", uploadController);
|
||||
statsRouter.post("/leaderboardWeekly.php", leaderboardController);
|
||||
statsRouter.post("/leaderboardArchived.php", leaderboardController);
|
||||
|
||||
@@ -423,3 +423,45 @@ export const getConquest = (
|
||||
RandomSeed: rng.randomInt(0, 1_000_000)
|
||||
};
|
||||
};
|
||||
|
||||
export const getMissionTypeForLegacyOverride = (missionType: TMissionType, conquestType: TConquestType): string => {
|
||||
if (missionType == "MT_ENDLESS_CAPTURE") {
|
||||
return "EndlessCapture";
|
||||
}
|
||||
let str = missionType.substring(3, 4).toUpperCase() + missionType.substring(4).toLowerCase();
|
||||
if (str == "Artifact") {
|
||||
str = "Disruption";
|
||||
}
|
||||
if (str == "Defense" && conquestType == "CT_LAB") {
|
||||
str = "DualDefense";
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
export const factionToInt = (faction: TFaction | "FC_TENNO"): number => {
|
||||
switch (faction) {
|
||||
case "FC_GRINEER":
|
||||
return 0;
|
||||
case "FC_CORPUS":
|
||||
return 1;
|
||||
case "FC_INFESTATION":
|
||||
return 2;
|
||||
case "FC_OROKIN":
|
||||
return 3;
|
||||
case "FC_RED_VEIL":
|
||||
return 4;
|
||||
case "FC_SENTIENT":
|
||||
return 5;
|
||||
case "FC_NARMER":
|
||||
return 6;
|
||||
case "FC_MITW":
|
||||
return 7;
|
||||
case "FC_SCALDRA":
|
||||
return 8;
|
||||
case "FC_TECHROT":
|
||||
return 9;
|
||||
case "FC_DUVIRI":
|
||||
return 10;
|
||||
}
|
||||
throw new Error(`unexpected faction ${faction}`);
|
||||
};
|
||||
|
||||
@@ -254,17 +254,21 @@ const convertItemConfig = <T extends IItemConfig>(client: T): T => {
|
||||
};
|
||||
};
|
||||
|
||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
|
||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): boolean => {
|
||||
let anyKnownKey = false;
|
||||
for (const key of equipmentKeys) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IEquipmentDatabase>(db[key], client[key].map(convertEquipment));
|
||||
}
|
||||
}
|
||||
if (client.WeaponSkins !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IWeaponSkinDatabase>(db.WeaponSkins, client.WeaponSkins.map(convertWeaponSkin));
|
||||
}
|
||||
for (const key of ["Upgrades", "CrewShipSalvagedWeaponSkins", "CrewShipWeaponSkins"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IUpgradeDatabase>(db[key], client[key].map(convertUpgrade));
|
||||
}
|
||||
}
|
||||
@@ -280,6 +284,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"CrewShipRawSalvage"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key].splice(0, db[key].length);
|
||||
client[key].forEach(x => {
|
||||
db[key].push({
|
||||
@@ -291,11 +296,13 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
}
|
||||
for (const key of ["AdultOperatorLoadOuts", "OperatorLoadOuts", "KahlLoadOuts"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
||||
}
|
||||
}
|
||||
for (const key of slotNames) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceSlots(db[key], client[key]);
|
||||
}
|
||||
}
|
||||
@@ -312,6 +319,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"Counselor"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -338,6 +346,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EchoesHexConquestCacheScoreMission"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -353,6 +362,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"ActiveAvatarImageType"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -369,6 +379,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EchoesHexConquestActiveStickers"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
@@ -382,103 +393,133 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
"EntratiVaultCountResetDate"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = fromMongoDate(client[key]);
|
||||
}
|
||||
}
|
||||
// IRewardAtten[]
|
||||
for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.XPInfo !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.XPInfo = client.XPInfo;
|
||||
}
|
||||
if (client.CurrentLoadOutIds !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CurrentLoadOutIds = client.CurrentLoadOutIds;
|
||||
}
|
||||
if (client.Affiliations !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Affiliations = client.Affiliations;
|
||||
}
|
||||
if (client.FusionTreasures !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FusionTreasures = client.FusionTreasures;
|
||||
}
|
||||
if (client.FocusUpgrades !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FocusUpgrades = client.FocusUpgrades;
|
||||
}
|
||||
if (client.EvolutionProgress !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.EvolutionProgress = client.EvolutionProgress;
|
||||
}
|
||||
if (client.InfestedFoundry !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.InfestedFoundry = convertInfestedFoundry(client.InfestedFoundry);
|
||||
}
|
||||
if (client.DialogueHistory !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.DialogueHistory = convertDialogueHistory(client.DialogueHistory);
|
||||
}
|
||||
if (client.CustomMarkers !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CustomMarkers = client.CustomMarkers;
|
||||
}
|
||||
if (client.ChallengeProgress !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.ChallengeProgress = client.ChallengeProgress;
|
||||
}
|
||||
if (client.QuestKeys !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IQuestKeyDatabase>(db.QuestKeys, client.QuestKeys.map(convertQuestKey));
|
||||
}
|
||||
if (client.LastRegionPlayed !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LastRegionPlayed = client.LastRegionPlayed;
|
||||
}
|
||||
if (client.PendingRecipes !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IPendingRecipeDatabase>(db.PendingRecipes, client.PendingRecipes.map(convertPendingRecipe));
|
||||
}
|
||||
if (client.TauntHistory !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.TauntHistory = client.TauntHistory;
|
||||
}
|
||||
if (client.LoreFragmentScans !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LoreFragmentScans = client.LoreFragmentScans;
|
||||
}
|
||||
for (const key of ["PendingSpectreLoadouts", "SpectreLoadouts"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.FocusXP !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FocusXP = client.FocusXP;
|
||||
}
|
||||
for (const key of ["Alignment", "AlignmentReplay"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.StepSequencers !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.StepSequencers = client.StepSequencers;
|
||||
}
|
||||
if (client.CompletedJobChains !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CompletedJobChains = client.CompletedJobChains;
|
||||
}
|
||||
if (client.Nemesis !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Nemesis = convertNemesis(client.Nemesis);
|
||||
}
|
||||
if (client.PlayerSkills !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.PlayerSkills = client.PlayerSkills;
|
||||
}
|
||||
if (client.LotusCustomization !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.LotusCustomization = convertItemConfig(client.LotusCustomization);
|
||||
}
|
||||
if (client.CollectibleSeries !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.CollectibleSeries = client.CollectibleSeries;
|
||||
}
|
||||
for (const key of ["LibraryAvailableDailyTaskInfo", "LibraryActiveDailyTaskInfo"] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db[key] = client[key];
|
||||
}
|
||||
}
|
||||
if (client.SongChallenges !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.SongChallenges = client.SongChallenges;
|
||||
}
|
||||
if (client.Missions !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Missions = client.Missions;
|
||||
}
|
||||
if (client.FlavourItems !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.FlavourItems.splice(0, db.FlavourItems.length);
|
||||
client.FlavourItems.forEach(x => {
|
||||
db.FlavourItems.push({
|
||||
@@ -487,11 +528,14 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
});
|
||||
}
|
||||
if (client.Accolades !== undefined) {
|
||||
anyKnownKey = true;
|
||||
db.Accolades = client.Accolades;
|
||||
}
|
||||
if (client.Boosters !== undefined) {
|
||||
anyKnownKey = true;
|
||||
replaceArray<IBooster>(db.Boosters, client.Boosters);
|
||||
}
|
||||
return anyKnownKey;
|
||||
};
|
||||
|
||||
export const importLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
||||
|
||||
@@ -137,18 +137,19 @@ export const createInventory = async (
|
||||
|
||||
export const addStartingGear = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
startingGear?: TPartialStartingGear
|
||||
startingGear?: Partial<TPartialStartingGear>
|
||||
): Promise<IInventoryChanges> => {
|
||||
if (inventory.ReceivedStartingGear) {
|
||||
throw new Error(`account has already received starting gear`);
|
||||
}
|
||||
inventory.ReceivedStartingGear = true;
|
||||
|
||||
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
|
||||
const { LongGuns, Pistols, Suits, Melee } = {
|
||||
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
|
||||
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
|
||||
Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }],
|
||||
Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }]
|
||||
Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }],
|
||||
...startingGear
|
||||
};
|
||||
|
||||
//TODO: properly merge weapon bin changes it is currently static here
|
||||
|
||||
@@ -1524,7 +1524,38 @@ export const addMissionRewards = async (
|
||||
syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
|
||||
if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
|
||||
}
|
||||
if (syndicateEntry && syndicateEntry.Jobs && !jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs")) {
|
||||
const specialCase = [
|
||||
{
|
||||
endings: ["Heists/HeistProfitTakerBountyOne"],
|
||||
stage: 2,
|
||||
amount: 1000,
|
||||
tag: "SolarisSyndicate"
|
||||
},
|
||||
{
|
||||
endings: [
|
||||
"Heists/HeistProfitTakerBountyTwo",
|
||||
"Heists/HeistProfitTakerBountyThree",
|
||||
"Heists/HeistProfitTakerBountyFour",
|
||||
"Heists/HeistExploiterBountyOne"
|
||||
],
|
||||
amount: 1000,
|
||||
tag: "SolarisSyndicate"
|
||||
},
|
||||
{ endings: ["Hunts/AllTeralystsHunt"], stage: 2, amount: 5000, tag: "CetusSyndicate" },
|
||||
{
|
||||
endings: ["Hunts/TeralystHunt"],
|
||||
amount: 1000,
|
||||
tag: "CetusSyndicate"
|
||||
},
|
||||
{ endings: ["Jobs/NewbieJob"], amount: 200, tag: "CetusSyndicate" }
|
||||
];
|
||||
const match = specialCase.find(rule => rule.endings.some(e => jobType.endsWith(e)));
|
||||
if (match) {
|
||||
const specialCaseReward = match.stage === undefined || rewardInfo.JobStage === match.stage ? match : null;
|
||||
if (specialCaseReward) {
|
||||
addStanding(inventory, match.tag, Math.floor(match.amount / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
|
||||
}
|
||||
} else if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
|
||||
if (
|
||||
[
|
||||
@@ -1582,42 +1613,15 @@ export const addMissionRewards = async (
|
||||
);
|
||||
logger.warning(`currentJob`, { currentJob: currentJob });
|
||||
}
|
||||
} else {
|
||||
const specialCase = [
|
||||
{ endings: ["Heists/HeistProfitTakerBountyOne"], stage: 2, amount: 1000 },
|
||||
{ endings: ["Hunts/AllTeralystsHunt"], stage: 2, amount: 5000 },
|
||||
{
|
||||
endings: [
|
||||
"Hunts/TeralystHunt",
|
||||
"Heists/HeistProfitTakerBountyTwo",
|
||||
"Heists/HeistProfitTakerBountyThree",
|
||||
"Heists/HeistProfitTakerBountyFour",
|
||||
"Heists/HeistExploiterBountyOne"
|
||||
],
|
||||
amount: 1000
|
||||
}
|
||||
];
|
||||
const specialCaseReward = specialCase.find(
|
||||
rule =>
|
||||
rule.endings.some(e => jobType.endsWith(e)) &&
|
||||
(rule.stage === undefined || rewardInfo.JobStage === rule.stage)
|
||||
} else if (!jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs")) {
|
||||
addStanding(
|
||||
inventory,
|
||||
syndicateEntry.Tag,
|
||||
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
|
||||
AffiliationMods
|
||||
);
|
||||
|
||||
if (specialCaseReward) {
|
||||
addStanding(inventory, syndicateEntry.Tag, specialCaseReward.amount, AffiliationMods);
|
||||
} else {
|
||||
addStanding(
|
||||
inventory,
|
||||
syndicateEntry.Tag,
|
||||
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
|
||||
AffiliationMods
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
|
||||
addStanding(inventory, "CetusSyndicate", Math.floor(200 / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
|
||||
}
|
||||
}
|
||||
|
||||
if (rewardInfo.challengeMissionId) {
|
||||
@@ -1971,7 +1975,8 @@ function getRandomMissionDrops(
|
||||
if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
|
||||
}
|
||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
|
||||
let job;
|
||||
if (RewardInfo.JobTier && RewardInfo.JobTier > 0) job = syndicateEntry.Jobs[RewardInfo.JobTier];
|
||||
|
||||
if (syndicateEntry.Tag === "EntratiSyndicate") {
|
||||
if (
|
||||
@@ -2075,38 +2080,40 @@ function getRandomMissionDrops(
|
||||
) {
|
||||
job = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
|
||||
}
|
||||
rewardManifests = [job.rewards];
|
||||
if (job.xpAmounts.length > 1) {
|
||||
const curentStage = RewardInfo.JobStage! + 1;
|
||||
const totalStage = job.xpAmounts.length;
|
||||
let tableIndex = 1; // Stage 2, Stage 3 of 4, and Stage 3 of 5
|
||||
if (job) {
|
||||
rewardManifests = [job.rewards];
|
||||
if (job.xpAmounts.length > 1) {
|
||||
const curentStage = RewardInfo.JobStage! + 1;
|
||||
const totalStage = job.xpAmounts.length;
|
||||
let tableIndex = 1; // Stage 2, Stage 3 of 4, and Stage 3 of 5
|
||||
|
||||
if (curentStage == 1) {
|
||||
tableIndex = 0;
|
||||
} else if (curentStage == totalStage) {
|
||||
tableIndex = 3;
|
||||
} else if (totalStage == 5 && curentStage == 4) {
|
||||
tableIndex = 2;
|
||||
}
|
||||
if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
|
||||
if (RewardInfo.JobStage === job.xpAmounts.length - 1) {
|
||||
rotations = [0, 1, 2];
|
||||
if (curentStage == 1) {
|
||||
tableIndex = 0;
|
||||
} else if (curentStage == totalStage) {
|
||||
tableIndex = 3;
|
||||
} else if (totalStage == 5 && curentStage == 4) {
|
||||
tableIndex = 2;
|
||||
}
|
||||
if (jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob")) {
|
||||
if (RewardInfo.JobStage === job.xpAmounts.length - 1) {
|
||||
rotations = [0, 1, 2];
|
||||
} else {
|
||||
rewardManifests = [];
|
||||
}
|
||||
} else {
|
||||
rewardManifests = [];
|
||||
rotations = [tableIndex];
|
||||
}
|
||||
} else {
|
||||
rotations = [tableIndex];
|
||||
rotations = [0];
|
||||
}
|
||||
if (
|
||||
RewardInfo.Q &&
|
||||
(RewardInfo.JobStage === job.xpAmounts.length - 1 || jobType.endsWith("VaultBounty")) &&
|
||||
!jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob") &&
|
||||
!isEndlessJob
|
||||
) {
|
||||
rotations.push(ExportRewards[job.rewards].length - 1);
|
||||
}
|
||||
} else {
|
||||
rotations = [0];
|
||||
}
|
||||
if (
|
||||
RewardInfo.Q &&
|
||||
(RewardInfo.JobStage === job.xpAmounts.length - 1 || jobType.endsWith("VaultBounty")) &&
|
||||
!jobType.startsWith("/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob") &&
|
||||
!isEndlessJob
|
||||
) {
|
||||
rotations.push(ExportRewards[job.rewards].length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,6 +491,7 @@ export const handleStoreItemAcquisition = async (
|
||||
const slotPurchaseNameToSlotName: Record<string, { name: SlotNames; purchaseQuantity: number }> = {
|
||||
SuitSlotItem: { name: "SuitBin", purchaseQuantity: 1 },
|
||||
TwoSentinelSlotItem: { name: "SentinelBin", purchaseQuantity: 2 },
|
||||
WeaponSlotItem: { name: "WeaponBin", purchaseQuantity: 1 },
|
||||
TwoWeaponSlotItem: { name: "WeaponBin", purchaseQuantity: 2 },
|
||||
SpaceSuitSlotItem: { name: "SpaceSuitBin", purchaseQuantity: 1 },
|
||||
TwoSpaceWeaponSlotItem: { name: "SpaceWeaponBin", purchaseQuantity: 2 },
|
||||
@@ -514,7 +515,9 @@ const handleSlotPurchase = (
|
||||
): IPurchaseResponse => {
|
||||
logger.debug(`slot name ${slotPurchaseNameFull}`);
|
||||
const slotPurchaseName = slotPurchaseNameFull.substring(slotPurchaseNameFull.lastIndexOf("/") + 1);
|
||||
if (!(slotPurchaseName in slotPurchaseNameToSlotName)) throw new Error(`invalid slot name ${slotPurchaseName}`);
|
||||
if (!(slotPurchaseName in slotPurchaseNameToSlotName)) {
|
||||
throw new Error(`invalid slot purchase name ${slotPurchaseName}`);
|
||||
}
|
||||
logger.debug(`slot purchase name ${slotPurchaseName}`);
|
||||
|
||||
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name;
|
||||
|
||||
@@ -41,7 +41,7 @@ import type {
|
||||
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers.ts";
|
||||
import { logger } from "../utils/logger.ts";
|
||||
import { DailyDeal, Fissure } from "../models/worldStateModel.ts";
|
||||
import { getConquest } from "./conquestService.ts";
|
||||
import { factionToInt, getConquest, getMissionTypeForLegacyOverride } from "./conquestService.ts";
|
||||
|
||||
const sortieBosses = [
|
||||
"SORTIE_BOSS_HYENA",
|
||||
@@ -3562,13 +3562,11 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1));
|
||||
}
|
||||
|
||||
const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[week % 4];
|
||||
const labConquest = getConquest("CT_LAB", week, null);
|
||||
const hexConquest = getConquest("CT_HEX", week, season);
|
||||
if (!buildLabel || version_compare(buildLabel, "2025.10.14.16.10") >= 0) {
|
||||
worldState.Conquests = [];
|
||||
{
|
||||
const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[week % 4];
|
||||
worldState.Conquests.push(getConquest("CT_LAB", week, null));
|
||||
worldState.Conquests.push(getConquest("CT_HEX", week, season));
|
||||
}
|
||||
worldState.Conquests = [labConquest, hexConquest];
|
||||
if (isBeforeNextExpectedWorldStateRefresh(timeMs, weekEnd)) {
|
||||
const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[(week + 1) % 4];
|
||||
worldState.Conquests.push(getConquest("CT_LAB", week, null));
|
||||
@@ -3621,6 +3619,19 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
e: cheeseEnd,
|
||||
n: cheeseNext
|
||||
},
|
||||
lqo: {
|
||||
mt: labConquest.Missions.map(x => getMissionTypeForLegacyOverride(x.missionType, "CT_LAB")),
|
||||
mv: labConquest.Missions.map(x => x.difficulties[1].deviation),
|
||||
c: labConquest.Missions.map(x => x.difficulties[1].risks),
|
||||
fv: labConquest.Variables
|
||||
},
|
||||
hqo: {
|
||||
mt: hexConquest.Missions.map(x => getMissionTypeForLegacyOverride(x.missionType, "CT_HEX")),
|
||||
mv: hexConquest.Missions.map(x => x.difficulties[1].deviation),
|
||||
mf: hexConquest.Missions.map(x => factionToInt(x.faction)),
|
||||
c: hexConquest.Missions.map(x => x.difficulties[1].risks),
|
||||
fv: hexConquest.Variables
|
||||
},
|
||||
sfn: [550, 553, 554, 555][halfHour % 4]
|
||||
};
|
||||
if (Array.isArray(config.worldState?.circuitGameModes)) {
|
||||
@@ -3628,6 +3639,15 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
}
|
||||
worldState.Tmp = JSON.stringify(tmp);
|
||||
|
||||
// This must be the last field in these versions.
|
||||
if (
|
||||
buildLabel &&
|
||||
version_compare(buildLabel, "2016.08.19.17.12") >= 0 &&
|
||||
version_compare(buildLabel, "2017.05.05.15.41") <= 0
|
||||
) {
|
||||
worldState.WorldSeed = "4763605";
|
||||
}
|
||||
|
||||
return worldState;
|
||||
};
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ interface IWsMsgToClient {
|
||||
|
||||
// to game/bootstrapper (https://openwf.io/bootstrapper-manual)
|
||||
sync_inventory?: boolean;
|
||||
sync_world_state?: boolean;
|
||||
tunables?: ITunables;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { IMissionReward, TFaction, TMissionType } from "warframe-public-exp
|
||||
import type { IMongoDate, IOid } from "./commonTypes.ts";
|
||||
|
||||
export interface IWorldState {
|
||||
WorldSeed?: string;
|
||||
Version: number; // for goals
|
||||
BuildLabel: string;
|
||||
Time: number;
|
||||
@@ -485,9 +486,9 @@ interface IFbst {
|
||||
|
||||
// < 40.0.0
|
||||
interface IConquestOverride {
|
||||
mt?: string[]; // mission types but "Exterminate" instead of "MT_EXTERMINATION", etc. and "DualDefense" instead of "Defense" for hex conquest
|
||||
mt?: string[];
|
||||
mv?: string[];
|
||||
mf?: number[]; // hex conquest only
|
||||
mf?: number[];
|
||||
c?: [string, string][];
|
||||
fv?: string[];
|
||||
}
|
||||
|
||||
@@ -840,6 +840,10 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="equipmentFeatures-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_equipmentFeaturesLabel"></h5>
|
||||
<div id="equipmentFeaturesButtons-card" class="card-body d-flex flex-wrap gap-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-route="/webui/mods" data-title="Mods | OpenWF WebUI">
|
||||
<p class="mb-3 inventory-update-note"></p>
|
||||
|
||||
@@ -734,7 +734,7 @@ const accountCheats = document.querySelectorAll("#account-cheats input[id]");
|
||||
|
||||
// Assumes that caller revalidates authz
|
||||
function updateInventory() {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1");
|
||||
req.done(data => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
window.didInitialInventoryUpdate = true;
|
||||
@@ -912,12 +912,7 @@ function updateInventory() {
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (
|
||||
["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(
|
||||
category
|
||||
) ||
|
||||
modularWeapons.includes(item.ItemType)
|
||||
) {
|
||||
{
|
||||
const a = document.createElement("a");
|
||||
a.href =
|
||||
"/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
|
||||
@@ -930,7 +925,7 @@ function updateInventory() {
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
gildEquipment(category, item.ItemId.$oid);
|
||||
equipmentFeatures(category, item.ItemId.$oid, 8);
|
||||
};
|
||||
a.title = loc("code_gild");
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>`;
|
||||
@@ -1560,6 +1555,57 @@ function updateInventory() {
|
||||
$("#detailedView-title").text(itemName);
|
||||
}
|
||||
|
||||
{
|
||||
document.getElementById("equipmentFeatures-card").classList.remove("d-none");
|
||||
const buttonsCard = document.getElementById("equipmentFeaturesButtons-card");
|
||||
buttonsCard.innerHTML = "";
|
||||
item.Features ??= 0;
|
||||
const bits = [];
|
||||
if (category != "OperatorAmps") bits.push(1);
|
||||
if (["Suits", "LongGuns", "Pistols", "Melee"].includes(category)) bits.push(2);
|
||||
if (modularWeapons.includes(item.ItemType)) bits.push(8);
|
||||
if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "OperatorAmps"].includes(category))
|
||||
bits.push(32);
|
||||
if (category == "SpaceGuns") bits.push(4, 64);
|
||||
if (
|
||||
["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) &&
|
||||
item.UpgradeFingerprint
|
||||
)
|
||||
bits.push(1024);
|
||||
for (const bit of bits.sort((a, b) => a - b)) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList = "form-check";
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.classList = "form-check-input";
|
||||
input.type = "checkbox";
|
||||
input.id = `detailedView-feature-${bit}`;
|
||||
input.checked = item.Features & bit;
|
||||
|
||||
const label = document.createElement("label");
|
||||
label.classList = "form-check-label";
|
||||
label.htmlFor = input.id;
|
||||
label.innerHTML = loc(`code_feature_${bit}`);
|
||||
label.setAttribute("data-loc", `code_feature_${bit}`);
|
||||
|
||||
input.onchange = function (event) {
|
||||
event.preventDefault();
|
||||
equipmentFeatures(category, oid, bit);
|
||||
};
|
||||
if (
|
||||
(data.unlockDoubleCapacityPotatoesEverywhere && bit === 1) ||
|
||||
(data.unlockExilusEverywhere && bit === 2) ||
|
||||
(data.unlockArcanesEverywhere && (bit === 32 || bit === 64))
|
||||
) {
|
||||
input.disabled = true;
|
||||
}
|
||||
|
||||
wrapper.appendChild(input);
|
||||
wrapper.appendChild(label);
|
||||
buttonsCard.appendChild(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
if (category == "Suits") {
|
||||
document.getElementById("archonShards-card").classList.remove("d-none");
|
||||
|
||||
@@ -2673,7 +2719,7 @@ function addMissingEvolutionProgress() {
|
||||
|
||||
function maxRankAllEvolutions() {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1");
|
||||
req.done(data => {
|
||||
const requests = [];
|
||||
|
||||
@@ -2697,7 +2743,7 @@ function maxRankAllEvolutions() {
|
||||
|
||||
function maxRankAllEquipment(categories) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1");
|
||||
req.done(data => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
const batchData = {};
|
||||
@@ -2864,15 +2910,11 @@ function disposeOfItems(category, type, count) {
|
||||
});
|
||||
}
|
||||
|
||||
function gildEquipment(category, oid) {
|
||||
function equipmentFeatures(category, oid, bit) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/gildWeapon.php?" + window.authz + "&ItemId=" + oid + "&Category=" + category,
|
||||
contentType: "application/octet-stream",
|
||||
data: JSON.stringify({
|
||||
Recipe: "webui"
|
||||
})
|
||||
}).done(function () {
|
||||
$.get(
|
||||
"/custom/equipmentFeatures?" + window.authz + "&ItemId=" + oid + "&Category=" + category + "&bit=" + bit
|
||||
).done(function () {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
@@ -3035,7 +3077,7 @@ function doAcquireRiven() {
|
||||
])
|
||||
}).done(function () {
|
||||
// Get riven's assigned id
|
||||
$.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1").done(data => {
|
||||
$.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1").done(data => {
|
||||
for (const rawUpgrade of data.RawUpgrades) {
|
||||
if (rawUpgrade.ItemType === uniqueName) {
|
||||
// Add fingerprint to riven
|
||||
@@ -3259,33 +3301,35 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||
|
||||
function doUnlockAllFocusSchools() {
|
||||
revalidateAuthz().then(() => {
|
||||
$.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1").done(async data => {
|
||||
const missingFocusUpgrades = {
|
||||
"/Lotus/Upgrades/Focus/Attack/AttackFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Ward/WardFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Power/PowerFocusAbility": true
|
||||
};
|
||||
if (data.FocusUpgrades) {
|
||||
for (const focusUpgrade of data.FocusUpgrades) {
|
||||
if (focusUpgrade.ItemType in missingFocusUpgrades) {
|
||||
delete missingFocusUpgrades[focusUpgrade.ItemType];
|
||||
$.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1").done(
|
||||
async data => {
|
||||
const missingFocusUpgrades = {
|
||||
"/Lotus/Upgrades/Focus/Attack/AttackFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Ward/WardFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility": true,
|
||||
"/Lotus/Upgrades/Focus/Power/PowerFocusAbility": true
|
||||
};
|
||||
if (data.FocusUpgrades) {
|
||||
for (const focusUpgrade of data.FocusUpgrades) {
|
||||
if (focusUpgrade.ItemType in missingFocusUpgrades) {
|
||||
delete missingFocusUpgrades[focusUpgrade.ItemType];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const upgradeType of Object.keys(missingFocusUpgrades)) {
|
||||
await unlockFocusSchool(upgradeType);
|
||||
}
|
||||
if (Object.keys(missingFocusUpgrades).length == 0) {
|
||||
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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const upgradeType of Object.keys(missingFocusUpgrades)) {
|
||||
await unlockFocusSchool(upgradeType);
|
||||
}
|
||||
if (Object.keys(missingFocusUpgrades).length == 0) {
|
||||
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 }));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3403,7 +3447,7 @@ function doAddAllMods() {
|
||||
modsAll.delete("/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser");
|
||||
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1");
|
||||
req.done(data => {
|
||||
for (const modOwned of data.RawUpgrades) {
|
||||
if ((modOwned.ItemCount ?? 1) > 0) {
|
||||
@@ -3435,7 +3479,7 @@ function doAddAllMods() {
|
||||
|
||||
function doRemoveUnrankedMods() {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
|
||||
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1&ignoreBuildLabel=1");
|
||||
req.done(inventory => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
$.post({
|
||||
@@ -3478,6 +3522,8 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
|
||||
document.getElementById("modularParts-card").classList.add("d-none");
|
||||
document.getElementById("modularParts-form").innerHTML = "";
|
||||
document.getElementById("valenceBonus-card").classList.add("d-none");
|
||||
document.getElementById("equipmentFeatures-card").classList.add("d-none");
|
||||
document.getElementById("equipmentFeaturesButtons-card").innerHTML = "";
|
||||
if (window.didInitialInventoryUpdate) {
|
||||
updateInventory();
|
||||
}
|
||||
@@ -3552,16 +3598,21 @@ function doPopArchonCrystalUpgrade(type) {
|
||||
|
||||
function doImport() {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/import?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
inventory: JSON.parse($("#import-inventory").val())
|
||||
})
|
||||
}).then(function () {
|
||||
toast(loc("code_succImport"));
|
||||
updateInventory();
|
||||
});
|
||||
try {
|
||||
$.post({
|
||||
url: "/custom/import?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
inventory: JSON.parse($("#import-inventory").val())
|
||||
})
|
||||
}).then(function (anyKnownKey) {
|
||||
toast(loc(anyKnownKey ? "code_succImport" : "code_nothingToDo"));
|
||||
updateInventory();
|
||||
});
|
||||
} catch (e) {
|
||||
toast(e);
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `Operator-Gesicht: |INDEX|`,
|
||||
code_succChange: `Erfolgreich geändert.`,
|
||||
code_requiredInvigorationUpgrade: `Du musst sowohl ein Offensiv- als auch ein Support-Upgrade auswählen.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
|
||||
code_feature_4: `[UNTRANSLATED] Gravimag`,
|
||||
code_feature_8: `[UNTRANSLATED] Gild`,
|
||||
code_feature_32: `[UNTRANSLATED] Arcane Slot`,
|
||||
code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
|
||||
code_feature_1024: `[UNTRANSLATED] Valence Override`,
|
||||
login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
|
||||
login_emailLabel: `E-Mail-Adresse`,
|
||||
login_passwordLabel: `Passwort`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Modulare Teile ändern`,
|
||||
detailedView_invigorationLabel: `Kräftigung`,
|
||||
detailedView_loadoutLabel: `Loadouts`,
|
||||
detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
|
||||
invigorations_offensive_AbilityRange: `+100% Fähigkeitsreichweite`,
|
||||
|
||||
@@ -80,6 +80,13 @@ dict = {
|
||||
code_operatorFaceName: `Operator Visage |INDEX|`,
|
||||
code_succChange: `Successfully changed.`,
|
||||
code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`,
|
||||
code_feature_1: `Orokin Reactor`,
|
||||
code_feature_2: `Exilus Adapter`,
|
||||
code_feature_4: `Gravimag`,
|
||||
code_feature_8: `Gild`,
|
||||
code_feature_32: `Arcane Slot`,
|
||||
code_feature_64: `Second Arcane Slot`,
|
||||
code_feature_1024: `Valence Override`,
|
||||
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
|
||||
login_emailLabel: `Email address`,
|
||||
login_passwordLabel: `Password`,
|
||||
@@ -154,6 +161,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Change Modular Parts`,
|
||||
detailedView_invigorationLabel: `Invigoration`,
|
||||
detailedView_loadoutLabel: `Loadouts`,
|
||||
detailedView_equipmentFeaturesLabel: `Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
|
||||
invigorations_offensive_AbilityRange: `+100% Ability Range`,
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `Rostro del operador |INDEX|`,
|
||||
code_succChange: `Cambiado correctamente`,
|
||||
code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
|
||||
code_feature_4: `[UNTRANSLATED] Gravimag`,
|
||||
code_feature_8: `[UNTRANSLATED] Gild`,
|
||||
code_feature_32: `[UNTRANSLATED] Arcane Slot`,
|
||||
code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
|
||||
code_feature_1024: `[UNTRANSLATED] Valence Override`,
|
||||
login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
|
||||
login_emailLabel: `Dirección de correo electrónico`,
|
||||
login_passwordLabel: `Contraseña`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Cambiar partes modulares`,
|
||||
detailedView_invigorationLabel: `Fortalecimiento`,
|
||||
detailedView_loadoutLabel: `Equipamientos`,
|
||||
detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
|
||||
invigorations_offensive_AbilityRange: `+100% Alcance de Habilidad`,
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `Visage de l'Opérateur |INDEX|`,
|
||||
code_succChange: `Changement effectué.`,
|
||||
code_requiredInvigorationUpgrade: `Invigoration offensive et défensive requises.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
|
||||
code_feature_4: `[UNTRANSLATED] Gravimag`,
|
||||
code_feature_8: `[UNTRANSLATED] Gild`,
|
||||
code_feature_32: `[UNTRANSLATED] Arcane Slot`,
|
||||
code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
|
||||
code_feature_1024: `[UNTRANSLATED] Valence Override`,
|
||||
login_description: `Connexion avec les informations de connexion OpenWF.`,
|
||||
login_emailLabel: `Email`,
|
||||
login_passwordLabel: `Mot de passe`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
|
||||
detailedView_invigorationLabel: `Dynamisation`,
|
||||
detailedView_loadoutLabel: `Équipements`,
|
||||
detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
|
||||
invigorations_offensive_AbilityRange: `+100% de portée de pouvoir`,
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `Внешность оператора: |INDEX|`,
|
||||
code_succChange: `Успешно изменено.`,
|
||||
code_requiredInvigorationUpgrade: `Вы должны выбрать как атакующее, так и вспомогательное улучшение.`,
|
||||
code_feature_1: `Реактор Орокин`,
|
||||
code_feature_2: `Адаптер Эксилус`,
|
||||
code_feature_4: `Гравимаг`,
|
||||
code_feature_8: `Улучшение`,
|
||||
code_feature_32: `Слот Мистификатора`,
|
||||
code_feature_64: `Второй слот Мистификатора`,
|
||||
code_feature_1024: `Переопределение валентности`,
|
||||
login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
|
||||
login_emailLabel: `Адрес электронной почты`,
|
||||
login_passwordLabel: `Пароль`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Изменить модульные части`,
|
||||
detailedView_invigorationLabel: `Воодушевление`,
|
||||
detailedView_loadoutLabel: `Конфигурации`,
|
||||
detailedView_equipmentFeaturesLabel: `Модификаторы снаряжения`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% к силе способностей.`,
|
||||
invigorations_offensive_AbilityRange: `+100% к зоне поражения способностей.`,
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `Зовнішність оператора: |INDEX|`,
|
||||
code_succChange: `Успішно змінено.`,
|
||||
code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
|
||||
code_feature_4: `[UNTRANSLATED] Gravimag`,
|
||||
code_feature_8: `[UNTRANSLATED] Gild`,
|
||||
code_feature_32: `[UNTRANSLATED] Arcane Slot`,
|
||||
code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
|
||||
code_feature_1024: `[UNTRANSLATED] Valence Override`,
|
||||
login_description: `Увійдіть, використовуючи облікові дані OpenWF (ті ж, що й у грі при підключенні до цього серверу).`,
|
||||
login_emailLabel: `Адреса електронної пошти`,
|
||||
login_passwordLabel: `Пароль`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `Змінити модульні частини`,
|
||||
detailedView_invigorationLabel: `Зміцнення`,
|
||||
detailedView_loadoutLabel: `Конфігурації`,
|
||||
detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`,
|
||||
invigorations_offensive_AbilityRange: `+100% до досяжності здібностей.`,
|
||||
|
||||
@@ -81,6 +81,13 @@ dict = {
|
||||
code_operatorFaceName: `指挥官面部 |INDEX|`,
|
||||
code_succChange: `更改成功`,
|
||||
code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
|
||||
code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
|
||||
code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
|
||||
code_feature_4: `[UNTRANSLATED] Gravimag`,
|
||||
code_feature_8: `[UNTRANSLATED] Gild`,
|
||||
code_feature_32: `[UNTRANSLATED] Arcane Slot`,
|
||||
code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
|
||||
code_feature_1024: `[UNTRANSLATED] Valence Override`,
|
||||
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`,
|
||||
login_emailLabel: `电子邮箱`,
|
||||
login_passwordLabel: `密码`,
|
||||
@@ -155,6 +162,7 @@ dict = {
|
||||
detailedView_modularPartsLabel: `更换部件`,
|
||||
detailedView_invigorationLabel: `活化`,
|
||||
detailedView_loadoutLabel: `配置`,
|
||||
detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200%技能强度`,
|
||||
invigorations_offensive_AbilityRange: `+100%技能范围`,
|
||||
|
||||
Reference in New Issue
Block a user