Compare commits

..

No commits in common. "218df461e1e29e1345642fc75a205dfbeb10606a" and "8b0ba0b84a767e3ff76032791155673c24a512ae" have entirely different histories.

16 changed files with 195 additions and 374 deletions

View File

@ -1,28 +0,0 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
import { Types } from "mongoose";
export const crewMembersController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "CrewMembers");
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
dbCrewMember.WeaponConfigIdx = data.crewMember.WeaponConfigIdx;
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
dbCrewMember.Configs = data.crewMember.Configs;
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
await inventory.save();
res.json({
crewMemberId: data.crewMember.ItemId.$oid,
NemesisFingerprint: data.crewMember.NemesisFingerprint
});
};
interface ICrewMembersRequest {
crewMember: ICrewMemberClient;
}

View File

@ -12,7 +12,6 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getRandomInt } from "@/src/services/rngService"; import { getRandomInt } from "@/src/services/rngService";
import { IFingerprintStat } from "@/src/helpers/rivenHelper"; import { IFingerprintStat } from "@/src/helpers/rivenHelper";
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => { export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -43,9 +42,7 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
); );
} else { } else {
const meta = ExportRailjackWeapons[payload.ItemType]; const meta = ExportRailjackWeapons[payload.ItemType];
let defaultOverwrites: Partial<IEquipmentDatabase> | undefined; const upgradeType = meta.defaultUpgrades![0].ItemType;
if (meta.defaultUpgrades?.[0]) {
const upgradeType = meta.defaultUpgrades[0].ItemType;
const upgradeMeta = ExportUpgrades[upgradeType]; const upgradeMeta = ExportUpgrades[upgradeType];
const buffs: IFingerprintStat[] = []; const buffs: IFingerprintStat[] = [];
for (const buff of upgradeMeta.upgradeEntries!) { for (const buff of upgradeMeta.upgradeEntries!) {
@ -54,22 +51,13 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
Value: Math.trunc(Math.random() * 0x40000000) Value: Math.trunc(Math.random() * 0x40000000)
}); });
} }
defaultOverwrites = { addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, undefined, inventoryChanges, {
UpgradeType: upgradeType, UpgradeType: upgradeType,
UpgradeFingerprint: JSON.stringify({ UpgradeFingerprint: JSON.stringify({
compat: payload.ItemType, compat: payload.ItemType,
buffs buffs
} satisfies IInnateDamageFingerprint) } satisfies IInnateDamageFingerprint)
}; });
}
addEquipment(
inventory,
"CrewShipSalvagedWeapons",
payload.ItemType,
undefined,
inventoryChanges,
defaultOverwrites
);
} }
inventoryChanges.CrewShipRawSalvage = [ inventoryChanges.CrewShipRawSalvage = [

View File

@ -360,22 +360,6 @@ export const guildTechController: RequestHandler = async (req, res) => {
res.json({ res.json({
inventoryChanges: inventoryChanges inventoryChanges: inventoryChanges
}); });
} else if (data.Action == "InstantFinish") {
if (data.TechProductCategory != "CrewShipWeapons" && data.TechProductCategory != "CrewShipWeaponSkins") {
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
}
const inventoryChanges = finishComponentRepair(inventory, data.TechProductCategory, data.CategoryItemId!);
inventoryChanges.MiscItems = [
{
ItemType: "/Lotus/Types/Items/MiscItems/InstantSalvageRepairItem",
ItemCount: -1
}
];
addMiscItems(inventory, inventoryChanges.MiscItems);
await inventory.save();
res.json({
inventoryChanges: inventoryChanges
});
} 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(`unhandled guildTech request`); throw new Error(`unhandled guildTech request`);
@ -388,7 +372,7 @@ type TGuildTechRequest =
| IGuildTechContributeRequest; | IGuildTechContributeRequest;
interface IGuildTechBasicRequest { interface IGuildTechBasicRequest {
Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush" | "InstantFinish"; Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush";
Mode: "Guild" | "Personal"; Mode: "Guild" | "Personal";
RecipeType: string; RecipeType: string;
TechProductCategory?: string; TechProductCategory?: string;
@ -422,19 +406,11 @@ const claimSalvagedComponent = (inventory: TInventoryDatabaseDocument, itemId: s
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1); inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
const category = personalTechProject.ProductCategory! as "CrewShipWeapons" | "CrewShipWeaponSkins"; const category = personalTechProject.ProductCategory! as "CrewShipWeapons" | "CrewShipWeaponSkins";
return finishComponentRepair(inventory, category, itemId);
};
const finishComponentRepair = (
inventory: TInventoryDatabaseDocument,
category: "CrewShipWeapons" | "CrewShipWeaponSkins",
itemId: string
): IInventoryChanges => {
const salvageCategory = getSalvageCategory(category); const salvageCategory = getSalvageCategory(category);
// find salved part & delete it // find salved part & delete it
const salvageIndex = inventory[salvageCategory].findIndex(x => x._id.equals(itemId)); const salvageIndex = inventory[salvageCategory].findIndex(x => x._id.equals(itemId));
const salvageItem = inventory[salvageCategory][salvageIndex]; const salvageItem = inventory[category][salvageIndex];
inventory[salvageCategory].splice(salvageIndex, 1); inventory[salvageCategory].splice(salvageIndex, 1);
// add final item // add final item

View File

@ -94,8 +94,7 @@ import {
ICrewMemberClient, ICrewMemberClient,
ISortieRewardAttenuation, ISortieRewardAttenuation,
IInvasionProgressDatabase, IInvasionProgressDatabase,
IInvasionProgressClient, IInvasionProgressClient
IAccolades
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -1060,13 +1059,6 @@ pendingRecipeSchema.set("toJSON", {
} }
}); });
const accoladesSchema = new Schema<IAccolades>(
{
Heirloom: Boolean
},
{ _id: false }
);
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>( const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
{ {
Name: String, Name: String,
@ -1474,16 +1466,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Mastery Rank next availability //Mastery Rank next availability
TrainingDate: { type: Date, default: new Date(0) }, TrainingDate: { type: Date, default: new Date(0) },
//Accolades
Staff: Boolean,
Founder: Number,
Guide: Number,
Moderator: Boolean,
Partner: Boolean,
Accolades: accoladesSchema,
//Not an accolade but unlocks an extra chat
Counselor: Boolean,
//you saw last played Region when you opened the star map //you saw last played Region when you opened the star map
LastRegionPlayed: String, LastRegionPlayed: String,

View File

@ -28,7 +28,6 @@ import { contributeToVaultController } from "@/src/controllers/api/contributeToV
import { createAllianceController } from "@/src/controllers/api/createAllianceController"; import { createAllianceController } from "@/src/controllers/api/createAllianceController";
import { createGuildController } from "@/src/controllers/api/createGuildController"; import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController"; import { creditsController } from "@/src/controllers/api/creditsController";
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController"; import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController"; import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController"; import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
@ -223,7 +222,6 @@ apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentContro
apiRouter.post("/contributeToVault.php", contributeToVaultController); apiRouter.post("/contributeToVault.php", contributeToVaultController);
apiRouter.post("/createAlliance.php", createAllianceController); apiRouter.post("/createAlliance.php", createAllianceController);
apiRouter.post("/createGuild.php", createGuildController); apiRouter.post("/createGuild.php", createGuildController);
apiRouter.post("/crewMembers.php", crewMembersController);
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController); apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController); apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController); apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);

