forked from OpenWF/SpaceNinjaServer
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			intrinsics
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0dfd1e91ea | 
							
								
								
									
										1449
									
								
								inventoryService.ts
									
									
									
									
									
								
							
							
						
						
									
										1449
									
								
								inventoryService.ts
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,906 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
    ExportEnemies,
 | 
					 | 
				
			||||||
    ExportFusionBundles,
 | 
					 | 
				
			||||||
    ExportRegions,
 | 
					 | 
				
			||||||
    ExportRewards,
 | 
					 | 
				
			||||||
    IMissionReward as IMissionRewardExternal,
 | 
					 | 
				
			||||||
    IReward
 | 
					 | 
				
			||||||
} from "warframe-public-export-plus";
 | 
					 | 
				
			||||||
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
 | 
					 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					 | 
				
			||||||
import { IRngResult, getRandomElement, getRandomReward } from "@/src/services/rngService";
 | 
					 | 
				
			||||||
import { equipmentKeys, IInventoryDatabase, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    addChallenges,
 | 
					 | 
				
			||||||
    addConsumables,
 | 
					 | 
				
			||||||
    addCrewShipAmmo,
 | 
					 | 
				
			||||||
    addCrewShipRawSalvage,
 | 
					 | 
				
			||||||
    addEmailItem,
 | 
					 | 
				
			||||||
    addFocusXpIncreases,
 | 
					 | 
				
			||||||
    addFusionTreasures,
 | 
					 | 
				
			||||||
    addGearExpByCategory,
 | 
					 | 
				
			||||||
    addItem,
 | 
					 | 
				
			||||||
    addMiscItems,
 | 
					 | 
				
			||||||
    addMissionComplete,
 | 
					 | 
				
			||||||
    addMods,
 | 
					 | 
				
			||||||
    addRecipes,
 | 
					 | 
				
			||||||
    combineInventoryChanges,
 | 
					 | 
				
			||||||
    updateSyndicate
 | 
					 | 
				
			||||||
} from "@/src/services/inventoryService";
 | 
					 | 
				
			||||||
import { updateQuestKey } from "@/src/services/questService";
 | 
					 | 
				
			||||||
import { HydratedDocument } from "mongoose";
 | 
					 | 
				
			||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					 | 
				
			||||||
import { getLevelKeyRewards, getNode, toStoreItem } from "@/src/services/itemDataService";
 | 
					 | 
				
			||||||
import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
					 | 
				
			||||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
					 | 
				
			||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
					 | 
				
			||||||
import { handleStoreItemAcquisition } from "./purchaseService";
 | 
					 | 
				
			||||||
import { IMissionReward } from "../types/missionTypes";
 | 
					 | 
				
			||||||
import { crackRelic } from "@/src/helpers/relicHelper";
 | 
					 | 
				
			||||||
