2025-01-24 14:13:21 +01:00
import {
Inventory ,
InventoryDocumentProps ,
TInventoryDatabaseDocument
} from "@/src/models/inventoryModels/inventoryModel" ;
2024-05-15 21:55:59 +02:00
import { config } from "@/src/services/configService" ;
2025-01-24 14:13:21 +01:00
import { HydratedDocument , Types } from "mongoose" ;
2025-02-26 15:41:07 -08:00
import { SlotNames , IInventoryChanges , IBinChanges , slotNames } from "@/src/types/purchaseTypes" ;
2023-08-31 14:29:09 +04:00
import {
2023-09-05 07:37:30 -05:00
IChallengeProgress ,
IFlavourItem ,
2023-09-06 14:02:54 +04:00
IMiscItem ,
2023-09-10 00:10:21 +04:00
IMission ,
2024-05-09 16:04:31 +02:00
IRawUpgrade ,
2024-06-16 17:51:20 +02:00
ISeasonChallenge ,
2024-06-15 02:50:43 +02:00
ITypeCount ,
2024-06-22 23:19:07 +02:00
InventorySlot ,
2024-06-29 13:55:15 +02:00
IWeaponSkinClient ,
TEquipmentKey ,
2025-01-17 05:27:12 +01:00
IFusionTreasure ,
2025-01-24 14:13:21 +01:00
IDailyAffiliations ,
2025-01-31 17:02:46 +01:00
IInventoryDatabase ,
IKubrowPetEggDatabase ,
2025-02-22 11:10:52 -08:00
IKubrowPetEggClient ,
2025-02-25 17:31:52 -08:00
ILibraryDailyTaskInfo ,
2025-02-24 05:28:43 -08:00
ICalendarProgress ,
2025-02-25 17:31:33 -08:00
IDroneClient ,
2025-03-23 13:33:26 -07:00
IUpgradeClient
2023-09-05 07:37:30 -05:00
} from "@/src/types/inventoryTypes/inventoryTypes" ;
2025-03-23 05:07:15 -07:00
import { IGenericUpdate , IUpdateNodeIntrosResponse } from "../types/genericUpdate" ;
2025-03-24 11:32:01 -07:00
import { IMissionInventoryUpdateRequest } from "../types/requestTypes" ;
2024-01-06 16:26:58 +01:00
import { logger } from "@/src/utils/logger" ;
2025-03-09 07:42:55 -07:00
import { convertInboxMessage , fromStoreItem , getExalted , getKeyChainItems } from "@/src/services/itemDataService" ;
2025-02-28 12:39:51 -08:00
import {
EquipmentFeatures ,
IEquipmentClient ,
IEquipmentDatabase ,
IItemConfig
} from "../types/inventoryTypes/commonInventoryTypes" ;
2024-07-03 12:30:32 +02:00
import {
2024-12-22 16:15:05 +01:00
ExportArcanes ,
2025-02-25 04:38:17 -08:00
ExportBundles ,
2024-07-03 12:30:32 +02:00
ExportCustoms ,
2025-02-24 05:28:43 -08:00
ExportDrones ,
2025-03-02 04:18:59 -08:00
ExportEmailItems ,
2025-02-25 17:31:52 -08:00
ExportEnemies ,
2024-07-03 12:30:32 +02:00
ExportFlavour ,
2025-02-23 03:54:26 -08:00
ExportFusionBundles ,
2024-12-22 05:38:46 +01:00
ExportGear ,
2025-02-19 12:42:21 -08:00
ExportKeys ,
2025-02-24 21:46:20 -08:00
ExportMisc ,
2025-03-11 07:56:18 -07:00
ExportRailjackWeapons ,
2024-07-03 12:30:32 +02:00
ExportRecipes ,
2024-12-22 05:32:30 +01:00
ExportResources ,
2024-12-23 22:44:24 +01:00
ExportSentinels ,
2025-02-01 07:32:56 -08:00
ExportSyndicates ,
2025-01-17 05:27:12 +01:00
ExportUpgrades ,
2025-01-25 06:25:13 +01:00
ExportWeapons ,
2025-02-25 04:41:14 -08:00
IDefaultUpgrade ,
2025-01-17 05:27:12 +01:00
TStandingLimitBin
2024-07-03 12:30:32 +02:00
} from "warframe-public-export-plus" ;
2024-12-23 09:15:41 +01:00
import { createShip } from "./shipService" ;
2025-01-31 17:24:42 +01:00
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController" ;
2025-01-31 17:02:46 +01:00
import { toOid } from "../helpers/inventoryHelpers" ;
2025-02-22 11:10:52 -08:00
import { generateRewardSeed } from "@/src/controllers/api/getNewRewardSeedController" ;
import { addStartingGear } from "@/src/controllers/api/giveStartingGearController" ;
2025-02-24 06:14:47 -08:00
import { addQuestKey , completeQuest } from "@/src/services/questService" ;
2025-02-25 04:38:17 -08:00
import { handleBundleAcqusition } from "./purchaseService" ;
2025-02-25 17:31:52 -08:00
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json" ;
import { getRandomElement , getRandomInt } from "./rngService" ;
2025-03-02 04:18:59 -08:00
import { createMessage } from "./inboxService" ;
2023-06-04 03:06:22 +02:00
2024-02-18 13:58:43 +01:00
export const createInventory = async (
accountOwnerId : Types.ObjectId ,
defaultItemReferences : { loadOutPresetId : Types.ObjectId ; ship : Types.ObjectId }
2024-12-29 21:47:18 +01:00
) : Promise < void > = > {
2023-06-04 03:06:22 +02:00
try {
2025-01-20 18:25:50 +01:00
const inventory = new Inventory ( {
accountOwnerId : accountOwnerId ,
LoadOutPresets : defaultItemReferences.loadOutPresetId ,
Ships : [ defaultItemReferences . ship ] ,
PlayedParkourTutorial : config.skipTutorial ,
ReceivedStartingGear : config.skipTutorial
} ) ;
2025-02-25 17:31:52 -08:00
inventory . LibraryAvailableDailyTaskInfo = createLibraryDailyTask ( ) ;
2025-02-22 11:10:52 -08:00
inventory . CalendarProgress = createCalendar ( ) ;
inventory . RewardSeed = generateRewardSeed ( ) ;
inventory . DuviriInfo = {
Seed : generateRewardSeed ( ) ,
NumCompletions : 0
} ;
await addItem ( inventory , "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords" ) ;
2025-01-20 18:25:50 +01:00
2025-02-22 11:10:52 -08:00
if ( config . skipTutorial ) {
await addStartingGear ( inventory ) ;
await completeQuest ( inventory , "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain" ) ;
2025-01-20 18:25:50 +01:00
const completedMissions = [ "SolNode27" , "SolNode89" , "SolNode63" , "SolNode85" , "SolNode15" , "SolNode79" ] ;
inventory . Missions . push (
. . . completedMissions . map ( tag = > ( {
Completes : 1 ,
Tag : tag
} ) )
) ;
}
2023-06-04 03:06:22 +02:00
await inventory . save ( ) ;
} catch ( error ) {
2025-02-22 11:10:52 -08:00
throw new Error ( ` Error creating inventory: ${ error instanceof Error ? error . message : "Unknown error type" } ` ) ;
2023-06-04 03:06:22 +02:00
}
} ;
2025-01-24 14:13:21 +01:00
/ * *
* Combines two inventory changes objects into one .
*
* @param InventoryChanges - will hold the combined changes
* @param delta - inventory changes to be added
* /
2025-02-22 11:10:52 -08:00
//TODO: this fails silently when providing an incorrect object to delta
2024-07-03 12:30:32 +02:00
export const combineInventoryChanges = ( InventoryChanges : IInventoryChanges , delta : IInventoryChanges ) : void = > {
for ( const key in delta ) {
if ( ! ( key in InventoryChanges ) ) {
InventoryChanges [ key ] = delta [ key ] ;
} else if ( Array . isArray ( delta [ key ] ) ) {
const left = InventoryChanges [ key ] as object [ ] ;
2025-02-28 12:04:43 +01:00
const right : object [ ] = delta [ key ] ;
2024-07-03 12:30:32 +02:00
for ( const item of right ) {
left . push ( item ) ;
}
2025-02-26 15:41:07 -08:00
} else if ( slotNames . indexOf ( key as SlotNames ) != - 1 ) {
const left = InventoryChanges [ key as SlotNames ] ! ;
const right = delta [ key as SlotNames ] ! ;
if ( right . count ) {
left . count ? ? = 0 ;
left . count += right . count ;
}
if ( right . platinum ) {
left . platinum ? ? = 0 ;
left . platinum += right . platinum ;
}
2024-07-03 12:30:32 +02:00
left . Slots += right . Slots ;
if ( right . Extra ) {
left . Extra ? ? = 0 ;
left . Extra += right . Extra ;
}
2025-01-24 14:13:21 +01:00
} else if ( typeof delta [ key ] === "number" ) {
( InventoryChanges [ key ] as number ) += delta [ key ] ;
2024-12-29 21:40:54 +01:00
} else {
2025-01-24 14:13:21 +01:00
throw new Error ( ` inventory change not merged: unhandled type for inventory key ${ key } ` ) ;
2024-07-03 12:30:32 +02:00
}
}
} ;
2025-01-19 01:57:52 +01:00
export const getInventory = async (
accountOwnerId : string ,
projection : string | undefined = undefined
) : Promise < TInventoryDatabaseDocument > = > {
const inventory = await Inventory . findOne ( { accountOwnerId : accountOwnerId } , projection ) ;
2023-06-14 02:26:19 +02:00
if ( ! inventory ) {
throw new Error ( ` Didn't find an inventory for ${ accountOwnerId } ` ) ;
}
return inventory ;
} ;
2025-03-16 04:33:48 -07:00
export const productCategoryToInventoryBin = ( productCategory : string ) : InventorySlot | undefined = > {
switch ( productCategory ) {
case "Suits" :
return InventorySlot . SUITS ;
case "Pistols" :
case "LongGuns" :
case "Melee" :
return InventorySlot . WEAPONS ;
case "Sentinels" :
case "SentinelWeapons" :
case "KubrowPets" :
case "MoaPets" :
return InventorySlot . SENTINELS ;
case "SpaceSuits" :
case "Hoverboards" :
return InventorySlot . SPACESUITS ;
case "SpaceGuns" :
case "SpaceMelee" :
return InventorySlot . SPACEWEAPONS ;
case "OperatorAmps" :
return InventorySlot . AMPS ;
case "CrewShipWeapons" :
case "CrewShipWeaponSkins" :
return InventorySlot . RJ_COMPONENT_AND_ARMAMENTS ;
case "MechSuits" :
return InventorySlot . MECHSUITS ;
case "CrewMembers" :
return InventorySlot . CREWMEMBERS ;
}
return undefined ;
} ;
2025-03-13 04:25:59 -07:00
export const occupySlot = (
2025-02-26 15:41:07 -08:00
inventory : TInventoryDatabaseDocument ,
bin : InventorySlot ,
premiumPurchase : boolean
) : IInventoryChanges = > {
const slotChanges = {
Slots : 0 ,
Extra : 0
} ;
if ( premiumPurchase ) {
slotChanges . Extra += 1 ;
} else {
// { count: 1, platinum: 0, Slots: -1 }
slotChanges . Slots -= 1 ;
}
updateSlots ( inventory , bin , slotChanges . Slots , slotChanges . Extra ) ;
const inventoryChanges : IInventoryChanges = { } ;
inventoryChanges [ bin ] = slotChanges satisfies IBinChanges ;
return inventoryChanges ;
} ;
2025-03-13 04:25:59 -07:00
export const freeUpSlot = ( inventory : TInventoryDatabaseDocument , bin : InventorySlot ) : void = > {
// { count: -1, platinum: 0, Slots: 1 }
updateSlots ( inventory , bin , 1 , 0 ) ;
} ;
2024-06-15 02:50:43 +02:00
export const addItem = async (
2025-01-04 00:25:09 +01:00
inventory : TInventoryDatabaseDocument ,
2024-06-15 02:50:43 +02:00
typeName : string ,
2025-02-26 15:41:07 -08:00
quantity : number = 1 ,
premiumPurchase : boolean = false
2025-03-23 08:26:46 -07:00
) : Promise < IInventoryChanges > = > {
2025-02-25 04:38:17 -08:00
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
if ( typeName in ExportBundles ) {
2025-03-23 08:26:46 -07:00
return await handleBundleAcqusition ( typeName , inventory , quantity ) ;
2025-02-25 04:38:17 -08:00
}
2024-06-17 16:41:02 +02:00
// Strict typing
if ( typeName in ExportRecipes ) {
const recipeChanges = [
{
ItemType : typeName ,
ItemCount : quantity
} satisfies ITypeCount
] ;
addRecipes ( inventory , recipeChanges ) ;
return {
2025-03-23 08:26:46 -07:00
Recipes : recipeChanges
2024-06-17 16:41:02 +02:00
} ;
}
2024-06-20 16:35:24 +02:00
if ( typeName in ExportResources ) {
2025-01-31 17:02:46 +01:00
if ( ExportResources [ typeName ] . productCategory == "MiscItems" ) {
const miscItemChanges = [
{
ItemType : typeName ,
ItemCount : quantity
} satisfies IMiscItem
] ;
addMiscItems ( inventory , miscItemChanges ) ;
return {
2025-03-23 08:26:46 -07:00
MiscItems : miscItemChanges
2025-01-31 17:02:46 +01:00
} ;
2025-02-27 21:54:31 -08:00
} else if ( ExportResources [ typeName ] . productCategory == "FusionTreasures" ) {
const fusionTreasureChanges = [
{
ItemType : typeName ,
ItemCount : quantity ,
Sockets : 0
} satisfies IFusionTreasure
] ;
addFusionTreasures ( inventory , fusionTreasureChanges ) ;
return {
2025-03-23 08:26:46 -07:00
FusionTreasures : fusionTreasureChanges
2025-02-27 21:54:31 -08:00
} ;
2025-01-31 17:02:46 +01:00
} else if ( ExportResources [ typeName ] . productCategory == "Ships" ) {
2025-01-04 00:25:09 +01:00
const oid = await createShip ( inventory . accountOwnerId , typeName ) ;
2024-12-23 09:15:41 +01:00
inventory . Ships . push ( oid ) ;
return {
2025-03-23 08:26:46 -07:00
Ships : [
{
ItemId : { $oid : oid.toString ( ) } ,
ItemType : typeName
}
]
2024-12-23 09:15:41 +01:00
} ;
2024-12-23 22:47:58 +01:00
} else if ( ExportResources [ typeName ] . productCategory == "CrewShips" ) {
2025-03-23 08:26:46 -07:00
return {
2025-02-04 02:29:23 -08:00
. . . addCrewShip ( inventory , typeName ) ,
// fix to unlock railjack modding, item bellow supposed to be obtained from archwing quest
2025-02-24 20:56:34 -08:00
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2025-02-04 02:29:23 -08:00
. . . ( ! inventory . CrewShipHarnesses ? . length
? addCrewShipHarness ( inventory , "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness" )
: { } )
} ;
2025-01-11 12:54:11 +01:00
} else if ( ExportResources [ typeName ] . productCategory == "ShipDecorations" ) {
const changes = [
{
ItemType : typeName ,
ItemCount : quantity
} satisfies IMiscItem
] ;
addShipDecorations ( inventory , changes ) ;
return {
2025-03-23 08:26:46 -07:00
ShipDecorations : changes
2025-01-11 12:54:11 +01:00
} ;
2025-01-31 17:02:46 +01:00
} else if ( ExportResources [ typeName ] . productCategory == "KubrowPetEggs" ) {
const changes : IKubrowPetEggClient [ ] = [ ] ;
2025-03-08 04:33:33 -08:00
if ( quantity < 0 ) {
throw new Error ( ` removal of KubrowPetEggs not handled ` ) ;
}
2025-01-31 17:02:46 +01:00
for ( let i = 0 ; i != quantity ; ++ i ) {
const egg : IKubrowPetEggDatabase = {
ItemType : "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg" ,
_id : new Types . ObjectId ( )
} ;
inventory . KubrowPetEggs ? ? = [ ] ;
inventory . KubrowPetEggs . push ( egg ) ;
changes . push ( {
ItemType : egg.ItemType ,
ExpirationDate : { $date : { $numberLong : "2000000000000" } } ,
ItemId : toOid ( egg . _id )
} ) ;
}
2024-12-23 09:15:41 +01:00
return {
2025-03-23 08:26:46 -07:00
KubrowPetEggs : changes
2024-12-23 09:15:41 +01:00
} ;
2025-02-27 21:54:31 -08:00
} else {
throw new Error ( ` unknown product category: ${ ExportResources [ typeName ] . productCategory } ` ) ;
2024-12-23 09:15:41 +01:00
}
2024-06-20 16:35:24 +02:00
}
2024-06-22 23:19:07 +02:00
if ( typeName in ExportCustoms ) {
2025-03-23 13:09:02 -07:00
const meta = ExportCustoms [ typeName ] ;
let inventoryChanges : IInventoryChanges ;
if ( meta . productCategory == "CrewShipWeaponSkins" ) {
inventoryChanges = addCrewShipWeaponSkin ( inventory , typeName ) ;
2025-02-25 17:31:33 -08:00
} else {
2025-03-23 13:09:02 -07:00
inventoryChanges = addSkin ( inventory , typeName ) ;
2025-02-25 17:31:33 -08:00
}
2025-03-23 13:09:02 -07:00
if ( meta . additionalItems ) {
for ( const item of meta . additionalItems ) {
combineInventoryChanges ( inventoryChanges , await addItem ( inventory , item ) ) ;
}
}
return inventoryChanges ;
2024-06-22 23:19:07 +02:00
}
2024-06-24 12:30:32 +02:00
if ( typeName in ExportFlavour ) {
2025-03-23 08:26:46 -07:00
return addCustomization ( inventory , typeName ) ;
2024-06-24 12:30:32 +02:00
}
2024-12-22 16:15:05 +01:00
if ( typeName in ExportUpgrades || typeName in ExportArcanes ) {
2024-12-22 05:32:30 +01:00
const changes = [
{
ItemType : typeName ,
ItemCount : quantity
}
] ;
addMods ( inventory , changes ) ;
return {
2025-03-23 08:26:46 -07:00
RawUpgrades : changes
2024-12-22 05:32:30 +01:00
} ;
}
2024-12-22 05:38:46 +01:00
if ( typeName in ExportGear ) {
const consumablesChanges = [
{
ItemType : typeName ,
ItemCount : quantity
2025-03-17 05:10:28 -07:00
} satisfies ITypeCount
2024-12-22 05:38:46 +01:00
] ;
addConsumables ( inventory , consumablesChanges ) ;
return {
2025-03-23 08:26:46 -07:00
Consumables : consumablesChanges
2024-12-22 05:38:46 +01:00
} ;
}
2025-01-25 06:25:13 +01:00
if ( typeName in ExportWeapons ) {
const weapon = ExportWeapons [ typeName ] ;
if ( weapon . totalDamage != 0 ) {
2025-03-20 05:37:53 -07:00
const defaultOverwrites : Partial < IEquipmentDatabase > = { } ;
if ( premiumPurchase ) {
defaultOverwrites . Features = EquipmentFeatures . DOUBLE_CAPACITY ;
}
if (
2025-03-26 05:09:48 -07:00
weapon . defaultUpgrades ? . [ 0 ] ? . ItemType ==
"/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod"
2025-03-20 05:37:53 -07:00
) {
defaultOverwrites . UpgradeType = "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod" ;
defaultOverwrites . UpgradeFingerprint = JSON . stringify ( {
compat : typeName ,
buffs : [
{
Tag : getRandomElement ( [
"InnateElectricityDamage" ,
"InnateFreezeDamage" ,
"InnateHeatDamage" ,
"InnateImpactDamage" ,
"InnateMagDamage" ,
"InnateRadDamage" ,
"InnateToxinDamage"
] ) ,
Value : Math.trunc ( Math . random ( ) * 0x40000000 )
}
]
} ) ;
}
2025-02-28 12:39:51 -08:00
const inventoryChanges = addEquipment (
inventory ,
weapon . productCategory ,
typeName ,
[ ] ,
{ } ,
2025-03-20 05:37:53 -07:00
defaultOverwrites
2025-02-28 12:39:51 -08:00
) ;
2025-02-25 04:39:59 -08:00
if ( weapon . additionalItems ) {
for ( const item of weapon . additionalItems ) {
2025-03-23 08:26:46 -07:00
combineInventoryChanges ( inventoryChanges , await addItem ( inventory , item , 1 ) ) ;
2025-02-25 04:39:59 -08:00
}
}
2025-01-25 06:25:13 +01:00
return {
2025-03-23 08:26:46 -07:00
. . . inventoryChanges ,
. . . occupySlot ( inventory , InventorySlot . WEAPONS , premiumPurchase )
2025-01-25 06:25:13 +01:00
} ;
2025-02-05 09:00:20 -08:00
} else {
// Modular weapon parts
const miscItemChanges = [
{
ItemType : typeName ,
ItemCount : quantity
} satisfies IMiscItem
] ;
addMiscItems ( inventory , miscItemChanges ) ;
return {
2025-03-23 08:26:46 -07:00
MiscItems : miscItemChanges
2025-02-05 09:00:20 -08:00
} ;
2025-01-25 06:25:13 +01:00
}
}
2025-03-11 07:56:18 -07:00
if ( typeName in ExportRailjackWeapons ) {
return {
2025-03-23 13:33:26 -07:00
. . . addEquipment ( inventory , ExportRailjackWeapons [ typeName ] . productCategory , typeName ) ,
2025-03-23 08:26:46 -07:00
. . . occupySlot ( inventory , InventorySlot . RJ_COMPONENT_AND_ARMAMENTS , premiumPurchase )
2025-03-11 07:56:18 -07:00
} ;
}
2025-02-24 21:46:20 -08:00
if ( typeName in ExportMisc . creditBundles ) {
const creditsTotal = ExportMisc . creditBundles [ typeName ] * quantity ;
2025-01-24 14:13:21 +01:00
inventory . RegularCredits += creditsTotal ;
return {
2025-03-23 08:26:46 -07:00
RegularCredits : creditsTotal
2025-01-24 14:13:21 +01:00
} ;
}
2025-02-23 03:54:26 -08:00
if ( typeName in ExportFusionBundles ) {
const fusionPointsTotal = ExportFusionBundles [ typeName ] . fusionPoints * quantity ;
2025-01-24 14:13:21 +01:00
inventory . FusionPoints += fusionPointsTotal ;
return {
2025-03-23 08:26:46 -07:00
FusionPoints : fusionPointsTotal
2025-01-24 14:13:21 +01:00
} ;
}
2025-02-19 12:42:21 -08:00
if ( typeName in ExportKeys ) {
2025-02-19 14:09:47 -08:00
// Note: "/Lotus/Types/Keys/" contains some EmailItems
2025-02-24 06:14:47 -08:00
const key = ExportKeys [ typeName ] ;
if ( key . chainStages ) {
const key = addQuestKey ( inventory , { ItemType : typeName } ) ;
2025-03-23 08:26:46 -07:00
if ( ! key ) return { } ;
return { QuestKeys : [ key ] } ;
2025-02-24 06:14:47 -08:00
} else {
const key = { ItemType : typeName , ItemCount : quantity } ;
const index = inventory . LevelKeys . findIndex ( levelKey = > levelKey . ItemType == typeName ) ;
2025-03-10 09:15:11 +01:00
if ( index != - 1 ) {
2025-02-24 06:14:47 -08:00
inventory . LevelKeys [ index ] . ItemCount += quantity ;
} else {
inventory . LevelKeys . push ( key ) ;
}
2025-03-23 08:26:46 -07:00
return { LevelKeys : [ key ] } ;
2025-02-24 06:14:47 -08:00
}
2025-02-19 12:42:21 -08:00
}
2025-02-24 05:28:43 -08:00
if ( typeName in ExportDrones ) {
2025-03-23 08:26:46 -07:00
return addDrone ( inventory , typeName ) ;
2025-02-24 05:28:43 -08:00
}
2025-03-02 04:18:59 -08:00
if ( typeName in ExportEmailItems ) {
2025-03-23 08:26:46 -07:00
return await addEmailItem ( inventory , typeName ) ;
2025-03-02 04:18:59 -08:00
}
2024-06-17 16:41:02 +02:00
// Path-based duck typing
2024-06-15 02:50:43 +02:00
switch ( typeName . substr ( 1 ) . split ( "/" ) [ 1 ] ) {
case "Powersuits" :
2024-06-19 17:46:12 +02:00
switch ( typeName . substr ( 1 ) . split ( "/" ) [ 2 ] ) {
default : {
return {
2025-03-23 08:26:46 -07:00
. . . addPowerSuit (
inventory ,
typeName ,
{ } ,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
) ,
. . . occupySlot ( inventory , InventorySlot . SUITS , premiumPurchase )
2024-06-19 17:46:12 +02:00
} ;
2024-06-15 02:50:43 +02:00
}
2024-06-19 17:46:12 +02:00
case "Archwing" : {
2025-03-08 03:36:52 -08:00
inventory . ArchwingEnabled = true ;
2024-06-19 17:46:12 +02:00
return {
2025-03-23 08:26:46 -07:00
. . . addSpaceSuit (
inventory ,
typeName ,
{ } ,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
) ,
. . . occupySlot ( inventory , InventorySlot . SPACESUITS , premiumPurchase )
2024-06-19 17:46:12 +02:00
} ;
}
case "EntratiMech" : {
return {
2025-03-23 08:26:46 -07:00
. . . addMechSuit (
inventory ,
typeName ,
{ } ,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
) ,
. . . occupySlot ( inventory , InventorySlot . MECHSUITS , premiumPurchase )
2024-06-19 17:46:12 +02:00
} ;
}
}
break ;
2025-01-24 16:12:39 +01:00
case "Upgrades" : {
2025-02-12 18:15:22 -08:00
switch ( typeName . substr ( 1 ) . split ( "/" ) [ 2 ] ) {
case "Mods" : // Legendary Core
case "CosmeticEnhancers" : // Traumatic Peculiar
2025-02-24 20:56:27 -08:00
{
const changes = [
{
ItemType : typeName ,
ItemCount : quantity
}
] ;
addMods ( inventory , changes ) ;
return {
2025-03-23 08:26:46 -07:00
RawUpgrades : changes
2025-02-24 20:56:27 -08:00
} ;
}
break ;
2025-03-23 05:07:15 -07:00
case "Stickers" :
{
const entry = inventory . RawUpgrades . find ( x = > x . ItemType == typeName ) ;
if ( entry && entry . ItemCount >= 10 ) {
const miscItemChanges = [
{
ItemType : "/Lotus/Types/Items/MiscItems/1999ConquestBucks" ,
ItemCount : 1
}
] ;
addMiscItems ( inventory , miscItemChanges ) ;
return {
2025-03-23 08:26:46 -07:00
MiscItems : miscItemChanges
2025-03-23 05:07:15 -07:00
} ;
} else {
const changes = [
{
ItemType : typeName ,
ItemCount : quantity
}
] ;
addMods ( inventory , changes ) ;
return {
2025-03-23 08:26:46 -07:00
RawUpgrades : changes
2025-03-23 05:07:15 -07:00
} ;
}
}
break ;
2025-02-07 02:07:18 -08:00
}
break ;
2025-01-24 16:12:39 +01:00
}
2024-06-15 02:50:43 +02:00
case "Types" :
switch ( typeName . substr ( 1 ) . split ( "/" ) [ 2 ] ) {
2024-12-23 22:44:24 +01:00
case "Sentinels" : {
2025-03-23 08:26:46 -07:00
return addSentinel ( inventory , typeName , premiumPurchase ) ;
2024-12-23 22:44:24 +01:00
}
2025-01-24 14:13:21 +01:00
case "Game" : {
2024-07-03 12:30:32 +02:00
if ( typeName . substr ( 1 ) . split ( "/" ) [ 3 ] == "Projections" ) {
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
const miscItemChanges = [
{
ItemType : typeName ,
ItemCount : quantity
} satisfies IMiscItem
] ;
addMiscItems ( inventory , miscItemChanges ) ;
2025-03-15 10:25:32 -07:00
inventory . HasOwnedVoidProjectionsPreviously = true ;
2024-07-03 12:30:32 +02:00
return {
2025-03-23 08:26:46 -07:00
MiscItems : miscItemChanges
2024-07-03 12:30:32 +02:00
} ;
}
break ;
2025-01-24 14:13:21 +01:00
}
case "NeutralCreatures" : {
const horseIndex = inventory . Horses . push ( { ItemType : typeName } ) ;
return {
2025-03-23 08:26:46 -07:00
Horses : [ inventory . Horses [ horseIndex - 1 ] . toJSON < IEquipmentClient > ( ) ]
2025-01-24 14:13:21 +01:00
} ;
}
case "Recipes" : {
inventory . MiscItems . push ( { ItemType : typeName , ItemCount : quantity } ) ;
return {
2025-03-23 08:26:46 -07:00
MiscItems : [
{
ItemType : typeName ,
ItemCount : quantity
}
]
2025-01-24 14:13:21 +01:00
} ;
}
2025-02-19 13:53:21 -08:00
case "Vehicles" :
if ( typeName == "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit" ) {
2025-03-23 08:26:46 -07:00
return addMotorcycle ( inventory , typeName ) ;
2025-02-19 13:53:21 -08:00
}
break ;
2024-06-15 02:50:43 +02:00
}
break ;
}
2025-02-19 14:09:47 -08:00
throw new Error ( ` unable to add item: ${ typeName } ` ) ;
2024-06-15 02:50:43 +02:00
} ;
2025-01-31 14:15:36 +01:00
export const addItems = async (
inventory : TInventoryDatabaseDocument ,
2025-01-31 17:24:42 +01:00
items : ITypeCount [ ] | string [ ] ,
2025-01-31 14:15:36 +01:00
inventoryChanges : IInventoryChanges = { }
) : Promise < IInventoryChanges > = > {
2025-01-31 17:24:42 +01:00
let inventoryDelta ;
2025-01-31 14:15:36 +01:00
for ( const item of items ) {
2025-01-31 17:24:42 +01:00
if ( typeof item === "string" ) {
2025-02-26 15:41:07 -08:00
inventoryDelta = await addItem ( inventory , item , 1 , true ) ;
2025-01-31 17:24:42 +01:00
} else {
2025-02-26 15:41:07 -08:00
inventoryDelta = await addItem ( inventory , item . ItemType , item . ItemCount , true ) ;
2025-01-31 17:24:42 +01:00
}
2025-03-23 08:26:46 -07:00
combineInventoryChanges ( inventoryChanges , inventoryDelta ) ;
2025-01-31 14:15:36 +01:00
}
return inventoryChanges ;
} ;
2025-02-25 04:41:14 -08:00
export const applyDefaultUpgrades = (
2025-01-04 00:25:09 +01:00
inventory : TInventoryDatabaseDocument ,
2025-02-25 04:41:14 -08:00
defaultUpgrades : IDefaultUpgrade [ ] | undefined
) : IItemConfig [ ] = > {
2024-12-23 22:44:24 +01:00
const modsToGive : IRawUpgrade [ ] = [ ] ;
const configs : IItemConfig [ ] = [ ] ;
2025-02-25 04:41:14 -08:00
if ( defaultUpgrades ) {
2024-12-23 22:44:24 +01:00
const upgrades = [ ] ;
2025-02-25 04:41:14 -08:00
for ( const defaultUpgrade of defaultUpgrades ) {
2024-12-23 22:44:24 +01:00
modsToGive . push ( { ItemType : defaultUpgrade.ItemType , ItemCount : 1 } ) ;
if ( defaultUpgrade . Slot != - 1 ) {
2025-02-25 17:31:16 -08:00
while ( upgrades . length < defaultUpgrade . Slot ) {
upgrades . push ( "" ) ;
}
2024-12-23 22:44:24 +01:00
upgrades [ defaultUpgrade . Slot ] = defaultUpgrade . ItemType ;
}
}
if ( upgrades . length != 0 ) {
configs . push ( { Upgrades : upgrades } ) ;
}
}
addMods ( inventory , modsToGive ) ;
2025-02-25 04:41:14 -08:00
return configs ;
} ;
//TODO: maybe genericMethod for all the add methods, they share a lot of logic
2025-03-11 13:00:12 -07:00
const addSentinel = (
2025-02-25 04:41:14 -08:00
inventory : TInventoryDatabaseDocument ,
sentinelName : string ,
2025-03-11 13:00:12 -07:00
premiumPurchase : boolean ,
inventoryChanges : IInventoryChanges = { }
2025-02-25 04:41:14 -08:00
) : IInventoryChanges = > {
2025-03-11 13:00:12 -07:00
// Sentinel itself occupies a slot in the sentinels bin
combineInventoryChanges ( inventoryChanges , occupySlot ( inventory , InventorySlot . SENTINELS , premiumPurchase ) ) ;
2025-02-25 04:41:14 -08:00
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if ( ExportSentinels [ sentinelName ] ? . defaultWeapon ) {
2025-03-11 13:00:12 -07:00
addSentinelWeapon ( inventory , ExportSentinels [ sentinelName ] . defaultWeapon , premiumPurchase , inventoryChanges ) ;
2025-02-25 04:41:14 -08:00
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const configs : IItemConfig [ ] = applyDefaultUpgrades ( inventory , ExportSentinels [ sentinelName ] ? . defaultUpgrades ) ;
2025-03-11 13:00:12 -07:00
const features = premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined ;
2025-02-28 12:39:51 -08:00
const sentinelIndex =
inventory . Sentinels . push ( { ItemType : sentinelName , Configs : configs , XP : 0 , Features : features } ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . Sentinels ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . Sentinels . push ( inventory . Sentinels [ sentinelIndex ] . toJSON < IEquipmentClient > ( ) ) ;
2024-12-23 22:44:24 +01:00
return inventoryChanges ;
} ;
2025-03-11 13:00:12 -07:00
const addSentinelWeapon = (
2025-01-04 00:25:09 +01:00
inventory : TInventoryDatabaseDocument ,
typeName : string ,
2025-03-11 13:00:12 -07:00
premiumPurchase : boolean ,
2025-01-04 00:25:09 +01:00
inventoryChanges : IInventoryChanges
) : void = > {
2025-03-11 13:00:12 -07:00
// Sentinel weapons also occupy a slot in the sentinels bin
combineInventoryChanges ( inventoryChanges , occupySlot ( inventory , InventorySlot . SENTINELS , premiumPurchase ) ) ;
2025-01-19 15:03:34 +01:00
const index = inventory . SentinelWeapons . push ( { ItemType : typeName , XP : 0 } ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . SentinelWeapons ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . SentinelWeapons . push ( inventory . SentinelWeapons [ index ] . toJSON < IEquipmentClient > ( ) ) ;
2023-12-14 17:34:15 +01:00
} ;
2025-01-04 00:25:09 +01:00
export const addPowerSuit = (
inventory : TInventoryDatabaseDocument ,
powersuitName : string ,
2025-02-28 12:39:51 -08:00
inventoryChanges : IInventoryChanges = { } ,
features : number | undefined = undefined
2025-01-04 00:25:09 +01:00
) : IInventoryChanges = > {
2024-06-07 15:58:20 +02:00
const specialItems = getExalted ( powersuitName ) ;
2024-10-17 22:12:28 +02:00
if ( specialItems ) {
2025-01-04 00:25:09 +01:00
for ( const specialItem of specialItems ) {
addSpecialItem ( inventory , specialItem , inventoryChanges ) ;
2024-06-07 15:58:20 +02:00
}
}
2025-02-28 12:39:51 -08:00
const suitIndex =
inventory . Suits . push ( { ItemType : powersuitName , Configs : [ ] , UpgradeVer : 101 , XP : 0 , Features : features } ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . Suits ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . Suits . push ( inventory . Suits [ suitIndex ] . toJSON < IEquipmentClient > ( ) ) ;
2024-12-29 03:42:22 +01:00
return inventoryChanges ;
2023-06-14 02:26:19 +02:00
} ;
2025-01-04 00:25:09 +01:00
export const addMechSuit = (
inventory : TInventoryDatabaseDocument ,
mechsuitName : string ,
2025-02-28 12:39:51 -08:00
inventoryChanges : IInventoryChanges = { } ,
features : number | undefined = undefined
2025-01-04 00:25:09 +01:00
) : IInventoryChanges = > {
2024-06-07 15:58:20 +02:00
const specialItems = getExalted ( mechsuitName ) ;
2024-10-17 22:12:28 +02:00
if ( specialItems ) {
2025-01-04 00:25:09 +01:00
for ( const specialItem of specialItems ) {
addSpecialItem ( inventory , specialItem , inventoryChanges ) ;
2024-06-07 15:58:20 +02:00
}
}
2025-02-28 12:39:51 -08:00
const suitIndex =
inventory . MechSuits . push ( { ItemType : mechsuitName , Configs : [ ] , UpgradeVer : 101 , XP : 0 , Features : features } ) -
1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . MechSuits ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . MechSuits . push ( inventory . MechSuits [ suitIndex ] . toJSON < IEquipmentClient > ( ) ) ;
2024-12-29 03:42:22 +01:00
return inventoryChanges ;
2023-12-14 17:34:15 +01:00
} ;
2025-01-04 00:25:09 +01:00
export const addSpecialItem = (
inventory : TInventoryDatabaseDocument ,
2024-12-29 03:42:22 +01:00
itemName : string ,
inventoryChanges : IInventoryChanges
2025-01-04 00:25:09 +01:00
) : void = > {
2024-12-29 02:46:57 +01:00
if ( inventory . SpecialItems . find ( x = > x . ItemType == itemName ) ) {
return ;
}
2025-01-04 00:25:09 +01:00
const specialItemIndex =
inventory . SpecialItems . push ( {
ItemType : itemName ,
Configs : [ ] ,
Features : 1 ,
UpgradeVer : 101 ,
XP : 0
} ) - 1 ;
2024-12-29 03:42:22 +01:00
inventoryChanges . SpecialItems ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . SpecialItems . push ( inventory . SpecialItems [ specialItemIndex ] . toJSON < IEquipmentClient > ( ) ) ;
2024-06-07 15:58:20 +02:00
} ;
2025-01-04 00:25:09 +01:00
export const addSpaceSuit = (
inventory : TInventoryDatabaseDocument ,
spacesuitName : string ,
2025-02-28 12:39:51 -08:00
inventoryChanges : IInventoryChanges = { } ,
features : number | undefined = undefined
2025-01-04 00:25:09 +01:00
) : IInventoryChanges = > {
2025-02-28 12:39:51 -08:00
const suitIndex =
inventory . SpaceSuits . push ( {
ItemType : spacesuitName ,
Configs : [ ] ,
UpgradeVer : 101 ,
XP : 0 ,
Features : features
} ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . SpaceSuits ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . SpaceSuits . push ( inventory . SpaceSuits [ suitIndex ] . toJSON < IEquipmentClient > ( ) ) ;
2025-01-04 00:25:09 +01:00
return inventoryChanges ;
2024-06-19 17:46:12 +02:00
} ;
2025-01-04 00:25:09 +01:00
export const updateSlots = (
inventory : TInventoryDatabaseDocument ,
2024-12-29 21:47:18 +01:00
slotName : SlotNames ,
slotAmount : number ,
extraAmount : number
2025-01-04 00:25:09 +01:00
) : void = > {
2023-12-28 16:24:52 +01:00
inventory [ slotName ] . Slots += slotAmount ;
2025-02-26 06:00:40 +01:00
if ( extraAmount != 0 ) {
inventory [ slotName ] . Extra ? ? = 0 ;
2023-12-28 16:24:52 +01:00
inventory [ slotName ] . Extra += extraAmount ;
2023-06-14 02:26:19 +02:00
}
} ;
2024-12-31 01:41:29 +01:00
const isCurrencyTracked = ( usePremium : boolean ) : boolean = > {
return usePremium ? ! config . infinitePlatinum : ! config . infiniteCredits ;
} ;
export const updateCurrency = (
2025-01-03 22:17:34 +01:00
inventory : TInventoryDatabaseDocument ,
2024-12-31 01:41:29 +01:00
price : number ,
usePremium : boolean
2025-02-26 15:41:07 -08:00
) : IInventoryChanges = > {
const currencyChanges : IInventoryChanges = { } ;
2024-12-31 01:41:29 +01:00
if ( price != 0 && isCurrencyTracked ( usePremium ) ) {
if ( usePremium ) {
if ( inventory . PremiumCreditsFree > 0 ) {
currencyChanges . PremiumCreditsFree = Math . min ( price , inventory . PremiumCreditsFree ) * - 1 ;
inventory . PremiumCreditsFree += currencyChanges . PremiumCreditsFree ;
}
currencyChanges . PremiumCredits = - price ;
inventory . PremiumCredits += currencyChanges . PremiumCredits ;
} else {
currencyChanges . RegularCredits = - price ;
inventory . RegularCredits += currencyChanges . RegularCredits ;
}
logger . debug ( ` currency changes ` , currencyChanges ) ;
}
return currencyChanges ;
} ;
2025-01-17 05:27:12 +01:00
const standingLimitBinToInventoryKey : Record <
Exclude < TStandingLimitBin , "STANDING_LIMIT_BIN_NONE" > ,
keyof IDailyAffiliations
> = {
STANDING_LIMIT_BIN_NORMAL : "DailyAffiliation" ,
STANDING_LIMIT_BIN_PVP : "DailyAffiliationPvp" ,
STANDING_LIMIT_BIN_LIBRARY : "DailyAffiliationLibrary" ,
STANDING_LIMIT_BIN_CETUS : "DailyAffiliationCetus" ,
STANDING_LIMIT_BIN_QUILLS : "DailyAffiliationQuills" ,
STANDING_LIMIT_BIN_SOLARIS : "DailyAffiliationSolaris" ,
STANDING_LIMIT_BIN_VENTKIDS : "DailyAffiliationVentkids" ,
STANDING_LIMIT_BIN_VOX : "DailyAffiliationVox" ,
STANDING_LIMIT_BIN_ENTRATI : "DailyAffiliationEntrati" ,
STANDING_LIMIT_BIN_NECRALOID : "DailyAffiliationNecraloid" ,
STANDING_LIMIT_BIN_ZARIMAN : "DailyAffiliationZariman" ,
STANDING_LIMIT_BIN_KAHL : "DailyAffiliationKahl" ,
STANDING_LIMIT_BIN_CAVIA : "DailyAffiliationCavia" ,
STANDING_LIMIT_BIN_HEX : "DailyAffiliationHex"
} ;
2025-01-17 07:25:15 +01:00
export const allDailyAffiliationKeys : ( keyof IDailyAffiliations ) [ ] = Object . values ( standingLimitBinToInventoryKey ) ;
2025-01-17 07:02:19 +01:00
2025-01-17 05:27:12 +01:00
export const getStandingLimit = ( inventory : IDailyAffiliations , bin : TStandingLimitBin ) : number = > {
2025-01-17 07:02:19 +01:00
if ( bin == "STANDING_LIMIT_BIN_NONE" || config . noDailyStandingLimits ) {
2025-01-17 05:27:12 +01:00
return Number . MAX_SAFE_INTEGER ;
}
return inventory [ standingLimitBinToInventoryKey [ bin ] ] ;
} ;
export const updateStandingLimit = (
inventory : IDailyAffiliations ,
bin : TStandingLimitBin ,
subtrahend : number
) : void = > {
2025-01-17 07:02:19 +01:00
if ( bin != "STANDING_LIMIT_BIN_NONE" && ! config . noDailyStandingLimits ) {
2025-01-17 05:27:12 +01:00
inventory [ standingLimitBinToInventoryKey [ bin ] ] -= subtrahend ;
}
} ;
2023-09-05 07:37:30 -05:00
// TODO: AffiliationMods support (Nightwave).
2025-03-23 05:07:15 -07:00
export const updateGeneric = async ( data : IGenericUpdate , accountId : string ) : Promise < IUpdateNodeIntrosResponse > = > {
const inventory = await getInventory ( accountId , "NodeIntrosCompleted MiscItems" ) ;
2023-09-05 07:37:30 -05:00
// Make it an array for easier parsing.
if ( typeof data . NodeIntrosCompleted === "string" ) {
data . NodeIntrosCompleted = [ data . NodeIntrosCompleted ] ;
}
2025-03-23 05:07:15 -07:00
const inventoryChanges : IInventoryChanges = { } ;
for ( const node of data . NodeIntrosCompleted ) {
if ( node == "KayaFirstVisitPack" ) {
inventoryChanges . MiscItems = [
{
ItemType : "/Lotus/Types/Items/MiscItems/1999FixedStickersPack" ,
ItemCount : 1
}
] ;
addMiscItems ( inventory , inventoryChanges . MiscItems ) ;
}
}
2023-09-05 07:37:30 -05:00
// Combine the two arrays into one.
data . NodeIntrosCompleted = inventory . NodeIntrosCompleted . concat ( data . NodeIntrosCompleted ) ;
// Remove duplicate entries.
const nodes = [ . . . new Set ( data . NodeIntrosCompleted ) ] ;
inventory . NodeIntrosCompleted = nodes ;
await inventory . save ( ) ;
2025-03-23 05:07:15 -07:00
return {
MissionRewards : [ ] ,
InventoryChanges : inventoryChanges
} ;
2023-09-05 07:37:30 -05:00
} ;
2025-01-04 00:25:09 +01:00
export const addEquipment = (
inventory : TInventoryDatabaseDocument ,
2024-06-29 15:11:12 +02:00
category : TEquipmentKey ,
type : string ,
2025-01-04 00:25:09 +01:00
modularParts : string [ ] | undefined = undefined ,
2025-02-22 11:10:52 -08:00
inventoryChanges : IInventoryChanges = { } ,
defaultOverwrites : Partial < IEquipmentDatabase > | undefined = undefined
2025-01-04 00:25:09 +01:00
) : IInventoryChanges = > {
2025-02-22 11:10:52 -08:00
const equipment = Object . assign (
{
2025-01-04 00:25:09 +01:00
ItemType : type ,
Configs : [ ] ,
XP : 0 ,
2025-03-27 12:27:38 -07:00
ModularParts : modularParts ,
IsNew : true
2025-02-22 11:10:52 -08:00
} ,
defaultOverwrites
) ;
const index = inventory [ category ] . push ( equipment ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges [ category ] ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges [ category ] . push ( inventory [ category ] [ index ] . toJSON < IEquipmentClient > ( ) ) ;
2025-01-04 00:25:09 +01:00
return inventoryChanges ;
2023-06-14 02:26:19 +02:00
} ;
2025-01-04 00:25:09 +01:00
export const addCustomization = (
inventory : TInventoryDatabaseDocument ,
customizationName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
const flavourItemIndex = inventory . FlavourItems . push ( { ItemType : customizationName } ) - 1 ;
2025-03-11 07:56:18 -07:00
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2025-01-04 00:25:09 +01:00
inventoryChanges . FlavourItems ? ? = [ ] ;
( inventoryChanges . FlavourItems as IFlavourItem [ ] ) . push (
inventory . FlavourItems [ flavourItemIndex ] . toJSON < IFlavourItem > ( )
) ;
return inventoryChanges ;
2023-06-14 02:26:19 +02:00
} ;
2025-01-04 00:25:09 +01:00
export const addSkin = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
2024-06-22 23:19:07 +02:00
const index = inventory . WeaponSkins . push ( { ItemType : typeName } ) - 1 ;
2025-03-11 07:56:18 -07:00
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2025-01-04 00:25:09 +01:00
inventoryChanges . WeaponSkins ? ? = [ ] ;
( inventoryChanges . WeaponSkins as IWeaponSkinClient [ ] ) . push (
inventory . WeaponSkins [ index ] . toJSON < IWeaponSkinClient > ( )
) ;
return inventoryChanges ;
2024-06-22 23:19:07 +02:00
} ;
2025-02-25 17:31:33 -08:00
const addCrewShipWeaponSkin = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
const index = inventory . CrewShipWeaponSkins . push ( { ItemType : typeName } ) - 1 ;
2025-03-11 07:56:18 -07:00
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2025-02-25 17:31:33 -08:00
inventoryChanges . CrewShipWeaponSkins ? ? = [ ] ;
( inventoryChanges . CrewShipWeaponSkins as IUpgradeClient [ ] ) . push (
inventory . CrewShipWeaponSkins [ index ] . toJSON < IUpgradeClient > ( )
) ;
return inventoryChanges ;
} ;
2025-01-04 00:25:09 +01:00
const addCrewShip = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
2025-02-19 13:42:36 -08:00
if ( inventory . CrewShips . length != 0 ) {
throw new Error ( "refusing to add CrewShip because account already has one" ) ;
}
2024-12-23 22:47:58 +01:00
const index = inventory . CrewShips . push ( { ItemType : typeName } ) - 1 ;
2025-01-04 00:25:09 +01:00
inventoryChanges . CrewShips ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . CrewShips . push ( inventory . CrewShips [ index ] . toJSON < IEquipmentClient > ( ) ) ;
2025-01-04 00:25:09 +01:00
return inventoryChanges ;
2024-12-23 22:47:58 +01:00
} ;
2025-02-04 02:29:23 -08:00
const addCrewShipHarness = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
2025-02-28 03:51:08 +01:00
if ( inventory . CrewShipHarnesses . length != 0 ) {
2025-02-19 13:42:36 -08:00
throw new Error ( "refusing to add CrewShipHarness because account already has one" ) ;
}
2025-02-04 02:29:23 -08:00
const index = inventory . CrewShipHarnesses . push ( { ItemType : typeName } ) - 1 ;
inventoryChanges . CrewShipHarnesses ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . CrewShipHarnesses . push ( inventory . CrewShipHarnesses [ index ] . toJSON < IEquipmentClient > ( ) ) ;
2025-02-04 02:29:23 -08:00
return inventoryChanges ;
} ;
2025-02-19 13:53:21 -08:00
const addMotorcycle = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
if ( inventory . Motorcycles . length != 0 ) {
throw new Error ( "refusing to add Motorcycle because account already has one" ) ;
}
const index = inventory . Motorcycles . push ( { ItemType : typeName } ) - 1 ;
inventoryChanges . Motorcycles ? ? = [ ] ;
2025-02-20 02:58:44 -08:00
inventoryChanges . Motorcycles . push ( inventory . Motorcycles [ index ] . toJSON < IEquipmentClient > ( ) ) ;
2025-02-19 13:53:21 -08:00
return inventoryChanges ;
} ;
2025-02-24 05:28:43 -08:00
const addDrone = (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : IInventoryChanges = > {
const index = inventory . Drones . push ( { ItemType : typeName , CurrentHP : ExportDrones [ typeName ] . durability } ) - 1 ;
inventoryChanges . Drones ? ? = [ ] ;
inventoryChanges . Drones . push ( inventory . Drones [ index ] . toJSON < IDroneClient > ( ) ) ;
return inventoryChanges ;
} ;
2025-03-07 00:41:18 -08:00
export const addEmailItem = async (
inventory : TInventoryDatabaseDocument ,
typeName : string ,
inventoryChanges : IInventoryChanges = { }
) : Promise < IInventoryChanges > = > {
const meta = ExportEmailItems [ typeName ] ;
const emailItem = inventory . EmailItems . find ( x = > x . ItemType == typeName ) ;
if ( ! emailItem || ! meta . sendOnlyOnce ) {
await createMessage ( inventory . accountOwnerId . toString ( ) , [ convertInboxMessage ( meta . message ) ] ) ;
if ( emailItem ) {
emailItem . ItemCount += 1 ;
} else {
inventory . EmailItems . push ( { ItemType : typeName , ItemCount : 1 } ) ;
}
inventoryChanges . EmailItems ? ? = [ ] ;
inventoryChanges . EmailItems . push ( { ItemType : typeName , ItemCount : 1 } ) ;
}
return inventoryChanges ;
} ;
2025-01-24 14:13:21 +01:00
//TODO: wrong id is not erroring
export const addGearExpByCategory = (
2025-01-03 22:17:34 +01:00
inventory : TInventoryDatabaseDocument ,
2025-02-28 03:05:32 -08:00
gearArray : IEquipmentClient [ ] ,
2024-06-29 13:55:15 +02:00
categoryName : TEquipmentKey
2024-12-29 21:47:18 +01:00
) : void = > {
2023-08-31 14:29:09 +04:00
const category = inventory [ categoryName ] ;
2025-02-28 03:05:32 -08:00
gearArray . forEach ( ( { ItemId , XP } ) = > {
2024-05-08 22:53:06 +02:00
if ( ! XP ) {
return ;
}
2023-08-31 14:29:09 +04:00
2025-02-03 13:20:56 -08:00
const item = category . id ( ItemId . $oid ) ;
if ( item ) {
2024-05-08 22:53:06 +02:00
item . XP ? ? = 0 ;
item . XP += XP ;
const xpinfoIndex = inventory . XPInfo . findIndex ( x = > x . ItemType == item . ItemType ) ;
if ( xpinfoIndex !== - 1 ) {
const xpinfo = inventory . XPInfo [ xpinfoIndex ] ;
xpinfo . XP += XP ;
} else {
inventory . XPInfo . push ( {
ItemType : item.ItemType ,
XP : XP
} ) ;
}
2023-08-31 14:29:09 +04:00
}
} ) ;
} ;
2025-02-28 03:05:32 -08:00
export const addMiscItems = ( inventory : TInventoryDatabaseDocument , itemsArray : IMiscItem [ ] ) : void = > {
2023-09-06 14:02:54 +04:00
const { MiscItems } = inventory ;
2025-02-28 03:05:32 -08:00
itemsArray . forEach ( ( { ItemCount , ItemType } ) = > {
2025-01-31 17:02:27 +01:00
if ( ItemCount == 0 ) {
return ;
}
2023-09-06 14:02:54 +04:00
2025-01-31 17:02:27 +01:00
let itemIndex = MiscItems . findIndex ( x = > x . ItemType === ItemType ) ;
if ( itemIndex == - 1 ) {
itemIndex = MiscItems . push ( { ItemType , ItemCount : 0 } ) - 1 ;
}
MiscItems [ itemIndex ] . ItemCount += ItemCount ;
2025-03-15 06:39:54 -07:00
if ( ItemType == "/Lotus/Types/Items/MiscItems/ArgonCrystal" ) {
inventory . FoundToday ? ? = [ ] ;
let foundTodayIndex = inventory . FoundToday . findIndex ( x = > x . ItemType == ItemType ) ;
if ( foundTodayIndex == - 1 ) {
foundTodayIndex = inventory . FoundToday . push ( { ItemType , ItemCount : 0 } ) - 1 ;
}
inventory . FoundToday [ foundTodayIndex ] . ItemCount += ItemCount ;
if ( inventory . FoundToday [ foundTodayIndex ] . ItemCount <= 0 ) {
inventory . FoundToday . splice ( foundTodayIndex , 1 ) ;
}
if ( inventory . FoundToday . length == 0 ) {
inventory . FoundToday = undefined ;
}
}
2025-01-31 17:02:27 +01:00
if ( MiscItems [ itemIndex ] . ItemCount == 0 ) {
MiscItems . splice ( itemIndex , 1 ) ;
} else if ( MiscItems [ itemIndex ] . ItemCount <= 0 ) {
logger . warn ( ` account now owns a negative amount of ${ ItemType } ` ) ;
2023-09-06 14:02:54 +04:00
}
} ) ;
} ;
2025-03-17 05:10:28 -07:00
const applyArrayChanges = ( arr : ITypeCount [ ] , changes : ITypeCount [ ] ) : void = > {
for ( const change of changes ) {
if ( change . ItemCount != 0 ) {
let itemIndex = arr . findIndex ( x = > x . ItemType === change . ItemType ) ;
if ( itemIndex == - 1 ) {
itemIndex = arr . push ( { ItemType : change.ItemType , ItemCount : 0 } ) - 1 ;
}
2024-06-15 15:12:08 +02:00
2025-03-17 05:10:28 -07:00
arr [ itemIndex ] . ItemCount += change . ItemCount ;
if ( arr [ itemIndex ] . ItemCount == 0 ) {
arr . splice ( itemIndex , 1 ) ;
} else if ( arr [ itemIndex ] . ItemCount <= 0 ) {
logger . warn ( ` account now owns a negative amount of ${ change . ItemType } ` ) ;
}
2024-06-15 15:12:08 +02:00
}
2025-03-17 05:10:28 -07:00
}
2024-06-15 15:12:08 +02:00
} ;
2025-03-17 05:10:28 -07:00
export const addShipDecorations = ( inventory : TInventoryDatabaseDocument , itemsArray : ITypeCount [ ] ) : void = > {
applyArrayChanges ( inventory . ShipDecorations , itemsArray ) ;
} ;
2023-09-06 14:02:54 +04:00
2025-03-17 05:10:28 -07:00
export const addConsumables = ( inventory : TInventoryDatabaseDocument , itemsArray : ITypeCount [ ] ) : void = > {
applyArrayChanges ( inventory . Consumables , itemsArray ) ;
2023-09-06 14:02:54 +04:00
} ;
2025-02-28 03:05:32 -08:00
export const addCrewShipRawSalvage = ( inventory : TInventoryDatabaseDocument , itemsArray : ITypeCount [ ] ) : void = > {
2025-03-17 05:10:28 -07:00
applyArrayChanges ( inventory . CrewShipRawSalvage , itemsArray ) ;
2025-02-05 12:23:35 -08:00
} ;
2025-02-28 03:05:32 -08:00
export const addCrewShipAmmo = ( inventory : TInventoryDatabaseDocument , itemsArray : ITypeCount [ ] ) : void = > {
2025-03-17 05:10:28 -07:00
applyArrayChanges ( inventory . CrewShipAmmo , itemsArray ) ;
2025-02-05 12:23:35 -08:00
} ;
2025-02-28 03:05:32 -08:00
export const addRecipes = ( inventory : TInventoryDatabaseDocument , itemsArray : ITypeCount [ ] ) : void = > {
2025-03-17 05:10:28 -07:00
applyArrayChanges ( inventory . Recipes , itemsArray ) ;
2023-09-06 14:02:54 +04:00
} ;
2025-02-28 03:05:32 -08:00
export const addMods = ( inventory : TInventoryDatabaseDocument , itemsArray : IRawUpgrade [ ] ) : void = > {
2023-09-06 14:02:54 +04:00
const { RawUpgrades } = inventory ;
2025-01-31 17:02:27 +01:00
2025-02-28 03:05:32 -08:00
itemsArray . forEach ( ( { ItemType , ItemCount } ) = > {
2025-01-31 17:02:27 +01:00
if ( ItemCount == 0 ) {
return ;
}
2023-08-31 14:29:09 +04:00
2025-01-31 17:02:27 +01:00
let itemIndex = RawUpgrades . findIndex ( x = > x . ItemType === ItemType ) ;
if ( itemIndex == - 1 ) {
itemIndex = RawUpgrades . push ( { ItemType , ItemCount : 0 } ) - 1 ;
}
RawUpgrades [ itemIndex ] . ItemCount += ItemCount ;
if ( RawUpgrades [ itemIndex ] . ItemCount == 0 ) {
RawUpgrades . splice ( itemIndex , 1 ) ;
} else if ( RawUpgrades [ itemIndex ] . ItemCount <= 0 ) {
logger . warn ( ` account now owns a negative amount of ${ ItemType } ` ) ;
2023-08-31 14:29:09 +04:00
}
} ) ;
} ;
2025-02-28 03:05:32 -08:00
export const addFusionTreasures = ( inventory : TInventoryDatabaseDocument , itemsArray : IFusionTreasure [ ] ) : void = > {
2024-07-03 23:01:35 +02:00
const { FusionTreasures } = inventory ;
2025-02-28 03:05:32 -08:00
itemsArray . forEach ( ( { ItemType , ItemCount , Sockets } ) = > {
2024-10-12 23:51:45 +02:00
const itemIndex = FusionTreasures . findIndex ( i = > i . ItemType == ItemType && ( i . Sockets || 0 ) == ( Sockets || 0 ) ) ;
2024-07-03 23:01:35 +02:00
if ( itemIndex !== - 1 ) {
FusionTreasures [ itemIndex ] . ItemCount += ItemCount ;
2025-02-28 06:08:46 -08:00
if ( FusionTreasures [ itemIndex ] . ItemCount == 0 ) {
FusionTreasures . splice ( itemIndex , 1 ) ;
} else if ( FusionTreasures [ itemIndex ] . ItemCount <= 0 ) {
logger . warn ( ` account now owns a negative amount of ${ ItemType } ` ) ;
}
2024-07-03 23:01:35 +02:00
} else {
FusionTreasures . push ( { ItemCount , ItemType , Sockets } ) ;
}
} ) ;
} ;
2025-02-28 03:05:32 -08:00
export const addFocusXpIncreases = ( inventory : TInventoryDatabaseDocument , focusXpPlus : number [ ] ) : void = > {
2025-01-27 18:11:05 +01:00
enum FocusType {
AP_UNIVERSAL ,
AP_ATTACK ,
AP_DEFENSE ,
AP_TACTIC ,
AP_POWER ,
AP_PRECEPT ,
AP_FUSION ,
AP_WARD ,
AP_UMBRA ,
AP_ANY
}
2025-02-28 03:05:32 -08:00
inventory . FocusXP ? ? = { AP_ATTACK : 0 , AP_DEFENSE : 0 , AP_TACTIC : 0 , AP_POWER : 0 , AP_WARD : 0 } ;
inventory . FocusXP . AP_ATTACK += focusXpPlus [ FocusType . AP_ATTACK ] ;
inventory . FocusXP . AP_DEFENSE += focusXpPlus [ FocusType . AP_DEFENSE ] ;
inventory . FocusXP . AP_TACTIC += focusXpPlus [ FocusType . AP_TACTIC ] ;
inventory . FocusXP . AP_POWER += focusXpPlus [ FocusType . AP_POWER ] ;
inventory . FocusXP . AP_WARD += focusXpPlus [ FocusType . AP_WARD ] ;
2025-01-27 18:11:05 +01:00
} ;
2024-06-02 18:35:06 +03:00
export const addSeasonalChallengeHistory = (
2025-01-03 22:17:34 +01:00
inventory : TInventoryDatabaseDocument ,
2025-03-25 06:38:37 -07:00
itemsArray : ISeasonChallenge [ ]
2024-12-29 21:47:18 +01:00
) : void = > {
2024-06-02 18:35:06 +03:00
const category = inventory . SeasonChallengeHistory ;
2025-03-25 06:38:37 -07:00
itemsArray . forEach ( ( { challenge , id } ) = > {
2024-06-02 18:35:06 +03:00
const itemIndex = category . findIndex ( i = > i . challenge === challenge ) ;
if ( itemIndex !== - 1 ) {
category [ itemIndex ] . id = id ;
} else {
category . push ( { challenge , id } ) ;
}
} ) ;
} ;
2025-03-25 06:38:37 -07:00
export const addChallenges = ( inventory : TInventoryDatabaseDocument , itemsArray : IChallengeProgress [ ] ) : void = > {
2023-08-31 14:29:09 +04:00
const category = inventory . ChallengeProgress ;
2025-03-25 06:38:37 -07:00
itemsArray . forEach ( ( { Name , Progress } ) = > {
2023-08-31 14:29:09 +04:00
const itemIndex = category . findIndex ( i = > i . Name === Name ) ;
if ( itemIndex !== - 1 ) {
2025-03-25 06:38:37 -07:00
category [ itemIndex ] . Progress = Progress ;
2023-08-31 14:29:09 +04:00
} else {
category . push ( { Name , Progress } ) ;
}
} ) ;
} ;
2025-01-24 14:13:21 +01:00
export const addMissionComplete = ( inventory : TInventoryDatabaseDocument , { Tag , Completes } : IMission ) : void = > {
2023-09-10 00:10:21 +04:00
const { Missions } = inventory ;
const itemIndex = Missions . findIndex ( item = > item . Tag === Tag ) ;
if ( itemIndex !== - 1 ) {
Missions [ itemIndex ] . Completes += Completes ;
} else {
Missions . push ( { Tag , Completes } ) ;
}
} ;
2025-01-24 15:24:29 +01:00
export const addBooster = ( ItemType : string , time : number , inventory : TInventoryDatabaseDocument ) : void = > {
2025-03-22 17:35:18 -07:00
const currentTime = Math . floor ( Date . now ( ) / 1000 ) ;
2025-01-24 14:13:21 +01:00
const { Boosters } = inventory ;
2023-08-31 14:29:09 +04:00
2025-01-24 14:13:21 +01:00
const itemIndex = Boosters . findIndex ( booster = > booster . ItemType === ItemType ) ;
2023-09-06 14:02:54 +04:00
2025-01-24 14:13:21 +01:00
if ( itemIndex !== - 1 ) {
const existingBooster = Boosters [ itemIndex ] ;
existingBooster . ExpiryDate = Math . max ( existingBooster . ExpiryDate , currentTime ) + time ;
} else {
Boosters . push ( { ItemType , ExpiryDate : currentTime + time } ) ;
}
} ;
export const updateSyndicate = (
inventory : HydratedDocument < IInventoryDatabase , InventoryDocumentProps > ,
syndicateUpdate : IMissionInventoryUpdateRequest [ "AffiliationChanges" ]
2025-02-01 07:32:56 -08:00
) : void = > {
2025-01-24 14:13:21 +01:00
syndicateUpdate ? . forEach ( affiliation = > {
2024-06-02 18:35:06 +03:00
const syndicate = inventory . Affiliations . find ( x = > x . Tag == affiliation . Tag ) ;
if ( syndicate !== undefined ) {
2025-02-01 07:32:56 -08:00
syndicate . Standing += affiliation . Standing ;
2024-06-02 18:35:06 +03:00
syndicate . Title = syndicate . Title === undefined ? affiliation.Title : syndicate.Title + affiliation . Title ;
} else {
inventory . Affiliations . push ( {
Standing : affiliation.Standing ,
Title : affiliation.Title ,
Tag : affiliation.Tag ,
FreeFavorsEarned : [ ] ,
FreeFavorsUsed : [ ]
} ) ;
}
2025-02-01 07:32:56 -08:00
updateStandingLimit ( inventory , ExportSyndicates [ affiliation . Tag ] . dailyLimitBin , affiliation . Standing ) ;
2024-06-02 18:35:06 +03:00
} ) ;
2023-08-31 14:29:09 +04:00
} ;
2025-01-24 14:13:21 +01:00
/ * *
* @returns object with inventory keys of changes or empty object when no items were added
* /
export const addKeyChainItems = async (
inventory : TInventoryDatabaseDocument ,
2025-01-31 17:24:42 +01:00
keyChainData : IKeyChainRequest
2025-01-24 14:13:21 +01:00
) : Promise < IInventoryChanges > = > {
const keyChainItems = getKeyChainItems ( keyChainData ) ;
2023-08-31 14:29:09 +04:00
2025-01-24 14:13:21 +01:00
logger . debug (
` adding key chain items ${ keyChainItems . join ( ) } for ${ keyChainData . KeyChain } at stage ${ keyChainData . ChainStage } `
) ;
2023-08-31 14:29:09 +04:00
2025-03-09 07:42:55 -07:00
const nonStoreItems = keyChainItems . map ( item = > fromStoreItem ( item ) ) ;
2023-08-31 14:29:09 +04:00
2025-03-23 08:26:46 -07:00
const inventoryChanges : IInventoryChanges = { } ;
2025-01-24 14:13:21 +01:00
for ( const item of nonStoreItems ) {
const inventoryChangesDelta = await addItem ( inventory , item ) ;
2025-03-23 08:26:46 -07:00
combineInventoryChanges ( inventoryChanges , inventoryChangesDelta ) ;
2023-08-31 14:29:09 +04:00
}
2025-01-24 14:13:21 +01:00
return inventoryChanges ;
2023-08-31 14:29:09 +04:00
} ;
2025-02-25 17:31:52 -08:00
export const createLibraryDailyTask = ( ) : ILibraryDailyTaskInfo = > {
const enemyTypes = getRandomElement ( libraryDailyTasks ) ;
const enemyAvatar = ExportEnemies . avatars [ enemyTypes [ 0 ] ] ;
const scansRequired = getRandomInt ( 2 , 4 ) ;
2025-02-22 11:10:52 -08:00
return {
2025-02-25 17:31:52 -08:00
EnemyTypes : enemyTypes ,
EnemyLocTag : enemyAvatar.name ,
EnemyIcon : enemyAvatar.icon ! ,
ScansRequired : scansRequired ,
RewardStoreItem : "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/RareFusionBundle" ,
RewardQuantity : Math.trunc ( scansRequired * 2.5 ) ,
RewardStanding : 2500 * scansRequired
2025-02-22 11:10:52 -08:00
} ;
} ;
const createCalendar = ( ) : ICalendarProgress = > {
return {
Version : 19 ,
Iteration : 2 ,
YearProgress : { Upgrades : [ ] } ,
SeasonProgress : {
SeasonType : "CST_SPRING" ,
LastCompletedDayIdx : - 1 ,
LastCompletedChallengeDayIdx : - 1 ,
ActivatedChallenges : [ ]
}
} ;
} ;