forked from OpenWF/SpaceNinjaServer
Compare commits
10 Commits
65be1083ce
...
77a4ccde9d
Author | SHA1 | Date | |
---|---|---|---|
77a4ccde9d | |||
f796f9a851 | |||
e18b8e09ea | |||
0d8044b87c | |||
a109ea6c5d | |||
7eb95c995c | |||
dc8f32d4d8 | |||
ba70ba88dd | |||
08d4a03c50 | |||
45feff682b |
8
package-lock.json
generated
8
package-lock.json
generated
@ -23,7 +23,7 @@
|
|||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"undici": "^7.10.0",
|
"undici": "^7.10.0",
|
||||||
"warframe-public-export-plus": "^0.5.77",
|
"warframe-public-export-plus": "^0.5.78",
|
||||||
"warframe-riven-info": "^0.1.2",
|
"warframe-riven-info": "^0.1.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0",
|
"winston-daily-rotate-file": "^5.0.0",
|
||||||
@ -5479,9 +5479,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/warframe-public-export-plus": {
|
"node_modules/warframe-public-export-plus": {
|
||||||
"version": "0.5.77",
|
"version": "0.5.78",
|
||||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.77.tgz",
|
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.78.tgz",
|
||||||
"integrity": "sha512-Th/b82pYB4i95afC/s8MDDXsVMl3vyy1qDwLO/AzV6peVBTs2kCSO68nNh7yXDiviGlNl0HRq+LR25jMjtFwSg=="
|
"integrity": "sha512-Zvg7N+EdXS8cOAZIxqCbqiqyvQZBgh2xTxEwpHnoyJjNBpm3sP/7dtXmzHaxAZjyaCL4pvi9e7kTvxmpH8Pcag=="
|
||||||
},
|
},
|
||||||
"node_modules/warframe-riven-info": {
|
"node_modules/warframe-riven-info": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": "^5.5",
|
"typescript": "^5.5",
|
||||||
"undici": "^7.10.0",
|
"undici": "^7.10.0",
|
||||||
"warframe-public-export-plus": "^0.5.77",
|
"warframe-public-export-plus": "^0.5.78",
|
||||||
"warframe-riven-info": "^0.1.2",
|
"warframe-riven-info": "^0.1.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0",
|
"winston-daily-rotate-file": "^5.0.0",
|
||||||
|
@ -6,10 +6,18 @@ import allDialogue from "@/static/fixed_responses/allDialogue.json";
|
|||||||
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
||||||
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
|
import {
|
||||||
|
eFaction,
|
||||||
|
ExportCustoms,
|
||||||
|
ExportFlavour,
|
||||||
|
ExportResources,
|
||||||
|
ExportVirtuals,
|
||||||
|
ICountedItem
|
||||||
|
} from "warframe-public-export-plus";
|
||||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
||||||
import {
|
import {
|
||||||
addEmailItem,
|
addEmailItem,
|
||||||
|
addItem,
|
||||||
addMiscItems,
|
addMiscItems,
|
||||||
allDailyAffiliationKeys,
|
allDailyAffiliationKeys,
|
||||||
checkCalendarAutoAdvance,
|
checkCalendarAutoAdvance,
|
||||||
@ -30,7 +38,8 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
|
|||||||
import { DailyDeal } from "@/src/models/worldStateModel";
|
import { DailyDeal } from "@/src/models/worldStateModel";
|
||||||
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
|
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
|
||||||
import { generateRewardSeed } from "@/src/services/rngService";
|
import { generateRewardSeed } from "@/src/services/rngService";
|
||||||
import { getWorldState } from "@/src/services/worldStateService";
|
import { getInvasionByOid, getWorldState } from "@/src/services/worldStateService";
|
||||||
|
import { createMessage } from "@/src/services/inboxService";
|
||||||
|
|
||||||
export const inventoryController: RequestHandler = async (request, response) => {
|
export const inventoryController: RequestHandler = async (request, response) => {
|
||||||
const account = await getAccountForRequest(request);
|
const account = await getAccountForRequest(request);
|
||||||
@ -186,6 +195,63 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
|||||||
//await inventory.save();
|
//await inventory.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i != inventory.QualifyingInvasions.length; ) {
|
||||||
|
const qi = inventory.QualifyingInvasions[i];
|
||||||
|
const invasion = getInvasionByOid(qi.invasionId.toString());
|
||||||
|
if (!invasion) {
|
||||||
|
logger.debug(`removing QualifyingInvasions entry for unknown invasion: ${qi.invasionId.toString()}`);
|
||||||
|
inventory.QualifyingInvasions.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (invasion.Completed) {
|
||||||
|
let factionSidedWith: string | undefined;
|
||||||
|
let battlePay: ICountedItem[] | undefined;
|
||||||
|
if (qi.AttackerScore >= 3) {
|
||||||
|
factionSidedWith = invasion.Faction;
|
||||||
|
battlePay = invasion.AttackerReward.countedItems;
|
||||||
|
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
|
||||||
|
} else if (qi.DefenderScore >= 3) {
|
||||||
|
factionSidedWith = invasion.DefenderFaction;
|
||||||
|
battlePay = invasion.DefenderReward.countedItems;
|
||||||
|
logger.debug(`invasion pay from ${factionSidedWith}`, { battlePay });
|
||||||
|
}
|
||||||
|
if (factionSidedWith) {
|
||||||
|
if (battlePay) {
|
||||||
|
// Decoupling rewards from the inbox message because it may delete itself without being read
|
||||||
|
for (const item of battlePay) {
|
||||||
|
await addItem(inventory, item.ItemType, item.ItemCount);
|
||||||
|
}
|
||||||
|
await createMessage(account._id, [
|
||||||
|
{
|
||||||
|
sndr: eFaction.find(x => x.tag == factionSidedWith)?.name ?? factionSidedWith, // TOVERIFY
|
||||||
|
msg: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageBody`,
|
||||||
|
sub: `/Lotus/Language/G1Quests/${factionSidedWith}_InvasionThankyouMessageSubject`,
|
||||||
|
countedAtt: battlePay,
|
||||||
|
attVisualOnly: true,
|
||||||
|
icon:
|
||||||
|
factionSidedWith == "FC_GRINEER"
|
||||||
|
? "/Lotus/Interface/Icons/Npcs/EliteRifleLancerAvatar.png" // Source: https://www.reddit.com/r/Warframe/comments/1aj4usx/battle_pay_worth_10_plat/, https://www.youtube.com/watch?v=XhNZ6ai6BOY
|
||||||
|
: "/Lotus/Interface/Icons/Npcs/CrewmanNormal.png", // My best source for this is https://www.youtube.com/watch?v=rxrCCFm73XE around 1:37
|
||||||
|
// TOVERIFY: highPriority?
|
||||||
|
endDate: new Date(Date.now() + 86400_000) // TOVERIFY: This type of inbox message seems to automatically delete itself. We'll just delete it after 24 hours, but it's not clear if this is correct.
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (invasion.Faction != "FC_INFESTATION") {
|
||||||
|
// Sided with grineer -> opposed corpus -> send zanuka (harvester)
|
||||||
|
// Sided with corpus -> opposed grineer -> send g3 (death squad)
|
||||||
|
inventory[factionSidedWith != "FC_GRINEER" ? "DeathSquadable" : "Harvestable"] = true;
|
||||||
|
// TOVERIFY: Should this happen earlier?
|
||||||
|
// TOVERIFY: Should this send an (ephemeral) email?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug(`removing QualifyingInvasions entry for completed invasion: ${qi.invasionId.toString()}`);
|
||||||
|
inventory.QualifyingInvasions.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
if (inventory.LastInventorySync) {
|
if (inventory.LastInventorySync) {
|
||||||
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
|
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
|
||||||
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
|
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
|
||||||
|
@ -1,25 +1,39 @@
|
|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
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 { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { IPlayerSkills } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IPlayerSkills } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
export const playerSkillsController: RequestHandler = async (req, res) => {
|
export const playerSkillsController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
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 request = getJSONfromString<IPlayerSkillsRequest>(String(req.body));
|
||||||
|
|
||||||
const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
|
const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
|
||||||
const cost = (request.Pool == "LPP_DRIFTER" ? drifterCosts[oldRank] : 1 << oldRank) * 1000;
|
const cost = (request.Pool == "LPP_DRIFTER" ? drifterCosts[oldRank] : 1 << oldRank) * 1000;
|
||||||
inventory.PlayerSkills[request.Pool as keyof IPlayerSkills] -= cost;
|
inventory.PlayerSkills[request.Pool as keyof IPlayerSkills] -= cost;
|
||||||
inventory.PlayerSkills[request.Skill as keyof IPlayerSkills]++;
|
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({
|
res.json({
|
||||||
Pool: request.Pool,
|
Pool: request.Pool,
|
||||||
PoolInc: -cost,
|
PoolInc: -cost,
|
||||||
Skill: request.Skill,
|
Skill: request.Skill,
|
||||||
Rank: oldRank + 1
|
Rank: oldRank + 1,
|
||||||
|
InventoryChanges: inventoryChanges
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
5
src/controllers/api/resetQuestProgressController.ts
Normal file
5
src/controllers/api/resetQuestProgressController.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const resetQuestProgressController: RequestHandler = (_req, res) => {
|
||||||
|
res.send("1").end();
|
||||||
|
};
|
65
src/controllers/custom/changeModularPartsController.ts
Normal file
65
src/controllers/custom/changeModularPartsController.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const changeModularPartsController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const request = req.body as IUpdateFingerPrintRequest;
|
||||||
|
const inventory = await getInventory(accountId, request.category);
|
||||||
|
const item = inventory[request.category].id(request.oid);
|
||||||
|
if (item) {
|
||||||
|
item.ModularParts = request.modularParts;
|
||||||
|
|
||||||
|
request.modularParts.forEach(part => {
|
||||||
|
const categoryMap = mapping[part];
|
||||||
|
if (categoryMap && categoryMap[request.category]) {
|
||||||
|
item.ItemType = categoryMap[request.category]!;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await inventory.save();
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IUpdateFingerPrintRequest {
|
||||||
|
category: TEquipmentKey;
|
||||||
|
oid: string;
|
||||||
|
modularParts: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping: Partial<Record<string, Partial<Record<TEquipmentKey, string>>>> = {
|
||||||
|
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelAPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||||
|
},
|
||||||
|
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelEgg/InfModularBarrelEggPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||||
|
},
|
||||||
|
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||||
|
},
|
||||||
|
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelCPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||||
|
},
|
||||||
|
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelDPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||||
|
},
|
||||||
|
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelBeam/InfModularBarrelBeamPart": {
|
||||||
|
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||||
|
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||||
|
},
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA": {
|
||||||
|
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit"
|
||||||
|
},
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB": {
|
||||||
|
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit"
|
||||||
|
},
|
||||||
|
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC": {
|
||||||
|
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
||||||
|
}
|
||||||
|
};
|
@ -112,6 +112,7 @@ import { removeFromGuildController } from "@/src/controllers/api/removeFromGuild
|
|||||||
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
||||||
import { renamePetController } from "@/src/controllers/api/renamePetController";
|
import { renamePetController } from "@/src/controllers/api/renamePetController";
|
||||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
||||||
|
import { resetQuestProgressController } from "@/src/controllers/api/resetQuestProgressController";
|
||||||
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
||||||
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
||||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadoutController";
|
import { saveLoadoutController } from "@/src/controllers/api/saveLoadoutController";
|
||||||
@ -209,6 +210,7 @@ apiRouter.get("/questControl.php", questControlController);
|
|||||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
||||||
apiRouter.get("/removeFriend.php", removeFriendGetController);
|
apiRouter.get("/removeFriend.php", removeFriendGetController);
|
||||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
||||||
|
apiRouter.get("/resetQuestProgress.php", resetQuestProgressController);
|
||||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
||||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
||||||
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
||||||
|
@ -25,6 +25,7 @@ import { manageQuestsController } from "@/src/controllers/custom/manageQuestsCon
|
|||||||
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
||||||
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
|
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
|
||||||
import { updateFingerprintController } from "@/src/controllers/custom/updateFingerprintController";
|
import { updateFingerprintController } from "@/src/controllers/custom/updateFingerprintController";
|
||||||
|
import { changeModularPartsController } from "@/src/controllers/custom/changeModularPartsController";
|
||||||
|
|
||||||
import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
|
import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ customRouter.post("/manageQuests", manageQuestsController);
|
|||||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||||
customRouter.post("/setBooster", setBoosterController);
|
customRouter.post("/setBooster", setBoosterController);
|
||||||
customRouter.post("/updateFingerprint", updateFingerprintController);
|
customRouter.post("/updateFingerprint", updateFingerprintController);
|
||||||
|
customRouter.post("/changeModularParts", changeModularPartsController);
|
||||||
|
|
||||||
customRouter.post("/getConfig", getConfigController);
|
customRouter.post("/getConfig", getConfigController);
|
||||||
customRouter.post("/setConfig", setConfigController);
|
customRouter.post("/setConfig", setConfigController);
|
||||||
|
@ -815,7 +815,7 @@ export const addItem = async (
|
|||||||
if (!seed) {
|
if (!seed) {
|
||||||
throw new Error(`Expected crew member to have a 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 {
|
return {
|
||||||
...addCrewMember(inventory, typeName, seed),
|
...addCrewMember(inventory, typeName, seed),
|
||||||
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
...occupySlot(inventory, InventorySlot.CREWMEMBERS, premiumPurchase)
|
||||||
@ -1635,6 +1635,15 @@ export const addEmailItem = async (
|
|||||||
return inventoryChanges;
|
return inventoryChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const xpEarningParts: readonly string[] = [
|
||||||
|
"LWPT_BLADE",
|
||||||
|
"LWPT_GUN_BARREL",
|
||||||
|
"LWPT_AMP_OCULUS",
|
||||||
|
"LWPT_MOA_HEAD",
|
||||||
|
"LWPT_ZANUKA_HEAD",
|
||||||
|
"LWPT_HB_DECK"
|
||||||
|
];
|
||||||
|
|
||||||
export const applyClientEquipmentUpdates = (
|
export const applyClientEquipmentUpdates = (
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
gearArray: IEquipmentClient[],
|
gearArray: IEquipmentClient[],
|
||||||
@ -1653,13 +1662,26 @@ export const applyClientEquipmentUpdates = (
|
|||||||
item.XP ??= 0;
|
item.XP ??= 0;
|
||||||
item.XP += XP;
|
item.XP += XP;
|
||||||
|
|
||||||
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == item.ItemType);
|
let xpItemType = item.ItemType;
|
||||||
|
if (item.ModularParts) {
|
||||||
|
for (const part of item.ModularParts) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
const partType = ExportWeapons[part]?.partType;
|
||||||
|
if (partType !== undefined && xpEarningParts.indexOf(partType) != -1) {
|
||||||
|
xpItemType = part;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug(`adding xp to ${xpItemType} for modular item ${fromOid(ItemId)} (${item.ItemType})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == xpItemType);
|
||||||
if (xpinfoIndex !== -1) {
|
if (xpinfoIndex !== -1) {
|
||||||
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
||||||
xpinfo.XP += XP;
|
xpinfo.XP += XP;
|
||||||
} else {
|
} else {
|
||||||
inventory.XPInfo.push({
|
inventory.XPInfo.push({
|
||||||
ItemType: item.ItemType,
|
ItemType: xpItemType,
|
||||||
XP: XP
|
XP: XP
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -558,6 +558,7 @@ export const addMissionInventoryUpdates = async (
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
inventory.DeathSquadable = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "LockedWeaponGroup": {
|
case "LockedWeaponGroup": {
|
||||||
@ -576,7 +577,7 @@ export const addMissionInventoryUpdates = async (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "IncHarvester": {
|
case "IncHarvester": {
|
||||||
inventory.Harvestable = true;
|
// Unsure what to do with this
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "CurrentLoadOutIds": {
|
case "CurrentLoadOutIds": {
|
||||||
|
@ -6,15 +6,18 @@ import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.j
|
|||||||
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
|
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
|
||||||
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
|
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
|
||||||
import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
|
import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
|
||||||
|
import invasionNodes from "@/static/fixed_responses/worldState/invasionNodes.json";
|
||||||
|
import invasionRewards from "@/static/fixed_responses/worldState/invasionRewards.json";
|
||||||
import { buildConfig } from "@/src/services/buildConfigService";
|
import { buildConfig } from "@/src/services/buildConfigService";
|
||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "@/src/services/rngService";
|
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "@/src/services/rngService";
|
||||||
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
|
import { eMissionType, ExportRegions, ExportSyndicates, IMissionReward, IRegion } from "warframe-public-export-plus";
|
||||||
import {
|
import {
|
||||||
ICalendarDay,
|
ICalendarDay,
|
||||||
ICalendarEvent,
|
ICalendarEvent,
|
||||||
ICalendarSeason,
|
ICalendarSeason,
|
||||||
|
IInvasion,
|
||||||
ILiteSortie,
|
ILiteSortie,
|
||||||
IPrimeVaultTrader,
|
IPrimeVaultTrader,
|
||||||
IPrimeVaultTraderOffer,
|
IPrimeVaultTraderOffer,
|
||||||
@ -1227,6 +1230,78 @@ const getAllVarziaManifests = (): IPrimeVaultTraderOffer[] => {
|
|||||||
return [...dualPacks, ...singlePacks, ...items, ...bobbleHeads, ...relics];
|
return [...dualPacks, ...singlePacks, ...items, ...bobbleHeads, ...relics];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createInvasion = (day: number, idx: number): IInvasion => {
|
||||||
|
const id = day * 3 + idx;
|
||||||
|
const defender = (["FC_GRINEER", "FC_CORPUS", day % 2 ? "FC_GRINEER" : "FC_CORPUS"] as const)[idx];
|
||||||
|
const rng = new SRng(new SRng(id).randomInt(0, 1_000_000));
|
||||||
|
const isInfestationOutbreak = rng.randomInt(0, 1) == 0;
|
||||||
|
const attacker = isInfestationOutbreak ? "FC_INFESTATION" : defender == "FC_GRINEER" ? "FC_CORPUS" : "FC_GRINEER";
|
||||||
|
const startMs = EPOCH + day * 86400_000;
|
||||||
|
const oid =
|
||||||
|
((startMs / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
||||||
|
"fd148cb8" +
|
||||||
|
(idx & 0xffffffff).toString(16).padStart(8, "0");
|
||||||
|
const node = sequentiallyUniqueRandomElement(invasionNodes[defender], id, 5, 690175)!; // Can't repeat the other 2 on this day nor the last 3
|
||||||
|
const progress = (Date.now() - startMs) / 86400_000;
|
||||||
|
const countMultiplier = isInfestationOutbreak || rng.randomInt(0, 1) ? -1 : 1; // if defender is winning, count is negative
|
||||||
|
const fiftyPercent = rng.randomInt(1000, 29000); // introduce some 'yitter' for the percentages
|
||||||
|
const rewardFloat = rng.randomFloat();
|
||||||
|
const rewardTier = rewardFloat < 0.201 ? "RARE" : rewardFloat < 0.7788 ? "COMMON" : "UNCOMMON";
|
||||||
|
const attackerReward: IMissionReward = {};
|
||||||
|
const defenderReward: IMissionReward = {};
|
||||||
|
if (isInfestationOutbreak) {
|
||||||
|
defenderReward.countedItems = [
|
||||||
|
rng.randomElement(invasionRewards[rng.randomInt(0, 1) ? "FC_INFESTATION" : defender][rewardTier])!
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
attackerReward.countedItems = [rng.randomElement(invasionRewards[attacker][rewardTier])!];
|
||||||
|
defenderReward.countedItems = [rng.randomElement(invasionRewards[defender][rewardTier])!];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
_id: { $oid: oid },
|
||||||
|
Faction: attacker,
|
||||||
|
DefenderFaction: defender,
|
||||||
|
Node: node,
|
||||||
|
Count: Math.round(
|
||||||
|
(progress < 0.5 ? progress * 2 * fiftyPercent : fiftyPercent + (30_000 - fiftyPercent) * (progress - 0.5)) *
|
||||||
|
countMultiplier
|
||||||
|
),
|
||||||
|
Goal: 30000, // Value seems to range from 30000 to 98000 in intervals of 1000. Higher values are increasingly rare. I don't think this is relevant for the frontend besides dividing count by it.
|
||||||
|
LocTag: isInfestationOutbreak
|
||||||
|
? ExportRegions[node].missionIndex == 0
|
||||||
|
? "/Lotus/Language/Menu/InfestedInvasionBoss"
|
||||||
|
: "/Lotus/Language/Menu/InfestedInvasionGeneric"
|
||||||
|
: attacker == "FC_CORPUS"
|
||||||
|
? "/Lotus/Language/Menu/CorpusInvasionGeneric"
|
||||||
|
: "/Lotus/Language/Menu/GrineerInvasionGeneric",
|
||||||
|
Completed: startMs + 86400_000 < Date.now(), // Sorta unfaithful. Invasions on live are (at least in part) in fluenced by people completing them. And otherwise also probably not hardcoded to last 24 hours.
|
||||||
|
ChainID: { $oid: oid },
|
||||||
|
AttackerReward: attackerReward,
|
||||||
|
AttackerMissionInfo: {
|
||||||
|
seed: rng.randomInt(0, 1_000_000),
|
||||||
|
faction: defender
|
||||||
|
},
|
||||||
|
DefenderReward: defenderReward,
|
||||||
|
DefenderMissionInfo: {
|
||||||
|
seed: rng.randomInt(0, 1_000_000),
|
||||||
|
faction: attacker
|
||||||
|
},
|
||||||
|
Activation: {
|
||||||
|
$date: {
|
||||||
|
$numberLong: startMs.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getInvasionByOid = (oid: string): IInvasion | undefined => {
|
||||||
|
const arr = oid.split("fd148cb8");
|
||||||
|
if (arr.length == 2 && arr[0].length == 8 && arr[1].length == 8) {
|
||||||
|
return createInvasion(idToDay(oid), parseInt(arr[1], 16));
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const getWorldState = (buildLabel?: string): IWorldState => {
|
export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||||
const constraints: ITimeConstraint[] = [];
|
const constraints: ITimeConstraint[] = [];
|
||||||
if (config.worldState?.eidolonOverride) {
|
if (config.worldState?.eidolonOverride) {
|
||||||
@ -1275,6 +1350,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
LiteSorties: [],
|
LiteSorties: [],
|
||||||
ActiveMissions: [],
|
ActiveMissions: [],
|
||||||
GlobalUpgrades: [],
|
GlobalUpgrades: [],
|
||||||
|
Invasions: [],
|
||||||
VoidTraders: [],
|
VoidTraders: [],
|
||||||
PrimeVaultTraders: [],
|
PrimeVaultTraders: [],
|
||||||
VoidStorms: [],
|
VoidStorms: [],
|
||||||
@ -1477,6 +1553,20 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rough outline of dynamic invasions.
|
||||||
|
// TODO: Invasions chains, e.g. an infestation mission would soon lead to other nodes on that planet also having an infestation invasion.
|
||||||
|
// TODO: Grineer/Corpus to fund their death stars with each invasion win.
|
||||||
|
{
|
||||||
|
worldState.Invasions.push(createInvasion(day, 0));
|
||||||
|
worldState.Invasions.push(createInvasion(day, 1));
|
||||||
|
worldState.Invasions.push(createInvasion(day, 2));
|
||||||
|
|
||||||
|
// Completed invasions stay for up to 24 hours as the winner 'occupies' that node
|
||||||
|
worldState.Invasions.push(createInvasion(day - 1, 0));
|
||||||
|
worldState.Invasions.push(createInvasion(day - 1, 1));
|
||||||
|
worldState.Invasions.push(createInvasion(day - 1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
// Baro
|
// Baro
|
||||||
{
|
{
|
||||||
const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
|
const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
|
||||||
|
@ -12,6 +12,7 @@ export interface IWorldState {
|
|||||||
SyndicateMissions: ISyndicateMissionInfo[];
|
SyndicateMissions: ISyndicateMissionInfo[];
|
||||||
ActiveMissions: IFissure[];
|
ActiveMissions: IFissure[];
|
||||||
GlobalUpgrades: IGlobalUpgrade[];
|
GlobalUpgrades: IGlobalUpgrade[];
|
||||||
|
Invasions: IInvasion[];
|
||||||
NodeOverrides: INodeOverride[];
|
NodeOverrides: INodeOverride[];
|
||||||
VoidTraders: IVoidTrader[];
|
VoidTraders: IVoidTrader[];
|
||||||
PrimeVaultTraders: IPrimeVaultTrader[];
|
PrimeVaultTraders: IPrimeVaultTrader[];
|
||||||
@ -82,6 +83,28 @@ export interface IGlobalUpgrade {
|
|||||||
LocalizeDescTag: string;
|
LocalizeDescTag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IInvasion {
|
||||||
|
_id: IOid;
|
||||||
|
Faction: string;
|
||||||
|
DefenderFaction: string;
|
||||||
|
Node: string;
|
||||||
|
Count: number;
|
||||||
|
Goal: number;
|
||||||
|
LocTag: string;
|
||||||
|
Completed: boolean;
|
||||||
|
ChainID: IOid;
|
||||||
|
AttackerReward: IMissionReward;
|
||||||
|
AttackerMissionInfo: IInvasionMissionInfo;
|
||||||
|
DefenderReward: IMissionReward;
|
||||||
|
DefenderMissionInfo: IInvasionMissionInfo;
|
||||||
|
Activation: IMongoDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInvasionMissionInfo {
|
||||||
|
seed: number;
|
||||||
|
faction: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFissure {
|
export interface IFissure {
|
||||||
_id: IOid;
|
_id: IOid;
|
||||||
Region: number;
|
Region: number;
|
||||||
|
114
static/fixed_responses/worldState/invasionNodes.json
Normal file
114
static/fixed_responses/worldState/invasionNodes.json
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"FC_CORPUS": [
|
||||||
|
"SettlementNode1",
|
||||||
|
"SettlementNode2",
|
||||||
|
"SettlementNode3",
|
||||||
|
"SettlementNode11",
|
||||||
|
"SettlementNode12",
|
||||||
|
"SettlementNode14",
|
||||||
|
"SettlementNode15",
|
||||||
|
"SettlementNode20",
|
||||||
|
"SolNode1",
|
||||||
|
"SolNode2",
|
||||||
|
"SolNode4",
|
||||||
|
"SolNode6",
|
||||||
|
"SolNode10",
|
||||||
|
"SolNode17",
|
||||||
|
"SolNode21",
|
||||||
|
"SolNode22",
|
||||||
|
"SolNode23",
|
||||||
|
"SolNode25",
|
||||||
|
"SolNode38",
|
||||||
|
"SolNode43",
|
||||||
|
"SolNode48",
|
||||||
|
"SolNode49",
|
||||||
|
"SolNode51",
|
||||||
|
"SolNode53",
|
||||||
|
"SolNode56",
|
||||||
|
"SolNode57",
|
||||||
|
"SolNode61",
|
||||||
|
"SolNode62",
|
||||||
|
"SolNode65",
|
||||||
|
"SolNode66",
|
||||||
|
"SolNode72",
|
||||||
|
"SolNode73",
|
||||||
|
"SolNode74",
|
||||||
|
"SolNode76",
|
||||||
|
"SolNode78",
|
||||||
|
"SolNode81",
|
||||||
|
"SolNode84",
|
||||||
|
"SolNode88",
|
||||||
|
"SolNode97",
|
||||||
|
"SolNode100",
|
||||||
|
"SolNode101",
|
||||||
|
"SolNode102",
|
||||||
|
"SolNode104",
|
||||||
|
"SolNode107",
|
||||||
|
"SolNode109",
|
||||||
|
"SolNode118",
|
||||||
|
"SolNode121",
|
||||||
|
"SolNode123",
|
||||||
|
"SolNode125",
|
||||||
|
"SolNode126",
|
||||||
|
"SolNode127",
|
||||||
|
"SolNode128",
|
||||||
|
"SolNode203",
|
||||||
|
"SolNode205",
|
||||||
|
"SolNode209",
|
||||||
|
"SolNode210",
|
||||||
|
"SolNode211",
|
||||||
|
"SolNode212",
|
||||||
|
"SolNode214",
|
||||||
|
"SolNode216",
|
||||||
|
"SolNode217",
|
||||||
|
"SolNode220"
|
||||||
|
],
|
||||||
|
"FC_GRINEER": [
|
||||||
|
"SolNode11",
|
||||||
|
"SolNode16",
|
||||||
|
"SolNode18",
|
||||||
|
"SolNode19",
|
||||||
|
"SolNode20",
|
||||||
|
"SolNode30",
|
||||||
|
"SolNode31",
|
||||||
|
"SolNode32",
|
||||||
|
"SolNode36",
|
||||||
|
"SolNode41",
|
||||||
|
"SolNode42",
|
||||||
|
"SolNode45",
|
||||||
|
"SolNode46",
|
||||||
|
"SolNode50",
|
||||||
|
"SolNode58",
|
||||||
|
"SolNode67",
|
||||||
|
"SolNode68",
|
||||||
|
"SolNode70",
|
||||||
|
"SolNode82",
|
||||||
|
"SolNode93",
|
||||||
|
"SolNode96",
|
||||||
|
"SolNode99",
|
||||||
|
"SolNode106",
|
||||||
|
"SolNode113",
|
||||||
|
"SolNode131",
|
||||||
|
"SolNode132",
|
||||||
|
"SolNode135",
|
||||||
|
"SolNode137",
|
||||||
|
"SolNode138",
|
||||||
|
"SolNode139",
|
||||||
|
"SolNode140",
|
||||||
|
"SolNode141",
|
||||||
|
"SolNode144",
|
||||||
|
"SolNode146",
|
||||||
|
"SolNode147",
|
||||||
|
"SolNode149",
|
||||||
|
"SolNode177",
|
||||||
|
"SolNode181",
|
||||||
|
"SolNode184",
|
||||||
|
"SolNode185",
|
||||||
|
"SolNode187",
|
||||||
|
"SolNode188",
|
||||||
|
"SolNode189",
|
||||||
|
"SolNode191",
|
||||||
|
"SolNode195",
|
||||||
|
"SolNode196"
|
||||||
|
]
|
||||||
|
}
|
190
static/fixed_responses/worldState/invasionRewards.json
Normal file
190
static/fixed_responses/worldState/invasionRewards.json
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
{
|
||||||
|
"FC_GRINEER": {
|
||||||
|
"COMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Items/Research/ChemComponent",
|
||||||
|
"ItemCount": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UNCOMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/KarakWraithBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/KarakWraithStock",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/StrunWraithBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/StrunWraithStock",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/LatronWraithBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/LatronWraithStock",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/TwinVipersWraithBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithLink",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/TwinVipersWraithReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/GrineerCombatKnifeSortieBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHilt",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeBlade",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/GrineerCombatKnifeHeatsink",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"RARE": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FC_CORPUS": {
|
||||||
|
"COMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
|
||||||
|
"ItemCount": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UNCOMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/DeraVandalBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/DeraVandalStock",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/SnipetronVandalBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalStock",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalReceiver",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Weapons/WeaponParts/SnipetronVandalBarrel",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"RARE": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/OrokinCatalystBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/OrokinReactorBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/FormaBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Recipes/Components/UtilityUnlockerBlueprint",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FC_INFESTATION": {
|
||||||
|
"COMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UNCOMMON": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Items/Research/BioComponent",
|
||||||
|
"ItemCount": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"RARE": [
|
||||||
|
{
|
||||||
|
"ItemType": "/Lotus/Types/Items/MiscItems/InfestedAladCoordinate",
|
||||||
|
"ItemCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -117,46 +117,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Invasions": [
|
|
||||||
{
|
|
||||||
"_id": {
|
|
||||||
"$oid": "67c8ec8b3d0d86b236c1c18f"
|
|
||||||
},
|
|
||||||
"Faction": "FC_INFESTATION",
|
|
||||||
"DefenderFaction": "FC_CORPUS",
|
|
||||||
"Node": "SolNode53",
|
|
||||||
"Count": -28558,
|
|
||||||
"Goal": 30000,
|
|
||||||
"LocTag": "/Lotus/Language/Menu/InfestedInvasionBoss",
|
|
||||||
"Completed": false,
|
|
||||||
"ChainID": {
|
|
||||||
"$oid": "67c8b6a2bde0dfd0f7c1c18d"
|
|
||||||
},
|
|
||||||
"AttackerReward": [],
|
|
||||||
"AttackerMissionInfo": {
|
|
||||||
"seed": 488863,
|
|
||||||
"faction": "FC_CORPUS"
|
|
||||||
},
|
|
||||||
"DefenderReward": {
|
|
||||||
"countedItems": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Research/EnergyComponent",
|
|
||||||
"ItemCount": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"DefenderMissionInfo": {
|
|
||||||
"seed": 127653,
|
|
||||||
"faction": "FC_INFESTATION",
|
|
||||||
"missionReward": []
|
|
||||||
},
|
|
||||||
"Activation": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "1741221003031"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SyndicateMissions": [
|
"SyndicateMissions": [
|
||||||
{
|
{
|
||||||
"_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
|
"_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
|
||||||
|
@ -478,6 +478,12 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="modularParts-card" class="card mb-3 d-none">
|
||||||
|
<h5 class="card-header" data-loc="detailedView_modularPartsLabel"></h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<form id="modularParts-form" class="input-group mb-3" onsubmit="handleModularPartsChange(event)"></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="valenceBonus-card" class="card mb-3 d-none">
|
<div id="valenceBonus-card" class="card mb-3 d-none">
|
||||||
<h5 class="card-header" data-loc="detailedView_valenceBonusLabel"></h5>
|
<h5 class="card-header" data-loc="detailedView_valenceBonusLabel"></h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@ -273,6 +273,8 @@ function fetchItemList() {
|
|||||||
window.itemListPromise = new Promise(resolve => {
|
window.itemListPromise = new Promise(resolve => {
|
||||||
const req = $.get("/custom/getItemLists?lang=" + window.lang);
|
const req = $.get("/custom/getItemLists?lang=" + window.lang);
|
||||||
req.done(async data => {
|
req.done(async data => {
|
||||||
|
window.allQuestKeys = data.QuestKeys;
|
||||||
|
|
||||||
await dictPromise;
|
await dictPromise;
|
||||||
|
|
||||||
document.querySelectorAll('[id^="datalist-"]').forEach(datalist => {
|
document.querySelectorAll('[id^="datalist-"]').forEach(datalist => {
|
||||||
@ -728,7 +730,10 @@ function updateInventory() {
|
|||||||
td.appendChild(a);
|
td.appendChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
|
if (
|
||||||
|
["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) ||
|
||||||
|
modularWeapons.includes(item.ItemType)
|
||||||
|
) {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
|
a.href = "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
|
||||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
|
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
|
||||||
@ -879,6 +884,14 @@ function updateInventory() {
|
|||||||
|
|
||||||
// Populate quests route
|
// Populate quests route
|
||||||
document.getElementById("QuestKeys-list").innerHTML = "";
|
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 => {
|
data.QuestKeys.forEach(item => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.setAttribute("data-item-type", item.ItemType);
|
tr.setAttribute("data-item-type", item.ItemType);
|
||||||
@ -972,10 +985,7 @@ function updateInventory() {
|
|||||||
a.href = "#";
|
a.href = "#";
|
||||||
a.onclick = function (event) {
|
a.onclick = function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const option = document.createElement("option");
|
readdQuestKey(itemMap, item.ItemType);
|
||||||
option.setAttribute("data-key", item.ItemType);
|
|
||||||
option.value = itemMap[item.ItemType]?.name ?? item.ItemType;
|
|
||||||
document.getElementById("datalist-QuestKeys").appendChild(option);
|
|
||||||
doQuestUpdate("deleteKey", item.ItemType);
|
doQuestUpdate("deleteKey", item.ItemType);
|
||||||
};
|
};
|
||||||
a.title = loc("code_remove");
|
a.title = loc("code_remove");
|
||||||
@ -1226,6 +1236,35 @@ function updateInventory() {
|
|||||||
document.getElementById("valenceBonus-procent").value = Math.round(buffValue * 1000) / 10;
|
document.getElementById("valenceBonus-procent").value = Math.round(buffValue * 1000) / 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (modularWeapons.includes(item.ItemType)) {
|
||||||
|
document.getElementById("modularParts-card").classList.remove("d-none");
|
||||||
|
const form = document.getElementById("modularParts-form");
|
||||||
|
form.innerHTML = "";
|
||||||
|
const requiredParts = getRequiredParts(category, item.ItemType);
|
||||||
|
|
||||||
|
requiredParts.forEach(modularPart => {
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.classList.add("form-control");
|
||||||
|
input.id = "detailedView-modularPart-" + modularPart;
|
||||||
|
input.setAttribute("list", "datalist-ModularParts-" + modularPart);
|
||||||
|
|
||||||
|
const datalist = document.getElementById("datalist-ModularParts-" + modularPart);
|
||||||
|
const options = Array.from(datalist.options);
|
||||||
|
|
||||||
|
input.value =
|
||||||
|
options.find(option => item.ModularParts.includes(option.getAttribute("data-key")))
|
||||||
|
?.value || "";
|
||||||
|
form.appendChild(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeButton = document.createElement("button");
|
||||||
|
changeButton.classList.add("btn");
|
||||||
|
changeButton.classList.add("btn-primary");
|
||||||
|
changeButton.type = "submit";
|
||||||
|
changeButton.setAttribute("data-loc", "cheats_changeButton");
|
||||||
|
changeButton.innerHTML = loc("cheats_changeButton");
|
||||||
|
form.appendChild(changeButton);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
single.loadRoute("/webui/inventory");
|
single.loadRoute("/webui/inventory");
|
||||||
}
|
}
|
||||||
@ -1325,47 +1364,41 @@ function doAcquireEquipment(category) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doAcquireModularEquipment(category, WeaponType) {
|
function getRequiredParts(category, WeaponType) {
|
||||||
let requiredParts;
|
|
||||||
let Parts = [];
|
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case "HoverBoards":
|
case "Hoverboards":
|
||||||
WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
return ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
||||||
requiredParts = ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
|
||||||
break;
|
|
||||||
case "OperatorAmps":
|
case "OperatorAmps":
|
||||||
requiredParts = ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
return ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
||||||
break;
|
|
||||||
case "Melee":
|
case "Melee":
|
||||||
requiredParts = ["BLADE", "HILT", "HILT_WEIGHT"];
|
return ["BLADE", "HILT", "HILT_WEIGHT"];
|
||||||
break;
|
|
||||||
case "LongGuns":
|
case "LongGuns":
|
||||||
requiredParts = ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
return ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
||||||
break;
|
|
||||||
case "Pistols":
|
case "Pistols":
|
||||||
requiredParts = ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
return ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
||||||
break;
|
|
||||||
case "MoaPets":
|
case "MoaPets":
|
||||||
if (WeaponType == "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
|
return WeaponType === "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit"
|
||||||
requiredParts = ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"];
|
? ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"]
|
||||||
} else {
|
: ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
||||||
requiredParts = ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
|
||||||
}
|
case "KubrowPets": {
|
||||||
break;
|
return WeaponType.endsWith("InfestedCatbrowPetPowerSuit")
|
||||||
case "KubrowPets":
|
? ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"]
|
||||||
if (
|
: ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
||||||
[
|
}
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
|
||||||
].includes(WeaponType)
|
|
||||||
) {
|
|
||||||
requiredParts = ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"];
|
|
||||||
} else {
|
|
||||||
requiredParts = ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doAcquireModularEquipment(category, WeaponType) {
|
||||||
|
if (category === "Hoverboards") WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
||||||
|
const requiredParts = getRequiredParts(category, WeaponType);
|
||||||
|
let Parts = [];
|
||||||
|
|
||||||
requiredParts.forEach(part => {
|
requiredParts.forEach(part => {
|
||||||
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
|
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
|
||||||
if (partName) {
|
if (partName) {
|
||||||
@ -2189,6 +2222,8 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
|
|||||||
document.getElementById("detailedView-title").textContent = "";
|
document.getElementById("detailedView-title").textContent = "";
|
||||||
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
|
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
|
||||||
document.getElementById("archonShards-card").classList.add("d-none");
|
document.getElementById("archonShards-card").classList.add("d-none");
|
||||||
|
document.getElementById("modularParts-card").classList.add("d-none");
|
||||||
|
document.getElementById("modularParts-form").innerHTML = "";
|
||||||
document.getElementById("valenceBonus-card").classList.add("d-none");
|
document.getElementById("valenceBonus-card").classList.add("d-none");
|
||||||
if (window.didInitialInventoryUpdate) {
|
if (window.didInitialInventoryUpdate) {
|
||||||
updateInventory();
|
updateInventory();
|
||||||
@ -2270,6 +2305,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) {
|
function doQuestUpdate(operation, itemType) {
|
||||||
revalidateAuthz().then(() => {
|
revalidateAuthz().then(() => {
|
||||||
$.post({
|
$.post({
|
||||||
@ -2339,22 +2381,10 @@ function handleModularSelection(category) {
|
|||||||
modularFieldsZanuka.style.display = "none";
|
modularFieldsZanuka.style.display = "none";
|
||||||
}
|
}
|
||||||
} else if (inventoryCategory === "KubrowPets") {
|
} else if (inventoryCategory === "KubrowPets") {
|
||||||
if (
|
if (key.endsWith("InfestedCatbrowPetPowerSuit")) {
|
||||||
[
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
|
||||||
].includes(key)
|
|
||||||
) {
|
|
||||||
modularFieldsCatbrow.style.display = "";
|
modularFieldsCatbrow.style.display = "";
|
||||||
modularFieldsKubrow.style.display = "none";
|
modularFieldsKubrow.style.display = "none";
|
||||||
} else if (
|
} else if (key.endsWith("PredatorKubrowPetPowerSuit")) {
|
||||||
[
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
|
|
||||||
].includes(key)
|
|
||||||
) {
|
|
||||||
modularFieldsCatbrow.style.display = "none";
|
modularFieldsCatbrow.style.display = "none";
|
||||||
modularFieldsKubrow.style.display = "";
|
modularFieldsKubrow.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
@ -2793,3 +2823,38 @@ async function markAllAsRead() {
|
|||||||
}
|
}
|
||||||
toast(loc(any ? "code_succRelog" : "code_nothingToDo"));
|
toast(loc(any ? "code_succRelog" : "code_nothingToDo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleModularPartsChange(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const form = document.getElementById("modularParts-form");
|
||||||
|
const inputs = form.querySelectorAll("input");
|
||||||
|
const modularParts = [];
|
||||||
|
inputs.forEach(input => {
|
||||||
|
const key = getKey(input);
|
||||||
|
if (!getKey(input)) {
|
||||||
|
input.addClass("is-invalid");
|
||||||
|
} else {
|
||||||
|
modularParts.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidateAuthz().then(() => {
|
||||||
|
console.log({
|
||||||
|
category: urlParams.get("productCategory"),
|
||||||
|
oid: urlParams.get("itemId"),
|
||||||
|
modularParts
|
||||||
|
});
|
||||||
|
$.post({
|
||||||
|
url: "/custom/changeModularParts?" + window.authz,
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify({
|
||||||
|
category: urlParams.get("productCategory"),
|
||||||
|
oid: urlParams.get("itemId"),
|
||||||
|
modularParts
|
||||||
|
})
|
||||||
|
}).then(function () {
|
||||||
|
updateInventory();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -123,6 +123,7 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `Hinweis: Jede Archon-Scherbe benötigt beim Laden etwas Zeit, um angewendet zu werden.`,
|
detailedView_archonShardsDescription2: `Hinweis: Jede Archon-Scherbe benötigt beim Laden etwas Zeit, um angewendet zu werden.`,
|
||||||
detailedView_valenceBonusLabel: `Valenz-Bonus`,
|
detailedView_valenceBonusLabel: `Valenz-Bonus`,
|
||||||
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
||||||
|
detailedView_modularPartsLabel: `[UNTRANSLATED] Change Modular Parts`,
|
||||||
|
|
||||||
mods_addRiven: `Riven hinzufügen`,
|
mods_addRiven: `Riven hinzufügen`,
|
||||||
mods_fingerprint: `Fingerabdruck`,
|
mods_fingerprint: `Fingerabdruck`,
|
||||||
|
@ -122,6 +122,7 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `Note that each archon shard takes some time to be applied when loading in.`,
|
detailedView_archonShardsDescription2: `Note that each archon shard takes some time to be applied when loading in.`,
|
||||||
detailedView_valenceBonusLabel: `Valence Bonus`,
|
detailedView_valenceBonusLabel: `Valence Bonus`,
|
||||||
detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
|
detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
|
||||||
|
detailedView_modularPartsLabel: `Change Modular Parts`,
|
||||||
|
|
||||||
mods_addRiven: `Add Riven`,
|
mods_addRiven: `Add Riven`,
|
||||||
mods_fingerprint: `Fingerprint`,
|
mods_fingerprint: `Fingerprint`,
|
||||||
|
@ -4,7 +4,7 @@ dict = {
|
|||||||
general_addButton: `Agregar`,
|
general_addButton: `Agregar`,
|
||||||
general_setButton: `Establecer`,
|
general_setButton: `Establecer`,
|
||||||
general_bulkActions: `Acciones masivas`,
|
general_bulkActions: `Acciones masivas`,
|
||||||
general_loading: `[UNTRANSLATED] Loading...`,
|
general_loading: `Cargando...`,
|
||||||
|
|
||||||
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
|
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
|
||||||
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
|
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
|
||||||
@ -45,8 +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_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_addModsConfirm: `¿Estás seguro de que deseas agregar |COUNT| modificadores a tu cuenta?`,
|
||||||
code_succImport: `Importación exitosa.`,
|
code_succImport: `Importación exitosa.`,
|
||||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
code_succRelog: `Hecho. Ten en cuenta que deberás volver a iniciar sesión para ver los cambios en el juego.`,
|
||||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
code_nothingToDo: `Hecho. No había nada que hacer.`,
|
||||||
code_gild: `Refinar`,
|
code_gild: `Refinar`,
|
||||||
code_moa: `Moa`,
|
code_moa: `Moa`,
|
||||||
code_zanuka: `Sabueso`,
|
code_zanuka: `Sabueso`,
|
||||||
@ -123,13 +123,14 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `Ten en cuenta que cada fragmento de archón tarda un poco en aplicarse al cargar`,
|
detailedView_archonShardsDescription2: `Ten en cuenta que cada fragmento de archón tarda un poco en aplicarse al cargar`,
|
||||||
detailedView_valenceBonusLabel: `Bônus de Valência`,
|
detailedView_valenceBonusLabel: `Bônus de Valência`,
|
||||||
detailedView_valenceBonusDescription: `Puedes establecer o quitar el bono de valencia de tu arma.`,
|
detailedView_valenceBonusDescription: `Puedes establecer o quitar el bono de valencia de tu arma.`,
|
||||||
|
detailedView_modularPartsLabel: `[UNTRANSLATED] Change Modular Parts`,
|
||||||
|
|
||||||
mods_addRiven: `Agregar Agrietado`,
|
mods_addRiven: `Agregar Agrietado`,
|
||||||
mods_fingerprint: `Huella digital`,
|
mods_fingerprint: `Huella digital`,
|
||||||
mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,
|
mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,
|
||||||
mods_rivens: `Agrietados`,
|
mods_rivens: `Agrietados`,
|
||||||
mods_mods: `Mods`,
|
mods_mods: `Mods`,
|
||||||
mods_addMax: `[UNTRANSLATED] Add Maxed`,
|
mods_addMax: `Agregar al máximo`,
|
||||||
mods_addMissingUnrankedMods: `Agregar mods sin rango faltantes`,
|
mods_addMissingUnrankedMods: `Agregar mods sin rango faltantes`,
|
||||||
mods_removeUnranked: `Quitar mods sin rango`,
|
mods_removeUnranked: `Quitar mods sin rango`,
|
||||||
mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
|
mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
|
||||||
@ -185,13 +186,13 @@ dict = {
|
|||||||
cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`,
|
cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`,
|
||||||
cheats_fastClanAscension: `Ascenso rápido del clan`,
|
cheats_fastClanAscension: `Ascenso rápido del clan`,
|
||||||
cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`,
|
cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`,
|
||||||
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `[UNTRANSLATED] Exceptional Relics Always Give Bronze Reward`,
|
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `Las reliquias excepcionales siempre otorgan recompensa de bronce`,
|
||||||
cheats_flawlessRelicsAlwaysGiveSilverReward: `[UNTRANSLATED] Flawless Relics Always Give Silver Reward`,
|
cheats_flawlessRelicsAlwaysGiveSilverReward: `Las reliquias impecables siempre otorgan recompensa de plata`,
|
||||||
cheats_radiantRelicsAlwaysGiveGoldReward: `[UNTRANSLATED] Radiant Relics Always Give Gold Reward`,
|
cheats_radiantRelicsAlwaysGiveGoldReward: `Las reliquias radiantes siempre otorgan recompensa de oro`,
|
||||||
cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`,
|
cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`,
|
||||||
cheats_disableDailyTribute: `Desactivar tributo diario`,
|
cheats_disableDailyTribute: `Desactivar tributo diario`,
|
||||||
cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
|
cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
|
||||||
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
|
cheats_relicRewardItemCountMultiplier: `Multiplicador de cantidad de recompensas de reliquia`,
|
||||||
cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
|
cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
|
||||||
cheats_save: `Guardar`,
|
cheats_save: `Guardar`,
|
||||||
cheats_account: `Cuenta`,
|
cheats_account: `Cuenta`,
|
||||||
@ -202,7 +203,7 @@ dict = {
|
|||||||
cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
|
cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
|
||||||
cheats_changeButton: `Cambiar`,
|
cheats_changeButton: `Cambiar`,
|
||||||
cheats_none: `Ninguno`,
|
cheats_none: `Ninguno`,
|
||||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
|
||||||
|
|
||||||
worldState: `Estado del mundo`,
|
worldState: `Estado del mundo`,
|
||||||
worldState_creditBoost: `Potenciador de Créditos`,
|
worldState_creditBoost: `Potenciador de Créditos`,
|
||||||
@ -249,8 +250,8 @@ dict = {
|
|||||||
worldState_allAtOnceSteelPath: `Todo a la vez, Camino de Acero`,
|
worldState_allAtOnceSteelPath: `Todo a la vez, Camino de Acero`,
|
||||||
worldState_theCircuitOverride: `Cambio del Circuito`,
|
worldState_theCircuitOverride: `Cambio del Circuito`,
|
||||||
worldState_darvoStockMultiplier: `Multiplicador de stock de Darvo`,
|
worldState_darvoStockMultiplier: `Multiplicador de stock de Darvo`,
|
||||||
worldState_varziaFullyStocked: `[UNTRANSLATED] Varzia Fully Stocked`,
|
worldState_varziaFullyStocked: `Varzia con stock completo`,
|
||||||
worldState_varziaOverride: `[UNTRANSLATED] Varzia Rotation Override`,
|
worldState_varziaOverride: `Cambio en rotación de Varzia`,
|
||||||
|
|
||||||
import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`,
|
import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`,
|
||||||
import_submit: `Enviar`,
|
import_submit: `Enviar`,
|
||||||
@ -303,10 +304,10 @@ dict = {
|
|||||||
upgrade_OnExecutionTerrify: `50% de probabilidad de que enemigos en un radio de 15m entren en pánico por 8s tras una ejecución`,
|
upgrade_OnExecutionTerrify: `50% de probabilidad de que enemigos en un radio de 15m entren en pánico por 8s tras una ejecución`,
|
||||||
upgrade_OnHackLockers: `Desbloquea 5 casilleros en un radio de 20m tras hackear`,
|
upgrade_OnHackLockers: `Desbloquea 5 casilleros en un radio de 20m tras hackear`,
|
||||||
upgrade_OnExecutionBlind: `Ciega a los enemigos en un radio de 18m tras una ejecución`,
|
upgrade_OnExecutionBlind: `Ciega a los enemigos en un radio de 18m tras una ejecución`,
|
||||||
upgrade_OnExecutionDrainPower: `[UNTRANSLATED] Next ability cast gains +50% Ability Strength on Mercy`,
|
upgrade_OnExecutionDrainPower: `La próxima habilidad usada gana +50% de fuerza al realizar un remate (Mercy)`,
|
||||||
upgrade_OnHackSprintSpeed: `+75% de velocidad de carrera durante 15s después de hackear`,
|
upgrade_OnHackSprintSpeed: `+75% de velocidad de carrera durante 15s después de hackear`,
|
||||||
upgrade_SwiftExecute: `[UNTRANSLATED] +50% Mercy Kill Speed`,
|
upgrade_SwiftExecute: `+50% de velocidad al ejecutar remates (Mercy)`,
|
||||||
upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after Hacking`,
|
upgrade_OnHackInvis: `Invisible durante 15 segundos después de hackear`,
|
||||||
|
|
||||||
damageType_Electricity: `Eletricidade`,
|
damageType_Electricity: `Eletricidade`,
|
||||||
damageType_Fire: `Ígneo`,
|
damageType_Fire: `Ígneo`,
|
||||||
|
@ -123,6 +123,7 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`,
|
detailedView_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`,
|
||||||
detailedView_valenceBonusLabel: `Bonus de Valence`,
|
detailedView_valenceBonusLabel: `Bonus de Valence`,
|
||||||
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
||||||
|
detailedView_modularPartsLabel: `[UNTRANSLATED] Change Modular Parts`,
|
||||||
|
|
||||||
mods_addRiven: `Ajouter un riven`,
|
mods_addRiven: `Ajouter un riven`,
|
||||||
mods_fingerprint: `Empreinte`,
|
mods_fingerprint: `Empreinte`,
|
||||||
|
@ -123,6 +123,7 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`,
|
detailedView_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`,
|
||||||
detailedView_valenceBonusLabel: `Бонус Валентности`,
|
detailedView_valenceBonusLabel: `Бонус Валентности`,
|
||||||
detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус валентности с вашего оружия.`,
|
detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус валентности с вашего оружия.`,
|
||||||
|
detailedView_modularPartsLabel: `Изменить Модульные Части`,
|
||||||
|
|
||||||
mods_addRiven: `Добавить Мод Разлома`,
|
mods_addRiven: `Добавить Мод Разлома`,
|
||||||
mods_fingerprint: `Отпечаток`,
|
mods_fingerprint: `Отпечаток`,
|
||||||
|
@ -4,7 +4,7 @@ dict = {
|
|||||||
general_addButton: `添加`,
|
general_addButton: `添加`,
|
||||||
general_setButton: `设置`,
|
general_setButton: `设置`,
|
||||||
general_bulkActions: `批量操作`,
|
general_bulkActions: `批量操作`,
|
||||||
general_loading: `[UNTRANSLATED] Loading...`,
|
general_loading: `加载中...`,
|
||||||
|
|
||||||
code_loginFail: `登录失败.请检查邮箱和密码.`,
|
code_loginFail: `登录失败.请检查邮箱和密码.`,
|
||||||
code_regFail: `注册失败.账号已存在.`,
|
code_regFail: `注册失败.账号已存在.`,
|
||||||
@ -45,8 +45,8 @@ dict = {
|
|||||||
code_focusUnlocked: `已解锁|COUNT|个新专精学派!需要游戏内仓库更新才能生效,您可以通过访问星图来触发仓库更新.`,
|
code_focusUnlocked: `已解锁|COUNT|个新专精学派!需要游戏内仓库更新才能生效,您可以通过访问星图来触发仓库更新.`,
|
||||||
code_addModsConfirm: `确定要向账户添加|COUNT|张MOD吗?`,
|
code_addModsConfirm: `确定要向账户添加|COUNT|张MOD吗?`,
|
||||||
code_succImport: `导入成功。`,
|
code_succImport: `导入成功。`,
|
||||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
code_succRelog: `完成. 需要重新登录游戏才能看到变化.`,
|
||||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
code_nothingToDo: `完成. 没有可执行的操作.`,
|
||||||
code_gild: `镀金`,
|
code_gild: `镀金`,
|
||||||
code_moa: `恐鸟`,
|
code_moa: `恐鸟`,
|
||||||
code_zanuka: `猎犬`,
|
code_zanuka: `猎犬`,
|
||||||
@ -123,13 +123,14 @@ dict = {
|
|||||||
detailedView_archonShardsDescription2: `请注意,在加载时,每个执政官源力石都需要一定的时间来生效。`,
|
detailedView_archonShardsDescription2: `请注意,在加载时,每个执政官源力石都需要一定的时间来生效。`,
|
||||||
detailedView_valenceBonusLabel: `效价加成`,
|
detailedView_valenceBonusLabel: `效价加成`,
|
||||||
detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
|
detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
|
||||||
|
detailedView_modularPartsLabel: `[UNTRANSLATED] Change Modular Parts`,
|
||||||
|
|
||||||
mods_addRiven: `添加裂罅MOD`,
|
mods_addRiven: `添加裂罅MOD`,
|
||||||
mods_fingerprint: `印记`,
|
mods_fingerprint: `印记`,
|
||||||
mods_fingerprintHelp: `需要印记相关的帮助?`,
|
mods_fingerprintHelp: `需要印记相关的帮助?`,
|
||||||
mods_rivens: `裂罅MOD`,
|
mods_rivens: `裂罅MOD`,
|
||||||
mods_mods: `Mods`,
|
mods_mods: `Mods`,
|
||||||
mods_addMax: `添加(满级)`,
|
mods_addMax: `满级添加`,
|
||||||
mods_addMissingUnrankedMods: `添加所有缺失的Mods`,
|
mods_addMissingUnrankedMods: `添加所有缺失的Mods`,
|
||||||
mods_removeUnranked: `删除所有未升级的Mods`,
|
mods_removeUnranked: `删除所有未升级的Mods`,
|
||||||
mods_addMissingMaxRankMods: `添加所有缺失的满级Mods`,
|
mods_addMissingMaxRankMods: `添加所有缺失的满级Mods`,
|
||||||
@ -202,7 +203,7 @@ dict = {
|
|||||||
cheats_changeSupportedSyndicate: `支持的集团`,
|
cheats_changeSupportedSyndicate: `支持的集团`,
|
||||||
cheats_changeButton: `更改`,
|
cheats_changeButton: `更改`,
|
||||||
cheats_none: `无`,
|
cheats_none: `无`,
|
||||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
cheats_markAllAsRead: `收件箱全部标记为已读`,
|
||||||
|
|
||||||
worldState: `世界状态配置`,
|
worldState: `世界状态配置`,
|
||||||
worldState_creditBoost: `现金加成`,
|
worldState_creditBoost: `现金加成`,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user