chore(webui): update Chinese translation #2453
@ -14,7 +14,9 @@ import {
|
||||
addRecipes,
|
||||
occupySlot,
|
||||
combineInventoryChanges,
|
||||
addKubrowPetPrint
|
||||
addKubrowPetPrint,
|
||||
addPowerSuit,
|
||||
addEquipment
|
||||
} from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { InventorySlot, IPendingRecipeDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -22,7 +24,7 @@ import { toOid2 } from "@/src/helpers/inventoryHelpers";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IRecipe } from "warframe-public-export-plus";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { IEquipmentClient, Status } from "@/src/types/equipmentTypes";
|
||||
import { EquipmentFeatures, IEquipmentClient, Status } from "@/src/types/equipmentTypes";
|
||||
|
||||
interface IClaimCompletedRecipeRequest {
|
||||
RecipeIds: IOid[];
|
||||
@ -124,6 +126,110 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
|
||||
addKubrowPetPrint(inventory, pet, InventoryChanges);
|
||||
} else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
||||
if (recipe.resultType == "/Lotus/Powersuits/Excalibur/ExcaliburUmbra") {
|
||||
// Quite the special case here...
|
||||
// We don't just get Umbra, but also Skiajati and Umbra Mods. Both items are max rank, potatoed, and with the mods are pre-installed.
|
||||
// Source: https://wiki.warframe.com/w/The_Sacrifice, https://wiki.warframe.com/w/Excalibur/Umbra, https://wiki.warframe.com/w/Skiajati
|
||||
|
||||
const umbraModA = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const umbraModB = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const umbraModC = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const sacrificeModA = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
const sacrificeModB = (
|
||||
await addItem(
|
||||
inventory,
|
||||
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
`{"lvl":5}`
|
||||
)
|
||||
).Upgrades![0];
|
||||
InventoryChanges.Upgrades ??= [];
|
||||
InventoryChanges.Upgrades.push(umbraModA, umbraModB, umbraModC, sacrificeModA, sacrificeModB);
|
||||
|
||||
await addPowerSuit(
|
||||
inventory,
|
||||
"/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
|
||||
{
|
||||
Configs: [
|
||||
{
|
||||
Upgrades: [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
umbraModA.ItemId.$oid,
|
||||
umbraModB.ItemId.$oid,
|
||||
umbraModC.ItemId.$oid
|
||||
]
|
||||
}
|
||||
],
|
||||
XP: 900_000,
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY
|
||||
},
|
||||
InventoryChanges
|
||||
);
|
||||
inventory.XPInfo.push({
|
||||
ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
|
||||
XP: 900_000
|
||||
});
|
||||
|
||||
addEquipment(
|
||||
inventory,
|
||||
"Melee",
|
||||
"/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
|
||||
{
|
||||
Configs: [
|
||||
{ Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] }
|
||||
],
|
||||
XP: 450_000,
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY
|
||||
},
|
||||
InventoryChanges
|
||||
);
|
||||
inventory.XPInfo.push({
|
||||
ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
|
||||
XP: 450_000
|
||||
});
|
||||
} else {
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...(await addItem(
|
||||
@ -136,6 +242,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
if (
|
||||
config.claimingBlueprintRefundsIngredients &&
|
||||
recipe.secretIngredientAction != "SIA_CREATE_KUBROW" // Can't refund the egg
|
||||
|
@ -2,7 +2,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory, updateCurrency, updateSlots } from "@/src/services/inventoryService";
|
||||
import { RequestHandler } from "express";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { exhaustive } from "@/src/utils/ts-utils";
|
||||
|
||||
/*
|
||||
loadout slots are additionally purchased slots only
|
||||
@ -22,13 +22,44 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const body = JSON.parse(req.body as string) as IInventorySlotsRequest;
|
||||
|
||||
if (body.Bin != InventorySlot.SUITS && body.Bin != InventorySlot.PVE_LOADOUTS) {
|
||||
logger.warn(`unexpected slot purchase of type ${body.Bin}, account may be overcharged`);
|
||||
let price;
|
||||
let amount;
|
||||
switch (body.Bin) {
|
||||
case InventorySlot.SUITS:
|
||||
case InventorySlot.MECHSUITS:
|
||||
case InventorySlot.PVE_LOADOUTS:
|
||||
case InventorySlot.CREWMEMBERS:
|
||||
price = 20;
|
||||
amount = 1;
|
||||
break;
|
||||
|
||||
case InventorySlot.SPACESUITS:
|
||||
price = 12;
|
||||
amount = 1;
|
||||
break;
|
||||
|
||||
case InventorySlot.WEAPONS:
|
||||
case InventorySlot.SPACEWEAPONS:
|
||||
case InventorySlot.SENTINELS:
|
||||
case InventorySlot.RJ_COMPONENT_AND_ARMAMENTS:
|
||||
case InventorySlot.AMPS:
|
||||
price = 12;
|
||||
amount = 2;
|
||||
break;
|
||||
|
||||
case InventorySlot.RIVENS:
|
||||
price = 60;
|
||||
amount = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
exhaustive(body.Bin);
|
||||
throw new Error(`unexpected slot purchase of type ${body.Bin as string}`);
|
||||
}
|
||||
|
||||
const inventory = await getInventory(accountId);
|
||||
const currencyChanges = updateCurrency(inventory, 20, true);
|
||||
updateSlots(inventory, body.Bin, 1, 1);
|
||||
const currencyChanges = updateCurrency(inventory, price, true);
|
||||
updateSlots(inventory, body.Bin, amount, amount);
|
||||
await inventory.save();
|
||||
|
||||
res.json({ InventoryChanges: currencyChanges });
|
||||
|
@ -1,25 +1,39 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { addConsumables, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IPlayerSkills } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const playerSkillsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "PlayerSkills");
|
||||
const inventory = await getInventory(accountId, "PlayerSkills Consumables");
|
||||
const request = getJSONfromString<IPlayerSkillsRequest>(String(req.body));
|
||||
|
||||
const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
|
||||
const cost = (request.Pool == "LPP_DRIFTER" ? drifterCosts[oldRank] : 1 << oldRank) * 1000;
|
||||
inventory.PlayerSkills[request.Pool as keyof IPlayerSkills] -= cost;
|
||||
inventory.PlayerSkills[request.Skill as keyof IPlayerSkills]++;
|
||||
await inventory.save();
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (request.Skill == "LPS_COMMAND" && inventory.PlayerSkills.LPS_COMMAND == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/Consumable/CrewmateBall",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
Pool: request.Pool,
|
||||
PoolInc: -cost,
|
||||
Skill: request.Skill,
|
||||
Rank: oldRank + 1
|
||||
Rank: oldRank + 1,
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -23,10 +23,16 @@ export const crackRelic = async (
|
||||
weights = { COMMON: 0, UNCOMMON: 0, RARE: 1, LEGENDARY: 0 };
|
||||
}
|
||||
logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
|
||||
const reward = getRandomWeightedReward(
|
||||
let reward = getRandomWeightedReward(
|
||||
ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
|
||||
weights
|
||||
)!;
|
||||
if (config.relicRewardItemCountMultiplier !== undefined && (config.relicRewardItemCountMultiplier ?? 1) != 1) {
|
||||
reward = {
|
||||
...reward,
|
||||
itemCount: reward.itemCount * config.relicRewardItemCountMultiplier
|
||||
};
|
||||
}
|
||||
logger.debug(`relic rolled`, reward);
|
||||
participant.Reward = reward.type;
|
||||
|
||||
@ -43,13 +49,7 @@ export const crackRelic = async (
|
||||
// Give reward
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
(
|
||||
await handleStoreItemAcquisition(
|
||||
reward.type,
|
||||
inventory,
|
||||
reward.itemCount * (config.relicRewardItemCountMultiplier ?? 1)
|
||||
)
|
||||
).InventoryChanges
|
||||
(await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount)).InventoryChanges
|
||||
);
|
||||
|
||||
return reward;
|
||||
|
@ -482,11 +482,14 @@ export const addItem = async (
|
||||
if (quantity != 1) {
|
||||
logger.warn(`adding 1 of ${typeName} ${targetFingerprint} even tho quantity ${quantity} was requested`);
|
||||
}
|
||||
const upgrade =
|
||||
inventory.Upgrades[
|
||||
inventory.Upgrades.push({
|
||||
ItemType: typeName,
|
||||
UpgradeFingerprint: targetFingerprint
|
||||
});
|
||||
return {}; // there's not exactly a common "InventoryChanges" format for these
|
||||
}) - 1
|
||||
];
|
||||
return { Upgrades: [upgrade.toJSON<IUpgradeClient>()] };
|
||||
}
|
||||
const changes = [
|
||||
{
|
||||
@ -812,7 +815,7 @@ export const addItem = async (
|
||||
if (!seed) {
|
||||
throw new Error(`Expected crew member to have a seed`);
|
||||
}
|
||||
seed |= 0x33b81en << 32n;
|
||||
seed |= BigInt(Math.trunc(inventory.Created.getTime() / 1000) & 0xffffff) << 32n;
|
||||
return {
|
||||
...addCrewMember(inventory, typeName, seed),
|
||||
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
||||
@ -1106,6 +1109,10 @@ export const addKubrowPet = (
|
||||
};
|
||||
} else {
|
||||
dominantTraits = createRandomTraits(kubrowPetName, traitsPool);
|
||||
if (kubrowPetName == "/Lotus/Types/Game/KubrowPet/ChargerKubrowPetPowerSuit") {
|
||||
dominantTraits.BodyType = "/Lotus/Types/Game/KubrowPet/BodyTypes/ChargerKubrowPetBodyType";
|
||||
dominantTraits.FurPattern = "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternInfested";
|
||||
}
|
||||
}
|
||||
|
||||
const recessiveTraits: ITraits = createRandomTraits(
|
||||
|
@ -236,7 +236,7 @@ const handleQuestCompletion = async (
|
||||
setupKahlSyndicate(inventory);
|
||||
}
|
||||
|
||||
// Whispers in the Walls is unlocked once The New + Heart of Deimos are completed.
|
||||
// Whispers in the Walls is unlocked once The New War + Heart of Deimos are completed.
|
||||
if (
|
||||
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||
"/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain",
|
||||
|
@ -971,25 +971,26 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
|
||||
|
||||
// Not very faithful, but to avoid the same node coming up back-to-back (which is not valid), I've split these into 2 arrays which we're alternating between.
|
||||
|
||||
const voidStormMissionsA = {
|
||||
VoidT1: ["CrewBattleNode519", "CrewBattleNode518", "CrewBattleNode515", "CrewBattleNode503"],
|
||||
VoidT2: ["CrewBattleNode501", "CrewBattleNode534", "CrewBattleNode530"],
|
||||
VoidT3: ["CrewBattleNode521", "CrewBattleNode516"],
|
||||
const voidStormMissions = {
|
||||
VoidT1: [
|
||||
"CrewBattleNode519",
|
||||
"CrewBattleNode518",
|
||||
"CrewBattleNode515",
|
||||
"CrewBattleNode503",
|
||||
"CrewBattleNode509",
|
||||
"CrewBattleNode522",
|
||||
"CrewBattleNode511",
|
||||
"CrewBattleNode512"
|
||||
],
|
||||
VoidT2: ["CrewBattleNode501", "CrewBattleNode534", "CrewBattleNode530", "CrewBattleNode535", "CrewBattleNode533"],
|
||||
VoidT3: ["CrewBattleNode521", "CrewBattleNode516", "CrewBattleNode524", "CrewBattleNode525"],
|
||||
VoidT4: [
|
||||
"CrewBattleNode555",
|
||||
"CrewBattleNode553",
|
||||
"CrewBattleNode554",
|
||||
"CrewBattleNode539",
|
||||
"CrewBattleNode531",
|
||||
"CrewBattleNode527"
|
||||
]
|
||||
};
|
||||
|
||||
const voidStormMissionsB = {
|
||||
VoidT1: ["CrewBattleNode509", "CrewBattleNode522", "CrewBattleNode511", "CrewBattleNode512"],
|
||||
VoidT2: ["CrewBattleNode535", "CrewBattleNode533"],
|
||||
VoidT3: ["CrewBattleNode524", "CrewBattleNode525"],
|
||||
VoidT4: [
|
||||
"CrewBattleNode527",
|
||||
"CrewBattleNode542",
|
||||
"CrewBattleNode538",
|
||||
"CrewBattleNode543",
|
||||
@ -997,18 +998,21 @@ const voidStormMissionsB = {
|
||||
"CrewBattleNode550",
|
||||
"CrewBattleNode529"
|
||||
]
|
||||
};
|
||||
} as const;
|
||||
|
||||
const voidStormLookbehind = {
|
||||
VoidT1: 3,
|
||||
VoidT2: 1,
|
||||
VoidT3: 1,
|
||||
VoidT4: 3
|
||||
} as const;
|
||||
|
||||
const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => {
|
||||
const activation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
|
||||
const expiry = activation + 90 * unixTimesInMs.minute;
|
||||
let accum = 0;
|
||||
const rng = new SRng(new SRng(hour).randomInt(0, 100_000));
|
||||
const voidStormMissions = structuredClone(hour & 1 ? voidStormMissionsA : voidStormMissionsB);
|
||||
const tierIdx = { VoidT1: hour * 2, VoidT2: hour, VoidT3: hour, VoidT4: hour * 2 };
|
||||
for (const tier of ["VoidT1", "VoidT1", "VoidT2", "VoidT3", "VoidT4", "VoidT4"] as const) {
|
||||
const idx = rng.randomInt(0, voidStormMissions[tier].length - 1);
|
||||
const node = voidStormMissions[tier][idx];
|
||||
voidStormMissions[tier].splice(idx, 1);
|
||||
arr.push({
|
||||
_id: {
|
||||
$oid:
|
||||
@ -1016,7 +1020,12 @@ const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => {
|
||||
"0321e89b" +
|
||||
(accum++).toString().padStart(8, "0")
|
||||
},
|
||||
Node: node,
|
||||
Node: sequentiallyUniqueRandomElement(
|
||||
voidStormMissions[tier],
|
||||
tierIdx[tier]++,
|
||||
voidStormLookbehind[tier],
|
||||
2051969264
|
||||
)!,
|
||||
Activation: { $date: { $numberLong: activation.toString() } },
|
||||
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||
ActiveMissionTier: tier
|
||||
@ -1024,75 +1033,124 @@ const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => {
|
||||
}
|
||||
};
|
||||
|
||||
const doesTimeSatsifyConstraints = (timeSecs: number): boolean => {
|
||||
if (config.worldState?.eidolonOverride) {
|
||||
interface ITimeConstraint {
|
||||
//name: string;
|
||||
isValidTime: (timeSecs: number) => boolean;
|
||||
getIdealTimeBefore: (timeSecs: number) => number;
|
||||
}
|
||||
|
||||
const eidolonDayConstraint: ITimeConstraint = {
|
||||
//name: "eidolon day",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
const eidolonCycleStart = eidolonEpoch + eidolonCycle * 9000;
|
||||
const eidolonCycleEnd = eidolonCycleStart + 9000;
|
||||
const eidolonCycleNightStart = eidolonCycleEnd - 3000;
|
||||
if (config.worldState.eidolonOverride == "day") {
|
||||
if (
|
||||
//timeSecs < eidolonCycleStart ||
|
||||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, eidolonCycleNightStart * 1000)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
timeSecs < eidolonCycleNightStart ||
|
||||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, eidolonCycleEnd * 1000)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, eidolonCycleNightStart * 1000);
|
||||
},
|
||||
getIdealTimeBefore: (timeSecs: number): number => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
const eidolonCycleStart = eidolonEpoch + eidolonCycle * 9000;
|
||||
return eidolonCycleStart;
|
||||
}
|
||||
};
|
||||
|
||||
if (config.worldState?.vallisOverride) {
|
||||
const eidolonNightConstraint: ITimeConstraint = {
|
||||
//name: "eidolon night",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
const eidolonCycleStart = eidolonEpoch + eidolonCycle * 9000;
|
||||
const eidolonCycleEnd = eidolonCycleStart + 9000;
|
||||
const eidolonCycleNightStart = eidolonCycleEnd - 3000;
|
||||
return (
|
||||
timeSecs >= eidolonCycleNightStart &&
|
||||
!isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, eidolonCycleEnd * 1000)
|
||||
);
|
||||
},
|
||||
getIdealTimeBefore: (timeSecs: number): number => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
const eidolonCycleStart = eidolonEpoch + eidolonCycle * 9000;
|
||||
const eidolonCycleEnd = eidolonCycleStart + 9000;
|
||||
const eidolonCycleNightStart = eidolonCycleEnd - 3000;
|
||||
if (eidolonCycleNightStart > timeSecs) {
|
||||
// Night hasn't started yet, but we need to return a time in the past.
|
||||
return eidolonCycleNightStart - 9000;
|
||||
}
|
||||
return eidolonCycleNightStart;
|
||||
}
|
||||
};
|
||||
|
||||
const venusColdConstraint: ITimeConstraint = {
|
||||
//name: "venus cold",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
const vallisCycleStart = vallisEpoch + vallisCycle * 1600;
|
||||
const vallisCycleEnd = vallisCycleStart + 1600;
|
||||
const vallisCycleColdStart = vallisCycleStart + 400;
|
||||
if (config.worldState.vallisOverride == "cold") {
|
||||
if (
|
||||
timeSecs < vallisCycleColdStart ||
|
||||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, vallisCycleEnd * 1000)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
//timeSecs < vallisCycleStart ||
|
||||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, vallisCycleColdStart * 1000)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
timeSecs >= vallisCycleColdStart &&
|
||||
!isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, vallisCycleEnd * 1000)
|
||||
);
|
||||
},
|
||||
getIdealTimeBefore: (timeSecs: number): number => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
const vallisCycleStart = vallisEpoch + vallisCycle * 1600;
|
||||
const vallisCycleColdStart = vallisCycleStart + 400;
|
||||
if (vallisCycleColdStart > timeSecs) {
|
||||
// Cold hasn't started yet, but we need to return a time in the past.
|
||||
return vallisCycleColdStart - 1600;
|
||||
}
|
||||
return vallisCycleColdStart;
|
||||
}
|
||||
};
|
||||
|
||||
if (config.worldState?.duviriOverride) {
|
||||
const duviriMoods = ["sorrow", "fear", "joy", "anger", "envy"];
|
||||
const desiredMood = duviriMoods.indexOf(config.worldState.duviriOverride);
|
||||
if (desiredMood == -1) {
|
||||
logger.warn(`ignoring invalid config value for worldState.duviriOverride`, {
|
||||
value: config.worldState.duviriOverride,
|
||||
valid_values: duviriMoods
|
||||
});
|
||||
} else {
|
||||
const moodIndex = Math.trunc(timeSecs / 7200);
|
||||
const moodStart = moodIndex * 7200;
|
||||
const moodEnd = moodStart + 7200;
|
||||
if (
|
||||
moodIndex % 5 != desiredMood ||
|
||||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, moodEnd * 1000)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const venusWarmConstraint: ITimeConstraint = {
|
||||
//name: "venus warm",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
const vallisCycleStart = vallisEpoch + vallisCycle * 1600;
|
||||
const vallisCycleColdStart = vallisCycleStart + 400;
|
||||
return !isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, vallisCycleColdStart * 1000);
|
||||
},
|
||||
getIdealTimeBefore: (timeSecs: number): number => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
const vallisCycleStart = vallisEpoch + vallisCycle * 1600;
|
||||
return vallisCycleStart;
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
const getIdealTimeSatsifyingConstraints = (constraints: ITimeConstraint[]): number => {
|
||||
let timeSecs = Math.trunc(Date.now() / 1000);
|
||||
let allGood;
|
||||
do {
|
||||
allGood = true;
|
||||
for (const constraint of constraints) {
|
||||
if (!constraint.isValidTime(timeSecs)) {
|
||||
//logger.debug(`${constraint.name} is not happy with ${timeSecs}`);
|
||||
const prevTimeSecs = timeSecs;
|
||||
const suggestion = constraint.getIdealTimeBefore(timeSecs);
|
||||
timeSecs = suggestion;
|
||||
do {
|
||||
timeSecs += 60;
|
||||
if (timeSecs >= prevTimeSecs || !constraint.isValidTime(timeSecs)) {
|
||||
timeSecs = suggestion; // Can't find a compromise; just take the suggestion and try to compromise on another constraint.
|
||||
break;
|
||||
}
|
||||
} while (!constraints.every(constraint => constraint.isValidTime(timeSecs)));
|
||||
allGood = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!allGood);
|
||||
return timeSecs;
|
||||
};
|
||||
|
||||
const getVarziaRotation = (week: number): string => {
|
||||
@ -1170,10 +1228,38 @@ const getAllVarziaManifests = (): IPrimeVaultTraderOffer[] => {
|
||||
};
|
||||
|
||||
export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
let timeSecs = Math.round(Date.now() / 1000);
|
||||
while (!doesTimeSatsifyConstraints(timeSecs)) {
|
||||
timeSecs -= 60;
|
||||
const constraints: ITimeConstraint[] = [];
|
||||
if (config.worldState?.eidolonOverride) {
|
||||
constraints.push(config.worldState.eidolonOverride == "day" ? eidolonDayConstraint : eidolonNightConstraint);
|
||||
}
|
||||
if (config.worldState?.vallisOverride) {
|
||||
constraints.push(config.worldState.vallisOverride == "cold" ? venusColdConstraint : venusWarmConstraint);
|
||||
}
|
||||
if (config.worldState?.duviriOverride) {
|
||||
const duviriMoods = ["sorrow", "fear", "joy", "anger", "envy"];
|
||||
const desiredMood = duviriMoods.indexOf(config.worldState.duviriOverride);
|
||||
if (desiredMood == -1) {
|
||||
logger.warn(`ignoring invalid config value for worldState.duviriOverride`, {
|
||||
value: config.worldState.duviriOverride,
|
||||
valid_values: duviriMoods
|
||||
});
|
||||
} else {
|
||||
constraints.push({
|
||||
//name: `duviri ${config.worldState.duviriOverride}`,
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const moodIndex = Math.trunc(timeSecs / 7200);
|
||||
return moodIndex % 5 == desiredMood;
|
||||
},
|
||||
getIdealTimeBefore: (timeSecs: number): number => {
|
||||
let moodIndex = Math.trunc(timeSecs / 7200);
|
||||
moodIndex -= ((moodIndex % 5) - desiredMood + 5) % 5; // while (moodIndex % 5 != desiredMood) --moodIndex;
|
||||
const moodStart = moodIndex * 7200;
|
||||
return moodStart;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const timeSecs = getIdealTimeSatsifyingConstraints(constraints);
|
||||
const timeMs = timeSecs * 1000;
|
||||
const day = Math.trunc((timeMs - EPOCH) / 86400000);
|
||||
const week = Math.trunc(day / 7);
|
||||
|
@ -520,7 +520,8 @@ export enum InventorySlot {
|
||||
SENTINELS = "SentinelBin",
|
||||
AMPS = "OperatorAmpBin",
|
||||
RJ_COMPONENT_AND_ARMAMENTS = "CrewShipSalvageBin",
|
||||
CREWMEMBERS = "CrewMemberBin"
|
||||
CREWMEMBERS = "CrewMemberBin",
|
||||
RIVENS = "RandomModBin"
|
||||
}
|
||||
|
||||
export interface ISlots {
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
IRecentVendorPurchaseClient,
|
||||
TEquipmentKey,
|
||||
ICrewMemberClient,
|
||||
IKubrowPetPrintClient
|
||||
IKubrowPetPrintClient,
|
||||
IUpgradeClient
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export enum PurchaseSource {
|
||||
@ -80,6 +81,7 @@ export type IInventoryChanges = {
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
|
||||
CrewMembers?: ICrewMemberClient[];
|
||||
KubrowPetPrints?: IKubrowPetPrintClient[];
|
||||
Upgrades?: IUpgradeClient[]; // TOVERIFY
|
||||
} & Record<
|
||||
Exclude<
|
||||
string,
|
||||
|
@ -3,3 +3,5 @@ type Entries<T, K extends keyof T = keyof T> = (K extends unknown ? [K, T[K]] :
|
||||
export function getEntriesUnsafe<T extends object>(object: T): Entries<T> {
|
||||
return Object.entries(object) as Entries<T>;
|
||||
}
|
||||
|
||||
export const exhaustive = (_: never): void => {};
|
||||
|
@ -135,5 +135,6 @@
|
||||
"/Lotus/Language/EntratiLab/EntratiGeneral/HumanLoidLoved",
|
||||
"ConquestSetupIntro",
|
||||
"EntratiLabConquestHardModeUnlocked",
|
||||
"/Lotus/Language/Npcs/KonzuPostNewWar"
|
||||
"/Lotus/Language/Npcs/KonzuPostNewWar",
|
||||
"/Lotus/Language/SolarisVenus/EudicoPostNewWar"
|
||||
]
|
||||
|
@ -16,6 +16,10 @@
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Keys/1999PrologueQuest/1999PrologueQuestKeyChain",
|
||||
"ItemCount": 1
|
||||
},
|
||||
{
|
||||
"ItemType": "/Lotus/Types/Items/EmailItems/TennokaiEmailItem",
|
||||
"ItemCount": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -457,7 +457,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="detailedView-route" data-route="/webui/detailedView" data-title="Inventory | OpenWF WebUI">
|
||||
<h3 class="mb-0"></h3>
|
||||
<h3 id="detailedView-loading" class="mb-0" data-loc="general_loading"></h3>
|
||||
<h3 id="detailedView-title" class="mb-0"></h3>
|
||||
<p class="text-body-secondary"></p>
|
||||
<div id="archonShards-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_archonShardsLabel"></h5>
|
||||
@ -527,8 +528,8 @@
|
||||
<form class="input-group mb-3" onsubmit="doAcquireMod();return false;">
|
||||
<input class="form-control" id="mod-count" type="number" value="1"/>
|
||||
<input class="form-control w-50" id="mod-to-acquire" list="datalist-mods" />
|
||||
<button class="btn btn-success" onclick="window.maxed=true" type="submit" data-loc="mods_addMax"></button>
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
<button class="btn btn-success" onclick="window.maxed=true" data-loc="mods_addMax"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
<tbody id="mods-list"></tbody>
|
||||
@ -803,21 +804,21 @@
|
||||
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" data-default="-1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigInt('relicRewardItemCountMultiplier'); return false;">
|
||||
<label class="form-label" for="relicRewardItemCountMultiplier" data-loc="cheats_relicRewardItemCountMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="relicRewardItemCountMultiplier" type="number" min="1" max="1000000" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;">
|
||||
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -830,6 +831,7 @@
|
||||
<div class="card-body">
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(markAllAsRead);" data-loc="cheats_markAllAsRead"></button>
|
||||
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
|
||||
<button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
|
||||
@ -942,14 +944,14 @@
|
||||
<label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_theCircuitOverride"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.circuitGameModes" type="text" class="form-control tags-input" list="datalist-circuitGameModes" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
<form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;">
|
||||
<label class="form-label" for="worldState.darvoStockMultiplier" data-loc="worldState_darvoStockMultiplier"></label>
|
||||
<div class="input-group">
|
||||
<input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" data-default="1" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
|
||||
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -273,6 +273,8 @@ function fetchItemList() {
|
||||
window.itemListPromise = new Promise(resolve => {
|
||||
const req = $.get("/custom/getItemLists?lang=" + window.lang);
|
||||
req.done(async data => {
|
||||
window.allQuestKeys = data.QuestKeys;
|
||||
|
||||
await dictPromise;
|
||||
|
||||
document.querySelectorAll('[id^="datalist-"]').forEach(datalist => {
|
||||
@ -879,6 +881,14 @@ function updateInventory() {
|
||||
|
||||
// Populate quests route
|
||||
document.getElementById("QuestKeys-list").innerHTML = "";
|
||||
window.allQuestKeys.forEach(questKey => {
|
||||
if (!data.QuestKeys.some(x => x.ItemType == questKey.uniqueName)) {
|
||||
const datalist = document.getElementById("datalist-QuestKeys");
|
||||
if (!datalist.querySelector(`option[data-key="${questKey.uniqueName}"]`)) {
|
||||
readdQuestKey(itemMap, questKey.uniqueName);
|
||||
}
|
||||
}
|
||||
});
|
||||
data.QuestKeys.forEach(item => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("data-item-type", item.ItemType);
|
||||
@ -972,10 +982,7 @@ function updateInventory() {
|
||||
a.href = "#";
|
||||
a.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", item.ItemType);
|
||||
option.value = itemMap[item.ItemType]?.name ?? item.ItemType;
|
||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
||||
readdQuestKey(itemMap, item.ItemType);
|
||||
doQuestUpdate("deleteKey", item.ItemType);
|
||||
};
|
||||
a.title = loc("code_remove");
|
||||
@ -1166,14 +1173,15 @@ function updateInventory() {
|
||||
const item = data[category].find(x => x.ItemId.$oid == oid);
|
||||
|
||||
if (item) {
|
||||
document.getElementById("detailedView-loading").classList.add("d-none");
|
||||
|
||||
if (item.ItemName) {
|
||||
$("#detailedView-route h3").text(item.ItemName);
|
||||
$("#detailedView-title").text(item.ItemName);
|
||||
$("#detailedView-route .text-body-secondary").text(
|
||||
itemMap[item.ItemType]?.name ?? item.ItemType
|
||||
);
|
||||
} else {
|
||||
$("#detailedView-route h3").text(itemMap[item.ItemType]?.name ?? item.ItemType);
|
||||
$("#detailedView-route .text-body-secondary").text("");
|
||||
$("#detailedView-title").text(itemMap[item.ItemType]?.name ?? item.ItemType);
|
||||
}
|
||||
|
||||
if (category == "Suits") {
|
||||
@ -1954,6 +1962,19 @@ for (const id of uiConfigs) {
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll(".config-form .input-group").forEach(grp => {
|
||||
const input = grp.querySelector("input");
|
||||
const btn = grp.querySelector("button");
|
||||
input.oninput = input.onchange = function () {
|
||||
btn.classList.remove("btn-secondary");
|
||||
btn.classList.add("btn-primary");
|
||||
};
|
||||
btn.onclick = function () {
|
||||
btn.classList.remove("btn-primary");
|
||||
btn.classList.add("btn-secondary");
|
||||
};
|
||||
});
|
||||
|
||||
function doSaveConfigInt(id) {
|
||||
$.post({
|
||||
url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
|
||||
@ -2171,7 +2192,9 @@ function doAddMissingMaxRankMods() {
|
||||
// DetailedView Route
|
||||
|
||||
single.getRoute("#detailedView-route").on("beforeload", function () {
|
||||
this.element.querySelector("h3").textContent = "Loading...";
|
||||
document.getElementById("detailedView-loading").classList.remove("d-none");
|
||||
document.getElementById("detailedView-title").textContent = "";
|
||||
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
|
||||
document.getElementById("archonShards-card").classList.add("d-none");
|
||||
document.getElementById("valenceBonus-card").classList.add("d-none");
|
||||
if (window.didInitialInventoryUpdate) {
|
||||
@ -2254,6 +2277,13 @@ function doAddCurrency(currency) {
|
||||
});
|
||||
}
|
||||
|
||||
function readdQuestKey(itemMap, itemType) {
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", itemType);
|
||||
option.value = itemMap[itemType]?.name ?? itemType;
|
||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
||||
}
|
||||
|
||||
function doQuestUpdate(operation, itemType) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
@ -2764,3 +2794,16 @@ document.querySelectorAll("#sidebar .nav-link").forEach(function (elm) {
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
});
|
||||
|
||||
async function markAllAsRead() {
|
||||
await revalidateAuthz();
|
||||
const { Inbox } = await fetch("/api/inbox.php?" + window.authz).then(x => x.json());
|
||||
let any = false;
|
||||
for (const msg of Inbox) {
|
||||
if (!msg.r) {
|
||||
await fetch("/api/inbox.php?" + window.authz + "&messageId=" + msg.messageId.$oid);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
toast(loc(any ? "code_succRelog" : "code_nothingToDo"));
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ dict = {
|
||||
general_addButton: `Hinzufügen`,
|
||||
general_setButton: `[UNTRANSLATED] Set`,
|
||||
general_bulkActions: `Massenaktionen`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
|
||||
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
|
||||
@ -44,6 +45,8 @@ dict = {
|
||||
code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`,
|
||||
code_addModsConfirm: `Bist du sicher, dass du |COUNT| Mods zu deinem Account hinzufügen möchtest?`,
|
||||
code_succImport: `Erfolgreich importiert.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_gild: `Veredeln`,
|
||||
code_moa: `Moa`,
|
||||
code_zanuka: `Jagdhund`,
|
||||
@ -199,6 +202,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
|
||||
cheats_changeButton: `Ändern`,
|
||||
cheats_none: `Keines`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
|
@ -3,6 +3,7 @@ dict = {
|
||||
general_addButton: `Add`,
|
||||
general_setButton: `Set`,
|
||||
general_bulkActions: `Bulk Actions`,
|
||||
general_loading: `Loading...`,
|
||||
|
||||
code_loginFail: `Login failed. Double-check the email and password.`,
|
||||
code_regFail: `Registration failed. Account already exists?`,
|
||||
@ -43,6 +44,8 @@ dict = {
|
||||
code_focusUnlocked: `Unlocked |COUNT| new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that.`,
|
||||
code_addModsConfirm: `Are you sure you want to add |COUNT| mods to your account?`,
|
||||
code_succImport: `Successfully imported.`,
|
||||
code_succRelog: `Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `Done. There was nothing to do.`,
|
||||
code_gild: `Gild`,
|
||||
code_moa: `Moa`,
|
||||
code_zanuka: `Hound`,
|
||||
@ -198,6 +201,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Supported syndicate`,
|
||||
cheats_changeButton: `Change`,
|
||||
cheats_none: `None`,
|
||||
cheats_markAllAsRead: `Mark Inbox As Read`,
|
||||
|
||||
worldState: `World State`,
|
||||
worldState_creditBoost: `Credit Boost`,
|
||||
|
@ -4,6 +4,7 @@ dict = {
|
||||
general_addButton: `Agregar`,
|
||||
general_setButton: `Establecer`,
|
||||
general_bulkActions: `Acciones masivas`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
|
||||
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
|
||||
@ -44,6 +45,8 @@ dict = {
|
||||
code_focusUnlocked: `¡Desbloqueadas |COUNT| nuevas escuelas de enfoque! Se necesita una actualización del inventario para reflejar los cambios en el juego. Visitar la navegación debería ser la forma más sencilla de activarlo.`,
|
||||
code_addModsConfirm: `¿Estás seguro de que deseas agregar |COUNT| modificadores a tu cuenta?`,
|
||||
code_succImport: `Importación exitosa.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_gild: `Refinar`,
|
||||
code_moa: `Moa`,
|
||||
code_zanuka: `Sabueso`,
|
||||
@ -199,6 +202,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
|
||||
cheats_changeButton: `Cambiar`,
|
||||
cheats_none: `Ninguno`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `Estado del mundo`,
|
||||
worldState_creditBoost: `Potenciador de Créditos`,
|
||||
|
@ -4,6 +4,7 @@ dict = {
|
||||
general_addButton: `Ajouter`,
|
||||
general_setButton: `[UNTRANSLATED] Set`,
|
||||
general_bulkActions: `Action groupée`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`,
|
||||
code_regFail: `Enregistrement impossible. Compte existant?`,
|
||||
@ -44,6 +45,8 @@ dict = {
|
||||
code_focusUnlocked: `|COUNT| écoles de Focus déverrouillées ! Synchronisation de l'inventaire nécessaire.`,
|
||||
code_addModsConfirm: `Ajouter |COUNT| mods à l'inventaire ?`,
|
||||
code_succImport: `Importé.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_gild: `Polir`,
|
||||
code_moa: `Moa`,
|
||||
code_zanuka: `Molosse`,
|
||||
@ -199,6 +202,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Allégeance`,
|
||||
cheats_changeButton: `Changer`,
|
||||
cheats_none: `Aucun`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
|
@ -4,6 +4,7 @@ dict = {
|
||||
general_addButton: `Добавить`,
|
||||
general_setButton: `Установить`,
|
||||
general_bulkActions: `Массовые действия`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
|
||||
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
|
||||
@ -44,6 +45,8 @@ dict = {
|
||||
code_focusUnlocked: `Разблокировано |COUNT| новых школ фокуса! Для отображения изменений в игре потребуется обновление инвентаря. Посещение навигации — самый простой способ этого добиться.`,
|
||||
code_addModsConfirm: `Вы уверены, что хотите добавить |COUNT| модов на ваш аккаунт?`,
|
||||
code_succImport: `Успешно импортировано.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_gild: `Улучшить`,
|
||||
code_moa: `МОА`,
|
||||
code_zanuka: `Гончая`,
|
||||
@ -199,6 +202,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
|
||||
cheats_changeButton: `Изменить`,
|
||||
cheats_none: `Отсутствует`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
|
@ -4,6 +4,7 @@ dict = {
|
||||
general_addButton: `添加`,
|
||||
general_setButton: `设置`,
|
||||
general_bulkActions: `批量操作`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
code_loginFail: `登录失败.请检查邮箱和密码.`,
|
||||
code_regFail: `注册失败.账号已存在.`,
|
||||
@ -44,6 +45,8 @@ dict = {
|
||||
code_focusUnlocked: `已解锁|COUNT|个新专精学派!需要游戏内仓库更新才能生效,您可以通过访问星图来触发仓库更新.`,
|
||||
code_addModsConfirm: `确定要向账户添加|COUNT|张MOD吗?`,
|
||||
code_succImport: `导入成功。`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_gild: `镀金`,
|
||||
code_moa: `恐鸟`,
|
||||
code_zanuka: `猎犬`,
|
||||
@ -199,6 +202,7 @@ dict = {
|
||||
cheats_changeSupportedSyndicate: `支持的集团`,
|
||||
cheats_changeButton: `更改`,
|
||||
cheats_none: `无`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `世界状态配置`,
|
||||
worldState_creditBoost: `现金加成`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user