More bot generation implementation
This commit is contained in:
@@ -1,11 +1,53 @@
|
||||
using SptCommon.Annotations;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Utils;
|
||||
using Core.Helpers;
|
||||
using Core.Services;
|
||||
using Core.Servers;
|
||||
using Core.Models.Spt.Config;
|
||||
|
||||
namespace Core.Generators;
|
||||
|
||||
[Injectable]
|
||||
public class PMCLootGenerator()
|
||||
public class PMCLootGenerator
|
||||
{
|
||||
private readonly ISptLogger<PMCLootGenerator> _logger;
|
||||
private readonly DatabaseService _databaseService;
|
||||
private readonly ItemHelper _itemHelper;
|
||||
private readonly ItemFilterService _itemFilterService;
|
||||
private readonly RagfairPriceService _ragfairPriceService;
|
||||
private readonly SeasonalEventService _seasonalEventService;
|
||||
private readonly WeightedRandomHelper _weightedRandomHelper;
|
||||
private readonly ConfigServer _configServer;
|
||||
|
||||
private Dictionary<string, double>? _backpackLootPool;
|
||||
private Dictionary<string, double>? _pocketLootPool;
|
||||
private Dictionary<string, double>? _vestLootPool;
|
||||
private readonly PmcConfig _pmcConfig;
|
||||
|
||||
public PMCLootGenerator(
|
||||
ISptLogger<PMCLootGenerator> logger,
|
||||
DatabaseService databaseService,
|
||||
ItemHelper itemHelper,
|
||||
ItemFilterService itemFilterService,
|
||||
RagfairPriceService ragfairPriceService,
|
||||
SeasonalEventService seasonalEventService,
|
||||
WeightedRandomHelper weightedRandomHelper,
|
||||
ConfigServer configServer
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_databaseService = databaseService;
|
||||
_itemHelper = itemHelper;
|
||||
_itemFilterService = itemFilterService;
|
||||
_ragfairPriceService = ragfairPriceService;
|
||||
_seasonalEventService = seasonalEventService;
|
||||
_weightedRandomHelper = weightedRandomHelper;
|
||||
_configServer = configServer;
|
||||
|
||||
_pmcConfig = _configServer.GetConfig<PmcConfig>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a List of loot items a PMC can have in their pockets
|
||||
@@ -14,7 +56,76 @@ public class PMCLootGenerator()
|
||||
/// <returns>Dictionary of string and number</returns>
|
||||
public Dictionary<string, double> GeneratePMCPocketLootPool(string botRole)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Hydrate loot dictionary if empty
|
||||
if (_pocketLootPool is null)
|
||||
{
|
||||
var items = _databaseService.GetItems();
|
||||
var pmcPriceOverrides =
|
||||
_databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.Pockets;
|
||||
|
||||
var allowedItemTypeWhitelist = _pmcConfig.PocketLoot.Whitelist;
|
||||
|
||||
var blacklist = GetLootBlacklist();
|
||||
|
||||
var itemsToAdd = items.Where(
|
||||
(item) =>
|
||||
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
|
||||
_itemHelper.isValidItem(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent) &&
|
||||
ItemFitsInto1By2Slot(item.Value)
|
||||
);
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd)
|
||||
{
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
_pocketLootPool[tpl] = pmcPriceOverrides[tpl];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set price of item as its weight
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_pocketLootPool[tpl] = price;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _pocketLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _pocketLootPool)
|
||||
{
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_pocketLootPool[key] = Math.Round((1 / _pocketLootPool[key]) * highestPrice);
|
||||
}
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_pocketLootPool);
|
||||
}
|
||||
|
||||
return _pocketLootPool;
|
||||
}
|
||||
|
||||
private HashSet<string> GetLootBlacklist()
|
||||
{
|
||||
var blacklist = new HashSet<string>();
|
||||
foreach (var blacklistedItem in _pmcConfig.PocketLoot.Blacklist)
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _pmcConfig.GlobalLootBlacklist)
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _itemFilterService.GetBlacklistedItems())
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _seasonalEventService.GetInactiveSeasonalEventItems())
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -24,7 +135,52 @@ public class PMCLootGenerator()
|
||||
/// <returns>Dictionary of string and number</returns>
|
||||
public Dictionary<string, double> GeneratePMCVestLootPool(string botRole)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Hydrate loot dictionary if empty
|
||||
if (_vestLootPool is null)
|
||||
{
|
||||
var items = _databaseService.GetItems();
|
||||
var pmcPriceOverrides =
|
||||
_databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.TacticalVest;
|
||||
|
||||
var allowedItemTypeWhitelist = _pmcConfig.VestLoot.Whitelist;
|
||||
|
||||
var blacklist = GetLootBlacklist();
|
||||
|
||||
var itemsToAdd = items.Where(
|
||||
(item) =>
|
||||
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
|
||||
_itemHelper.isValidItem(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent) &&
|
||||
ItemFitsInto2By2Slot(item.Value));
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd)
|
||||
{
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
_vestLootPool[tpl] = pmcPriceOverrides[tpl];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set price of item as its weight
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_vestLootPool[tpl] = price;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _vestLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _vestLootPool)
|
||||
{
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_vestLootPool[key] = Math.Round((1 / _vestLootPool[key]) * highestPrice);
|
||||
}
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_vestLootPool);
|
||||
}
|
||||
|
||||
return _vestLootPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +191,7 @@ public class PMCLootGenerator()
|
||||
/// <returns>true if it fits</returns>
|
||||
protected bool ItemFitsInto2By2Slot(TemplateItem item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return item.Properties.Width <= 2 && item.Properties.Height <= 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -46,7 +202,11 @@ public class PMCLootGenerator()
|
||||
/// <returns>true if it fits</returns>
|
||||
protected bool ItemFitsInto1By2Slot(TemplateItem item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return $"{item.Properties.Width}x{item.Properties.Height}" switch
|
||||
{
|
||||
"1x1" or "1x2" or "2x1" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,6 +216,48 @@ public class PMCLootGenerator()
|
||||
/// <returns>Dictionary of string and number</returns>
|
||||
public Dictionary<string, double> GeneratePMCBackpackLootPool(string botRole)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Hydrate loot dictionary if empty
|
||||
if (_backpackLootPool is null)
|
||||
{
|
||||
var items = _databaseService.GetItems();
|
||||
var pmcPriceOverrides =
|
||||
_databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.Backpack;
|
||||
|
||||
var allowedItemTypeWhitelist = _pmcConfig.BackpackLoot.Whitelist;
|
||||
|
||||
var blacklist = GetLootBlacklist();
|
||||
|
||||
var itemsToAdd = items.Where(
|
||||
(item) =>
|
||||
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
|
||||
_itemHelper.isValidItem(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent));
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd) {
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
_backpackLootPool[tpl] = pmcPriceOverrides[tpl];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set price of item as its weight
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_backpackLootPool[tpl] = price;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _backpackLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _backpackLootPool) {
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_backpackLootPool[key] = Math.Round((1 / _backpackLootPool[key]) * highestPrice);
|
||||
}
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_backpackLootPool);
|
||||
}
|
||||
|
||||
return _backpackLootPool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Core.Models.Common;
|
||||
using Core.Models.Common;
|
||||
|
||||
namespace Core.Models.Spt.Config;
|
||||
|
||||
@@ -136,7 +136,7 @@ public record Dynamic
|
||||
|
||||
[JsonPropertyName("itemPriceMultiplier")]
|
||||
/** A multipler to apply to individual tpls price just prior to item quality adjustment */
|
||||
public Dictionary<string, double> ItemPriceMultiplier { get; set; }
|
||||
public Dictionary<string, double>? ItemPriceMultiplier { get; set; }
|
||||
|
||||
[JsonPropertyName("_currencies")]
|
||||
public string? CurrenciesDescription { get; set; }
|
||||
|
||||
@@ -14,9 +14,11 @@ public class ItemFilterService(
|
||||
ConfigServer _configServer
|
||||
)
|
||||
{
|
||||
protected HashSet<string> _lootableItemBlacklistCache = [];
|
||||
protected ItemConfig _itemConfig = _configServer.GetConfig<ItemConfig>();
|
||||
|
||||
protected HashSet<string>? _lootableItemBlacklistCache;
|
||||
protected HashSet<string>? _itemBlacklistCache;
|
||||
|
||||
/**
|
||||
* Check if the provided template id is blacklisted in config/item.json/blacklist
|
||||
* @param tpl template id
|
||||
@@ -104,12 +106,9 @@ public class ItemFilterService(
|
||||
|
||||
public bool IsLootableItemBlacklisted(string itemKey)
|
||||
{
|
||||
if (_lootableItemBlacklistCache.Count == 0)
|
||||
if (_lootableItemBlacklistCache is null)
|
||||
{
|
||||
foreach (var item in _itemConfig.LootableItemBlacklist)
|
||||
{
|
||||
_lootableItemBlacklistCache.Add(item);
|
||||
}
|
||||
HydrateLootableItemBlacklist();
|
||||
}
|
||||
|
||||
return _lootableItemBlacklistCache.Contains(itemKey);
|
||||
@@ -117,6 +116,27 @@ public class ItemFilterService(
|
||||
|
||||
public bool IsItemBlacklisted(string tpl)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (_itemBlacklistCache is null)
|
||||
{
|
||||
HydrateBlacklist();
|
||||
}
|
||||
|
||||
return _itemBlacklistCache.Contains(tpl);
|
||||
}
|
||||
|
||||
protected void HydrateLootableItemBlacklist()
|
||||
{
|
||||
foreach (var item in _itemConfig.LootableItemBlacklist)
|
||||
{
|
||||
_lootableItemBlacklistCache.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
protected void HydrateBlacklist()
|
||||
{
|
||||
_itemBlacklistCache = [];
|
||||
foreach (var item in _itemConfig.Blacklist) {
|
||||
_itemBlacklistCache.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Models.Spt.Ragfair;
|
||||
using Core.Models.Utils;
|
||||
using Core.Models.Enums;
|
||||
using System;
|
||||
using Core.Servers;
|
||||
|
||||
namespace Core.Services;
|
||||
|
||||
@@ -12,9 +15,15 @@ namespace Core.Services;
|
||||
public class RagfairPriceService(
|
||||
ISptLogger<RagfairPriceService> _logger,
|
||||
HandbookHelper _handbookHelper,
|
||||
DatabaseService _databaseService
|
||||
TraderHelper _traderHelper,
|
||||
PresetHelper _presetHelper,
|
||||
ItemHelper _itemHelper,
|
||||
DatabaseService _databaseService,
|
||||
ConfigServer _configServer
|
||||
)
|
||||
{
|
||||
RagfairConfig _ragfairConfig = _configServer.GetConfig<RagfairConfig>();
|
||||
|
||||
protected RagfairServerPrices _prices = new RagfairServerPrices
|
||||
{ StaticPrices = new Dictionary<string, double>(), DynamicPrices = new Dictionary<string, double>() };
|
||||
|
||||
@@ -152,7 +161,80 @@ public class RagfairPriceService(
|
||||
/// <returns></returns>
|
||||
public double GetDynamicItemPrice(string itemTemplateId, string desiredCurrency, Item item = null, List<Item> offerItems = null, bool? isPackOffer = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var isPreset = false;
|
||||
var price = GetFleaPriceForItem(itemTemplateId);
|
||||
|
||||
// Adjust price if below handbook price, based on config.
|
||||
if (_ragfairConfig.Dynamic.OfferAdjustment.AdjustPriceWhenBelowHandbookPrice)
|
||||
{
|
||||
price = AdjustPriceIfBelowHandbook(price, itemTemplateId);
|
||||
}
|
||||
|
||||
// Use trader price if higher, based on config.
|
||||
if (_ragfairConfig.Dynamic.UseTraderPriceForOffersIfHigher)
|
||||
{
|
||||
var traderPrice = _traderHelper.GetHighestSellToTraderPrice(itemTemplateId);
|
||||
if (traderPrice > price)
|
||||
{
|
||||
price = traderPrice;
|
||||
}
|
||||
}
|
||||
|
||||
// Prices for weapon presets are handled differently.
|
||||
if (
|
||||
item?.Upd?.SptPresetId is not null &&
|
||||
offerItems is not null &&
|
||||
_presetHelper.IsPresetBaseClass(item.Upd.SptPresetId, BaseClasses.WEAPON)
|
||||
)
|
||||
{
|
||||
price = GetWeaponPresetPrice(item, offerItems, price);
|
||||
isPreset = true;
|
||||
}
|
||||
|
||||
// Check for existence of manual price adjustment multiplier
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(itemTemplateId, out var multiplier))
|
||||
{
|
||||
price *= multiplier;
|
||||
}
|
||||
|
||||
// The quality of the item affects the price + not on the ignore list
|
||||
if (item is not null && !_ragfairConfig.Dynamic.IgnoreQualityPriceVarianceBlacklist.Contains(itemTemplateId))
|
||||
{
|
||||
var qualityModifier = _itemHelper.GetItemQualityModifier(item);
|
||||
price *= qualityModifier;
|
||||
}
|
||||
|
||||
// Make adjustments for unreasonably priced items.
|
||||
foreach (var (key, value) in _ragfairConfig.Dynamic.UnreasonableModPrices)
|
||||
{
|
||||
if (!_itemHelper.IsOfBaseclass(itemTemplateId, key) || !value.Enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
price = AdjustUnreasonablePrice(
|
||||
_databaseService.GetHandbook().Items,
|
||||
value,
|
||||
itemTemplateId,
|
||||
price);
|
||||
}
|
||||
|
||||
// Vary the price based on the type of offer.
|
||||
var range = GetOfferTypeRangeValues(isPreset, isPackOffer ?? false);
|
||||
price = RandomiseOfferPrice(price, range);
|
||||
|
||||
// Convert to different currency if required.
|
||||
var roublesId = Money.ROUBLES;
|
||||
if (desiredCurrency != roublesId)
|
||||
{
|
||||
price = _handbookHelper.FromRUB(price, desiredCurrency);
|
||||
}
|
||||
|
||||
if (price < 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,11 +245,11 @@ public class RagfairPriceService(
|
||||
/// <param name="itemTpl">Item being adjusted</param>
|
||||
/// <param name="price">Current price of item</param>
|
||||
/// <returns>Adjusted price of item</returns>
|
||||
protected decimal AdjustUnreasonablePrice(
|
||||
protected double AdjustUnreasonablePrice(
|
||||
List<HandbookItem> handbookPrices,
|
||||
UnreasonableModPrices unreasonableItemChange,
|
||||
string itemTpl,
|
||||
decimal price)
|
||||
double price)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -189,7 +271,7 @@ public class RagfairPriceService(
|
||||
/// <param name="itemPrice">price of item</param>
|
||||
/// <param name="itemTpl">item template Id being checked</param>
|
||||
/// <returns>adjusted price value in roubles</returns>
|
||||
protected decimal AdjustPriceIfBelowHandbook(decimal itemPrice, string itemTpl)
|
||||
protected double AdjustPriceIfBelowHandbook(double itemPrice, string itemTpl)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -200,7 +282,7 @@ public class RagfairPriceService(
|
||||
/// <param name="existingPrice">price to alter</param>
|
||||
/// <param name="rangeValues">min and max to adjust price by</param>
|
||||
/// <returns>multiplied price</returns>
|
||||
protected decimal RandomiseOfferPrice(decimal existingPrice, MinMax rangeValues)
|
||||
protected double RandomiseOfferPrice(double existingPrice, MinMax rangeValues)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -212,7 +294,7 @@ public class RagfairPriceService(
|
||||
/// <param name="weaponWithChildren">weapon plus mods</param>
|
||||
/// <param name="existingPrice">price of existing base weapon</param>
|
||||
/// <returns>price of weapon in roubles</returns>
|
||||
protected decimal GetWeaponPresetPrice(Item weaponRootItem, List<Item> weaponWithChildren, decimal existingPrice)
|
||||
protected double GetWeaponPresetPrice(Item weaponRootItem, List<Item> weaponWithChildren, double existingPrice)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user