Compare commits

...

21 Commits
main ... main

Author SHA1 Message Date
9468768947 fix: weapon seed's low dword being sign extended (#1914)
JavaScript's semantics here are incredibly stupid, but basically if the initial DWORD's high WORD's MSB is true, the number would become negative after the shift left by 16. Then when ORing it with the highDword, the initial DWORD would be sign-extended to a QWORD, meaning the high DWORD would become all 1s, basically cancelling out the entire OR operation.

Reviewed-on: OpenWF/SpaceNinjaServer#1914
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-29 12:28:01 -07:00
0af7f41201 fix: unset LibraryPersonalTarget after completing it (#1913)
Reviewed-on: OpenWF/SpaceNinjaServer#1913
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-29 12:27:47 -07:00
de1e2a25f2 fix(webui): ensure that all requests using authz revalidate it (#1911)
Closes #1907

Reviewed-on: OpenWF/SpaceNinjaServer#1911
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-29 12:27:38 -07:00
1cf7b41d3f chore: note that random element functions could return undefined (#1910)
We should be explicit about the fact that we expect the arrays to not be empty.

Reviewed-on: OpenWF/SpaceNinjaServer#1910
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-29 12:27:25 -07:00
ab9cc685eb fix: exclude capture as a mission type for sorties (#1909)
Closes #1865

Reviewed-on: OpenWF/SpaceNinjaServer#1909
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-29 02:05:18 -07:00
743b784754 chore(webui): use plural form of "Moa", just to stay consistent with the other categories (#1905)
Reviewed-on: OpenWF/SpaceNinjaServer#1905
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-04-28 14:01:35 -07:00
5df533a7fb chore: auto-generate "daily special" for fish vendors (#1902)
Trying to go a bit more towards an "auto-generate by default" approach, with manual overrides where needed.

Reviewed-on: OpenWF/SpaceNinjaServer#1902
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:01:17 -07:00
9417aa3c84 fix: only consider market-listed blueprints for login reward (#1900)
Closes #1882

Reviewed-on: OpenWF/SpaceNinjaServer#1900
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:01:02 -07:00
a1872e2b07 chore: simplify getInnateDamageTag (#1899)
Reviewed-on: OpenWF/SpaceNinjaServer#1899
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:00:51 -07:00
9042e85355 feat: infested lich rewards (#1898)
Closes #1884

Reviewed-on: OpenWF/SpaceNinjaServer#1898
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:00:38 -07:00
66ee550ccd feat: refresh duviri seed when mood changes (#1895)
Closes #1887

Reviewed-on: OpenWF/SpaceNinjaServer#1895
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:00:22 -07:00
7a295a86ec fix: handle boosters in store item utilities (#1894)
e.g. `/Lotus/Types/StoreItems/Boosters/AffinityBoosterStoreItem`

Reviewed-on: OpenWF/SpaceNinjaServer#1894
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 14:00:06 -07:00
88d00eaaa1 feat: weaken nemesis (#1893)
Closes #1885

Reviewed-on: OpenWF/SpaceNinjaServer#1893
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 13:59:54 -07:00
1e8f2fc766 chore: comment out mixed fields in inventory (#1892)
If they are needed in the future, they schould be properly schema'd.

Reviewed-on: OpenWF/SpaceNinjaServer#1892
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-28 13:58:39 -07:00
0d842ade90 chore(webui): update German translation (#1904)
Reviewed-on: OpenWF/SpaceNinjaServer#1904
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-04-28 05:14:25 -07:00
4e3a2e17ee chore: removing unnecessary entries in allScans.json (#1903)
Reviewed-on: OpenWF/SpaceNinjaServer#1903
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-04-28 03:36:39 -07:00
61864b2be1 chore(webui): update to Spanish translation (#1901)
Reviewed-on: OpenWF/SpaceNinjaServer#1901
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-04-27 19:39:49 -07:00
45748fa8be fix: import failing for LotusCustomization from live (#1891)
Reviewed-on: OpenWF/SpaceNinjaServer#1891
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-27 14:20:52 -07:00
afec59e8a6 feat: skipClanKeyCrafting cheat (#1883)
Closes #1843

Reviewed-on: OpenWF/SpaceNinjaServer#1883
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-27 12:38:55 -07:00
ee1a49f5f2 feat: handle NemesisKillConvert at missionInventoryUpdate (#1880)
Closes #1848

Reviewed-on: OpenWF/SpaceNinjaServer#1880
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-27 12:38:48 -07:00
9e94083875 feat: handle KubrowPetEggs in missionInventoryUpdate (#1876)
Closes #1866

Reviewed-on: OpenWF/SpaceNinjaServer#1876
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-27 12:36:00 -07:00
40 changed files with 985 additions and 714 deletions

View File

@ -38,6 +38,7 @@
"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.58", "warframe-public-export-plus": "^0.5.59",
"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.58", "version": "0.5.59",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.58.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.59.tgz",
"integrity": "sha512-2G3tKcoblUl7S3Rkk5k/qH+VGZBUmU2QjtIrEO/Bt6UlgO83s648elkNdDKOLBKXnxIsa194nVwz+ci1K86sXg==" "integrity": "sha512-/SUCVjngVDBz6gahz7CdVLywtHLODL6O5nmNtQcxFDUwrUGnF1lETcG8/UO+WLeGxBVAy4BDPbq+9ZWlYZM4uQ=="
}, },
"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.58", "warframe-public-export-plus": "^0.5.59",
"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

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

View File

@ -1,8 +1,14 @@
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 { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService"; import {
import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService"; deleteGuild,
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";
@ -41,14 +47,7 @@ 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);
const recipeChanges = [ giveClanKey(inventory, inventoryChanges);
{
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))!;
@ -96,14 +95,9 @@ 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 Recipes"); const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes");
inventory.GuildId = new Types.ObjectId(req.query.clanId as string); inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
addRecipes(inventory, [ giveClanKey(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,8 +2,9 @@ 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 } from "@/src/services/guildService"; import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService";
import { addRecipes, getInventory } from "@/src/services/inventoryService"; import { 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);
@ -26,26 +27,15 @@ export const createGuildController: RequestHandler = async (req, res) => {
rank: 0 rank: 0
}); });
const inventory = await getInventory(accountId, "GuildId Recipes"); const inventory = await getInventory(accountId, "GuildId LevelKeys Recipes");
inventory.GuildId = guild._id; inventory.GuildId = guild._id;
addRecipes(inventory, [ const inventoryChanges: IInventoryChanges = {};
{ 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

@ -13,6 +13,7 @@ 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;
@ -48,7 +49,7 @@ export const inboxController: RequestHandler = async (req, res) => {
await addItems( await addItems(
inventory, inventory,
attachmentItems.map(attItem => ({ attachmentItems.map(attItem => ({
ItemType: attItem, ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1 ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
})), })),
inventoryChanges inventoryChanges

View File

@ -18,10 +18,12 @@ 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);
@ -87,7 +89,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 (
@ -96,9 +98,20 @@ export const inventoryController: RequestHandler = async (request, response) =>
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
) { ) {
handleSubsumeCompletion(inventory); handleSubsumeCompletion(inventory);
await inventory.save(); //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();
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query)); response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
}; };
@ -274,7 +287,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 = toOid(new Types.ObjectId()); inventoryResponse.LastInventorySync = undefined;
// 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

@ -141,7 +141,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,8 +2,12 @@ import {
consumeModCharge, consumeModCharge,
encodeNemesisGuess, encodeNemesisGuess,
getInfNodes, getInfNodes,
getKnifeUpgrade,
getNemesisPasscode, getNemesisPasscode,
IKnifeResponse getNemesisPasscodeModTypes,
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";
@ -14,6 +18,8 @@ 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,
@ -99,50 +105,45 @@ 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 // Increase antivirus if correct antivirus mod is installed
let antivirusGain = 5;
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 response: IKnifeResponse = {}; const response: IKnifeResponse = {};
for (const upgrade of body.knife!.AttachedUpgrades) { if (result1 == 0 || result2 == 0 || result3 == 0) {
switch (upgrade.ItemType) { let antivirusGain = 5;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod": const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
antivirusGain += 10; const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades); const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
break; const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod": for (const upgrade of body.knife!.AttachedUpgrades) {
antivirusGain += 10; switch (upgrade.ItemType) {
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades); case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod":
break; antivirusGain += 10;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod": // Instant Secure consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
antivirusGain += 15; break;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades); case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod":
break; antivirusGain += 10;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod": // Immuno Shield consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
antivirusGain += 15; break;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades); case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod": // Instant Secure
break; antivirusGain += 15;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod": consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
antivirusGain += 10; break;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades); case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod": // Immuno Shield
break; antivirusGain += 15;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod":
antivirusGain += 10;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
}
} }
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);
@ -170,18 +171,7 @@ 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") {
let weapons: readonly string[]; const weapons = getWeaponsForManifest(body.target.manifest);
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 {
@ -223,6 +213,38 @@ 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)}`);
@ -274,48 +296,19 @@ 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?: { knife?: IKnife;
Item: IEquipmentClient;
Skins: IWeaponSkinClient[];
ModSlot: number;
CustSlot: number;
AttachedUpgrades: IUpgradeClient[];
HiddenWhenHolstered: boolean;
};
} }
const kuvaLichVersionSixWeapons = [ // interface INemesisWeakenRequest {
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon", // target: INemesisClient;
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak", // knife: IKnife;
"/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 = [ interface IKnife {
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher", Item: IEquipmentClient;
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor", Skins: IWeaponSkinClient[];
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle", ModSlot: number;
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra", CustSlot: number;
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron", AttachedUpgrades: IUpgradeClient[];
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron", HiddenWhenHolstered: boolean;
"/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

@ -1,12 +1,14 @@
import { ExportRegions } from "warframe-public-export-plus"; import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes"; import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { SRng } from "@/src/services/rngService"; import { getRewardAtPercentage, 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 } from "../services/inventoryService"; import { addMods, generateRewardSeed } from "../services/inventoryService";
import { isArchwingMission } from "../services/worldStateService"; 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 = [];
@ -38,17 +40,59 @@ 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 passcode = [rng.randomInt(0, 7)]; const choices = [0, 1, 2, 3, 5, 6, 7];
let choiceIndex = rng.randomInt(0, choices.length - 1);
const passcode = [choices[choiceIndex]];
if (nemesis.Faction != "FC_INFESTATION") { if (nemesis.Faction != "FC_INFESTATION") {
passcode.push(rng.randomInt(0, 7)); choices.splice(choiceIndex, 1);
passcode.push(rng.randomInt(0, 7)); choiceIndex = rng.randomInt(0, choices.length - 1);
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,
@ -79,6 +123,31 @@ 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,
@ -129,3 +198,181 @@ 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

@ -781,9 +781,25 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
{ _id: false } { _id: false }
); );
const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({ // const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({
Persona: String // 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>(
{ {
@ -1039,6 +1055,8 @@ 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 },
@ -1260,11 +1278,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
PrevOwners: Number, PrevOwners: Number,
SecondInCommand: Boolean, SecondInCommand: Boolean,
Weakened: Boolean, Weakened: Boolean,
InfNodes: [infNodeSchema], InfNodes: { type: [infNodeSchema], default: undefined },
HenchmenKilled: Number, HenchmenKilled: Number,
HintProgress: Number, HintProgress: Number,
Hints: [Number], Hints: { type: [Number], default: undefined },
GuessHistory: [Number], GuessHistory: { type: [Number], default: undefined },
MissionCount: Number, MissionCount: Number,
LastEnc: Number LastEnc: Number
}, },
@ -1381,7 +1399,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,
@ -1431,7 +1449,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],
@ -1477,7 +1495,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],
@ -1516,7 +1534,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],
@ -1536,7 +1554,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],
@ -1571,10 +1589,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,
@ -1604,13 +1622,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: [Schema.Types.Mixed], NemesisHistory: { type: [nemesisSchema], default: undefined },
LastNemesisAllySpawnTime: Schema.Types.Mixed, //LastNemesisAllySpawnTime: Schema.Types.Mixed,
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social) //TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
Settings: settingsSchema, Settings: settingsSchema,
@ -1624,7 +1642,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,
@ -1644,23 +1662,24 @@ 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 },
@ -1741,6 +1760,9 @@ 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

@ -44,6 +44,7 @@ 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 { getInventory } from "@/src/services/inventoryService"; import { addLevelKeys, addRecipes, combineInventoryChanges, 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 {
@ -657,6 +657,32 @@ 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,6 +2,7 @@ import { Types } from "mongoose";
import { import {
IEquipmentClient, IEquipmentClient,
IEquipmentDatabase, IEquipmentDatabase,
IItemConfig,
IOperatorConfigClient, IOperatorConfigClient,
IOperatorConfigDatabase IOperatorConfigDatabase
} from "../types/inventoryTypes/commonInventoryTypes"; } from "../types/inventoryTypes/commonInventoryTypes";
@ -174,6 +175,20 @@ const convertNemesis = (client: INemesisClient): INemesisDatabase => {
}; };
}; };
// 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) {
@ -352,7 +367,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 = client.LotusCustomization; db.LotusCustomization = convertItemConfig(client.LotusCustomization);
} }
if (client.CollectibleSeries !== undefined) { if (client.CollectibleSeries !== undefined) {
db.CollectibleSeries = client.CollectibleSeries; db.CollectibleSeries = client.CollectibleSeries;

View File

@ -26,7 +26,9 @@ import {
Status, Status,
IKubrowPetDetailsDatabase, IKubrowPetDetailsDatabase,
ITraits, ITraits,
ICalendarProgress 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";
@ -79,6 +81,7 @@ import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from ".
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 { getWorldState } from "./worldStateService";
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
export const createInventory = async ( export const createInventory = async (
accountOwnerId: Types.ObjectId, accountOwnerId: Types.ObjectId,
@ -327,7 +330,8 @@ 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) {
@ -530,6 +534,12 @@ export const addItem = async (
] ]
}); });
} }
if (targetFingerprint) {
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisWeaponTargetFingerprint;
defaultOverwrites.UpgradeType = targetFingerprintObj.ItemType;
defaultOverwrites.UpgradeFingerprint = JSON.stringify(targetFingerprintObj.UpgradeFingerprint);
defaultOverwrites.ItemName = targetFingerprintObj.Name;
}
const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites); const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites);
if (weapon.additionalItems) { if (weapon.additionalItems) {
for (const item of weapon.additionalItems) { for (const item of weapon.additionalItems) {
@ -544,6 +554,27 @@ export const addItem = async (
premiumPurchase 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
const miscItemChanges = [ const miscItemChanges = [
@ -1767,7 +1798,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 {
@ -1816,6 +1847,25 @@ 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 => { export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
@ -1851,3 +1901,78 @@ export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICal
return inventory.CalendarProgress; 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,6 +17,7 @@ import {
dict_uk, dict_uk,
dict_zh, dict_zh,
ExportArcanes, ExportArcanes,
ExportBoosters,
ExportCustoms, ExportCustoms,
ExportDrones, ExportDrones,
ExportGear, ExportGear,
@ -217,15 +218,30 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
}; };
export const isStoreItem = (type: string): boolean => { export const isStoreItem = (type: string): boolean => {
return type.startsWith("/Lotus/StoreItems/"); return type.startsWith("/Lotus/StoreItems/") || type in ExportBoosters;
}; };
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 => {
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length); if (type.startsWith("/Lotus/StoreItems/")) {
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,7 +77,6 @@ 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);
@ -95,15 +94,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 (unmasteredItems.has(recipe.resultType)) { if (!recipe.excludeFromMarket && unmasteredItems.has(recipe.resultType)) {
eligibleRecipes.push(uniqueName); eligibleRecipes.push(uniqueName);
} }
} }
if (eligibleRecipes.length == 0) { if (eligibleRecipes.length == 0) {
// This account has all warframes and weapons already mastered (filthy cheater), need a different reward. // This account has all applicable 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

@ -35,6 +35,8 @@ import {
combineInventoryChanges, combineInventoryChanges,
generateRewardSeed, generateRewardSeed,
getCalendarProgress, getCalendarProgress,
giveNemesisPetRecipe,
giveNemesisWeaponRecipe,
updateCurrency, updateCurrency,
updateSyndicate updateSyndicate
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
@ -53,7 +55,7 @@ import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.
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 } from "@/src/helpers/nemesisHelpers"; import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } 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";
@ -378,6 +380,7 @@ 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;
@ -513,6 +516,16 @@ 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);
@ -602,6 +615,60 @@ export const addMissionInventoryUpdates = async (
} }
break; 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)) {
@ -864,7 +931,7 @@ export const addMissionRewards = async (
if (rewardInfo.useVaultManifest) { if (rewardInfo.useVaultManifest) {
MissionRewards.push({ MissionRewards.push({
StoreItem: getRandomElement(corruptedMods), StoreItem: getRandomElement(corruptedMods)!,
ItemCount: 1 ItemCount: 1
}); });
} }

View File

@ -47,12 +47,12 @@ export const createGarden = (): IGardeningDatabase => {
Name: "Garden0", Name: "Garden0",
Plants: [ Plants: [
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 0 PlotIndex: 0
}, },
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 1 PlotIndex: 1
} }
@ -62,12 +62,12 @@ export const createGarden = (): IGardeningDatabase => {
Name: "Garden1", Name: "Garden1",
Plants: [ Plants: [
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 0 PlotIndex: 0
}, },
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 1 PlotIndex: 1
} }
@ -77,12 +77,12 @@ export const createGarden = (): IGardeningDatabase => {
Name: "Garden2", Name: "Garden2",
Plants: [ Plants: [
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 0 PlotIndex: 0
}, },
{ {
PlantType: getRandomElement(plantTypes), PlantType: getRandomElement(plantTypes)!,
EndTime: endTime, EndTime: endTime,
PlotIndex: 1 PlotIndex: 1
} }

View File

@ -6,7 +6,7 @@ export interface IRngResult {
probability: number; probability: number;
} }
export const getRandomElement = <T>(arr: T[]): T => { export const getRandomElement = <T>(arr: T[]): T | undefined => {
return arr[Math.floor(Math.random() * arr.length)]; return arr[Math.floor(Math.random() * arr.length)];
}; };
@ -18,7 +18,10 @@ 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;
}; };
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => { export const getRewardAtPercentage = <T extends { probability: number }>(
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);
@ -110,7 +113,7 @@ export class CRng {
return min; return min;
} }
randomElement<T>(arr: T[]): T { randomElement<T>(arr: T[]): T | undefined {
return arr[Math.floor(this.random() * arr.length)]; return arr[Math.floor(this.random() * arr.length)];
} }
@ -142,7 +145,7 @@ export class SRng {
return min; return min;
} }
randomElement<T>(arr: T[]): T { randomElement<T>(arr: T[]): T | undefined {
return arr[this.randomInt(0, arr.length - 1)]; return arr[this.randomInt(0, arr.length - 1)];
} }

View File

@ -1,4 +1,5 @@
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 { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
@ -6,7 +7,6 @@ import { ExportVendors, IRange } 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";
@ -22,12 +22,10 @@ import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorI
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 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 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";
@ -36,7 +34,6 @@ import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVe
const rawVendorManifests: IVendorManifest[] = [ const rawVendorManifests: IVendorManifest[] = [
ArchimedeanVendorManifest, ArchimedeanVendorManifest,
DeimosEntratiFragmentVendorProductsManifest, DeimosEntratiFragmentVendorProductsManifest,
DeimosFishmongerVendorManifest,
DeimosHivemindCommisionsManifestFishmonger, DeimosHivemindCommisionsManifestFishmonger,
DeimosHivemindCommisionsManifestPetVendor, DeimosHivemindCommisionsManifestPetVendor,
DeimosHivemindCommisionsManifestProspector, DeimosHivemindCommisionsManifestProspector,
@ -52,12 +49,10 @@ const rawVendorManifests: IVendorManifest[] = [
HubsRailjackCrewMemberVendorManifest, HubsRailjackCrewMemberVendorManifest,
MaskSalesmanManifest, MaskSalesmanManifest,
Nova1999ConquestShopManifest, Nova1999ConquestShopManifest,
OstronFishmongerVendorManifest,
OstronPetVendorManifest, OstronPetVendorManifest,
OstronProspectorVendorManifest, OstronProspectorVendorManifest,
RadioLegionIntermission12VendorManifest, RadioLegionIntermission12VendorManifest,
SolarisDebtTokenVendorRepossessionsManifest, SolarisDebtTokenVendorRepossessionsManifest,
SolarisFishmongerVendorManifest,
SolarisProspectorVendorManifest, SolarisProspectorVendorManifest,
Temple1999VendorManifest, Temple1999VendorManifest,
TeshinHardModeVendorManifest, // uses preprocessing TeshinHardModeVendorManifest, // uses preprocessing
@ -87,17 +82,11 @@ const generatableVendors: IGeneratableVendorInfo[] = [
cycleOffset: 1744934400_000, cycleOffset: 1744934400_000,
cycleDuration: 4 * unixTimesInMs.day cycleDuration: 4 * unixTimesInMs.day
}, },
{
_id: { $oid: "5be4a159b144f3cdf1c22efa" },
TypeName: "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorManifest",
RandomSeedType: "VRST_FLAVOUR_TEXT",
cycleDuration: unixTimesInMs.hour
},
{ {
_id: { $oid: "61ba123467e5d37975aeeb03" }, _id: { $oid: "61ba123467e5d37975aeeb03" },
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest", TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
RandomSeedType: "VRST_FLAVOUR_TEXT", RandomSeedType: "VRST_FLAVOUR_TEXT",
cycleDuration: unixTimesInMs.week 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" },
@ -105,6 +94,10 @@ const generatableVendors: IGeneratableVendorInfo[] = [
// } // }
]; ];
const getVendorOid = (typeName: string): string => {
return "5be4a159b144f3cd" + catBreadHash(typeName).toString(16).padStart(8, "0");
};
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => { 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) {
@ -116,6 +109,14 @@ 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;
}; };
@ -130,6 +131,17 @@ 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;
}; };
@ -195,12 +207,12 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex)); const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
const manifest = ExportVendors[vendorInfo.TypeName]; const manifest = ExportVendors[vendorInfo.TypeName];
const offersToAdd = []; const offersToAdd = [];
if (manifest.numItems && manifest.numItems.minValue != manifest.numItems.maxValue) { if (manifest.numItems && !manifest.isOneBinPerCycle) {
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue); const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) { while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) {
// TODO: Consider per-bin item limits // TODO: Consider per-bin item limits
// TODO: Consider item probability weightings // TODO: Consider item probability weightings
offersToAdd.push(rng.randomElement(manifest.items)); offersToAdd.push(rng.randomElement(manifest.items)!);
} }
} else { } else {
let binThisCycle; let binThisCycle;
@ -244,7 +256,7 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) { for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
let itemPrice: { type: string; count: IRange }; let itemPrice: { type: string; count: IRange };
do { do {
itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin]); itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin])!;
} while (item.ItemPrices.find(x => x.ItemType == itemPrice.type)); } while (item.ItemPrices.find(x => x.ItemType == itemPrice.type));
item.ItemPrices.push({ item.ItemPrices.push({
ItemType: itemPrice.type, ItemType: itemPrice.type,
@ -263,11 +275,18 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
) * rawItem.credits.step; ) * rawItem.credits.step;
item.RegularPrice = [value, value]; 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 = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
if (vendorInfo.RandomSeedType == "VRST_WEAPON") { if (vendorInfo.RandomSeedType == "VRST_WEAPON") {
const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff); const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
item.LocTagRandSeed = (BigInt(highDword) << 32n) | BigInt(item.LocTagRandSeed); item.LocTagRandSeed = (BigInt(highDword) << 32n) | (BigInt(item.LocTagRandSeed) & 0xffffffffn);
} }
} }
processed.ItemManifest.push(item); processed.ItemManifest.push(item);

View File

@ -225,7 +225,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
const seed = new CRng(day).randomInt(0, 0xffff); const seed = new CRng(day).randomInt(0, 0xffff);
const rng = new CRng(seed); 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",
@ -269,7 +269,10 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) && sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
key in sortieTilesets key in sortieTilesets
) { ) {
if (!availableMissionIndexes.includes(value.missionIndex)) { if (
value.missionIndex != 5 && // Sorties do not have capture missions
!availableMissionIndexes.includes(value.missionIndex)
) {
availableMissionIndexes.push(value.missionIndex); availableMissionIndexes.push(value.missionIndex);
} }
nodes.push(key); nodes.push(key);
@ -299,7 +302,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
missionIndex = ExportRegions[node].missionIndex; missionIndex = ExportRegions[node].missionIndex;
if (missionIndex != 28) { if (missionIndex != 28) {
missionIndex = rng.randomElement(availableMissionIndexes); missionIndex = rng.randomElement(availableMissionIndexes)!;
} }
} while ( } while (
specialMissionTypes.indexOf(missionIndex) != -1 && specialMissionTypes.indexOf(missionIndex) != -1 &&
@ -309,7 +312,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
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({
@ -343,7 +346,7 @@ 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, missionType,
@ -386,7 +389,7 @@ const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
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)!
}; };
}; };
@ -405,7 +408,7 @@ const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge =>
_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)!
}; };
}; };
@ -422,7 +425,7 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
_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)!
}; };
}; };
@ -1193,7 +1196,7 @@ export const getLiteSortie = (week: number): ILiteSortie => {
"MT_EXTERMINATION", "MT_EXTERMINATION",
"MT_SABOTAGE", "MT_SABOTAGE",
"MT_RESCUE" "MT_RESCUE"
]), ])!,
node: firstNode node: firstNode
}, },
{ {
@ -1203,8 +1206,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",

View File

@ -43,6 +43,7 @@ export interface IInventoryDatabase
| "RecentVendorPurchases" | "RecentVendorPurchases"
| "NextRefill" | "NextRefill"
| "Nemesis" | "Nemesis"
| "NemesisHistory"
| "EntratiVaultCountResetDate" | "EntratiVaultCountResetDate"
| "BrandedSuits" | "BrandedSuits"
| "LockedWeaponGroup" | "LockedWeaponGroup"
@ -51,6 +52,7 @@ export interface IInventoryDatabase
| "LastLiteSortieReward" | "LastLiteSortieReward"
| "CrewMembers" | "CrewMembers"
| "QualifyingInvasions" | "QualifyingInvasions"
| "LastInventorySync"
| TEquipmentKey | TEquipmentKey
>, >,
InventoryDatabaseEquipment { InventoryDatabaseEquipment {
@ -79,6 +81,7 @@ 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;
@ -87,6 +90,7 @@ export interface IInventoryDatabase
LastLiteSortieReward?: ILastSortieRewardDatabase[]; LastLiteSortieReward?: ILastSortieRewardDatabase[];
CrewMembers: ICrewMemberDatabase[]; CrewMembers: ICrewMemberDatabase[];
QualifyingInvasions: IInvasionProgressDatabase[]; QualifyingInvasions: IInvasionProgressDatabase[];
LastInventorySync?: Types.ObjectId;
} }
export interface IQuestKeyDatabase { export interface IQuestKeyDatabase {
@ -256,7 +260,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[];
@ -266,10 +270,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[];
@ -291,19 +295,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[];
@ -311,17 +315,17 @@ 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;
@ -331,17 +335,17 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
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[];
@ -902,8 +906,8 @@ export interface IPendingRecipeDatabase {
ItemType: string; ItemType: string;
CompletionDate: Date; CompletionDate: Date;
ItemId: IOid; ItemId: IOid;
TargetItemId?: string; // likely related to liches TargetItemId?: string; // unsure what this is for
TargetFingerprint?: string; // likely related to liches TargetFingerprint?: string;
LongGuns?: IEquipmentDatabase[]; LongGuns?: IEquipmentDatabase[];
Pistols?: IEquipmentDatabase[]; Pistols?: IEquipmentDatabase[];
Melee?: IEquipmentDatabase[]; Melee?: IEquipmentDatabase[];
@ -951,6 +955,17 @@ 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",

View File

@ -21,7 +21,9 @@ import {
ILockedWeaponGroupClient, ILockedWeaponGroupClient,
ILoadOutPresets, ILoadOutPresets,
IInvasionProgressClient, IInvasionProgressClient,
IWeaponSkinClient IWeaponSkinClient,
IKubrowPetEggClient,
INemesisClient
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
import { IGroup } from "./loginTypes"; import { IGroup } from "./loginTypes";
@ -73,6 +75,14 @@ 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;
@ -118,6 +128,7 @@ 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

View File

@ -1097,7 +1097,5 @@
"/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",
"/Lotus/Types/Enemies/Grineer/AIWeek/Avatars/BeastMasterAvatar"
] ]

View File

@ -1,106 +0,0 @@
{
"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

@ -1,106 +0,0 @@
{
"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

@ -1,106 +0,0 @@
{
"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

@ -674,6 +674,10 @@
<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>

View File

@ -375,6 +375,7 @@ 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 => {
@ -487,25 +488,27 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
if (item.XP < maxXP) { revalidateAuthz(() => {
addGearExp(category, item.ItemId.$oid, maxXP - item.XP); if (item.XP < maxXP) {
} addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
if ("exalted" in itemMap[item.ItemType]) { }
for (const exaltedType of itemMap[item.ItemType].exalted) { if ("exalted" in itemMap[item.ItemType]) {
const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType); for (const exaltedType of itemMap[item.ItemType].exalted) {
if (exaltedItem) { const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
const exaltedCap = if (exaltedItem) {
itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000; const exaltedCap =
if (exaltedItem.XP < exaltedCap) { itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
addGearExp( if (exaltedItem.XP < exaltedCap) {
"SpecialItems", addGearExp(
exaltedItem.ItemId.$oid, "SpecialItems",
exaltedCap - exaltedItem.XP exaltedItem.ItemId.$oid,
); exaltedCap - exaltedItem.XP
);
}
} }
} }
} }
} });
}; };
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>`;
@ -1229,76 +1232,22 @@ function addMissingEvolutionProgress() {
} }
function maxRankAllEvolutions() { function maxRankAllEvolutions() {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1"); revalidateAuthz(() => {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => {
const requests = [];
req.done(data => { data.EvolutionProgress.forEach(item => {
const requests = []; if (item.Rank < 5) {
requests.push({
data.EvolutionProgress.forEach(item => { ItemType: item.ItemType,
if (item.Rank < 5) { 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) {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => {
window.itemListPromise.then(itemMap => {
const batchData = {};
categories.forEach(category => {
data[category].forEach(item => {
const maxXP =
category === "Suits" ||
category === "SpaceSuits" ||
category === "Sentinels" ||
category === "Hoverboards"
? 1_600_000
: 800_000;
if (item.XP < maxXP) {
if (!batchData[category]) {
batchData[category] = [];
}
batchData[category].push({
ItemId: { $oid: item.ItemId.$oid },
XP: maxXP
});
}
if (category === "Suits") {
if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType);
if (exaltedItem) {
const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) {
batchData["SpecialItems"] ??= [];
batchData["SpecialItems"].push({
ItemId: { $oid: exaltedItem.ItemId.$oid },
XP: exaltedCap
});
}
}
}
}
}
});
}); });
if (Object.keys(batchData).length > 0) { if (Object.keys(requests).length > 0) {
return sendBatchGearExp(batchData); return setEvolutionProgress(requests);
} }
toast(loc("code_noEquipmentToRankUp")); toast(loc("code_noEquipmentToRankUp"));
@ -1306,6 +1255,64 @@ function maxRankAllEquipment(categories) {
}); });
} }
function maxRankAllEquipment(categories) {
revalidateAuthz(() => {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => {
window.itemListPromise.then(itemMap => {
const batchData = {};
categories.forEach(category => {
data[category].forEach(item => {
const maxXP =
category === "Suits" ||
category === "SpaceSuits" ||
category === "Sentinels" ||
category === "Hoverboards"
? 1_600_000
: 800_000;
if (item.XP < maxXP) {
if (!batchData[category]) {
batchData[category] = [];
}
batchData[category].push({
ItemId: { $oid: item.ItemId.$oid },
XP: maxXP
});
}
if (category === "Suits") {
if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType);
if (exaltedItem) {
const exaltedCap =
itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) {
batchData["SpecialItems"] ??= [];
batchData["SpecialItems"].push({
ItemId: { $oid: exaltedItem.ItemId.$oid },
XP: exaltedCap
});
}
}
}
}
}
});
});
if (Object.keys(batchData).length > 0) {
return sendBatchGearExp(batchData);
}
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] = [
@ -1314,16 +1321,14 @@ 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", data: JSON.stringify(data)
data: JSON.stringify(data) }).done(function () {
}).done(function () { if (category != "SpecialItems") {
if (category != "SpecialItems") { updateInventory();
updateInventory(); }
}
});
}); });
} }
@ -1598,32 +1603,34 @@ 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() {
fetch("/custom/config?" + window.authz) revalidateAuthz(() => {
.then(response => response.json()) fetch("/custom/config?" + window.authz)
.then(json => { .then(response => response.json())
for (const i of uiConfigs) { .then(json => {
var x = document.getElementById(i); for (const i of uiConfigs) {
if (x != null) { var x = document.getElementById(i);
if (x.type == "checkbox") { if (x != null) {
if (x.checked === true) { if (x.type == "checkbox") {
json[i] = true; if (x.checked === true) {
} else { json[i] = true;
json[i] = false; } else {
json[i] = false;
}
} else if (x.type == "number") {
json[i] = parseInt(x.value);
} }
} else if (x.type == "number") {
json[i] = parseInt(x.value);
} }
} }
} $.post({
$.post({ url: "/custom/config?" + window.authz,
url: "/custom/config?" + window.authz, contentType: "text/plain",
contentType: "text/plain", data: JSON.stringify(json, null, 2)
data: JSON.stringify(json, null, 2) }).then(() => {
}).then(() => { // A few cheats affect the inventory response which in turn may change what values we need to show
// A few cheats affect the inventory response which in turn may change what values we need to show updateInventory();
updateInventory(); });
}); });
}); });
} }
// Cheats route // Cheats route
@ -1876,33 +1883,39 @@ function doChangeSupportedSyndicate() {
} }
function doAddCurrency(currency) { function doAddCurrency(currency) {
$.post({ revalidateAuthz(() => {
url: "/custom/addCurrency?" + window.authz, $.post({
contentType: "application/json", url: "/custom/addCurrency?" + window.authz,
data: JSON.stringify({ contentType: "application/json",
currency, data: JSON.stringify({
delta: document.getElementById(currency + "-delta").valueAsNumber currency,
}) delta: document.getElementById(currency + "-delta").valueAsNumber
}).then(function () { })
updateInventory(); }).then(function () {
updateInventory();
});
}); });
} }
function doQuestUpdate(operation, itemType) { function doQuestUpdate(operation, itemType) {
$.post({ revalidateAuthz(() => {
url: "/custom/manageQuests?" + window.authz + "&operation=" + operation + "&itemType=" + itemType, $.post({
contentType: "application/json" url: "/custom/manageQuests?" + window.authz + "&operation=" + operation + "&itemType=" + itemType,
}).then(function () { contentType: "application/json"
updateInventory(); }).then(function () {
updateInventory();
});
}); });
} }
function doBulkQuestUpdate(operation) { function doBulkQuestUpdate(operation) {
$.post({ revalidateAuthz(() => {
url: "/custom/manageQuests?" + window.authz + "&operation=" + operation, $.post({
contentType: "application/json" url: "/custom/manageQuests?" + window.authz + "&operation=" + operation,
}).then(function () { contentType: "application/json"
updateInventory(); }).then(function () {
updateInventory();
});
}); });
} }

View File

@ -34,8 +34,8 @@ 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: `[UNTRANSLATED] Rank up`, code_rankUp: `Rang erhöhen`,
code_rankDown: `[UNTRANSLATED] Rank down`, 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.`,
@ -84,23 +84,23 @@ 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: `Moa`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bestien`, inventory_kubrowPets: `Bestien`,
inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`, 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: `[UNTRANSLATED] Add Missing Incarnon Evolution Progress`, 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: `[UNTRANSLATED] Max Rank All Incarnon Evolution Progress`, 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 +120,9 @@ 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: `[UNTRANSLATED] Add Missing Unranked Mods`, mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`,
mods_removeUnranked: `Mods ohne Rang entfernen`, mods_removeUnranked: `Mods ohne Rang entfernen`,
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank 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 +134,7 @@ 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: `[UNTRANSLATED] Don't Subtract Consumables`, 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,6 +154,7 @@ 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

@ -83,7 +83,7 @@ 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: `Moa`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Beasts`, inventory_kubrowPets: `Beasts`,
inventory_evolutionProgress: `Incarnon Evolution Progress`, inventory_evolutionProgress: `Incarnon Evolution Progress`,
inventory_bulkAddSuits: `Add Missing Warframes`, inventory_bulkAddSuits: `Add Missing Warframes`,
@ -153,6 +153,7 @@ 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

@ -84,7 +84,7 @@ 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: `Moa`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bestias`, inventory_kubrowPets: `Bestias`,
inventory_evolutionProgress: `Progreso de evolución Incarnon`, inventory_evolutionProgress: `Progreso de evolución Incarnon`,
inventory_bulkAddSuits: `Agregar Warframes faltantes`, inventory_bulkAddSuits: `Agregar Warframes faltantes`,
@ -154,6 +154,7 @@ 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

@ -84,7 +84,7 @@ 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: `Moa`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bêtes`, inventory_kubrowPets: `Bêtes`,
inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`, inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`,
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`, inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
@ -154,6 +154,7 @@ 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

@ -154,6 +154,7 @@ 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

@ -154,6 +154,7 @@ 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: `快速拆除道场房间`,