Compare commits

..

1 Commits

Author SHA1 Message Date
019ff331b2 chore: allow MT_CAPTURE for sorties 2025-04-22 21:52:17 +02:00
84 changed files with 1543 additions and 3489 deletions

View File

@ -1,6 +1,7 @@
@echo off @echo off
echo Updating SpaceNinjaServer... echo Updating SpaceNinjaServer...
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
git fetch --prune git fetch --prune
git stash git stash
git reset --hard origin/main git reset --hard origin/main

View File

@ -19,7 +19,6 @@
"infiniteEndo": false, "infiniteEndo": false,
"infiniteRegalAya": false, "infiniteRegalAya": false,
"infiniteHelminthMaterials": false, "infiniteHelminthMaterials": false,
"dontSubtractConsumables": false,
"unlockAllShipFeatures": false, "unlockAllShipFeatures": false,
"unlockAllShipDecorations": false, "unlockAllShipDecorations": false,
"unlockAllFlavourItems": false, "unlockAllFlavourItems": false,
@ -38,7 +37,6 @@
"noKimCooldowns": false, "noKimCooldowns": false,
"instantResourceExtractorDrones": false, "instantResourceExtractorDrones": false,
"noResourceExtractorDronesDamage": false, "noResourceExtractorDronesDamage": false,
"skipClanKeyCrafting": false,
"noDojoRoomBuildStage": false, "noDojoRoomBuildStage": false,
"noDecoBuildStage": false, "noDecoBuildStage": false,
"fastDojoRoomDestruction": false, "fastDojoRoomDestruction": false,

8
package-lock.json generated
View File

@ -18,7 +18,7 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"warframe-public-export-plus": "^0.5.59", "warframe-public-export-plus": "^0.5.56",
"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"
@ -3789,9 +3789,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.59", "version": "0.5.56",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.59.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.56.tgz",
"integrity": "sha512-/SUCVjngVDBz6gahz7CdVLywtHLODL6O5nmNtQcxFDUwrUGnF1lETcG8/UO+WLeGxBVAy4BDPbq+9ZWlYZM4uQ==" "integrity": "sha512-px+J7tUm6fkSzwKkvL73ySQReDq9oM1UrHSLM3vbYGBvELM892iBgPYG45okIhScCSdwmmXTiWZTf4x/I4qiNQ=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -25,7 +25,7 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"warframe-public-export-plus": "^0.5.59", "warframe-public-export-plus": "^0.5.56",
"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"

View File

@ -2,18 +2,15 @@ const millisecondsPerSecond = 1000;
const secondsPerMinute = 60; const secondsPerMinute = 60;
const minutesPerHour = 60; const minutesPerHour = 60;
const hoursPerDay = 24; const hoursPerDay = 24;
const daysPerWeek = 7;
const unixSecond = millisecondsPerSecond; const unixSecond = millisecondsPerSecond;
const unixMinute = secondsPerMinute * millisecondsPerSecond; const unixMinute = secondsPerMinute * millisecondsPerSecond;
const unixHour = unixMinute * minutesPerHour; const unixHour = unixMinute * minutesPerHour;
const unixDay = hoursPerDay * unixHour; const unixDay = hoursPerDay * unixHour;
const unixWeek = daysPerWeek * unixDay;
export const unixTimesInMs = { export const unixTimesInMs = {
second: unixSecond, second: unixSecond,
minute: unixMinute, minute: unixMinute,
hour: unixHour, hour: unixHour,
day: unixDay, day: unixDay
week: unixWeek
}; };

View File

@ -17,7 +17,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
ItemCount: -1 ItemCount: -1
} }
]); ]);
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType])!; const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]);
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]); const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
const upgradeIndex = const upgradeIndex =
inventory.Upgrades.push({ inventory.Upgrades.push({

View File

@ -28,7 +28,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
}); });
const rawRivenType = getRandomRawRivenType(); const rawRivenType = getRandomRawRivenType();
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType])!; const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType]);
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]); const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
const upgradeIndex = const upgradeIndex =

View File

@ -133,14 +133,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
if (recipe.secretIngredientAction != "SIA_UNBRAND") { if (recipe.secretIngredientAction != "SIA_UNBRAND") {
InventoryChanges = { InventoryChanges = {
...InventoryChanges, ...InventoryChanges,
...(await addItem( ...(await addItem(inventory, recipe.resultType, recipe.num, false))
inventory,
recipe.resultType,
recipe.num,
false,
undefined,
pendingRecipe.TargetFingerprint
))
}; };
} }
await inventory.save(); await inventory.save();

View File

@ -1,4 +1,4 @@
import { addFusionPoints, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
@ -17,7 +17,7 @@ export const claimLibraryDailyTaskRewardController: RequestHandler = async (req,
} }
syndicate.Standing += rewardStanding; syndicate.Standing += rewardStanding;
addFusionPoints(inventory, 80 * rewardQuantity); inventory.FusionPoints += 80 * rewardQuantity;
await inventory.save(); await inventory.save();
res.json({ res.json({

View File

@ -1,41 +0,0 @@
import { getCalendarProgress, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { getWorldState } from "@/src/services/worldStateService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER
export const completeCalendarEventController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const calendarProgress = getCalendarProgress(inventory);
const currentSeason = getWorldState().KnownCalendarSeasons[0];
let inventoryChanges: IInventoryChanges = {};
let dayIndex = 0;
for (const day of currentSeason.Days) {
if (day.events.length == 0 || day.events[0].type != "CET_CHALLENGE") {
if (dayIndex == calendarProgress.SeasonProgress.LastCompletedDayIdx) {
if (day.events.length != 0) {
const selection = day.events[parseInt(req.query.CompletedEventIdx as string)];
if (selection.type == "CET_REWARD") {
inventoryChanges = (await handleStoreItemAcquisition(selection.reward!, inventory))
.InventoryChanges;
} else if (selection.type == "CET_UPGRADE") {
calendarProgress.YearProgress.Upgrades.push(selection.upgrade!);
} else if (selection.type != "CET_PLOT") {
throw new Error(`unexpected selection type: ${selection.type}`);
}
}
break;
}
++dayIndex;
}
}
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
await inventory.save();
res.json({
InventoryChanges: inventoryChanges,
CalendarProgress: inventory.CalendarProgress
});
};

View File

@ -1,14 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild, GuildMember } from "@/src/models/guildModel"; import { Guild, GuildMember } from "@/src/models/guildModel";
import { Account } from "@/src/models/loginModel"; import { Account } from "@/src/models/loginModel";
import { import { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
deleteGuild, import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
getGuildClient,
giveClanKey,
hasGuildPermission,
removeDojoKeyItems
} from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService"; import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes"; import { GuildPermission } from "@/src/types/guildTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
@ -47,7 +41,14 @@ export const confirmGuildInvitationGetController: RequestHandler = async (req, r
// Update inventory of new member // Update inventory of new member
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes"); const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
inventory.GuildId = new Types.ObjectId(req.query.clanId as string); inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
giveClanKey(inventory, inventoryChanges); const recipeChanges = [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
];
addRecipes(inventory, recipeChanges);
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
await inventory.save(); await inventory.save();
const guild = (await Guild.findById(req.query.clanId as string))!; const guild = (await Guild.findById(req.query.clanId as string))!;
@ -95,9 +96,14 @@ export const confirmGuildInvitationPostController: RequestHandler = async (req,
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 }); await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
// Update inventory of new member // Update inventory of new member
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes"); const inventory = await getInventory(guildMember.accountId.toString(), "GuildId Recipes");
inventory.GuildId = new Types.ObjectId(req.query.clanId as string); inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
giveClanKey(inventory); addRecipes(inventory, [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
]);
await inventory.save(); await inventory.save();
// Add join to clan log // Add join to clan log

View File

@ -2,7 +2,7 @@ import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild } from "@/src/models/guildModel"; import { Guild } from "@/src/models/guildModel";
import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService"; import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService";
import { addFusionPoints, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { Types } from "mongoose"; import { Types } from "mongoose";
@ -36,7 +36,7 @@ export const contributeGuildClassController: RequestHandler = async (req, res) =
// Either way, endo is given to the contributor. // Either way, endo is given to the contributor.
const inventory = await getInventory(accountId, "FusionPoints"); const inventory = await getInventory(accountId, "FusionPoints");
addFusionPoints(inventory, guild.CeremonyEndo!); inventory.FusionPoints += guild.CeremonyEndo!;
await inventory.save(); await inventory.save();
res.json({ res.json({

View File

@ -2,9 +2,8 @@ import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Guild, GuildMember } from "@/src/models/guildModel"; import { Guild, GuildMember } from "@/src/models/guildModel";
import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService"; import { createUniqueClanName, getGuildClient } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService"; import { addRecipes, getInventory } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
export const createGuildController: RequestHandler = async (req, res) => { export const createGuildController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -27,15 +26,26 @@ export const createGuildController: RequestHandler = async (req, res) => {
rank: 0 rank: 0
}); });
const inventory = await getInventory(accountId, "GuildId LevelKeys Recipes"); const inventory = await getInventory(accountId, "GuildId Recipes");
inventory.GuildId = guild._id; inventory.GuildId = guild._id;
const inventoryChanges: IInventoryChanges = {}; addRecipes(inventory, [
giveClanKey(inventory, inventoryChanges); {
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
]);
await inventory.save(); await inventory.save();
res.json({ res.json({
...(await getGuildClient(guild, accountId)), ...(await getGuildClient(guild, accountId)),
InventoryChanges: inventoryChanges InventoryChanges: {
Recipes: [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
]
}
}); });
}; };

View File

@ -62,7 +62,14 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
} satisfies IInnateDamageFingerprint) } satisfies IInnateDamageFingerprint)
}; };
} }
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, defaultOverwrites, inventoryChanges); addEquipment(
inventory,
"CrewShipSalvagedWeapons",
payload.ItemType,
undefined,
inventoryChanges,
defaultOverwrites
);
} }
inventoryChanges.CrewShipRawSalvage = [ inventoryChanges.CrewShipRawSalvage = [

View File

@ -21,12 +21,10 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
inventory.EntratiVaultCountResetDate = new Date(weekEnd); inventory.EntratiVaultCountResetDate = new Date(weekEnd);
if (inventory.EntratiLabConquestUnlocked) { if (inventory.EntratiLabConquestUnlocked) {
inventory.EntratiLabConquestUnlocked = 0; inventory.EntratiLabConquestUnlocked = 0;
inventory.EntratiLabConquestCacheScoreMission = 0;
inventory.EntratiLabConquestActiveFrameVariants = []; inventory.EntratiLabConquestActiveFrameVariants = [];
} }
if (inventory.EchoesHexConquestUnlocked) { if (inventory.EchoesHexConquestUnlocked) {
inventory.EchoesHexConquestUnlocked = 0; inventory.EchoesHexConquestUnlocked = 0;
inventory.EchoesHexConquestCacheScoreMission = 0;
inventory.EchoesHexConquestActiveFrameVariants = []; inventory.EchoesHexConquestActiveFrameVariants = [];
inventory.EchoesHexConquestActiveStickers = []; inventory.EchoesHexConquestActiveStickers = [];
} }

View File

@ -104,14 +104,13 @@ export const focusController: RequestHandler = async (req, res) => {
} }
case FocusOperation.SentTrainingAmplifier: { case FocusOperation.SentTrainingAmplifier: {
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest; const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
const inventory = await getInventory(accountId); const parts: string[] = [
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
ModularParts: [
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip", "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis", "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel" "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
] ];
}); const inventory = await getInventory(accountId);
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
occupySlot(inventory, InventorySlot.AMPS, false); occupySlot(inventory, InventorySlot.AMPS, false);
await inventory.save(); await inventory.save();
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]); res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);

View File

@ -1,84 +0,0 @@
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
import { toStoreItem } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
import { IMongoDate } from "@/src/types/commonTypes";
import { IMissionReward } from "@/src/types/missionTypes";
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IGardeningClient } from "@/src/types/shipTypes";
import { RequestHandler } from "express";
import { dict_en, ExportResources } from "warframe-public-export-plus";
export const gardeningController: RequestHandler = async (req, res) => {
const data = getJSONfromString<IGardeningRequest>(String(req.body));
if (data.Mode != "HarvestAll") {
throw new Error(`unexpected gardening mode: ${data.Mode}`);
}
const accountId = await getAccountIdForRequest(req);
const [inventory, personalRooms] = await Promise.all([
getInventory(accountId, "MiscItems"),
getPersonalRooms(accountId, "Apartment")
]);
// Harvest plants
const inventoryChanges: IInventoryChanges = {};
const rewards: Record<string, IMissionReward[][]> = {};
for (const planter of personalRooms.Apartment.Gardening.Planters) {
rewards[planter.Name] = [];
for (const plant of planter.Plants) {
const itemType =
"/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" +
plant.PlantType.substring(plant.PlantType.length - 1);
const itemCount = Math.random() < 0.775 ? 2 : 4;
addMiscItem(inventory, itemType, itemCount, inventoryChanges);
rewards[planter.Name].push([
{
StoreItem: toStoreItem(itemType),
TypeName: itemType,
ItemCount: itemCount,
DailyCooldown: false,
Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564,
TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`,
ProductCategory: "MiscItems"
}
]);
}
}
// Refresh garden
personalRooms.Apartment.Gardening = createGarden();
await Promise.all([inventory.save(), personalRooms.save()]);
const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1];
const plant = planter.Plants[planter.Plants.length - 1];
res.json({
GardenTagName: planter.Name,
PlantType: plant.PlantType,
PlotIndex: plant.PlotIndex,
EndTime: toMongoDate(plant.EndTime),
InventoryChanges: inventoryChanges,
Gardening: personalRooms.toJSON<IPersonalRoomsClient>().Apartment.Gardening,
Rewards: rewards
} satisfies IGardeningResponse);
};
interface IGardeningRequest {
Mode: string;
}
interface IGardeningResponse {
GardenTagName: string;
PlantType: string;
PlotIndex: number;
EndTime: IMongoDate;
InventoryChanges: IInventoryChanges;
Gardening: IGardeningClient;
Rewards: Record<string, IMissionReward[][]>;
}

View File

@ -2,24 +2,17 @@ import { RequestHandler } from "express";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json"; import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService"; import { getPersonalRooms } from "@/src/services/personalRoomsService";
import { getShip } from "@/src/services/shipService"; import { getShip } from "@/src/services/shipService";
import { toOid } from "@/src/helpers/inventoryHelpers"; import { toOid } from "@/src/helpers/inventoryHelpers";
import { IGetShipResponse } from "@/src/types/shipTypes"; import { IGetShipResponse } from "@/src/types/shipTypes";
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes"; import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
import { getLoadout } from "@/src/services/loadoutService"; import { getLoadout } from "@/src/services/loadoutService";
export const getShipController: RequestHandler = async (req, res) => { export const getShipController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const personalRoomsDb = await getPersonalRooms(accountId); const personalRoomsDb = await getPersonalRooms(accountId);
const personalRooms = personalRoomsDb.toJSON<IPersonalRooms>();
// Setup gardening if it's missing. Maybe should be done as part of some quest completion in the future.
if (personalRoomsDb.Apartment.Gardening.Planters.length == 0) {
personalRoomsDb.Apartment.Gardening = createGarden();
await personalRoomsDb.save();
}
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
const loadout = await getLoadout(accountId); const loadout = await getLoadout(accountId);
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem"); const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");

View File

@ -441,9 +441,16 @@ const finishComponentRepair = (
const inventoryChanges = { const inventoryChanges = {
...(category == "CrewShipWeaponSkins" ...(category == "CrewShipWeaponSkins"
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint) ? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
: addEquipment(inventory, category, salvageItem.ItemType, { : addEquipment(
inventory,
category,
salvageItem.ItemType,
undefined,
{},
{
UpgradeFingerprint: salvageItem.UpgradeFingerprint UpgradeFingerprint: salvageItem.UpgradeFingerprint
})), }
)),
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false) ...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
}; };

View File

@ -13,7 +13,6 @@ import { addItems, combineInventoryChanges, getInventory } from "@/src/services/
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { ExportFlavour, ExportGear } from "warframe-public-export-plus"; import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService"; import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
export const inboxController: RequestHandler = async (req, res) => { export const inboxController: RequestHandler = async (req, res) => {
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query; const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
@ -49,7 +48,7 @@ export const inboxController: RequestHandler = async (req, res) => {
await addItems( await addItems(
inventory, inventory,
attachmentItems.map(attItem => ({ attachmentItems.map(attItem => ({
ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem, ItemType: attItem,
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1 ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
})), })),
inventoryChanges inventoryChanges

View File

@ -18,12 +18,10 @@ import {
addMiscItems, addMiscItems,
allDailyAffiliationKeys, allDailyAffiliationKeys,
cleanupInventory, cleanupInventory,
createLibraryDailyTask, createLibraryDailyTask
generateRewardSeed
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { catBreadHash } from "@/src/helpers/stringHelpers"; import { catBreadHash } from "@/src/helpers/stringHelpers";
import { Types } from "mongoose";
export const inventoryController: RequestHandler = async (request, response) => { export const inventoryController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
@ -89,7 +87,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
cleanupInventory(inventory); cleanupInventory(inventory);
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000); inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
//await inventory.save(); await inventory.save();
} }
if ( if (
@ -98,19 +96,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
) { ) {
handleSubsumeCompletion(inventory); handleSubsumeCompletion(inventory);
//await inventory.save();
}
if (inventory.LastInventorySync) {
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
if (lastSyncDuviriMood != currentDuviriMood) {
logger.debug(`refreshing duviri seed`);
inventory.DuviriInfo.Seed = generateRewardSeed();
}
}
inventory.LastInventorySync = new Types.ObjectId();
await inventory.save(); await inventory.save();
}
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query)); response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
}; };
@ -287,7 +274,7 @@ export const getInventoryResponse = async (
} }
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage. // Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
inventoryResponse.LastInventorySync = undefined; //inventoryResponse.LastInventorySync = toOid(new Types.ObjectId());
// Set 2FA enabled so trading post can be used // Set 2FA enabled so trading post can be used
inventoryResponse.HWIDProtectEnabled = true; inventoryResponse.HWIDProtectEnabled = true;

View File

@ -21,11 +21,7 @@ export const loginController: RequestHandler = async (request, response) => {
const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress; const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress;
if ( if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
!account &&
((config.autoCreateAccount && loginRequest.ClientType != "webui") ||
loginRequest.ClientType == "webui-register")
) {
try { try {
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@")); const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja"; let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
@ -41,7 +37,7 @@ export const loginController: RequestHandler = async (request, response) => {
password: loginRequest.password, password: loginRequest.password,
DisplayName: name, DisplayName: name,
CountryCode: loginRequest.lang.toUpperCase(), CountryCode: loginRequest.lang.toUpperCase(),
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType, ClientType: loginRequest.ClientType,
CrossPlatformAllowed: true, CrossPlatformAllowed: true,
ForceLogoutVersion: 0, ForceLogoutVersion: 0,
ConsentNeeded: false, ConsentNeeded: false,
@ -63,11 +59,6 @@ export const loginController: RequestHandler = async (request, response) => {
return; return;
} }
if (loginRequest.ClientType == "webui-register") {
response.status(400).json({ error: "account already exists" });
return;
}
if (!isCorrectPassword(loginRequest.password, account.password)) { if (!isCorrectPassword(loginRequest.password, account.password)) {
response.status(400).json({ error: "incorrect login data" }); response.status(400).json({ error: "incorrect login data" });
return; return;

View File

@ -3,10 +3,9 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes"; import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService"; import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
import { generateRewardSeed, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { getInventoryResponse } from "./inventoryController"; import { getInventoryResponse } from "./inventoryController";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
/* /*
**** INPUT **** **** INPUT ****
@ -63,7 +62,6 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
missionReport.MissionStatus !== "GS_SUCCESS" && missionReport.MissionStatus !== "GS_SUCCESS" &&
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId) !(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
) { ) {
inventory.RewardSeed = generateRewardSeed();
await inventory.save(); await inventory.save();
const inventoryResponse = await getInventoryResponse(inventory, true); const inventoryResponse = await getInventoryResponse(inventory, true);
res.json({ res.json({
@ -73,16 +71,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
return; return;
} }
const { const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } =
MissionRewards, await addMissionRewards(inventory, missionReport, firstCompletion);
inventoryChanges,
credits,
AffiliationMods,
SyndicateXPItemReward,
ConquestCompletedMissionsCount
} = await addMissionRewards(inventory, missionReport, firstCompletion);
inventory.RewardSeed = generateRewardSeed();
await inventory.save(); await inventory.save();
const inventoryResponse = await getInventoryResponse(inventory, true); const inventoryResponse = await getInventoryResponse(inventory, true);
@ -95,9 +86,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
...inventoryUpdates, ...inventoryUpdates,
//FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed. //FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
SyndicateXPItemReward, SyndicateXPItemReward,
AffiliationMods, AffiliationMods
ConquestCompletedMissionsCount });
} satisfies IMissionInventoryUpdateResponse);
}; };
/* /*

View File

@ -17,7 +17,7 @@ import { getDefaultUpgrades } from "@/src/services/itemDataService";
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper"; import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { getRandomInt } from "@/src/services/rngService"; import { getRandomInt } from "@/src/services/rngService";
import { ExportSentinels, ExportWeapons, IDefaultUpgrade } from "warframe-public-export-plus"; import { ExportSentinels, IDefaultUpgrade } from "warframe-public-export-plus";
import { Status } from "@/src/types/inventoryTypes/inventoryTypes"; import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
interface IModularCraftRequest { interface IModularCraftRequest {
@ -36,9 +36,7 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
let defaultUpgrades: IDefaultUpgrade[] | undefined; let defaultUpgrades: IDefaultUpgrade[] | undefined;
const defaultOverwrites: Partial<IEquipmentDatabase> = { const defaultOverwrites: Partial<IEquipmentDatabase> = {};
ModularParts: data.Parts
};
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};
if (category == "KubrowPets") { if (category == "KubrowPets") {
const traits = { const traits = {
@ -140,20 +138,8 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
} else { } else {
defaultUpgrades = getDefaultUpgrades(data.Parts); defaultUpgrades = getDefaultUpgrades(data.Parts);
} }
if (category == "MoaPets") {
const weapon = ExportSentinels[data.WeaponType].defaultWeapon;
if (weapon) {
const category = ExportWeapons[weapon].productCategory;
addEquipment(inventory, category, weapon, undefined, inventoryChanges);
combineInventoryChanges(
inventoryChanges,
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
);
}
}
defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades); defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades);
addEquipment(inventory, category, data.WeaponType, defaultOverwrites, inventoryChanges); addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
combineInventoryChanges( combineInventoryChanges(
inventoryChanges, inventoryChanges,
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi) occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)

View File

@ -21,11 +21,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
export const modularWeaponSaleController: RequestHandler = async (req, res) => { export const modularWeaponSaleController: RequestHandler = async (req, res) => {
const partTypeToParts: Record<string, string[]> = {}; const partTypeToParts: Record<string, string[]> = {};
for (const [uniqueName, data] of Object.entries(ExportWeapons)) { for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
if ( if (data.partType && data.premiumPrice) {
data.partType &&
data.premiumPrice &&
!data.excludeFromCodex // exclude pvp variants
) {
partTypeToParts[data.partType] ??= []; partTypeToParts[data.partType] ??= [];
partTypeToParts[data.partType].push(uniqueName); partTypeToParts[data.partType].push(uniqueName);
} }
@ -45,18 +41,24 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts); const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
const configs = applyDefaultUpgrades(inventory, defaultUpgrades); const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
const inventoryChanges: IInventoryChanges = { const inventoryChanges: IInventoryChanges = {
...addEquipment(inventory, category, weaponInfo.ItemType, { ...addEquipment(
inventory,
category,
weaponInfo.ItemType,
weaponInfo.ModularParts,
{},
{
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED, Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
ItemName: payload.ItemName, ItemName: payload.ItemName,
Configs: configs, Configs: configs,
ModularParts: weaponInfo.ModularParts,
Polarity: [ Polarity: [
{ {
Slot: payload.PolarizeSlot, Slot: payload.PolarizeSlot,
Value: payload.PolarizeValue Value: payload.PolarizeValue
} }
] ]
}), }
),
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true), ...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
...updateCurrency(inventory, weaponInfo.PremiumPrice, true) ...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
}; };
@ -141,7 +143,7 @@ const getModularWeaponSale = (
getItemType: (parts: string[]) => string getItemType: (parts: string[]) => string
): IModularWeaponSaleInfo => { ): IModularWeaponSaleInfo => {
const rng = new CRng(day); const rng = new CRng(day);
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType])!); const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType]));
let partsCost = 0; let partsCost = 0;
for (const part of parts) { for (const part of parts) {
partsCost += ExportWeapons[part].premiumPrice!; partsCost += ExportWeapons[part].premiumPrice!;

View File

@ -2,12 +2,8 @@ import {
consumeModCharge, consumeModCharge,
encodeNemesisGuess, encodeNemesisGuess,
getInfNodes, getInfNodes,
getKnifeUpgrade,
getNemesisPasscode, getNemesisPasscode,
getNemesisPasscodeModTypes, IKnifeResponse
getWeaponsForManifest,
IKnifeResponse,
showdownNodes
} from "@/src/helpers/nemesisHelpers"; } from "@/src/helpers/nemesisHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Loadout } from "@/src/models/inventoryModels/loadoutModel"; import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
@ -18,8 +14,6 @@ import { IMongoDate, IOid } from "@/src/types/commonTypes";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { import {
IInnateDamageFingerprint, IInnateDamageFingerprint,
IInventoryClient,
INemesisClient,
InventorySlot, InventorySlot,
IUpgradeClient, IUpgradeClient,
IWeaponSkinClient, IWeaponSkinClient,
@ -105,14 +99,13 @@ export const nemesisController: RequestHandler = async (req, res) => {
encodeNemesisGuess(guess[0], result1, guess[1], result2, guess[2], result3) encodeNemesisGuess(guess[0], result1, guess[1], result2, guess[2], result3)
); );
// Increase antivirus if correct antivirus mod is installed // Increase antivirus
const response: IKnifeResponse = {};
if (result1 == 0 || result2 == 0 || result3 == 0) {
let antivirusGain = 5; let antivirusGain = 5;
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!; const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid); const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0; const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!; const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
const response: IKnifeResponse = {};
for (const upgrade of body.knife!.AttachedUpgrades) { for (const upgrade of body.knife!.AttachedUpgrades) {
switch (upgrade.ItemType) { switch (upgrade.ItemType) {
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod": case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod":
@ -138,12 +131,18 @@ export const nemesisController: RequestHandler = async (req, res) => {
} }
} }
inventory.Nemesis!.HenchmenKilled += antivirusGain; inventory.Nemesis!.HenchmenKilled += antivirusGain;
}
if (inventory.Nemesis!.HenchmenKilled >= 100) { if (inventory.Nemesis!.HenchmenKilled >= 100) {
inventory.Nemesis!.HenchmenKilled = 100; inventory.Nemesis!.HenchmenKilled = 100;
inventory.Nemesis!.InfNodes = [
{
Node: "CrewBattleNode559",
Influence: 1
} }
];
inventory.Nemesis!.Weakened = true;
} else {
inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0); inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
}
await inventory.save(); await inventory.save();
res.json(response); res.json(response);
@ -171,7 +170,18 @@ export const nemesisController: RequestHandler = async (req, res) => {
let weaponIdx = -1; let weaponIdx = -1;
if (body.target.Faction != "FC_INFESTATION") { if (body.target.Faction != "FC_INFESTATION") {
const weapons = getWeaponsForManifest(body.target.manifest); let weapons: readonly string[];
if (body.target.manifest == "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix") {
weapons = kuvaLichVersionSixWeapons;
} else if (
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour" ||
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"
) {
weapons = corpusVersionThreeWeapons;
} else {
throw new Error(`unknown nemesis manifest: ${body.target.manifest}`);
}
const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1); const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
weaponIdx = initialWeaponIdx; weaponIdx = initialWeaponIdx;
do { do {
@ -213,38 +223,6 @@ export const nemesisController: RequestHandler = async (req, res) => {
res.json({ res.json({
target: inventory.toJSON().Nemesis target: inventory.toJSON().Nemesis
}); });
} else if ((req.query.mode as string) == "w") {
const inventory = await getInventory(
accountId,
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
);
//const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
inventory.Nemesis!.InfNodes = [
{
Node: showdownNodes[inventory.Nemesis!.Faction],
Influence: 1
}
];
inventory.Nemesis!.Weakened = true;
const response: IKnifeResponse & { target: INemesisClient } = {
target: inventory.toJSON<IInventoryClient>().Nemesis!
};
// Consume charge of the correct requiem mod(s)
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
const modTypes = getNemesisPasscodeModTypes(inventory.Nemesis!);
for (const modType of modTypes) {
const upgrade = getKnifeUpgrade(inventory, dataknifeUpgrades, modType);
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
}
await inventory.save();
res.json(response);
} else { } else {
logger.debug(`data provided to ${req.path}: ${String(req.body)}`); logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`); throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
@ -296,19 +274,48 @@ interface INemesisRequiemRequest {
guess: number; // grn/crp: 4 bits | coda: 3x 4 bits guess: number; // grn/crp: 4 bits | coda: 3x 4 bits
position: number; // grn/crp: 0-2 | coda: 0 position: number; // grn/crp: 0-2 | coda: 0
// knife field provided for coda only // knife field provided for coda only
knife?: IKnife; knife?: {
}
// interface INemesisWeakenRequest {
// target: INemesisClient;
// knife: IKnife;
// }
interface IKnife {
Item: IEquipmentClient; Item: IEquipmentClient;
Skins: IWeaponSkinClient[]; Skins: IWeaponSkinClient[];
ModSlot: number; ModSlot: number;
CustSlot: number; CustSlot: number;
AttachedUpgrades: IUpgradeClient[]; AttachedUpgrades: IUpgradeClient[];
HiddenWhenHolstered: boolean; HiddenWhenHolstered: boolean;
};
} }
const kuvaLichVersionSixWeapons = [
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
];
const corpusVersionThreeWeapons = [
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
];

View File

@ -37,7 +37,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
const deco = component.Decos.find(x => x._id.equals(request.MoveId))!; const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
deco.Pos = request.Pos; deco.Pos = request.Pos;
deco.Rot = request.Rot; deco.Rot = request.Rot;
deco.Scale = request.Scale;
} else { } else {
const deco = const deco =
component.Decos[ component.Decos[
@ -46,7 +45,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
Type: request.Type, Type: request.Type,
Pos: request.Pos, Pos: request.Pos,
Rot: request.Rot, Rot: request.Rot,
Scale: request.Scale,
Name: request.Name, Name: request.Name,
Sockets: request.Sockets Sockets: request.Sockets
}) - 1 }) - 1
@ -115,9 +113,9 @@ interface IPlaceDecoInComponentRequest {
Type: string; Type: string;
Pos: number[]; Pos: number[];
Rot: number[]; Rot: number[];
Scale?: number;
Name?: string; Name?: string;
Sockets?: number; Sockets?: number;
Scale?: number; // only provided alongside MoveId and seems to always be 1
MoveId?: string; MoveId?: string;
ShipDeco?: boolean; ShipDeco?: boolean;
VaultDeco?: boolean; VaultDeco?: boolean;

View File

@ -4,6 +4,7 @@ import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inven
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const saveDialogueController: RequestHandler = async (req, res) => { export const saveDialogueController: RequestHandler = async (req, res) => {
@ -26,6 +27,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
const dialogue = getDialogue(inventory, request.DialogueName); const dialogue = getDialogue(inventory, request.DialogueName);
dialogue.Rank = request.Rank; dialogue.Rank = request.Rank;
dialogue.Chemistry = request.Chemistry; dialogue.Chemistry = request.Chemistry;
if (request.Data) {
dialogue.QueuedDialogues = request.QueuedDialogues; dialogue.QueuedDialogues = request.QueuedDialogues;
for (const bool of request.Booleans) { for (const bool of request.Booleans) {
dialogue.Booleans.push(bool); dialogue.Booleans.push(bool);
@ -43,6 +45,8 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
dialogue.Booleans.splice(index, 1); dialogue.Booleans.splice(index, 1);
} }
} }
dialogue.Completed.push(request.Data);
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
for (const info of request.OtherDialogueInfos) { for (const info of request.OtherDialogueInfos) {
const otherDialogue = getDialogue(inventory, info.Dialogue); const otherDialogue = getDialogue(inventory, info.Dialogue);
if (info.Tag != "") { if (info.Tag != "") {
@ -50,9 +54,6 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
} }
otherDialogue.Chemistry += info.Value; // unsure otherDialogue.Chemistry += info.Value; // unsure
} }
if (request.Data) {
dialogue.Completed.push(request.Data);
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
await inventory.save(); await inventory.save();
res.json({ res.json({
InventoryChanges: inventoryChanges, InventoryChanges: inventoryChanges,
@ -73,7 +74,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } } AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
}); });
} else { } else {
res.end(); logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
} }
} }
}; };

View File

