Refactored airdrop loot code
This commit is contained in:
parent
4cf1c10c51
commit
1f76ce5d10
@ -2,55 +2,42 @@ import { inject, injectable } from "tsyringe";
|
|||||||
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
||||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||||
import { LocationGenerator } from "@spt/generators/LocationGenerator";
|
import { LocationGenerator } from "@spt/generators/LocationGenerator";
|
||||||
import { LootGenerator } from "@spt/generators/LootGenerator";
|
|
||||||
import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper";
|
|
||||||
import { ILocationBase } from "@spt/models/eft/common/ILocationBase";
|
import { ILocationBase } from "@spt/models/eft/common/ILocationBase";
|
||||||
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
||||||
import { ILooseLoot, SpawnpointTemplate } from "@spt/models/eft/common/ILooseLoot";
|
import { ILooseLoot, SpawnpointTemplate } from "@spt/models/eft/common/ILooseLoot";
|
||||||
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
||||||
import { IGetLocationRequestData } from "@spt/models/eft/location/IGetLocationRequestData";
|
import { IGetLocationRequestData } from "@spt/models/eft/location/IGetLocationRequestData";
|
||||||
import { AirdropTypeEnum } from "@spt/models/enums/AirdropType";
|
|
||||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||||
import { AirdropLoot, IAirdropConfig } from "@spt/models/spt/config/IAirdropConfig";
|
|
||||||
import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig";
|
import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig";
|
||||||
import { IRaidChanges } from "@spt/models/spt/location/IRaidChanges";
|
import { IRaidChanges } from "@spt/models/spt/location/IRaidChanges";
|
||||||
import { ILocations } from "@spt/models/spt/server/ILocations";
|
import { ILocations } from "@spt/models/spt/server/ILocations";
|
||||||
import { LootRequest } from "@spt/models/spt/services/LootRequest";
|
|
||||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||||
|
import { AirdropService } from "@spt/services/AirdropService";
|
||||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||||
import { ItemFilterService } from "@spt/services/ItemFilterService";
|
|
||||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||||
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
||||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||||
import { HashUtil } from "@spt/utils/HashUtil";
|
|
||||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
|
||||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LocationController
|
export class LocationController
|
||||||
{
|
{
|
||||||
protected airdropConfig: IAirdropConfig;
|
|
||||||
protected locationConfig: ILocationConfig;
|
protected locationConfig: ILocationConfig;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
||||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
||||||
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
|
||||||
@inject("PrimaryLogger") protected logger: ILogger,
|
@inject("PrimaryLogger") protected logger: ILogger,
|
||||||
@inject("LocationGenerator") protected locationGenerator: LocationGenerator,
|
@inject("LocationGenerator") protected locationGenerator: LocationGenerator,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("RaidTimeAdjustmentService") protected raidTimeAdjustmentService: RaidTimeAdjustmentService,
|
@inject("RaidTimeAdjustmentService") protected raidTimeAdjustmentService: RaidTimeAdjustmentService,
|
||||||
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
|
||||||
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
|
||||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||||
|
@inject("AirdropService") protected airdropService: AirdropService,
|
||||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
|
||||||
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
|
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,65 +152,8 @@ export class LocationController
|
|||||||
return { locations: locations, paths: locationsFromDb.base.paths };
|
return { locations: locations, paths: locationsFromDb.base.paths };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle client/location/getAirdropLoot
|
|
||||||
* Get loot for an airdrop container
|
|
||||||
* Generates it randomly based on config/airdrop.json values
|
|
||||||
* @returns Array of LootItem objects
|
|
||||||
*/
|
|
||||||
public getAirdropLoot(): IGetAirdropLootResponse
|
public getAirdropLoot(): IGetAirdropLootResponse
|
||||||
{
|
{
|
||||||
const airdropType = this.chooseAirdropType();
|
return this.airdropService.generateAirdropLoot();
|
||||||
this.logger.debug(`Chose ${airdropType} for airdrop loot`);
|
|
||||||
|
|
||||||
const airdropConfig = this.getAirdropLootConfigByType(airdropType);
|
|
||||||
|
|
||||||
return { icon: airdropType, container: this.lootGenerator.createRandomLoot(airdropConfig) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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: AirdropLoot = this.airdropConfig.loot[airdropType];
|
|
||||||
if (!lootSettingsByType)
|
|
||||||
{
|
|
||||||
this.logger.error(
|
|
||||||
this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType),
|
|
||||||
);
|
|
||||||
lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.COMMON];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
airdropLoot: airdropType,
|
|
||||||
weaponPresetCount: lootSettingsByType.weaponPresetCount,
|
|
||||||
armorPresetCount: lootSettingsByType.armorPresetCount,
|
|
||||||
itemCount: lootSettingsByType.itemCount,
|
|
||||||
weaponCrateCount: lootSettingsByType.weaponCrateCount,
|
|
||||||
itemBlacklist: [
|
|
||||||
...lootSettingsByType.itemBlacklist,
|
|
||||||
...this.itemFilterService.getItemRewardBlacklist(),
|
|
||||||
...this.itemFilterService.getBossItems(),
|
|
||||||
],
|
|
||||||
itemTypeWhitelist: lootSettingsByType.itemTypeWhitelist,
|
|
||||||
itemLimits: lootSettingsByType.itemLimits,
|
|
||||||
itemStackLimits: lootSettingsByType.itemStackLimits,
|
|
||||||
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
|
|
||||||
allowBossItems: lootSettingsByType.allowBossItems,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ export class ProfileController
|
|||||||
if (!pmc?.Info?.Level)
|
if (!pmc?.Info?.Level)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
username: profile.info.username,
|
username: profile.info?.username ?? "",
|
||||||
nickname: "unknown",
|
nickname: "unknown",
|
||||||
side: "unknown",
|
side: "unknown",
|
||||||
currlvl: 0,
|
currlvl: 0,
|
||||||
|
@ -197,6 +197,7 @@ import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectio
|
|||||||
import { DefaultSptWebSocketMessageHandler } from "@spt/servers/ws/message/DefaultSptWebSocketMessageHandler";
|
import { DefaultSptWebSocketMessageHandler } from "@spt/servers/ws/message/DefaultSptWebSocketMessageHandler";
|
||||||
import { ISptWebSocketMessageHandler } from "@spt/servers/ws/message/ISptWebSocketMessageHandler";
|
import { ISptWebSocketMessageHandler } from "@spt/servers/ws/message/ISptWebSocketMessageHandler";
|
||||||
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
|
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
|
||||||
|
import { AirdropService } from "@spt/services/AirdropService";
|
||||||
import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService";
|
import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService";
|
||||||
import { BotEquipmentModPoolService } from "@spt/services/BotEquipmentModPoolService";
|
import { BotEquipmentModPoolService } from "@spt/services/BotEquipmentModPoolService";
|
||||||
import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService";
|
import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService";
|
||||||
@ -795,6 +796,9 @@ export class Container
|
|||||||
depContainer.register<ProfileActivityService>("ProfileActivityService", ProfileActivityService, {
|
depContainer.register<ProfileActivityService>("ProfileActivityService", ProfileActivityService, {
|
||||||
lifecycle: Lifecycle.Singleton,
|
lifecycle: Lifecycle.Singleton,
|
||||||
});
|
});
|
||||||
|
depContainer.register<AirdropService>("AirdropService", AirdropService, {
|
||||||
|
lifecycle: Lifecycle.Singleton,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static registerServers(depContainer: DependencyContainer): void
|
private static registerServers(depContainer: DependencyContainer): void
|
||||||
|
@ -8,7 +8,6 @@ import { Item } from "@spt/models/eft/common/tables/IItem";
|
|||||||
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
|
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
|
||||||
import { BaseClasses } from "@spt/models/enums/BaseClasses";
|
import { BaseClasses } from "@spt/models/enums/BaseClasses";
|
||||||
import { ISealedAirdropContainerSettings, RewardDetails } from "@spt/models/spt/config/IInventoryConfig";
|
import { ISealedAirdropContainerSettings, RewardDetails } from "@spt/models/spt/config/IInventoryConfig";
|
||||||
import { LootItem } from "@spt/models/spt/services/LootItem";
|
|
||||||
import { LootRequest } from "@spt/models/spt/services/LootRequest";
|
import { LootRequest } from "@spt/models/spt/services/LootRequest";
|
||||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||||
@ -17,8 +16,6 @@ import { LocalisationService } from "@spt/services/LocalisationService";
|
|||||||
import { RagfairLinkedItemService } from "@spt/services/RagfairLinkedItemService";
|
import { RagfairLinkedItemService } from "@spt/services/RagfairLinkedItemService";
|
||||||
import { HashUtil } from "@spt/utils/HashUtil";
|
import { HashUtil } from "@spt/utils/HashUtil";
|
||||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||||
import { AirdropTypeEnum } from "@spt/models/enums/AirdropType";
|
|
||||||
import { ItemTpl } from "@spt/models/enums/ItemTpl";
|
|
||||||
|
|
||||||
type ItemLimit = { current: number, max: number };
|
type ItemLimit = { current: number, max: number };
|
||||||
|
|
||||||
@ -48,39 +45,6 @@ export class LootGenerator
|
|||||||
public createRandomLoot(options: LootRequest): Item[]
|
public createRandomLoot(options: LootRequest): Item[]
|
||||||
{
|
{
|
||||||
const result: Item[] = [];
|
const result: Item[] = [];
|
||||||
let airdropContainerParentID = "";
|
|
||||||
|
|
||||||
if (options.airdropLoot)
|
|
||||||
{
|
|
||||||
airdropContainerParentID = this.hashUtil.generate();
|
|
||||||
let airdropContainer = {
|
|
||||||
_id: airdropContainerParentID,
|
|
||||||
_tpl: "",
|
|
||||||
upd: {
|
|
||||||
SpawnedInSession: true,
|
|
||||||
StackObjectsCount: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (options.airdropLoot) {
|
|
||||||
case AirdropTypeEnum.MEDICAL:
|
|
||||||
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_MEDICAL_CRATE
|
|
||||||
break;
|
|
||||||
case AirdropTypeEnum.SUPPLY:
|
|
||||||
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_SUPPLY_CRATE
|
|
||||||
break;
|
|
||||||
case AirdropTypeEnum.WEAPON:
|
|
||||||
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_WEAPON_CRATE
|
|
||||||
break;
|
|
||||||
case AirdropTypeEnum.COMMON:
|
|
||||||
default:
|
|
||||||
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_COMMON_SUPPLY_CRATE
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(airdropContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemTypeCounts = this.initItemLimitCounter(options.itemLimits);
|
const itemTypeCounts = this.initItemLimitCounter(options.itemLimits);
|
||||||
|
|
||||||
const itemsDb = this.databaseService.getItems();
|
const itemsDb = this.databaseService.getItems();
|
||||||
@ -104,18 +68,18 @@ export class LootGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle sealed weapon containers
|
// Handle sealed weapon containers
|
||||||
const desiredWeaponCrateCount = this.randomUtil.getInt(
|
const sealedWeaponCrateCount = this.randomUtil.getInt(
|
||||||
options.weaponCrateCount.min,
|
options.weaponCrateCount.min,
|
||||||
options.weaponCrateCount.max,
|
options.weaponCrateCount.max,
|
||||||
);
|
);
|
||||||
if (desiredWeaponCrateCount > 0)
|
if (sealedWeaponCrateCount > 0)
|
||||||
{
|
{
|
||||||
// Get list of all sealed containers from db
|
// Get list of all sealed containers from db - they're all the same, just for flavor
|
||||||
const sealedWeaponContainerPool = Object.values(itemsDb).filter((item) =>
|
const sealedWeaponContainerPool = Object.values(itemsDb).filter((item) =>
|
||||||
item._name.includes("event_container_airdrop"),
|
item._name.includes("event_container_airdrop"),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let index = 0; index < desiredWeaponCrateCount; index++)
|
for (let index = 0; index < sealedWeaponCrateCount; index++)
|
||||||
{
|
{
|
||||||
// Choose one at random + add to results array
|
// Choose one at random + add to results array
|
||||||
const chosenSealedContainer = this.randomUtil.getArrayValue(sealedWeaponContainerPool);
|
const chosenSealedContainer = this.randomUtil.getArrayValue(sealedWeaponContainerPool);
|
||||||
@ -124,8 +88,8 @@ export class LootGenerator
|
|||||||
_tpl: chosenSealedContainer._id,
|
_tpl: chosenSealedContainer._id,
|
||||||
upd: {
|
upd: {
|
||||||
StackObjectsCount: 1,
|
StackObjectsCount: 1,
|
||||||
SpawnedInSession: true
|
SpawnedInSession: true,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,6 +103,7 @@ export class LootGenerator
|
|||||||
&& options.itemTypeWhitelist.includes(item[1]._parent),
|
&& options.itemTypeWhitelist.includes(item[1]._parent),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Pool has items we could add as loot, proceed
|
||||||
if (items.length > 0)
|
if (items.length > 0)
|
||||||
{
|
{
|
||||||
const randomisedItemCount = this.randomUtil.getInt(options.itemCount.min, options.itemCount.max);
|
const randomisedItemCount = this.randomUtil.getInt(options.itemCount.min, options.itemCount.max);
|
||||||
@ -221,19 +186,6 @@ export class LootGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of result) {
|
|
||||||
if (item._id == airdropContainerParentID)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!item.parentId)
|
|
||||||
{
|
|
||||||
item.parentId = airdropContainerParentID;
|
|
||||||
item.slotId = "main"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,8 +265,8 @@ export class LootGenerator
|
|||||||
_tpl: randomItem._id,
|
_tpl: randomItem._id,
|
||||||
upd: {
|
upd: {
|
||||||
StackObjectsCount: 1,
|
StackObjectsCount: 1,
|
||||||
SpawnedInSession: true
|
SpawnedInSession: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special case - handle items that need a stackcount > 1
|
// Special case - handle items that need a stackcount > 1
|
||||||
@ -421,8 +373,9 @@ export class LootGenerator
|
|||||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(chosenPreset._items);
|
const presetAndMods: Item[] = this.itemHelper.replaceIDs(chosenPreset._items);
|
||||||
this.itemHelper.remapRootItemId(presetAndMods);
|
this.itemHelper.remapRootItemId(presetAndMods);
|
||||||
// Add chosen preset tpl to result array
|
// Add chosen preset tpl to result array
|
||||||
presetAndMods.forEach(item => {
|
presetAndMods.forEach((item) =>
|
||||||
result.push(item)
|
{
|
||||||
|
result.push(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (itemLimitCount)
|
if (itemLimitCount)
|
||||||
|
@ -3,5 +3,5 @@ export enum AirdropTypeEnum
|
|||||||
COMMON = "common",
|
COMMON = "common",
|
||||||
SUPPLY = "supply",
|
SUPPLY = "supply",
|
||||||
MEDICAL = "medical",
|
MEDICAL = "medical",
|
||||||
WEAPON = "weapon"
|
WEAPON = "weapon",
|
||||||
}
|
}
|
@ -1,9 +1,7 @@
|
|||||||
import { MinMax } from "@spt/models/common/MinMax";
|
import { MinMax } from "@spt/models/common/MinMax";
|
||||||
import { AirdropTypeEnum } from "@spt/models/enums/AirdropType";
|
|
||||||
|
|
||||||
export interface LootRequest
|
export interface LootRequest
|
||||||
{
|
{
|
||||||
airdropLoot?: AirdropTypeEnum
|
|
||||||
weaponPresetCount: MinMax
|
weaponPresetCount: MinMax
|
||||||
armorPresetCount: MinMax
|
armorPresetCount: MinMax
|
||||||
itemCount: MinMax
|
itemCount: MinMax
|
||||||
|
@ -3,6 +3,7 @@ import { LocationCallbacks } from "@spt/callbacks/LocationCallbacks";
|
|||||||
import { RouteAction, StaticRouter } from "@spt/di/Router";
|
import { RouteAction, StaticRouter } from "@spt/di/Router";
|
||||||
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
||||||
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||||
|
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LocationStaticRouter extends StaticRouter
|
export class LocationStaticRouter extends StaticRouter
|
||||||
@ -24,7 +25,12 @@ export class LocationStaticRouter extends StaticRouter
|
|||||||
),
|
),
|
||||||
new RouteAction(
|
new RouteAction(
|
||||||
"/client/airdrop/loot",
|
"/client/airdrop/loot",
|
||||||
async (url: string, info: any, sessionID: string, _output: string): Promise<string> =>
|
async (
|
||||||
|
url: string,
|
||||||
|
info: any,
|
||||||
|
sessionID: string,
|
||||||
|
output: string,
|
||||||
|
): Promise<IGetBodyResponseData<IGetAirdropLootResponse>> =>
|
||||||
{
|
{
|
||||||
return this.locationCallbacks.getAirdropLoot(url, info, sessionID);
|
return this.locationCallbacks.getAirdropLoot(url, info, sessionID);
|
||||||
},
|
},
|
||||||
|
164
project/src/services/AirdropService.ts
Normal file
164
project/src/services/AirdropService.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import { inject, injectable } from "tsyringe";
|
||||||
|
import { LootGenerator } from "@spt/generators/LootGenerator";
|
||||||
|
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||||
|
import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper";
|
||||||
|
import { Item } from "@spt/models/eft/common/tables/IItem";
|
||||||
|
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
||||||
|
import { AirdropTypeEnum } from "@spt/models/enums/AirdropType";
|
||||||
|
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||||
|
import { ItemTpl } from "@spt/models/enums/ItemTpl";
|
||||||
|
import { AirdropLoot, IAirdropConfig } from "@spt/models/spt/config/IAirdropConfig";
|
||||||
|
import { LootRequest } from "@spt/models/spt/services/LootRequest";
|
||||||
|
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||||
|
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||||
|
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||||
|
import { ItemFilterService } from "@spt/services/ItemFilterService";
|
||||||
|
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||||
|
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||||
|
import { HashUtil } from "@spt/utils/HashUtil";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class AirdropService
|
||||||
|
{
|
||||||
|
protected airdropConfig: IAirdropConfig;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@inject("PrimaryLogger") protected logger: ILogger,
|
||||||
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||||
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||||
|
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
||||||
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
|
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
||||||
|
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
||||||
|
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||||
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
|
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle client/location/getAirdropLoot
|
||||||
|
* Get loot for an airdrop container
|
||||||
|
* Generates it randomly based on config/airdrop.json values
|
||||||
|
* @returns Array of LootItem objects
|
||||||
|
*/
|
||||||
|
public generateAirdropLoot(): IGetAirdropLootResponse
|
||||||
|
{
|
||||||
|
const airdropType = this.chooseAirdropType();
|
||||||
|
this.logger.debug(`Chose ${airdropType} for airdrop loot`);
|
||||||
|
|
||||||
|
// Common/weapon/etc
|
||||||
|
const airdropConfig = this.getAirdropLootConfigByType(airdropType);
|
||||||
|
|
||||||
|
// generate loot to put into airdrop crate
|
||||||
|
const crateLoot = this.lootGenerator.createRandomLoot(airdropConfig);
|
||||||
|
|
||||||
|
// Create airdrop crate and add to result in first spot
|
||||||
|
const airdropCrateItem = this.getAirdropCrateItem(airdropType);
|
||||||
|
|
||||||
|
// Add crate to front of array
|
||||||
|
crateLoot.unshift(airdropCrateItem);
|
||||||
|
|
||||||
|
// Reparent loot items to create we added above
|
||||||
|
for (const item of crateLoot)
|
||||||
|
{
|
||||||
|
if (item._id == airdropCrateItem._id)
|
||||||
|
{
|
||||||
|
// Crate itself, don't alter
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no parentId = root item, make item have create as parent
|
||||||
|
if (!item.parentId)
|
||||||
|
{
|
||||||
|
item.parentId = airdropCrateItem._id;
|
||||||
|
item.slotId = "main";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { icon: airdropType, container: crateLoot };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a container create item based on passed in airdrop type
|
||||||
|
* @param airdropType What tpye of container: weapon/common etc
|
||||||
|
* @returns Item
|
||||||
|
*/
|
||||||
|
protected getAirdropCrateItem(airdropType: AirdropTypeEnum): Item
|
||||||
|
{
|
||||||
|
const airdropContainer = {
|
||||||
|
_id: this.hashUtil.generate(),
|
||||||
|
_tpl: "", // picked later
|
||||||
|
upd: {
|
||||||
|
SpawnedInSession: true,
|
||||||
|
StackObjectsCount: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (airdropType)
|
||||||
|
{
|
||||||
|
case AirdropTypeEnum.MEDICAL:
|
||||||
|
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_MEDICAL_CRATE;
|
||||||
|
break;
|
||||||
|
case AirdropTypeEnum.SUPPLY:
|
||||||
|
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_SUPPLY_CRATE;
|
||||||
|
break;
|
||||||
|
case AirdropTypeEnum.WEAPON:
|
||||||
|
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_WEAPON_CRATE;
|
||||||
|
break;
|
||||||
|
case AirdropTypeEnum.COMMON:
|
||||||
|
default:
|
||||||
|
airdropContainer._tpl = ItemTpl.LOOTCONTAINER_AIRDROP_COMMON_SUPPLY_CRATE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return airdropContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: AirdropLoot = this.airdropConfig.loot[airdropType];
|
||||||
|
if (!lootSettingsByType)
|
||||||
|
{
|
||||||
|
this.logger.error(
|
||||||
|
this.localisationService.getText("location-unable_to_find_airdrop_drop_config_of_type", airdropType),
|
||||||
|
);
|
||||||
|
lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.COMMON];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
weaponPresetCount: lootSettingsByType.weaponPresetCount,
|
||||||
|
armorPresetCount: lootSettingsByType.armorPresetCount,
|
||||||
|
itemCount: lootSettingsByType.itemCount,
|
||||||
|
weaponCrateCount: lootSettingsByType.weaponCrateCount,
|
||||||
|
itemBlacklist: [
|
||||||
|
...lootSettingsByType.itemBlacklist,
|
||||||
|
...this.itemFilterService.getItemRewardBlacklist(),
|
||||||
|
...this.itemFilterService.getBossItems(),
|
||||||
|
],
|
||||||
|
itemTypeWhitelist: lootSettingsByType.itemTypeWhitelist,
|
||||||
|
itemLimits: lootSettingsByType.itemLimits,
|
||||||
|
itemStackLimits: lootSettingsByType.itemStackLimits,
|
||||||
|
armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist,
|
||||||
|
allowBossItems: lootSettingsByType.allowBossItems,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user