View File

@ -230,23 +230,17 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
replaceSlots(db[key], client[key]); replaceSlots(db[key], client[key]);
} }
} }
// boolean
for (const key of [ for (const key of [
"UseAdultOperatorLoadout", "UseAdultOperatorLoadout",
"HasOwnedVoidProjectionsPreviously", "HasOwnedVoidProjectionsPreviously",
"ReceivedStartingGear", "ReceivedStartingGear",
"ArchwingEnabled", "ArchwingEnabled",
"PlayedParkourTutorial", "PlayedParkourTutorial"
"Staff",
"Moderator",
"Partner",
"Counselor"
] as const) { ] as const) {
if (client[key] !== undefined) { if (client[key] !== undefined) {
db[key] = client[key]; db[key] = client[key];
} }
} }
// number
for (const key of [ for (const key of [
"PlayerLevel", "PlayerLevel",
"RegularCredits", "RegularCredits",
@ -256,15 +250,12 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
"PrimeTokens", "PrimeTokens",
"TradesRemaining", "TradesRemaining",
"GiftsRemaining", "GiftsRemaining",
"ChallengesFixVersion", "ChallengesFixVersion"
"Founder",
"Guide"
] as const) { ] as const) {
if (client[key] !== undefined) { if (client[key] !== undefined) {
db[key] = client[key]; db[key] = client[key];
} }
} }
// string
for (const key of [ for (const key of [
"ThemeStyle", "ThemeStyle",
"ThemeBackground", "ThemeBackground",
@ -279,7 +270,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
db[key] = client[key]; db[key] = client[key];
} }
} }
// string[]
for (const key of [ for (const key of [
"EquippedGear", "EquippedGear",
"EquippedEmotes", "EquippedEmotes",
@ -390,9 +380,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
}); });
}); });
} }
if (client.Accolades !== undefined) {
db.Accolades = client.Accolades;
}
}; };
const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => { const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {

View File

@ -53,7 +53,7 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js
import { getInfNodes } from "@/src/helpers/nemesisHelpers"; import { getInfNodes } from "@/src/helpers/nemesisHelpers";
import { Loadout } from "../models/inventoryModels/loadoutModel"; import { Loadout } from "../models/inventoryModels/loadoutModel";
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes"; import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService"; import { getWorldState } from "./worldStateService";
import { config } from "./configService"; import { config } from "./configService";
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => { const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
@ -71,12 +71,7 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
return [rewardInfo.rewardTier]; return [rewardInfo.rewardTier];
} }
// Aborting a railjack mission should not give any rewards (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741) const rotationCount = rewardInfo.rewardQualifications?.length || 0;
if (rewardInfo.rewardQualifications === undefined) {
return [];
}
const rotationCount = rewardInfo.rewardQualifications.length || 0;
if (rotationCount === 0) return [0]; if (rotationCount === 0) return [0];
const rotationPattern = const rotationPattern =
@ -137,13 +132,11 @@ export const addMissionInventoryUpdates = async (
// Somewhat heuristically detect G3 capture: // Somewhat heuristically detect G3 capture:
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1365 // - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1365
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1694 // - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1694
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1724
if ( if (
inventoryUpdates.MissionFailed && inventoryUpdates.MissionFailed &&
inventoryUpdates.MissionStatus == "GS_FAILURE" && inventoryUpdates.MissionStatus == "GS_FAILURE" &&
inventoryUpdates.ObjectiveReached && inventoryUpdates.ObjectiveReached &&
!inventoryUpdates.LockedWeaponGroup && !inventoryUpdates.LockedWeaponGroup &&
!inventory.LockedWeaponGroup &&
!inventoryUpdates.LevelKeyName !inventoryUpdates.LevelKeyName
) { ) {
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!; const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!;
@ -995,7 +988,11 @@ function getRandomMissionDrops(
if (sortieId == "Lite") { if (sortieId == "Lite") {
sortieId = arr[2]; sortieId = arr[2];
const boss = getLiteSortie(idToWeek(sortieId)).Boss; // TODO: Some way to get from sortieId to reward to make this faster + more reliable at week rollover.
const boss = getWorldState().LiteSorties[0].Boss as
| "SORTIE_BOSS_AMAR"
| "SORTIE_BOSS_NIRA"
| "SORTIE_BOSS_BOREAL";
let crystalType = { let crystalType = {
SORTIE_BOSS_AMAR: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar", SORTIE_BOSS_AMAR: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar",
SORTIE_BOSS_NIRA: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira", SORTIE_BOSS_NIRA: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira",

View File

@ -216,27 +216,6 @@ const handleQuestCompletion = async (
setupKahlSyndicate(inventory); setupKahlSyndicate(inventory);
} }
// Whispers in the Walls is unlocked once The New + Heart of Deimos are completed.
if (
(questKey == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain" &&
inventory.QuestKeys.find(
x => x.ItemType == "/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain"
)?.Completed) ||
(questKey == "/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain" &&
inventory.QuestKeys.find(x => x.ItemType == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain")?.Completed)
) {
await createMessage(inventory.accountOwnerId, [
{
sndr: "/Lotus/Language/Bosses/Loid",
msg: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxBody",
att: ["/Lotus/Types/Keys/EntratiLab/EntratiQuestKeyChain"],
sub: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxTitle",
icon: "/Lotus/Interface/Icons/Npcs/Entrati/Loid.png",
highPriority: true
}
]);
}
const questCompletionItems = getQuestCompletionItems(questKey); const questCompletionItems = getQuestCompletionItems(questKey);
logger.debug(`quest completion items`, questCompletionItems); logger.debug(`quest completion items`, questCompletionItems);
if (questCompletionItems) { if (questCompletionItems) {

View File

@ -1,4 +1,6 @@
import { unixTimesInMs } from "@/src/constants/timeConstants"; import fs from "fs";
import path from "path";
import { repoDir } from "@/src/helpers/pathHelper";
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 { import {
@ -7,68 +9,44 @@ import {
IVendorInfo, IVendorInfo,
IVendorManifestPreprocessed IVendorManifestPreprocessed
} from "@/src/types/vendorTypes"; } from "@/src/types/vendorTypes";
import { JSONParse } from "json-with-bigint";
import { ExportVendors } from "warframe-public-export-plus"; import { ExportVendors } from "warframe-public-export-plus";
import { unixTimesInMs } from "../constants/timeConstants";
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json"; const getVendorManifestJson = (name: string): IRawVendorManifest => {
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json"; return JSONParse(fs.readFileSync(path.join(repoDir, `static/fixed_responses/getVendorInfo/${name}.json`), "utf-8"));
import DeimosFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosFishmongerVendorManifest.json"; };
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
import DeimosHivemindCommisionsManifestTokenVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestTokenVendor.json";
import DeimosHivemindCommisionsManifestWeaponsmith from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestWeaponsmith.json";
import DeimosHivemindTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosHivemindTokenVendorManifest.json";
import DeimosPetVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosPetVendorManifest.json";
import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosProspectorVendorManifest.json";
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
import GuildAdvertisementVendorManifest from "@/static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json";
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.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 OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
import SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json";
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json";
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
const rawVendorManifests: IRawVendorManifest[] = [ const rawVendorManifests: IRawVendorManifest[] = [
ArchimedeanVendorManifest, getVendorManifestJson("ArchimedeanVendorManifest"),
DeimosEntratiFragmentVendorProductsManifest, getVendorManifestJson("DeimosEntratiFragmentVendorProductsManifest"),
DeimosFishmongerVendorManifest, getVendorManifestJson("DeimosFishmongerVendorManifest"),
DeimosHivemindCommisionsManifestFishmonger, getVendorManifestJson("DeimosHivemindCommisionsManifestFishmonger"),
DeimosHivemindCommisionsManifestPetVendor, getVendorManifestJson("DeimosHivemindCommisionsManifestPetVendor"),
DeimosHivemindCommisionsManifestProspector, getVendorManifestJson("DeimosHivemindCommisionsManifestProspector"),
DeimosHivemindCommisionsManifestTokenVendor, getVendorManifestJson("DeimosHivemindCommisionsManifestTokenVendor"),
DeimosHivemindCommisionsManifestWeaponsmith, getVendorManifestJson("DeimosHivemindCommisionsManifestWeaponsmith"),
DeimosHivemindTokenVendorManifest, getVendorManifestJson("DeimosHivemindTokenVendorManifest"),
DeimosPetVendorManifest, getVendorManifestJson("DeimosPetVendorManifest"),
DeimosProspectorVendorManifest, getVendorManifestJson("DeimosProspectorVendorManifest"),
DuviriAcrithisVendorManifest, getVendorManifestJson("DuviriAcrithisVendorManifest"),
EntratiLabsEntratiLabsCommisionsManifest, getVendorManifestJson("EntratiLabsEntratiLabsCommisionsManifest"),
EntratiLabsEntratiLabVendorManifest, getVendorManifestJson("EntratiLabsEntratiLabVendorManifest"),
GuildAdvertisementVendorManifest, // uses preprocessing getVendorManifestJson("GuildAdvertisementVendorManifest"), // uses preprocessing
HubsIronwakeDondaVendorManifest, // uses preprocessing getVendorManifestJson("HubsIronwakeDondaVendorManifest"), // uses preprocessing
HubsRailjackCrewMemberVendorManifest, getVendorManifestJson("HubsRailjackCrewMemberVendorManifest"),
MaskSalesmanManifest, getVendorManifestJson("MaskSalesmanManifest"),
Nova1999ConquestShopManifest, getVendorManifestJson("Nova1999ConquestShopManifest"),
OstronFishmongerVendorManifest, getVendorManifestJson("OstronFishmongerVendorManifest"),
OstronPetVendorManifest, getVendorManifestJson("OstronPetVendorManifest"),
OstronProspectorVendorManifest, getVendorManifestJson("OstronProspectorVendorManifest"),
RadioLegionIntermission12VendorManifest, getVendorManifestJson("RadioLegionIntermission12VendorManifest"),
SolarisDebtTokenVendorManifest, getVendorManifestJson("SolarisDebtTokenVendorManifest"),
SolarisDebtTokenVendorRepossessionsManifest, getVendorManifestJson("SolarisDebtTokenVendorRepossessionsManifest"),
SolarisFishmongerVendorManifest, getVendorManifestJson("SolarisFishmongerVendorManifest"),
SolarisProspectorVendorManifest, getVendorManifestJson("SolarisProspectorVendorManifest"),
TeshinHardModeVendorManifest, // uses preprocessing getVendorManifestJson("TeshinHardModeVendorManifest"), // uses preprocessing
ZarimanCommisionsManifestArchimedean getVendorManifestJson("ZarimanCommisionsManifestArchimedean")
]; ];
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> { interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {

View File

@ -1,5 +1,6 @@
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel"; import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
import { import {
IEnemy,
IStatsAdd, IStatsAdd,
IStatsMax, IStatsMax,
IStatsSet, IStatsSet,
@ -136,21 +137,16 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
case "HEADSHOT": case "HEADSHOT":
case "KILL_ASSIST": { case "KILL_ASSIST": {
playerStats.Enemies ??= []; playerStats.Enemies ??= [];
const enemyStatKey = ( const enemyStatKey = {
{
KILL_ENEMY: "kills", KILL_ENEMY: "kills",
EXECUTE_ENEMY: "executions", EXECUTE_ENEMY: "executions",
HEADSHOT: "headshots", HEADSHOT: "headshots",
KILL_ASSIST: "assists" KILL_ASSIST: "assists"
} as const }[category] as "kills" | "executions" | "headshots" | "assists";
)[category];
for (const [type, count] of Object.entries(data as IUploadEntry)) { for (const [type, count] of Object.entries(data as IUploadEntry)) {
let enemy = playerStats.Enemies.find(element => element.type === type); const enemy = playerStats.Enemies.find(element => element.type === type);
if (!enemy) { if (enemy) {
enemy = { type: type };
playerStats.Enemies.push(enemy);
}
if (category === "KILL_ENEMY") { if (category === "KILL_ENEMY") {
enemy.kills ??= 0; enemy.kills ??= 0;
const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type]; const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type];
@ -165,6 +161,11 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
enemy[enemyStatKey] ??= 0; enemy[enemyStatKey] ??= 0;
enemy[enemyStatKey] += count; enemy[enemyStatKey] += count;
} }
} else {
const newEnemy: IEnemy = { type: type };
newEnemy[enemyStatKey] = count;
playerStats.Enemies.push(newEnemy);
}
} }
break; break;
} }

View File

@ -4,14 +4,7 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import { CRng } from "@/src/services/rngService"; import { CRng } from "@/src/services/rngService";
import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus"; import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus";
import { import { ICalendarDay, ICalendarSeason, ISeasonChallenge, ISortie, IWorldState } from "../types/worldStateTypes";
ICalendarDay,
ICalendarSeason,
ILiteSortie,
ISeasonChallenge,
ISortie,
IWorldState
} from "../types/worldStateTypes";
const sortieBosses = [ const sortieBosses = [
"SORTIE_BOSS_HYENA", "SORTIE_BOSS_HYENA",
@ -355,34 +348,6 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
}; };
}; };
const pushWeeklyActs = (worldState: IWorldState, week: number): void => {
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
});
};
const birthdays: number[] = [ const birthdays: number[] = [
1, // Kaya 1, // Kaya
45, // Lettie 45, // Lettie
@ -639,10 +604,29 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) { if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) {
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day + 1)); worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day + 1));
} }
pushWeeklyActs(worldState, week); worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) { worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
pushWeeklyActs(worldState, week + 1); worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
} worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
});
// TODO: Provide upcoming weekly acts if rollover is imminent
// Elite Sanctuary Onslaught cycling every week // Elite Sanctuary Onslaught cycling every week
worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
@ -957,9 +941,65 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
pushSortieIfRelevant(worldState.Sorties, day); pushSortieIfRelevant(worldState.Sorties, day);
// Archon Hunt cycling every week // Archon Hunt cycling every week
worldState.LiteSorties.push(getLiteSortie(week)); // TODO: Handle imminent rollover
if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) { {
worldState.LiteSorties.push(getLiteSortie(week + 1)); const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
const nodes: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.systemIndex === systemIndex &&
value.factionIndex !== undefined &&
value.factionIndex < 2 &&
value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 // Exclude MT_ASSASSINATION
) {
nodes.push(key);
}
}
const rng = new CRng(week);
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
const firstNode = nodes[firstNodeIndex];
nodes.splice(firstNodeIndex, 1);
worldState.LiteSorties.push({
_id: {
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
},
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
Seed: week,
Boss: boss,
Missions: [
{
missionType: rng.randomElement([
"MT_INTEL",
"MT_MOBILE_DEFENSE",
"MT_EXTERMINATION",
"MT_SABOTAGE",
"MT_RESCUE"
]),
node: firstNode
},
{
missionType: rng.randomElement([
"MT_DEFENSE",
"MT_TERRITORY",
"MT_ARTIFACT",
"MT_EXCAVATE",
"MT_SURVIVAL"
]),
node: rng.randomElement(nodes)
},
{
missionType: "MT_ASSASSINATION",
node: showdownNode
}
]
});
} }
// Circuit choices cycling every week // Circuit choices cycling every week
@ -1031,70 +1071,3 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
return worldState; return worldState;
}; };
export const idToWeek = (id: string): number => {
return (parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800000;
};
export const getLiteSortie = (week: number): ILiteSortie => {
const boss = (["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"] as const)[week % 3];
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
const nodes: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.systemIndex === systemIndex &&
value.factionIndex !== undefined &&
value.factionIndex < 2 &&
value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 // Exclude MT_ASSASSINATION
) {
nodes.push(key);
}
}
const rng = new CRng(week);
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
const firstNode = nodes[firstNodeIndex];
nodes.splice(firstNodeIndex, 1);
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
return {
_id: {
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
},
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
Seed: week,
Boss: boss,
Missions: [
{
missionType: rng.randomElement([
"MT_INTEL",
"MT_MOBILE_DEFENSE",
"MT_EXTERMINATION",
"MT_SABOTAGE",
"MT_RESCUE"
]),
node: firstNode
},
{
missionType: rng.randomElement([
"MT_DEFENSE",
"MT_TERRITORY",
"MT_ARTIFACT",
"MT_EXCAVATE",
"MT_SURVIVAL"
]),
node: rng.randomElement(nodes)
},
{
missionType: "MT_ASSASSINATION",
node: showdownNode
}
]
};
};

