2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-11-28 13:42:58 +01:00
import { ApplicationContext } from "@spt-aki/context/ApplicationContext" ;
import { ContextVariableType } from "@spt-aki/context/ContextVariableType" ;
2023-10-19 19:21:17 +02:00
import { LocationGenerator } from "@spt-aki/generators/LocationGenerator" ;
import { LootGenerator } from "@spt-aki/generators/LootGenerator" ;
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper" ;
import { ILocation } from "@spt-aki/models/eft/common/ILocation" ;
import { ILocationBase } from "@spt-aki/models/eft/common/ILocationBase" ;
import { ILocationsGenerateAllResponse } from "@spt-aki/models/eft/common/ILocationsSourceDestinationBase" ;
import { ILooseLoot , SpawnpointTemplate } from "@spt-aki/models/eft/common/ILooseLoot" ;
import { IAirdropLootResult } from "@spt-aki/models/eft/location/IAirdropLootResult" ;
import { IGetLocationRequestData } from "@spt-aki/models/eft/location/IGetLocationRequestData" ;
import { AirdropTypeEnum } from "@spt-aki/models/enums/AirdropType" ;
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes" ;
import { IAirdropConfig } from "@spt-aki/models/spt/config/IAirdropConfig" ;
2023-11-28 13:42:58 +01:00
import { ILocationConfig , LootMultiplier } from "@spt-aki/models/spt/config/ILocationConfig" ;
import { ILootMultiplerChange } from "@spt-aki/models/spt/location/ILootMultiplerChange" ;
2023-10-19 19:21:17 +02:00
import { ILocations } from "@spt-aki/models/spt/server/ILocations" ;
import { LootRequest } from "@spt-aki/models/spt/services/LootRequest" ;
import { ILogger } from "@spt-aki/models/spt/utils/ILogger" ;
import { ConfigServer } from "@spt-aki/servers/ConfigServer" ;
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer" ;
import { LocalisationService } from "@spt-aki/services/LocalisationService" ;
import { HashUtil } from "@spt-aki/utils/HashUtil" ;
import { JsonUtil } from "@spt-aki/utils/JsonUtil" ;
import { RandomUtil } from "@spt-aki/utils/RandomUtil" ;
import { TimeUtil } from "@spt-aki/utils/TimeUtil" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class LocationController
{
protected airdropConfig : IAirdropConfig ;
2023-10-10 13:03:20 +02:00
protected locationConfig : ILocationConfig ;
2023-03-03 16:23:46 +01:00
constructor (
@inject ( "JsonUtil" ) protected jsonUtil : JsonUtil ,
@inject ( "HashUtil" ) protected hashUtil : HashUtil ,
2023-10-10 13:03:20 +02:00
@inject ( "RandomUtil" ) protected randomUtil : RandomUtil ,
2023-03-18 18:29:26 +01:00
@inject ( "WeightedRandomHelper" ) protected weightedRandomHelper : WeightedRandomHelper ,
2023-03-03 16:23:46 +01:00
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "LocationGenerator" ) protected locationGenerator : LocationGenerator ,
@inject ( "LocalisationService" ) protected localisationService : LocalisationService ,
@inject ( "LootGenerator" ) protected lootGenerator : LootGenerator ,
@inject ( "DatabaseServer" ) protected databaseServer : DatabaseServer ,
@inject ( "TimeUtil" ) protected timeUtil : TimeUtil ,
2023-11-16 22:42:06 +01:00
@inject ( "ConfigServer" ) protected configServer : ConfigServer ,
2023-11-28 13:42:58 +01:00
@inject ( "ApplicationContext" ) protected applicationContext : ApplicationContext ,
2023-03-03 16:23:46 +01:00
)
{
this . airdropConfig = this . configServer . getConfig ( ConfigTypes . AIRDROP ) ;
2023-10-10 13:03:20 +02:00
this . locationConfig = this . configServer . getConfig ( ConfigTypes . LOCATION ) ;
2023-03-03 16:23:46 +01:00
}
2023-07-15 15:49:25 +02:00
/* */
/ * *
* Handle client / location / getLocalloot
* Get a location ( map ) with generated loot data
2023-10-10 13:03:20 +02:00
* @param sessionId Player id
* @param request Map request to generate
2023-07-15 15:49:25 +02:00
* @returns ILocationBase
* /
2023-10-10 13:03:20 +02:00
public get ( sessionId : string , request : IGetLocationRequestData ) : ILocationBase
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
this . logger . debug ( ` Generating data for: ${ request . locationId } , variant: ${ request . variantId } ` ) ;
const name = request . locationId . toLowerCase ( ) . replace ( " " , "" ) ;
2023-03-03 16:23:46 +01:00
return this . generate ( name ) ;
}
2023-07-24 16:52:55 +02:00
/ * *
2023-10-10 13:03:20 +02:00
* Generate a maps base location with loot
2023-07-24 16:52:55 +02:00
* @param name Map name
* @returns ILocationBase
* /
protected generate ( name : string ) : ILocationBase
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
const db = this . databaseServer . getTables ( ) ;
const location : ILocation = db . locations [ name ] ;
2023-07-09 15:47:02 +02:00
const output : ILocationBase = this . jsonUtil . clone ( location . base ) ;
2023-03-03 16:23:46 +01:00
output . UnixDateTime = this . timeUtil . getTimestamp ( ) ;
2023-08-04 13:57:16 +02:00
// Don't generate loot for hideout
2023-03-03 16:23:46 +01:00
if ( name === "hideout" )
{
return output ;
}
2023-11-28 13:42:58 +01:00
// Check for a loot multipler adjustment in app context and apply if one is found
let locationConfigCopy : ILocationConfig ;
const lootMultiplierAdjustment = this . applicationContext . getLatestValue ( ContextVariableType . LOOT_MULTIPLER_CHANGE ) ? . getValue < ILootMultiplerChange > ( ) ;
if ( lootMultiplierAdjustment )
{
this . logger . debug ( ` Adjusting dynamic loot multipliers to ${ lootMultiplierAdjustment . dynamicLootPercent } % and static loot multipliers to ${ lootMultiplierAdjustment . staticLootPercent } % of original ` )
locationConfigCopy = this . jsonUtil . clone ( this . locationConfig ) ; // Clone values so they can be used to reset originals later
// Change loot multipler values before they're used below
this . adjustLootMultipliers ( this . locationConfig . looseLootMultiplier , lootMultiplierAdjustment . dynamicLootPercent ) ;
this . adjustLootMultipliers ( this . locationConfig . staticLootMultiplier , lootMultiplierAdjustment . staticLootPercent ) ;
}
2023-08-04 13:57:16 +02:00
const staticAmmoDist = this . jsonUtil . clone ( db . loot . staticAmmo ) ;
2023-03-03 16:23:46 +01:00
2023-10-10 13:03:20 +02:00
// Create containers and add loot to them
2023-11-28 13:42:58 +01:00
const staticLoot = this . locationGenerator . generateStaticContainers ( output , staticAmmoDist ) ;
2023-10-10 13:03:20 +02:00
output . Loot . push ( . . . staticLoot ) ;
2023-11-16 22:42:06 +01:00
2023-08-04 13:57:16 +02:00
// Add dyanmic loot to output loot
2023-03-03 16:23:46 +01:00
const dynamicLootDist : ILooseLoot = this . jsonUtil . clone ( location . looseLoot ) ;
2023-11-16 22:42:06 +01:00
const dynamicSpawnPoints : SpawnpointTemplate [ ] = this . locationGenerator . generateDynamicLoot (
dynamicLootDist ,
staticAmmoDist ,
name ,
) ;
2023-10-10 13:03:20 +02:00
for ( const spawnPoint of dynamicSpawnPoints )
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
output . Loot . push ( spawnPoint ) ;
2023-03-03 16:23:46 +01:00
}
2023-08-04 13:57:16 +02:00
// Done generating, log results
2023-11-16 22:42:06 +01:00
this . logger . success (
this . localisationService . getText ( "location-dynamic_items_spawned_success" , dynamicSpawnPoints . length ) ,
) ;
2023-03-03 16:23:46 +01:00
this . logger . success ( this . localisationService . getText ( "location-generated_success" , name ) ) ;
2023-11-28 13:42:58 +01:00
// Reset loot multipliers back to original values
if ( lootMultiplierAdjustment )
{
this . logger . debug ( "Resetting loot multipliers back to their original values" ) ;
this . locationConfig . staticLootMultiplier = locationConfigCopy . staticLootMultiplier ;
this . locationConfig . looseLootMultiplier = locationConfigCopy . looseLootMultiplier ;
this . applicationContext . clearValues ( ContextVariableType . LOOT_MULTIPLER_CHANGE ) ;
}
2023-03-03 16:23:46 +01:00
return output ;
}
2023-11-28 13:42:58 +01:00
/ * *
* Adjust the loot multiplier values passed in to be a % of their original value
* @param mapLootMultiplers Multiplers to adjust
* @param loosePercent Percent to change values to
* /
protected adjustLootMultipliers ( mapLootMultiplers : LootMultiplier , loosePercent : number ) : void
{
for ( const key in mapLootMultiplers )
{
mapLootMultiplers [ key ] = this . randomUtil . getPercentOfValue ( mapLootMultiplers [ key ] , loosePercent ) ;
}
}
2023-03-18 18:29:26 +01:00
/ * *
2023-07-15 12:58:35 +02:00
* Handle client / locations
2023-03-18 18:29:26 +01:00
* Get all maps base location properties without loot data
2023-07-24 14:20:17 +02:00
* @param sessionId Players Id
2023-03-18 18:29:26 +01:00
* @returns ILocationsGenerateAllResponse
* /
2023-07-24 14:20:17 +02:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public generateAll ( sessionId : string ) : ILocationsGenerateAllResponse
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
const locationsFromDb = this . databaseServer . getTables ( ) . locations ;
2023-07-15 12:58:35 +02:00
const locations : ILocations = { } ;
for ( const mapName in locationsFromDb )
2023-03-03 16:23:46 +01:00
{
2023-07-15 12:58:35 +02:00
const mapBase = locationsFromDb [ mapName ] ? . base ;
if ( ! mapBase )
2023-03-03 16:23:46 +01:00
{
2023-07-15 12:58:35 +02:00
this . logger . debug ( ` Map: ${ mapName } has no base json file, skipping generation ` ) ;
2023-03-03 16:23:46 +01:00
continue ;
}
2023-07-15 12:58:35 +02:00
// Clear out loot array
mapBase . Loot = [ ] ;
// Add map base data to dictionary
locations [ mapBase . _Id ] = mapBase ;
2023-03-03 16:23:46 +01:00
}
2023-11-16 22:42:06 +01:00
return { locations : locations , paths : locationsFromDb.base.paths } ;
2023-03-03 16:23:46 +01:00
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle client / location / getAirdropLoot
2023-03-03 16:23:46 +01:00
* Get loot for an airdop container
* Generates it randomly based on config / airdrop . json values
2023-03-18 18:29:26 +01:00
* @returns Array of LootItem objects
2023-03-03 16:23:46 +01:00
* /
2023-07-09 15:47:02 +02:00
public getAirdropLoot ( ) : IAirdropLootResult
2023-03-03 16:23:46 +01:00
{
2023-03-18 18:29:26 +01:00
const airdropType = this . chooseAirdropType ( ) ;
this . logger . debug ( ` Chose ${ airdropType } for airdrop loot ` ) ;
const airdropConfig = this . getAirdropLootConfigByType ( airdropType ) ;
2023-11-16 22:42:06 +01:00
return { dropType : airdropType , loot : this.lootGenerator.createRandomLoot ( airdropConfig ) } ;
2023-03-18 18:29:26 +01:00
}
/ * *
* Randomly pick a type of airdrop loot using weighted values from config
* @returns airdrop type value
* /
protected chooseAirdropType ( ) : AirdropTypeEnum
{
const possibleAirdropTypes = this . airdropConfig . airdropTypeWeightings ;
return this . weightedRandomHelper . getWeightedValue ( possibleAirdropTypes ) ;
}
/ * *
* Get the configuration for a specific type of airdrop
* @param airdropType Type of airdrop to get settings for
* @returns LootRequest
* /
protected getAirdropLootConfigByType ( airdropType : AirdropTypeEnum ) : LootRequest
{
let lootSettingsByType = this . airdropConfig . loot [ airdropType ] ;
if ( ! lootSettingsByType )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "location-unable_to_find_airdrop_drop_config_of_type" , airdropType ) ,
) ;
2023-03-18 18:29:26 +01:00
lootSettingsByType = this . airdropConfig . loot [ AirdropTypeEnum . MIXED ] ;
}
2023-03-03 16:23:46 +01:00
2023-03-18 18:29:26 +01:00
return {
presetCount : lootSettingsByType.presetCount ,
itemCount : lootSettingsByType.itemCount ,
2023-06-20 17:59:15 +02:00
weaponCrateCount : lootSettingsByType.weaponCrateCount ,
2023-03-18 18:29:26 +01:00
itemBlacklist : lootSettingsByType.itemBlacklist ,
itemTypeWhitelist : lootSettingsByType.itemTypeWhitelist ,
itemLimits : lootSettingsByType.itemLimits ,
itemStackLimits : lootSettingsByType.itemStackLimits ,
2023-10-12 12:00:04 +02:00
armorLevelWhitelist : lootSettingsByType.armorLevelWhitelist ,
2023-11-16 22:42:06 +01:00
allowBossItems : lootSettingsByType.allowBossItems ,
2023-03-18 18:29:26 +01:00
} ;
2023-03-03 16:23:46 +01:00
}
2023-11-16 22:42:06 +01:00
}