merge upstream
This commit is contained in:
		
						commit
						90c1fdeace
					
				
							
								
								
									
										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);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										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();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -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" },
 | 
				
			||||||
 | 
				
			|||||||
@ -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`,
 | 
				
			||||||
@ -129,7 +129,7 @@ dict = {
 | 
				
			|||||||
    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 +185,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 +202,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 +249,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 +303,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`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user