implement method in botequipgen

This commit is contained in:
CWX
2025-01-16 10:36:59 +00:00
parent a2ec0e0f5b
commit 255034786a
2 changed files with 356 additions and 43 deletions
+355 -42
View File
@@ -21,41 +21,65 @@ public class BotEquipmentModGenerator
private readonly ISptLogger<BotEquipmentModGenerator> _logger;
private readonly HashUtil _hashUtil;
private readonly RandomUtil _randomUtil;
private readonly ItemHelper _itemHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly BotEquipmentModPoolService _botEquipmentModPoolService;
private readonly PresetHelper _presetHelper;
private readonly ProbabilityHelper _probabilityHelper;
private readonly LocalisationService _localisationService;
private readonly DatabaseService _databaseService;
private readonly ItemHelper _itemHelper;
private readonly BotEquipmentFilterService _botEquipmentFilterService;
private readonly ItemFilterService _itemFilterService;
private readonly ProfileHelper _profileHelper;
private readonly BotWeaponModLimitService _botWeaponModLimitService;
private readonly BotHelper _botHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly BotWeaponGeneratorHelper _botWeaponGeneratorHelper;
private readonly WeightedRandomHelper _weightedRandomHelper;
private readonly PresetHelper _presetHelper;
private readonly LocalisationService _localisationService;
private readonly BotEquipmentModPoolService _botEquipmentModPoolService;
private readonly ConfigServer _configServer;
private readonly ICloner _cloner;
private BotConfig _botConfig;
public BotEquipmentModGenerator(
public BotEquipmentModGenerator
(
ISptLogger<BotEquipmentModGenerator> logger,
HashUtil hashUtil,
RandomUtil randomUtil,
ItemHelper itemHelper,
BotGeneratorHelper botGeneratorHelper,
BotEquipmentModPoolService botEquipmentModPoolService,
PresetHelper presetHelper,
ProbabilityHelper probabilityHelper,
LocalisationService localisationService,
DatabaseService databaseService,
ItemHelper itemHelper,
BotEquipmentFilterService botEquipmentFilterService,
ItemFilterService itemFilterService,
ProfileHelper profileHelper,
BotWeaponModLimitService botWeaponModLimitService,
BotHelper botHelper,
BotGeneratorHelper botGeneratorHelper,
BotWeaponGeneratorHelper botWeaponGeneratorHelper,
WeightedRandomHelper weightedRandomHelper,
PresetHelper presetHelper,
LocalisationService localisationService,
BotEquipmentModPoolService botEquipmentModPoolService,
ConfigServer configServer,
ICloner cloner)
ICloner cloner
)
{
_logger = logger;
_hashUtil = hashUtil;
_randomUtil = randomUtil;
_itemHelper = itemHelper;
_botGeneratorHelper = botGeneratorHelper;
_botEquipmentModPoolService = botEquipmentModPoolService;
_presetHelper = presetHelper;
_probabilityHelper = probabilityHelper;
_localisationService = localisationService;
_databaseService = databaseService;
_itemHelper = itemHelper;
_botEquipmentFilterService = botEquipmentFilterService;
_itemFilterService = itemFilterService;
_profileHelper = profileHelper;
_botWeaponModLimitService = botWeaponModLimitService;
_botHelper = botHelper;
_botGeneratorHelper = botGeneratorHelper;
_botWeaponGeneratorHelper = botWeaponGeneratorHelper;
_weightedRandomHelper = weightedRandomHelper;
_presetHelper = presetHelper;
_localisationService = localisationService;
_botEquipmentModPoolService = botEquipmentModPoolService;
_configServer = configServer;
_cloner = cloner;
@@ -76,12 +100,12 @@ public class BotEquipmentModGenerator
EquipmentFilterDetails specificBlacklist, bool shouldForceSpawn = false)
{
var forceSpawn = shouldForceSpawn;
// Get mod pool for the desired item
var compatibleModsPool = settings.ModPool[parentTemplate.Id];
if (compatibleModsPool is null)
{
_logger.Warning($"bot: { settings.BotData.Role} lacks a mod slot pool for item: { parentTemplate.Id} { parentTemplate.Name}");
_logger.Warning($"bot: {settings.BotData.Role} lacks a mod slot pool for item: {parentTemplate.Id} {parentTemplate.Name}");
}
// Iterate over mod pool and choose mods to add to item
@@ -92,12 +116,18 @@ public class BotEquipmentModGenerator
var itemSlotTemplate = GetModItemSlotFromDb(modSlotName, parentTemplate);
if (itemSlotTemplate is null)
{
_logger.Error(_localisationService.GetText("bot-mod_slot_missing_from_item", new {
modSlot = modSlotName,
parentId= parentTemplate.Id,
parentName= parentTemplate.Name,
botRole= settings.BotData.Role
}));
_logger.Error(
_localisationService.GetText(
"bot-mod_slot_missing_from_item",
new
{
modSlot = modSlotName,
parentId = parentTemplate.Id,
parentName = parentTemplate.Name,
botRole = settings.BotData.Role
}
)
);
continue;
}
@@ -106,7 +136,8 @@ public class BotEquipmentModGenerator
itemSlotTemplate,
modSlotName,
settings.SpawnChances.EquipmentModsChances,
settings.BotEquipmentConfig);
settings.BotEquipmentConfig
);
// Rolled to skip mod and it shouldnt be force-spawned
if (modSpawnResult == ModSpawn.SKIP && !forceSpawn)
@@ -141,7 +172,8 @@ public class BotEquipmentModGenerator
settings,
modSlotName.ToLower(),
compatibleModsPool[modSlotName],
parentTemplate);
parentTemplate
);
if (plateSlotFilteringOutcome.Result is Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER)
{
_logger.Debug($"Plate slot: {modSlotName} selection for armor: {parentTemplate.Id} failed: {plateSlotFilteringOutcome.Result}, skipping");
@@ -151,7 +183,9 @@ public class BotEquipmentModGenerator
if (plateSlotFilteringOutcome.Result == Result.LACKS_PLATE_WEIGHTS)
{
_logger.Warning($"Plate slot: {modSlotName} lacks weights for armor: { parentTemplate.Id}, unable to adjust plate choice, using existing data");
_logger.Warning(
$"Plate slot: {modSlotName} lacks weights for armor: {parentTemplate.Id}, unable to adjust plate choice, using existing data"
);
}
// Replace mod pool with pool of chosen plate items
@@ -165,7 +199,8 @@ public class BotEquipmentModGenerator
while (exhaustableModPool.HasValues())
{
modTpl = exhaustableModPool.GetRandomValue();
if (modTpl is not null && !_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).Incompatible.GetValueOrDefault(false))
if (modTpl is not null &&
!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).Incompatible.GetValueOrDefault(false))
{
found = true;
break;
@@ -193,7 +228,8 @@ public class BotEquipmentModGenerator
itemSlotTemplate,
modSlotName,
parentTemplate,
settings.BotData.Role)
settings.BotData.Role
)
)
{
continue;
@@ -202,7 +238,8 @@ public class BotEquipmentModGenerator
// Generate new id to ensure all items are unique on bot
var modId = _hashUtil.Generate();
equipment.Add(
CreateModItem(modId, modTpl, parentId, modSlotName, modTemplate.Value, settings.BotData.Role));
CreateModItem(modId, modTpl, parentId, modSlotName, modTemplate.Value, settings.BotData.Role)
);
// Does item being added exist in mod pool - has its own mod pool
if (settings.ModPool.ContainsKey(modTpl))
@@ -214,7 +251,8 @@ public class BotEquipmentModGenerator
modTemplate.Value,
settings,
specificBlacklist,
forceSpawn);
forceSpawn
);
}
}
@@ -243,7 +281,275 @@ public class BotEquipmentModGenerator
/// <returns>Weapon + mods array</returns>
public List<Item> GenerateModsForWeapon(string sessionId, GenerateWeaponRequest request)
{
throw new NotImplementedException();
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
// Get pool of mods that fit weapon
var compatibleModsPool = request.ModPool[request.ParentTemplate.Id];
if (
!(
request.ParentTemplate.Properties.Slots.Any() ||
request.ParentTemplate.Properties.Cartridges.Any() ||
request.ParentTemplate.Properties.Chambers.Any()
)
)
{
_logger.Error(
_localisationService.GetText(
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot",
new
{
WeaponName = request.ParentTemplate.Name,
WeaponId = request.ParentTemplate.Id,
BotRole = request.BotData.Role,
}
)
);
return request.Weapon;
}
var botEquipConfig = _botConfig.Equipment[request.BotData.EquipmentRole];
var botEquipBlacklist = _botEquipmentFilterService.GetBotEquipmentBlacklist(
request.BotData.EquipmentRole,
pmcProfile.Info.Level ?? 0
);
var botWeaponSightWhitelist = _botEquipmentFilterService.GetBotWeaponSightWhitelist(
request.BotData.EquipmentRole
);
var randomisationSettings = _botHelper.GetBotRandomizationDetails(request.BotData.Level ?? 0, botEquipConfig);
// Iterate over mod pool and choose mods to attach
var sortedModKeys = SortModKeys(compatibleModsPool.Keys.ToList(), request.ParentTemplate.Id);
foreach (var modSlot in sortedModKeys)
{
// Check weapon has slot for mod to fit in
var modsParentSlot = GetModItemSlotFromDb(modSlot, request.ParentTemplate);
if (modsParentSlot is null)
{
_logger.Error(
_localisationService.GetText(
"bot-weapon_missing_mod_slot",
new
{
ModSlot = modSlot,
WeaponId = request.ParentTemplate.Id,
WeaponName = request.ParentTemplate.Name,
BotRole = request.BotData.Role,
}
)
);
continue;
}
// Check spawn chance of mod
var modSpawnResult = ShouldModBeSpawned(
modsParentSlot,
modSlot,
request.ModSpawnChances,
botEquipConfig
);
if (modSpawnResult == ModSpawn.SKIP)
{
continue;
}
var isRandomisableSlot = randomisationSettings?.RandomisedWeaponModSlots?.Contains(modSlot) ?? false;
ModToSpawnRequest modToSpawnRequest = new()
{
ModSlot = modSlot,
IsRandomisableSlot = isRandomisableSlot,
RandomisationSettings = randomisationSettings,
BotWeaponSightWhitelist = botWeaponSightWhitelist,
BotEquipBlacklist = botEquipBlacklist,
ItemModPool = compatibleModsPool,
Weapon = request.Weapon,
AmmoTpl = request.AmmoTpl,
ParentTemplate = request.ParentTemplate,
ModSpawnResult = modSpawnResult,
WeaponStats = request.WeaponStats,
ConflictingItemTpls = request.ConflictingItemTpls,
BotData = request.BotData
};
var modToAdd = ChooseModToPutIntoSlot(modToSpawnRequest);
// Compatible mod not found
if (modToAdd is null)
{
continue;
}
if (
IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role)
)
{
continue;
}
var modToAddTemplate = modToAdd.Value;
// Skip adding mod to weapon if type limit reached
if (
_botWeaponModLimitService.WeaponModHasReachedLimit(
request.BotData.EquipmentRole,
modToAddTemplate.Value,
request.ModLimits,
request.ParentTemplate,
request.Weapon
)
)
{
continue;
}
// If item is a mount for scopes, set scope chance to 100%, this helps fix empty mounts appearing on weapons
if (ModSlotCanHoldScope(modSlot, modToAddTemplate.Value.Parent))
{
// mod_mount was picked to be added to weapon, force scope chance to ensure its filled
List<string> scopeSlots = ["mod_scope", "mod_scope_000", "mod_scope_001", "mod_scope_002", "mod_scope_003"];
AdjustSlotSpawnChances(request.ModSpawnChances, scopeSlots, 100);
// Hydrate pool of mods that fit into mount as its a randomisable slot
if (isRandomisableSlot)
{
// Add scope mods to modPool dictionary to ensure the mount has a scope in the pool to pick
AddCompatibleModsForProvidedMod(
"mod_scope",
modToAddTemplate.Value,
request.ModPool,
botEquipBlacklist
);
}
}
// If picked item is muzzle adapter that can hold a child, adjust spawn chance
if (ModSlotCanHoldMuzzleDevices(modSlot, modToAddTemplate.Value.Parent))
{
List<string> muzzleSlots = ["mod_muzzle", "mod_muzzle_000", "mod_muzzle_001"];
// Make chance of muzzle devices 95%, nearly certain but not guaranteed
AdjustSlotSpawnChances(request.ModSpawnChances, muzzleSlots, 95);
}
// If front/rear sight are to be added, set opposite to 100% chance
if (ModIsFrontOrRearSight(modSlot, modToAddTemplate.Value.Id))
{
request.ModSpawnChances["mod_sight_front"] = 100;
request.ModSpawnChances["mod_sight_rear"] = 100;
}
// Handguard mod can take a sub handguard mod + weapon has no UBGL (takes same slot)
// Force spawn chance to be 100% to ensure it gets added
if (
modSlot == "mod_handguard" &&
modToAddTemplate.Value.Properties.Slots.Any((slot) => slot.Name == "mod_handguard") &&
!request.Weapon.Any((item) => item.SlotId == "mod_launcher")
)
{
// Needed for handguards with lower
request.ModSpawnChances["mod_handguard"] = 100;
}
// If stock mod can take a sub stock mod, force spawn chance to be 100% to ensure sub-stock gets added
// Or if bot has stock force enabled
if (ShouldForceSubStockSlots(modSlot, botEquipConfig, modToAddTemplate.Value))
{
// Stock mod can take additional stocks, could be a locking device, force 100% chance
List<string> subStockSlots = ["mod_stock", "mod_stock_000", "mod_stock_001", "mod_stock_akms"];
AdjustSlotSpawnChances(request.ModSpawnChances, subStockSlots, 100);
}
// Gather stats on mods being added to weapon
if (_itemHelper.IsOfBaseclass(modToAddTemplate.Value.Id, BaseClasses.IRON_SIGHT))
{
if (modSlot == "mod_sight_front")
{
request.WeaponStats.HasFrontIronSight = true;
}
else if (modSlot == "mod_sight_rear")
{
request.WeaponStats.HasRearIronSight = true;
}
}
else if (
!request.WeaponStats.HasOptic ??
false &&
_itemHelper.IsOfBaseclass(modToAddTemplate.Value.Id, BaseClasses.SIGHTS)
)
{
request.WeaponStats.HasOptic = true;
}
var modId = _hashUtil.Generate();
request.Weapon.Add(
CreateModItem(
modId,
modToAddTemplate.Value.Id,
request.WeaponId,
modSlot,
modToAddTemplate.Value,
request.BotData.Role
)
);
// Update conflicting item list now item has been chosen
foreach (var conflictingItem in modToAddTemplate.Value.Properties.ConflictingItems)
{
request.ConflictingItemTpls.Add(conflictingItem);
}
// I first thought we could use the recursive generateModsForItems as previously for cylinder magazines.
// However, the recursion doesn't go over the slots of the parent mod but over the modPool which is given by the bot config
// where we decided to keep cartridges instead of camoras. And since a CylinderMagazine only has one cartridge entry and
// this entry is not to be filled, we need a special handling for the CylinderMagazine
var modParentItem = _itemHelper.GetItem(modToAddTemplate.Value.Parent).Value;
if (_botWeaponGeneratorHelper.MagazineIsCylinderRelated(modParentItem.Name))
{
// We don't have child mods, we need to create the camoras for the magazines instead
FillCamora(request.Weapon, request.ModPool, modId, modToAddTemplate.Value);
}
else
{
var containsModInPool = request.ModPool.Keys.Contains(modToAddTemplate.Value.Id);
// Sometimes randomised slots are missing sub-mods, if so, get values from mod pool service
// Check for a randomisable slot + without data in modPool + item being added as additional slots
if (isRandomisableSlot && !containsModInPool && modToAddTemplate.Value.Properties.Slots.Any())
{
var modFromService = _botEquipmentModPoolService.GetModsForWeaponSlot(modToAddTemplate.Value.Id);
if (modFromService.Keys.Any())
{
request.ModPool[modToAddTemplate.Value.Id] = modFromService;
containsModInPool = true;
}
}
if (containsModInPool)
{
GenerateWeaponRequest recursiveRequestData = new()
{
Weapon = request.Weapon,
ModPool = request.ModPool,
WeaponId = modId,
ParentTemplate = modToAddTemplate.Value,
ModSpawnChances = request.ModSpawnChances,
AmmoTpl = request.AmmoTpl,
BotData = new()
{
Role = request.BotData.Role,
Level = request.BotData.Level,
EquipmentRole = request.BotData.EquipmentRole
},
ModLimits = request.ModLimits,
WeaponStats = request.WeaponStats,
ConflictingItemTpls = request.ConflictingItemTpls
};
// Call self recursively to add mods to this mod
GenerateModsForWeapon(sessionId, recursiveRequestData);
}
}
}
return request.Weapon;
}
/// <summary>
@@ -279,7 +585,7 @@ public class BotEquipmentModGenerator
return true;
}
return ((string[])["mod_sight_front", "mod_sight_rear"]).Contains(modSlot);
return ((string[]) ["mod_sight_front", "mod_sight_rear"]).Contains(modSlot);
}
/// <summary>
@@ -291,7 +597,8 @@ public class BotEquipmentModGenerator
public bool ModSlotCanHoldScope(string modSlot, string modsParentId)
{
return (
((string[])[
((string[])
[
"mod_scope",
"mod_mount",
"mod_mount_000",
@@ -299,7 +606,8 @@ public class BotEquipmentModGenerator
"mod_scope_001",
"mod_scope_002",
"mod_scope_003",
]).Contains(modSlot.ToLower()) && modsParentId == BaseClasses.MOUNT
]).Contains(modSlot.ToLower()) &&
modsParentId == BaseClasses.MOUNT
);
}
@@ -325,7 +633,8 @@ public class BotEquipmentModGenerator
return;
}
foreach (var modName in modSlotsToAdjust) {
foreach (var modName in modSlotsToAdjust)
{
modSpawnChances[modName] = newChancePercent;
}
}
@@ -338,7 +647,7 @@ public class BotEquipmentModGenerator
/// <returns>True if modSlot can have muzzle-related items</returns>
public bool ModSlotCanHoldMuzzleDevices(string modSlot, string? modsParentId)
{
return ((string[])["mod_muzzle", "mod_muzzle_000", "mod_muzzle_001"]).Contains(modSlot.ToLower());
return ((string[]) ["mod_muzzle", "mod_muzzle_000", "mod_muzzle_001"]).Contains(modSlot.ToLower());
}
/// <summary>
@@ -390,6 +699,7 @@ public class BotEquipmentModGenerator
// Always force mags/cartridges in weapon to spawn
return ModSpawn.SPAWN;
}
var spawnMod = _probabilityHelper.RollChance(modSpawnChances[modSlotName]);
if (!spawnMod && (slotRequired.GetValueOrDefault(false) || botEquipConfig.WeaponSlotIdsToMakeRequired.Contains(modSlotName)))
{
@@ -405,7 +715,7 @@ public class BotEquipmentModGenerator
/// </summary>
/// <param name="request">Data used to choose an appropriate mod with</param>
/// <returns>itemHelper.getItem() result</returns>
public object? ChooseModToPutIntoSlot(ModToSpawnRequest request) // TODO: type fuckery: [boolean, ITemplateItem] | undefined
public KeyValuePair<bool, TemplateItem>? ChooseModToPutIntoSlot(ModToSpawnRequest request) // TODO: type fuckery: [boolean, ITemplateItem] | undefined
{
throw new NotImplementedException();
}
@@ -558,12 +868,14 @@ public class BotEquipmentModGenerator
/// <returns>Item object</returns>
public Item CreateModItem(string modId, string modTpl, string parentId, string modSlot, TemplateItem modTemplate, string botRole)
{
return new Item {
return new Item
{
Id = modId,
Template = modTpl,
ParentId = parentId,
SlotId = modSlot,
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(modTemplate, botRole)};
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(modTemplate, botRole)
};
}
/// <summary>
@@ -614,7 +926,8 @@ public class BotEquipmentModGenerator
/// <param name="parentTemplate">Db template of the mods being added</param>
/// <param name="botRole">Bots wildspawntype (assault/pmcBot/exUsec etc)</param>
/// <returns>True if valid for slot</returns>
public bool IsModValidForSlot(KeyValuePair<bool, TemplateItem?> modToAdd, Slot slotAddedToTemplate, string modSlot, TemplateItem parentTemplate, string botRole)
public bool IsModValidForSlot(KeyValuePair<bool, TemplateItem>? modToAdd, Slot slotAddedToTemplate, string modSlot, TemplateItem parentTemplate,
string botRole)
{
throw new NotImplementedException();
}
+1 -1
View File
@@ -153,7 +153,7 @@ public class Info
public bool? HasCoopExtension { get; set; }
public bool? HasPveGame { get; set; }
public string? Voice { get; set; }
public int? Level { get; set; }
public double? Level { get; set; }
public double? Experience { get; set; }
[JsonConverter(typeof(StringToNumberFactoryConverter))]
public long? RegistrationDate { get; set; }