forked from OpenWF/SpaceNinjaServer
		
	Compare commits
	
		
			14 Commits
		
	
	
		
			8b0ba0b84a
			...
			218df461e1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 218df461e1 | |||
| 86d871537b | |||
| 11f2ffe64d | |||
| 8fd7152c41 | |||
| 0f3d9f6c2c | |||
| c2a633b549 | |||
| 7040d422a2 | |||
| ba1380ec4c | |||
| 26f37f58e5 | |||
| e59bdcdfbc | |||
| c1ca303310 | |||
| 8afb515231 | |||
| 5eecf11b1a | |||
| 37ac10acd2 | 
							
								
								
									
										28
									
								
								src/controllers/api/crewMembersController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/controllers/api/crewMembersController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -12,6 +12,7 @@ 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);
 | 
				
			||||||
@ -42,7 +43,9 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const meta = ExportRailjackWeapons[payload.ItemType];
 | 
					        const meta = ExportRailjackWeapons[payload.ItemType];
 | 
				
			||||||
        const upgradeType = meta.defaultUpgrades![0].ItemType;
 | 
					        let defaultOverwrites: Partial<IEquipmentDatabase> | undefined;
 | 
				
			||||||
 | 
					        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!) {
 | 
				
			||||||
@ -51,13 +54,22 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
 | 
				
			|||||||
                    Value: Math.trunc(Math.random() * 0x40000000)
 | 
					                    Value: Math.trunc(Math.random() * 0x40000000)
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, undefined, inventoryChanges, {
 | 
					            defaultOverwrites = {
 | 
				
			||||||
                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 = [
 | 
				
			||||||
 | 
				
			|||||||
@ -360,6 +360,22 @@ 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`);
 | 
				
			||||||
@ -372,7 +388,7 @@ type TGuildTechRequest =
 | 
				
			|||||||
    | IGuildTechContributeRequest;
 | 
					    | IGuildTechContributeRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IGuildTechBasicRequest {
 | 
					interface IGuildTechBasicRequest {
 | 
				
			||||||
    Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush";
 | 
					    Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush" | "InstantFinish";
 | 
				
			||||||
    Mode: "Guild" | "Personal";
 | 
					    Mode: "Guild" | "Personal";
 | 
				
			||||||
    RecipeType: string;
 | 
					    RecipeType: string;
 | 
				
			||||||
    TechProductCategory?: string;
 | 
					    TechProductCategory?: string;
 | 
				
			||||||
@ -406,11 +422,19 @@ 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[category][salvageIndex];
 | 
					    const salvageItem = inventory[salvageCategory][salvageIndex];
 | 
				
			||||||
    inventory[salvageCategory].splice(salvageIndex, 1);
 | 
					    inventory[salvageCategory].splice(salvageIndex, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // add final item
 | 
					    // add final item
 | 
				
			||||||
 | 
				
			|||||||
@ -94,7 +94,8 @@ 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 {
 | 
				
			||||||
@ -1059,6 +1060,13 @@ 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,
 | 
				
			||||||
@ -1466,6 +1474,16 @@ 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,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@ 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";
 | 
				
			||||||
@ -222,6 +223,7 @@ 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);
 | 
				
			||||||
 | 
				
			|||||||
@ -230,17 +230,23 @@ 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",
 | 
				
			||||||
@ -250,12 +256,15 @@ 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",
 | 
				
			||||||
@ -270,6 +279,7 @@ 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",
 | 
				
			||||||
@ -380,6 +390,9 @@ 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 => {
 | 
				
			||||||
 | 
				
			|||||||
@ -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 { getWorldState } from "./worldStateService";
 | 
					import { getLiteSortie, getWorldState, idToWeek } 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,7 +71,12 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
 | 
				
			|||||||
        return [rewardInfo.rewardTier];
 | 
					        return [rewardInfo.rewardTier];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const rotationCount = rewardInfo.rewardQualifications?.length || 0;
 | 
					    // Aborting a railjack mission should not give any rewards (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
 | 
				
			||||||
 | 
					    if (rewardInfo.rewardQualifications === undefined) {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const rotationCount = rewardInfo.rewardQualifications.length || 0;
 | 
				
			||||||
    if (rotationCount === 0) return [0];
 | 
					    if (rotationCount === 0) return [0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const rotationPattern =
 | 
					    const rotationPattern =
 | 
				
			||||||
@ -132,11 +137,13 @@ 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"))!;
 | 
				
			||||||
@ -988,11 +995,7 @@ function getRandomMissionDrops(
 | 
				
			|||||||
        if (sortieId == "Lite") {
 | 
					        if (sortieId == "Lite") {
 | 
				
			||||||
            sortieId = arr[2];
 | 
					            sortieId = arr[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Some way to get from sortieId to reward to make this faster + more reliable at week rollover.
 | 
					            const boss = getLiteSortie(idToWeek(sortieId)).Boss;
 | 
				
			||||||
            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",
 | 
				
			||||||
 | 
				
			|||||||
@ -216,6 +216,27 @@ 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) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,4 @@
 | 
				
			|||||||
import fs from "fs";
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
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 {
 | 
				
			||||||
@ -9,44 +7,68 @@ 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";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getVendorManifestJson = (name: string): IRawVendorManifest => {
 | 
					import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
 | 
				
			||||||
    return JSONParse(fs.readFileSync(path.join(repoDir, `static/fixed_responses/getVendorInfo/${name}.json`), "utf-8"));
 | 
					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 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[] = [
 | 
				
			||||||
    getVendorManifestJson("ArchimedeanVendorManifest"),
 | 
					    ArchimedeanVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("DeimosEntratiFragmentVendorProductsManifest"),
 | 
					    DeimosEntratiFragmentVendorProductsManifest,
 | 
				
			||||||
    getVendorManifestJson("DeimosFishmongerVendorManifest"),
 | 
					    DeimosFishmongerVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindCommisionsManifestFishmonger"),
 | 
					    DeimosHivemindCommisionsManifestFishmonger,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindCommisionsManifestPetVendor"),
 | 
					    DeimosHivemindCommisionsManifestPetVendor,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindCommisionsManifestProspector"),
 | 
					    DeimosHivemindCommisionsManifestProspector,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindCommisionsManifestTokenVendor"),
 | 
					    DeimosHivemindCommisionsManifestTokenVendor,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindCommisionsManifestWeaponsmith"),
 | 
					    DeimosHivemindCommisionsManifestWeaponsmith,
 | 
				
			||||||
    getVendorManifestJson("DeimosHivemindTokenVendorManifest"),
 | 
					    DeimosHivemindTokenVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("DeimosPetVendorManifest"),
 | 
					    DeimosPetVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("DeimosProspectorVendorManifest"),
 | 
					    DeimosProspectorVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("DuviriAcrithisVendorManifest"),
 | 
					    DuviriAcrithisVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("EntratiLabsEntratiLabsCommisionsManifest"),
 | 
					    EntratiLabsEntratiLabsCommisionsManifest,
 | 
				
			||||||
    getVendorManifestJson("EntratiLabsEntratiLabVendorManifest"),
 | 
					    EntratiLabsEntratiLabVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("GuildAdvertisementVendorManifest"), // uses preprocessing
 | 
					    GuildAdvertisementVendorManifest, // uses preprocessing
 | 
				
			||||||
    getVendorManifestJson("HubsIronwakeDondaVendorManifest"), // uses preprocessing
 | 
					    HubsIronwakeDondaVendorManifest, // uses preprocessing
 | 
				
			||||||
    getVendorManifestJson("HubsRailjackCrewMemberVendorManifest"),
 | 
					    HubsRailjackCrewMemberVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("MaskSalesmanManifest"),
 | 
					    MaskSalesmanManifest,
 | 
				
			||||||
    getVendorManifestJson("Nova1999ConquestShopManifest"),
 | 
					    Nova1999ConquestShopManifest,
 | 
				
			||||||
    getVendorManifestJson("OstronFishmongerVendorManifest"),
 | 
					    OstronFishmongerVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("OstronPetVendorManifest"),
 | 
					    OstronPetVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("OstronProspectorVendorManifest"),
 | 
					    OstronProspectorVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("RadioLegionIntermission12VendorManifest"),
 | 
					    RadioLegionIntermission12VendorManifest,
 | 
				
			||||||
    getVendorManifestJson("SolarisDebtTokenVendorManifest"),
 | 
					    SolarisDebtTokenVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("SolarisDebtTokenVendorRepossessionsManifest"),
 | 
					    SolarisDebtTokenVendorRepossessionsManifest,
 | 
				
			||||||
    getVendorManifestJson("SolarisFishmongerVendorManifest"),
 | 
					    SolarisFishmongerVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("SolarisProspectorVendorManifest"),
 | 
					    SolarisProspectorVendorManifest,
 | 
				
			||||||
    getVendorManifestJson("TeshinHardModeVendorManifest"), // uses preprocessing
 | 
					    TeshinHardModeVendorManifest, // uses preprocessing
 | 
				
			||||||
    getVendorManifestJson("ZarimanCommisionsManifestArchimedean")
 | 
					    ZarimanCommisionsManifestArchimedean
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
 | 
					interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
					import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IEnemy,
 | 
					 | 
				
			||||||
    IStatsAdd,
 | 
					    IStatsAdd,
 | 
				
			||||||
    IStatsMax,
 | 
					    IStatsMax,
 | 
				
			||||||
    IStatsSet,
 | 
					    IStatsSet,
 | 
				
			||||||
@ -137,16 +136,21 @@ 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"
 | 
				
			||||||
                            }[category] as "kills" | "executions" | "headshots" | "assists";
 | 
					                                } as const
 | 
				
			||||||
 | 
					                            )[category];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            for (const [type, count] of Object.entries(data as IUploadEntry)) {
 | 
					                            for (const [type, count] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
                                const enemy = playerStats.Enemies.find(element => element.type === type);
 | 
					                                let 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];
 | 
				
			||||||
@ -161,11 +165,6 @@ 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;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,14 @@ 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 { ICalendarDay, ICalendarSeason, ISeasonChallenge, ISortie, IWorldState } from "../types/worldStateTypes";
 | 
					import {
 | 
				
			||||||
 | 
					    ICalendarDay,
 | 
				
			||||||
 | 
					    ICalendarSeason,
 | 
				
			||||||
 | 
					    ILiteSortie,
 | 
				
			||||||
 | 
					    ISeasonChallenge,
 | 
				
			||||||
 | 
					    ISortie,
 | 
				
			||||||
 | 
					    IWorldState
 | 
				
			||||||
 | 
					} from "../types/worldStateTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieBosses = [
 | 
					const sortieBosses = [
 | 
				
			||||||
    "SORTIE_BOSS_HYENA",
 | 
					    "SORTIE_BOSS_HYENA",
 | 
				
			||||||
@ -348,6 +355,34 @@ 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
 | 
				
			||||||
@ -604,29 +639,10 @@ 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));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
 | 
					    pushWeeklyActs(worldState, week);
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
 | 
					    if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
 | 
					        pushWeeklyActs(worldState, week + 1);
 | 
				
			||||||
    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
 | 
				
			||||||
@ -941,65 +957,9 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
    pushSortieIfRelevant(worldState.Sorties, day);
 | 
					    pushSortieIfRelevant(worldState.Sorties, day);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Archon Hunt cycling every week
 | 
					    // Archon Hunt cycling every week
 | 
				
			||||||
    // TODO: Handle imminent rollover
 | 
					    worldState.LiteSorties.push(getLiteSortie(week));
 | 
				
			||||||
    {
 | 
					    if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
 | 
				
			||||||
        const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
 | 
					        worldState.LiteSorties.push(getLiteSortie(week + 1));
 | 
				
			||||||
        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
 | 
				
			||||||
@ -1071,3 +1031,70 @@ 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
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -250,9 +250,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    Guide?: number;
 | 
					    Guide?: number;
 | 
				
			||||||
    Moderator?: boolean;
 | 
					    Moderator?: boolean;
 | 
				
			||||||
    Partner?: boolean;
 | 
					    Partner?: boolean;
 | 
				
			||||||
    Accolades?: {
 | 
					    Accolades?: IAccolades;
 | 
				
			||||||
        Heirloom?: boolean;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    Counselor?: boolean;
 | 
					    Counselor?: boolean;
 | 
				
			||||||
    Upgrades: IUpgradeClient[];
 | 
					    Upgrades: IUpgradeClient[];
 | 
				
			||||||
    EquippedGear: string[];
 | 
					    EquippedGear: string[];
 | 
				
			||||||
@ -489,7 +487,7 @@ export interface ICrewMemberClient {
 | 
				
			|||||||
    XP: number;
 | 
					    XP: number;
 | 
				
			||||||
    PowersuitType: string;
 | 
					    PowersuitType: string;
 | 
				
			||||||
    Configs: IItemConfig[];
 | 
					    Configs: IItemConfig[];
 | 
				
			||||||
    SecondInCommand: boolean;
 | 
					    SecondInCommand: boolean; // on call
 | 
				
			||||||
    ItemId: IOid;
 | 
					    ItemId: IOid;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -914,6 +912,10 @@ 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;
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ export interface IVendorInfo {
 | 
				
			|||||||
    TypeName: string;
 | 
					    TypeName: string;
 | 
				
			||||||
    ItemManifest: IItemManifest[];
 | 
					    ItemManifest: IItemManifest[];
 | 
				
			||||||
    PropertyTextHash?: string;
 | 
					    PropertyTextHash?: string;
 | 
				
			||||||
    RandomSeedType?: "VRST_WEAPON";
 | 
					    RandomSeedType?: string;
 | 
				
			||||||
    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.
 | 
				
			||||||
 | 
				
			|||||||
@ -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: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"
 | 
					    Boss: "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL";
 | 
				
			||||||
    Missions: {
 | 
					    Missions: {
 | 
				
			||||||
        missionType: string;
 | 
					        missionType: string;
 | 
				
			||||||
        node: string;
 | 
					        node: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -1097,5 +1097,6 @@
 | 
				
			|||||||
  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLamp",
 | 
					  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLamp",
 | 
				
			||||||
  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampLarge",
 | 
					  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampLarge",
 | 
				
			||||||
  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampSmall",
 | 
					  "/Lotus/Types/LevelObjects/InfestedPumpkinCocoonLampSmall",
 | 
				
			||||||
  "/Lotus/Types/LevelObjects/InfestedPumpkinExplosiveTotem"
 | 
					  "/Lotus/Types/LevelObjects/InfestedPumpkinExplosiveTotem",
 | 
				
			||||||
 | 
					  "/Lotus/Types/Enemies/Orokin/OrokinMoaBipedAvatar"
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
@ -487,6 +487,7 @@ 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");
 | 
				
			||||||
@ -683,6 +684,7 @@ 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");
 | 
				
			||||||
@ -723,6 +725,7 @@ 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");
 | 
				
			||||||
@ -765,6 +768,7 @@ 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");
 | 
				
			||||||
@ -1097,8 +1101,6 @@ 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();
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1120,8 +1122,6 @@ 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,6 +1260,8 @@ 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,
 | 
				
			||||||
@ -1267,14 +1269,19 @@ function doAcquireMod() {
 | 
				
			|||||||
                data: JSON.stringify([
 | 
					                data: JSON.stringify([
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        ItemType: uniqueName,
 | 
					                        ItemType: uniqueName,
 | 
				
			||||||
                    ItemCount: parseInt($("#mod-count").val())
 | 
					                        ItemCount: count
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ])
 | 
					                ])
 | 
				
			||||||
            }).done(function () {
 | 
					            }).done(function () {
 | 
				
			||||||
            document.getElementById("mod-to-acquire").value = "";
 | 
					                if (count > 0) {
 | 
				
			||||||
 | 
					                    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);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user