@ -8,8 +8,7 @@ import {
addConsumables, addConsumables,
freeUpSlot, freeUpSlot,
combineInventoryChanges, combineInventoryChanges,
addCrewShipRawSalvage, addCrewShipRawSalvage
addFusionPoints
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportDojoRecipes } from "warframe-public-export-plus"; import { ExportDojoRecipes } from "warframe-public-export-plus";
@ -45,7 +44,7 @@ export const sellController: RequestHandler = async (req, res) => {
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) { if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
requiredFields.add(InventorySlot.SPACEWEAPONS); requiredFields.add(InventorySlot.SPACEWEAPONS);
} }
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) { if (payload.Items.Sentinels || payload.Items.SentinelWeapons) {
requiredFields.add(InventorySlot.SENTINELS); requiredFields.add(InventorySlot.SENTINELS);
} }
if (payload.Items.OperatorAmps) { if (payload.Items.OperatorAmps) {
@ -70,7 +69,7 @@ export const sellController: RequestHandler = async (req, res) => {
if (payload.SellCurrency == "SC_RegularCredits") { if (payload.SellCurrency == "SC_RegularCredits") {
inventory.RegularCredits += payload.SellPrice; inventory.RegularCredits += payload.SellPrice;
} else if (payload.SellCurrency == "SC_FusionPoints") { } else if (payload.SellCurrency == "SC_FusionPoints") {
addFusionPoints(inventory, payload.SellPrice); inventory.FusionPoints += payload.SellPrice;
} else if (payload.SellCurrency == "SC_PrimeBucks") { } else if (payload.SellCurrency == "SC_PrimeBucks") {
addMiscItems(inventory, [ addMiscItems(inventory, [
{ {
@ -148,12 +147,6 @@ export const sellController: RequestHandler = async (req, res) => {
freeUpSlot(inventory, InventorySlot.SENTINELS); freeUpSlot(inventory, InventorySlot.SENTINELS);
}); });
} }
if (payload.Items.MoaPets) {
payload.Items.MoaPets.forEach(sellItem => {
inventory.MoaPets.pull({ _id: sellItem.String });
freeUpSlot(inventory, InventorySlot.SENTINELS);
});
}
if (payload.Items.OperatorAmps) { if (payload.Items.OperatorAmps) {
payload.Items.OperatorAmps.forEach(sellItem => { payload.Items.OperatorAmps.forEach(sellItem => {
inventory.OperatorAmps.pull({ _id: sellItem.String }); inventory.OperatorAmps.pull({ _id: sellItem.String });
@ -287,7 +280,6 @@ interface ISellRequest {
SpaceMelee?: ISellItem[]; SpaceMelee?: ISellItem[];
Sentinels?: ISellItem[]; Sentinels?: ISellItem[];
SentinelWeapons?: ISellItem[]; SentinelWeapons?: ISellItem[];
MoaPets?: ISellItem[];
OperatorAmps?: ISellItem[]; OperatorAmps?: ISellItem[];
Hoverboards?: ISellItem[]; Hoverboards?: ISellItem[];
Drones?: ISellItem[]; Drones?: ISellItem[];

View File

@ -20,7 +20,7 @@ export const setShipFavouriteLoadoutController: RequestHandler = async (req, res
throw new Error(`unexpected BootLocation: ${body.BootLocation}`); throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
} }
await personalRooms.save(); await personalRooms.save();
res.json(body); res.json({});
}; };
interface ISetShipFavouriteLoadoutRequest { interface ISetShipFavouriteLoadoutRequest {

View File

@ -6,9 +6,6 @@ import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { isStoreItem, toStoreItem } from "@/src/services/itemDataService"; import { isStoreItem, toStoreItem } from "@/src/services/itemDataService";
import { logger } from "@/src/utils/logger";
const nightwaveCredsItemType = ExportNightwave.rewards[ExportNightwave.rewards.length - 1].uniqueName;
export const syndicateSacrificeController: RequestHandler = async (request, response) => { export const syndicateSacrificeController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
@ -77,14 +74,10 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
if (!isStoreItem(rewardType)) { if (!isStoreItem(rewardType)) {
rewardType = toStoreItem(rewardType); rewardType = toStoreItem(rewardType);
} }
const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount)) combineInventoryChanges(
.InventoryChanges; res.InventoryChanges,
if (Object.keys(rewardInventoryChanges).length == 0) { (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount)).InventoryChanges
logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`); );
rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
addMiscItems(inventory, rewardInventoryChanges.MiscItems);
}
combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
} }
} }

View File

@ -1,16 +1,12 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { addFusionPoints, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
export const addCurrencyController: RequestHandler = async (req, res) => { export const addCurrencyController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const request = req.body as IAddCurrencyRequest; const request = req.body as IAddCurrencyRequest;
const inventory = await getInventory(accountId, request.currency);
if (request.currency == "FusionPoints") {
addFusionPoints(inventory, request.delta);
} else {
inventory[request.currency] += request.delta; inventory[request.currency] += request.delta;
}
await inventory.save(); await inventory.save();
res.end(); res.end();
}; };

View File

@ -1,44 +0,0 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus";
export const addMissingMaxRankModsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "Upgrades");
const maxOwnedRanks: Record<string, number> = {};
for (const upgrade of inventory.Upgrades) {
const fingerprint = JSON.parse(upgrade.UpgradeFingerprint ?? "{}") as { lvl?: number };
if (fingerprint.lvl) {
maxOwnedRanks[upgrade.ItemType] ??= 0;
if (fingerprint.lvl > maxOwnedRanks[upgrade.ItemType]) {
maxOwnedRanks[upgrade.ItemType] = fingerprint.lvl;
}
}
}
for (const [uniqueName, data] of Object.entries(ExportUpgrades)) {
if (data.fusionLimit != 0 && data.type != "PARAZON" && maxOwnedRanks[uniqueName] != data.fusionLimit) {
inventory.Upgrades.push({
ItemType: uniqueName,
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
});
}
}
for (const [uniqueName, data] of Object.entries(ExportArcanes)) {
if (
data.name != "/Lotus/Language/Items/GenericCosmeticEnhancerName" &&
maxOwnedRanks[uniqueName] != data.fusionLimit
) {
inventory.Upgrades.push({
ItemType: uniqueName,
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
});
}
}
await inventory.save();
res.end();
};

View File

@ -20,7 +20,6 @@ import {
TRelicQuality TRelicQuality
} from "warframe-public-export-plus"; } from "warframe-public-export-plus";
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json"; import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
import allIncarnons from "@/static/fixed_responses/allIncarnonList.json";
interface ListedItem { interface ListedItem {
uniqueName: string; uniqueName: string;
@ -33,29 +32,6 @@ interface ListedItem {
parazon?: boolean; parazon?: boolean;
} }
interface ItemLists {
archonCrystalUpgrades: Record<string, string>;
uniqueLevelCaps: Record<string, number>;
Suits: ListedItem[];
LongGuns: ListedItem[];
Melee: ListedItem[];
ModularParts: ListedItem[];
Pistols: ListedItem[];
Sentinels: ListedItem[];
SentinelWeapons: ListedItem[];
SpaceGuns: ListedItem[];
SpaceMelee: ListedItem[];
SpaceSuits: ListedItem[];
MechSuits: ListedItem[];
miscitems: ListedItem[];
Syndicates: ListedItem[];
OperatorAmps: ListedItem[];
QuestKeys: ListedItem[];
KubrowPets: ListedItem[];
EvolutionProgress: ListedItem[];
mods: ListedItem[];
}
const relicQualitySuffixes: Record<TRelicQuality, string> = { const relicQualitySuffixes: Record<TRelicQuality, string> = {
VPQ_BRONZE: "", VPQ_BRONZE: "",
VPQ_SILVER: " [Flawless]", VPQ_SILVER: " [Flawless]",
@ -65,28 +41,23 @@ const relicQualitySuffixes: Record<TRelicQuality, string> = {
const getItemListsController: RequestHandler = (req, response) => { const getItemListsController: RequestHandler = (req, response) => {
const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en"); const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
const res: ItemLists = { const res: Record<string, ListedItem[]> = {};
archonCrystalUpgrades, res.Suits = [];
uniqueLevelCaps: ExportMisc.uniqueLevelCaps, res.LongGuns = [];
Suits: [], res.Melee = [];
LongGuns: [], res.ModularParts = [];
Melee: [], res.Pistols = [];
ModularParts: [], res.Sentinels = [];
Pistols: [], res.SentinelWeapons = [];
Sentinels: [], res.SpaceGuns = [];
SentinelWeapons: [], res.SpaceMelee = [];
SpaceGuns: [], res.SpaceSuits = [];
SpaceMelee: [], res.MechSuits = [];
SpaceSuits: [], res.miscitems = [];
MechSuits: [], res.Syndicates = [];
miscitems: [], res.OperatorAmps = [];
Syndicates: [], res.QuestKeys = [];
OperatorAmps: [], res.KubrowPets = [];
QuestKeys: [],
KubrowPets: [],
EvolutionProgress: [],
mods: []
};
for (const [uniqueName, item] of Object.entries(ExportWarframes)) { for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
res[item.productCategory].push({ res[item.productCategory].push({
uniqueName, uniqueName,
@ -95,7 +66,7 @@ const getItemListsController: RequestHandler = (req, response) => {
}); });
} }
for (const [uniqueName, item] of Object.entries(ExportSentinels)) { for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") { if (item.productCategory != "SpecialItems") {
res[item.productCategory].push({ res[item.productCategory].push({
uniqueName, uniqueName,
name: getString(item.name, lang), name: getString(item.name, lang),
@ -144,7 +115,6 @@ const getItemListsController: RequestHandler = (req, response) => {
let name = getString(item.name, lang); let name = getString(item.name, lang);
if ("dissectionParts" in item) { if ("dissectionParts" in item) {
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name); name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
if (item.syndicateTag == "CetusSyndicate") {
if (uniqueName.indexOf("Large") != -1) { if (uniqueName.indexOf("Large") != -1) {
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang)); name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
} else if (uniqueName.indexOf("Medium") != -1) { } else if (uniqueName.indexOf("Medium") != -1) {
@ -152,21 +122,6 @@ const getItemListsController: RequestHandler = (req, response) => {
} else { } else {
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang)); name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
} }
} else {
if (uniqueName.indexOf("Large") != -1) {
name = name
.split("|FISH_SIZE|")
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryElderAbbrev", lang));
} else if (uniqueName.indexOf("Medium") != -1) {
name = name
.split("|FISH_SIZE|")
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryMatureAbbrev", lang));
} else {
name = name
.split("|FISH_SIZE|")
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryYoungAbbrev", lang));
}
}
} }
if ( if (
name && name &&
@ -229,6 +184,7 @@ const getItemListsController: RequestHandler = (req, response) => {
}); });
} }
res.mods = [];
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) { for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
const mod: ListedItem = { const mod: ListedItem = {
uniqueName, uniqueName,
@ -286,14 +242,12 @@ const getItemListsController: RequestHandler = (req, response) => {
}); });
} }
} }
for (const uniqueName of allIncarnons) {
res.EvolutionProgress.push({
uniqueName,
name: getString(getItemName(uniqueName) || "", lang)
});
}
response.json(res); response.json({
archonCrystalUpgrades,
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
...res
});
}; };
export { getItemListsController }; export { getItemListsController };

View File

@ -1,33 +0,0 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express";
export const setEvolutionProgressController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const payload = req.body as ISetEvolutionProgressRequest;
inventory.EvolutionProgress ??= [];
payload.forEach(element => {
const entry = inventory.EvolutionProgress!.find(entry => entry.ItemType === element.ItemType);
if (entry) {
entry.Progress = 0;
entry.Rank = element.Rank;
} else {
inventory.EvolutionProgress!.push({
Progress: 0,
Rank: element.Rank,
ItemType: element.ItemType
});
}
});
await inventory.save();
res.end();
};
type ISetEvolutionProgressRequest = {
ItemType: string;
Rank: number;
}[];

View File

@ -27,15 +27,7 @@ const viewController: RequestHandler = async (req, res) => {
for (const type of Object.keys(ExportEnemies.avatars)) { for (const type of Object.keys(ExportEnemies.avatars)) {
if (!scans.has(type)) scans.add(type); if (!scans.has(type)) scans.add(type);
} }
responseJson.Scans ??= [];
// Take any existing scans and also set them to 9999
if (responseJson.Scans) {
for (const scan of responseJson.Scans) {
scans.add(scan.type);
}
}
responseJson.Scans = [];
for (const type of scans) { for (const type of scans) {
responseJson.Scans.push({ type: type, scans: 9999 }); responseJson.Scans.push({ type: type, scans: 9999 });
} }

View File

@ -1,14 +1,11 @@
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus"; import { ExportRegions } from "warframe-public-export-plus";
import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
import { getRewardAtPercentage, SRng } from "@/src/services/rngService"; import { SRng } from "@/src/services/rngService";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { IOid } from "../types/commonTypes"; import { IOid } from "../types/commonTypes";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { addMods, generateRewardSeed } from "../services/inventoryService"; import { addMods } from "../services/inventoryService";
import { isArchwingMission } from "../services/worldStateService";
import { fromStoreItem, toStoreItem } from "../services/itemDataService";
import { createMessage } from "../services/inboxService";
export const getInfNodes = (faction: string, rank: number): IInfNode[] => { export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
const infNodes = []; const infNodes = [];
@ -25,7 +22,7 @@ export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
value.missionIndex != 42 && // not face off value.missionIndex != 42 && // not face off
value.name.indexOf("1999NodeI") == -1 && // not stage defence value.name.indexOf("1999NodeI") == -1 && // not stage defence
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
!isArchwingMission(value) value.name.indexOf("Archwing") == -1
) { ) {
//console.log(dict_en[value.name]); //console.log(dict_en[value.name]);
infNodes.push({ Node: key, Influence: 1 }); infNodes.push({ Node: key, Influence: 1 });
@ -40,59 +37,17 @@ const systemIndexes: Record<string, number[]> = {
FC_INFESTATION: [23] FC_INFESTATION: [23]
}; };
export const showdownNodes: Record<string, string> = {
FC_GRINEER: "CrewBattleNode557",
FC_CORPUS: "CrewBattleNode558",
FC_INFESTATION: "CrewBattleNode559"
};
// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis. // Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: string }): number[] => { export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: string }): number[] => {
const rng = new SRng(nemesis.fp); const rng = new SRng(nemesis.fp);
const choices = [0, 1, 2, 3, 5, 6, 7]; const passcode = [rng.randomInt(0, 7)];
let choiceIndex = rng.randomInt(0, choices.length - 1);
const passcode = [choices[choiceIndex]];
if (nemesis.Faction != "FC_INFESTATION") { if (nemesis.Faction != "FC_INFESTATION") {
choices.splice(choiceIndex, 1); passcode.push(rng.randomInt(0, 7));
choiceIndex = rng.randomInt(0, choices.length - 1); passcode.push(rng.randomInt(0, 7));
passcode.push(choices[choiceIndex]);
choices.splice(choiceIndex, 1);
choiceIndex = rng.randomInt(0, choices.length - 1);
passcode.push(choices[choiceIndex]);
} }
return passcode; return passcode;
}; };
const reqiuemMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalFourMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalFiveMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod"
];
const antivirusMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusFourMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusFiveMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusSixMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusSevenMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
];
export const getNemesisPasscodeModTypes = (nemesis: { fp: bigint; Faction: string }): string[] => {
const passcode = getNemesisPasscode(nemesis);
return nemesis.Faction == "FC_INFESTATION"
? passcode.map(i => antivirusMods[i])
: passcode.map(i => reqiuemMods[i]);
};
export const encodeNemesisGuess = ( export const encodeNemesisGuess = (
symbol1: number, symbol1: number,
result1: number, result1: number,
@ -123,31 +78,6 @@ export interface IKnifeResponse {
HasKnife?: boolean; HasKnife?: boolean;
} }
export const getKnifeUpgrade = (
inventory: TInventoryDatabaseDocument,
dataknifeUpgrades: string[],
type: string
): { ItemId: IOid; ItemType: string } => {
if (dataknifeUpgrades.indexOf(type) != -1) {
return {
ItemId: { $oid: "000000000000000000000000" },
ItemType: type
};
}
for (const upgradeId of dataknifeUpgrades) {
if (upgradeId.length == 24) {
const upgrade = inventory.Upgrades.id(upgradeId);
if (upgrade && upgrade.ItemType == type) {
return {
ItemId: { $oid: upgradeId },
ItemType: type
};
}
}
}
throw new Error(`${type} does not seem to be installed on parazon?!`);
};
export const consumeModCharge = ( export const consumeModCharge = (
response: IKnifeResponse, response: IKnifeResponse,
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
@ -198,181 +128,3 @@ export const consumeModCharge = (
response.UpgradeNew.push(true); response.UpgradeNew.push(true);
} }
}; };
const kuvaLichVersionSixWeapons = [
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
];
const corpusVersionThreeWeapons = [
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
];
export const getWeaponsForManifest = (manifest: string): readonly string[] => {
switch (manifest) {
case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix":
return kuvaLichVersionSixWeapons;
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree":
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour":
return corpusVersionThreeWeapons;
}
throw new Error(`unknown nemesis manifest: ${manifest}`);
};
export const getInnateDamageTag = (
KillingSuit: string
):
| "InnateElectricityDamage"
| "InnateFreezeDamage"
| "InnateHeatDamage"
| "InnateImpactDamage"
| "InnateMagDamage"
| "InnateRadDamage"
| "InnateToxinDamage" => {
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
};
// TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
export const getInnateDamageValue = (fp: bigint): number => {
const rng = new SRng(fp);
rng.randomFloat(); // used for the weapon index
const WeaponUpgradeValueAttenuationExponent = 2.25;
let value = Math.pow(rng.randomFloat(), WeaponUpgradeValueAttenuationExponent);
if (value >= 0.941428) {
value = 1;
}
return Math.trunc(value * 0x40000000);
};
export const getKillTokenRewardCount = (fp: bigint): number => {
const rng = new SRng(fp);
return rng.randomInt(10, 15);
};
// /Lotus/Types/Enemies/InfestedLich/InfestedLichRewardManifest
const infestedLichRotA = [
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomHuman", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomInfested", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitHuman", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitInfested", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveHuman", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveInfested", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketHuman", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketInfested", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeHuman", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeInfested", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterA", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterB", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandDespairPoster", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandGridPoster", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandHuddlePoster", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandJumpPoster", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLimoPoster", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterDay", probability: 0.046 },
{
type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterNight",
probability: 0.045
},
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandSillyPoster", probability: 0.046 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhiteBluePoster", probability: 0.045 },
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhitePinkPoster", probability: 0.045 }
];
const infestedLichRotB = [
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraA", probability: 0.072 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraB", probability: 0.071 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraC", probability: 0.072 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraD", probability: 0.071 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraE", probability: 0.072 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraF", probability: 0.071 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraG", probability: 0.071 },
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraH", probability: 0.072 },
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDJRomHype", probability: 0.071 },
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DancePacketWindmillShuffle", probability: 0.072 },
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceHarddrivePony", probability: 0.071 },
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDrillbitCrisscross", probability: 0.072 },
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceZekeCanthavethis", probability: 0.071 },
{ type: "/Lotus/StoreItems/Types/Items/PhotoBooth/PhotoboothTileRJLasXStadiumBossArena", probability: 0.071 }
];
export const getInfestedLichItemRewards = (fp: bigint): string[] => {
const rng = new SRng(fp);
const rotAReward = getRewardAtPercentage(infestedLichRotA, rng.randomFloat())!.type;
rng.randomFloat(); // unused afaict
const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
return [rotAReward, rotBReward];
};
export const sendCodaFinishedMessage = async (
inventory: TInventoryDatabaseDocument,
fp: bigint = generateRewardSeed(),
name: string = "ZEKE_BEATWOMAN_TM.1999",
killed: boolean = true
): Promise<void> => {
const att: string[] = [];
// First vanquish/convert gives a sigil
const sigil = killed
? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
: "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
att.push(toStoreItem(sigil));
}
const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
att.push(fromStoreItem(rotAReward));
att.push(fromStoreItem(rotBReward));
let countedAtt: ITypeCount[] | undefined;
if (killed) {
countedAtt = [
{
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
ItemCount: getKillTokenRewardCount(fp)
}
];
}
await createMessage(inventory.accountOwnerId, [
{
sndr: "/Lotus/Language/Bosses/Ordis",
msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
arg: [
{
Key: "LICH_NAME",
Tag: name
}
],
att: att,
countedAtt: countedAtt,
sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
highPriority: true
}
]);
};

View File

@ -31,7 +31,7 @@ export interface IFingerprintStat {
} }
export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => { export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => {
const challenge = getRandomElement(meta.availableChallenges!)!; const challenge = getRandomElement(meta.availableChallenges!);
const fingerprintChallenge: IRivenChallenge = { const fingerprintChallenge: IRivenChallenge = {
Type: challenge.fullName, Type: challenge.fullName,
Progress: 0, Progress: 0,
@ -54,11 +54,11 @@ export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFinger
export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => { export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
const fingerprint: IUnveiledRivenFingerprint = { const fingerprint: IUnveiledRivenFingerprint = {
compat: getRandomElement(meta.compatibleItems!)!, compat: getRandomElement(meta.compatibleItems!),
lim: 0, lim: 0,
lvl: 0, lvl: 0,
lvlReq: getRandomInt(8, 16), lvlReq: getRandomInt(8, 16),
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"])!, pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
buffs: [], buffs: [],
curses: [] curses: []
}; };
@ -81,7 +81,7 @@ export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenF
if (Math.random() < 0.5) { if (Math.random() < 0.5) {
const entry = getRandomElement( const entry = getRandomElement(
meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag)) meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
)!; );
fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) }); fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
} }
}; };

View File

@ -23,7 +23,6 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
Type: String, Type: String,
Pos: [Number], Pos: [Number],
Rot: [Number], Rot: [Number],
Scale: Number,
Name: String, Name: String,
Sockets: Number, Sockets: Number,
RegularCredits: Number, RegularCredits: Number,

View File

@ -96,8 +96,7 @@ import {
IInvasionProgressDatabase, IInvasionProgressDatabase,
IInvasionProgressClient, IInvasionProgressClient,
IAccolades, IAccolades,
IHubNpcCustomization, IHubNpcCustomization
ILotusCustomization
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -391,7 +390,7 @@ MailboxSchema.set("toJSON", {
const DuviriInfoSchema = new Schema<IDuviriInfo>( const DuviriInfoSchema = new Schema<IDuviriInfo>(
{ {
Seed: BigInt, Seed: Number,
NumCompletions: { type: Number, default: 0 } NumCompletions: { type: Number, default: 0 }
}, },
{ {
@ -781,26 +780,6 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
{ _id: false } { _id: false }
); );
// const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({
// Persona: String
// });
// Laxer schema for cleanupInventory
const lotusCustomizationSchema = new Schema<ILotusCustomization>(
{
Skins: [String],
pricol: colorSchema,
attcol: Schema.Types.Mixed,
sigcol: Schema.Types.Mixed,
eyecol: Schema.Types.Mixed,
facial: Schema.Types.Mixed,
cloth: Schema.Types.Mixed,
syancol: Schema.Types.Mixed,
Persona: String
},
{ _id: false }
);
const evolutionProgressSchema = new Schema<IEvolutionProgress>( const evolutionProgressSchema = new Schema<IEvolutionProgress>(
{ {
Progress: Number, Progress: Number,
@ -1055,8 +1034,6 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
{ {
ItemType: String, ItemType: String,
CompletionDate: Date, CompletionDate: Date,
TargetItemId: String,
TargetFingerprint: String,
LongGuns: { type: [EquipmentSchema], default: undefined }, LongGuns: { type: [EquipmentSchema], default: undefined },
Pistols: { type: [EquipmentSchema], default: undefined }, Pistols: { type: [EquipmentSchema], default: undefined },
Melee: { type: [EquipmentSchema], default: undefined }, Melee: { type: [EquipmentSchema], default: undefined },
@ -1148,15 +1125,15 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
const calenderProgressSchema = new Schema<ICalendarProgress>( const calenderProgressSchema = new Schema<ICalendarProgress>(
{ {
Version: { type: Number, default: 19 }, Version: { type: Number, default: 19 },
Iteration: { type: Number, required: true }, Iteration: { type: Number, default: 2 },
YearProgress: { YearProgress: {
Upgrades: { type: [String], default: [] } Upgrades: { type: [] }
}, },
SeasonProgress: { SeasonProgress: {
SeasonType: { type: String, required: true }, SeasonType: String,
LastCompletedDayIdx: { type: Number, default: 0 }, LastCompletedDayIdx: { type: Number, default: -1 },
LastCompletedChallengeDayIdx: { type: Number, default: 0 }, LastCompletedChallengeDayIdx: { type: Number, default: -1 },
ActivatedChallenges: { type: [String], default: [] } ActivatedChallenges: []
} }
}, },
{ _id: false } { _id: false }
@ -1278,11 +1255,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
PrevOwners: Number, PrevOwners: Number,
SecondInCommand: Boolean, SecondInCommand: Boolean,
Weakened: Boolean, Weakened: Boolean,
InfNodes: { type: [infNodeSchema], default: undefined }, InfNodes: [infNodeSchema],
HenchmenKilled: Number, HenchmenKilled: Number,
HintProgress: Number, HintProgress: Number,
Hints: { type: [Number], default: undefined }, Hints: [Number],
GuessHistory: { type: [Number], default: undefined }, GuessHistory: [Number],
MissionCount: Number, MissionCount: Number,
LastEnc: Number LastEnc: Number
}, },
@ -1399,7 +1376,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//How many Gift do you have left*(gift spends the trade) //How many Gift do you have left*(gift spends the trade)
GiftsRemaining: { type: Number, default: 8 }, GiftsRemaining: { type: Number, default: 8 },
//Curent trade info Giving or Getting items //Curent trade info Giving or Getting items
//PendingTrades: [Schema.Types.Mixed], PendingTrades: [Schema.Types.Mixed],
//Syndicate currently being pledged to. //Syndicate currently being pledged to.
SupportedSyndicate: String, SupportedSyndicate: String,
@ -1449,7 +1426,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
KubrowPetEggs: [kubrowPetEggSchema], KubrowPetEggs: [kubrowPetEggSchema],
//Prints Cat(3 Prints)\Kubrow(2 Prints) Pets //Prints Cat(3 Prints)\Kubrow(2 Prints) Pets
//KubrowPetPrints: [Schema.Types.Mixed], KubrowPetPrints: [Schema.Types.Mixed],
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc //Item for EquippedGear example:Scaner,LoadoutTechSummon etc
Consumables: [typeCountSchema], Consumables: [typeCountSchema],
@ -1495,7 +1472,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//item like DojoKey or Boss missions key //item like DojoKey or Boss missions key
LevelKeys: [typeCountSchema], LevelKeys: [typeCountSchema],
//Active quests //Active quests
//Quests: [Schema.Types.Mixed], Quests: [Schema.Types.Mixed],
//Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc //Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
FlavourItems: [FlavourItemSchema], FlavourItems: [FlavourItemSchema],
@ -1534,7 +1511,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
TauntHistory: { type: [tauntSchema], default: undefined }, TauntHistory: { type: [tauntSchema], default: undefined },
//noShow2FA,VisitPrimeVault etc //noShow2FA,VisitPrimeVault etc
//WebFlags: Schema.Types.Mixed, WebFlags: Schema.Types.Mixed,
//Id CompletedAlerts //Id CompletedAlerts
CompletedAlerts: [String], CompletedAlerts: [String],
@ -1554,7 +1531,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB //the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB
ActiveDojoColorResearch: String, ActiveDojoColorResearch: String,
//SentientSpawnChanceBoosters: Schema.Types.Mixed, SentientSpawnChanceBoosters: Schema.Types.Mixed,
QualifyingInvasions: [invasionProgressSchema], QualifyingInvasions: [invasionProgressSchema],
FactionScores: [Number], FactionScores: [Number],
@ -1589,10 +1566,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable // open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
DiscoveredMarkers: [discoveredMarkerSchema], DiscoveredMarkers: [discoveredMarkerSchema],
//Open location mission like "JobId" + "StageCompletions" //Open location mission like "JobId" + "StageCompletions"
//CompletedJobs: [Schema.Types.Mixed], CompletedJobs: [Schema.Types.Mixed],
//Game mission\ivent score example "Tag": "WaterFight", "Best": 170, "Count": 1258, //Game mission\ivent score example "Tag": "WaterFight", "Best": 170, "Count": 1258,
//PersonalGoalProgress: [Schema.Types.Mixed], PersonalGoalProgress: [Schema.Types.Mixed],
//Setting interface Style //Setting interface Style
ThemeStyle: String, ThemeStyle: String,
@ -1622,13 +1599,13 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema, LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema,
//https://warframe.fandom.com/wiki/Invasion //https://warframe.fandom.com/wiki/Invasion
//InvasionChainProgress: [Schema.Types.Mixed], InvasionChainProgress: [Schema.Types.Mixed],
//CorpusLich or GrineerLich //CorpusLich or GrineerLich
NemesisAbandonedRewards: { type: [String], default: [] }, NemesisAbandonedRewards: { type: [String], default: [] },
Nemesis: nemesisSchema, Nemesis: nemesisSchema,
NemesisHistory: { type: [nemesisSchema], default: undefined }, NemesisHistory: [Schema.Types.Mixed],
//LastNemesisAllySpawnTime: Schema.Types.Mixed, LastNemesisAllySpawnTime: Schema.Types.Mixed,
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social) //TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
Settings: settingsSchema, Settings: settingsSchema,
@ -1642,7 +1619,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
PlayerSkills: { type: playerSkillsSchema, default: {} }, PlayerSkills: { type: playerSkillsSchema, default: {} },
//TradeBannedUntil data //TradeBannedUntil data
//TradeBannedUntil: Schema.Types.Mixed, TradeBannedUntil: Schema.Types.Mixed,
//https://warframe.fandom.com/wiki/Helminth //https://warframe.fandom.com/wiki/Helminth
InfestedFoundry: infestedFoundrySchema, InfestedFoundry: infestedFoundrySchema,
@ -1651,7 +1628,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter. //Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter.
//https://warframe.fandom.com/wiki/Lotus#The_New_War //https://warframe.fandom.com/wiki/Lotus#The_New_War
LotusCustomization: { type: lotusCustomizationSchema, default: undefined }, LotusCustomization: Schema.Types.Mixed,
//Progress+Rank+ItemType(ZarimanPumpShotgun) //Progress+Rank+ItemType(ZarimanPumpShotgun)
//https://warframe.fandom.com/wiki/Incarnon //https://warframe.fandom.com/wiki/Incarnon
@ -1662,24 +1639,23 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Unknown and system //Unknown and system
DuviriInfo: DuviriInfoSchema, DuviriInfo: DuviriInfoSchema,
LastInventorySync: Schema.Types.ObjectId,
Mailbox: MailboxSchema, Mailbox: MailboxSchema,
HandlerPoints: Number, HandlerPoints: Number,
ChallengesFixVersion: { type: Number, default: 6 }, ChallengesFixVersion: { type: Number, default: 6 },
PlayedParkourTutorial: Boolean, PlayedParkourTutorial: Boolean,
//ActiveLandscapeTraps: [Schema.Types.Mixed], ActiveLandscapeTraps: [Schema.Types.Mixed],
//RepVotes: [Schema.Types.Mixed], RepVotes: [Schema.Types.Mixed],
//LeagueTickets: [Schema.Types.Mixed], LeagueTickets: [Schema.Types.Mixed],
HasContributedToDojo: Boolean, HasContributedToDojo: Boolean,
HWIDProtectEnabled: Boolean, HWIDProtectEnabled: Boolean,
LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" }, LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" },
CurrentLoadOutIds: [oidSchema], CurrentLoadOutIds: [oidSchema],
RandomUpgradesIdentified: Number, RandomUpgradesIdentified: Number,
BountyScore: Number, BountyScore: Number,
//ChallengeInstanceStates: [Schema.Types.Mixed], ChallengeInstanceStates: [Schema.Types.Mixed],
RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined }, RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
//Robotics: [Schema.Types.Mixed], Robotics: [Schema.Types.Mixed],
//UsedDailyDeals: [Schema.Types.Mixed], UsedDailyDeals: [Schema.Types.Mixed],
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined }, CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
HasResetAccount: { type: Boolean, default: false }, HasResetAccount: { type: Boolean, default: false },
@ -1760,9 +1736,6 @@ inventorySchema.set("toJSON", {
sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined
}; };
} }
if (inventoryDatabase.LastInventorySync) {
inventoryResponse.LastInventorySync = toOid(inventoryDatabase.LastInventorySync);
}
} }
}); });

View File

@ -1,17 +1,14 @@
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; import { toOid } from "@/src/helpers/inventoryHelpers";
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel"; import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes"; import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
import { import {
IFavouriteLoadoutDatabase, IFavouriteLoadoutDatabase,
IGardeningDatabase, IGardening,
IPlacedDecosDatabase, IPlacedDecosDatabase,
IPictureFrameInfo, IPictureFrameInfo,
IRoom, IRoom,
ITailorShopDatabase, ITailorShopDatabase,
IApartmentDatabase, IApartmentDatabase
IPlanterDatabase,
IPlantDatabase,
IPlantClient
} from "@/src/types/shipTypes"; } from "@/src/types/shipTypes";
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
@ -80,45 +77,15 @@ favouriteLoadoutSchema.set("toJSON", {
} }
}); });
const plantSchema = new Schema<IPlantDatabase>( const gardeningSchema = new Schema<IGardening>({
{ Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
PlantType: String,
EndTime: Date,
PlotIndex: Number
},
{ _id: false }
);
plantSchema.set("toJSON", {
virtuals: true,
transform(_doc, obj) {
const client = obj as IPlantClient;
const db = obj as IPlantDatabase;
client.EndTime = toMongoDate(db.EndTime);
}
}); });
const planterSchema = new Schema<IPlanterDatabase>(
{
Name: { type: String, required: true },
Plants: { type: [plantSchema], default: [] }
},
{ _id: false }
);
const gardeningSchema = new Schema<IGardeningDatabase>(
{
Planters: { type: [planterSchema], default: [] }
},
{ _id: false }
);
const apartmentSchema = new Schema<IApartmentDatabase>( const apartmentSchema = new Schema<IApartmentDatabase>(
{ {
Rooms: [roomSchema], Rooms: [roomSchema],
FavouriteLoadouts: [favouriteLoadoutSchema], FavouriteLoadouts: [favouriteLoadoutSchema],
Gardening: gardeningSchema Gardening: gardeningSchema // TODO: ensure this is correct
}, },
{ _id: false } { _id: false }
); );
@ -131,9 +98,7 @@ const apartmentDefault: IApartmentDatabase = {
{ Name: "DuviriHallway", MaxCapacity: 1600 } { Name: "DuviriHallway", MaxCapacity: 1600 }
], ],
FavouriteLoadouts: [], FavouriteLoadouts: [],
Gardening: { Gardening: {}
Planters: []
}
}; };
const orbiterSchema = new Schema<IOrbiter>( const orbiterSchema = new Schema<IOrbiter>(

View File

@ -19,7 +19,6 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController"; import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController"; import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController"; import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController";
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController"; import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController"; import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController"; import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
@ -48,7 +47,6 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
import { fishmongerController } from "@/src/controllers/api/fishmongerController"; import { fishmongerController } from "@/src/controllers/api/fishmongerController";
import { focusController } from "@/src/controllers/api/focusController"; import { focusController } from "@/src/controllers/api/focusController";
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController"; import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
import { gardeningController } from "@/src/controllers/api/gardeningController";
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController"; import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
import { getAllianceController } from "@/src/controllers/api/getAllianceController"; import { getAllianceController } from "@/src/controllers/api/getAllianceController";
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController"; import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
@ -160,7 +158,6 @@ apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
apiRouter.get("/changeGuildRank.php", changeGuildRankController); apiRouter.get("/changeGuildRank.php", changeGuildRankController);
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController); apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController);
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController); apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController); apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
apiRouter.get("/credits.php", creditsController); apiRouter.get("/credits.php", creditsController);
@ -241,7 +238,6 @@ apiRouter.post("/findSessions.php", findSessionsController);
apiRouter.post("/fishmonger.php", fishmongerController); apiRouter.post("/fishmonger.php", fishmongerController);
apiRouter.post("/focus.php", focusController); apiRouter.post("/focus.php", focusController);
apiRouter.post("/fusionTreasures.php", fusionTreasuresController); apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
apiRouter.post("/gardening.php", gardeningController);
apiRouter.post("/genericUpdate.php", genericUpdateController); apiRouter.post("/genericUpdate.php", genericUpdateController);
apiRouter.post("/getAlliance.php", getAllianceController); apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/getFriends.php", getFriendsController); apiRouter.post("/getFriends.php", getFriendsController);

View File

@ -10,7 +10,6 @@ import { getAccountInfoController } from "@/src/controllers/custom/getAccountInf
import { renameAccountController } from "@/src/controllers/custom/renameAccountController"; import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController"; import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController"; import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
import { createAccountController } from "@/src/controllers/custom/createAccountController"; import { createAccountController } from "@/src/controllers/custom/createAccountController";
import { createMessageController } from "@/src/controllers/custom/createMessageController"; import { createMessageController } from "@/src/controllers/custom/createMessageController";
@ -18,11 +17,10 @@ import { addCurrencyController } from "@/src/controllers/custom/addCurrencyContr
import { addItemsController } from "@/src/controllers/custom/addItemsController"; import { addItemsController } from "@/src/controllers/custom/addItemsController";
import { addXpController } from "@/src/controllers/custom/addXpController"; import { addXpController } from "@/src/controllers/custom/addXpController";
import { importController } from "@/src/controllers/custom/importController"; import { importController } from "@/src/controllers/custom/importController";
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController"; import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController"; import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
const customRouter = express.Router(); const customRouter = express.Router();
@ -36,7 +34,6 @@ customRouter.get("/getAccountInfo", getAccountInfoController);
customRouter.get("/renameAccount", renameAccountController); customRouter.get("/renameAccount", renameAccountController);
customRouter.get("/ircDropped", ircDroppedController); customRouter.get("/ircDropped", ircDroppedController);
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController); customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
customRouter.post("/createAccount", createAccountController); customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController); customRouter.post("/createMessage", createMessageController);
@ -45,7 +42,6 @@ customRouter.post("/addItems", addItemsController);
customRouter.post("/addXp", addXpController); customRouter.post("/addXp", addXpController);
customRouter.post("/import", importController); customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController); customRouter.post("/manageQuests", manageQuestsController);
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
customRouter.get("/config", getConfigDataController); customRouter.get("/config", getConfigDataController);
customRouter.post("/config", updateConfigDataController); customRouter.post("/config", updateConfigDataController);

View File

@ -24,7 +24,6 @@ interface IConfig {
infiniteEndo?: boolean; infiniteEndo?: boolean;
infiniteRegalAya?: boolean; infiniteRegalAya?: boolean;
infiniteHelminthMaterials?: boolean; infiniteHelminthMaterials?: boolean;
dontSubtractConsumables?: boolean;
unlockAllShipFeatures?: boolean; unlockAllShipFeatures?: boolean;
unlockAllShipDecorations?: boolean; unlockAllShipDecorations?: boolean;
unlockAllFlavourItems?: boolean; unlockAllFlavourItems?: boolean;
@ -44,7 +43,6 @@ interface IConfig {
noKimCooldowns?: boolean; noKimCooldowns?: boolean;
instantResourceExtractorDrones?: boolean; instantResourceExtractorDrones?: boolean;
noResourceExtractorDronesDamage?: boolean; noResourceExtractorDronesDamage?: boolean;
skipClanKeyCrafting?: boolean;
noDojoRoomBuildStage?: boolean; noDojoRoomBuildStage?: boolean;
noDojoDecoBuildStage?: boolean; noDojoDecoBuildStage?: boolean;
fastDojoRoomDestruction?: boolean; fastDojoRoomDestruction?: boolean;

View File

@ -1,6 +1,6 @@
import { Request } from "express"; import { Request } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { addLevelKeys, addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel"; import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { import {
@ -105,7 +105,6 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
Members: members, Members: members,
Ranks: guild.Ranks, Ranks: guild.Ranks,
Tier: guild.Tier, Tier: guild.Tier,
Emblem: guild.Emblem,
Vault: getGuildVault(guild), Vault: getGuildVault(guild),
ActiveDojoColorResearch: guild.ActiveDojoColorResearch, ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
Class: guild.Class, Class: guild.Class,
@ -134,7 +133,7 @@ export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
export const getDojoClient = async ( export const getDojoClient = async (
guild: TGuildDatabaseDocument, guild: TGuildDatabaseDocument,
status: number, status: number,
componentId?: Types.ObjectId | string componentId: Types.ObjectId | string | undefined = undefined
): Promise<IDojoClient> => { ): Promise<IDojoClient> => {
const dojo: IDojoClient = { const dojo: IDojoClient = {
_id: { $oid: guild._id.toString() }, _id: { $oid: guild._id.toString() },
@ -223,7 +222,6 @@ export const getDojoClient = async (
Type: deco.Type, Type: deco.Type,
Pos: deco.Pos, Pos: deco.Pos,
Rot: deco.Rot, Rot: deco.Rot,
Scale: deco.Scale,
Name: deco.Name, Name: deco.Name,
Sockets: deco.Sockets, Sockets: deco.Sockets,
PictureFrameInfo: deco.PictureFrameInfo PictureFrameInfo: deco.PictureFrameInfo
@ -505,7 +503,7 @@ export const hasGuildPermissionEx = (
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => { export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
const members = await GuildMember.find({ guildId, status: 0 }, "accountId"); const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
await parallelForeach(members, async member => { for (const member of members) {
const inventory = await getInventory(member.accountId.toString(), "MiscItems"); const inventory = await getInventory(member.accountId.toString(), "MiscItems");
const index = inventory.MiscItems.findIndex( const index = inventory.MiscItems.findIndex(
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment" x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
@ -514,7 +512,7 @@ export const removePigmentsFromGuildMembers = async (guildId: string | Types.Obj
inventory.MiscItems.splice(index, 1); inventory.MiscItems.splice(index, 1);
await inventory.save(); await inventory.save();
} }
}); }
}; };
export const processGuildTechProjectContributionsUpdate = async ( export const processGuildTechProjectContributionsUpdate = async (
@ -554,7 +552,7 @@ export const setGuildTechLogState = (
guild: TGuildDatabaseDocument, guild: TGuildDatabaseDocument,
type: string, type: string,
state: number, state: number,
dateTime?: Date dateTime: Date | undefined = undefined
): boolean => { ): boolean => {
guild.TechChanges ??= []; guild.TechChanges ??= [];
const entry = guild.TechChanges.find(x => x.details == type); const entry = guild.TechChanges.find(x => x.details == type);
@ -657,32 +655,6 @@ export const checkClanAscensionHasRequiredContributors = async (guild: TGuildDat
} }
}; };
export const giveClanKey = (inventory: TInventoryDatabaseDocument, inventoryChanges?: IInventoryChanges): void => {
if (config.skipClanKeyCrafting) {
const levelKeyChanges = [
{
ItemType: "/Lotus/Types/Keys/DojoKey",
ItemCount: 1
}
];
addLevelKeys(inventory, levelKeyChanges);
if (inventoryChanges) {
combineInventoryChanges(inventoryChanges, { LevelKeys: levelKeyChanges });
}
} else {
const recipeChanges = [
{
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
ItemCount: 1
}
];
addRecipes(inventory, recipeChanges);
if (inventoryChanges) {
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
}
}
};
export const removeDojoKeyItems = (inventory: TInventoryDatabaseDocument): IInventoryChanges => { export const removeDojoKeyItems = (inventory: TInventoryDatabaseDocument): IInventoryChanges => {
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};

View File

@ -2,7 +2,6 @@ import { Types } from "mongoose";
import { import {
IEquipmentClient, IEquipmentClient,
IEquipmentDatabase, IEquipmentDatabase,
IItemConfig,
IOperatorConfigClient, IOperatorConfigClient,
IOperatorConfigDatabase IOperatorConfigDatabase
} from "../types/inventoryTypes/commonInventoryTypes"; } from "../types/inventoryTypes/commonInventoryTypes";
@ -38,7 +37,6 @@ import {
} from "../types/inventoryTypes/inventoryTypes"; } from "../types/inventoryTypes/inventoryTypes";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes"; import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
import { slotNames } from "../types/purchaseTypes";
const convertDate = (value: IMongoDate): Date => { const convertDate = (value: IMongoDate): Date => {
return new Date(parseInt(value.$date.$numberLong)); return new Date(parseInt(value.$date.$numberLong));
@ -170,25 +168,10 @@ const convertPendingRecipe = (client: IPendingRecipeClient): IPendingRecipeDatab
const convertNemesis = (client: INemesisClient): INemesisDatabase => { const convertNemesis = (client: INemesisClient): INemesisDatabase => {
return { return {
...client, ...client,
fp: BigInt(client.fp),
d: convertDate(client.d) d: convertDate(client.d)
}; };
}; };
// Empty objects from live may have been encoded as empty arrays because of PHP.
const convertItemConfig = <T extends IItemConfig>(client: T): T => {
return {
...client,
pricol: Array.isArray(client.pricol) ? {} : client.pricol,
attcol: Array.isArray(client.attcol) ? {} : client.attcol,
sigcol: Array.isArray(client.sigcol) ? {} : client.sigcol,
eyecol: Array.isArray(client.eyecol) ? {} : client.eyecol,
facial: Array.isArray(client.facial) ? {} : client.facial,
cloth: Array.isArray(client.cloth) ? {} : client.cloth,
syancol: Array.isArray(client.syancol) ? {} : client.syancol
};
};
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => { export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
for (const key of equipmentKeys) { for (const key of equipmentKeys) {
if (client[key] !== undefined) { if (client[key] !== undefined) {
@ -229,7 +212,20 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig)); replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
} }
} }
for (const key of slotNames) { for (const key of [
"SuitBin",
"WeaponBin",
"SentinelBin",
"SpaceSuitBin",
"SpaceWeaponBin",
"PvpBonusLoadoutBin",
"PveBonusLoadoutBin",
"RandomModBin",
"MechBin",
"CrewMemberBin",
"OperatorAmpBin",
"CrewShipSalvageBin"
] as const) {
if (client[key] !== undefined) { if (client[key] !== undefined) {
replaceSlots(db[key], client[key]); replaceSlots(db[key], client[key]);
} }
@ -367,7 +363,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
db.PlayerSkills = client.PlayerSkills; db.PlayerSkills = client.PlayerSkills;
} }
if (client.LotusCustomization !== undefined) { if (client.LotusCustomization !== undefined) {
db.LotusCustomization = convertItemConfig(client.LotusCustomization); db.LotusCustomization = client.LotusCustomization;
} }
if (client.CollectibleSeries !== undefined) { if (client.CollectibleSeries !== undefined) {
db.CollectibleSeries = client.CollectibleSeries; db.CollectibleSeries = client.CollectibleSeries;

View File

@ -18,6 +18,7 @@ import {
IKubrowPetEggDatabase, IKubrowPetEggDatabase,
IKubrowPetEggClient, IKubrowPetEggClient,
ILibraryDailyTaskInfo, ILibraryDailyTaskInfo,
ICalendarProgress,
IDroneClient, IDroneClient,
IUpgradeClient, IUpgradeClient,
TPartialStartingGear, TPartialStartingGear,
@ -25,10 +26,7 @@ import {
ICrewMemberClient, ICrewMemberClient,
Status, Status,
IKubrowPetDetailsDatabase, IKubrowPetDetailsDatabase,
ITraits, ITraits
ICalendarProgress,
INemesisWeaponTargetFingerprint,
INemesisPetTargetFingerprint
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@ -80,8 +78,6 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService"; import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
import { createMessage } from "./inboxService"; import { createMessage } from "./inboxService";
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper"; import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
import { getWorldState } from "./worldStateService";
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
export const createInventory = async ( export const createInventory = async (
accountOwnerId: Types.ObjectId, accountOwnerId: Types.ObjectId,
@ -95,6 +91,7 @@ export const createInventory = async (
}); });
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask(); inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
inventory.CalendarProgress = createCalendar();
inventory.RewardSeed = generateRewardSeed(); inventory.RewardSeed = generateRewardSeed();
inventory.DuviriInfo = { inventory.DuviriInfo = {
Seed: generateRewardSeed(), Seed: generateRewardSeed(),
@ -123,15 +120,10 @@ export const createInventory = async (
} }
}; };
export const generateRewardSeed = (): bigint => { export const generateRewardSeed = (): number => {
const hiDword = getRandomInt(0, 0x7fffffff); const min = -Number.MAX_SAFE_INTEGER;
const loDword = getRandomInt(0, 0xffffffff); const max = Number.MAX_SAFE_INTEGER;
let seed = (BigInt(hiDword) << 32n) | BigInt(loDword); return Math.floor(Math.random() * (max - min + 1)) + min;
if (Math.random() < 0.5) {
seed *= -1n;
seed -= 1n;
}
return seed;
}; };
//TODO: RawUpgrades might need to return a LastAdded //TODO: RawUpgrades might need to return a LastAdded
@ -146,7 +138,7 @@ const awakeningRewards = [
export const addStartingGear = async ( export const addStartingGear = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
startingGear?: TPartialStartingGear startingGear: TPartialStartingGear | undefined = undefined
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
const { LongGuns, Pistols, Suits, Melee } = startingGear || { const { LongGuns, Pistols, Suits, Melee } = startingGear || {
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }], LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
@ -157,22 +149,23 @@ export const addStartingGear = async (
//TODO: properly merge weapon bin changes it is currently static here //TODO: properly merge weapon bin changes it is currently static here
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, { IsNew: false }, inventoryChanges); addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Pistols", Pistols[0].ItemType, { IsNew: false }, inventoryChanges); addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Melee", Melee[0].ItemType, { IsNew: false }, inventoryChanges); addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
await addPowerSuit(inventory, Suits[0].ItemType, { IsNew: false }, inventoryChanges); await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
addEquipment( addEquipment(
inventory, inventory,
"DataKnives", "DataKnives",
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon", "/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
{ XP: 450_000, IsNew: false }, undefined,
inventoryChanges inventoryChanges,
{ XP: 450_000 }
); );
addEquipment( addEquipment(
inventory, inventory,
"Scoops", "Scoops",
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest", "/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
{ IsNew: false }, undefined,
inventoryChanges inventoryChanges
); );
@ -215,15 +208,6 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
for (const key in delta) { for (const key in delta) {
if (!(key in InventoryChanges)) { if (!(key in InventoryChanges)) {
InventoryChanges[key] = delta[key]; InventoryChanges[key] = delta[key];
} else if (key == "MiscItems") {
for (const deltaItem of delta[key]!) {
const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType);
if (existing) {
existing.ItemCount += deltaItem.ItemCount;
} else {
InventoryChanges[key]!.push(deltaItem);
}
}
} else if (Array.isArray(delta[key])) { } else if (Array.isArray(delta[key])) {
const left = InventoryChanges[key] as object[]; const left = InventoryChanges[key] as object[];
const right: object[] = delta[key]; const right: object[] = delta[key];
@ -256,7 +240,7 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
export const getInventory = async ( export const getInventory = async (
accountOwnerId: string, accountOwnerId: string,
projection?: string projection: string | undefined = undefined
): Promise<TInventoryDatabaseDocument> => { ): Promise<TInventoryDatabaseDocument> => {
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId }, projection); const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId }, projection);
@ -330,8 +314,7 @@ export const addItem = async (
typeName: string, typeName: string,
quantity: number = 1, quantity: number = 1,
premiumPurchase: boolean = false, premiumPurchase: boolean = false,
seed?: bigint, seed?: bigint
targetFingerprint?: string
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments. // Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
if (typeName in ExportBundles) { if (typeName in ExportBundles) {
@ -534,13 +517,14 @@ export const addItem = async (
] ]
}); });
} }
if (targetFingerprint) { const inventoryChanges = addEquipment(
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisWeaponTargetFingerprint; inventory,
defaultOverwrites.UpgradeType = targetFingerprintObj.ItemType; weapon.productCategory,
defaultOverwrites.UpgradeFingerprint = JSON.stringify(targetFingerprintObj.UpgradeFingerprint); typeName,
defaultOverwrites.ItemName = targetFingerprintObj.Name; [],
} {},
const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites); defaultOverwrites
);
if (weapon.additionalItems) { if (weapon.additionalItems) {
for (const item of weapon.additionalItems) { for (const item of weapon.additionalItems) {
combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1)); combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1));
@ -548,32 +532,7 @@ export const addItem = async (
} }
return { return {
...inventoryChanges, ...inventoryChanges,
...occupySlot( ...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase)
inventory,
productCategoryToInventoryBin(weapon.productCategory) ?? InventorySlot.WEAPONS,
premiumPurchase
)
};
} else if (targetFingerprint) {
// Sister's Hound
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisPetTargetFingerprint;
const head = targetFingerprintObj.Parts[0];
const defaultOverwrites: Partial<IEquipmentDatabase> = {
ModularParts: targetFingerprintObj.Parts,
ItemName: targetFingerprintObj.Name,
Configs: applyDefaultUpgrades(inventory, ExportWeapons[head].defaultUpgrades)
};
const itemType = {
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
}[head] as string;
return {
...addEquipment(inventory, "MoaPets", itemType, defaultOverwrites),
...occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase)
}; };
} else { } else {
// Modular weapon parts // Modular weapon parts
@ -621,7 +580,7 @@ export const addItem = async (
} }
if (typeName in ExportFusionBundles) { if (typeName in ExportFusionBundles) {
const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity; const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity;
addFusionPoints(inventory, fusionPointsTotal); inventory.FusionPoints += fusionPointsTotal;
return { return {
FusionPoints: fusionPointsTotal FusionPoints: fusionPointsTotal
}; };
@ -662,9 +621,12 @@ export const addItem = async (
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
default: { default: {
return { return {
...(await addPowerSuit(inventory, typeName, { ...(await addPowerSuit(
Features: premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined inventory,
})), typeName,
{},
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
)),
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase) ...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase)
}; };
} }
@ -859,8 +821,7 @@ const addSentinel = (
const features = premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined; const features = premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined;
const sentinelIndex = const sentinelIndex =
inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features, IsNew: true }) - inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features }) - 1;
1;
inventoryChanges.Sentinels ??= []; inventoryChanges.Sentinels ??= [];
inventoryChanges.Sentinels.push(inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>()); inventoryChanges.Sentinels.push(inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>());
@ -884,8 +845,8 @@ const addSentinelWeapon = (
export const addPowerSuit = async ( export const addPowerSuit = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
powersuitName: string, powersuitName: string,
defaultOverwrites?: Partial<IEquipmentDatabase>, inventoryChanges: IInventoryChanges = {},
inventoryChanges: IInventoryChanges = {} features: number | undefined = undefined
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
const powersuit = ExportWarframes[powersuitName] as IPowersuit | undefined; const powersuit = ExportWarframes[powersuitName] as IPowersuit | undefined;
const exalted = powersuit?.exalted ?? []; const exalted = powersuit?.exalted ?? [];
@ -899,20 +860,15 @@ export const addPowerSuit = async (
} }
} }
} }
const suit: Omit<IEquipmentDatabase, "_id"> = Object.assign( const suitIndex =
{ inventory.Suits.push({
ItemType: powersuitName, ItemType: powersuitName,
Configs: [], Configs: [],
UpgradeVer: 101, UpgradeVer: 101,
XP: 0, XP: 0,
Features: features,
IsNew: true IsNew: true
}, }) - 1;
defaultOverwrites
);
if (!suit.IsNew) {
suit.IsNew = undefined;
}
const suitIndex = inventory.Suits.push(suit) - 1;
inventoryChanges.Suits ??= []; inventoryChanges.Suits ??= [];
inventoryChanges.Suits.push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>()); inventoryChanges.Suits.push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
return inventoryChanges; return inventoryChanges;
@ -922,7 +878,7 @@ export const addMechSuit = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
mechsuitName: string, mechsuitName: string,
inventoryChanges: IInventoryChanges = {}, inventoryChanges: IInventoryChanges = {},
features?: number features: number | undefined = undefined
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
const powersuit = ExportWarframes[mechsuitName] as IPowersuit | undefined; const powersuit = ExportWarframes[mechsuitName] as IPowersuit | undefined;
const exalted = powersuit?.exalted ?? []; const exalted = powersuit?.exalted ?? [];
@ -974,7 +930,7 @@ export const addSpaceSuit = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
spacesuitName: string, spacesuitName: string,
inventoryChanges: IInventoryChanges = {}, inventoryChanges: IInventoryChanges = {},
features?: number features: number | undefined = undefined
): IInventoryChanges => { ): IInventoryChanges => {
const suitIndex = const suitIndex =
inventory.SpaceSuits.push({ inventory.SpaceSuits.push({
@ -1113,15 +1069,6 @@ export const updateCurrency = (
return currencyChanges; return currencyChanges;
}; };
export const addFusionPoints = (inventory: TInventoryDatabaseDocument, add: number): number => {
if (inventory.FusionPoints + add > 2147483647) {
logger.warn(`capping FusionPoints balance at 2147483647`);
add = 2147483647 - inventory.FusionPoints;
}
inventory.FusionPoints += add;
return add;
};
const standingLimitBinToInventoryKey: Record< const standingLimitBinToInventoryKey: Record<
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">, Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
keyof IDailyAffiliations keyof IDailyAffiliations
@ -1233,21 +1180,20 @@ export const addEquipment = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
category: TEquipmentKey, category: TEquipmentKey,
type: string, type: string,
defaultOverwrites?: Partial<IEquipmentDatabase>, modularParts: string[] | undefined = undefined,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {},
defaultOverwrites: Partial<IEquipmentDatabase> | undefined = undefined
): IInventoryChanges => { ): IInventoryChanges => {
const equipment: Omit<IEquipmentDatabase, "_id"> = Object.assign( const equipment = Object.assign(
{ {
ItemType: type, ItemType: type,
Configs: [], Configs: [],
XP: 0, XP: 0,
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons" ModularParts: modularParts,
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons" ? true : undefined
}, },
defaultOverwrites defaultOverwrites
); );
if (!equipment.IsNew) {
equipment.IsNew = undefined;
}
const index = inventory[category].push(equipment) - 1; const index = inventory[category].push(equipment) - 1;
inventoryChanges[category] ??= []; inventoryChanges[category] ??= [];
@ -1276,16 +1222,12 @@ export const addSkin = (
typeName: string, typeName: string,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) {
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
} else {
const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1; const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.WeaponSkins ??= []; inventoryChanges.WeaponSkins ??= [];
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push( (inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>() inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
); );
}
return inventoryChanges; return inventoryChanges;
}; };
@ -1503,22 +1445,6 @@ export const addGearExpByCategory = (
}); });
}; };
export const addMiscItem = (
inventory: TInventoryDatabaseDocument,
type: string,
count: number,
inventoryChanges: IInventoryChanges
): void => {
const miscItemChanges: IMiscItem[] = [
{
ItemType: type,
ItemCount: count
}
];
addMiscItems(inventory, miscItemChanges);
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
};
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => { export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => {
const { MiscItems } = inventory; const { MiscItems } = inventory;
@ -1798,7 +1724,7 @@ export const addKeyChainItems = async (
}; };
export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => { export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
const enemyTypes = getRandomElement(libraryDailyTasks)!; const enemyTypes = getRandomElement(libraryDailyTasks);
const enemyAvatar = ExportEnemies.avatars[enemyTypes[0]]; const enemyAvatar = ExportEnemies.avatars[enemyTypes[0]];
const scansRequired = getRandomInt(2, 4); const scansRequired = getRandomInt(2, 4);
return { return {
@ -1812,6 +1738,20 @@ export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
}; };
}; };
const createCalendar = (): ICalendarProgress => {
return {
Version: 19,
Iteration: 2,
YearProgress: { Upgrades: [] },
SeasonProgress: {
SeasonType: "CST_SPRING",
LastCompletedDayIdx: -1,
LastCompletedChallengeDayIdx: -1,
ActivatedChallenges: []
}
};
};
export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => { export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => {
inventory.Affiliations.push({ inventory.Affiliations.push({
Title: 1, Title: 1,
@ -1847,132 +1787,4 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
logger.debug(`removing FreeFavorsEarned from LibrarySyndicate`); logger.debug(`removing FreeFavorsEarned from LibrarySyndicate`);
LibrarySyndicate.FreeFavorsEarned = undefined; LibrarySyndicate.FreeFavorsEarned = undefined;
} }
if (inventory.LotusCustomization) {
if (
Array.isArray(inventory.LotusCustomization.attcol) ||
Array.isArray(inventory.LotusCustomization.sigcol) ||
Array.isArray(inventory.LotusCustomization.eyecol) ||
Array.isArray(inventory.LotusCustomization.facial) ||
Array.isArray(inventory.LotusCustomization.cloth) ||
Array.isArray(inventory.LotusCustomization.syancol)
) {
logger.debug(`fixing empty objects represented as empty arrays in LotusCustomization`);
inventory.LotusCustomization.attcol = {};
inventory.LotusCustomization.sigcol = {};
inventory.LotusCustomization.eyecol = {};
inventory.LotusCustomization.facial = {};
inventory.LotusCustomization.cloth = {};
inventory.LotusCustomization.syancol = {};
}
}
};
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
const currentSeason = getWorldState().KnownCalendarSeasons[0];
if (!inventory.CalendarProgress) {
inventory.CalendarProgress = {
Version: 19,
Iteration: currentSeason.YearIteration,
YearProgress: {
Upgrades: []
},
SeasonProgress: {
SeasonType: currentSeason.Season,
LastCompletedDayIdx: 0,
LastCompletedChallengeDayIdx: 0,
ActivatedChallenges: []
}
};
}
const yearRolledOver = inventory.CalendarProgress.Iteration != currentSeason.YearIteration;
if (yearRolledOver) {
inventory.CalendarProgress.Iteration = currentSeason.YearIteration;
inventory.CalendarProgress.YearProgress.Upgrades = [];
}
if (yearRolledOver || inventory.CalendarProgress.SeasonProgress.SeasonType != currentSeason.Season) {
inventory.CalendarProgress.SeasonProgress.SeasonType = currentSeason.Season;
inventory.CalendarProgress.SeasonProgress.LastCompletedDayIdx = -1;
inventory.CalendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = -1;
inventory.CalendarProgress.SeasonProgress.ActivatedChallenges = [];
}
return inventory.CalendarProgress;
};
export const giveNemesisWeaponRecipe = (
inventory: TInventoryDatabaseDocument,
weaponType: string,
nemesisName: string = "AGOR ROK",
weaponLoc?: string,
KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
fp: bigint = generateRewardSeed()
): void => {
if (!weaponLoc) {
weaponLoc = ExportWeapons[weaponType].name;
}
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == weaponType)![0];
addRecipes(inventory, [
{
ItemType: recipeType,
ItemCount: 1
}
]);
inventory.PendingRecipes.push({
CompletionDate: new Date(),
ItemType: recipeType,
TargetFingerprint: JSON.stringify({
ItemType: "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod",
UpgradeFingerprint: {
compat: weaponType,
buffs: [
{
Tag: getInnateDamageTag(KillingSuit),
Value: getInnateDamageValue(fp)
}
]
},
Name: weaponLoc + "|" + nemesisName
} satisfies INemesisWeaponTargetFingerprint)
});
};
export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
const head = getRandomElement([
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
])!;
const body = getRandomElement([
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
])!;
const legs = getRandomElement([
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
])!;
const tail = getRandomElement([
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
])!;
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
addRecipes(inventory, [
{
ItemType: recipeType,
ItemCount: 1
}
]);
inventory.PendingRecipes.push({
CompletionDate: new Date(),
ItemType: recipeType,
TargetFingerprint: JSON.stringify({
Parts: [head, body, legs, tail],
Name: "/Lotus/Language/Pets/ZanukaPetName|" + nemesisName
} satisfies INemesisPetTargetFingerprint)
});
}; };

View File

@ -17,7 +17,6 @@ import {
dict_uk, dict_uk,
dict_zh, dict_zh,
ExportArcanes, ExportArcanes,
ExportBoosters,
ExportCustoms, ExportCustoms,
ExportDrones, ExportDrones,
ExportGear, ExportGear,
@ -218,30 +217,15 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
}; };
export const isStoreItem = (type: string): boolean => { export const isStoreItem = (type: string): boolean => {
return type.startsWith("/Lotus/StoreItems/") || type in ExportBoosters; return type.startsWith("/Lotus/StoreItems/");
}; };
export const toStoreItem = (type: string): string => { export const toStoreItem = (type: string): string => {
if (type.startsWith("/Lotus/Types/StoreItems/Boosters/")) {
const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == type);
if (boosterEntry) {
return boosterEntry[0];
}
throw new Error(`could not convert ${type} to a store item`);
}
return "/Lotus/StoreItems/" + type.substring("/Lotus/".length); return "/Lotus/StoreItems/" + type.substring("/Lotus/".length);
}; };
export const fromStoreItem = (type: string): string => { export const fromStoreItem = (type: string): string => {
if (type.startsWith("/Lotus/StoreItems/")) {
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length); return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
}
if (type in ExportBoosters) {
return ExportBoosters[type].typeName;
}
throw new Error(`${type} is not a store item`);
}; };
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => { export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {

View File

@ -77,6 +77,7 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
const reward = rng.randomReward(randomRewards)!; const reward = rng.randomReward(randomRewards)!;
//const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!; //const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!;
if (reward.RewardType == "RT_RANDOM_RECIPE") { if (reward.RewardType == "RT_RANDOM_RECIPE") {
// Not very faithful implementation but roughly the same idea
const masteredItems = new Set(); const masteredItems = new Set();
for (const entry of inventory.XPInfo) { for (const entry of inventory.XPInfo) {
masteredItems.add(entry.ItemType); masteredItems.add(entry.ItemType);
@ -94,15 +95,15 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
} }
const eligibleRecipes: string[] = []; const eligibleRecipes: string[] = [];
for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) { for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) {
if (!recipe.excludeFromMarket && unmasteredItems.has(recipe.resultType)) { if (unmasteredItems.has(recipe.resultType)) {
eligibleRecipes.push(uniqueName); eligibleRecipes.push(uniqueName);
} }
} }
if (eligibleRecipes.length == 0) { if (eligibleRecipes.length == 0) {
// This account has all applicable warframes and weapons already mastered (filthy cheater), need a different reward. // This account has all warframes and weapons already mastered (filthy cheater), need a different reward.
return getRandomLoginReward(rng, day, inventory); return getRandomLoginReward(rng, day, inventory);
} }
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes)!); reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes));
} }
return { return {
//_id: toOid(new Types.ObjectId()), //_id: toOid(new Types.ObjectId()),

View File

@ -19,7 +19,6 @@ import {
addCrewShipRawSalvage, addCrewShipRawSalvage,
addEmailItem, addEmailItem,
addFocusXpIncreases, addFocusXpIncreases,
addFusionPoints,
addFusionTreasures, addFusionTreasures,
addGearExpByCategory, addGearExpByCategory,
addItem, addItem,
@ -30,13 +29,9 @@ import {
addMods, addMods,
addRecipes, addRecipes,
addShipDecorations, addShipDecorations,
addSkin,
addStanding, addStanding,
combineInventoryChanges, combineInventoryChanges,
generateRewardSeed, generateRewardSeed,
getCalendarProgress,
giveNemesisPetRecipe,
giveNemesisWeaponRecipe,
updateCurrency, updateCurrency,
updateSyndicate updateSyndicate
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
@ -48,14 +43,14 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
import { getEntriesUnsafe } from "@/src/utils/ts-utils"; import { getEntriesUnsafe } from "@/src/utils/ts-utils";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { handleStoreItemAcquisition } from "./purchaseService"; import { handleStoreItemAcquisition } from "./purchaseService";
import { IMissionCredits, IMissionReward } from "../types/missionTypes"; import { IMissionReward } from "../types/missionTypes";
import { crackRelic } from "@/src/helpers/relicHelper"; import { crackRelic } from "@/src/helpers/relicHelper";
import { createMessage } from "./inboxService"; import { createMessage } from "./inboxService";
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json"; import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json"; import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json"; import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json"; import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers"; import { getInfNodes } from "@/src/helpers/nemesisHelpers";
import { Loadout } from "../models/inventoryModels/loadoutModel"; import { Loadout } from "../models/inventoryModels/loadoutModel";
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes"; import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService"; import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
@ -72,23 +67,19 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
return rotations; return rotations;
} }
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const missionIndex: number | undefined = ExportRegions[rewardInfo.node]?.missionIndex;
// For Rescue missions // For Rescue missions
if (missionIndex == 3 && rewardInfo.rewardTier) { if (rewardInfo.node in ExportRegions && ExportRegions[rewardInfo.node].missionIndex == 3 && rewardInfo.rewardTier) {
return [rewardInfo.rewardTier]; return [rewardInfo.rewardTier];
} }
const rotationCount = rewardInfo.rewardQualifications?.length || 0; // Aborting a railjack mission should not give any rewards (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
if (rewardInfo.rewardQualifications === undefined) {
// Empty or absent rewardQualifications should not give rewards when: return [];
// - Completing only 1 zone of (E)SO (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
if (rotationCount == 0 && missionIndex != 30 && missionIndex != 32) {
return [0];
} }
const rotationCount = rewardInfo.rewardQualifications.length || 0;
if (rotationCount === 0) return [0];
const rotationPattern = const rotationPattern =
tierOverride === undefined tierOverride === undefined
? [0, 0, 1, 2] // A, A, B, C ? [0, 0, 1, 2] // A, A, B, C
@ -193,12 +184,6 @@ export const addMissionInventoryUpdates = async (
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) { if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards; inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
} }
if (inventoryUpdates.RewardInfo.NemesisHenchmenKills && inventory.Nemesis) {
inventory.Nemesis.HenchmenKilled += inventoryUpdates.RewardInfo.NemesisHenchmenKills;
}
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress;
}
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) { if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
// e.g. for Profit-Taker Phase 1: // e.g. for Profit-Taker Phase 1:
// JobTier: -6, // JobTier: -6,
@ -280,14 +265,7 @@ export const addMissionInventoryUpdates = async (
addMiscItems(inventory, value); addMiscItems(inventory, value);
break; break;
case "Consumables": case "Consumables":
if (config.dontSubtractConsumables) {
addConsumables(
inventory,
value.filter(x => x.ItemCount > 0)
);
} else {
addConsumables(inventory, value); addConsumables(inventory, value);
}
break; break;
case "Recipes": case "Recipes":
addRecipes(inventory, value); addRecipes(inventory, value);
@ -309,14 +287,14 @@ export const addMissionInventoryUpdates = async (
addShipDecorations(inventory, value); addShipDecorations(inventory, value);
break; break;
case "FusionBundles": { case "FusionBundles": {
let fusionPointsDelta = 0; let fusionPoints = 0;
for (const fusionBundle of value) { for (const fusionBundle of value) {
fusionPointsDelta += addFusionPoints( const fusionPointsTotal =
inventory, ExportFusionBundles[fusionBundle.ItemType].fusionPoints * fusionBundle.ItemCount;
ExportFusionBundles[fusionBundle.ItemType].fusionPoints * fusionBundle.ItemCount inventory.FusionPoints += fusionPointsTotal;
); fusionPoints += fusionPointsTotal;
} }
inventoryChanges.FusionPoints = fusionPointsDelta; inventoryChanges.FusionPoints = fusionPoints;
break; break;
} }
case "EmailItems": { case "EmailItems": {
@ -380,7 +358,6 @@ export const addMissionInventoryUpdates = async (
: 10) : 10)
) { ) {
progress.Completed = true; progress.Completed = true;
inventory.LibraryPersonalTarget = undefined;
} }
logger.debug(`synthesis of ${scan.EnemyType} added to personal target progress`); logger.debug(`synthesis of ${scan.EnemyType} added to personal target progress`);
synthesisIgnored = false; synthesisIgnored = false;
@ -434,11 +411,6 @@ export const addMissionInventoryUpdates = async (
upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
}); });
break; break;
case "WeaponSkins":
for (const item of value) {
addSkin(inventory, item.ItemType);
}
break;
case "Boosters": case "Boosters":
value.forEach(booster => { value.forEach(booster => {
addBooster(booster.ItemType, booster.ExpiryDate, inventory); addBooster(booster.ItemType, booster.ExpiryDate, inventory);
@ -516,16 +488,6 @@ export const addMissionInventoryUpdates = async (
} }
break; break;
} }
case "KubrowPetEggs": {
for (const egg of value) {
inventory.KubrowPetEggs ??= [];
inventory.KubrowPetEggs.push({
ItemType: egg.ItemType,
_id: new Types.ObjectId()
});
}
break;
}
case "DiscoveredMarkers": { case "DiscoveredMarkers": {
for (const clientMarker of value) { for (const clientMarker of value) {
const dbMarker = inventory.DiscoveredMarkers.find(x => x.tag == clientMarker.tag); const dbMarker = inventory.DiscoveredMarkers.find(x => x.tag == clientMarker.tag);
@ -599,76 +561,6 @@ export const addMissionInventoryUpdates = async (
} }
break; break;
} }
case "CalendarProgress": {
const calendarProgress = getCalendarProgress(inventory);
for (const progress of value) {
const challengeName = progress.challenge.substring(progress.challenge.lastIndexOf("/") + 1);
calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx++;
calendarProgress.SeasonProgress.ActivatedChallenges.push(challengeName);
}
break;
}
case "duviriCaveOffers": {
// Duviri cave offers (generated with the duviri seed) change after completing one of its game modes (not when aborting).
if (inventoryUpdates.MissionStatus != "GS_QUIT") {
inventory.DuviriInfo.Seed = generateRewardSeed();
}
break;
}
case "NemesisKillConvert":
if (inventory.Nemesis) {
inventory.NemesisHistory ??= [];
inventory.NemesisHistory.push({
// Copy over all 'base' values
fp: inventory.Nemesis.fp,
d: inventory.Nemesis.d,
manifest: inventory.Nemesis.manifest,
KillingSuit: inventory.Nemesis.KillingSuit,
killingDamageType: inventory.Nemesis.killingDamageType,
ShoulderHelmet: inventory.Nemesis.ShoulderHelmet,
WeaponIdx: inventory.Nemesis.WeaponIdx,
AgentIdx: inventory.Nemesis.AgentIdx,
BirthNode: inventory.Nemesis.BirthNode,
Faction: inventory.Nemesis.Faction,
Rank: inventory.Nemesis.Rank,
Traded: inventory.Nemesis.Traded,
PrevOwners: inventory.Nemesis.PrevOwners,
SecondInCommand: inventory.Nemesis.SecondInCommand,
Weakened: inventory.Nemesis.Weakened,
// And set killed flag
k: value.killed
});
if (value.killed) {
if (
value.weaponLoc &&
inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
) {
const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
inventory.Nemesis.WeaponIdx
];
giveNemesisWeaponRecipe(
inventory,
weaponType,
value.nemesisName,
value.weaponLoc,
inventory.Nemesis.KillingSuit,
inventory.Nemesis.fp
);
}
if (value.petLoc) {
giveNemesisPetRecipe(inventory);
}
}
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
}
inventory.Nemesis = undefined;
}
break;
default: default:
// Equipment XP updates // Equipment XP updates
if (equipmentKeys.includes(key as TEquipmentKey)) { if (equipmentKeys.includes(key as TEquipmentKey)) {
@ -694,140 +586,8 @@ interface AddMissionRewardsReturnType {
credits?: IMissionCredits; credits?: IMissionCredits;
AffiliationMods?: IAffiliationMods[]; AffiliationMods?: IAffiliationMods[];
SyndicateXPItemReward?: number; SyndicateXPItemReward?: number;
ConquestCompletedMissionsCount?: number;
} }
interface IConquestReward {
at: number;
pool: IRngResult[];
}
const labConquestRewards: IConquestReward[] = [
{
at: 5,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
][0] as IRngResult[]
},
{
at: 10,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
][0] as IRngResult[]
},
{
at: 15,
pool: [
{
type: "/Lotus/StoreItems/Types/Gameplay/EntratiLab/Resources/EntratiLanthornBundle",
itemCount: 3,
probability: 1
}
]
},
{
at: 20,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
][0] as IRngResult[]
},
{
at: 28,
pool: [
{
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
itemCount: 20,
probability: 1
}
]
},
{
at: 31,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
][0] as IRngResult[]
},
{
at: 34,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestArcaneRewards"
][0] as IRngResult[]
},
{
at: 37,
pool: [
{
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
itemCount: 50,
probability: 1
}
]
}
];
const hexConquestRewards: IConquestReward[] = [
{
at: 5,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
][0] as IRngResult[]
},
{
at: 10,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
][0] as IRngResult[]
},
{
at: 15,
pool: [
{
type: "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea",
itemCount: 1,
probability: 1
}
]
},
{
at: 20,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
][0] as IRngResult[]
},
{
at: 28,
pool: [
{
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
itemCount: 6,
probability: 1
}
]
},
{
at: 31,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
][0] as IRngResult[]
},
{
at: 34,
pool: ExportRewards[
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestArcaneRewards"
][0] as IRngResult[]
},
{
at: 37,
pool: [
{
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
itemCount: 9,
probability: 1
}
]
}
];
//TODO: return type of partial missioninventoryupdate response //TODO: return type of partial missioninventoryupdate response
export const addMissionRewards = async ( export const addMissionRewards = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
@ -849,13 +609,17 @@ export const addMissionRewards = async (
return { MissionRewards: [] }; return { MissionRewards: [] };
} }
if (rewardInfo.rewardSeed) {
// We're using a reward seed, so give the client a new one in the response. On live, missionInventoryUpdate seems to always provide a fresh one in the response.
inventory.RewardSeed = generateRewardSeed();
}
//TODO: check double reward merging //TODO: check double reward merging
const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion); const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
logger.debug("random mission drops:", MissionRewards); logger.debug("random mission drops:", MissionRewards);
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};
const AffiliationMods: IAffiliationMods[] = []; const AffiliationMods: IAffiliationMods[] = [];
let SyndicateXPItemReward; let SyndicateXPItemReward;
let ConquestCompletedMissionsCount;
let missionCompletionCredits = 0; let missionCompletionCredits = 0;
//inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
@ -894,8 +658,7 @@ export const addMissionRewards = async (
missions.Tag != "SolNode761" && // the index missions.Tag != "SolNode761" && // the index
missions.Tag != "SolNode762" && // the index missions.Tag != "SolNode762" && // the index
missions.Tag != "SolNode763" && // the index missions.Tag != "SolNode763" && // the index
missions.Tag != "CrewBattleNode556" && // free flight missions.Tag != "CrewBattleNode556" // free flight
getRotations(rewardInfo).length > 0 // (E)SO should not give credits for only completing zone 1, in which case it has no rewardQualifications (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
) { ) {
const levelCreditReward = getLevelCreditRewards(node); const levelCreditReward = getLevelCreditRewards(node);
missionCompletionCredits += levelCreditReward; missionCompletionCredits += levelCreditReward;
@ -907,14 +670,6 @@ export const addMissionRewards = async (
missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo); missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
} }
if (rewardInfo.sortieTag == "Mission1") {
missionCompletionCredits += 20_000;
} else if (rewardInfo.sortieTag == "Mission2") {
missionCompletionCredits += 30_000;
} else if (rewardInfo.sortieTag == "Final") {
missionCompletionCredits += 50_000;
}
if (missions.Tag == "PlutoToErisJunction") { if (missions.Tag == "PlutoToErisJunction") {
await createMessage(inventory.accountOwnerId, [ await createMessage(inventory.accountOwnerId, [
{ {
@ -931,67 +686,11 @@ export const addMissionRewards = async (
if (rewardInfo.useVaultManifest) { if (rewardInfo.useVaultManifest) {
MissionRewards.push({ MissionRewards.push({
StoreItem: getRandomElement(corruptedMods)!, StoreItem: getRandomElement(corruptedMods),
ItemCount: 1 ItemCount: 1
}); });
} }
if (rewardInfo.ConquestCompleted !== undefined) {
let score = 1;
if (rewardInfo.ConquestHardModeActive === 1) score += 3;
if (rewardInfo.ConquestPersonalModifiersActive !== undefined)
score += rewardInfo.ConquestPersonalModifiersActive;
if (rewardInfo.ConquestEquipmentSuggestionsFulfilled !== undefined)
score += rewardInfo.ConquestEquipmentSuggestionsFulfilled;
score *= rewardInfo.ConquestCompleted + 1;
if (rewardInfo.ConquestCompleted == 2 && rewardInfo.ConquestHardModeActive === 1) score += 1;
logger.debug(`completed conquest mission ${rewardInfo.ConquestCompleted + 1} for a score of ${score}`);
const conquestType = rewardInfo.ConquestType;
const conquestNode =
conquestType == "HexConquest" ? "EchoesHexConquestHardModeUnlocked" : "EntratiLabConquestHardModeUnlocked";
if (score >= 25 && inventory.NodeIntrosCompleted.indexOf(conquestNode) == -1)
inventory.NodeIntrosCompleted.push(conquestNode);
if (conquestType == "HexConquest") {
inventory.EchoesHexConquestCacheScoreMission ??= 0;
if (score > inventory.EchoesHexConquestCacheScoreMission) {
for (const reward of hexConquestRewards) {
if (score >= reward.at && inventory.EchoesHexConquestCacheScoreMission < reward.at) {
const rolled = getRandomReward(reward.pool)!;
logger.debug(`rolled hex conquest reward for reaching ${reward.at} points`, rolled);
MissionRewards.push({
StoreItem: rolled.type,
ItemCount: rolled.itemCount
});
}
}
inventory.EchoesHexConquestCacheScoreMission = score;
}
} else {
inventory.EntratiLabConquestCacheScoreMission ??= 0;
if (score > inventory.EntratiLabConquestCacheScoreMission) {
for (const reward of labConquestRewards) {
if (score >= reward.at && inventory.EntratiLabConquestCacheScoreMission < reward.at) {
const rolled = getRandomReward(reward.pool)!;
logger.debug(`rolled lab conquest reward for reaching ${reward.at} points`, rolled);
MissionRewards.push({
StoreItem: rolled.type,
ItemCount: rolled.itemCount
});
}
}
inventory.EntratiLabConquestCacheScoreMission = score;
}
}
ConquestCompletedMissionsCount = rewardInfo.ConquestCompleted == 2 ? 0 : rewardInfo.ConquestCompleted + 1;
}
for (const reward of MissionRewards) { for (const reward of MissionRewards) {
const inventoryChange = await handleStoreItemAcquisition( const inventoryChange = await handleStoreItemAcquisition(
reward.StoreItem, reward.StoreItem,
@ -1183,16 +882,16 @@ export const addMissionRewards = async (
} }
} }
return { return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward };
inventoryChanges,
MissionRewards,
credits,
AffiliationMods,
SyndicateXPItemReward,
ConquestCompletedMissionsCount
};
}; };
interface IMissionCredits {
MissionCredits: number[];
CreditBonus: number[];
TotalCredits: number[];
DailyMissionBonus?: boolean;
}
//creditBonus is not entirely accurate. //creditBonus is not entirely accurate.
//TODO: consider ActiveBoosters //TODO: consider ActiveBoosters
export const addCredits = ( export const addCredits = (
@ -1369,9 +1068,6 @@ function getRandomMissionDrops(
// Invasion assassination has Phorid has the boss who should drop Nyx parts // Invasion assassination has Phorid has the boss who should drop Nyx parts
// TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic // TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"]; rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
} else if (RewardInfo.sortieId && region.missionIndex != 0) {
// Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes. Assassinations are an exception to this.
rewardManifests = [];
} else { } else {
rewardManifests = region.rewardManifests; rewardManifests = region.rewardManifests;
} }
@ -1520,11 +1216,6 @@ function getRandomMissionDrops(
if (rewardManifests.length != 0) { if (rewardManifests.length != 0) {
logger.debug(`generating random mission rewards`, { rewardManifests, rotations }); logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
} }
if (RewardInfo.rewardSeed) {
if (RewardInfo.rewardSeed != inventory.RewardSeed) {
logger.warn(`RewardSeed mismatch:`, { client: RewardInfo.rewardSeed, database: inventory.RewardSeed });
}
}
const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn); const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn);
rewardManifests.forEach(name => { rewardManifests.forEach(name => {
const table = ExportRewards[name]; const table = ExportRewards[name];

View File

@ -1,14 +1,9 @@
import { PersonalRooms } from "@/src/models/personalRoomsModel"; import { PersonalRooms } from "@/src/models/personalRoomsModel";
import { addItem, getInventory } from "@/src/services/inventoryService"; import { addItem, getInventory } from "@/src/services/inventoryService";
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes"; import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
import { IGardeningDatabase } from "../types/shipTypes";
import { getRandomElement } from "./rngService";
export const getPersonalRooms = async ( export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
accountId: string, const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
projection?: string
): Promise<TPersonalRoomsDatabaseDocument> => {
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
if (!personalRooms) { if (!personalRooms) {
throw new Error(`personal rooms not found for account ${accountId}`); throw new Error(`personal rooms not found for account ${accountId}`);
@ -30,64 +25,3 @@ export const updateShipFeature = async (accountId: string, shipFeature: string):
await addItem(inventory, shipFeature, -1); await addItem(inventory, shipFeature, -1);
await inventory.save(); await inventory.save();
}; };
export const createGarden = (): IGardeningDatabase => {
const plantTypes = [
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantA",
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantB",
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantC",
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantD",
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantE",
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantF"
];
const endTime = new Date((Math.trunc(Date.now() / 1000) + 79200) * 1000); // Plants will take 22 hours to grow
return {
Planters: [
{
Name: "Garden0",
Plants: [
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 0
},
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 1
}
]
},
{
Name: "Garden1",
Plants: [
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 0
},
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 1
}
]
},
{
Name: "Garden2",
Plants: [
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 0
},
{
PlantType: getRandomElement(plantTypes)!,
EndTime: endTime,
PlotIndex: 1
}
]
}
]
};
};

View File

@ -66,18 +66,6 @@ export const handlePurchase = async (
if (!offer) { if (!offer) {
throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`); throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`);
} }
if (offer.RegularPrice) {
combineInventoryChanges(
prePurchaseInventoryChanges,
updateCurrency(inventory, offer.RegularPrice[0], false)
);
}
if (offer.PremiumPrice) {
combineInventoryChanges(
prePurchaseInventoryChanges,
updateCurrency(inventory, offer.PremiumPrice[0], true)
);
}
if (offer.ItemPrices) { if (offer.ItemPrices) {
handleItemPrices( handleItemPrices(
inventory, inventory,
@ -182,9 +170,6 @@ export const handlePurchase = async (
purchaseResponse.InventoryChanges, purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.RegularPrice, false) updateCurrency(inventory, offer.RegularPrice, false)
); );
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
throw new Error(`vendor purchase should not have an expected price`);
}
const invItem: IMiscItem = { const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks", ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
@ -238,18 +223,12 @@ export const handlePurchase = async (
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!]; const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem); const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
if (offer) { if (offer) {
if (typeof offer.credits == "number") { if (offer.credits) {
combineInventoryChanges( combineInventoryChanges(
purchaseResponse.InventoryChanges, purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.credits, false) updateCurrency(inventory, offer.credits, false)
); );
} }
if (typeof offer.platinum == "number") {
combineInventoryChanges(
purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.platinum, true)
);
}
if (offer.itemPrices) { if (offer.itemPrices) {
handleItemPrices( handleItemPrices(
inventory, inventory,
@ -260,9 +239,6 @@ export const handlePurchase = async (
} }
} }
} }
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
throw new Error(`vendor purchase should not have an expected price`);
}
break; break;
case 18: { case 18: {
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) { if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {

View File

@ -6,7 +6,7 @@ export interface IRngResult {
probability: number; probability: number;
} }
export const getRandomElement = <T>(arr: T[]): T | undefined => { export const getRandomElement = <T>(arr: T[]): T => {
return arr[Math.floor(Math.random() * arr.length)]; return arr[Math.floor(Math.random() * arr.length)];
}; };
@ -18,10 +18,7 @@ export const getRandomInt = (min: number, max: number): number => {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
}; };
export const getRewardAtPercentage = <T extends { probability: number }>( const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
pool: T[],
percentage: number
): T | undefined => {
if (pool.length == 0) return; if (pool.length == 0) return;
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0); const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
@ -100,20 +97,12 @@ export class CRng {
} }
randomInt(min: number, max: number): number { randomInt(min: number, max: number): number {
const diff = max - min; min = Math.ceil(min);
if (diff != 0) { max = Math.floor(max);
if (diff < 0) { return Math.floor(this.random() * (max - min + 1)) + min;
throw new Error(`max must be greater than min`);
}
if (diff > 0x3fffffff) {
throw new Error(`insufficient entropy`);
}
min += Math.floor(this.random() * (diff + 1));
}
return min;
} }
randomElement<T>(arr: T[]): T | undefined { randomElement<T>(arr: T[]): T {
return arr[Math.floor(this.random() * arr.length)]; return arr[Math.floor(this.random() * arr.length)];
} }
@ -145,7 +134,7 @@ export class SRng {
return min; return min;
} }
randomElement<T>(arr: T[]): T | undefined { randomElement<T>(arr: T[]): T {
return arr[this.randomInt(0, arr.length - 1)]; return arr[this.randomInt(0, arr.length - 1)];
} }

View File

@ -161,11 +161,6 @@ export const handleInventoryItemConfigChange = async (
} }
break; break;
} }
case "LotusCustomization": {
logger.debug(`saved LotusCustomization`, equipmentChanges.LotusCustomization);
inventory.LotusCustomization = equipmentChanges.LotusCustomization;
break;
}
default: { default: {
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") { if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
logger.debug(`general Item config saved of type ${equipmentName}`, { logger.debug(`general Item config saved of type ${equipmentName}`, {

View File

@ -1,12 +1,17 @@
import { unixTimesInMs } from "@/src/constants/timeConstants"; import { unixTimesInMs } from "@/src/constants/timeConstants";
import { catBreadHash } from "@/src/helpers/stringHelpers";
import { CRng, mixSeeds } from "@/src/services/rngService"; import { CRng, mixSeeds } from "@/src/services/rngService";
import { IMongoDate } from "@/src/types/commonTypes"; import { IMongoDate } from "@/src/types/commonTypes";
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes"; import {
import { ExportVendors, IRange } from "warframe-public-export-plus"; IItemManifestPreprocessed,
IRawVendorManifest,
IVendorInfo,
IVendorManifestPreprocessed
} from "@/src/types/vendorTypes";
import { ExportVendors } from "warframe-public-export-plus";
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json"; import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json"; import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
import DeimosFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosFishmongerVendorManifest.json";
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json"; import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json"; import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json"; import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
@ -18,22 +23,26 @@ import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorIn
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json"; import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json"; import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json"; import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
import GuildAdvertisementVendorManifest from "@/static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json";
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json"; import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json"; import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json"; import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json"; import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
import OstronFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronFishmongerVendorManifest.json";
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json"; import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json"; import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json"; import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
import SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json";
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json"; import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json";
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json"; import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json"; import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json"; import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
const rawVendorManifests: IVendorManifest[] = [ const rawVendorManifests: IRawVendorManifest[] = [
ArchimedeanVendorManifest, ArchimedeanVendorManifest,
DeimosEntratiFragmentVendorProductsManifest, DeimosEntratiFragmentVendorProductsManifest,
DeimosFishmongerVendorManifest,
DeimosHivemindCommisionsManifestFishmonger, DeimosHivemindCommisionsManifestFishmonger,
DeimosHivemindCommisionsManifestPetVendor, DeimosHivemindCommisionsManifestPetVendor,
DeimosHivemindCommisionsManifestProspector, DeimosHivemindCommisionsManifestProspector,
@ -45,22 +54,25 @@ const rawVendorManifests: IVendorManifest[] = [
DuviriAcrithisVendorManifest, DuviriAcrithisVendorManifest,
EntratiLabsEntratiLabsCommisionsManifest, EntratiLabsEntratiLabsCommisionsManifest,
EntratiLabsEntratiLabVendorManifest, EntratiLabsEntratiLabVendorManifest,
GuildAdvertisementVendorManifest, // uses preprocessing
HubsIronwakeDondaVendorManifest, // uses preprocessing HubsIronwakeDondaVendorManifest, // uses preprocessing
HubsRailjackCrewMemberVendorManifest, HubsRailjackCrewMemberVendorManifest,
MaskSalesmanManifest, MaskSalesmanManifest,
Nova1999ConquestShopManifest, Nova1999ConquestShopManifest,
OstronFishmongerVendorManifest,
OstronPetVendorManifest, OstronPetVendorManifest,
OstronProspectorVendorManifest, OstronProspectorVendorManifest,
RadioLegionIntermission12VendorManifest, RadioLegionIntermission12VendorManifest,
SolarisDebtTokenVendorManifest,
SolarisDebtTokenVendorRepossessionsManifest, SolarisDebtTokenVendorRepossessionsManifest,
SolarisFishmongerVendorManifest,
SolarisProspectorVendorManifest, SolarisProspectorVendorManifest,
Temple1999VendorManifest,
TeshinHardModeVendorManifest, // uses preprocessing TeshinHardModeVendorManifest, // uses preprocessing
ZarimanCommisionsManifestArchimedean ZarimanCommisionsManifestArchimedean
]; ];
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> { interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
cycleOffset?: number; cycleStart: number;
cycleDuration: number; cycleDuration: number;
} }
@ -68,37 +80,30 @@ const generatableVendors: IGeneratableVendorInfo[] = [
{ {
_id: { $oid: "67dadc30e4b6e0e5979c8d84" }, _id: { $oid: "67dadc30e4b6e0e5979c8d84" },
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest", TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
PropertyTextHash: "77093DD05A8561A022DEC9A4B9BB4A56",
RandomSeedType: "VRST_WEAPON", RandomSeedType: "VRST_WEAPON",
RequiredGoalTag: "", RequiredGoalTag: "",
WeaponUpgradeValueAttenuationExponent: 2.25, WeaponUpgradeValueAttenuationExponent: 2.25,
cycleOffset: 1740960000_000, cycleStart: 1740960000_000,
cycleDuration: 4 * unixTimesInMs.day cycleDuration: 4 * unixTimesInMs.day
}, },
{ {
_id: { $oid: "60ad3b6ec96976e97d227e19" }, _id: { $oid: "60ad3b6ec96976e97d227e19" },
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/PerrinSequenceWeaponVendorManifest", TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/PerrinSequenceWeaponVendorManifest",
PropertyTextHash: "34F8CF1DFF745F0D67433A5EF0A03E70",
RandomSeedType: "VRST_WEAPON", RandomSeedType: "VRST_WEAPON",
WeaponUpgradeValueAttenuationExponent: 2.25, WeaponUpgradeValueAttenuationExponent: 2.25,
cycleOffset: 1744934400_000, cycleStart: 1744934400_000,
cycleDuration: 4 * unixTimesInMs.day cycleDuration: 4 * unixTimesInMs.day
},
{
_id: { $oid: "61ba123467e5d37975aeeb03" },
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
RandomSeedType: "VRST_FLAVOUR_TEXT",
cycleDuration: unixTimesInMs.week // TODO: Auto-detect this based on the items, so we don't need to specify it explicitly.
} }
// { // {
// _id: { $oid: "5dbb4c41e966f7886c3ce939" }, // _id: { $oid: "5dbb4c41e966f7886c3ce939" },
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest" // TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest",
// PropertyTextHash: "62B64A8065B7C0FA345895D4BC234621"
// } // }
]; ];
const getVendorOid = (typeName: string): string => { export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPreprocessed | undefined => {
return "5be4a159b144f3cd" + catBreadHash(typeName).toString(16).padStart(8, "0");
};
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
for (const vendorManifest of rawVendorManifests) { for (const vendorManifest of rawVendorManifests) {
if (vendorManifest.VendorInfo.TypeName == typeName) { if (vendorManifest.VendorInfo.TypeName == typeName) {
return preprocessVendorManifest(vendorManifest); return preprocessVendorManifest(vendorManifest);
@ -109,18 +114,10 @@ export const getVendorManifestByTypeName = (typeName: string): IVendorManifest |
return generateVendorManifest(vendorInfo); return generateVendorManifest(vendorInfo);
} }
} }
if (typeName in ExportVendors) {
return generateVendorManifest({
_id: { $oid: getVendorOid(typeName) },
TypeName: typeName,
RandomSeedType: ExportVendors[typeName].randomSeedType,
cycleDuration: unixTimesInMs.hour
});
}
return undefined; return undefined;
}; };
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => { export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed | undefined => {
for (const vendorManifest of rawVendorManifests) { for (const vendorManifest of rawVendorManifests) {
if (vendorManifest.VendorInfo._id.$oid == oid) { if (vendorManifest.VendorInfo._id.$oid == oid) {
return preprocessVendorManifest(vendorManifest); return preprocessVendorManifest(vendorManifest);
@ -131,34 +128,32 @@ export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined
return generateVendorManifest(vendorInfo); return generateVendorManifest(vendorInfo);
} }
} }
for (const [typeName, manifest] of Object.entries(ExportVendors)) {
const typeNameOid = getVendorOid(typeName);
if (typeNameOid == oid) {
return generateVendorManifest({
_id: { $oid: typeNameOid },
TypeName: typeName,
RandomSeedType: manifest.randomSeedType,
cycleDuration: unixTimesInMs.hour
});
}
}
return undefined; return undefined;
}; };
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => { const preprocessVendorManifest = (originalManifest: IRawVendorManifest): IVendorManifestPreprocessed => {
if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) { if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
const manifest = structuredClone(originalManifest); const manifest = structuredClone(originalManifest);
const info = manifest.VendorInfo; const info = manifest.VendorInfo;
refreshExpiry(info.Expiry); refreshExpiry(info.Expiry);
for (const offer of info.ItemManifest) { for (const offer of info.ItemManifest) {
refreshExpiry(offer.Expiry); const iteration = refreshExpiry(offer.Expiry);
if (offer.ItemPrices) {
for (const price of offer.ItemPrices) {
if (typeof price.ItemType != "string") {
const itemSeed = parseInt(offer.Id.$oid.substring(16), 16);
const rng = new CRng(mixSeeds(itemSeed, iteration));
price.ItemType = rng.randomElement(price.ItemType);
} }
return manifest;
} }
return originalManifest; }
}
return manifest as IVendorManifestPreprocessed;
}
return originalManifest as IVendorManifestPreprocessed;
}; };
const refreshExpiry = (expiry: IMongoDate): void => { const refreshExpiry = (expiry: IMongoDate): number => {
const period = parseInt(expiry.$date.$numberLong); const period = parseInt(expiry.$date.$numberLong);
if (Date.now() >= period) { if (Date.now() >= period) {
const epoch = 1734307200_000; // Monday (for weekly schedules) const epoch = 1734307200_000; // Monday (for weekly schedules)
@ -166,143 +161,65 @@ const refreshExpiry = (expiry: IMongoDate): void => {
const start = epoch + iteration * period; const start = epoch + iteration * period;
const end = start + period; const end = start + period;
expiry.$date.$numberLong = end.toString(); expiry.$date.$numberLong = end.toString();
return iteration;
} }
return 0;
}; };
const toRange = (value: IRange | number): IRange => { const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifestPreprocessed => {
if (typeof value == "number") { const EPOCH = vendorInfo.cycleStart;
return { minValue: value, maxValue: value };
}
return value;
};
const vendorInfoCache: Record<string, IVendorInfo> = {};
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
if (!(vendorInfo.TypeName in vendorInfoCache)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
vendorInfoCache[vendorInfo.TypeName] = {
...clientVendorInfo,
ItemManifest: [],
Expiry: { $date: { $numberLong: "0" } }
};
}
const processed = vendorInfoCache[vendorInfo.TypeName];
if (Date.now() >= parseInt(processed.Expiry.$date.$numberLong)) {
// Remove expired offers
for (let i = 0; i != processed.ItemManifest.length; ) {
if (Date.now() >= parseInt(processed.ItemManifest[i].Expiry.$date.$numberLong)) {
processed.ItemManifest.splice(i, 1);
} else {
++i;
}
}
// Add new offers
const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
const cycleOffset = vendorInfo.cycleOffset ?? 1734307200_000;
const cycleDuration = vendorInfo.cycleDuration;
const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
const manifest = ExportVendors[vendorInfo.TypeName]; const manifest = ExportVendors[vendorInfo.TypeName];
const offersToAdd = [];
if (manifest.numItems && !manifest.isOneBinPerCycle) {
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) {
// TODO: Consider per-bin item limits
// TODO: Consider item probability weightings
offersToAdd.push(rng.randomElement(manifest.items)!);
}
} else {
let binThisCycle; let binThisCycle;
if (manifest.isOneBinPerCycle) { if (manifest.isOneBinPerCycle) {
const cycleDuration = vendorInfo.cycleDuration; // manifest.items[0].durationHours! * 3600_000;
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now. binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
} }
for (const rawItem of manifest.items) { const items: IItemManifestPreprocessed[] = [];
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) { let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
offersToAdd.push(rawItem); for (let i = 0; i != manifest.items.length; ++i) {
const rawItem = manifest.items[i];
if (manifest.isOneBinPerCycle && rawItem.bin != binThisCycle) {
continue;
} }
const cycleDuration = vendorInfo.cycleDuration; // rawItem.durationHours! * 3600_000;
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
const cycleStart = EPOCH + cycleIndex * cycleDuration;
const cycleEnd = cycleStart + cycleDuration;
if (soonestOfferExpiry > cycleEnd) {
soonestOfferExpiry = cycleEnd;
} }
const rng = new CRng(cycleIndex);
// For most vendors, the offers seem to roughly be in reverse order from the manifest. Coda weapons are an odd exception. rng.churnSeed(i);
if (!manifest.isOneBinPerCycle) { /*for (let j = -1; j != rawItem.duplicates; ++j)*/ {
offersToAdd.reverse(); const item: IItemManifestPreprocessed = {
}
}
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
for (const rawItem of offersToAdd) {
const durationHoursRange = toRange(rawItem.durationHours);
const expiry =
cycleStart +
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
const item: IItemManifest = {
StoreItem: rawItem.storeItem, StoreItem: rawItem.storeItem,
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })), ItemPrices: rawItem.itemPrices!.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
Bin: "BIN_" + rawItem.bin, Bin: "BIN_" + rawItem.bin,
QuantityMultiplier: 1, QuantityMultiplier: 1,
Expiry: { $date: { $numberLong: expiry.toString() } }, Expiry: { $date: { $numberLong: cycleEnd.toString() } },
AllowMultipurchase: false, AllowMultipurchase: false,
Id: { Id: {
$oid: $oid:
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + i.toString(16).padStart(8, "0") +
vendorInfo._id.$oid.substring(8, 16) + vendorInfo._id.$oid.substring(8, 16) +
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0") + rng.randomInt(0, 0xffffffff).toString(16).padStart(8, "0")
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0")
} }
}; };
if (rawItem.numRandomItemPrices) {
item.ItemPrices = [];
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
let itemPrice: { type: string; count: IRange };
do {
itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin])!;
} while (item.ItemPrices.find(x => x.ItemType == itemPrice.type));
item.ItemPrices.push({
ItemType: itemPrice.type,
ItemCount: rng.randomInt(itemPrice.count.minValue, itemPrice.count.maxValue),
ProductCategory: "MiscItems"
});
}
}
if (rawItem.credits) {
const value =
typeof rawItem.credits == "number"
? rawItem.credits
: rng.randomInt(
rawItem.credits.minValue / rawItem.credits.step,
rawItem.credits.maxValue / rawItem.credits.step
) * rawItem.credits.step;
item.RegularPrice = [value, value];
}
if (rawItem.platinum) {
const value =
typeof rawItem.platinum == "number"
? rawItem.platinum
: rng.randomInt(rawItem.platinum.minValue, rawItem.platinum.maxValue);
item.PremiumPrice = [value, value];
}
if (vendorInfo.RandomSeedType) { if (vendorInfo.RandomSeedType) {
item.LocTagRandSeed = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff); item.LocTagRandSeed =
if (vendorInfo.RandomSeedType == "VRST_WEAPON") { (BigInt(rng.randomInt(0, 0xffffffff)) << 32n) | BigInt(rng.randomInt(0, 0xffffffff));
const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff); }
item.LocTagRandSeed = (BigInt(highDword) << 32n) | (BigInt(item.LocTagRandSeed) & 0xffffffffn); items.push(item);
} }
} }
processed.ItemManifest.push(item); // eslint-disable-next-line @typescript-eslint/no-unused-vars
} const { cycleStart, cycleDuration, ...clientVendorInfo } = vendorInfo;
// Update vendor expiry
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
for (const offer of processed.ItemManifest) {
const offerExpiry = parseInt(offer.Expiry.$date.$numberLong);
if (soonestOfferExpiry > offerExpiry) {
soonestOfferExpiry = offerExpiry;
}
}
processed.Expiry.$date.$numberLong = soonestOfferExpiry.toString();
}
return { return {
VendorInfo: processed VendorInfo: {
...clientVendorInfo,
ItemManifest: items,
Expiry: { $date: { $numberLong: soonestOfferExpiry.toString() } }
}
}; };
}; };

View File

@ -1,20 +1,10 @@
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json"; import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.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 { CRng } from "@/src/services/rngService"; import { CRng } from "@/src/services/rngService";
import { eMissionType, ExportNightwave, ExportRegions, IRegion } from "warframe-public-export-plus"; import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus";
import { import { ICalendarDay, ICalendarSeason, ILiteSortie, ISeasonChallenge, IWorldState } from "../types/worldStateTypes";
ICalendarDay,
ICalendarEvent,
ICalendarSeason,
ILiteSortie,
ISeasonChallenge,
ISortieMission,
IWorldState
} from "../types/worldStateTypes";
const sortieBosses = [ const sortieBosses = [
"SORTIE_BOSS_HYENA", "SORTIE_BOSS_HYENA",
@ -51,27 +41,21 @@ const sortieBossToFaction: Record<string, string> = {
SORTIE_BOSS_PHORID: "FC_INFESTATION", SORTIE_BOSS_PHORID: "FC_INFESTATION",
SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION", SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION",
SORTIE_BOSS_INFALAD: "FC_INFESTATION", SORTIE_BOSS_INFALAD: "FC_INFESTATION",
SORTIE_BOSS_CORRUPTED_VOR: "FC_OROKIN" SORTIE_BOSS_CORRUPTED_VOR: "FC_CORRUPTED"
}; };
const sortieFactionToSystemIndexes: Record<string, number[]> = { const sortieFactionToSystemIndexes: Record<string, number[]> = {
FC_GRINEER: [0, 2, 3, 5, 6, 9, 11, 18], FC_GRINEER: [0, 2, 3, 5, 6, 9, 11, 18],
FC_CORPUS: [1, 4, 7, 8, 12, 15], FC_CORPUS: [1, 4, 7, 8, 12, 15],
FC_INFESTATION: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15], FC_INFESTATION: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15],
FC_OROKIN: [14] FC_CORRUPTED: [14]
}; };
const sortieFactionToFactionIndexes: Record<string, number[]> = { const sortieFactionToFactionIndexes: Record<string, number[]> = {
FC_GRINEER: [0], FC_GRINEER: [0],
FC_CORPUS: [1], FC_CORPUS: [1],
FC_INFESTATION: [0, 1, 2], FC_INFESTATION: [0, 1, 2],
FC_OROKIN: [3] FC_CORRUPTED: [3]
};
const sortieFactionToSpecialMissionTileset: Record<string, string> = {
FC_GRINEER: "GrineerGalleonTileset",
FC_CORPUS: "CorpusShipTileset",
FC_INFESTATION: "CorpusShipTileset"
}; };
const sortieBossNode: Record<string, string> = { const sortieBossNode: Record<string, string> = {
@ -190,7 +174,18 @@ const pushSyndicateMissions = (
idSuffix: string, idSuffix: string,
syndicateTag: string syndicateTag: string
): void => { ): void => {
const nodeOptions: string[] = [...syndicateMissions]; const nodeOptions: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.name.indexOf("Archwing") == -1 && // no archwing
value.systemIndex != 23 && // no 1999 stuff
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
value.missionIndex != 23 && // no junctions
value.missionIndex <= 28 // no railjack or some such
) {
nodeOptions.push(key);
}
}
const rng = new CRng(seed); const rng = new CRng(seed);
const nodes: string[] = []; const nodes: string[] = [];
@ -203,7 +198,7 @@ const pushSyndicateMissions = (
const dayStart = getSortieTime(day); const dayStart = getSortieTime(day);
const dayEnd = getSortieTime(day + 1); const dayEnd = getSortieTime(day + 1);
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + idSuffix }, _id: { $oid: Math.trunc(dayStart / 1000).toString(16) + idSuffix },
Activation: { $date: { $numberLong: dayStart.toString() } }, Activation: { $date: { $numberLong: dayStart.toString() } },
Expiry: { $date: { $numberLong: dayEnd.toString() } }, Expiry: { $date: { $numberLong: dayEnd.toString() } },
Tag: syndicateTag, Tag: syndicateTag,
@ -222,10 +217,9 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
return; return;
} }
const seed = new CRng(day).randomInt(0, 0xffff); const rng = new CRng(day);
const rng = new CRng(seed);
const boss = rng.randomElement(sortieBosses)!; const boss = rng.randomElement(sortieBosses);
const modifiers = [ const modifiers = [
"SORTIE_MODIFIER_LOW_ENERGY", "SORTIE_MODIFIER_LOW_ENERGY",
@ -267,69 +261,47 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
if ( if (
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) && sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) && sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
key in sortieTilesets value.name.indexOf("Archwing") == -1 &&
) { value.missionIndex != 0 && // Exclude MT_ASSASSINATION
if ( value.missionIndex != 10 && // Exclude MT_PVP (for relays)
value.missionIndex != 5 && // Sorties do not have capture missions value.missionIndex != 21 && // Exclude MT_PURIFY
!availableMissionIndexes.includes(value.missionIndex) value.missionIndex != 22 && // Exclude MT_ARENA
value.missionIndex != 23 && // Exclude MT_JUNCTION
value.missionIndex <= 28
) { ) {
if (!availableMissionIndexes.includes(value.missionIndex)) {
availableMissionIndexes.push(value.missionIndex); availableMissionIndexes.push(value.missionIndex);
} }
nodes.push(key); nodes.push(key);
} }
} }
const specialMissionTypes = [1, 3, 5, 9]; const selectedNodes: { missionType: string; modifierType: string; node: string }[] = [];
if (!(sortieBossToFaction[boss] in sortieFactionToSpecialMissionTileset)) {
for (const missionType of specialMissionTypes) {
const i = availableMissionIndexes.indexOf(missionType);
if (i != -1) {
availableMissionIndexes.splice(i, 1);
}
}
}
const selectedNodes: ISortieMission[] = [];
const missionTypes = new Set(); const missionTypes = new Set();
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
let randomIndex; const randomIndex = rng.randomInt(0, nodes.length - 1);
let node; const node = nodes[randomIndex];
let missionIndex; let missionIndex = ExportRegions[node].missionIndex;
do {
randomIndex = rng.randomInt(0, nodes.length - 1);
node = nodes[randomIndex];
missionIndex = ExportRegions[node].missionIndex; if (
if (missionIndex != 28) { !["SolNode404", "SolNode411"].includes(node) && // for some reason the game doesn't like missionType changes for these missions
missionIndex = rng.randomElement(availableMissionIndexes)!; missionIndex != 28 &&
rng.randomInt(0, 2) == 2
) {
missionIndex = rng.randomElement(availableMissionIndexes);
} }
} while (
specialMissionTypes.indexOf(missionIndex) != -1 &&
sortieTilesets[node as keyof typeof sortieTilesets] !=
sortieFactionToSpecialMissionTileset[sortieBossToFaction[boss]]
);
if (i == 2 && rng.randomInt(0, 2) == 2) { if (i == 2 && rng.randomInt(0, 2) == 2) {
const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY"); const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY");
const modifierType = rng.randomElement(filteredModifiers)!; const modifierType = rng.randomElement(filteredModifiers);
if (boss == "SORTIE_BOSS_PHORID") { if (boss == "SORTIE_BOSS_PHORID") {
selectedNodes.push({ selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node });
missionType: "MT_ASSASSINATION",
modifierType,
node,
tileset: sortieTilesets[node as keyof typeof sortieTilesets]
});
nodes.splice(randomIndex, 1); nodes.splice(randomIndex, 1);
continue; continue;
} else if (sortieBossNode[boss]) { } else if (sortieBossNode[boss]) {
selectedNodes.push({ selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node: sortieBossNode[boss] });
missionType: "MT_ASSASSINATION",
modifierType,
node: sortieBossNode[boss],
tileset: sortieTilesets[sortieBossNode[boss] as keyof typeof sortieTilesets]
});
continue; continue;
} }
} }
@ -346,24 +318,19 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
? modifiers.filter(mod => mod != "SORTIE_MODIFIER_HAZARD_RADIATION") ? modifiers.filter(mod => mod != "SORTIE_MODIFIER_HAZARD_RADIATION")
: modifiers; : modifiers;
const modifierType = rng.randomElement(filteredModifiers)!; const modifierType = rng.randomElement(filteredModifiers);
selectedNodes.push({ selectedNodes.push({ missionType, modifierType, node });
missionType,
modifierType,
node,
tileset: sortieTilesets[node as keyof typeof sortieTilesets]
});
nodes.splice(randomIndex, 1); nodes.splice(randomIndex, 1);
missionTypes.add(missionType); missionTypes.add(missionType);
} }
worldState.Sorties.push({ worldState.Sorties.push({
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "d4d932c97c0a3acd" }, _id: { $oid: Math.trunc(dayStart / 1000).toString(16) + "d4d932c97c0a3acd" },
Activation: { $date: { $numberLong: dayStart.toString() } }, Activation: { $date: { $numberLong: dayStart.toString() } },
Expiry: { $date: { $numberLong: dayEnd.toString() } }, Expiry: { $date: { $numberLong: dayEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards", Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
Seed: seed, Seed: day,
Boss: boss, Boss: boss,
Variants: selectedNodes Variants: selectedNodes
}); });
@ -383,13 +350,13 @@ const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
const getSeasonDailyChallenge = (day: number): ISeasonChallenge => { const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
const dayStart = EPOCH + day * 86400000; const dayStart = EPOCH + day * 86400000;
const dayEnd = EPOCH + (day + 3) * 86400000; const dayEnd = EPOCH + (day + 3) * 86400000;
const rng = new CRng(new CRng(day).randomInt(0, 0xffff)); const rng = new CRng(day);
return { return {
_id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") }, _id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") },
Daily: true, Daily: true,
Activation: { $date: { $numberLong: dayStart.toString() } }, Activation: { $date: { $numberLong: dayStart.toString() } },
Expiry: { $date: { $numberLong: dayEnd.toString() } }, Expiry: { $date: { $numberLong: dayEnd.toString() } },
Challenge: rng.randomElement(dailyChallenges)! Challenge: rng.randomElement(dailyChallenges)
}; };
}; };
@ -403,12 +370,12 @@ const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge =>
const weekStart = EPOCH + week * 604800000; const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000; const weekEnd = weekStart + 604800000;
const challengeId = week * 7 + id; const challengeId = week * 7 + id;
const rng = new CRng(new CRng(challengeId).randomInt(0, 0xffff)); const rng = new CRng(challengeId);
return { return {
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") }, _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } }, Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } }, Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: rng.randomElement(weeklyChallenges)! Challenge: rng.randomElement(weeklyChallenges)
}; };
}; };
@ -420,12 +387,12 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
const weekStart = EPOCH + week * 604800000; const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000; const weekEnd = weekStart + 604800000;
const challengeId = week * 7 + id; const challengeId = week * 7 + id;
const rng = new CRng(new CRng(challengeId).randomInt(0, 0xffff)); const rng = new CRng(challengeId);
return { return {
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") }, _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } }, Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } }, Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: rng.randomElement(weeklyHardChallenges)! Challenge: rng.randomElement(weeklyHardChallenges)
}; };
}; };
@ -472,8 +439,8 @@ const birthdays: number[] = [
const getCalendarSeason = (week: number): ICalendarSeason => { const getCalendarSeason = (week: number): ICalendarSeason => {
const seasonIndex = week % 4; const seasonIndex = week % 4;
const seasonDay1 = [1, 91, 182, 274][seasonIndex]; const seasonDay1 = seasonIndex * 90 + 1;
const seasonDay91 = seasonDay1 + 90; const seasonDay91 = seasonIndex * 90 + 91;
const eventDays: ICalendarDay[] = []; const eventDays: ICalendarDay[] = [];
for (const day of birthdays) { for (const day of birthdays) {
if (day < seasonDay1) { if (day < seasonDay1) {
@ -485,7 +452,7 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
//logger.debug(`birthday on day ${day}`); //logger.debug(`birthday on day ${day}`);
eventDays.push({ day, events: [] }); // This is how CET_PLOT looks in worldState as of around 38.5.0 eventDays.push({ day, events: [] }); // This is how CET_PLOT looks in worldState as of around 38.5.0
} }
const rng = new CRng(new CRng(week).randomInt(0, 0xffff)); const rng = new CRng(week);
const challenges = [ const challenges = [
"/Lotus/Types/Challenges/Calendar1999/CalendarKillEnemiesEasy", "/Lotus/Types/Challenges/Calendar1999/CalendarKillEnemiesEasy",
"/Lotus/Types/Challenges/Calendar1999/CalendarKillEnemiesMedium", "/Lotus/Types/Challenges/Calendar1999/CalendarKillEnemiesMedium",
@ -532,12 +499,8 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
challengeDay = rng.randomInt(chunkDay1, chunkDay13); challengeDay = rng.randomInt(chunkDay1, chunkDay13);
} while (birthdays.indexOf(challengeDay) != -1); } while (birthdays.indexOf(challengeDay) != -1);
let challengeIndex; const challengeIndex = rng.randomInt(0, challenges.length - 1);
let challenge; const challenge = challenges[challengeIndex];
do {
challengeIndex = rng.randomInt(0, challenges.length - 1);
challenge = challenges[challengeIndex];
} while (i < 2 && !challenge.endsWith("Easy")); // First 2 challenges should be easy
challenges.splice(challengeIndex, 1); challenges.splice(challengeIndex, 1);
//logger.debug(`challenge on day ${challengeDay}`); //logger.debug(`challenge on day ${challengeDay}`);
@ -584,100 +547,69 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
"/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalViolet" "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalViolet"
]; ];
for (let i = 0; i != rewardRanges.length - 1; ++i) { for (let i = 0; i != rewardRanges.length - 1; ++i) {
const events: ICalendarEvent[] = [];
for (let j = 0; j != 2; ++j) {
const rewardIndex = rng.randomInt(0, rewards.length - 1); const rewardIndex = rng.randomInt(0, rewards.length - 1);
events.push({ type: "CET_REWARD", reward: rewards[rewardIndex] }); const reward = rewards[rewardIndex];
rewards.splice(rewardIndex, 1); rewards.splice(rewardIndex, 1);
}
//logger.debug(`trying to fit rewards between day ${rewardRanges[i]} and ${rewardRanges[i + 1]}`); //logger.debug(`trying to fit a reward between day ${rewardRanges[i]} and ${rewardRanges[i + 1]}`);
let day: number; let day: number;
do { do {
day = rng.randomInt(rewardRanges[i] + 1, rewardRanges[i + 1] - 1); day = rng.randomInt(rewardRanges[i] + 1, rewardRanges[i + 1] - 1);
} while (eventDays.find(x => x.day == day)); } while (eventDays.find(x => x.day == day));
eventDays.push({ day, events }); eventDays.push({ day, events: [{ type: "CET_REWARD", reward }] });
} }
const upgradesByHexMember = [ const upgrades = [
[
"/Lotus/Upgrades/Calendar/AttackAndMovementSpeedOnCritMelee",
"/Lotus/Upgrades/Calendar/ElectricalDamageOnBulletJump",
"/Lotus/Upgrades/Calendar/ElectricDamagePerDistance",
"/Lotus/Upgrades/Calendar/ElectricStatusDamageAndChance",
"/Lotus/Upgrades/Calendar/OvershieldCap",
"/Lotus/Upgrades/Calendar/SpeedBuffsWhenAirborne"
],
[
"/Lotus/Upgrades/Calendar/AbilityStrength",
"/Lotus/Upgrades/Calendar/EnergyOrbToAbilityRange",
"/Lotus/Upgrades/Calendar/MagnetStatusPull",
"/Lotus/Upgrades/Calendar/MagnitizeWithinRangeEveryXCasts",
"/Lotus/Upgrades/Calendar/PowerStrengthAndEfficiencyPerEnergySpent",
"/Lotus/Upgrades/Calendar/SharedFreeAbilityEveryXCasts"
],
[
"/Lotus/Upgrades/Calendar/EnergyWavesOnCombo",
"/Lotus/Upgrades/Calendar/FinisherChancePerComboMultiplier",
"/Lotus/Upgrades/Calendar/MeleeAttackSpeed",
"/Lotus/Upgrades/Calendar/MeleeCritChance", "/Lotus/Upgrades/Calendar/MeleeCritChance",
"/Lotus/Upgrades/Calendar/MeleeSlideFowardMomentumOnEnemyHit", "/Lotus/Upgrades/Calendar/MeleeAttackSpeed",
"/Lotus/Upgrades/Calendar/RadialJavelinOnHeavy" "/Lotus/Upgrades/Calendar/EnergyOrbToAbilityRange",
], "/Lotus/Upgrades/Calendar/AbilityStrength",
[
"/Lotus/Upgrades/Calendar/Armor", "/Lotus/Upgrades/Calendar/Armor",
"/Lotus/Upgrades/Calendar/CloneActiveCompanionForEnergySpent",
"/Lotus/Upgrades/Calendar/CompanionDamage",
"/Lotus/Upgrades/Calendar/CompanionsBuffNearbyPlayer",
"/Lotus/Upgrades/Calendar/CompanionsRadiationChance",
"/Lotus/Upgrades/Calendar/RadiationProcOnTakeDamage", "/Lotus/Upgrades/Calendar/RadiationProcOnTakeDamage",
"/Lotus/Upgrades/Calendar/ReviveEnemyAsSpectreOnKill" "/Lotus/Upgrades/Calendar/CompanionDamage",
],
[
"/Lotus/Upgrades/Calendar/EnergyOrbsGrantShield",
"/Lotus/Upgrades/Calendar/EnergyRestoration",
"/Lotus/Upgrades/Calendar/ExplodingHealthOrbs",
"/Lotus/Upgrades/Calendar/GenerateOmniOrbsOnWeakKill",
"/Lotus/Upgrades/Calendar/HealingEffects",
"/Lotus/Upgrades/Calendar/OrbsDuplicateOnPickup"
],
[
"/Lotus/Upgrades/Calendar/BlastEveryXShots",
"/Lotus/Upgrades/Calendar/GasChanceToPrimaryAndSecondary", "/Lotus/Upgrades/Calendar/GasChanceToPrimaryAndSecondary",
"/Lotus/Upgrades/Calendar/GuidingMissilesChance",
"/Lotus/Upgrades/Calendar/MagazineCapacity", "/Lotus/Upgrades/Calendar/MagazineCapacity",
"/Lotus/Upgrades/Calendar/PunchToPrimary", "/Lotus/Upgrades/Calendar/PunchToPrimary",
"/Lotus/Upgrades/Calendar/HealingEffects",
"/Lotus/Upgrades/Calendar/EnergyRestoration",
"/Lotus/Upgrades/Calendar/OvershieldCap",
"/Lotus/Upgrades/Calendar/ElectricStatusDamageAndChance",
"/Lotus/Upgrades/Calendar/FinisherChancePerComboMultiplier",
"/Lotus/Upgrades/Calendar/MagnetStatusPull",
"/Lotus/Upgrades/Calendar/CompanionsBuffNearbyPlayer",
"/Lotus/Upgrades/Calendar/StatusChancePerAmmoSpent",
"/Lotus/Upgrades/Calendar/OrbsDuplicateOnPickup",
"/Lotus/Upgrades/Calendar/AttackAndMovementSpeedOnCritMelee",
"/Lotus/Upgrades/Calendar/RadialJavelinOnHeavy",
"/Lotus/Upgrades/Calendar/MagnitizeWithinRangeEveryXCasts",
"/Lotus/Upgrades/Calendar/CompanionsRadiationChance",
"/Lotus/Upgrades/Calendar/BlastEveryXShots",
"/Lotus/Upgrades/Calendar/GenerateOmniOrbsOnWeakKill",
"/Lotus/Upgrades/Calendar/ElectricDamagePerDistance",
"/Lotus/Upgrades/Calendar/MeleeSlideFowardMomentumOnEnemyHit",
"/Lotus/Upgrades/Calendar/SharedFreeAbilityEveryXCasts",
"/Lotus/Upgrades/Calendar/ReviveEnemyAsSpectreOnKill",
"/Lotus/Upgrades/Calendar/RefundBulletOnStatusProc", "/Lotus/Upgrades/Calendar/RefundBulletOnStatusProc",
"/Lotus/Upgrades/Calendar/StatusChancePerAmmoSpent" "/Lotus/Upgrades/Calendar/ExplodingHealthOrbs",
] "/Lotus/Upgrades/Calendar/SpeedBuffsWhenAirborne",
"/Lotus/Upgrades/Calendar/EnergyWavesOnCombo",
"/Lotus/Upgrades/Calendar/PowerStrengthAndEfficiencyPerEnergySpent",
"/Lotus/Upgrades/Calendar/CloneActiveCompanionForEnergySpent",
"/Lotus/Upgrades/Calendar/GuidingMissilesChance",
"/Lotus/Upgrades/Calendar/EnergyOrbsGrantShield",
"/Lotus/Upgrades/Calendar/ElectricalDamageOnBulletJump"
]; ];
for (let i = 0; i != upgradeRanges.length - 1; ++i) { for (let i = 0; i != upgradeRanges.length - 1; ++i) {
// Pick 3 unique hex members
const hexMembersPickedForThisDay: number[] = [];
for (let j = 0; j != 3; ++j) {
let hexMemberIndex: number;
do {
hexMemberIndex = rng.randomInt(0, upgradesByHexMember.length - 1);
} while (hexMembersPickedForThisDay.indexOf(hexMemberIndex) != -1);
hexMembersPickedForThisDay.push(hexMemberIndex);
}
hexMembersPickedForThisDay.sort(); // Always present them in the same order
// For each hex member, pick an upgrade that was not yet picked this season.
const events: ICalendarEvent[] = [];
for (const hexMemberIndex of hexMembersPickedForThisDay) {
const upgrades = upgradesByHexMember[hexMemberIndex];
const upgradeIndex = rng.randomInt(0, upgrades.length - 1); const upgradeIndex = rng.randomInt(0, upgrades.length - 1);
events.push({ type: "CET_UPGRADE", upgrade: upgrades[upgradeIndex] }); const upgrade = upgrades[upgradeIndex];
upgrades.splice(upgradeIndex, 1); upgrades.splice(upgradeIndex, 1);
}
//logger.debug(`trying to fit upgrades between day ${upgradeRanges[i]} and ${upgradeRanges[i + 1]}`); //logger.debug(`trying to fit an upgrade between day ${upgradeRanges[i]} and ${upgradeRanges[i + 1]}`);
let day: number; let day: number;
do { do {
day = rng.randomInt(upgradeRanges[i] + 1, upgradeRanges[i + 1] - 1); day = rng.randomInt(upgradeRanges[i] + 1, upgradeRanges[i + 1] - 1);
} while (eventDays.find(x => x.day == day)); } while (eventDays.find(x => x.day == day));
eventDays.push({ day, events }); eventDays.push({ day, events: [{ type: "CET_UPGRADE", upgrade }] });
} }
eventDays.sort((a, b) => a.day - b.day); eventDays.sort((a, b) => a.day - b.day);
@ -688,7 +620,7 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
Activation: { $date: { $numberLong: weekStart.toString() } }, Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } }, Expiry: { $date: { $numberLong: weekEnd.toString() } },
Days: eventDays, Days: eventDays,
Season: (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[seasonIndex], Season: ["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"][seasonIndex],
YearIteration: Math.trunc(week / 4), YearIteration: Math.trunc(week / 4),
Version: 19, Version: 19,
UpgradeAvaliabilityRequirements: ["/Lotus/Upgrades/Calendar/1999UpgradeApplicationRequirement"] UpgradeAvaliabilityRequirements: ["/Lotus/Upgrades/Calendar/1999UpgradeApplicationRequirement"]
@ -754,7 +686,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
} }
// Elite Sanctuary Onslaught cycling every week // Elite Sanctuary Onslaught cycling every week
worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = new CRng(week).randomInt(0, 0xffff); worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
// Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation // Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation
let bountyCycle = Math.trunc(Date.now() / 9000000); let bountyCycle = Math.trunc(Date.now() / 9000000);
@ -763,7 +695,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
const bountyCycleStart = bountyCycle * 9000000; const bountyCycleStart = bountyCycle * 9000000;
bountyCycleEnd = bountyCycleStart + 9000000; bountyCycleEnd = bountyCycleStart + 9000000;
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000029" }, _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000029" },
Activation: { $date: { $numberLong: bountyCycleStart.toString() } }, Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
Tag: "ZarimanSyndicate", Tag: "ZarimanSyndicate",
@ -771,7 +703,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
Nodes: [] Nodes: []
}); });
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000004" }, _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000004" },
Activation: { $date: { $numberLong: bountyCycleStart.toString() } }, Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
Tag: "EntratiLabSyndicate", Tag: "EntratiLabSyndicate",
@ -779,7 +711,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
Nodes: [] Nodes: []
}); });
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000006" }, _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000006" },
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } }, Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
Tag: "HexSyndicate", Tag: "HexSyndicate",
@ -793,18 +725,14 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
// TODO: xpAmounts need to be calculated based on the jobType somehow? // TODO: xpAmounts need to be calculated based on the jobType somehow?
const seed = new CRng(bountyCycle).randomInt(0, 0xffff);
{ {
const rng = new CRng(seed); const rng = new CRng(bountyCycle);
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000008" },
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
},
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } }, Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
Tag: "CetusSyndicate", Tag: "CetusSyndicate",
Seed: seed, Seed: bountyCycle,
Nodes: [], Nodes: [],
Jobs: [ Jobs: [
{ {
@ -868,15 +796,13 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
} }
{ {
const rng = new CRng(seed); const rng = new CRng(bountyCycle);
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000025" },
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000025"
},
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } }, Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
Tag: "SolarisSyndicate", Tag: "SolarisSyndicate",
Seed: seed, Seed: bountyCycle,
Nodes: [], Nodes: [],
Jobs: [ Jobs: [
{ {
@ -940,15 +866,13 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
} }
{ {
const rng = new CRng(seed); const rng = new CRng(bountyCycle);
worldState.SyndicateMissions.push({ worldState.SyndicateMissions.push({
_id: { _id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000002" },
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000002"
},
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } }, Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } }, Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
Tag: "EntratiSyndicate", Tag: "EntratiSyndicate",
Seed: seed, Seed: bountyCycle,
Nodes: [], Nodes: [],
Jobs: [ Jobs: [
{ {
@ -1164,15 +1088,14 @@ export const getLiteSortie = (week: number): ILiteSortie => {
value.systemIndex === systemIndex && value.systemIndex === systemIndex &&
value.factionIndex !== undefined && value.factionIndex !== undefined &&
value.factionIndex < 2 && value.factionIndex < 2 &&
!isArchwingMission(value) && value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 // Exclude MT_ASSASSINATION value.missionIndex != 0 // Exclude MT_ASSASSINATION
) { ) {
nodes.push(key); nodes.push(key);
} }
} }
const seed = new CRng(week).randomInt(0, 0xffff); const rng = new CRng(week);
const rng = new CRng(seed);
const firstNodeIndex = rng.randomInt(0, nodes.length - 1); const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
const firstNode = nodes[firstNodeIndex]; const firstNode = nodes[firstNodeIndex];
nodes.splice(firstNodeIndex, 1); nodes.splice(firstNodeIndex, 1);
@ -1181,12 +1104,12 @@ export const getLiteSortie = (week: number): ILiteSortie => {
const weekEnd = weekStart + 604800000; const weekEnd = weekStart + 604800000;
return { return {
_id: { _id: {
$oid: ((weekStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "5e23a244740a190c" $oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
}, },
Activation: { $date: { $numberLong: weekStart.toString() } }, Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } }, Expiry: { $date: { $numberLong: weekEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards", Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
Seed: seed, Seed: week,
Boss: boss, Boss: boss,
Missions: [ Missions: [
{ {
@ -1196,7 +1119,7 @@ export const getLiteSortie = (week: number): ILiteSortie => {
"MT_EXTERMINATION", "MT_EXTERMINATION",
"MT_SABOTAGE", "MT_SABOTAGE",
"MT_RESCUE" "MT_RESCUE"
])!, ]),
node: firstNode node: firstNode
}, },
{ {
@ -1206,8 +1129,8 @@ export const getLiteSortie = (week: number): ILiteSortie => {
"MT_ARTIFACT", "MT_ARTIFACT",
"MT_EXCAVATE", "MT_EXCAVATE",
"MT_SURVIVAL" "MT_SURVIVAL"
])!, ]),
node: rng.randomElement(nodes)! node: rng.randomElement(nodes)
}, },
{ {
missionType: "MT_ASSASSINATION", missionType: "MT_ASSASSINATION",
@ -1216,14 +1139,3 @@ export const getLiteSortie = (week: number): ILiteSortie => {
] ]
}; };
}; };
export const isArchwingMission = (node: IRegion): boolean => {
if (node.name.indexOf("Archwing") != -1) {
return true;
}
// SettlementNode10
if (node.missionIndex == 25) {
return true;
}
return false;
};

View File

@ -11,7 +11,6 @@ export interface IGuildClient {
Members: IGuildMemberClient[]; Members: IGuildMemberClient[];
Ranks: IGuildRank[]; Ranks: IGuildRank[];
Tier: number; Tier: number;
Emblem?: boolean;
Vault: IGuildVault; Vault: IGuildVault;
ActiveDojoColorResearch: string; ActiveDojoColorResearch: string;
Class: number; Class: number;
@ -207,7 +206,6 @@ export interface IDojoDecoClient {
Type: string; Type: string;
Pos: number[]; Pos: number[];
Rot: number[]; Rot: number[];
Scale?: number;
Name?: string; // for teleporters Name?: string; // for teleporters
Sockets?: number; Sockets?: number;
RegularCredits?: number; RegularCredits?: number;

View File

@ -43,7 +43,6 @@ export interface IInventoryDatabase
| "RecentVendorPurchases" | "RecentVendorPurchases"
| "NextRefill" | "NextRefill"
| "Nemesis" | "Nemesis"
| "NemesisHistory"
| "EntratiVaultCountResetDate" | "EntratiVaultCountResetDate"
| "BrandedSuits" | "BrandedSuits"
| "LockedWeaponGroup" | "LockedWeaponGroup"
@ -52,7 +51,6 @@ export interface IInventoryDatabase
| "LastLiteSortieReward" | "LastLiteSortieReward"
| "CrewMembers" | "CrewMembers"
| "QualifyingInvasions" | "QualifyingInvasions"
| "LastInventorySync"
| TEquipmentKey | TEquipmentKey
>, >,
InventoryDatabaseEquipment { InventoryDatabaseEquipment {
@ -81,7 +79,6 @@ export interface IInventoryDatabase
RecentVendorPurchases?: IRecentVendorPurchaseDatabase[]; RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
NextRefill?: Date; NextRefill?: Date;
Nemesis?: INemesisDatabase; Nemesis?: INemesisDatabase;
NemesisHistory?: INemesisBaseDatabase[];
EntratiVaultCountResetDate?: Date; EntratiVaultCountResetDate?: Date;
BrandedSuits?: Types.ObjectId[]; BrandedSuits?: Types.ObjectId[];
LockedWeaponGroup?: ILockedWeaponGroupDatabase; LockedWeaponGroup?: ILockedWeaponGroupDatabase;
@ -90,7 +87,6 @@ export interface IInventoryDatabase
LastLiteSortieReward?: ILastSortieRewardDatabase[]; LastLiteSortieReward?: ILastSortieRewardDatabase[];
CrewMembers: ICrewMemberDatabase[]; CrewMembers: ICrewMemberDatabase[];
QualifyingInvasions: IInvasionProgressDatabase[]; QualifyingInvasions: IInvasionProgressDatabase[];
LastInventorySync?: Types.ObjectId;
} }
export interface IQuestKeyDatabase { export interface IQuestKeyDatabase {
@ -138,7 +134,7 @@ export const equipmentKeys = [
export type TEquipmentKey = (typeof equipmentKeys)[number]; export type TEquipmentKey = (typeof equipmentKeys)[number];
export interface IDuviriInfo { export interface IDuviriInfo {
Seed: bigint; Seed: number;
NumCompletions: number; NumCompletions: number;
} }
@ -206,7 +202,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
Mailbox?: IMailboxClient; Mailbox?: IMailboxClient;
SubscribedToEmails: number; SubscribedToEmails: number;
Created: IMongoDate; Created: IMongoDate;
RewardSeed: bigint; RewardSeed: number | bigint;
RegularCredits: number; RegularCredits: number;
PremiumCredits: number; PremiumCredits: number;
PremiumCreditsFree: number; PremiumCreditsFree: number;
@ -260,7 +256,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
EquippedGear: string[]; EquippedGear: string[];
DeathMarks: string[]; DeathMarks: string[];
FusionTreasures: IFusionTreasure[]; FusionTreasures: IFusionTreasure[];
//WebFlags: IWebFlags; WebFlags: IWebFlags;
CompletedAlerts: string[]; CompletedAlerts: string[];
Consumables: ITypeCount[]; Consumables: ITypeCount[];
LevelKeys: ITypeCount[]; LevelKeys: ITypeCount[];
@ -270,10 +266,10 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
KubrowPetEggs?: IKubrowPetEggClient[]; KubrowPetEggs?: IKubrowPetEggClient[];
LoreFragmentScans: ILoreFragmentScan[]; LoreFragmentScans: ILoreFragmentScan[];
EquippedEmotes: string[]; EquippedEmotes: string[];
//PendingTrades: IPendingTrade[]; PendingTrades: IPendingTrade[];
Boosters: IBooster[]; Boosters: IBooster[];
ActiveDojoColorResearch: string; ActiveDojoColorResearch: string;
//SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters; SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
SupportedSyndicate?: string; SupportedSyndicate?: string;
Affiliations: IAffiliation[]; Affiliations: IAffiliation[];
QualifyingInvasions: IInvasionProgressClient[]; QualifyingInvasions: IInvasionProgressClient[];
@ -295,19 +291,19 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
ActiveAvatarImageType: string; ActiveAvatarImageType: string;
ShipDecorations: ITypeCount[]; ShipDecorations: ITypeCount[];
DiscoveredMarkers: IDiscoveredMarker[]; DiscoveredMarkers: IDiscoveredMarker[];
//CompletedJobs: ICompletedJob[]; CompletedJobs: ICompletedJob[];
FocusAbility?: string; FocusAbility?: string;
FocusUpgrades: IFocusUpgrade[]; FocusUpgrades: IFocusUpgrade[];
HasContributedToDojo?: boolean; HasContributedToDojo?: boolean;
HWIDProtectEnabled?: boolean; HWIDProtectEnabled?: boolean;
//KubrowPetPrints: IKubrowPetPrint[]; KubrowPetPrints: IKubrowPetPrint[];
AlignmentReplay?: IAlignment; AlignmentReplay?: IAlignment;
//PersonalGoalProgress: IPersonalGoalProgress[]; PersonalGoalProgress: IPersonalGoalProgress[];
ThemeStyle: string; ThemeStyle: string;
ThemeBackground: string; ThemeBackground: string;
ThemeSounds: string; ThemeSounds: string;
BountyScore: number; BountyScore: number;
//ChallengeInstanceStates: IChallengeInstanceState[]; ChallengeInstanceStates: IChallengeInstanceState[];
LoginMilestoneRewards: string[]; LoginMilestoneRewards: string[];
RecentVendorPurchases?: IRecentVendorPurchaseClient[]; RecentVendorPurchases?: IRecentVendorPurchaseClient[];
NodeIntrosCompleted: string[]; NodeIntrosCompleted: string[];
@ -315,37 +311,37 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
CompletedJobChains?: ICompletedJobChain[]; CompletedJobChains?: ICompletedJobChain[];
SeasonChallengeHistory: ISeasonChallenge[]; SeasonChallengeHistory: ISeasonChallenge[];
EquippedInstrument?: string; EquippedInstrument?: string;
//InvasionChainProgress: IInvasionChainProgress[]; InvasionChainProgress: IInvasionChainProgress[];
Nemesis?: INemesisClient; Nemesis?: INemesisClient;
NemesisHistory?: INemesisBaseClient[]; NemesisHistory: INemesisBaseClient[];
//LastNemesisAllySpawnTime?: IMongoDate; LastNemesisAllySpawnTime?: IMongoDate;
Settings?: ISettings; Settings?: ISettings;
PersonalTechProjects: IPersonalTechProjectClient[]; PersonalTechProjects: IPersonalTechProjectClient[];
PlayerSkills: IPlayerSkills; PlayerSkills: IPlayerSkills;
CrewShipAmmo: ITypeCount[]; CrewShipAmmo: ITypeCount[];
CrewShipWeaponSkins: IUpgradeClient[]; CrewShipWeaponSkins: IUpgradeClient[];
CrewShipSalvagedWeaponSkins: IUpgradeClient[]; CrewShipSalvagedWeaponSkins: IUpgradeClient[];
//TradeBannedUntil?: IMongoDate; TradeBannedUntil?: IMongoDate;
PlayedParkourTutorial: boolean; PlayedParkourTutorial: boolean;
SubscribedToEmailsPersonalized: number; SubscribedToEmailsPersonalized: number;
InfestedFoundry?: IInfestedFoundryClient; InfestedFoundry?: IInfestedFoundryClient;
BlessingCooldown?: IMongoDate; BlessingCooldown?: IMongoDate;
CrewShipRawSalvage: ITypeCount[]; CrewShipRawSalvage: ITypeCount[];
CrewMembers: ICrewMemberClient[]; CrewMembers: ICrewMemberClient[];
LotusCustomization?: ILotusCustomization; LotusCustomization: ILotusCustomization;
UseAdultOperatorLoadout?: boolean; UseAdultOperatorLoadout?: boolean;
NemesisAbandonedRewards: string[]; NemesisAbandonedRewards: string[];
LastInventorySync?: IOid; LastInventorySync: IOid;
NextRefill?: IMongoDate; NextRefill?: IMongoDate;
FoundToday?: IMiscItem[]; // for Argon Crystals FoundToday?: IMiscItem[]; // for Argon Crystals
CustomMarkers?: ICustomMarkers[]; CustomMarkers?: ICustomMarkers[];
//ActiveLandscapeTraps: any[]; ActiveLandscapeTraps: any[];
EvolutionProgress?: IEvolutionProgress[]; EvolutionProgress?: IEvolutionProgress[];
//RepVotes: any[]; RepVotes: any[];
//LeagueTickets: any[]; LeagueTickets: any[];
//Quests: any[]; Quests: any[];
//Robotics: any[]; Robotics: any[];
//UsedDailyDeals: any[]; UsedDailyDeals: any[];
LibraryPersonalTarget?: string; LibraryPersonalTarget?: string;
LibraryPersonalProgress: ILibraryPersonalProgress[]; LibraryPersonalProgress: ILibraryPersonalProgress[];
CollectibleSeries?: ICollectibleEntry[]; CollectibleSeries?: ICollectibleEntry[];
@ -357,7 +353,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
DeathSquadable: boolean; DeathSquadable: boolean;
EndlessXP?: IEndlessXpProgress[]; EndlessXP?: IEndlessXpProgress[];
DialogueHistory?: IDialogueHistoryClient; DialogueHistory?: IDialogueHistoryClient;
CalendarProgress?: ICalendarProgress; CalendarProgress: ICalendarProgress;
SongChallenges?: ISongChallenge[]; SongChallenges?: ISongChallenge[];
EntratiVaultCountLastPeriod?: number; EntratiVaultCountLastPeriod?: number;
EntratiVaultCountResetDate?: IMongoDate; EntratiVaultCountResetDate?: IMongoDate;
@ -849,7 +845,7 @@ export interface IMission extends IMissionDatabase {
} }
export interface INemesisBaseClient { export interface INemesisBaseClient {
fp: bigint | number; fp: bigint;
manifest: string; manifest: string;
KillingSuit: string; KillingSuit: string;
killingDamageType: number; killingDamageType: number;
@ -867,8 +863,7 @@ export interface INemesisBaseClient {
Weakened: boolean; Weakened: boolean;
} }
export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "fp" | "d"> { export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "d"> {
fp: bigint;
d: Date; d: Date;
} }
@ -882,8 +877,7 @@ export interface INemesisClient extends INemesisBaseClient {
LastEnc: number; LastEnc: number;
} }
export interface INemesisDatabase extends Omit<INemesisClient, "fp" | "d"> { export interface INemesisDatabase extends Omit<INemesisClient, "d"> {
fp: bigint;
d: Date; d: Date;
} }
@ -906,8 +900,8 @@ export interface IPendingRecipeDatabase {
ItemType: string; ItemType: string;
CompletionDate: Date; CompletionDate: Date;
ItemId: IOid; ItemId: IOid;
TargetItemId?: string; // unsure what this is for TargetItemId?: string; // likely related to liches
TargetFingerprint?: string; TargetFingerprint?: string; // likely related to liches
LongGuns?: IEquipmentDatabase[]; LongGuns?: IEquipmentDatabase[];
Pistols?: IEquipmentDatabase[]; Pistols?: IEquipmentDatabase[];
Melee?: IEquipmentDatabase[]; Melee?: IEquipmentDatabase[];
@ -955,17 +949,6 @@ export interface ICrewShipComponentFingerprint extends IInnateDamageFingerprint
SubroutineIndex?: number; SubroutineIndex?: number;
} }
export interface INemesisWeaponTargetFingerprint {
ItemType: string;
UpgradeFingerprint: IInnateDamageFingerprint;
Name: string;
}
export interface INemesisPetTargetFingerprint {
Parts: string[];
Name: string;
}
export enum GettingSlotOrderInfo { export enum GettingSlotOrderInfo {
Empty = "", Empty = "",
LotusUpgradesModsRandomizedPlayerMeleeWeaponRandomModRare0 = "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare:0", LotusUpgradesModsRandomizedPlayerMeleeWeaponRandomModRare0 = "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare:0",
@ -1210,18 +1193,17 @@ export interface IMarker {
z: number; z: number;
showInHud: boolean; showInHud: boolean;
} }
export interface ISeasonProgress { export interface ISeasonProgress {
SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"; SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
LastCompletedDayIdx: number; LastCompletedDayIdx: number;
LastCompletedChallengeDayIdx: number; LastCompletedChallengeDayIdx: number;
ActivatedChallenges: string[]; ActivatedChallenges: unknown[];
} }
export interface ICalendarProgress { export interface ICalendarProgress {
Version: number; Version: number;
Iteration: number; Iteration: number;
YearProgress: { Upgrades: string[] }; YearProgress: { Upgrades: unknown[] };
SeasonProgress: ISeasonProgress; SeasonProgress: ISeasonProgress;
} }

View File

@ -1,5 +1,3 @@
import { IAffiliationMods, IInventoryChanges } from "./purchaseTypes";
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const; export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
export type IInventoryFieldType = (typeof inventoryFields)[number]; export type IInventoryFieldType = (typeof inventoryFields)[number];
@ -8,27 +6,8 @@ export interface IMissionReward {
TypeName?: string; TypeName?: string;
UpgradeLevel?: number; UpgradeLevel?: number;
ItemCount: number; ItemCount: number;
DailyCooldown?: boolean;
Rarity?: number;
TweetText?: string; TweetText?: string;
ProductCategory?: string; ProductCategory?: string;
FromEnemyCache?: boolean; FromEnemyCache?: boolean;
IsStrippedItem?: boolean; IsStrippedItem?: boolean;
} }
export interface IMissionCredits {
MissionCredits: number[];
CreditBonus: number[];
TotalCredits: number[];
DailyMissionBonus?: boolean;
}
export interface IMissionInventoryUpdateResponse extends Partial<IMissionCredits> {
ConquestCompletedMissionsCount?: number;
InventoryJson?: string;
MissionRewards?: IMissionReward[];
InventoryChanges?: IInventoryChanges;
FusionPoints?: number;
SyndicateXPItemReward?: number;
AffiliationMods?: IAffiliationMods[];
}

View File

@ -1,12 +1,12 @@
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { import {
IApartment,
IRoom, IRoom,
IPlacedDecosDatabase, IPlacedDecosDatabase,
ITailorShop, ITailorShop,
ITailorShopDatabase, ITailorShopDatabase,
TBootLocation, TBootLocation,
IApartmentDatabase, IApartmentDatabase
IApartmentClient
} from "@/src/types/shipTypes"; } from "@/src/types/shipTypes";
import { Document, Model, Types } from "mongoose"; import { Document, Model, Types } from "mongoose";
@ -21,10 +21,10 @@ export interface IOrbiter {
BootLocation?: TBootLocation; BootLocation?: TBootLocation;
} }
export interface IPersonalRoomsClient { export interface IPersonalRooms {
ShipInteriorColors: IColor; ShipInteriorColors: IColor;
Ship: IOrbiter; Ship: IOrbiter;
Apartment: IApartmentClient; Apartment: IApartment;
TailorShop: ITailorShop; TailorShop: ITailorShop;
} }

View File

@ -103,7 +103,6 @@ export const slotNames = [
"WeaponBin", "WeaponBin",
"MechBin", "MechBin",
"PveBonusLoadoutBin", "PveBonusLoadoutBin",
"PvpBonusLoadoutBin",
"SentinelBin", "SentinelBin",
"SpaceSuitBin", "SpaceSuitBin",
"SpaceWeaponBin", "SpaceWeaponBin",

View File

@ -20,10 +20,7 @@ import {
IDiscoveredMarker, IDiscoveredMarker,
ILockedWeaponGroupClient, ILockedWeaponGroupClient,
ILoadOutPresets, ILoadOutPresets,
IInvasionProgressClient, IInvasionProgressClient
IWeaponSkinClient,
IKubrowPetEggClient,
INemesisClient
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
import { IGroup } from "./loginTypes"; import { IGroup } from "./loginTypes";
@ -47,7 +44,6 @@ export type IMissionInventoryUpdateRequest = {
SyndicateId?: string; SyndicateId?: string;
SortieId?: string; SortieId?: string;
CalendarProgress?: { challenge: string }[];
SeasonChallengeCompletions?: ISeasonChallenge[]; SeasonChallengeCompletions?: ISeasonChallenge[];
AffiliationChanges?: IAffiliationChange[]; AffiliationChanges?: IAffiliationChange[];
crossPlaySetting?: string; crossPlaySetting?: string;
@ -75,14 +71,6 @@ export type IMissionInventoryUpdateRequest = {
PS: string; PS: string;
ActiveDojoColorResearch: string; ActiveDojoColorResearch: string;
RewardInfo?: IRewardInfo; RewardInfo?: IRewardInfo;
NemesisKillConvert?: {
nemesisName: string;
weaponLoc: string;
petLoc: "" | "/Lotus/Language/Pets/ZanukaPetName";
fingerprint: bigint | number;
killed: boolean;
};
target?: INemesisClient;
ReceivedCeremonyMsg: boolean; ReceivedCeremonyMsg: boolean;
LastCeremonyResetDate: number; LastCeremonyResetDate: number;
MissionPTS: number; MissionPTS: number;
@ -112,7 +100,6 @@ export type IMissionInventoryUpdateRequest = {
}[]; }[];
CollectibleScans?: ICollectibleEntry[]; CollectibleScans?: ICollectibleEntry[];
Upgrades?: IUpgradeClient[]; // riven challenge progress Upgrades?: IUpgradeClient[]; // riven challenge progress
WeaponSkins?: IWeaponSkinClient[];
StrippedItems?: { StrippedItems?: {
DropTable: string; DropTable: string;
DROP_MOD?: number[]; DROP_MOD?: number[];
@ -128,7 +115,6 @@ export type IMissionInventoryUpdateRequest = {
NumExtraRewards: number; NumExtraRewards: number;
Count: number; Count: number;
}[]; }[];
KubrowPetEggs?: IKubrowPetEggClient[];
DiscoveredMarkers?: IDiscoveredMarker[]; DiscoveredMarkers?: IDiscoveredMarker[];
LockedWeaponGroup?: ILockedWeaponGroupClient; // sent when captured by zanuka LockedWeaponGroup?: ILockedWeaponGroupClient; // sent when captured by zanuka
UnlockWeapons?: boolean; // sent when recovered weapons from zanuka capture UnlockWeapons?: boolean; // sent when recovered weapons from zanuka capture
@ -139,16 +125,6 @@ export type IMissionInventoryUpdateRequest = {
wagerTier?: number; // the index wagerTier?: number; // the index
creditsFee?: number; // the index creditsFee?: number; // the index
InvasionProgress?: IInvasionProgressClient[]; InvasionProgress?: IInvasionProgressClient[];
ConquestMissionsCompleted?: number;
duviriSuitSelection?: string;
duviriPistolSelection?: string;
duviriLongGunSelection?: string;
duviriMeleeSelection?: string;
duviriCaveOffers?: {
Seed: number | bigint;
Warframes: string[];
Weapons: string[];
};
} & { } & {
[K in TEquipmentKey]?: IEquipmentClient[]; [K in TEquipmentKey]?: IEquipmentClient[];
}; };
@ -158,7 +134,7 @@ export interface IRewardInfo {
invasionId?: string; invasionId?: string;
invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS"; invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
sortieId?: string; sortieId?: string;
sortieTag?: "Mission1" | "Mission2" | "Final"; sortieTag?: string;
sortiePrereqs?: string[]; sortiePrereqs?: string[];
VaultsCracked?: number; // for Spy missions VaultsCracked?: number; // for Spy missions
rewardTier?: number; rewardTier?: number;
@ -169,19 +145,12 @@ export interface IRewardInfo {
lostTargetWave?: number; lostTargetWave?: number;
defenseTargetCount?: number; defenseTargetCount?: number;
NemesisAbandonedRewards?: string[]; NemesisAbandonedRewards?: string[];
NemesisHenchmenKills?: number;
NemesisHintProgress?: number;
EOM_AFK?: number; EOM_AFK?: number;
rewardQualifications?: string; // did a Survival for 5 minutes and this was "1" rewardQualifications?: string; // did a Survival for 5 minutes and this was "1"
PurgatoryRewardQualifications?: string; PurgatoryRewardQualifications?: string;
rewardSeed?: number | bigint; rewardSeed?: number | bigint;
periodicMissionTag?: string; periodicMissionTag?: string;
ConquestType?: string;
ConquestCompleted?: number;
ConquestEquipmentSuggestionsFulfilled?: number;
ConquestPersonalModifiersActive?: number;
ConquestStickersActive?: number;
ConquestHardModeActive?: number;
// for bounties, only EOM_AFK and node are given from above, plus: // for bounties, only EOM_AFK and node are given from above, plus:
JobTier?: number; JobTier?: number;
jobId?: string; jobId?: string;

View File

@ -6,8 +6,7 @@ import {
ICrewShipMembersClient, ICrewShipMembersClient,
ICrewShipWeapon, ICrewShipWeapon,
IFlavourItem, IFlavourItem,
ILoadoutConfigClient, ILoadoutConfigClient
ILotusCustomization
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
export interface ISaveLoadoutRequest { export interface ISaveLoadoutRequest {
@ -44,7 +43,6 @@ export interface ISaveLoadoutRequest {
EquippedEmotes: string[]; EquippedEmotes: string[];
UseAdultOperatorLoadout: boolean; UseAdultOperatorLoadout: boolean;
WeaponSkins: IItemEntry; WeaponSkins: IItemEntry;
LotusCustomization: ILotusCustomization;
} }
export type ISaveLoadoutRequestNoUpgradeVer = Omit<ISaveLoadoutRequest, "UpgradeVer">; export type ISaveLoadoutRequestNoUpgradeVer = Omit<ISaveLoadoutRequest, "UpgradeVer">;

View File

@ -1,12 +1,12 @@
import { Types } from "mongoose"; import { Types } from "mongoose";
import { IMongoDate, IOid } from "@/src/types/commonTypes"; import { IOid } from "@/src/types/commonTypes";
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { ILoadoutClient } from "./saveLoadoutTypes"; import { ILoadoutClient } from "./saveLoadoutTypes";
export interface IGetShipResponse { export interface IGetShipResponse {
ShipOwnerId: string; ShipOwnerId: string;
Ship: IShip; Ship: IShip;
Apartment: IApartmentClient; Apartment: IApartment;
TailorShop: ITailorShop; TailorShop: ITailorShop;
LoadOutInventory: { LoadOutPresets: ILoadoutClient }; LoadOutInventory: { LoadOutPresets: ILoadoutClient };
} }
@ -51,42 +51,28 @@ export interface IRoom {
PlacedDecos?: IPlacedDecosDatabase[]; PlacedDecos?: IPlacedDecosDatabase[];
} }
export interface IPlantClient { export interface IPlants {
PlantType: string; PlantType: string;
EndTime: IMongoDate; EndTime: IOid;
PlotIndex: number; PlotIndex: number;
} }
export interface IPlanters {
export interface IPlantDatabase extends Omit<IPlantClient, "EndTime"> {
EndTime: Date;
}
export interface IPlanterClient {
Name: string; Name: string;
Plants: IPlantClient[]; Plants: IPlants[];
} }
export interface IPlanterDatabase { export interface IGardening {
Name: string; Planters?: IPlanters[];
Plants: IPlantDatabase[];
} }
export interface IGardeningClient { export interface IApartment {
Planters: IPlanterClient[]; Gardening: IGardening;
}
export interface IGardeningDatabase {
Planters: IPlanterDatabase[];
}
export interface IApartmentClient {
Gardening: IGardeningClient;
Rooms: IRoom[]; Rooms: IRoom[];
FavouriteLoadouts: IFavouriteLoadout[]; FavouriteLoadouts: IFavouriteLoadout[];
} }
export interface IApartmentDatabase { export interface IApartmentDatabase {
Gardening: IGardeningDatabase; Gardening: IGardening;
Rooms: IRoom[]; Rooms: IRoom[];
FavouriteLoadouts: IFavouriteLoadoutDatabase[]; FavouriteLoadouts: IFavouriteLoadoutDatabase[];
} }

View File

@ -1,16 +1,18 @@
import { IMongoDate, IOid } from "./commonTypes"; import { IMongoDate, IOid } from "./commonTypes";
export interface IItemPrice { export interface IItemPrice {
ItemType: string; ItemType: string | string[]; // If string[], preprocessing will use RNG to pick one for the current period.
ItemCount: number; ItemCount: number;
ProductCategory: string; ProductCategory: string;
} }
export interface IItemPricePreprocessed extends Omit<IItemPrice, "ItemType"> {
ItemType: string;
}
export interface IItemManifest { export interface IItemManifest {
StoreItem: string; StoreItem: string;
ItemPrices?: IItemPrice[]; ItemPrices?: IItemPrice[];
RegularPrice?: number[];
PremiumPrice?: number[];
Bin: string; Bin: string;
QuantityMultiplier: number; QuantityMultiplier: number;
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing. Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
@ -21,6 +23,10 @@ export interface IItemManifest {
Id: IOid; Id: IOid;
} }
export interface IItemManifestPreprocessed extends Omit<IItemManifest, "ItemPrices"> {
ItemPrices?: IItemPricePreprocessed[];
}
export interface IVendorInfo { export interface IVendorInfo {
_id: IOid; _id: IOid;
TypeName: string; TypeName: string;
@ -32,6 +38,14 @@ export interface IVendorInfo {
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing. Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
} }
export interface IVendorManifest { export interface IVendorInfoPreprocessed extends Omit<IVendorInfo, "ItemManifest"> {
ItemManifest: IItemManifestPreprocessed[];
}
export interface IRawVendorManifest {
VendorInfo: IVendorInfo; VendorInfo: IVendorInfo;
} }
export interface IVendorManifestPreprocessed {
VendorInfo: IVendorInfoPreprocessed;
}

View File

@ -97,13 +97,6 @@ export interface ISortie {
}[]; }[];
} }
export interface ISortieMission {
missionType: string;
modifierType: string;
node: string;
tileset: string;
}
export interface ILiteSortie { export interface ILiteSortie {
_id: IOid; _id: IOid;
Activation: IMongoDate; Activation: IMongoDate;
@ -133,7 +126,7 @@ export interface ISeasonChallenge {
export interface ICalendarSeason { export interface ICalendarSeason {
Activation: IMongoDate; Activation: IMongoDate;
Expiry: IMongoDate; Expiry: IMongoDate;
Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"; Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"
Days: ICalendarDay[]; Days: ICalendarDay[];
YearIteration: number; YearIteration: number;
Version: number; Version: number;

View File

@ -1,49 +0,0 @@
[
"/Lotus/Weapons/ClanTech/Bio/BioWeapon",
"/Lotus/Weapons/ClanTech/Energy/EnergyRifle",
"/Lotus/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun",
"/Lotus/Weapons/Corpus/Pistols/CrpHandRL/CorpusHandRocketLauncher",
"/Lotus/Weapons/Grineer/LongGuns/GrineerSawbladeGun/SawBladeGun",
"/Lotus/Weapons/Grineer/Melee/GrineerTylAxeAndBoar/RegorAxeShield",
"/Lotus/Weapons/Grineer/Pistols/HeatGun/GrnHeatGun",
"/Lotus/Weapons/Infested/Pistols/InfVomitGun/InfVomitGunWep",
"/Lotus/Weapons/Syndicates/CephalonSuda/Pistols/CSDroidArray",
"/Lotus/Weapons/Tenno/Bows/HuntingBow",
"/Lotus/Weapons/Tenno/Bows/StalkerBow",
"/Lotus/Weapons/Tenno/LongGuns/TnoLeverAction/TnoLeverActionRifle",
"/Lotus/Weapons/Tenno/Melee/Axe/DualInfestedAxesWeapon",
"/Lotus/Weapons/Tenno/Melee/Dagger/CeramicDagger",
"/Lotus/Weapons/Tenno/Melee/Fist/Fist",
"/Lotus/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer",
"/Lotus/Weapons/Tenno/Melee/LongSword/LongSword",
"/Lotus/Weapons/Tenno/Melee/Maces/PaladinMace/PaladinMaceWeapon",
"/Lotus/Weapons/Tenno/Melee/Scythe/StalkerScytheWeapon",
"/Lotus/Weapons/Tenno/Melee/Scythe/ParisScythe/ParisScythe",
"/Lotus/Weapons/Tenno/Melee/Staff/Staff",
"/Lotus/Weapons/Tenno/Melee/Swords/CutlassAndPoignard/TennoCutlass",
"/Lotus/Weapons/Tenno/Melee/Swords/TennoSai/TennoSais",
"/Lotus/Weapons/Tenno/Pistol/AutoPistol",
"/Lotus/Weapons/Tenno/Pistol/BurstPistol",
"/Lotus/Weapons/Tenno/Pistol/HandShotGun",
"/Lotus/Weapons/Tenno/Pistol/HeavyPistol",
"/Lotus/Weapons/Tenno/Pistol/Pistol",
"/Lotus/Weapons/Tenno/Pistol/RevolverPistol",
"/Lotus/Weapons/Tenno/Pistols/ConclaveLeverPistol/ConclaveLeverPistol",
"/Lotus/Weapons/Tenno/Rifle/BoltoRifle",
"/Lotus/Weapons/Tenno/Rifle/BurstRifle",
"/Lotus/Weapons/Tenno/Rifle/HeavyRifle",
"/Lotus/Weapons/Tenno/Rifle/Rifle",
"/Lotus/Weapons/Tenno/Rifle/SemiAutoRifle",
"/Lotus/Weapons/Tenno/Rifle/TennoAR",
"/Lotus/Weapons/Tenno/Shotgun/FullAutoShotgun",
"/Lotus/Weapons/Tenno/Shotgun/Shotgun",
"/Lotus/Weapons/Tenno/ThrowingWeapons/Kunai",
"/Lotus/Weapons/Tenno/ThrowingWeapons/StalkerKunai",
"/Lotus/Weapons/Tenno/Zariman/LongGuns/PumpShotgun/ZarimanPumpShotgun",
"/Lotus/Weapons/Tenno/Zariman/LongGuns/SemiAutoRifle/ZarimanSemiAutoRifle",
"/Lotus/Weapons/Tenno/Zariman/Melee/Dagger/ZarimanDaggerWeapon",
"/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
"/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
"/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
"/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon"
]

View File

@ -1097,5 +1097,6 @@
"/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLamp", "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLamp",
"/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampLarge", "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampLarge",
"/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampSmall", "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampSmall",
"/Lotus/Types/LevelObjects/InfestedPumpkinExplosiveTotem" "/Lotus/Types/LevelObjects/InfestedPumpkinExplosiveTotem",
"/Lotus/Types/Enemies/Orokin/OrokinMoaBipedAvatar"
] ]

View File

@ -0,0 +1,106 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e01c96976e97d6b8016"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/FishmongerVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosOrokinFishAPartItem",
"PremiumPrice": [9, 9],
"Bin": "BIN_1",
"QuantityMultiplier": 10,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91b9"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishDPartItem",
"PremiumPrice": [17, 17],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91ba"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"PremiumPrice": [10, 10],
"Bin": "BIN_1",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91bb"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"PremiumPrice": [6, 6],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91bc"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishAPartItem",
"PremiumPrice": [5, 5],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91bd"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Deimos/FishParts/DeimosGenericSharedFishPartItem",
"PremiumPrice": [7, 7],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e91be"
}
}
],
"PropertyTextHash": "6DF13A7FB573C25B4B4F989CBEFFC615",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -0,0 +1,101 @@
{
"VendorInfo": {
"_id": { "$oid": "61ba123467e5d37975aeeb03" },
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementMoon",
"ItemPrices": [
{
"ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1, 1],
"Bin": "BIN_4",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "604800000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 79554843,
"Id": { "$oid": "67bbb592e1534511d6c1c1e2" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementMountain",
"ItemPrices": [
{
"ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
"ItemCount": 7,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1, 1],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "604800000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2413820225,
"Id": { "$oid": "67bbb592e1534511d6c1c1e3" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementStorm",
"ItemPrices": [
{
"ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
"ItemCount": 3,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1, 1],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "604800000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3262300883,
"Id": { "$oid": "67bbb592e1534511d6c1c1e4" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementShadow",
"ItemPrices": [
{
"ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1, 1],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "604800000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2797325750,
"Id": { "$oid": "67bbb592e1534511d6c1c1e5" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementGhost",
"ItemPrices": [
{
"ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1, 1],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "604800000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 554932310,
"Id": { "$oid": "67bbb592e1534511d6c1c1e6" }
}
],
"PropertyTextHash": "255AFE2169BAE4130B4B20D7C55D14FA",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": { "$date": { "$numberLong": "604800000" } }
}
}

View File

@ -0,0 +1,106 @@
{
"VendorInfo": {
"_id": {
"$oid": "59d6e27ebcc718474eb17115"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Ostron/FishmongerVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/DayUncommonFishAPartItem",
"PremiumPrice": [14, 14],
"Bin": "BIN_1",
"QuantityMultiplier": 10,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9808"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishBPartItem",
"PremiumPrice": [12, 12],
"Bin": "BIN_1",
"QuantityMultiplier": 10,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9809"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/DayCommonFishCPartItem",
"PremiumPrice": [8, 8],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e980a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/DayCommonFishBPartItem",
"PremiumPrice": [7, 7],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e980b"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/DayCommonFishAPartItem",
"PremiumPrice": [10, 10],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e980c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Eidolon/FishParts/BothCommonFishBPartItem",
"PremiumPrice": [8, 8],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e980d"
}
}
],
"PropertyTextHash": "CC3B9DAFB38F412998E90A41421A8986",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -0,0 +1,248 @@
{
"VendorInfo": {
"_id": {
"$oid": "5be4a159b144f3cdf1c22efa"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Venus/Resources/VenusCoconutItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/MiscItems/Circuits",
"ItemCount": 3664,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [87300, 87300],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 1881404827,
"Id": {
"$oid": "670daf92d21f34757a5e73b4"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleRareC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/MiscItems/NeuralSensor",
"ItemCount": 1,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [53300, 53300],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 1943984533,
"Id": {
"$oid": "6710b5029e1a3080a65e73a7"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonG",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/MiscItems/Salvage",
"ItemCount": 11540,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [27300, 27300],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 744199559,
"Id": {
"$oid": "67112582cc115756985e73a4"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishThermalLaserItem",
"ItemCount": 9,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [75800, 75800],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 3744711432,
"Id": {
"$oid": "670de7d28a6ec82cd25e73a2"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/MiscItems/Rubedo",
"ItemCount": 3343,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [52200, 52200],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 1579000687,
"Id": {
"$oid": "670e58526171148e125e73ad"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Venus/Resources/CoolantItem",
"ItemCount": 9,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishAnoscopicSensorItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [12400, 12400],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 3589081466,
"Id": {
"$oid": "67112582cc115756985e73a5"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Solaris/SolarisCommonOreBAlloyItem",
"ItemCount": 13,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [77500, 77500],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 1510234814,
"Id": {
"$oid": "670f0f21250ad046c35e73ee"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishParralelBiodeItem",
"ItemCount": 7,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Solaris/SolarisCommonGemBCutItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [94600, 94600],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 4222095721,
"Id": {
"$oid": "670f63827be40254f95e739d"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonJ",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/MiscItems/Nanospores",
"ItemCount": 14830,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [25600, 25600],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"LocTagRandSeed": 2694388669,
"Id": {
"$oid": "67112582cc115756985e73a6"
}
}
],
"PropertyTextHash": "A39621049CA3CA13761028CD21C239EF",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -0,0 +1,106 @@
{
"VendorInfo": {
"_id": {
"$oid": "5b0de8556df82a56ea9bae82"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Solaris/FishmongerVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/CorpusFishThermalLaserItem",
"PremiumPrice": [15, 15],
"Bin": "BIN_1",
"QuantityMultiplier": 10,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9515"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/CorpusFishVenedoCaseItem",
"PremiumPrice": [8, 8],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9516"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/SolarisFishDissipatorCoilItem",
"PremiumPrice": [18, 18],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9517"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/CorpusFishExaBrainItem",
"PremiumPrice": [5, 5],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9518"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/CorpusFishAnoscopicSensorItem",
"PremiumPrice": [5, 5],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9519"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Fish/Solaris/FishParts/GenericFishScrapItem",
"PremiumPrice": [5, 5],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e951a"
}
}
],
"PropertyTextHash": "946131D0CF5CDF7C2C03BB967DE0DF49",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,459 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "67dadc30e4b6e0e5979c8d56"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/TheHex/Temple1999VendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleBlueprint",
"ItemPrices": [
{
"ItemCount": 195,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleSystemsBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleChassisBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleHelmetBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/1999EntHybridPistolBlueprint",
"ItemPrices": [
{
"ItemCount": 120,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c190"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolBarrelBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c191"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolReceiverBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c192"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolStockBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c193"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumCoreKitA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c194"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumCymbalA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c195"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumFloorTomA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c196"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumSnareA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c197"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c198"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseB",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c199"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseC",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseD",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19b"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseE",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseF",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumSynthKeyboardA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/PhotoBooth/Vania/PhotoboothTileVaniaObjTempleDefense",
"ItemPrices": [
{
"ItemCount": 100,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": false,
"Id": {
"$oid": "67dadc30641da66dc5c1c19f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemCount": 110,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 6000,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 7,
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c1a5"
}
}
],
"PropertyTextHash": "20B13D9EB78FEC80EA32D0687F5BA1AE",
"RequiredGoalTag": "",
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
}
}
}

View File

@ -1,175 +0,0 @@
{
"SettlementNode1": "CorpusShipTileset",
"SettlementNode11": "CorpusShipTileset",
"SettlementNode12": "CorpusShipTileset",
"SettlementNode14": "CorpusShipTileset",
"SettlementNode15": "CorpusShipTileset",
"SettlementNode2": "CorpusShipTileset",
"SettlementNode20": "CorpusShipTileset",
"SettlementNode3": "CorpusShipTileset",
"SolNode1": "CorpusOutpostTileset",
"SolNode10": "CorpusGasCityTileset",
"SolNode100": "CorpusGasCityTileset",
"SolNode101": "CorpusOutpostTileset",
"SolNode102": "CorpusShipTileset",
"SolNode103": "GrineerAsteroidTileset",
"SolNode104": "CorpusShipTileset",
"SolNode105": "GrineerOceanTilesetAnywhere",
"SolNode106": "GrineerSettlementTileset",
"SolNode107": "CorpusOutpostTileset",
"SolNode108": "GrineerAsteroidTileset",
"SolNode109": "CorpusOutpostTileset",
"SolNode11": "GrineerSettlementTileset",
"SolNode113": "GrineerSettlementTileset",
"SolNode118": "CorpusShipTileset",
"SolNode119": "GrineerAsteroidTileset",
"SolNode12": "GrineerAsteroidTileset",
"SolNode121": "CorpusGasCityTileset",
"SolNode122": "GrineerOceanTileset",
"SolNode123": "CorpusShipTileset",
"SolNode125": "CorpusGasCityTileset",
"SolNode126": "CorpusGasCityTileset",
"SolNode127": "CorpusShipTileset",
"SolNode128": "CorpusOutpostTileset",
"SolNode130": "GrineerAsteroidTileset",
"SolNode131": "GrineerShipyardsTileset",
"SolNode132": "GrineerShipyardsTileset",
"SolNode135": "GrineerGalleonTileset",
"SolNode137": "GrineerShipyardsTileset",
"SolNode138": "GrineerShipyardsTileset",
"SolNode139": "GrineerShipyardsTileset",
"SolNode14": "CorpusIcePlanetTilesetCaves",
"SolNode140": "GrineerShipyardsTileset",
"SolNode141": "GrineerShipyardsTileset",
"SolNode144": "GrineerShipyardsTileset",
"SolNode146": "GrineerAsteroidTileset",
"SolNode147": "GrineerShipyardsTileset",
"SolNode149": "GrineerShipyardsTileset",
"SolNode15": "GrineerGalleonTileset",
"SolNode16": "GrineerSettlementTileset",
"SolNode162": "InfestedCorpusShipTileset",
"SolNode164": "InfestedCorpusShipTileset",
"SolNode166": "InfestedCorpusShipTileset",
"SolNode17": "CorpusShipTileset",
"SolNode171": "InfestedCorpusShipTileset",
"SolNode172": "CorpusShipTileset",
"SolNode173": "InfestedCorpusShipTileset",
"SolNode175": "InfestedCorpusShipTileset",
"SolNode177": "GrineerGalleonTileset",
"SolNode18": "GrineerAsteroidTileset",
"SolNode181": "GrineerAsteroidTileset",
"SolNode184": "GrineerGalleonTileset",
"SolNode185": "GrineerGalleonTileset",
"SolNode187": "GrineerAsteroidTileset",
"SolNode188": "GrineerGalleonTileset",
"SolNode189": "GrineerGalleonTileset",
"SolNode19": "GrineerAsteroidTileset",
"SolNode191": "GrineerShipyardsTileset",
"SolNode193": "GrineerAsteroidTileset",
"SolNode195": "GrineerGalleonTileset",
"SolNode196": "GrineerGalleonTileset",
"SolNode2": "CorpusOutpostTileset",
"SolNode20": "GrineerGalleonTileset",
"SolNode203": "CorpusIcePlanetTileset",
"SolNode205": "CorpusIcePlanetTileset",
"SolNode209": "CorpusIcePlanetTileset",
"SolNode21": "CorpusOutpostTileset",
"SolNode210": "CorpusIcePlanetTileset",
"SolNode211": "CorpusIcePlanetTileset",
"SolNode212": "CorpusIcePlanetTileset",
"SolNode214": "CorpusIcePlanetTileset",
"SolNode215": "CorpusShipTileset",
"SolNode216": "CorpusIcePlanetTileset",
"SolNode217": "CorpusIcePlanetTileset",
"SolNode22": "CorpusOutpostTileset",
"SolNode220": "CorpusIcePlanetTileset",
"SolNode223": "GrineerAsteroidTileset",
"SolNode224": "GrineerGalleonTileset",
"SolNode225": "GrineerGalleonTileset",
"SolNode226": "GrineerGalleonTileset",
"SolNode228": "EidolonTileset",
"SolNode23": "CorpusShipTileset",
"SolNode24": "GrineerForestTileset",
"SolNode25": "CorpusGasCityTileset",
"SolNode26": "GrineerForestTileset",
"SolNode30": "GrineerSettlementTileset",
"SolNode300": "OrokinMoonTilesetGrineer",
"SolNode301": "OrokinMoonTilesetGrineer",
"SolNode302": "OrokinMoonTilesetCorpus",
"SolNode304": "OrokinMoonTilesetCorpus",
"SolNode305": "OrokinMoonTilesetGrineer",
"SolNode306": "OrokinMoonTilesetCorpus",
"SolNode307": "OrokinMoonTilesetCorpus",
"SolNode308": "OrokinMoonTilesetCorpus",
"SolNode31": "GrineerGalleonTileset",
"SolNode32": "GrineerGalleonTileset",
"SolNode36": "GrineerSettlementTileset",
"SolNode38": "CorpusOutpostTileset",
"SolNode39": "GrineerForestTileset",
"SolNode4": "CorpusShipTileset",
"SolNode400": "OrokinVoidTileset",
"SolNode401": "OrokinVoidTileset",
"SolNode402": "OrokinVoidTileset",
"SolNode403": "OrokinVoidTileset",
"SolNode404": "OrokinVoidTileset",
"SolNode405": "OrokinVoidTileset",
"SolNode406": "OrokinVoidTileset",
"SolNode407": "OrokinVoidTileset",
"SolNode408": "OrokinVoidTileset",
"SolNode409": "OrokinVoidTileset",
"SolNode41": "GrineerSettlementTileset",
"SolNode410": "OrokinVoidTileset",
"SolNode412": "OrokinVoidTileset",
"SolNode42": "GrineerGalleonTileset",
"SolNode43": "CorpusOutpostTileset",
"SolNode45": "GrineerSettlementTileset",
"SolNode46": "GrineerSettlementTileset",
"SolNode48": "CorpusOutpostTileset",
"SolNode49": "CorpusShipTileset",
"SolNode50": "GrineerAsteroidTileset",
"SolNode51": "CorpusOutpostTileset",
"SolNode53": "CorpusGasCityTileset",
"SolNode56": "CorpusShipTileset",
"SolNode57": "CorpusOutpostTileset",
"SolNode58": "GrineerSettlementTileset",
"SolNode59": "GrineerForestTileset",
"SolNode6": "CorpusOutpostTileset",
"SolNode61": "CorpusShipTileset",
"SolNode62": "CorpusIcePlanetTilesetCaves",
"SolNode64": "GrineerOceanTileset",
"SolNode66": "CorpusOutpostTileset",
"SolNode67": "GrineerAsteroidTileset",
"SolNode68": "GrineerGalleonTileset",
"SolNode70": "GrineerGalleonTileset",
"SolNode706": "OrokinDerelictTileset",
"SolNode707": "OrokinDerelictTileset",
"SolNode708": "OrokinDerelictTileset",
"SolNode709": "OrokinDerelictTileset",
"SolNode710": "OrokinDerelictTileset",
"SolNode711": "OrokinDerelictTileset",
"SolNode712": "OrokinDerelictTileset",
"SolNode713": "OrokinDerelictTileset",
"SolNode72": "CorpusOutpostTileset",
"SolNode73": "CorpusGasCityTileset",
"SolNode74": "CorpusGasCityTileset",
"SolNode741": "GrineerFortressTileset",
"SolNode742": "GrineerFortressTileset",
"SolNode743": "GrineerFortressTileset",
"SolNode744": "GrineerFortressTileset",
"SolNode745": "GrineerFortressTileset",
"SolNode746": "GrineerFortressTileset",
"SolNode747": "GrineerFortressTileset",
"SolNode748": "GrineerFortressTileset",
"SolNode75": "GrineerForestTileset",
"SolNode76": "CorpusShipTileset",
"SolNode78": "CorpusShipTileset",
"SolNode79": "GrineerForestTileset",
"SolNode81": "CorpusShipTileset",
"SolNode82": "GrineerGalleonTileset",
"SolNode84": "CorpusIcePlanetTilesetCaves",
"SolNode88": "CorpusShipTileset",
"SolNode93": "GrineerAsteroidTileset",
"SolNode96": "GrineerGalleonTileset",
"SolNode97": "CorpusGasCityTileset",
"SolNode99": "GrineerSettlementTileset"
}

View File

@ -1,157 +0,0 @@
[
"SettlementNode1",
"SettlementNode11",
"SettlementNode12",
"SettlementNode14",
"SettlementNode15",
"SettlementNode2",
"SettlementNode3",
"SolNode1",
"SolNode10",
"SolNode100",
"SolNode101",
"SolNode102",
"SolNode103",
"SolNode106",
"SolNode107",
"SolNode109",
"SolNode11",
"SolNode113",
"SolNode118",
"SolNode119",
"SolNode12",
"SolNode121",
"SolNode122",
"SolNode123",
"SolNode125",
"SolNode126",
"SolNode128",
"SolNode130",
"SolNode131",
"SolNode132",
"SolNode135",
"SolNode137",
"SolNode138",
"SolNode139",
"SolNode14",
"SolNode140",
"SolNode141",
"SolNode146",
"SolNode147",
"SolNode149",
"SolNode15",
"SolNode153",
"SolNode16",
"SolNode162",
"SolNode164",
"SolNode166",
"SolNode167",
"SolNode17",
"SolNode171",
"SolNode172",
"SolNode173",
"SolNode175",
"SolNode177",
"SolNode18",
"SolNode181",
"SolNode184",
"SolNode185",
"SolNode187",
"SolNode188",
"SolNode189",
"SolNode19",
"SolNode191",
"SolNode195",
"SolNode196",
"SolNode2",
"SolNode20",
"SolNode203",
"SolNode204",
"SolNode205",
"SolNode209",
"SolNode21",
"SolNode211",
"SolNode212",
"SolNode214",
"SolNode215",
"SolNode216",
"SolNode217",
"SolNode22",
"SolNode220",
"SolNode223",
"SolNode224",
"SolNode225",
"SolNode226",
"SolNode23",
"SolNode25",
"SolNode26",
"SolNode27",
"SolNode30",
"SolNode31",
"SolNode36",
"SolNode38",
"SolNode39",
"SolNode4",
"SolNode400",
"SolNode401",
"SolNode402",
"SolNode403",
"SolNode404",
"SolNode405",
"SolNode406",
"SolNode407",
"SolNode408",
"SolNode409",
"SolNode41",
"SolNode410",
"SolNode412",
"SolNode42",
"SolNode43",
"SolNode45",
"SolNode46",
"SolNode48",
"SolNode49",
"SolNode50",
"SolNode56",
"SolNode57",
"SolNode58",
"SolNode59",
"SolNode6",
"SolNode61",
"SolNode62",
"SolNode63",
"SolNode64",
"SolNode66",
"SolNode67",
"SolNode68",
"SolNode70",
"SolNode706",
"SolNode707",
"SolNode708",
"SolNode709",
"SolNode710",
"SolNode711",
"SolNode72",
"SolNode73",
"SolNode74",
"SolNode741",
"SolNode742",
"SolNode743",
"SolNode744",
"SolNode745",
"SolNode746",
"SolNode748",
"SolNode75",
"SolNode76",
"SolNode78",
"SolNode79",
"SolNode81",
"SolNode82",
"SolNode84",
"SolNode85",
"SolNode88",
"SolNode89",
"SolNode93",
"SolNode96",
"SolNode97"
]

View File

@ -401,22 +401,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_evolutionProgress"></h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEvolution();return false;">
<input class="form-control" id="acquire-type-EvolutionProgress" list="datalist-EvolutionProgress" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<table class="table table-hover w-100">
<tbody id="EvolutionProgress-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card mb-3"> <div class="card mb-3">
<h5 class="card-header" data-loc="general_bulkActions"></h5> <h5 class="card-header" data-loc="general_bulkActions"></h5>
<div class="card-body"> <div class="card-body">
@ -427,7 +411,6 @@
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button> <button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button>
<button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button> <button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button>
<button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button> <button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button>
<button class="btn btn-primary" onclick="addMissingEvolutionProgress();" data-loc="inventory_bulkAddEvolutionProgress"></button>
</div> </div>
<div class="mb-2 d-flex flex-wrap gap-2"> <div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);" data-loc="inventory_bulkRankUpSuits"></button> <button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);" data-loc="inventory_bulkRankUpSuits"></button>
@ -436,7 +419,6 @@
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button> <button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button> <button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button> <button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button>
<button class="btn btn-success" onclick="maxRankAllEvolutions();" data-loc="inventory_bulkRankUpEvolutionProgress"></button>
</div> </div>
</div> </div>
</div> </div>
@ -510,9 +492,8 @@
<div class="card mb-3"> <div class="card mb-3">
<h5 class="card-header" data-loc="general_bulkActions"></h5> <h5 class="card-header" data-loc="general_bulkActions"></h5>
<div class="card-body d-flex flex-wrap gap-2"> <div class="card-body d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_addMissingUnrankedMods"></button> <button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_bulkAddMods"></button>
<button class="btn btn-danger" onclick="doRemoveUnrankedMods();" data-loc="mods_removeUnranked"></button> <button class="btn btn-danger" onclick="doRemoveUnrankedMods();" data-loc="mods_removeUnranked"></button>
<button class="btn btn-primary" onclick="doAddMissingMaxRankMods();" data-loc="mods_addMissingMaxRankMods"></button>
</div> </div>
</div> </div>
</div> </div>
@ -594,10 +575,6 @@
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" /> <input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label> <label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dontSubtractConsumables" />
<label class="form-check-label" for="dontSubtractConsumables" data-loc="cheats_dontSubtractConsumables"></label>
</div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" /> <input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
<label class="form-check-label" for="unlockAllShipFeatures" data-loc="cheats_unlockAllShipFeatures"></label> <label class="form-check-label" for="unlockAllShipFeatures" data-loc="cheats_unlockAllShipFeatures"></label>
@ -674,10 +651,6 @@
<input class="form-check-input" type="checkbox" id="noResourceExtractorDronesDamage" /> <input class="form-check-input" type="checkbox" id="noResourceExtractorDronesDamage" />
<label class="form-check-label" for="noResourceExtractorDronesDamage" data-loc="cheats_noResourceExtractorDronesDamage"></label> <label class="form-check-label" for="noResourceExtractorDronesDamage" data-loc="cheats_noResourceExtractorDronesDamage"></label>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="skipClanKeyCrafting" />
<label class="form-check-label" for="skipClanKeyCrafting" data-loc="cheats_skipClanKeyCrafting"></label>
</div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="noDojoRoomBuildStage" /> <input class="form-check-input" type="checkbox" id="noDojoRoomBuildStage" />
<label class="form-check-label" for="noDojoRoomBuildStage" data-loc="cheats_noDojoRoomBuildStage"></label> <label class="form-check-label" for="noDojoRoomBuildStage" data-loc="cheats_noDojoRoomBuildStage"></label>
@ -760,7 +733,6 @@
</datalist> </datalist>
<datalist id="datalist-archonCrystalUpgrades"></datalist> <datalist id="datalist-archonCrystalUpgrades"></datalist>
<datalist id="datalist-OperatorAmps"></datalist> <datalist id="datalist-OperatorAmps"></datalist>
<datalist id="datalist-EvolutionProgress"></datalist>
<datalist id="datalist-ModularParts"></datalist> <datalist id="datalist-ModularParts"></datalist>
<datalist id="datalist-ModularParts-HB_DECK"></datalist> <datalist id="datalist-ModularParts-HB_DECK"></datalist>
<datalist id="datalist-ModularParts-HB_ENGINE"></datalist> <datalist id="datalist-ModularParts-HB_ENGINE"></datalist>

View File

@ -13,7 +13,6 @@ function doLogin() {
} }
function loginFromLocalStorage() { function loginFromLocalStorage() {
const isRegister = registerSubmit;
doLoginRequest( doLoginRequest(
data => { data => {
if (single.getCurrentPath() == "/webui/") { if (single.getCurrentPath() == "/webui/") {
@ -29,7 +28,7 @@ function loginFromLocalStorage() {
}, },
() => { () => {
logout(); logout();
alert(isRegister ? "Registration failed. Account already exists?" : "Login failed"); alert("Login failed");
} }
); );
} }
@ -45,7 +44,7 @@ function doLoginRequest(succ_cb, fail_cb) {
s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind
lang: "en", lang: "en",
date: 1501230947855458660, // ??? date: 1501230947855458660, // ???
ClientType: registerSubmit ? "webui-register" : "webui", ClientType: registerSubmit ? "" : "webui",
PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data
}) })
}); });
@ -78,23 +77,19 @@ function logout() {
function renameAccount() { function renameAccount() {
const newname = window.prompt(loc("code_changeNameConfirm")); const newname = window.prompt(loc("code_changeNameConfirm"));
if (newname) { if (newname) {
revalidateAuthz(() => {
fetch("/custom/renameAccount?" + window.authz + "&newname=" + newname).then(() => { fetch("/custom/renameAccount?" + window.authz + "&newname=" + newname).then(() => {
$(".displayname").text(newname); $(".displayname").text(newname);
updateLocElements(); updateLocElements();
}); });
});
} }
} }
function deleteAccount() { function deleteAccount() {
if (window.confirm(loc("code_deleteAccountConfirm"))) { if (window.confirm(loc("code_deleteAccountConfirm"))) {
revalidateAuthz(() => {
fetch("/custom/deleteAccount?" + window.authz).then(() => { fetch("/custom/deleteAccount?" + window.authz).then(() => {
logout(); logout();
single.loadRoute("/webui/"); // Show login screen single.loadRoute("/webui/"); // Show login screen
}); });
});
} }
} }
@ -187,16 +182,6 @@ const webUiModularWeapons = [
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit" "/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
]; ];
const permanentEvolutionWeapons = new Set([
"/Lotus/Weapons/Tenno/Zariman/LongGuns/PumpShotgun/ZarimanPumpShotgun",
"/Lotus/Weapons/Tenno/Zariman/LongGuns/SemiAutoRifle/ZarimanSemiAutoRifle",
"/Lotus/Weapons/Tenno/Zariman/Melee/Dagger/ZarimanDaggerWeapon",
"/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
"/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
"/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
"/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon"
]);
let uniqueLevelCaps = {}; let uniqueLevelCaps = {};
function fetchItemList() { function fetchItemList() {
window.itemListPromise = new Promise(resolve => { window.itemListPromise = new Promise(resolve => {
@ -375,7 +360,6 @@ function fetchItemList() {
} }
fetchItemList(); fetchItemList();
// Assumes that caller revalidates authz
function updateInventory() { function updateInventory() {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1"); const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => { req.done(data => {
@ -488,7 +472,6 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
revalidateAuthz(() => {
if (item.XP < maxXP) { if (item.XP < maxXP) {
addGearExp(category, item.ItemId.$oid, maxXP - item.XP); addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
} }
@ -508,7 +491,6 @@ function updateInventory() {
} }
} }
} }
});
}; };
a.title = loc("code_maxRank"); a.title = loc("code_maxRank");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`; a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
@ -580,82 +562,6 @@ function updateInventory() {
}); });
}); });
document.getElementById("EvolutionProgress-list").innerHTML = "";
data.EvolutionProgress?.forEach(item => {
const datalist = document.getElementById("datalist-EvolutionProgress");
const optionToRemove = datalist.querySelector(`option[data-key="${item.ItemType}"]`);
if (optionToRemove) {
datalist.removeChild(optionToRemove);
}
const tr = document.createElement("tr");
tr.setAttribute("data-item-type", item.ItemType);
{
const td = document.createElement("td");
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
if (item.Rank != null) {
td.textContent += " | " + loc("code_rank") + ": [" + item.Rank + "/5]";
}
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
if (item.Rank < 5) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
setEvolutionProgress([{ ItemType: item.ItemType, Rank: 5 }]);
};
a.title = loc("code_maxRank");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
td.appendChild(a);
}
if ((permanentEvolutionWeapons.has(item.ItemType) && item.Rank > 0) || item.Rank > 1) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
setEvolutionProgress([{ ItemType: item.ItemType, Rank: item.Rank - 1 }]);
};
a.title = loc("code_rankDown");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>`;
td.appendChild(a);
}
if (item.Rank < 5) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
setEvolutionProgress([{ ItemType: item.ItemType, Rank: item.Rank + 1 }]);
};
a.title = loc("code_rankUp");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"/></svg>`;
td.appendChild(a);
}
tr.appendChild(td);
}
document.getElementById("EvolutionProgress-list").appendChild(tr);
});
const datalistEvolutionProgress = document.querySelectorAll("#datalist-EvolutionProgress option");
const formEvolutionProgress = document.querySelector('form[onsubmit*="doAcquireEvolution()"]');
const giveAllQEvolutionProgress = document.querySelector(
'button[onclick*="addMissingEvolutionProgress()"]'
);
if (datalistEvolutionProgress.length === 0) {
formEvolutionProgress.classList.add("disabled");
formEvolutionProgress.querySelector("input").disabled = true;
formEvolutionProgress.querySelector("button").disabled = true;
giveAllQEvolutionProgress.disabled = true;
}
// Populate quests route // Populate quests route
document.getElementById("QuestKeys-list").innerHTML = ""; document.getElementById("QuestKeys-list").innerHTML = "";
data.QuestKeys.forEach(item => { data.QuestKeys.forEach(item => {
@ -767,18 +673,18 @@ function updateInventory() {
}); });
const datalistQuestKeys = document.querySelectorAll("#datalist-QuestKeys option"); const datalistQuestKeys = document.querySelectorAll("#datalist-QuestKeys option");
const formQuestKeys = document.querySelector("form[onsubmit*=\"doAcquireEquipment('QuestKeys')\"]"); const form = document.querySelector("form[onsubmit*=\"doAcquireEquipment('QuestKeys')\"]");
const giveAllQuestButton = document.querySelector("button[onclick*=\"doBulkQuestUpdate('giveAll')\"]"); const giveAllQuestButton = document.querySelector("button[onclick*=\"doBulkQuestUpdate('giveAll')\"]");
if (datalistQuestKeys.length === 0) { if (datalistQuestKeys.length === 0) {
formQuestKeys.classList.add("disabled"); form.classList.add("disabled");
formQuestKeys.querySelector("input").disabled = true; form.querySelector("input").disabled = true;
formQuestKeys.querySelector("button").disabled = true; form.querySelector("button").disabled = true;
giveAllQuestButton.disabled = true; giveAllQuestButton.disabled = true;
} else { } else {
formQuestKeys.classList.remove("disabled"); form.classList.remove("disabled");
formQuestKeys.querySelector("input").disabled = false; form.querySelector("input").disabled = false;
formQuestKeys.querySelector("button").disabled = false; form.querySelector("button").disabled = false;
giveAllQuestButton.disabled = false; giveAllQuestButton.disabled = false;
} }
@ -1173,16 +1079,6 @@ function doAcquireModularEquipment(category, WeaponType) {
} }
} }
function doAcquireEvolution() {
const uniqueName = getKey(document.getElementById("acquire-type-EvolutionProgress"));
if (!uniqueName) {
$("#acquire-type-EvolutionProgress").addClass("is-invalid").focus();
return;
}
setEvolutionProgress([{ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 }]);
}
$("input[list]").on("input", function () { $("input[list]").on("input", function () {
$(this).removeClass("is-invalid"); $(this).removeClass("is-invalid");
}); });
@ -1220,44 +1116,9 @@ function addMissingEquipment(categories) {
} }
} }
function addMissingEvolutionProgress() {
const requests = [];
document.querySelectorAll("#datalist-EvolutionProgress option").forEach(elm => {
const uniqueName = elm.getAttribute("data-key");
requests.push({ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 });
});
if (requests.length != 0 && window.confirm(loc("code_addItemsConfirm").split("|COUNT|").join(requests.length))) {
setEvolutionProgress(requests);
}
}
function maxRankAllEvolutions() {
revalidateAuthz(() => {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => {
const requests = [];
data.EvolutionProgress.forEach(item => {
if (item.Rank < 5) {
requests.push({
ItemType: item.ItemType,
Rank: 5
});
}
});
if (Object.keys(requests).length > 0) {
return setEvolutionProgress(requests);
}
toast(loc("code_noEquipmentToRankUp"));
});
});
}
function maxRankAllEquipment(categories) { function maxRankAllEquipment(categories) {
revalidateAuthz(() => {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1"); const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => { req.done(data => {
window.itemListPromise.then(itemMap => { window.itemListPromise.then(itemMap => {
const batchData = {}; const batchData = {};
@ -1286,8 +1147,7 @@ function maxRankAllEquipment(categories) {
for (const exaltedType of itemMap[item.ItemType].exalted) { for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType); const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType);
if (exaltedItem) { if (exaltedItem) {
const exaltedCap = const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) { if (exaltedItem.XP < exaltedCap) {
batchData["SpecialItems"] ??= []; batchData["SpecialItems"] ??= [];
batchData["SpecialItems"].push({ batchData["SpecialItems"].push({
@ -1309,10 +1169,8 @@ function maxRankAllEquipment(categories) {
toast(loc("code_noEquipmentToRankUp")); toast(loc("code_noEquipmentToRankUp"));
}); });
}); });
});
} }
// Assumes that caller revalidates authz
function addGearExp(category, oid, xp) { function addGearExp(category, oid, xp) {
const data = {}; const data = {};
data[category] = [ data[category] = [
@ -1321,6 +1179,7 @@ function addGearExp(category, oid, xp) {
XP: xp XP: xp
} }
]; ];
revalidateAuthz(() => {
$.post({ $.post({
url: "/custom/addXp?" + window.authz, url: "/custom/addXp?" + window.authz,
contentType: "application/json", contentType: "application/json",
@ -1330,6 +1189,7 @@ function addGearExp(category, oid, xp) {
updateInventory(); updateInventory();
} }
}); });
});
} }
function sendBatchGearExp(data) { function sendBatchGearExp(data) {
@ -1443,19 +1303,6 @@ function maturePet(oid, revert) {
}); });
} }
function setEvolutionProgress(requests) {
revalidateAuthz(() => {
const req = $.post({
url: "/custom/setEvolutionProgress?" + window.authz,
contentType: "application/json",
data: JSON.stringify(requests)
});
req.done(() => {
updateInventory();
});
});
}
function doAcquireMiscItems() { function doAcquireMiscItems() {
const uniqueName = getKey(document.getElementById("miscitem-type")); const uniqueName = getKey(document.getElementById("miscitem-type"));
if (!uniqueName) { if (!uniqueName) {
@ -1603,7 +1450,6 @@ function doAcquireMod() {
const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id); const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id);
function doChangeSettings() { function doChangeSettings() {
revalidateAuthz(() => {
fetch("/custom/config?" + window.authz) fetch("/custom/config?" + window.authz)
.then(response => response.json()) .then(response => response.json())
.then(json => { .then(json => {
@ -1630,7 +1476,6 @@ function doChangeSettings() {
updateInventory(); updateInventory();
}); });
}); });
});
} }
// Cheats route // Cheats route
@ -1802,14 +1647,6 @@ function doRemoveUnrankedMods() {
}); });
} }
function doAddMissingMaxRankMods() {
revalidateAuthz(() => {
fetch("/custom/addMissingMaxRankMods?" + window.authz).then(() => {
updateInventory();
});
});
}
// Powersuit Route // Powersuit Route
single.getRoute("#powersuit-route").on("beforeload", function () { single.getRoute("#powersuit-route").on("beforeload", function () {
@ -1883,7 +1720,6 @@ function doChangeSupportedSyndicate() {
} }
function doAddCurrency(currency) { function doAddCurrency(currency) {
revalidateAuthz(() => {
$.post({ $.post({
url: "/custom/addCurrency?" + window.authz, url: "/custom/addCurrency?" + window.authz,
contentType: "application/json", contentType: "application/json",
@ -1894,29 +1730,24 @@ function doAddCurrency(currency) {
}).then(function () { }).then(function () {
updateInventory(); updateInventory();
}); });
});
} }
function doQuestUpdate(operation, itemType) { function doQuestUpdate(operation, itemType) {
revalidateAuthz(() => {
$.post({ $.post({
url: "/custom/manageQuests?" + window.authz + "&operation=" + operation + "&itemType=" + itemType, url: "/custom/manageQuests?" + window.authz + "&operation=" + operation + "&itemType=" + itemType,
contentType: "application/json" contentType: "application/json"
}).then(function () { }).then(function () {
updateInventory(); updateInventory();
}); });
});
} }
function doBulkQuestUpdate(operation) { function doBulkQuestUpdate(operation) {
revalidateAuthz(() => {
$.post({ $.post({
url: "/custom/manageQuests?" + window.authz + "&operation=" + operation, url: "/custom/manageQuests?" + window.authz + "&operation=" + operation,
contentType: "application/json" contentType: "application/json"
}).then(function () { }).then(function () {
updateInventory(); updateInventory();
}); });
});
} }
function toast(text) { function toast(text) {

View File

@ -34,8 +34,6 @@ dict = {
code_rerollsNumber: `Anzahl der Umrollversuche`, code_rerollsNumber: `Anzahl der Umrollversuche`,
code_viewStats: `Statistiken anzeigen`, code_viewStats: `Statistiken anzeigen`,
code_rank: `Rang`, code_rank: `Rang`,
code_rankUp: `Rang erhöhen`,
code_rankDown: `Rang verringern`,
code_count: `Anzahl`, code_count: `Anzahl`,
code_focusAllUnlocked: `Alle Fokus-Schulen sind bereits freigeschaltet.`, code_focusAllUnlocked: `Alle Fokus-Schulen sind bereits freigeschaltet.`,
code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`, code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`,
@ -62,7 +60,7 @@ dict = {
login_emailLabel: `E-Mail-Adresse`, login_emailLabel: `E-Mail-Adresse`,
login_passwordLabel: `Passwort`, login_passwordLabel: `Passwort`,
login_loginButton: `Anmelden`, login_loginButton: `Anmelden`,
login_registerButton: `Registrieren`, login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Abmelden`, navbar_logout: `Abmelden`,
navbar_renameAccount: `Account umbenennen`, navbar_renameAccount: `Account umbenennen`,
navbar_deleteAccount: `Account löschen`, navbar_deleteAccount: `Account löschen`,
@ -84,23 +82,20 @@ dict = {
inventory_sentinelWeapons: `Wächter-Waffen`, inventory_sentinelWeapons: `Wächter-Waffen`,
inventory_operatorAmps: `Verstärker`, inventory_operatorAmps: `Verstärker`,
inventory_hoverboards: `K-Drives`, inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moas`, inventory_moaPets: `Moa`,
inventory_kubrowPets: `Bestien`, inventory_kubrowPets: `Bestien`,
inventory_evolutionProgress: `Incarnon-Entwicklungsfortschritte`,
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`, inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`, inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`,
inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`, inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`,
inventory_bulkAddSpaceWeapons: `Fehlende Archwing-Waffen hinzufügen`, inventory_bulkAddSpaceWeapons: `Fehlende Archwing-Waffen hinzufügen`,
inventory_bulkAddSentinels: `Fehlende Wächter hinzufügen`, inventory_bulkAddSentinels: `Fehlende Wächter hinzufügen`,
inventory_bulkAddSentinelWeapons: `Fehlende Wächter-Waffen hinzufügen`, inventory_bulkAddSentinelWeapons: `Fehlende Wächter-Waffen hinzufügen`,
inventory_bulkAddEvolutionProgress: `Fehlende Incarnon-Entwicklungsfortschritte hinzufügen`,
inventory_bulkRankUpSuits: `Alle Warframes auf Max. Rang`, inventory_bulkRankUpSuits: `Alle Warframes auf Max. Rang`,
inventory_bulkRankUpWeapons: `Alle Waffen auf Max. Rang`, inventory_bulkRankUpWeapons: `Alle Waffen auf Max. Rang`,
inventory_bulkRankUpSpaceSuits: `Alle Archwings auf Max. Rang`, inventory_bulkRankUpSpaceSuits: `Alle Archwings auf Max. Rang`,
inventory_bulkRankUpSpaceWeapons: `Alle Archwing-Waffen auf Max. Rang`, inventory_bulkRankUpSpaceWeapons: `Alle Archwing-Waffen auf Max. Rang`,
inventory_bulkRankUpSentinels: `Alle Wächter auf Max. Rang`, inventory_bulkRankUpSentinels: `Alle Wächter auf Max. Rang`,
inventory_bulkRankUpSentinelWeapons: `Alle Wächter-Waffen auf Max. Rang`, inventory_bulkRankUpSentinelWeapons: `Alle Wächter-Waffen auf Max. Rang`,
inventory_bulkRankUpEvolutionProgress: `Alle Incarnon-Entwicklungsfortschritte auf Max. Rang`,
quests_list: `Quests`, quests_list: `Quests`,
quests_completeAll: `Alle Quests abschließen`, quests_completeAll: `Alle Quests abschließen`,
@ -120,9 +115,8 @@ dict = {
mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`, mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,
mods_rivens: `Rivens`, mods_rivens: `Rivens`,
mods_mods: `Mods`, mods_mods: `Mods`,
mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`, mods_bulkAddMods: `Fehlende Mods hinzufügen`,
mods_removeUnranked: `Mods ohne Rang entfernen`, mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
mods_addMissingMaxRankMods: `Fehlende Mods mit Max. Rang hinzufügen`,
cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>|DISPLAYNAME|</code> zu <code>administratorNames</code> in der config.json hinzu.`, cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>|DISPLAYNAME|</code> zu <code>administratorNames</code> in der config.json hinzu.`,
cheats_server: `Server`, cheats_server: `Server`,
cheats_skipTutorial: `Tutorial überspringen`, cheats_skipTutorial: `Tutorial überspringen`,
@ -134,7 +128,6 @@ dict = {
cheats_infiniteEndo: `Unendlich Endo`, cheats_infiniteEndo: `Unendlich Endo`,
cheats_infiniteRegalAya: `Unendlich Reines Aya`, cheats_infiniteRegalAya: `Unendlich Reines Aya`,
cheats_infiniteHelminthMaterials: `Unendlich Helminth-Materialien`, cheats_infiniteHelminthMaterials: `Unendlich Helminth-Materialien`,
cheats_dontSubtractConsumables: `Verbrauchsgegenstände (Ausrüstung) nicht verbrauchen`,
cheats_unlockAllShipFeatures: `Alle Schiffs-Funktionen freischalten`, cheats_unlockAllShipFeatures: `Alle Schiffs-Funktionen freischalten`,
cheats_unlockAllShipDecorations: `Alle Schiffsdekorationen freischalten`, cheats_unlockAllShipDecorations: `Alle Schiffsdekorationen freischalten`,
cheats_unlockAllFlavourItems: `Alle <abbr title=\"Animationssets, Glyphen, Farbpaletten usw.\">Sammlerstücke</abbr> freischalten`, cheats_unlockAllFlavourItems: `Alle <abbr title=\"Animationssets, Glyphen, Farbpaletten usw.\">Sammlerstücke</abbr> freischalten`,
@ -154,7 +147,6 @@ dict = {
cheats_noKimCooldowns: `Keine Wartezeit bei KIM`, cheats_noKimCooldowns: `Keine Wartezeit bei KIM`,
cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`, cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`, cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
cheats_skipClanKeyCrafting: `Clan-Schlüsselherstellung überspringen`,
cheats_noDojoRoomBuildStage: `Kein Dojo-Raum-Bauvorgang`, cheats_noDojoRoomBuildStage: `Kein Dojo-Raum-Bauvorgang`,
cheats_noDojoDecoBuildStage: `Kein Dojo-Deko-Bauvorgang`, cheats_noDojoDecoBuildStage: `Kein Dojo-Deko-Bauvorgang`,
cheats_fastDojoRoomDestruction: `Schnelle Dojo-Raum-Zerstörung`, cheats_fastDojoRoomDestruction: `Schnelle Dojo-Raum-Zerstörung`,

View File

@ -33,8 +33,6 @@ dict = {
code_rerollsNumber: `Number of rerolls`, code_rerollsNumber: `Number of rerolls`,
code_viewStats: `View Stats`, code_viewStats: `View Stats`,
code_rank: `Rank`, code_rank: `Rank`,
code_rankUp: `Rank up`,
code_rankDown: `Rank down`,
code_count: `Count`, code_count: `Count`,
code_focusAllUnlocked: `All focus schools are already unlocked.`, code_focusAllUnlocked: `All focus schools are already unlocked.`,
code_focusUnlocked: `Unlocked |COUNT| new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that.`, code_focusUnlocked: `Unlocked |COUNT| new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that.`,
@ -83,23 +81,20 @@ dict = {
inventory_sentinelWeapons: `Sentinel Weapons`, inventory_sentinelWeapons: `Sentinel Weapons`,
inventory_operatorAmps: `Amps`, inventory_operatorAmps: `Amps`,
inventory_hoverboards: `K-Drives`, inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moas`, inventory_moaPets: `Moa`,
inventory_kubrowPets: `Beasts`, inventory_kubrowPets: `Beasts`,
inventory_evolutionProgress: `Incarnon Evolution Progress`,
inventory_bulkAddSuits: `Add Missing Warframes`, inventory_bulkAddSuits: `Add Missing Warframes`,
inventory_bulkAddWeapons: `Add Missing Weapons`, inventory_bulkAddWeapons: `Add Missing Weapons`,
inventory_bulkAddSpaceSuits: `Add Missing Archwings`, inventory_bulkAddSpaceSuits: `Add Missing Archwings`,
inventory_bulkAddSpaceWeapons: `Add Missing Archwing Weapons`, inventory_bulkAddSpaceWeapons: `Add Missing Archwing Weapons`,
inventory_bulkAddSentinels: `Add Missing Sentinels`, inventory_bulkAddSentinels: `Add Missing Sentinels`,
inventory_bulkAddSentinelWeapons: `Add Missing Sentinel Weapons`, inventory_bulkAddSentinelWeapons: `Add Missing Sentinel Weapons`,
inventory_bulkAddEvolutionProgress: `Add Missing Incarnon Evolution Progress`,
inventory_bulkRankUpSuits: `Max Rank All Warframes`, inventory_bulkRankUpSuits: `Max Rank All Warframes`,
inventory_bulkRankUpWeapons: `Max Rank All Weapons`, inventory_bulkRankUpWeapons: `Max Rank All Weapons`,
inventory_bulkRankUpSpaceSuits: `Max Rank All Archwings`, inventory_bulkRankUpSpaceSuits: `Max Rank All Archwings`,
inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`, inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
inventory_bulkRankUpSentinels: `Max Rank All Sentinels`, inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`, inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
inventory_bulkRankUpEvolutionProgress: `Max Rank All Incarnon Evolution Progress`,
quests_list: `Quests`, quests_list: `Quests`,
quests_completeAll: `Complete All Quests`, quests_completeAll: `Complete All Quests`,
@ -119,9 +114,8 @@ dict = {
mods_fingerprintHelp: `Need help with the fingerprint?`, mods_fingerprintHelp: `Need help with the fingerprint?`,
mods_rivens: `Rivens`, mods_rivens: `Rivens`,
mods_mods: `Mods`, mods_mods: `Mods`,
mods_addMissingUnrankedMods: `Add Missing Unranked Mods`, mods_bulkAddMods: `Add Missing Mods`,
mods_removeUnranked: `Remove Unranked Mods`, mods_removeUnranked: `Remove Unranked Mods`,
mods_addMissingMaxRankMods: `Add Missing Max Rank Mods`,
cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>|DISPLAYNAME|</code> to <code>administratorNames</code> in the config.json.`, cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>|DISPLAYNAME|</code> to <code>administratorNames</code> in the config.json.`,
cheats_server: `Server`, cheats_server: `Server`,
cheats_skipTutorial: `Skip Tutorial`, cheats_skipTutorial: `Skip Tutorial`,
@ -133,7 +127,6 @@ dict = {
cheats_infiniteEndo: `Infinite Endo`, cheats_infiniteEndo: `Infinite Endo`,
cheats_infiniteRegalAya: `Infinite Regal Aya`, cheats_infiniteRegalAya: `Infinite Regal Aya`,
cheats_infiniteHelminthMaterials: `Infinite Helminth Materials`, cheats_infiniteHelminthMaterials: `Infinite Helminth Materials`,
cheats_dontSubtractConsumables: `Don't Subtract Consumables`,
cheats_unlockAllShipFeatures: `Unlock All Ship Features`, cheats_unlockAllShipFeatures: `Unlock All Ship Features`,
cheats_unlockAllShipDecorations: `Unlock All Ship Decorations`, cheats_unlockAllShipDecorations: `Unlock All Ship Decorations`,
cheats_unlockAllFlavourItems: `Unlock All <abbr title=\"Animation Sets, Glyphs, Palettes, etc.\">Flavor Items</abbr>`, cheats_unlockAllFlavourItems: `Unlock All <abbr title=\"Animation Sets, Glyphs, Palettes, etc.\">Flavor Items</abbr>`,
@ -153,7 +146,6 @@ dict = {
cheats_noKimCooldowns: `No KIM Cooldowns`, cheats_noKimCooldowns: `No KIM Cooldowns`,
cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`, cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`, cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
cheats_skipClanKeyCrafting: `Skip Clan Key Crafting`,
cheats_noDojoRoomBuildStage: `No Dojo Room Build Stage`, cheats_noDojoRoomBuildStage: `No Dojo Room Build Stage`,
cheats_noDojoDecoBuildStage: `No Dojo Deco Build Stage`, cheats_noDojoDecoBuildStage: `No Dojo Deco Build Stage`,
cheats_fastDojoRoomDestruction: `Fast Dojo Room Destruction`, cheats_fastDojoRoomDestruction: `Fast Dojo Room Destruction`,

View File

@ -34,8 +34,6 @@ dict = {
code_rerollsNumber: `Cantidad de reintentos`, code_rerollsNumber: `Cantidad de reintentos`,
code_viewStats: `Ver estadísticas`, code_viewStats: `Ver estadísticas`,
code_rank: `Rango`, code_rank: `Rango`,
code_rankUp: `Subir de rango`,
code_rankDown: `Bajar de rango`,
code_count: `Cantidad`, code_count: `Cantidad`,
code_focusAllUnlocked: `Todas las escuelas de enfoque ya están desbloqueadas.`, code_focusAllUnlocked: `Todas las escuelas de enfoque ya están desbloqueadas.`,
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.`,
@ -62,7 +60,7 @@ dict = {
login_emailLabel: `Dirección de correo electrónico`, login_emailLabel: `Dirección de correo electrónico`,
login_passwordLabel: `Contraseña`, login_passwordLabel: `Contraseña`,
login_loginButton: `Iniciar sesión`, login_loginButton: `Iniciar sesión`,
login_registerButton: `Registrarse`, login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Cerrar sesión`, navbar_logout: `Cerrar sesión`,
navbar_renameAccount: `Renombrar cuenta`, navbar_renameAccount: `Renombrar cuenta`,
navbar_deleteAccount: `Eliminar cuenta`, navbar_deleteAccount: `Eliminar cuenta`,
@ -84,23 +82,20 @@ dict = {
inventory_sentinelWeapons: `Armas de centinela`, inventory_sentinelWeapons: `Armas de centinela`,
inventory_operatorAmps: `Amps`, inventory_operatorAmps: `Amps`,
inventory_hoverboards: `K-Drives`, inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moas`, inventory_moaPets: `Moa`,
inventory_kubrowPets: `Bestias`, inventory_kubrowPets: `Bestias`,
inventory_evolutionProgress: `Progreso de evolución Incarnon`,
inventory_bulkAddSuits: `Agregar Warframes faltantes`, inventory_bulkAddSuits: `Agregar Warframes faltantes`,
inventory_bulkAddWeapons: `Agregar armas faltantes`, inventory_bulkAddWeapons: `Agregar armas faltantes`,
inventory_bulkAddSpaceSuits: `Agregar Archwings faltantes`, inventory_bulkAddSpaceSuits: `Agregar Archwings faltantes`,
inventory_bulkAddSpaceWeapons: `Agregar armas Archwing faltantes`, inventory_bulkAddSpaceWeapons: `Agregar armas Archwing faltantes`,
inventory_bulkAddSentinels: `Agregar centinelas faltantes`, inventory_bulkAddSentinels: `Agregar centinelas faltantes`,
inventory_bulkAddSentinelWeapons: `Agregar armas de centinela faltantes`, inventory_bulkAddSentinelWeapons: `Agregar armas de centinela faltantes`,
inventory_bulkAddEvolutionProgress: `Completar el progreso de evolución Incarnon faltante`,
inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`, inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`,
inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`, inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`,
inventory_bulkRankUpSpaceSuits: `Maximizar rango de todos los Archwings`, inventory_bulkRankUpSpaceSuits: `Maximizar rango de todos los Archwings`,
inventory_bulkRankUpSpaceWeapons: `Maximizar rango de todas las armas Archwing`, inventory_bulkRankUpSpaceWeapons: `Maximizar rango de todas las armas Archwing`,
inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`, inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`, inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
quests_list: `Misiones`, quests_list: `Misiones`,
quests_completeAll: `Completar todas las misiones`, quests_completeAll: `Completar todas las misiones`,
@ -120,9 +115,8 @@ 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_addMissingUnrankedMods: `Agregar mods sin rango faltantes`, mods_bulkAddMods: `Agregar mods faltantes`,
mods_removeUnranked: `Quitar mods sin rango`, mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>|DISPLAYNAME|</code> a <code>administratorNames</code> en el archivo config.json.`, cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>|DISPLAYNAME|</code> a <code>administratorNames</code> en el archivo config.json.`,
cheats_server: `Servidor`, cheats_server: `Servidor`,
cheats_skipTutorial: `Omitir tutorial`, cheats_skipTutorial: `Omitir tutorial`,
@ -134,7 +128,6 @@ dict = {
cheats_infiniteEndo: `Endo infinito`, cheats_infiniteEndo: `Endo infinito`,
cheats_infiniteRegalAya: `Aya Real infinita`, cheats_infiniteRegalAya: `Aya Real infinita`,
cheats_infiniteHelminthMaterials: `Materiales Helminto infinitos`, cheats_infiniteHelminthMaterials: `Materiales Helminto infinitos`,
cheats_dontSubtractConsumables: `No restar consumibles`,
cheats_unlockAllShipFeatures: `Desbloquear todas las funciones de nave`, cheats_unlockAllShipFeatures: `Desbloquear todas las funciones de nave`,
cheats_unlockAllShipDecorations: `Desbloquear todas las decoraciones de nave`, cheats_unlockAllShipDecorations: `Desbloquear todas las decoraciones de nave`,
cheats_unlockAllFlavourItems: `Desbloquear todos los <abbr title="Conjuntos de animaciones, glifos, paletas, etc.">ítems estéticos</abbr>`, cheats_unlockAllFlavourItems: `Desbloquear todos los <abbr title="Conjuntos de animaciones, glifos, paletas, etc.">ítems estéticos</abbr>`,
@ -154,7 +147,6 @@ dict = {
cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`, cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`,
cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`, cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`,
cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`, cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`,
cheats_skipClanKeyCrafting: `Saltar la fabricación de la llave de clan`,
cheats_noDojoRoomBuildStage: `Sin etapa de construcción de sala del dojo`, cheats_noDojoRoomBuildStage: `Sin etapa de construcción de sala del dojo`,
cheats_noDojoDecoBuildStage: `Sin etapa de construcción de decoraciones del dojo`, cheats_noDojoDecoBuildStage: `Sin etapa de construcción de decoraciones del dojo`,
cheats_fastDojoRoomDestruction: `Destrucción rápida de salas del dojo`, cheats_fastDojoRoomDestruction: `Destrucción rápida de salas del dojo`,

View File

@ -34,8 +34,6 @@ dict = {
code_rerollsNumber: `Nombre de rerolls`, code_rerollsNumber: `Nombre de rerolls`,
code_viewStats: `Voir les stats`, code_viewStats: `Voir les stats`,
code_rank: `Rang`, code_rank: `Rang`,
code_rankUp: `[UNTRANSLATED] Rank up`,
code_rankDown: `[UNTRANSLATED] Rank down`,
code_count: `Quantité`, code_count: `Quantité`,
code_focusAllUnlocked: `Les écoles de Focus sont déjà déverrouillées.`, code_focusAllUnlocked: `Les écoles de Focus sont déjà déverrouillées.`,
code_focusUnlocked: `|COUNT| écoles de Focus déverrouillées ! Synchronisation de l'inventaire nécessaire.`, code_focusUnlocked: `|COUNT| écoles de Focus déverrouillées ! Synchronisation de l'inventaire nécessaire.`,
@ -84,23 +82,20 @@ dict = {
inventory_sentinelWeapons: `Armes de sentinelles`, inventory_sentinelWeapons: `Armes de sentinelles`,
inventory_operatorAmps: `Amplificateurs`, inventory_operatorAmps: `Amplificateurs`,
inventory_hoverboards: `K-Drives`, inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moas`, inventory_moaPets: `Moa`,
inventory_kubrowPets: `Bêtes`, inventory_kubrowPets: `Bêtes`,
inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`,
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`, inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
inventory_bulkAddWeapons: `Ajouter les armes manquantes`, inventory_bulkAddWeapons: `Ajouter les armes manquantes`,
inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`, inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`,
inventory_bulkAddSpaceWeapons: `Ajouter les armes d'Archwing manquantes`, inventory_bulkAddSpaceWeapons: `Ajouter les armes d'Archwing manquantes`,
inventory_bulkAddSentinels: `Ajouter les Sentinelles manquantes`, inventory_bulkAddSentinels: `Ajouter les Sentinelles manquantes`,
inventory_bulkAddSentinelWeapons: `Ajouter les armes de Sentinelles manquantes`, inventory_bulkAddSentinelWeapons: `Ajouter les armes de Sentinelles manquantes`,
inventory_bulkAddEvolutionProgress: `[UNTRANSLATED] Add Missing Incarnon Evolution Progress`,
inventory_bulkRankUpSuits: `Toutes les Warframes rang max`, inventory_bulkRankUpSuits: `Toutes les Warframes rang max`,
inventory_bulkRankUpWeapons: `Toutes les armes rang max`, inventory_bulkRankUpWeapons: `Toutes les armes rang max`,
inventory_bulkRankUpSpaceSuits: `Tous les Archwings rang max`, inventory_bulkRankUpSpaceSuits: `Tous les Archwings rang max`,
inventory_bulkRankUpSpaceWeapons: `Toutes les armes d'Archwing rang max`, inventory_bulkRankUpSpaceWeapons: `Toutes les armes d'Archwing rang max`,
inventory_bulkRankUpSentinels: `Toutes les Sentinelles rang max`, inventory_bulkRankUpSentinels: `Toutes les Sentinelles rang max`,
inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles rang max`, inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles rang max`,
inventory_bulkRankUpEvolutionProgress: `[UNTRANSLATED] Max Rank All Incarnon Evolution Progress`,
quests_list: `Quêtes`, quests_list: `Quêtes`,
quests_completeAll: `Compléter toutes les quêtes`, quests_completeAll: `Compléter toutes les quêtes`,
@ -120,9 +115,8 @@ dict = {
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`, mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
mods_rivens: `Rivens`, mods_rivens: `Rivens`,
mods_mods: `Mods`, mods_mods: `Mods`,
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`, mods_bulkAddMods: `Ajouter les mods manquants`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`, mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>|DISPLAYNAME|</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`, cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>|DISPLAYNAME|</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`,
cheats_server: `Serveur`, cheats_server: `Serveur`,
cheats_skipTutorial: `Passer le tutoriel`, cheats_skipTutorial: `Passer le tutoriel`,
@ -134,7 +128,6 @@ dict = {
cheats_infiniteEndo: `Endo infini`, cheats_infiniteEndo: `Endo infini`,
cheats_infiniteRegalAya: `Aya Raffiné infini`, cheats_infiniteRegalAya: `Aya Raffiné infini`,
cheats_infiniteHelminthMaterials: `Ressources d'Helminth infinies`, cheats_infiniteHelminthMaterials: `Ressources d'Helminth infinies`,
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
cheats_unlockAllShipFeatures: `Débloquer tous les segments du vaisseau`, cheats_unlockAllShipFeatures: `Débloquer tous les segments du vaisseau`,
cheats_unlockAllShipDecorations: `Débloquer toutes les décorations du vaisseau`, cheats_unlockAllShipDecorations: `Débloquer toutes les décorations du vaisseau`,
cheats_unlockAllFlavourItems: `Débloquer tous les <abbr title=\"Animations, Glyphes, Palettes, etc.\">Flavor Items</abbr>`, cheats_unlockAllFlavourItems: `Débloquer tous les <abbr title=\"Animations, Glyphes, Palettes, etc.\">Flavor Items</abbr>`,
@ -154,7 +147,6 @@ dict = {
cheats_noKimCooldowns: `Aucun cooldown sur le KIM`, cheats_noKimCooldowns: `Aucun cooldown sur le KIM`,
cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`, cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`, cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
cheats_skipClanKeyCrafting: `[UNTRANSLATED] Skip Clan Key Crafting`,
cheats_noDojoRoomBuildStage: `Aucune attente (construction des salles)`, cheats_noDojoRoomBuildStage: `Aucune attente (construction des salles)`,
cheats_noDojoDecoBuildStage: `Aucune attente (construction des décorations)`, cheats_noDojoDecoBuildStage: `Aucune attente (construction des décorations)`,
cheats_fastDojoRoomDestruction: `Destruction de salle instantanée (Dojo)`, cheats_fastDojoRoomDestruction: `Destruction de salle instantanée (Dojo)`,

View File

@ -34,8 +34,6 @@ dict = {
code_rerollsNumber: `Количество циклов`, code_rerollsNumber: `Количество циклов`,
code_viewStats: `Просмотр характеристики`, code_viewStats: `Просмотр характеристики`,
code_rank: `Ранг`, code_rank: `Ранг`,
code_rankUp: `Повысить Ранг`,
code_rankDown: `Понизить Ранг`,
code_count: `Количество`, code_count: `Количество`,
code_focusAllUnlocked: `Все школы фокуса уже разблокированы.`, code_focusAllUnlocked: `Все школы фокуса уже разблокированы.`,
code_focusUnlocked: `Разблокировано |COUNT| новых школ фокуса! Для отображения изменений в игре потребуется обновление инвентаря. Посещение навигации — самый простой способ этого добиться.`, code_focusUnlocked: `Разблокировано |COUNT| новых школ фокуса! Для отображения изменений в игре потребуется обновление инвентаря. Посещение навигации — самый простой способ этого добиться.`,
@ -86,21 +84,18 @@ dict = {
inventory_hoverboards: `К-Драйвы`, inventory_hoverboards: `К-Драйвы`,
inventory_moaPets: `МОА`, inventory_moaPets: `МОА`,
inventory_kubrowPets: `Звери`, inventory_kubrowPets: `Звери`,
inventory_evolutionProgress: `Прогресс эволюции Инкарнонов`,
inventory_bulkAddSuits: `Добавить отсутствующие варфреймы`, inventory_bulkAddSuits: `Добавить отсутствующие варфреймы`,
inventory_bulkAddWeapons: `Добавить отсутствующее оружие`, inventory_bulkAddWeapons: `Добавить отсутствующее оружие`,
inventory_bulkAddSpaceSuits: `Добавить отсутствующие арчвинги`, inventory_bulkAddSpaceSuits: `Добавить отсутствующие арчвинги`,
inventory_bulkAddSpaceWeapons: `Добавить отсутствующее оружие арчвингов`, inventory_bulkAddSpaceWeapons: `Добавить отсутствующее оружие арчвингов`,
inventory_bulkAddSentinels: `Добавить отсутствующих стражей`, inventory_bulkAddSentinels: `Добавить отсутствующих стражей`,
inventory_bulkAddSentinelWeapons: `Добавить отсутствующее оружие стражей`, inventory_bulkAddSentinelWeapons: `Добавить отсутствующее оружие стражей`,
inventory_bulkAddEvolutionProgress: `Добавить отсуствующий прогресс эволюции Инкарнонов`,
inventory_bulkRankUpSuits: `Максимальный ранг всех варфреймов`, inventory_bulkRankUpSuits: `Максимальный ранг всех варфреймов`,
inventory_bulkRankUpWeapons: `Максимальный ранг всего оружия`, inventory_bulkRankUpWeapons: `Максимальный ранг всего оружия`,
inventory_bulkRankUpSpaceSuits: `Максимальный ранг всех арчвингов`, inventory_bulkRankUpSpaceSuits: `Максимальный ранг всех арчвингов`,
inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`, inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`,
inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`, inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`,
inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`, inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`,
inventory_bulkRankUpEvolutionProgress: `Максимальный ранг всех эволюций Инкарнонов`,
quests_list: `Квесты`, quests_list: `Квесты`,
quests_completeAll: `Завершить все квесты`, quests_completeAll: `Завершить все квесты`,
@ -120,9 +115,8 @@ dict = {
mods_fingerprintHelp: `Нужна помощь с отпечатком?`, mods_fingerprintHelp: `Нужна помощь с отпечатком?`,
mods_rivens: `Моды Разлома`, mods_rivens: `Моды Разлома`,
mods_mods: `Моды`, mods_mods: `Моды`,
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`, mods_bulkAddMods: `Добавить отсутствующие моды`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`, mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`, cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`,
cheats_server: `Сервер`, cheats_server: `Сервер`,
cheats_skipTutorial: `Пропустить обучение`, cheats_skipTutorial: `Пропустить обучение`,
@ -134,7 +128,6 @@ dict = {
cheats_infiniteEndo: `Бесконечное эндо`, cheats_infiniteEndo: `Бесконечное эндо`,
cheats_infiniteRegalAya: `Бесконечная Королевская Айя`, cheats_infiniteRegalAya: `Бесконечная Королевская Айя`,
cheats_infiniteHelminthMaterials: `Бесконечные Выделения Гельминта`, cheats_infiniteHelminthMaterials: `Бесконечные Выделения Гельминта`,
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
cheats_unlockAllShipFeatures: `Разблокировать все функции корабля`, cheats_unlockAllShipFeatures: `Разблокировать все функции корабля`,
cheats_unlockAllShipDecorations: `Разблокировать все украшения корабля`, cheats_unlockAllShipDecorations: `Разблокировать все украшения корабля`,
cheats_unlockAllFlavourItems: `Разблокировать все <abbr title=\"Наборы анимаций, глифы, палитры и т. д.\">уникальные предметы</abbr>`, cheats_unlockAllFlavourItems: `Разблокировать все <abbr title=\"Наборы анимаций, глифы, палитры и т. д.\">уникальные предметы</abbr>`,
@ -154,7 +147,6 @@ dict = {
cheats_noKimCooldowns: `Чаты KIM без кулдауна`, cheats_noKimCooldowns: `Чаты KIM без кулдауна`,
cheats_instantResourceExtractorDrones: `Мгновенные Экстракторы Ресурсов`, cheats_instantResourceExtractorDrones: `Мгновенные Экстракторы Ресурсов`,
cheats_noResourceExtractorDronesDamage: `Без урона по дронам-сборщикам`, cheats_noResourceExtractorDronesDamage: `Без урона по дронам-сборщикам`,
cheats_skipClanKeyCrafting: `[UNTRANSLATED] Skip Clan Key Crafting`,
cheats_noDojoRoomBuildStage: `Мгновенное Строительтво Комнат Додзё`, cheats_noDojoRoomBuildStage: `Мгновенное Строительтво Комнат Додзё`,
cheats_noDojoDecoBuildStage: `Мгновенное Строительтво Декораций Додзё`, cheats_noDojoDecoBuildStage: `Мгновенное Строительтво Декораций Додзё`,
cheats_fastDojoRoomDestruction: `Мгновенные Уничтожение Комнат Додзё`, cheats_fastDojoRoomDestruction: `Мгновенные Уничтожение Комнат Додзё`,

View File

@ -34,8 +34,6 @@ dict = {
code_rerollsNumber: `洗卡次数`, code_rerollsNumber: `洗卡次数`,
code_viewStats: `查看属性`, code_viewStats: `查看属性`,
code_rank: `等级`, code_rank: `等级`,
code_rankUp: `[UNTRANSLATED] Rank up`,
code_rankDown: `[UNTRANSLATED] Rank down`,
code_count: `数量`, code_count: `数量`,
code_focusAllUnlocked: `所有专精学派均已解锁。`, code_focusAllUnlocked: `所有专精学派均已解锁。`,
code_focusUnlocked: `已解锁 |COUNT| 个新专精学派!需要游戏内仓库更新才能生效,您可以通过访问星图来触发仓库更新。`, code_focusUnlocked: `已解锁 |COUNT| 个新专精学派!需要游戏内仓库更新才能生效,您可以通过访问星图来触发仓库更新。`,
@ -86,21 +84,18 @@ dict = {
inventory_hoverboards: `K式悬浮板`, inventory_hoverboards: `K式悬浮板`,
inventory_moaPets: `恐鸟`, inventory_moaPets: `恐鸟`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`, inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`,
inventory_bulkAddSuits: `添加缺失战甲`, inventory_bulkAddSuits: `添加缺失战甲`,
inventory_bulkAddWeapons: `添加缺失武器`, inventory_bulkAddWeapons: `添加缺失武器`,
inventory_bulkAddSpaceSuits: `添加缺失Archwing`, inventory_bulkAddSpaceSuits: `添加缺失Archwing`,
inventory_bulkAddSpaceWeapons: `添加缺失Archwing武器`, inventory_bulkAddSpaceWeapons: `添加缺失Archwing武器`,
inventory_bulkAddSentinels: `添加缺失守护`, inventory_bulkAddSentinels: `添加缺失守护`,
inventory_bulkAddSentinelWeapons: `添加缺失守护武器`, inventory_bulkAddSentinelWeapons: `添加缺失守护武器`,
inventory_bulkAddEvolutionProgress: `[UNTRANSLATED] Add Missing Incarnon Evolution Progress`,
inventory_bulkRankUpSuits: `所有战甲升满级`, inventory_bulkRankUpSuits: `所有战甲升满级`,
inventory_bulkRankUpWeapons: `所有武器升满级`, inventory_bulkRankUpWeapons: `所有武器升满级`,
inventory_bulkRankUpSpaceSuits: `所有Archwing升满级`, inventory_bulkRankUpSpaceSuits: `所有Archwing升满级`,
inventory_bulkRankUpSpaceWeapons: `所有Archwing武器升满级`, inventory_bulkRankUpSpaceWeapons: `所有Archwing武器升满级`,
inventory_bulkRankUpSentinels: `所有守护升满级`, inventory_bulkRankUpSentinels: `所有守护升满级`,
inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`, inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
inventory_bulkRankUpEvolutionProgress: `[UNTRANSLATED] Max Rank All Incarnon Evolution Progress`,
quests_list: `任务`, quests_list: `任务`,
quests_completeAll: `完成所有任务`, quests_completeAll: `完成所有任务`,
@ -120,9 +115,8 @@ dict = {
mods_fingerprintHelp: `需要印记相关的帮助?`, mods_fingerprintHelp: `需要印记相关的帮助?`,
mods_rivens: `裂罅MOD`, mods_rivens: `裂罅MOD`,
mods_mods: `Mods`, mods_mods: `Mods`,
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`, mods_bulkAddMods: `添加缺失MOD`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`, mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
cheats_administratorRequirement: `您必须是管理员才能使用此功能。要成为管理员,请将 <code>|DISPLAYNAME|</code> 添加到 config.json 的 <code>administratorNames</code> 中。`, cheats_administratorRequirement: `您必须是管理员才能使用此功能。要成为管理员,请将 <code>|DISPLAYNAME|</code> 添加到 config.json 的 <code>administratorNames</code> 中。`,
cheats_server: `服务器`, cheats_server: `服务器`,
cheats_skipTutorial: `跳过教程`, cheats_skipTutorial: `跳过教程`,
@ -134,7 +128,6 @@ dict = {
cheats_infiniteEndo: `无限内融核心`, cheats_infiniteEndo: `无限内融核心`,
cheats_infiniteRegalAya: `无限御品阿耶`, cheats_infiniteRegalAya: `无限御品阿耶`,
cheats_infiniteHelminthMaterials: `无限Helminth材料`, cheats_infiniteHelminthMaterials: `无限Helminth材料`,
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
cheats_unlockAllShipFeatures: `解锁所有飞船功能`, cheats_unlockAllShipFeatures: `解锁所有飞船功能`,
cheats_unlockAllShipDecorations: `解锁所有飞船装饰`, cheats_unlockAllShipDecorations: `解锁所有飞船装饰`,
cheats_unlockAllFlavourItems: `解锁所有<abbr title=\"动画组合、图标、调色板等\">装饰物品</abbr>`, cheats_unlockAllFlavourItems: `解锁所有<abbr title=\"动画组合、图标、调色板等\">装饰物品</abbr>`,
@ -154,7 +147,6 @@ dict = {
cheats_noKimCooldowns: `[UNTRANSLATED] No KIM Cooldowns`, cheats_noKimCooldowns: `[UNTRANSLATED] No KIM Cooldowns`,
cheats_instantResourceExtractorDrones: `即时资源采集无人机`, cheats_instantResourceExtractorDrones: `即时资源采集无人机`,
cheats_noResourceExtractorDronesDamage: `[UNTRANSLATED] No Resource Extractor Drones Damage`, cheats_noResourceExtractorDronesDamage: `[UNTRANSLATED] No Resource Extractor Drones Damage`,
cheats_skipClanKeyCrafting: `[UNTRANSLATED] Skip Clan Key Crafting`,
cheats_noDojoRoomBuildStage: `无视道场房间建造阶段`, cheats_noDojoRoomBuildStage: `无视道场房间建造阶段`,
cheats_noDojoDecoBuildStage: `[UNTRANSLATED] No Dojo Deco Build Stage`, cheats_noDojoDecoBuildStage: `[UNTRANSLATED] No Dojo Deco Build Stage`,
cheats_fastDojoRoomDestruction: `快速拆除道场房间`, cheats_fastDojoRoomDestruction: `快速拆除道场房间`,