View File

@ -250,7 +250,9 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
Guide?: number; Guide?: number;
Moderator?: boolean; Moderator?: boolean;
Partner?: boolean; Partner?: boolean;
Accolades?: IAccolades; Accolades?: {
Heirloom?: boolean;
};
Counselor?: boolean; Counselor?: boolean;
Upgrades: IUpgradeClient[]; Upgrades: IUpgradeClient[];
EquippedGear: string[]; EquippedGear: string[];
@ -487,7 +489,7 @@ export interface ICrewMemberClient {
XP: number; XP: number;
PowersuitType: string; PowersuitType: string;
Configs: IItemConfig[]; Configs: IItemConfig[];
SecondInCommand: boolean; // on call SecondInCommand: boolean;
ItemId: IOid; ItemId: IOid;
} }
@ -912,10 +914,6 @@ export interface IPendingRecipeClient
CompletionDate: IMongoDate; CompletionDate: IMongoDate;
} }
export interface IAccolades {
Heirloom?: boolean;
}
export interface IPendingTrade { export interface IPendingTrade {
State: number; State: number;
SelfReady: boolean; SelfReady: boolean;

View File

@ -32,7 +32,7 @@ export interface IVendorInfo {
TypeName: string; TypeName: string;
ItemManifest: IItemManifest[]; ItemManifest: IItemManifest[];
PropertyTextHash?: string; PropertyTextHash?: string;
RandomSeedType?: string; RandomSeedType?: "VRST_WEAPON";
RequiredGoalTag?: string; RequiredGoalTag?: string;
WeaponUpgradeValueAttenuationExponent?: number; WeaponUpgradeValueAttenuationExponent?: number;
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing. Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.

View File

@ -103,7 +103,7 @@ export interface ILiteSortie {
Expiry: IMongoDate; Expiry: IMongoDate;
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards"; Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards";
Seed: number; Seed: number;
Boss: "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"; Boss: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"
Missions: { Missions: {
missionType: string; missionType: string;
node: string; node: string;

View File

@ -1097,6 +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"
] ]

View File

@ -487,7 +487,6 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
document.getElementById(category + "-list").removeChild(tr);
disposeOfGear(category, item.ItemId.$oid); disposeOfGear(category, item.ItemId.$oid);
}; };
a.title = loc("code_remove"); a.title = loc("code_remove");
@ -684,7 +683,6 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
document.getElementById("riven-list").removeChild(tr);
disposeOfGear("Upgrades", item.ItemId.$oid); disposeOfGear("Upgrades", item.ItemId.$oid);
}; };
a.title = loc("code_remove"); a.title = loc("code_remove");
@ -725,7 +723,6 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
document.getElementById("mods-list").removeChild(tr);
disposeOfGear("Upgrades", item.ItemId.$oid); disposeOfGear("Upgrades", item.ItemId.$oid);
}; };
a.title = loc("code_remove"); a.title = loc("code_remove");
@ -768,7 +765,6 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
document.getElementById("mods-list").removeChild(tr);
disposeOfItems("Upgrades", item.ItemType, item.ItemCount); disposeOfItems("Upgrades", item.ItemType, item.ItemCount);
}; };
a.title = loc("code_remove"); a.title = loc("code_remove");
@ -1101,6 +1097,8 @@ function disposeOfGear(category, oid) {
url: "/api/sell.php?" + window.authz, url: "/api/sell.php?" + window.authz,
contentType: "text/plain", contentType: "text/plain",
data: JSON.stringify(data) data: JSON.stringify(data)
}).done(function () {
updateInventory();
}); });
}); });
} }
@ -1122,6 +1120,8 @@ function disposeOfItems(category, type, count) {
url: "/api/sell.php?" + window.authz, url: "/api/sell.php?" + window.authz,
contentType: "text/plain", contentType: "text/plain",
data: JSON.stringify(data) data: JSON.stringify(data)
}).done(function () {
updateInventory();
}); });
}); });
} }
@ -1260,8 +1260,6 @@ function doAcquireMod() {
$("#mod-to-acquire").addClass("is-invalid").focus(); $("#mod-to-acquire").addClass("is-invalid").focus();
return; return;
} }
const count = parseInt($("#mod-count").val());
if (count != 0) {
revalidateAuthz(() => { revalidateAuthz(() => {
$.post({ $.post({
url: "/custom/addItems?" + window.authz, url: "/custom/addItems?" + window.authz,
@ -1269,20 +1267,15 @@ function doAcquireMod() {
data: JSON.stringify([ data: JSON.stringify([
{ {
ItemType: uniqueName, ItemType: uniqueName,
ItemCount: count ItemCount: parseInt($("#mod-count").val())
} }
]) ])
}).done(function () { }).done(function () {
if (count > 0) { document.getElementById("mod-to-acquire").value = "";
toast(loc("code_succAdded"));
} else {
toast(loc("code_succRemoved"));
}
updateInventory(); updateInventory();
}); });
}); });
} }
}
const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id); const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id);