import { createMessage } from "./inboxService";
 | 
					 | 
				
			||||||
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
 | 
					 | 
				
			||||||
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
 | 
					 | 
				
			||||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getRotations = (rotationCount: number): number[] => {
 | 
					 | 
				
			||||||
    if (rotationCount === 0) return [0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rotationPattern = [0, 0, 1, 2]; // A, A, B, C
 | 
					 | 
				
			||||||
    const rotatedValues = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (let i = 0; i < rotationCount; i++) {
 | 
					 | 
				
			||||||
        rotatedValues.push(rotationPattern[i % rotationPattern.length]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return rotatedValues;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
 | 
					 | 
				
			||||||
    return getRandomReward(pool as IRngResult[]);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//type TMissionInventoryUpdateKeys = keyof IMissionInventoryUpdateRequest;
 | 
					 | 
				
			||||||
//const ignoredInventoryUpdateKeys = ["FpsAvg", "FpsMax", "FpsMin", "FpsSamples"] satisfies TMissionInventoryUpdateKeys[]; // for keys with no meaning for this server
 | 
					 | 
				
			||||||
//type TignoredInventoryUpdateKeys = (typeof ignoredInventoryUpdateKeys)[number];
 | 
					 | 
				
			||||||
//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const addMissionInventoryUpdates = async (
 | 
					 | 
				
			||||||
    inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
 | 
					 | 
				
			||||||
    inventoryUpdates: IMissionInventoryUpdateRequest
 | 
					 | 
				
			||||||
): Promise<IInventoryChanges> => {
 | 
					 | 
				
			||||||
    const inventoryChanges: IInventoryChanges = {};
 | 
					 | 
				
			||||||
    if (inventoryUpdates.RewardInfo && inventoryUpdates.RewardInfo.periodicMissionTag) {
 | 
					 | 
				
			||||||
        const tag = inventoryUpdates.RewardInfo.periodicMissionTag;
 | 
					 | 
				
			||||||
        const existingCompletion = inventory.PeriodicMissionCompletions.find(completion => completion.tag === tag);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (existingCompletion) {
 | 
					 | 
				
			||||||
            existingCompletion.date = new Date();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            inventory.PeriodicMissionCompletions.push({
 | 
					 | 
				
			||||||
                tag: tag,
 | 
					 | 
				
			||||||
                date: new Date()
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (inventoryUpdates.RewardInfo && inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
 | 
					 | 
				
			||||||
        inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
 | 
					 | 
				
			||||||
        if (value === undefined) {
 | 
					 | 
				
			||||||
            logger.error(`Inventory update key ${key} has no value `);
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        switch (key) {
 | 
					 | 
				
			||||||
            case "RegularCredits":
 | 
					 | 
				
			||||||
                inventory.RegularCredits += value;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "QuestKeys":
 | 
					 | 
				
			||||||
                await updateQuestKey(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "AffiliationChanges":
 | 
					 | 
				
			||||||
                updateSyndicate(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            // Incarnon Challenges
 | 
					 | 
				
			||||||
            case "EvolutionProgress": {
 | 
					 | 
				
			||||||
                for (const evoProgress of value) {
 | 
					 | 
				
			||||||
                    const entry = inventory.EvolutionProgress
 | 
					 | 
				
			||||||
                        ? inventory.EvolutionProgress.find(entry => entry.ItemType == evoProgress.ItemType)
 | 
					 | 
				
			||||||
                        : undefined;
 | 
					 | 
				
			||||||
                    if (entry) {
 | 
					 | 
				
			||||||
                        entry.Progress = evoProgress.Progress;
 | 
					 | 
				
			||||||
                        entry.Rank = evoProgress.Rank;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        inventory.EvolutionProgress ??= [];
 | 
					 | 
				
			||||||
                        inventory.EvolutionProgress.push(evoProgress);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "Missions":
 | 
					 | 
				
			||||||
                addMissionComplete(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "LastRegionPlayed":
 | 
					 | 
				
			||||||
                inventory.LastRegionPlayed = value;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "RawUpgrades":
 | 
					 | 
				
			||||||
                addMods(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "MiscItems":
 | 
					 | 
				
			||||||
            case "BonusMiscItems":
 | 
					 | 
				
			||||||
                addMiscItems(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "Consumables":
 | 
					 | 
				
			||||||
                addConsumables(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "Recipes":
 | 
					 | 
				
			||||||
                addRecipes(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "ChallengeProgress":
 | 
					 | 
				
			||||||
                addChallenges(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "FusionTreasures":
 | 
					 | 
				
			||||||
                addFusionTreasures(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "CrewShipRawSalvage":
 | 
					 | 
				
			||||||
                addCrewShipRawSalvage(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "CrewShipAmmo":
 | 
					 | 
				
			||||||
                addCrewShipAmmo(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "FusionBundles": {
 | 
					 | 
				
			||||||
                let fusionPoints = 0;
 | 
					 | 
				
			||||||
                for (const fusionBundle of value) {
 | 
					 | 
				
			||||||
                    const fusionPointsTotal =
 | 
					 | 
				
			||||||
                        ExportFusionBundles[fusionBundle.ItemType].fusionPoints * fusionBundle.ItemCount;
 | 
					 | 
				
			||||||
                    inventory.FusionPoints += fusionPointsTotal;
 | 
					 | 
				
			||||||
                    fusionPoints += fusionPointsTotal;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                inventoryChanges.FusionPoints = fusionPoints;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "EmailItems": {
 | 
					 | 
				
			||||||
                for (const tc of value) {
 | 
					 | 
				
			||||||
                    await addEmailItem(inventory, tc.ItemType);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "FocusXpIncreases": {
 | 
					 | 
				
			||||||
                addFocusXpIncreases(inventory, value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "PlayerSkillGains": {
 | 
					 | 
				
			||||||
                inventory.PlayerSkills.LPP_SPACE += value.LPP_SPACE;
 | 
					 | 
				
			||||||
                inventory.PlayerSkills.LPP_DRIFTER += value.LPP_DRIFTER;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "CustomMarkers": {
 | 
					 | 
				
			||||||
                value.forEach(markers => {
 | 
					 | 
				
			||||||
                    const map = inventory.CustomMarkers
 | 
					 | 
				
			||||||
                        ? inventory.CustomMarkers.find(entry => entry.tag == markers.tag)
 | 
					 | 
				
			||||||
                        : undefined;
 | 
					 | 
				
			||||||
                    if (map) {
 | 
					 | 
				
			||||||
                        map.markerInfos = markers.markerInfos;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        inventory.CustomMarkers ??= [];
 | 
					 | 
				
			||||||
                        inventory.CustomMarkers.push(markers);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "LoreFragmentScans":
 | 
					 | 
				
			||||||
                value.forEach(clientFragment => {
 | 
					 | 
				
			||||||
                    const fragment = inventory.LoreFragmentScans.find(x => x.ItemType == clientFragment.ItemType);
 | 
					 | 
				
			||||||
                    if (fragment) {
 | 
					 | 
				
			||||||
                        fragment.Progress += clientFragment.Progress;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        inventory.LoreFragmentScans.push(clientFragment);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "LibraryScans":
 | 
					 | 
				
			||||||
                value.forEach(scan => {
 | 
					 | 
				
			||||||
                    let synthesisIgnored = true;
 | 
					 | 
				
			||||||
                    if (
 | 
					 | 
				
			||||||
                        inventory.LibraryPersonalTarget &&
 | 
					 | 
				
			||||||
                        libraryPersonalTargetToAvatar[inventory.LibraryPersonalTarget] == scan.EnemyType
 | 
					 | 
				
			||||||
                    ) {
 | 
					 | 
				
			||||||
                        let progress = inventory.LibraryPersonalProgress.find(
 | 
					 | 
				
			||||||
                            x => x.TargetType == inventory.LibraryPersonalTarget
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                        if (!progress) {
 | 
					 | 
				
			||||||
                            progress =
 | 
					 | 
				
			||||||
                                inventory.LibraryPersonalProgress[
 | 
					 | 
				
			||||||
                                    inventory.LibraryPersonalProgress.push({
 | 
					 | 
				
			||||||
                                        TargetType: inventory.LibraryPersonalTarget,
 | 
					 | 
				
			||||||
                                        Scans: 0,
 | 
					 | 
				
			||||||
                                        Completed: false
 | 
					 | 
				
			||||||
                                    }) - 1
 | 
					 | 
				
			||||||
                                ];
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        progress.Scans += scan.Count;
 | 
					 | 
				
			||||||
                        if (
 | 
					 | 
				
			||||||
                            progress.Scans >=
 | 
					 | 
				
			||||||
                            (inventory.LibraryPersonalTarget ==
 | 
					 | 
				
			||||||
                            "/Lotus/Types/Game/Library/Targets/DragonframeQuestTarget"
 | 
					 | 
				
			||||||
                                ? 3
 | 
					 | 
				
			||||||
                                : 10)
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            progress.Completed = true;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        logger.debug(`synthesis of ${scan.EnemyType} added to personal target progress`);
 | 
					 | 
				
			||||||
                        synthesisIgnored = false;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (
 | 
					 | 
				
			||||||
                        inventory.LibraryActiveDailyTaskInfo &&
 | 
					 | 
				
			||||||
                        inventory.LibraryActiveDailyTaskInfo.EnemyTypes.find(x => x == scan.EnemyType)
 | 
					 | 
				
			||||||
                    ) {
 | 
					 | 
				
			||||||
                        inventory.LibraryActiveDailyTaskInfo.Scans ??= 0;
 | 
					 | 
				
			||||||
                        inventory.LibraryActiveDailyTaskInfo.Scans += scan.Count;
 | 
					 | 
				
			||||||
                        logger.debug(`synthesis of ${scan.EnemyType} added to daily task progress`);
 | 
					 | 
				
			||||||
                        synthesisIgnored = false;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (synthesisIgnored) {
 | 
					 | 
				
			||||||
                        logger.warn(`ignoring synthesis of ${scan.EnemyType} due to not knowing why you did that`);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "CollectibleScans":
 | 
					 | 
				
			||||||
                for (const scan of value) {
 | 
					 | 
				
			||||||
                    const entry = inventory.CollectibleSeries?.find(x => x.CollectibleType == scan.CollectibleType);
 | 
					 | 
				
			||||||
                    if (entry) {
 | 
					 | 
				
			||||||
                        entry.Count = scan.Count;
 | 
					 | 
				
			||||||
                        entry.Tracking = scan.Tracking;
 | 
					 | 
				
			||||||
                        if (entry.CollectibleType == "/Lotus/Objects/Orokin/Props/CollectibleSeriesOne") {
 | 
					 | 
				
			||||||
                            const progress = entry.Count / entry.ReqScans;
 | 
					 | 
				
			||||||
                            for (const gate of entry.IncentiveStates) {
 | 
					 | 
				
			||||||
                                gate.complete = progress >= gate.threshold;
 | 
					 | 
				
			||||||
                                if (gate.complete && !gate.sent) {
 | 
					 | 
				
			||||||
                                    gate.sent = true;
 | 
					 | 
				
			||||||
                                    if (gate.threshold == 0.5) {
 | 
					 | 
				
			||||||
                                        await createMessage(inventory.accountOwnerId.toString(), [kuriaMessage50]);
 | 
					 | 
				
			||||||
                                    } else {
 | 
					 | 
				
			||||||
                                        await createMessage(inventory.accountOwnerId.toString(), [kuriaMessage75]);
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            if (progress >= 1.0) {
 | 
					 | 
				
			||||||
                                await createMessage(inventory.accountOwnerId.toString(), [kuriaMessage100]);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        logger.warn(`${scan.CollectibleType} was not found in inventory, ignoring scans`);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "Upgrades":
 | 
					 | 
				
			||||||
                value.forEach(clientUpgrade => {
 | 
					 | 
				
			||||||
                    const upgrade = inventory.Upgrades.id(clientUpgrade.ItemId.$oid)!;
 | 
					 | 
				
			||||||
                    upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "SyndicateId": {
 | 
					 | 
				
			||||||
                inventory.CompletedSyndicates.push(value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "SortieId": {
 | 
					 | 
				
			||||||
                inventory.CompletedSorties.push(value);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "SeasonChallengeCompletions": {
 | 
					 | 
				
			||||||
                const processedCompletions = value.map(({ challenge, id }) => ({
 | 
					 | 
				
			||||||
                    challenge: challenge.substring(challenge.lastIndexOf("/") + 1),
 | 
					 | 
				
			||||||
                    id
 | 
					 | 
				
			||||||
                }));
 | 
					 | 
				
			||||||
                inventory.SeasonChallengeHistory.push(...processedCompletions);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            case "DeathMarks": {
 | 
					 | 
				
			||||||
                for (const deathMark of value) {
 | 
					 | 
				
			||||||
                    if (!inventory.DeathMarks.find(x => x == deathMark)) {
 | 
					 | 
				
			||||||
                        // It's a new death mark; we have to say the line.
 | 
					 | 
				
			||||||
                        await createMessage(inventory.accountOwnerId.toString(), [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                sub: "/Lotus/Language/G1Quests/DeathMarkTitle",
 | 
					 | 
				
			||||||
                                sndr: "/Lotus/Language/G1Quests/DeathMarkSender",
 | 
					 | 
				
			||||||
                                msg: "/Lotus/Language/G1Quests/DeathMarkMessage",
 | 
					 | 
				
			||||||
                                icon: "/Lotus/Interface/Icons/Npcs/Stalker_d.png",
 | 
					 | 
				
			||||||
                                highPriority: true
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        ]);
 | 
					 | 
				
			||||||
                        // TODO: This type of inbox message seems to automatically delete itself. Figure out under which conditions.
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                inventory.DeathMarks = value;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
default:
 | 
					 | 
				
			||||||
    // Equipment XP updates
 | 
					 | 
				
			||||||
    if (equipmentKeys.includes(key as TEquipmentKey)) {
 | 
					 | 
				
			||||||
        const multipliedValue = (value as IEquipmentClient[]).map(equipment => {
 | 
					 | 
				
			||||||
            // 生成一个 0 到 1 的随机数
 | 
					 | 
				
			||||||
            const randomChance = Math.random();
 | 
					 | 
				
			||||||
            // 基础倍率为 5 倍
 | 
					 | 
				
			||||||
            let multiplier = 5;
 | 
					 | 
				
			||||||
            // 10% 的概率触发 10 倍经验
 | 
					 | 
				
			||||||
            if (randomChance < 0.1) {
 | 
					 | 
				
			||||||
                multiplier = 10; // ✅ 10% 概率 10 倍经验
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // 计算最终经验值
 | 
					 | 
				
			||||||
            const finalXP = (equipment.XP ?? 0) * multiplier;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 日志输出(中文)
 | 
					 | 
				
			||||||
            logger.debug(`[经验倍率] 随机数: ${randomChance.toFixed(2)}, 倍率: ${multiplier}, 最终经验值: ${finalXP}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                ...equipment,
 | 
					 | 
				
			||||||
                XP: finalXP // ✅ 处理 undefined
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        addGearExpByCategory(inventory, multipliedValue, key as TEquipmentKey);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
            // if (
 | 
					 | 
				
			||||||
            //     (ignoredInventoryUpdateKeys as readonly string[]).includes(key) ||
 | 
					 | 
				
			||||||
            //     knownUnhandledKeys.includes(key)
 | 
					 | 
				
			||||||
            // ) {
 | 
					 | 
				
			||||||
            //     continue;
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
            // logger.error(`Unhandled inventory update key: ${key}`);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return inventoryChanges;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface AddMissionRewardsReturnType {
 | 
					 | 
				
			||||||
    MissionRewards: IMissionReward[];
 | 
					 | 
				
			||||||
    inventoryChanges?: IInventoryChanges;
 | 
					 | 
				
			||||||
    credits?: IMissionCredits;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//TODO: return type of partial missioninventoryupdate response
 | 
					 | 
				
			||||||
export const addMissionRewards = async (
 | 
					 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        RewardInfo: rewardInfo,
 | 
					 | 
				
			||||||
        LevelKeyName: levelKeyName,
 | 
					 | 
				
			||||||
        Missions: missions,
 | 
					 | 
				
			||||||
        RegularCredits: creditDrops,
 | 
					 | 
				
			||||||
        VoidTearParticipantsCurrWave: voidTearWave,
 | 
					 | 
				
			||||||
        StrippedItems: strippedItems
 | 
					 | 
				
			||||||
    }: IMissionInventoryUpdateRequest
 | 
					 | 
				
			||||||
): Promise<AddMissionRewardsReturnType> => {
 | 
					 | 
				
			||||||
    if (!rewardInfo) {
 | 
					 | 
				
			||||||
        //TODO: if there is a case where you can have credits collected during a mission but no rewardInfo, add credits needs to be handled earlier
 | 
					 | 
				
			||||||
        logger.debug(`Mission ${missions!.Tag} did not have Reward Info `);
 | 
					 | 
				
			||||||
        return { MissionRewards: [] };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //TODO: check double reward merging
 | 
					 | 
				
			||||||
    const MissionRewards: IMissionReward[] = getRandomMissionDrops(rewardInfo);
 | 
					 | 
				
			||||||
    logger.debug("random mission drops:", MissionRewards);
 | 
					 | 
				
			||||||
    const inventoryChanges: IInventoryChanges = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let missionCompletionCredits = 0;
 | 
					 | 
				
			||||||
    //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
 | 
					 | 
				
			||||||
    if (levelKeyName) {
 | 
					 | 
				
			||||||
        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
					 | 
				
			||||||
        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
					 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards) {
 | 
					 | 
				
			||||||
            addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, inventory, MissionRewards);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards2) {
 | 
					 | 
				
			||||||
            for (const reward of fixedLevelRewards.levelKeyRewards2) {
 | 
					 | 
				
			||||||
                //quest stage completion credit rewards
 | 
					 | 
				
			||||||
                if (reward.rewardType == "RT_CREDITS") {
 | 
					 | 
				
			||||||
                    inventory.RegularCredits += reward.amount;
 | 
					 | 
				
			||||||
                    missionCompletionCredits += reward.amount;
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                MissionRewards.push({
 | 
					 | 
				
			||||||
                    StoreItem: reward.itemType,
 | 
					 | 
				
			||||||
                    ItemCount: reward.rewardType === "RT_RESOURCE" ? reward.amount : 1
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (
 | 
					 | 
				
			||||||
        missions &&
 | 
					 | 
				
			||||||
        missions.Tag != "" // https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1013
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        const node = getNode(missions.Tag);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //node based credit rewards for mission completion
 | 
					 | 
				
			||||||
        if (node.missionIndex !== 28) {
 | 
					 | 
				
			||||||
            const levelCreditReward = getLevelCreditRewards(missions.Tag);
 | 
					 | 
				
			||||||
            missionCompletionCredits += levelCreditReward;
 | 
					 | 
				
			||||||
            inventory.RegularCredits += levelCreditReward;
 | 
					 | 
				
			||||||
            logger.debug(`levelCreditReward ${levelCreditReward}`);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (node.missionReward) {
 | 
					 | 
				
			||||||
            missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (rewardInfo.useVaultManifest) {
 | 
					 | 
				
			||||||
        MissionRewards.push({
 | 
					 | 
				
			||||||
            StoreItem: getRandomElement(corruptedMods),
 | 
					 | 
				
			||||||
            ItemCount: 1
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const reward of MissionRewards) {
 | 
					 | 
				
			||||||
        const inventoryChange = await handleStoreItemAcquisition(reward.StoreItem, inventory, reward.ItemCount);
 | 
					 | 
				
			||||||
        //TODO: combineInventoryChanges improve type safety, merging 2 of the same item?
 | 
					 | 
				
			||||||
        //TODO: check for the case when two of the same item are added, combineInventoryChanges should merge them, but the client also merges them
 | 
					 | 
				
			||||||
        //TODO: some conditional types to rule out binchanges?
 | 
					 | 
				
			||||||
        combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const credits = addCredits(inventory, {
 | 
					 | 
				
			||||||
        missionCompletionCredits,
 | 
					 | 
				
			||||||
        missionDropCredits: creditDrops ?? 0,
 | 
					 | 
				
			||||||
        rngRewardCredits: inventoryChanges.RegularCredits ?? 0
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (
 | 
					 | 
				
			||||||
        voidTearWave &&
 | 
					 | 
				
			||||||
        voidTearWave.Participants[0].QualifiesForReward &&
 | 
					 | 
				
			||||||
        !voidTearWave.Participants[0].HaveRewardResponse
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        const reward = await crackRelic(inventory, voidTearWave.Participants[0], inventoryChanges);
 | 
					 | 
				
			||||||
        MissionRewards.push({ StoreItem: reward.type, ItemCount: reward.itemCount });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (strippedItems) {
 | 
					 | 
				
			||||||
        for (const si of strippedItems) {
 | 
					 | 
				
			||||||
            const droptable = ExportEnemies.droptables[si.DropTable];
 | 
					 | 
				
			||||||
            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					 | 
				
			||||||
            if (!droptable) {
 | 
					 | 
				
			||||||
                logger.error(`unknown droptable ${si.DropTable}`);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                for (let i = 0; i != (si.DROP_MOD || []).length; ++i) {
 | 
					 | 
				
			||||||
                    for (const pool of droptable) {
 | 
					 | 
				
			||||||
                        const reward = getRandomReward(pool.items)!;
 | 
					 | 
				
			||||||
                        logger.debug(`stripped droptable rolled`, reward);
 | 
					 | 
				
			||||||
                        await addItem(inventory, reward.type);
 | 
					 | 
				
			||||||
                        MissionRewards.push({
 | 
					 | 
				
			||||||
                            StoreItem: toStoreItem(reward.type),
 | 
					 | 
				
			||||||
                            ItemCount: 1,
 | 
					 | 
				
			||||||
                            FromEnemyCache: true // to show "identified"
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return { inventoryChanges, MissionRewards, credits };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface IMissionCredits {
 | 
					 | 
				
			||||||
    MissionCredits: number[];
 | 
					 | 
				
			||||||
    CreditBonus: number[];
 | 
					 | 
				
			||||||
    TotalCredits: number[];
 | 
					 | 
				
			||||||
    DailyMissionBonus?: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//creditBonus is not entirely accurate.
 | 
					 | 
				
			||||||
//TODO: consider ActiveBoosters
 | 
					 | 
				
			||||||
export const addCredits = (
 | 
					 | 
				
			||||||
    inventory: HydratedDocument<IInventoryDatabase>,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        missionDropCredits,
 | 
					 | 
				
			||||||
        missionCompletionCredits,
 | 
					 | 
				
			||||||
        rngRewardCredits
 | 
					 | 
				
			||||||
    }: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
 | 
					 | 
				
			||||||
): IMissionCredits => {
 | 
					 | 
				
			||||||
    const hasDailyCreditBonus = true;
 | 
					 | 
				
			||||||
    const totalCredits = missionDropCredits + missionCompletionCredits + rngRewardCredits;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const finalCredits: IMissionCredits = {
 | 
					 | 
				
			||||||
        MissionCredits: [missionDropCredits, missionDropCredits],
 | 
					 | 
				
			||||||
        CreditBonus: [missionCompletionCredits, missionCompletionCredits],
 | 
					 | 
				
			||||||
        TotalCredits: [totalCredits, totalCredits]
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (hasDailyCreditBonus) {
 | 
					 | 
				
			||||||
        inventory.RegularCredits += missionCompletionCredits;
 | 
					 | 
				
			||||||
        finalCredits.CreditBonus[1] *= 2;
 | 
					 | 
				
			||||||
        finalCredits.MissionCredits[1] *= 2;
 | 
					 | 
				
			||||||
        finalCredits.TotalCredits[1] *= 2;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!hasDailyCreditBonus) {
 | 
					 | 
				
			||||||
        return finalCredits;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return { ...finalCredits, DailyMissionBonus: true };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const addFixedLevelRewards = (
 | 
					 | 
				
			||||||
    rewards: IMissionRewardExternal,
 | 
					 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					 | 
				
			||||||
    MissionRewards: IMissionReward[]
 | 
					 | 
				
			||||||
): number => {
 | 
					 | 
				
			||||||
    let missionBonusCredits = 0;
 | 
					 | 
				
			||||||
    if (rewards.credits) {
 | 
					 | 
				
			||||||
        missionBonusCredits += rewards.credits;
 | 
					 | 
				
			||||||
        inventory.RegularCredits += rewards.credits;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rewards.items) {
 | 
					 | 
				
			||||||
        for (const item of rewards.items) {
 | 
					 | 
				
			||||||
            MissionRewards.push({
 | 
					 | 
				
			||||||
                StoreItem: item,
 | 
					 | 
				
			||||||
                ItemCount: 1
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rewards.countedItems) {
 | 
					 | 
				
			||||||
        for (const item of rewards.countedItems) {
 | 
					 | 
				
			||||||
            MissionRewards.push({
 | 
					 | 
				
			||||||
                StoreItem: `/Lotus/StoreItems${item.ItemType.substring("Lotus/".length)}`,
 | 
					 | 
				
			||||||
                ItemCount: item.ItemCount
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (rewards.countedStoreItems) {
 | 
					 | 
				
			||||||
        for (const item of rewards.countedStoreItems) {
 | 
					 | 
				
			||||||
            MissionRewards.push(item);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return missionBonusCredits;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getLevelCreditRewards(nodeName: string): number {
 | 
					 | 
				
			||||||
    const minEnemyLevel = getNode(nodeName).minEnemyLevel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 1000 + (minEnemyLevel - 1) * 100;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //TODO: get dark sektor fixed credit rewards and railjack bonus
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getRandomMissionDrops(RewardInfo: IRewardInfo): IMissionReward[] {
 | 
					 | 
				
			||||||
    const drops: IMissionReward[] = [];
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	    // 模糊匹配 jobId 并处理奖励
 | 
					 | 
				
			||||||
    if (RewardInfo.jobId) {
 | 
					 | 
				
			||||||
        // 定义任务类型和对应的奖励表、声望阵营及声望值
 | 
					 | 
				
			||||||
        const jobRewardsMap: Record<string, { rewardManifest: string; tag: string; standingValue: number }> = {
 | 
					 | 
				
			||||||
            "AssassinateBountyCap": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableCRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 430
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosGrnSurvivorBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATableBRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 450
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosAreaDefenseBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTableBRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 500
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosEndlessExcavateBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTableARewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 550
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosAssassinateBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTableBRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 600
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosKeyPiecesBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 650
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "DeimosExcavateBounty": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 700
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusIntelJobSpy": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 450
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusCullJobResource": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 500
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusIntelJobRecovery": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 550
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusHelpingJobCaches": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 600
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusArtifactJobAmbush": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 650
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "VenusChaosJobExcavation": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 700
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "NarmerVenusCullJobExterminate": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTableBRewards",
 | 
					 | 
				
			||||||
                tag: "SolarisSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 800
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "AttritionBountyLib": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 500
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "RescueBountyResc": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 550
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "CaptureBountyCapTwo": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 600
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "ReclamationBountyCache": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 650
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "AttritionBountyCap": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 700
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "ChamberB": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierATableCRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 500
 | 
					 | 
				
			||||||
            },	
 | 
					 | 
				
			||||||
			"ChamberA": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierBTableCRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 800
 | 
					 | 
				
			||||||
            },	
 | 
					 | 
				
			||||||
			"Chamberc": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierCTableCRewards",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 1000
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
			"HeistProfitTakerBountyOne": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierCTableCRewards111",
 | 
					 | 
				
			||||||
                tag: "EntratiSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 1000
 | 
					 | 
				
			||||||
            },				
 | 
					 | 
				
			||||||
            "AssassinateBountyAss": {
 | 
					 | 
				
			||||||
                rewardManifest: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTableBRewards",
 | 
					 | 
				
			||||||
                tag: "CetusSyndicate",
 | 
					 | 
				
			||||||
                standingValue: 800
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 遍历任务类型,模糊匹配 jobId
 | 
					 | 
				
			||||||
        for (const [jobType, { rewardManifest, tag, standingValue }] of Object.entries(jobRewardsMap)) {
 | 
					 | 
				
			||||||
            if (RewardInfo.jobId.includes(jobType)) {
 | 
					 | 
				
			||||||
                logger.debug(`Job ID contains ${jobType}, using reward manifest: ${rewardManifest}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const rewardTable = ExportRewards[rewardManifest];
 | 
					 | 
				
			||||||
                if (rewardTable) {
 | 
					 | 
				
			||||||
                    // 使用 JobStage 作为轮次索引
 | 
					 | 
				
			||||||
                    let rotation = RewardInfo.JobStage || 0; // 默认值为 0
 | 
					 | 
				
			||||||
                    logger.debug("Using JobStage as rotation index:", rotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // 如果 JobStage 超过 3,则按最高档(第 3 档)处理
 | 
					 | 
				
			||||||
                    if (rotation > 3) {
 | 
					 | 
				
			||||||
                        rotation = 3;
 | 
					 | 
				
			||||||
                        logger.debug("JobStage exceeds 3, using highest rotation (3)");
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // 检查轮次索引是否在奖励表范围内
 | 
					 | 
				
			||||||
                    if (rotation >= rewardTable.length || rotation < 0) {
 | 
					 | 
				
			||||||
                        logger.error(`Rotation index ${rotation} is out of bounds for reward table ${rewardManifest}`);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        // 获取当前轮次的奖励池
 | 
					 | 
				
			||||||
                        const rotationRewards = rewardTable[rotation];
 | 
					 | 
				
			||||||
                        logger.debug("Rotation rewards:", rotationRewards);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // 从奖励池中随机选择一个奖励
 | 
					 | 
				
			||||||
                        const drop = getRandomRewardByChance(rotationRewards);
 | 
					 | 
				
			||||||
                        if (drop) {
 | 
					 | 
				
			||||||
                            logger.debug("Random drop selected:", drop);
 | 
					 | 
				
			||||||
                            drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            logger.debug("No drop selected from reward table");
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    logger.error(`Reward table ${rewardManifest} not found in ExportRewards`);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // 新增一个固定的物品奖励
 | 
					 | 
				
			||||||
                    const additionalReward = {
 | 
					 | 
				
			||||||
                        StoreItem: "/Lotus/StoreItems/Types/Items/SyndicateDogTags/UniversalSyndicateDogTag", // 新物品的路径
 | 
					 | 
				
			||||||
                        ItemCount: 1 // 物品数量
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    drops.push(additionalReward);
 | 
					 | 
				
			||||||
                    logger.debug("Added additional reward:", additionalReward);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // 直接返回,不再执行后续的区域奖励逻辑
 | 
					 | 
				
			||||||
                return drops;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
    if (RewardInfo.node in ExportRegions) {
 | 
					 | 
				
			||||||
        const region = ExportRegions[RewardInfo.node];
 | 
					 | 
				
			||||||
        const rewardManifests: string[] =
 | 
					 | 
				
			||||||
            RewardInfo.periodicMissionTag == "EliteAlert" || RewardInfo.periodicMissionTag == "EliteAlertB"
 | 
					 | 
				
			||||||
                ? ["/Lotus/Types/Game/MissionDecks/EliteAlertMissionRewards/EliteAlertMissionRewards"]
 | 
					 | 
				
			||||||
                : region.rewardManifests;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let rotations: number[] = [];
 | 
					 | 
				
			||||||
        if (RewardInfo.VaultsCracked) {
 | 
					 | 
				
			||||||
            // For Spy missions, e.g. 3 vaults cracked = A, B, C
 | 
					 | 
				
			||||||
            for (let i = 0; i != RewardInfo.VaultsCracked; ++i) {
 | 
					 | 
				
			||||||
                rotations.push(i);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            const rotationCount = RewardInfo.rewardQualifications?.length || 0;
 | 
					 | 
				
			||||||
            rotations = getRotations(rotationCount);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rewardManifests
 | 
					 | 
				
			||||||
    .map(name => ExportRewards[name])
 | 
					 | 
				
			||||||
    .forEach(table => {
 | 
					 | 
				
			||||||
        for (const rotation of rotations) {
 | 
					 | 
				
			||||||
            const rotationRewards = table[rotation];
 | 
					 | 
				
			||||||
            const drop = getRandomRewardByChance(rotationRewards);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 原始掉落逻辑
 | 
					 | 
				
			||||||
            if (drop) {
 | 
					 | 
				
			||||||
                drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // EliteAlert奖励逻辑
 | 
					 | 
				
			||||||
            if (RewardInfo.periodicMissionTag === "EliteAlert" || RewardInfo.periodicMissionTag === "EliteAlertB") {
 | 
					 | 
				
			||||||
                const randomCount = Math.floor(Math.random() * 5) + 1;
 | 
					 | 
				
			||||||
                drops.push({ StoreItem: "/Lotus/StoreItems/Types/Items/MiscItems/Elitium", ItemCount: randomCount });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
                // 添加 HardDaily 任务的 钢铁精华 掉落
 | 
					 | 
				
			||||||
                if (RewardInfo.periodicMissionTag?.startsWith("HardDaily")) {
 | 
					 | 
				
			||||||
                    let randomCount = Math.floor(Math.random() * 5) + 1; // 生成 1 到 5 的随机数
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // 20% 的几率翻 1 到 10 倍
 | 
					 | 
				
			||||||
                    if (Math.random() < 0.2) {
 | 
					 | 
				
			||||||
                        const multiplier = Math.floor(Math.random() * 10) + 1; // 生成 1 到 10 的随机倍数
 | 
					 | 
				
			||||||
                        randomCount *= multiplier;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    drops.push({ StoreItem: "/Lotus/StoreItems/Types/Items/MiscItems/SteelEssence", ItemCount: randomCount });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            // 新增10%概率独立掉落 ▼▼▼
 | 
					 | 
				
			||||||
            if (Math.random() < 0.01) { // 每个rotation独立判定
 | 
					 | 
				
			||||||
                drops.push({
 | 
					 | 
				
			||||||
                    StoreItem: "/Lotus/StoreItems/Upgrades/Skins/Volt/SWTechnoshockHelmet",				
 | 
					 | 
				
			||||||
                    ItemCount: 1
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (region.cacheRewardManifest && RewardInfo.EnemyCachesFound) {
 | 
					 | 
				
			||||||
            const deck = ExportRewards[region.cacheRewardManifest];
 | 
					 | 
				
			||||||
            for (let rotation = 0; rotation != RewardInfo.EnemyCachesFound; ++rotation) {
 | 
					 | 
				
			||||||
                const drop = getRandomRewardByChance(deck[rotation]);
 | 
					 | 
				
			||||||
                if (drop) {
 | 
					 | 
				
			||||||
                    drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount, FromEnemyCache: true });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (RewardInfo.nightmareMode) {
 | 
					 | 
				
			||||||
    const deck = ExportRewards["/Lotus/Types/Game/MissionDecks/NightmareModeRewards"];
 | 
					 | 
				
			||||||
    let rotation = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 确保 region 已正确初始化
 | 
					 | 
				
			||||||
    if (region) {
 | 
					 | 
				
			||||||
        if (region.missionIndex === 3 && RewardInfo.rewardTier) {
 | 
					 | 
				
			||||||
            // 如果 missionIndex 为 3 且 rewardTier 存在,则使用 rewardTier
 | 
					 | 
				
			||||||
            rotation = RewardInfo.rewardTier;
 | 
					 | 
				
			||||||
        } else if ([6, 7, 8, 10, 11].includes(region.systemIndex)) {
 | 
					 | 
				
			||||||
            // 如果 systemIndex 在 [6, 7, 8, 10, 11] 中,则 rotation 为 2
 | 
					 | 
				
			||||||
            rotation = 2;
 | 
					 | 
				
			||||||
        } else if ([4, 9, 12, 14, 15, 16, 17, 18].includes(region.systemIndex)) {
 | 
					 | 
				
			||||||
            // 如果 systemIndex 在 [4, 9, 12, 14, 15, 16, 17, 18] 中,则 rotation 为 1
 | 
					 | 
				
			||||||
            rotation = 1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 确保 rotation 在 deck 的范围内
 | 
					 | 
				
			||||||
    if (rotation >= deck.length || rotation < 0) {
 | 
					 | 
				
			||||||
        logger.error(`Rotation index ${rotation} is out of bounds for NightmareModeRewards`);
 | 
					 | 
				
			||||||
        rotation = 0; // 如果超出范围,则使用默认值 0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 获取当前轮次的奖励池
 | 
					 | 
				
			||||||
    const rotationRewards = deck[rotation];
 | 
					 | 
				
			||||||
    if (rotationRewards) {
 | 
					 | 
				
			||||||
        // 从奖励池中随机选择一个奖励
 | 
					 | 
				
			||||||
        const drop = getRandomRewardByChance(rotationRewards);
 | 
					 | 
				
			||||||
        if (drop) {
 | 
					 | 
				
			||||||
            logger.debug("Nightmare mode drop selected:", drop);
 | 
					 | 
				
			||||||
            drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            logger.debug("No drop selected from NightmareModeRewards");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        logger.error("No rewards found for NightmareModeRewards");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 确保函数有返回值
 | 
					 | 
				
			||||||
    return drops;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const corruptedMods = [
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Melee/DualStat/CorruptedHeavyDamageChargeSpeedMod", // Corrupt Charge
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedCritDamagePistol", // Hollow Point
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Melee/DualStat/CorruptedDamageSpeedMod", // Spoiled Strike
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedDamageRecoilPistol", // Magnum Force
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedMaxClipReloadSpeedPistol", // Tainted Clip
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedCritRateFireRateRifle", // Critical Delay
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedDamageRecoilRifle", // Heavy Caliber
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedMaxClipReloadSpeedRifle", // Tainted Mag
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedRecoilFireRateRifle", // Vile Precision
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/CorruptedDurationRangeWarframe", // Narrow Minded
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/CorruptedEfficiencyDurationWarframe", // Fleeting Expertise
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/CorruptedPowerEfficiencyWarframe", // Blind Rage
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/CorruptedRangePowerWarframe", // Overextended
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/CorruptedAccuracyFireRateShotgun", // Tainted Shell
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/CorruptedDamageAccuracyShotgun", // Vicious Spread
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/CorruptedMaxClipReloadSpeedShotgun", // Burdened Magazine
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedFireRateDamagePistol", // Anemic Agility
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedFireRateDamageRifle", // Vile Acceleration
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/CorruptedFireRateDamageShotgun", // Frail Momentum
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/CorruptedCritChanceFireRateShotgun", // Critical Deceleration
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/CorruptedCritChanceFireRatePistol", // Creeping Bullseye
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/CorruptedPowerStrengthPowerDurationWarframe", // Transient Fortitude
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/CorruptedReloadSpeedMaxClipRifle", // Depleted Reload
 | 
					 | 
				
			||||||
    "/Lotus/StoreItems/Upgrades/Mods/Warframe/DualStat/FixedShieldAndShieldGatingDuration" // Catalyzing Shields
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const libraryPersonalTargetToAvatar: Record<string, string> = {
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/DragonframeQuestTarget":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Grineer/Desert/Avatars/RifleLancerAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research1Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Grineer/Desert/Avatars/RifleLancerAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research2Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Corpus/BipedRobot/AIWeek/LaserDiscBipedAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research3Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Grineer/Desert/Avatars/EvisceratorLancerAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research4Target": "/Lotus/Types/Enemies/Orokin/OrokinHealingAncientAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research5Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/ShotgunSpacemanAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research6Target": "/Lotus/Types/Enemies/Infested/AiWeek/Runners/RunnerAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research7Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Grineer/AIWeek/Avatars/GrineerMeleeStaffAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research8Target": "/Lotus/Types/Enemies/Orokin/OrokinHeavyFemaleAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research9Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Infested/AiWeek/Quadrupeds/QuadrupedAvatar",
 | 
					 | 
				
			||||||
    "/Lotus/Types/Game/Library/Targets/Research10Target":
 | 
					 | 
				
			||||||
        "/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,100 +0,0 @@
 | 
				
			|||||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
 | 
					 | 
				
			||||||
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
 | 
					 | 
				
			||||||
import DeimosFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosFishmongerVendorManifest.json";
 | 
					 | 
				
			||||||
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
 | 
					 | 
				
			||||||
import 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 HubsPerrinSequenceWeaponVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsPerrinSequenceWeaponVendorManifest.json";
 | 
					 | 
				
			||||||
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
 | 
					 | 
				
			||||||
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.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";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface IVendorManifest {
 | 
					 | 
				
			||||||
    VendorInfo: {
 | 
					 | 
				
			||||||
        _id: IOid;
 | 
					 | 
				
			||||||
        TypeName: string;
 | 
					 | 
				
			||||||
        ItemManifest: {
 | 
					 | 
				
			||||||
            StoreItem: string;
 | 
					 | 
				
			||||||
            ItemPrices?: { ItemType: string; ItemCount: number; ProductCategory: string }[];
 | 
					 | 
				
			||||||
            Bin: string;
 | 
					 | 
				
			||||||
            QuantityMultiplier: number;
 | 
					 | 
				
			||||||
            Expiry: IMongoDate;
 | 
					 | 
				
			||||||
            PurchaseQuantityLimit?: number;
 | 
					 | 
				
			||||||
            RotatedWeekly?: boolean;
 | 
					 | 
				
			||||||
            AllowMultipurchase: boolean;
 | 
					 | 
				
			||||||
            Id: IOid;
 | 
					 | 
				
			||||||
        }[];
 | 
					 | 
				
			||||||
        Expiry: IMongoDate;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const vendorManifests: IVendorManifest[] = [
 | 
					 | 
				
			||||||
    ArchimedeanVendorManifest,
 | 
					 | 
				
			||||||
    DeimosEntratiFragmentVendorProductsManifest,
 | 
					 | 
				
			||||||
    DeimosFishmongerVendorManifest,
 | 
					 | 
				
			||||||
    DeimosHivemindCommisionsManifestFishmonger,
 | 
					 | 
				
			||||||
    DeimosHivemindCommisionsManifestPetVendor,
 | 
					 | 
				
			||||||
    DeimosHivemindCommisionsManifestProspector,
 | 
					 | 
				
			||||||
    DeimosHivemindCommisionsManifestTokenVendor,
 | 
					 | 
				
			||||||
    DeimosHivemindCommisionsManifestWeaponsmith,
 | 
					 | 
				
			||||||
    DeimosHivemindTokenVendorManifest,
 | 
					 | 
				
			||||||
    DeimosPetVendorManifest,
 | 
					 | 
				
			||||||
    DeimosProspectorVendorManifest,
 | 
					 | 
				
			||||||
    DuviriAcrithisVendorManifest,
 | 
					 | 
				
			||||||
    EntratiLabsEntratiLabsCommisionsManifest,
 | 
					 | 
				
			||||||
    EntratiLabsEntratiLabVendorManifest,
 | 
					 | 
				
			||||||
    GuildAdvertisementVendorManifest,
 | 
					 | 
				
			||||||
    HubsIronwakeDondaVendorManifest,
 | 
					 | 
				
			||||||
    HubsPerrinSequenceWeaponVendorManifest,
 | 
					 | 
				
			||||||
    HubsRailjackCrewMemberVendorManifest,
 | 
					 | 
				
			||||||
    MaskSalesmanManifest,
 | 
					 | 
				
			||||||
    OstronFishmongerVendorManifest,
 | 
					 | 
				
			||||||
    OstronPetVendorManifest,
 | 
					 | 
				
			||||||
    OstronProspectorVendorManifest,
 | 
					 | 
				
			||||||
    RadioLegionIntermission12VendorManifest,
 | 
					 | 
				
			||||||
    SolarisDebtTokenVendorManifest,
 | 
					 | 
				
			||||||
    SolarisDebtTokenVendorRepossessionsManifest,
 | 
					 | 
				
			||||||
    SolarisFishmongerVendorManifest,
 | 
					 | 
				
			||||||
    SolarisProspectorVendorManifest,
 | 
					 | 
				
			||||||
    TeshinHardModeVendorManifest,
 | 
					 | 
				
			||||||
    ZarimanCommisionsManifestArchimedean
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
 | 
					 | 
				
			||||||
    for (const vendorManifest of vendorManifests) {
 | 
					 | 
				
			||||||
        if (vendorManifest.VendorInfo.TypeName == typeName) {
 | 
					 | 
				
			||||||
            return vendorManifest;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return undefined;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
 | 
					 | 
				
			||||||
    for (const vendorManifest of vendorManifests) {
 | 
					 | 
				
			||||||
        if (vendorManifest.VendorInfo._id.$oid == oid) {
 | 
					 | 
				
			||||||
            return vendorManifest;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return undefined;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -12,8 +12,8 @@ import { ExportDrones, ExportResources, ExportSystems } from "warframe-public-ex
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const dronesController: RequestHandler = async (req, res) => {
 | 
					export const dronesController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
    if ("GetActive" in req.query) {
 | 
					    if ("GetActive" in req.query) {
 | 
				
			||||||
        const inventory = await getInventory(accountId, "Drones");
 | 
					 | 
				
			||||||
        const activeDrones: IActiveDrone[] = [];
 | 
					        const activeDrones: IActiveDrone[] = [];
 | 
				
			||||||
        for (const drone of inventory.Drones) {
 | 
					        for (const drone of inventory.Drones) {
 | 
				
			||||||
            if (drone.DeployTime) {
 | 
					            if (drone.DeployTime) {
 | 
				
			||||||
@ -39,7 +39,6 @@ export const dronesController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            ActiveDrones: activeDrones
 | 
					            ActiveDrones: activeDrones
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } else if ("droneId" in req.query && "systemIndex" in req.query) {
 | 
					    } else if ("droneId" in req.query && "systemIndex" in req.query) {
 | 
				
			||||||
        const inventory = await getInventory(accountId, "Drones");
 | 
					 | 
				
			||||||
        const drone = inventory.Drones.id(req.query.droneId as string)!;
 | 
					        const drone = inventory.Drones.id(req.query.droneId as string)!;
 | 
				
			||||||
        const droneMeta = ExportDrones[drone.ItemType];
 | 
					        const droneMeta = ExportDrones[drone.ItemType];
 | 
				
			||||||
        drone.DeployTime = config.instantResourceExtractorDrones ? new Date(0) : new Date();
 | 
					        drone.DeployTime = config.instantResourceExtractorDrones ? new Date(0) : new Date();
 | 
				
			||||||
@ -77,7 +76,6 @@ export const dronesController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        await inventory.save();
 | 
					        await inventory.save();
 | 
				
			||||||
        res.json({});
 | 
					        res.json({});
 | 
				
			||||||
    } else if ("collectDroneId" in req.query) {
 | 
					    } else if ("collectDroneId" in req.query) {
 | 
				
			||||||
        const inventory = await getInventory(accountId);
 | 
					 | 
				
			||||||
        const drone = inventory.Drones.id(req.query.collectDroneId as string)!;
 | 
					        const drone = inventory.Drones.id(req.query.collectDroneId as string)!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (new Date() >= drone.DamageTime!) {
 | 
					        if (new Date() >= drone.DamageTime!) {
 | 
				
			||||||
 | 
				
			|||||||
@ -128,7 +128,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            const miscItemChanges: IMiscItem[] = [];
 | 
					            const miscItemChanges: IMiscItem[] = [];
 | 
				
			||||||
            let totalPercentagePointsGained = 0;
 | 
					            let totalPercentagePointsGained = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const currentUnixSeconds = Math.trunc(Date.now() / 1000);
 | 
					            const currentUnixSeconds = Math.trunc(new Date().getTime() / 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const contribution of request.ResourceContributions) {
 | 
					            for (const contribution of request.ResourceContributions) {
 | 
				
			||||||
                const snack = ExportMisc.helminthSnacks[contribution.ItemType];
 | 
					                const snack = ExportMisc.helminthSnacks[contribution.ItemType];
 | 
				
			||||||
@ -260,7 +260,9 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            inventory.InfestedFoundry!.ConsumedSuits ??= [];
 | 
					            inventory.InfestedFoundry!.ConsumedSuits ??= [];
 | 
				
			||||||
            inventory.InfestedFoundry!.ConsumedSuits.push(consumedSuit);
 | 
					            inventory.InfestedFoundry!.ConsumedSuits.push(consumedSuit);
 | 
				
			||||||
            inventory.InfestedFoundry!.LastConsumedSuit = suit;
 | 
					            inventory.InfestedFoundry!.LastConsumedSuit = suit;
 | 
				
			||||||
            inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date(Date.now() + 24 * 60 * 60 * 1000);
 | 
					            inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date(
 | 
				
			||||||
 | 
					                new Date().getTime() + 24 * 60 * 60 * 1000
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00);
 | 
					            const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00);
 | 
				
			||||||
            addRecipes(inventory, recipeChanges);
 | 
					            addRecipes(inventory, recipeChanges);
 | 
				
			||||||
            freeUpSlot(inventory, InventorySlot.SUITS);
 | 
					            freeUpSlot(inventory, InventorySlot.SUITS);
 | 
				
			||||||
@ -308,7 +310,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            const request = getJSONfromString<IHelminthInvigorationRequest>(String(req.body));
 | 
					            const request = getJSONfromString<IHelminthInvigorationRequest>(String(req.body));
 | 
				
			||||||
            const inventory = await getInventory(accountId);
 | 
					            const inventory = await getInventory(accountId);
 | 
				
			||||||
            const suit = inventory.Suits.id(request.SuitId.$oid)!;
 | 
					            const suit = inventory.Suits.id(request.SuitId.$oid)!;
 | 
				
			||||||
            const upgradesExpiry = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
 | 
					            const upgradesExpiry = new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000);
 | 
				
			||||||
            suit.OffensiveUpgrade = request.OffensiveUpgradeType;
 | 
					            suit.OffensiveUpgrade = request.OffensiveUpgradeType;
 | 
				
			||||||
            suit.DefensiveUpgrade = request.DefensiveUpgradeType;
 | 
					            suit.DefensiveUpgrade = request.DefensiveUpgradeType;
 | 
				
			||||||
            suit.UpgradesExpiry = upgradesExpiry;
 | 
					            suit.UpgradesExpiry = upgradesExpiry;
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import { RequestHandler } from "express";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const playerSkillsController: RequestHandler = async (req, res) => {
 | 
					export const playerSkillsController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
    const inventory = await getInventory(accountId);
 | 
					    const inventory = await getInventory(accountId, "PlayerSkills");
 | 
				
			||||||
    const request = getJSONfromString<IPlayerSkillsRequest>(String(req.body));
 | 
					    const request = getJSONfromString<IPlayerSkillsRequest>(String(req.body));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
 | 
					    const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										19
									
								
								src/controllers/custom/unlockAllIntrinsicsController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/controllers/custom/unlockAllIntrinsicsController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const unlockAllIntrinsicsController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const inventory = await getInventory(accountId, "PlayerSkills");
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_PILOTING = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_GUNNERY = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_TACTICAL = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_ENGINEERING = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_COMMAND = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_DRIFT_COMBAT = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_DRIFT_RIDING = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_DRIFT_OPPORTUNITY = 10;
 | 
				
			||||||
 | 
					    inventory.PlayerSkills.LPS_DRIFT_ENDURANCE = 10;
 | 
				
			||||||
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    res.end();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -43,7 +43,7 @@ export const worldStateController: RequestHandler = (req, res) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
 | 
					    const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
 | 
				
			||||||
    const day = Math.trunc((Date.now() - EPOCH) / 86400000);
 | 
					    const day = Math.trunc((new Date().getTime() - EPOCH) / 86400000);
 | 
				
			||||||
    const week = Math.trunc(day / 7);
 | 
					    const week = Math.trunc(day / 7);
 | 
				
			||||||
    const weekStart = EPOCH + week * 604800000;
 | 
					    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
    const weekEnd = weekStart + 604800000;
 | 
					    const weekEnd = weekStart + 604800000;
 | 
				
			||||||
@ -52,7 +52,7 @@ export const worldStateController: RequestHandler = (req, res) => {
 | 
				
			|||||||
    worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
 | 
					    worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation
 | 
					    // Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation
 | 
				
			||||||
    const bountyCycle = Math.trunc(Date.now() / 9000000);
 | 
					    const bountyCycle = Math.trunc(new Date().getTime() / 9000000);
 | 
				
			||||||
    const bountyCycleStart = bountyCycle * 9000000;
 | 
					    const bountyCycleStart = bountyCycle * 9000000;
 | 
				
			||||||
    const bountyCycleEnd = bountyCycleStart + 9000000;
 | 
					    const bountyCycleEnd = bountyCycleStart + 9000000;
 | 
				
			||||||
    worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "ZarimanSyndicate")] = {
 | 
					    worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "ZarimanSyndicate")] = {
 | 
				
			||||||
@ -222,7 +222,7 @@ export const worldStateController: RequestHandler = (req, res) => {
 | 
				
			|||||||
    worldState.KnownCalendarSeasons[0].YearIteration = Math.trunc(week / 4);
 | 
					    worldState.KnownCalendarSeasons[0].YearIteration = Math.trunc(week / 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Sentient Anomaly cycling every 30 minutes
 | 
					    // Sentient Anomaly cycling every 30 minutes
 | 
				
			||||||
    const halfHour = Math.trunc(Date.now() / (unixTimesInMs.hour / 2));
 | 
					    const halfHour = Math.trunc(new Date().getTime() / (unixTimesInMs.hour / 2));
 | 
				
			||||||
    const tmp = {
 | 
					    const tmp = {
 | 
				
			||||||
        cavabegin: "1690761600",
 | 
					        cavabegin: "1690761600",
 | 
				
			||||||
        PurchasePlatformLockEnabled: true,
 | 
					        PurchasePlatformLockEnabled: true,
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import { deleteAccountController } from "@/src/controllers/custom/deleteAccountC
 | 
				
			|||||||
import { getNameController } from "@/src/controllers/custom/getNameController";
 | 
					import { getNameController } from "@/src/controllers/custom/getNameController";
 | 
				
			||||||
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
 | 
					import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
 | 
				
			||||||
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
 | 
					import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
 | 
				
			||||||
 | 
					import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
					import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
				
			||||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
 | 
					import { createMessageController } from "@/src/controllers/custom/createMessageController";
 | 
				
			||||||
@ -30,6 +31,7 @@ customRouter.get("/deleteAccount", deleteAccountController);
 | 
				
			|||||||
customRouter.get("/getName", getNameController);
 | 
					customRouter.get("/getName", getNameController);
 | 
				
			||||||
customRouter.get("/renameAccount", renameAccountController);
 | 
					customRouter.get("/renameAccount", renameAccountController);
 | 
				
			||||||
customRouter.get("/ircDropped", ircDroppedController);
 | 
					customRouter.get("/ircDropped", ircDroppedController);
 | 
				
			||||||
 | 
					customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
customRouter.post("/createAccount", createAccountController);
 | 
					customRouter.post("/createAccount", createAccountController);
 | 
				
			||||||
customRouter.post("/createMessage", createMessageController);
 | 
					customRouter.post("/createMessage", createMessageController);
 | 
				
			||||||
 | 
				
			|||||||
@ -562,8 +562,11 @@
 | 
				
			|||||||
                        <div class="card mb-3">
 | 
					                        <div class="card mb-3">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="cheats_account"></h5>
 | 
					                            <h5 class="card-header" data-loc="cheats_account"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <p><button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button></p>
 | 
					                                <div class="mb-2 d-flex flex-wrap gap-2">
 | 
				
			||||||
 | 
					                                    <button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
				
			||||||
 | 
					                                    <button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
                                <form class="mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
 | 
					                                <form class="mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
 | 
				
			||||||
                                    <label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
 | 
				
			||||||
                                    <div class="input-group">
 | 
					                                    <div class="input-group">
 | 
				
			||||||
@ -578,7 +581,6 @@
 | 
				
			|||||||
                                    <button class="btn btn-primary" onclick="doQuestUpdate('completeAllUnlocked');" data-loc="cheats_quests_completeAllUnlocked"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doQuestUpdate('completeAllUnlocked');" data-loc="cheats_quests_completeAllUnlocked"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doQuestUpdate('ResetAll');" data-loc="cheats_quests_resetAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doQuestUpdate('ResetAll');" data-loc="cheats_quests_resetAll"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doQuestUpdate('giveAll');" data-loc="cheats_quests_giveAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doQuestUpdate('giveAll');" data-loc="cheats_quests_giveAll"></button>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -1053,6 +1053,12 @@ function doHelminthUnlockAll() {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function doIntrinsicsUnlockAll() {
 | 
				
			||||||
 | 
					    revalidateAuthz(() => {
 | 
				
			||||||
 | 
					        $.get("/custom/unlockAllIntrinsics?" + window.authz);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function doAddAllMods() {
 | 
					function doAddAllMods() {
 | 
				
			||||||
    let modsAll = new Set();
 | 
					    let modsAll = new Set();
 | 
				
			||||||
    for (const child of document.getElementById("datalist-mods").children) {
 | 
					    for (const child of document.getElementById("datalist-mods").children) {
 | 
				
			||||||
 | 
				
			|||||||
@ -123,6 +123,7 @@ dict = {
 | 
				
			|||||||
    cheats_account: `Account`,
 | 
					    cheats_account: `Account`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Alle Fokus-Schulen freischalten`,
 | 
					    cheats_unlockAllFocusSchools: `Alle Fokus-Schulen freischalten`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
					    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
				
			||||||
 | 
					    cheats_intrinsicsUnlockAll: `[UNTRANSLATED] Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
					    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
				
			||||||
    cheats_changeButton: `Ändern`,
 | 
					    cheats_changeButton: `Ändern`,
 | 
				
			||||||
    cheats_none: `Keines`,
 | 
					    cheats_none: `Keines`,
 | 
				
			||||||
 | 
				
			|||||||
@ -122,6 +122,7 @@ dict = {
 | 
				
			|||||||
    cheats_account: `Account`,
 | 
					    cheats_account: `Account`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Unlock All Focus Schools`,
 | 
					    cheats_unlockAllFocusSchools: `Unlock All Focus Schools`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
					    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
				
			||||||
 | 
					    cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
					    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
				
			||||||
    cheats_changeButton: `Change`,
 | 
					    cheats_changeButton: `Change`,
 | 
				
			||||||
    cheats_none: `None`,
 | 
					    cheats_none: `None`,
 | 
				
			||||||
 | 
				
			|||||||
@ -123,6 +123,7 @@ dict = {
 | 
				
			|||||||
    cheats_account: `Compte`,
 | 
					    cheats_account: `Compte`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
 | 
					    cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
					    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
				
			||||||
 | 
					    cheats_intrinsicsUnlockAll: `[UNTRANSLATED] Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
					    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
				
			||||||
    cheats_changeButton: `Changer`,
 | 
					    cheats_changeButton: `Changer`,
 | 
				
			||||||
    cheats_none: `Aucun`,
 | 
					    cheats_none: `Aucun`,
 | 
				
			||||||
 | 
				
			|||||||
@ -123,6 +123,7 @@ dict = {
 | 
				
			|||||||
    cheats_account: `Аккаунт`,
 | 
					    cheats_account: `Аккаунт`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Разблокировать все школы фокуса`,
 | 
					    cheats_unlockAllFocusSchools: `Разблокировать все школы фокуса`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
					    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
				
			||||||
 | 
					    cheats_intrinsicsUnlockAll: `[UNTRANSLATED] Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
					    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
				
			||||||
    cheats_changeButton: `Изменить`,
 | 
					    cheats_changeButton: `Изменить`,
 | 
				
			||||||
    cheats_none: `Отсутствует`,
 | 
					    cheats_none: `Отсутствует`,
 | 
				
			||||||
 | 
				
			|||||||
@ -123,6 +123,7 @@ dict = {
 | 
				
			|||||||
    cheats_account: `账户`,
 | 
					    cheats_account: `账户`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `解锁所有专精学派`,
 | 
					    cheats_unlockAllFocusSchools: `解锁所有专精学派`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
					    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
				
			||||||
 | 
					    cheats_intrinsicsUnlockAll: `[UNTRANSLATED] Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
					    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
				
			||||||
    cheats_changeButton: `更改`,
 | 
					    cheats_changeButton: `更改`,
 | 
				
			||||||
    cheats_none: `无`,
 | 
					    cheats_none: `无`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user