Format Style Fixes
This commit is contained in:
@@ -39,11 +39,7 @@ public class BotEquipmentModGenerator(
|
||||
ICloner cloner
|
||||
)
|
||||
{
|
||||
protected static readonly FrozenSet<string> _modSightIds =
|
||||
[
|
||||
"mod_sight_front",
|
||||
"mod_sight_rear",
|
||||
];
|
||||
protected static readonly FrozenSet<string> _modSightIds = ["mod_sight_front", "mod_sight_rear"];
|
||||
|
||||
// Slots that hold scopes
|
||||
protected static readonly FrozenSet<string> _scopeIds =
|
||||
@@ -58,21 +54,10 @@ public class BotEquipmentModGenerator(
|
||||
];
|
||||
|
||||
// Slots that hold muzzles
|
||||
protected static readonly FrozenSet<string> _muzzleIds =
|
||||
[
|
||||
"mod_muzzle",
|
||||
"mod_muzzle_000",
|
||||
"mod_muzzle_001",
|
||||
];
|
||||
protected static readonly FrozenSet<string> _muzzleIds = ["mod_muzzle", "mod_muzzle_000", "mod_muzzle_001"];
|
||||
|
||||
// Slots a weapon can store its stock in
|
||||
protected static readonly FrozenSet<string> _stockSlots =
|
||||
[
|
||||
"mod_stock",
|
||||
"mod_stock_000",
|
||||
"mod_stock_001",
|
||||
"mod_stock_akms",
|
||||
];
|
||||
protected static readonly FrozenSet<string> _stockSlots = ["mod_stock", "mod_stock_000", "mod_stock_001", "mod_stock_akms"];
|
||||
|
||||
// Slots that hold cartridges
|
||||
protected static readonly FrozenSet<string> _cartridgeHolderSlots =
|
||||
@@ -121,9 +106,7 @@ public class BotEquipmentModGenerator(
|
||||
// Get mod pool for the desired item
|
||||
if (!settings.ModPool.TryGetValue(parentTemplate.Id, out var compatibleModsPool))
|
||||
{
|
||||
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
|
||||
@@ -172,11 +155,7 @@ public class BotEquipmentModGenerator(
|
||||
var modPoolToChooseFrom = modPool;
|
||||
|
||||
// Filter the pool of items in blacklist
|
||||
var filteredModPool = FilterModsByBlacklist(
|
||||
modPoolToChooseFrom,
|
||||
specificBlacklist,
|
||||
modSlotName
|
||||
);
|
||||
var filteredModPool = FilterModsByBlacklist(modPoolToChooseFrom, specificBlacklist, modSlotName);
|
||||
if (filteredModPool.Count > 0)
|
||||
// use filtered pool as it has items in it
|
||||
{
|
||||
@@ -239,12 +218,7 @@ public class BotEquipmentModGenerator(
|
||||
// Compatible item not found but slot REQUIRES item, get random item from db
|
||||
if (!found && itemSlotTemplate.Required.GetValueOrDefault(false))
|
||||
{
|
||||
modTpl = GetRandomModTplFromItemDb(
|
||||
modTpl.Value,
|
||||
itemSlotTemplate,
|
||||
modSlotName,
|
||||
equipment
|
||||
);
|
||||
modTpl = GetRandomModTplFromItemDb(modTpl.Value, itemSlotTemplate, modSlotName, equipment);
|
||||
found = modTpl is not null;
|
||||
}
|
||||
|
||||
@@ -256,44 +230,20 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
// Get chosen mods db template and check it fits into slot
|
||||
var modTemplate = itemHelper.GetItem(modTpl.Value);
|
||||
if (
|
||||
!IsModValidForSlot(
|
||||
modTemplate,
|
||||
itemSlotTemplate,
|
||||
modSlotName,
|
||||
parentTemplate,
|
||||
settings.BotData.Role
|
||||
)
|
||||
)
|
||||
if (!IsModValidForSlot(modTemplate, itemSlotTemplate, modSlotName, parentTemplate, settings.BotData.Role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate new id to ensure all items are unique on bot
|
||||
var modId = new MongoId();
|
||||
equipment.Add(
|
||||
CreateModItem(
|
||||
modId,
|
||||
modTpl.Value,
|
||||
parentId,
|
||||
modSlotName,
|
||||
modTemplate.Value,
|
||||
settings.BotData.Role
|
||||
)
|
||||
);
|
||||
equipment.Add(CreateModItem(modId, modTpl.Value, 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.Value))
|
||||
// Call self again with mod being added as item to add child mods to
|
||||
{
|
||||
GenerateModsForEquipment(
|
||||
equipment,
|
||||
modId,
|
||||
modTemplate.Value,
|
||||
settings,
|
||||
specificBlacklist,
|
||||
forceSpawn
|
||||
);
|
||||
GenerateModsForEquipment(equipment, modId, modTemplate.Value, settings, specificBlacklist, forceSpawn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,11 +265,7 @@ public class BotEquipmentModGenerator(
|
||||
TemplateItem armorItem
|
||||
)
|
||||
{
|
||||
var result = new FilterPlateModsForSlotByLevelResult
|
||||
{
|
||||
Result = Result.UNKNOWN_FAILURE,
|
||||
PlateModTemplates = null,
|
||||
};
|
||||
var result = new FilterPlateModsForSlotByLevelResult { Result = Result.UNKNOWN_FAILURE, PlateModTemplates = null };
|
||||
|
||||
// Not pmc or not a plate slot, return original mod pool array
|
||||
if (!itemHelper.IsRemovablePlateSlot(modSlot))
|
||||
@@ -331,10 +277,8 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Get the front/back/side weights based on bots level
|
||||
var plateSlotWeights = settings.BotEquipmentConfig?.ArmorPlateWeighting.FirstOrDefault(
|
||||
armorWeight =>
|
||||
settings.BotData.Level >= armorWeight.LevelRange.Min
|
||||
&& settings.BotData.Level <= armorWeight.LevelRange.Max
|
||||
var plateSlotWeights = settings.BotEquipmentConfig?.ArmorPlateWeighting.FirstOrDefault(armorWeight =>
|
||||
settings.BotData.Level >= armorWeight.LevelRange.Min && settings.BotData.Level <= armorWeight.LevelRange.Max
|
||||
);
|
||||
|
||||
if (plateSlotWeights is null)
|
||||
@@ -360,14 +304,11 @@ public class BotEquipmentModGenerator(
|
||||
var chosenArmorPlateLevelString = weightedRandomHelper.GetWeightedValue(plateWeights);
|
||||
|
||||
// Convert the array of ids into database items
|
||||
var platesFromDb = existingPlateTplPool.Select(plateTpl =>
|
||||
itemHelper.GetItem(plateTpl).Value
|
||||
);
|
||||
var platesFromDb = existingPlateTplPool.Select(plateTpl => itemHelper.GetItem(plateTpl).Value);
|
||||
|
||||
// Filter plates to the chosen level based on its armorClass property
|
||||
var platesOfDesiredLevel = platesFromDb.Where(item =>
|
||||
item.Properties.ArmorClass.Value
|
||||
== double.Parse(chosenArmorPlateLevelString, CultureInfo.InvariantCulture)
|
||||
item.Properties.ArmorClass.Value == double.Parse(chosenArmorPlateLevelString, CultureInfo.InvariantCulture)
|
||||
);
|
||||
if (platesOfDesiredLevel.Any())
|
||||
{
|
||||
@@ -399,9 +340,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
findCompatiblePlateAttempts++;
|
||||
|
||||
platesOfDesiredLevel = platesFromDb.Where(item =>
|
||||
item.Properties.ArmorClass == chosenArmorPlateLevelDouble
|
||||
);
|
||||
platesOfDesiredLevel = platesFromDb.Where(item => item.Properties.ArmorClass == chosenArmorPlateLevelDouble);
|
||||
// Valid plates found, exit
|
||||
if (platesOfDesiredLevel.Any())
|
||||
{
|
||||
@@ -496,9 +435,7 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
var defaultPreset = presetHelper.GetDefaultPreset(armorItemTpl);
|
||||
|
||||
return defaultPreset?.Items?.FirstOrDefault(item =>
|
||||
string.Equals(item.SlotId, modSlot, StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
return defaultPreset?.Items?.FirstOrDefault(item => string.Equals(item.SlotId, modSlot, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -536,19 +473,11 @@ public class BotEquipmentModGenerator(
|
||||
request.BotData.EquipmentRole,
|
||||
pmcProfile?.Info?.Level ?? 0
|
||||
);
|
||||
var botWeaponSightWhitelist = botEquipmentFilterService.GetBotWeaponSightWhitelist(
|
||||
request.BotData.EquipmentRole
|
||||
);
|
||||
var randomisationSettings = botHelper.GetBotRandomizationDetails(
|
||||
request.BotData.Level ?? 0,
|
||||
botEquipConfig
|
||||
);
|
||||
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.ToHashSet(),
|
||||
request.ParentTemplate.Id
|
||||
);
|
||||
var sortedModKeys = SortModKeys(compatibleModsPool.Keys.ToHashSet(), request.ParentTemplate.Id);
|
||||
foreach (var modSlot in sortedModKeys)
|
||||
{
|
||||
// Check weapon has slot for mod to fit in
|
||||
@@ -572,28 +501,19 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// If the parent is a UBGL, the patron_in_weapon will be generated later - so skip it for now
|
||||
if (
|
||||
modSlot == "patron_in_weapon"
|
||||
&& itemHelper.IsOfBaseclass(request.ParentTemplate.Id, BaseClasses.LAUNCHER)
|
||||
)
|
||||
if (modSlot == "patron_in_weapon" && itemHelper.IsOfBaseclass(request.ParentTemplate.Id, BaseClasses.LAUNCHER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check spawn chance of mod
|
||||
var modSpawnResult = ShouldModBeSpawned(
|
||||
modsParentSlot,
|
||||
modSlot,
|
||||
request.ModSpawnChances,
|
||||
botEquipConfig
|
||||
);
|
||||
var modSpawnResult = ShouldModBeSpawned(modsParentSlot, modSlot, request.ModSpawnChances, botEquipConfig);
|
||||
if (modSpawnResult == ModSpawn.SKIP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isRandomisableSlot =
|
||||
randomisationSettings?.RandomisedWeaponModSlots?.Contains(modSlot) ?? false;
|
||||
var isRandomisableSlot = randomisationSettings?.RandomisedWeaponModSlots?.Contains(modSlot) ?? false;
|
||||
ModToSpawnRequest modToSpawnRequest = new()
|
||||
{
|
||||
ModSlot = modSlot,
|
||||
@@ -618,15 +538,7 @@ public class BotEquipmentModGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!IsModValidForSlot(
|
||||
modToAdd,
|
||||
modsParentSlot,
|
||||
modSlot,
|
||||
request.ParentTemplate,
|
||||
request.BotData.Role
|
||||
)
|
||||
)
|
||||
if (!IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -650,26 +562,14 @@ public class BotEquipmentModGenerator(
|
||||
if (ModSlotCanHoldScope(modSlot, modToAddTemplate.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",
|
||||
];
|
||||
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,
|
||||
request.ModPool,
|
||||
botEquipBlacklist
|
||||
);
|
||||
AddCompatibleModsForProvidedMod("mod_scope", modToAddTemplate, request.ModPool, botEquipBlacklist);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,13 +606,7 @@ public class BotEquipmentModGenerator(
|
||||
if (ShouldForceSubStockSlots(modSlot, botEquipConfig, modToAddTemplate))
|
||||
{
|
||||
// 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",
|
||||
];
|
||||
List<string> subStockSlots = ["mod_stock", "mod_stock_000", "mod_stock_001", "mod_stock_akms"];
|
||||
AdjustSlotSpawnChances(request.ModSpawnChances, subStockSlots, 100);
|
||||
}
|
||||
|
||||
@@ -728,24 +622,14 @@ public class BotEquipmentModGenerator(
|
||||
request.WeaponStats.HasRearIronSight = true;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
!(request.WeaponStats.HasOptic ?? false)
|
||||
&& itemHelper.IsOfBaseclass(modToAddTemplate.Id, BaseClasses.SIGHTS)
|
||||
)
|
||||
else if (!(request.WeaponStats.HasOptic ?? false) && itemHelper.IsOfBaseclass(modToAddTemplate.Id, BaseClasses.SIGHTS))
|
||||
{
|
||||
request.WeaponStats.HasOptic = true;
|
||||
}
|
||||
|
||||
var modId = new MongoId();
|
||||
request.Weapon.Add(
|
||||
CreateModItem(
|
||||
modId,
|
||||
modToAddTemplate.Id,
|
||||
request.WeaponId,
|
||||
modSlot,
|
||||
modToAddTemplate,
|
||||
request.BotData.Role
|
||||
)
|
||||
CreateModItem(modId, modToAddTemplate.Id, request.WeaponId, modSlot, modToAddTemplate, request.BotData.Role)
|
||||
);
|
||||
|
||||
// Update conflicting item list now item has been chosen
|
||||
@@ -777,9 +661,7 @@ public class BotEquipmentModGenerator(
|
||||
&& modToAddTemplate.Properties.Slots.Any()
|
||||
)
|
||||
{
|
||||
var modFromService = botEquipmentModPoolService.GetModsForWeaponSlot(
|
||||
modToAddTemplate.Id
|
||||
);
|
||||
var modFromService = botEquipmentModPoolService.GetModsForWeaponSlot(modToAddTemplate.Id);
|
||||
if (modFromService?.Count > 0)
|
||||
{
|
||||
request.ModPool[modToAddTemplate.Id] = modFromService.ToDictionary();
|
||||
@@ -791,9 +673,7 @@ public class BotEquipmentModGenerator(
|
||||
if (!containsModInPool && !isRandomisableSlot)
|
||||
{
|
||||
// Check for required mods the item we've added needs to be classified as 'valid'
|
||||
var modFromService = botEquipmentModPoolService.GetRequiredModsForWeaponSlot(
|
||||
modToAddTemplate.Id
|
||||
);
|
||||
var modFromService = botEquipmentModPoolService.GetRequiredModsForWeaponSlot(modToAddTemplate.Id);
|
||||
if (modFromService?.Count > 0)
|
||||
{
|
||||
request.ModPool[modToAddTemplate.Id] = modFromService;
|
||||
@@ -837,19 +717,12 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="botEquipConfig">Bots equipment config/chance values</param>
|
||||
/// <param name="modToAddTemplate">Mod being added to bots weapon</param>
|
||||
/// <returns>True if it should</returns>
|
||||
public bool ShouldForceSubStockSlots(
|
||||
string modSlot,
|
||||
EquipmentFilters botEquipConfig,
|
||||
TemplateItem modToAddTemplate
|
||||
)
|
||||
public bool ShouldForceSubStockSlots(string modSlot, EquipmentFilters botEquipConfig, TemplateItem modToAddTemplate)
|
||||
{
|
||||
// Can the stock hold child items
|
||||
var hasSubSlots =
|
||||
modToAddTemplate.Properties?.Slots is not null
|
||||
&& modToAddTemplate.Properties.Slots.Any();
|
||||
var hasSubSlots = modToAddTemplate.Properties?.Slots is not null && modToAddTemplate.Properties.Slots.Any();
|
||||
|
||||
return (_stockSlots.Contains(modSlot) && hasSubSlots)
|
||||
|| botEquipConfig.ForceStock.GetValueOrDefault(false);
|
||||
return (_stockSlots.Contains(modSlot) && hasSubSlots) || botEquipConfig.ForceStock.GetValueOrDefault(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -887,11 +760,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modSpawnChances">Chance dictionary to update</param>
|
||||
/// <param name="modSlotsToAdjust"></param>
|
||||
/// <param name="newChancePercent"></param>
|
||||
public void AdjustSlotSpawnChances(
|
||||
Dictionary<string, double>? modSpawnChances,
|
||||
List<string>? modSlotsToAdjust,
|
||||
double newChancePercent
|
||||
)
|
||||
public void AdjustSlotSpawnChances(Dictionary<string, double>? modSpawnChances, List<string>? modSlotsToAdjust, double newChancePercent)
|
||||
{
|
||||
if (modSpawnChances is null)
|
||||
{
|
||||
@@ -930,10 +799,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="unsortedSlotKeys">Array of mod slot strings to sort</param>
|
||||
/// <param name="itemTplWithKeysToSort">The Tpl of the item with mod keys being sorted</param>
|
||||
/// <returns>Sorted array</returns>
|
||||
public HashSet<string> SortModKeys(
|
||||
HashSet<string> unsortedSlotKeys,
|
||||
MongoId itemTplWithKeysToSort
|
||||
)
|
||||
public HashSet<string> SortModKeys(HashSet<string> unsortedSlotKeys, MongoId itemTplWithKeysToSort)
|
||||
{
|
||||
// No need to sort with only 1 item in array
|
||||
if (unsortedSlotKeys.Count <= 1)
|
||||
@@ -1078,15 +944,10 @@ public class BotEquipmentModGenerator(
|
||||
return ModSpawn.SPAWN;
|
||||
}
|
||||
|
||||
var spawnMod = randomUtil.RollChance(
|
||||
modSpawnChances.GetValueOrDefault(modSlotName.ToLowerInvariant())
|
||||
);
|
||||
var spawnMod = randomUtil.RollChance(modSpawnChances.GetValueOrDefault(modSlotName.ToLowerInvariant()));
|
||||
if (
|
||||
!spawnMod
|
||||
&& (
|
||||
slotRequired.GetValueOrDefault(false)
|
||||
|| (botEquipConfig.WeaponSlotIdsToMakeRequired?.Contains(modSlotName) ?? false)
|
||||
)
|
||||
&& (slotRequired.GetValueOrDefault(false) || (botEquipConfig.WeaponSlotIdsToMakeRequired?.Contains(modSlotName) ?? false))
|
||||
)
|
||||
// Edge case: Mod is required but spawn chance roll failed, choose default mod spawn for slot
|
||||
{
|
||||
@@ -1104,9 +965,7 @@ public class BotEquipmentModGenerator(
|
||||
public KeyValuePair<bool, TemplateItem>? ChooseModToPutIntoSlot(ModToSpawnRequest request)
|
||||
{
|
||||
// Slot mod will fill
|
||||
var parentSlot = request.ParentTemplate.Properties.Slots?.FirstOrDefault(i =>
|
||||
i.Name == request.ModSlot
|
||||
);
|
||||
var parentSlot = request.ParentTemplate.Properties.Slots?.FirstOrDefault(i => i.Name == request.ModSlot);
|
||||
var weaponTemplate = itemHelper.GetItem(request.Weapon.First().Template).Value;
|
||||
|
||||
// It's ammo, use predefined ammo parameter
|
||||
@@ -1136,11 +995,7 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
if (modPool.Count > 1)
|
||||
{
|
||||
modPool = FilterSightsByWeaponType(
|
||||
request.Weapon.First(),
|
||||
modPool,
|
||||
request.BotWeaponSightWhitelist
|
||||
);
|
||||
modPool = FilterSightsByWeaponType(request.Weapon.First(), modPool, request.BotWeaponSightWhitelist);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1149,9 +1004,7 @@ public class BotEquipmentModGenerator(
|
||||
if ((request.WeaponStats?.HasOptic ?? false) && modPool.Count > 1)
|
||||
{
|
||||
// Attempt to limit modpool to low profile gas blocks when weapon has an optic
|
||||
var onlyLowProfileGasBlocks = modPool.Where(tpl =>
|
||||
_botConfig.LowProfileGasBlockTpls.Contains(tpl)
|
||||
);
|
||||
var onlyLowProfileGasBlocks = modPool.Where(tpl => _botConfig.LowProfileGasBlockTpls.Contains(tpl));
|
||||
if (onlyLowProfileGasBlocks.Any())
|
||||
{
|
||||
modPool = onlyLowProfileGasBlocks.ToHashSet();
|
||||
@@ -1160,9 +1013,7 @@ public class BotEquipmentModGenerator(
|
||||
else if ((request.WeaponStats?.HasRearIronSight ?? false) && modPool.Count > 1)
|
||||
{
|
||||
// Attempt to limit modpool to high profile gas blocks when weapon has rear iron sight + no front iron sight
|
||||
var onlyHighProfileGasBlocks = modPool.Where(tpl =>
|
||||
!_botConfig.LowProfileGasBlockTpls.Contains(tpl)
|
||||
);
|
||||
var onlyHighProfileGasBlocks = modPool.Where(tpl => !_botConfig.LowProfileGasBlockTpls.Contains(tpl));
|
||||
if (onlyHighProfileGasBlocks.Any())
|
||||
{
|
||||
modPool = onlyHighProfileGasBlocks.ToHashSet();
|
||||
@@ -1189,20 +1040,14 @@ public class BotEquipmentModGenerator(
|
||||
request.Weapon,
|
||||
request.ModSlot
|
||||
);
|
||||
if (
|
||||
chosenModResult.SlotBlocked.GetValueOrDefault(false)
|
||||
&& !parentSlot.Required.GetValueOrDefault(false)
|
||||
)
|
||||
if (chosenModResult.SlotBlocked.GetValueOrDefault(false) && !parentSlot.Required.GetValueOrDefault(false))
|
||||
// Don't bother trying to fit mod, slot is completely blocked
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Log if mod chosen was incompatible
|
||||
if (
|
||||
chosenModResult.Incompatible.GetValueOrDefault(false)
|
||||
&& !parentSlot.Required.GetValueOrDefault(false)
|
||||
)
|
||||
if (chosenModResult.Incompatible.GetValueOrDefault(false) && !parentSlot.Required.GetValueOrDefault(false))
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
@@ -1213,27 +1058,14 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Get random mod to attach from items db for required slots if none found above
|
||||
if (
|
||||
!(chosenModResult.Found ?? false)
|
||||
&& parentSlot != null
|
||||
&& (parentSlot.Required ?? false)
|
||||
)
|
||||
if (!(chosenModResult.Found ?? false) && parentSlot != null && (parentSlot.Required ?? false))
|
||||
{
|
||||
chosenModResult.ChosenTemplate = GetRandomModTplFromItemDb(
|
||||
"",
|
||||
parentSlot,
|
||||
request.ModSlot,
|
||||
request.Weapon
|
||||
);
|
||||
chosenModResult.ChosenTemplate = GetRandomModTplFromItemDb("", parentSlot, request.ModSlot, request.Weapon);
|
||||
chosenModResult.Found = true;
|
||||
}
|
||||
|
||||
// Compatible item not found + not required
|
||||
if (
|
||||
!chosenModResult.Found.GetValueOrDefault(false)
|
||||
&& parentSlot is not null
|
||||
&& !parentSlot.Required.GetValueOrDefault(false)
|
||||
)
|
||||
if (!chosenModResult.Found.GetValueOrDefault(false) && parentSlot is not null && !parentSlot.Required.GetValueOrDefault(false))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -1259,29 +1091,20 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modSpawnRequest">Request data</param>
|
||||
/// <param name="modPool">Pool of magazine tpls to filter</param>
|
||||
/// <returns>Filtered pool of magazine tpls</returns>
|
||||
public IEnumerable<MongoId> GetFilteredMagazinePoolByCapacity(
|
||||
ModToSpawnRequest modSpawnRequest,
|
||||
HashSet<MongoId> modPool
|
||||
)
|
||||
public IEnumerable<MongoId> GetFilteredMagazinePoolByCapacity(ModToSpawnRequest modSpawnRequest, HashSet<MongoId> modPool)
|
||||
{
|
||||
var weaponTpl = modSpawnRequest.Weapon.FirstOrDefault().Template;
|
||||
modSpawnRequest.RandomisationSettings.MinimumMagazineSize.TryGetValue(
|
||||
weaponTpl,
|
||||
out var minMagSizeFromSettings
|
||||
);
|
||||
modSpawnRequest.RandomisationSettings.MinimumMagazineSize.TryGetValue(weaponTpl, out var minMagSizeFromSettings);
|
||||
var desiredMagazineTpls = modPool.Where(magTpl =>
|
||||
{
|
||||
var magazineDb = itemHelper.GetItem(magTpl).Value;
|
||||
return magazineDb.Properties?.Cartridges is not null
|
||||
&& magazineDb.Properties.Cartridges.FirstOrDefault()?.MaxCount
|
||||
>= minMagSizeFromSettings;
|
||||
&& magazineDb.Properties.Cartridges.FirstOrDefault()?.MaxCount >= minMagSizeFromSettings;
|
||||
});
|
||||
|
||||
if (!desiredMagazineTpls.Any())
|
||||
{
|
||||
logger.Warning(
|
||||
$"Magazine size filter for: {weaponTpl} was too strict, ignoring filter"
|
||||
);
|
||||
logger.Warning($"Magazine size filter for: {weaponTpl} was too strict, ignoring filter");
|
||||
|
||||
return modPool;
|
||||
}
|
||||
@@ -1317,15 +1140,12 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason =
|
||||
$"Unable to add mod to {choiceTypeEnum.ToString()} slot: {modSlotName}. All: {modPool.Count} had conflicts",
|
||||
Reason = $"Unable to add mod to {choiceTypeEnum.ToString()} slot: {modSlotName}. All: {modPool.Count} had conflicts",
|
||||
};
|
||||
}
|
||||
|
||||
// Filter modpool to only items that appear in parents allowed list
|
||||
preFilteredModPool = preFilteredModPool
|
||||
.Where(tpl => parentSlot.Props.Filters.First().Filter.Contains(tpl))
|
||||
.ToHashSet();
|
||||
preFilteredModPool = preFilteredModPool.Where(tpl => parentSlot.Props.Filters.First().Filter.Contains(tpl)).ToHashSet();
|
||||
if (preFilteredModPool.Count == 0)
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult
|
||||
@@ -1393,8 +1213,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
// Check if existing weapon mods are incompatible with chosen item
|
||||
var existingItemBlockingChoice = weapon.FirstOrDefault(item =>
|
||||
pickedItemDetails.Value.Properties.ConflictingItems?.Contains(item.Template)
|
||||
?? false
|
||||
pickedItemDetails.Value.Properties.ConflictingItems?.Contains(item.Template) ?? false
|
||||
);
|
||||
if (existingItemBlockingChoice is not null)
|
||||
{
|
||||
@@ -1417,8 +1236,7 @@ public class BotEquipmentModGenerator(
|
||||
// Edge case - Some mod combos will never work, make sure this isn't the case
|
||||
if (WeaponModComboIsIncompatible(weapon, chosenTpl))
|
||||
{
|
||||
chosenModResult.Reason =
|
||||
$"Chosen weapon mod: {chosenTpl} can never be compatible with existing weapon mods";
|
||||
chosenModResult.Reason = $"Chosen weapon mod: {chosenTpl} can never be compatible with existing weapon mods";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1444,10 +1262,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modPool"></param>
|
||||
/// <param name="tplBlacklist">Tpls that are incompatible and should not be used</param>
|
||||
/// <returns>string array of compatible mod tpls with weapon</returns>
|
||||
public HashSet<MongoId> GetFilteredModPool(
|
||||
HashSet<MongoId> modPool,
|
||||
HashSet<MongoId> tplBlacklist
|
||||
)
|
||||
public HashSet<MongoId> GetFilteredModPool(HashSet<MongoId> modPool, HashSet<MongoId> tplBlacklist)
|
||||
{
|
||||
return modPool.Where(tpl => !tplBlacklist.Contains(tpl)).ToHashSet();
|
||||
}
|
||||
@@ -1461,10 +1276,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="request"></param>
|
||||
/// <param name="weaponTemplate">Mods root parent (weapon/equipment)</param>
|
||||
/// <returns>Array of mod tpls</returns>
|
||||
public HashSet<MongoId>? GetModPoolForSlot(
|
||||
ModToSpawnRequest request,
|
||||
TemplateItem weaponTemplate
|
||||
)
|
||||
public HashSet<MongoId>? GetModPoolForSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
|
||||
{
|
||||
// Mod is flagged as being default only, try and find it in globals
|
||||
if (request.ModSpawnResult == ModSpawn.DEFAULT_MOD)
|
||||
@@ -1474,11 +1286,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
if (request.IsRandomisableSlot.GetValueOrDefault(false))
|
||||
{
|
||||
return GetDynamicModPool(
|
||||
request.ParentTemplate.Id,
|
||||
request.ModSlot,
|
||||
request.BotEquipBlacklist
|
||||
);
|
||||
return GetDynamicModPool(request.ParentTemplate.Id, request.ModSlot, request.BotEquipBlacklist);
|
||||
}
|
||||
|
||||
// Required mod is not default or randomisable, use existing pool
|
||||
@@ -1491,10 +1299,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="request"></param>
|
||||
/// <param name="weaponTemplate"></param>
|
||||
/// <returns>Hashset of mods keyed by slot</returns>
|
||||
public HashSet<MongoId> GetModPoolForDefaultSlot(
|
||||
ModToSpawnRequest request,
|
||||
TemplateItem weaponTemplate
|
||||
)
|
||||
public HashSet<MongoId> GetModPoolForDefaultSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
|
||||
{
|
||||
var matchingModFromPreset = GetMatchingModFromPreset(request, weaponTemplate);
|
||||
if (matchingModFromPreset is null)
|
||||
@@ -1517,10 +1322,7 @@ public class BotEquipmentModGenerator(
|
||||
// Filtering mod pool to item that wasn't already there can have problems;
|
||||
// You'd have a mod being picked without any sub-mods in its chain, possibly resulting in missing required mods not being added
|
||||
// Mod is in existing mod pool
|
||||
if (
|
||||
request.ItemModPool.TryGetValue(request.ModSlot, out var ids)
|
||||
&& ids.Contains(matchingModFromPreset.Template)
|
||||
)
|
||||
if (request.ItemModPool.TryGetValue(request.ModSlot, out var ids) && ids.Contains(matchingModFromPreset.Template))
|
||||
// Found mod on preset + it already exists in mod pool
|
||||
{
|
||||
return [matchingModFromPreset.Template];
|
||||
@@ -1530,11 +1332,7 @@ public class BotEquipmentModGenerator(
|
||||
// Check the filter of the slot to ensure a chosen mod fits
|
||||
var parentSlotCompatibleItems = request
|
||||
.ParentTemplate.Properties.Slots?.FirstOrDefault(slot =>
|
||||
string.Equals(
|
||||
slot.Name.ToLowerInvariant(),
|
||||
request.ModSlot.ToLowerInvariant(),
|
||||
StringComparison.Ordinal
|
||||
)
|
||||
string.Equals(slot.Name.ToLowerInvariant(), request.ModSlot.ToLowerInvariant(), StringComparison.Ordinal)
|
||||
)
|
||||
?.Props.Filters?.First()
|
||||
.Filter;
|
||||
@@ -1571,9 +1369,7 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Last ditch, use full pool of items minus conflicts
|
||||
var newListOfModsForSlot = parentSlotCompatibleItems.Where(tpl =>
|
||||
!request.ConflictingItemTpls.Contains(tpl)
|
||||
);
|
||||
var newListOfModsForSlot = parentSlotCompatibleItems.Where(tpl => !request.ConflictingItemTpls.Contains(tpl));
|
||||
if (newListOfModsForSlot.Any())
|
||||
{
|
||||
return newListOfModsForSlot.ToHashSet();
|
||||
@@ -1618,8 +1414,7 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Edge case - dvl 500mm is the silenced barrel and has specific muzzle mods
|
||||
var isDvl500mmSilencedBarrel =
|
||||
parentItemTpl == ItemTpl.BARREL_DVL10_762X51_500MM_SUPPRESSED;
|
||||
var isDvl500mmSilencedBarrel = parentItemTpl == ItemTpl.BARREL_DVL10_762X51_500MM_SUPPRESSED;
|
||||
|
||||
if (isDvl500mmSilencedBarrel)
|
||||
{
|
||||
@@ -1638,10 +1433,7 @@ public class BotEquipmentModGenerator(
|
||||
public bool WeaponModComboIsIncompatible(IEnumerable<Item> weapon, MongoId modTpl)
|
||||
{
|
||||
// STM-9 + AR-15 Lone Star Ion Lite handguard
|
||||
if (
|
||||
weapon.First().Template == ItemTpl.SMG_SOYUZTM_STM9_GEN2_9X19_CARBINE
|
||||
&& modTpl == ItemTpl.HANDGUARD_AR15_LONE_STAR_ION_LITE
|
||||
)
|
||||
if (weapon.First().Template == ItemTpl.SMG_SOYUZTM_STM9_GEN2_9X19_CARBINE && modTpl == ItemTpl.HANDGUARD_AR15_LONE_STAR_ION_LITE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1659,14 +1451,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modTemplate">Used to add additional properties in the upd object</param>
|
||||
/// <param name="botRole">The bots role mod is being created for</param>
|
||||
/// <returns>Item object</returns>
|
||||
public Item CreateModItem(
|
||||
MongoId modId,
|
||||
MongoId modTpl,
|
||||
string parentId,
|
||||
string modSlot,
|
||||
TemplateItem modTemplate,
|
||||
string botRole
|
||||
)
|
||||
public Item CreateModItem(MongoId modId, MongoId modTpl, string parentId, string modSlot, TemplateItem modTemplate, string botRole)
|
||||
{
|
||||
return new Item
|
||||
{
|
||||
@@ -1696,12 +1481,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modSlot">Slot to get mod to fill</param>
|
||||
/// <param name="items">Items to ensure picked mod is compatible with</param>
|
||||
/// <returns>Item tpl</returns>
|
||||
public MongoId? GetRandomModTplFromItemDb(
|
||||
MongoId fallbackModTpl,
|
||||
Slot parentSlot,
|
||||
string modSlot,
|
||||
IEnumerable<Item> items
|
||||
)
|
||||
public MongoId? GetRandomModTplFromItemDb(MongoId fallbackModTpl, Slot parentSlot, string modSlot, IEnumerable<Item> items)
|
||||
{
|
||||
// Find compatible mods and make an array of them
|
||||
var allowedItems = parentSlot.Props.Filters.First().Filter;
|
||||
@@ -1712,11 +1492,7 @@ public class BotEquipmentModGenerator(
|
||||
while (exhaustableModPool.HasValues())
|
||||
{
|
||||
tmpModTpl = exhaustableModPool.GetRandomValue();
|
||||
if (
|
||||
!botGeneratorHelper
|
||||
.IsItemIncompatibleWithCurrentItems(items, tmpModTpl, modSlot)
|
||||
.Incompatible.GetValueOrDefault(false)
|
||||
)
|
||||
if (!botGeneratorHelper.IsItemIncompatibleWithCurrentItems(items, tmpModTpl, modSlot).Incompatible.GetValueOrDefault(false))
|
||||
{
|
||||
return tmpModTpl;
|
||||
}
|
||||
@@ -1803,9 +1579,7 @@ public class BotEquipmentModGenerator(
|
||||
EquipmentFilterDetails botEquipBlacklist
|
||||
)
|
||||
{
|
||||
var desiredSlotObject = modTemplate.Properties?.Slots?.FirstOrDefault(slot =>
|
||||
slot.Name.Contains(desiredSlotName)
|
||||
);
|
||||
var desiredSlotObject = modTemplate.Properties?.Slots?.FirstOrDefault(slot => slot.Name.Contains(desiredSlotName));
|
||||
|
||||
var supportedSubMods = desiredSlotObject?.Props?.Filters?.FirstOrDefault()?.Filter;
|
||||
if (supportedSubMods is null)
|
||||
@@ -1816,11 +1590,7 @@ public class BotEquipmentModGenerator(
|
||||
var supportedSubModsSet = supportedSubMods.ToHashSet();
|
||||
|
||||
// Filter mods
|
||||
var filteredMods = FilterModsByBlacklist(
|
||||
supportedSubModsSet,
|
||||
botEquipBlacklist,
|
||||
desiredSlotName
|
||||
);
|
||||
var filteredMods = FilterModsByBlacklist(supportedSubModsSet, botEquipBlacklist, desiredSlotName);
|
||||
if (!filteredMods.Any())
|
||||
{
|
||||
logger.Warning(
|
||||
@@ -1843,15 +1613,9 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="modSlot">Slot item should fit in</param>
|
||||
/// <param name="botEquipBlacklist">Equipment that should not be picked</param>
|
||||
/// <returns>Array of compatible items for that slot</returns>
|
||||
public HashSet<MongoId> GetDynamicModPool(
|
||||
string parentItemId,
|
||||
string modSlot,
|
||||
EquipmentFilterDetails botEquipBlacklist
|
||||
)
|
||||
public HashSet<MongoId> GetDynamicModPool(string parentItemId, string modSlot, EquipmentFilterDetails botEquipBlacklist)
|
||||
{
|
||||
var modsFromDynamicPool = cloner.Clone(
|
||||
botEquipmentModPoolService.GetCompatibleModsForWeaponSlot(parentItemId, modSlot)
|
||||
);
|
||||
var modsFromDynamicPool = cloner.Clone(botEquipmentModPoolService.GetCompatibleModsForWeaponSlot(parentItemId, modSlot));
|
||||
|
||||
if (modsFromDynamicPool.Count == 0)
|
||||
{
|
||||
@@ -1866,12 +1630,7 @@ public class BotEquipmentModGenerator(
|
||||
return filteredMods;
|
||||
}
|
||||
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-unable_to_filter_mod_slot_all_blacklisted",
|
||||
modSlot
|
||||
)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("bot-unable_to_filter_mod_slot_all_blacklisted", modSlot));
|
||||
|
||||
return modsFromDynamicPool;
|
||||
}
|
||||
@@ -1883,11 +1642,7 @@ public class BotEquipmentModGenerator(
|
||||
/// <param name="botEquipBlacklist">Equipment blacklist details for bot level range</param>
|
||||
/// <param name="modSlot">Mod slot mods belong to</param>
|
||||
/// <returns>New set of tpls not in blacklist(s)</returns>
|
||||
public HashSet<MongoId> FilterModsByBlacklist(
|
||||
HashSet<MongoId> modTplPool,
|
||||
EquipmentFilterDetails? botEquipBlacklist,
|
||||
string modSlot
|
||||
)
|
||||
public HashSet<MongoId> FilterModsByBlacklist(HashSet<MongoId> modTplPool, EquipmentFilterDetails? botEquipBlacklist, string modSlot)
|
||||
{
|
||||
if (!modTplPool.Any())
|
||||
{
|
||||
@@ -1898,10 +1653,7 @@ public class BotEquipmentModGenerator(
|
||||
// Get item blacklist and mod equipment blacklist as one Set
|
||||
var blacklist = new HashSet<MongoId>();
|
||||
blacklist.UnionWith(itemFilterService.GetBlacklistedItems());
|
||||
if (
|
||||
botEquipBlacklist?.Equipment is not null
|
||||
&& botEquipBlacklist.Equipment.TryGetValue(modSlot, out var equipmentBlacklistValues)
|
||||
)
|
||||
if (botEquipBlacklist?.Equipment is not null && botEquipBlacklist.Equipment.TryGetValue(modSlot, out var equipmentBlacklistValues))
|
||||
{
|
||||
blacklist.UnionWith(equipmentBlacklistValues);
|
||||
}
|
||||
@@ -1939,17 +1691,13 @@ public class BotEquipmentModGenerator(
|
||||
new { weaponId = cylinderMagTemplate.Id, weaponName = cylinderMagTemplate.Name }
|
||||
)
|
||||
);
|
||||
var camoraSlots = cylinderMagTemplate.Properties.Slots.Where(slot =>
|
||||
slot.Name.StartsWith("camora")
|
||||
);
|
||||
var camoraSlots = cylinderMagTemplate.Properties.Slots.Where(slot => slot.Name.StartsWith("camora"));
|
||||
|
||||
// Attempt to generate camora slots for item
|
||||
modPool[cylinderMagTemplate.Id] = new Dictionary<string, HashSet<MongoId>>();
|
||||
foreach (var camora in camoraSlots)
|
||||
{
|
||||
modPool[cylinderMagTemplate.Id][camora.Name] = camora
|
||||
.Props.Filters.First()
|
||||
.Filter.ToHashSet();
|
||||
modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters.First().Filter.ToHashSet();
|
||||
}
|
||||
|
||||
itemModPool = modPool[cylinderMagTemplate.Id];
|
||||
@@ -1969,12 +1717,7 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-missing_cartridge_slot",
|
||||
cylinderMagTemplate.Id
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-missing_cartridge_slot", cylinderMagTemplate.Id));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1984,11 +1727,7 @@ public class BotEquipmentModGenerator(
|
||||
while (exhaustibleModPool.HasValues())
|
||||
{
|
||||
modTpl = exhaustibleModPool.GetRandomValue();
|
||||
if (
|
||||
!botGeneratorHelper
|
||||
.IsItemIncompatibleWithCurrentItems(items, modTpl, modSlot)
|
||||
.Incompatible.GetValueOrDefault(false)
|
||||
)
|
||||
if (!botGeneratorHelper.IsItemIncompatibleWithCurrentItems(items, modTpl, modSlot).Incompatible.GetValueOrDefault(false))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@@ -1997,9 +1736,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
if (!found)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("bot-no_compatible_camora_ammo_found", modSlot)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-no_compatible_camora_ammo_found", modSlot));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -2048,12 +1785,7 @@ public class BotEquipmentModGenerator(
|
||||
var weaponDetails = itemHelper.GetItem(weapon.Template);
|
||||
|
||||
// Return original scopes array if whitelist not found
|
||||
if (
|
||||
!botWeaponSightWhitelist.TryGetValue(
|
||||
weaponDetails.Value.Parent,
|
||||
out var whitelistedSightTypes
|
||||
)
|
||||
)
|
||||
if (!botWeaponSightWhitelist.TryGetValue(weaponDetails.Value.Parent, out var whitelistedSightTypes))
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
@@ -2089,17 +1821,14 @@ public class BotEquipmentModGenerator(
|
||||
// Check to see if mount has a scope slot (only include primary slot, ignore the rest like the backup sight slots)
|
||||
// Should only find 1 as there's currently no items with a mod_scope AND a mod_scope_000
|
||||
HashSet<string> filter = ["mod_scope", "mod_scope_000"];
|
||||
var scopeSlot = itemDetails.Properties.Slots.Where(slot =>
|
||||
filter.Contains(slot.Name)
|
||||
);
|
||||
var scopeSlot = itemDetails.Properties.Slots.Where(slot => filter.Contains(slot.Name));
|
||||
|
||||
// Mods scope slot found must allow ALL whitelisted scope types OR be a mount
|
||||
if (
|
||||
scopeSlot?.All(slot =>
|
||||
slot.Props.Filters.FirstOrDefault()
|
||||
.Filter.All(tpl =>
|
||||
itemHelper.IsOfBaseclasses(tpl, whitelistedSightTypes)
|
||||
|| itemHelper.IsOfBaseclass(tpl, BaseClasses.MOUNT)
|
||||
itemHelper.IsOfBaseclasses(tpl, whitelistedSightTypes) || itemHelper.IsOfBaseclass(tpl, BaseClasses.MOUNT)
|
||||
)
|
||||
) ?? false
|
||||
)
|
||||
@@ -2115,9 +1844,7 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Scope whitelist too restrictive for: {weapon.Template} {weaponDetails.Value.Name}, skipping filter"
|
||||
);
|
||||
logger.Debug($"Scope whitelist too restrictive for: {weapon.Template} {weaponDetails.Value.Name}, skipping filter");
|
||||
}
|
||||
|
||||
return scopes;
|
||||
|
||||
@@ -48,13 +48,7 @@ public class BotGenerator(
|
||||
/// <param name="botTemplate">base bot template to use (e.g. assault/pmcbot)</param>
|
||||
/// <param name="profile">profile of player generating pscav</param>
|
||||
/// <returns>BotBase</returns>
|
||||
public PmcData GeneratePlayerScav(
|
||||
MongoId sessionId,
|
||||
string role,
|
||||
string difficulty,
|
||||
BotType botTemplate,
|
||||
PmcData profile
|
||||
)
|
||||
public PmcData GeneratePlayerScav(MongoId sessionId, string role, string difficulty, BotType botTemplate, PmcData profile)
|
||||
{
|
||||
var bot = GetBotBaseClone();
|
||||
bot.Info.Settings.BotDifficulty = difficulty;
|
||||
@@ -119,10 +113,7 @@ public class BotGenerator(
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="botGenerationDetails">details on how to generate bots</param>
|
||||
/// <returns>constructed bot</returns>
|
||||
public BotBase PrepareAndGenerateBot(
|
||||
MongoId sessionId,
|
||||
BotGenerationDetails botGenerationDetails
|
||||
)
|
||||
public BotBase PrepareAndGenerateBot(MongoId sessionId, BotGenerationDetails botGenerationDetails)
|
||||
{
|
||||
var preparedBotBase = GetPreparedBotBase(
|
||||
botGenerationDetails.EventRole ?? botGenerationDetails.Role, // Use eventRole if provided
|
||||
@@ -137,9 +128,7 @@ public class BotGenerator(
|
||||
var botJsonTemplateClone = cloner.Clone(botHelper.GetBotTemplate(botRole));
|
||||
if (botJsonTemplateClone is null)
|
||||
{
|
||||
logger.Error(
|
||||
$"Unable to retrieve: {botRole} bot template, cannot generate bot of this type"
|
||||
);
|
||||
logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
|
||||
}
|
||||
|
||||
return GenerateBot(sessionId, preparedBotBase, botJsonTemplateClone, botGenerationDetails);
|
||||
@@ -179,29 +168,15 @@ public class BotGenerator(
|
||||
/// <param name="botJsonTemplate">Bot template from db/bots/x.json</param>
|
||||
/// <param name="botGenerationDetails">details on how to generate the bot</param>
|
||||
/// <returns>BotBase object</returns>
|
||||
protected BotBase GenerateBot(
|
||||
MongoId sessionId,
|
||||
BotBase bot,
|
||||
BotType botJsonTemplate,
|
||||
BotGenerationDetails botGenerationDetails
|
||||
)
|
||||
protected BotBase GenerateBot(MongoId sessionId, BotBase bot, BotType botJsonTemplate, BotGenerationDetails botGenerationDetails)
|
||||
{
|
||||
var botRoleLowercase = botGenerationDetails.Role.ToLowerInvariant();
|
||||
var botLevel = botLevelGenerator.GenerateBotLevel(
|
||||
botJsonTemplate.BotExperience.Level,
|
||||
botGenerationDetails,
|
||||
bot
|
||||
);
|
||||
var botLevel = botLevelGenerator.GenerateBotLevel(botJsonTemplate.BotExperience.Level, botGenerationDetails, bot);
|
||||
|
||||
// Only filter bot equipment, never players
|
||||
if (!botGenerationDetails.IsPlayerScav)
|
||||
{
|
||||
botEquipmentFilterService.FilterBotEquipment(
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
botLevel.Level.Value,
|
||||
botGenerationDetails
|
||||
);
|
||||
botEquipmentFilterService.FilterBotEquipment(sessionId, botJsonTemplate, botLevel.Level.Value, botGenerationDetails);
|
||||
}
|
||||
|
||||
bot.Info.Nickname = botNameService.GenerateUniqueBotNickname(
|
||||
@@ -212,9 +187,7 @@ public class BotGenerator(
|
||||
);
|
||||
|
||||
// Only Pmcs should have a lower nickname
|
||||
bot.Info.LowerNickname = botGenerationDetails.IsPmc
|
||||
? bot.Info.Nickname.ToLowerInvariant()
|
||||
: string.Empty;
|
||||
bot.Info.LowerNickname = botGenerationDetails.IsPmc ? bot.Info.Nickname.ToLowerInvariant() : string.Empty;
|
||||
|
||||
// Only run when generating a 'fake' playerscav, not actual player scav
|
||||
if (!botGenerationDetails.IsPlayerScav && ShouldSimulatePlayerScav(botRoleLowercase))
|
||||
@@ -228,10 +201,7 @@ public class BotGenerator(
|
||||
{
|
||||
if (botGenerationDetails.Role != "gifter")
|
||||
{
|
||||
seasonalEventService.RemoveChristmasItemsFromBotInventory(
|
||||
botJsonTemplate.BotInventory,
|
||||
botGenerationDetails.Role
|
||||
);
|
||||
seasonalEventService.RemoveChristmasItemsFromBotInventory(botJsonTemplate.BotInventory, botGenerationDetails.Role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,14 +231,10 @@ public class BotGenerator(
|
||||
botGenerationDetails.Role
|
||||
);
|
||||
bot.Info.Settings.UseSimpleAnimator = botJsonTemplate.BotExperience.UseSimpleAnimator;
|
||||
var chosenVoiceName = weightedRandomHelper.GetWeightedValue(
|
||||
botJsonTemplate.BotAppearance.Voice
|
||||
);
|
||||
var chosenVoiceName = weightedRandomHelper.GetWeightedValue(botJsonTemplate.BotAppearance.Voice);
|
||||
bot.Customization.Voice = databaseService
|
||||
.GetCustomization()
|
||||
.FirstOrDefault(customisation =>
|
||||
customisation.Value.Name.Equals(chosenVoiceName, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
.FirstOrDefault(customisation => customisation.Value.Name.Equals(chosenVoiceName, StringComparison.OrdinalIgnoreCase))
|
||||
.Key;
|
||||
bot.Health = GenerateHealth(botJsonTemplate.BotHealth, botGenerationDetails.IsPlayerScav);
|
||||
bot.Skills = GenerateSkills(botJsonTemplate.BotSkills);
|
||||
@@ -323,8 +289,7 @@ public class BotGenerator(
|
||||
/// <returns>True if name should be simulated pscav</returns>
|
||||
protected bool ShouldSimulatePlayerScav(string botRole)
|
||||
{
|
||||
return botRole == Roles.Assault
|
||||
&& randomUtil.GetChance100(_botConfig.ChanceAssaultScavHasPlayerScavName);
|
||||
return botRole == Roles.Assault && randomUtil.GetChance100(_botConfig.ChanceAssaultScavHasPlayerScavName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -334,19 +299,13 @@ public class BotGenerator(
|
||||
/// <param name="botDifficulty">the killed bots difficulty</param>
|
||||
/// <param name="role">Role of bot (optional, used for error logging)</param>
|
||||
/// <returns>Experience for kill</returns>
|
||||
protected int GetExperienceRewardForKillByDifficulty(
|
||||
Dictionary<string, MinMax<int>> experiences,
|
||||
string botDifficulty,
|
||||
string role
|
||||
)
|
||||
protected int GetExperienceRewardForKillByDifficulty(Dictionary<string, MinMax<int>> experiences, string botDifficulty, string role)
|
||||
{
|
||||
if (!experiences.TryGetValue(botDifficulty.ToLowerInvariant(), out var result))
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`"
|
||||
);
|
||||
logger.Debug($"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`");
|
||||
}
|
||||
|
||||
return randomUtil.GetInt(experiences["normal"].Min, experiences["normal"].Max);
|
||||
@@ -369,17 +328,11 @@ public class BotGenerator(
|
||||
/// <param name="botDifficulty">Difficulty of bot to look up</param>
|
||||
/// <param name="role">Role of bot (optional, used for error logging)</param>
|
||||
/// <returns>Standing change value</returns>
|
||||
protected double GetStandingChangeForKillByDifficulty(
|
||||
Dictionary<string, double> standingsForKill,
|
||||
string botDifficulty,
|
||||
string role
|
||||
)
|
||||
protected double GetStandingChangeForKillByDifficulty(Dictionary<string, double> standingsForKill, string botDifficulty, string role)
|
||||
{
|
||||
if (!standingsForKill.TryGetValue(botDifficulty.ToLowerInvariant(), out var result))
|
||||
{
|
||||
logger.Warning(
|
||||
$"Unable to find standing for kill value for: {role} {botDifficulty}, falling back to `normal`"
|
||||
);
|
||||
logger.Warning($"Unable to find standing for kill value for: {role} {botDifficulty}, falling back to `normal`");
|
||||
|
||||
return standingsForKill["normal"];
|
||||
}
|
||||
@@ -394,17 +347,11 @@ public class BotGenerator(
|
||||
/// <param name="botDifficulty">Difficulty of bot to look up</param>
|
||||
/// <param name="role">Role of bot (optional, used for error logging)</param>
|
||||
/// <returns>Standing change value</returns>
|
||||
protected double GetAggressorBonusByDifficulty(
|
||||
Dictionary<string, double> aggressorBonuses,
|
||||
string botDifficulty,
|
||||
string role
|
||||
)
|
||||
protected double GetAggressorBonusByDifficulty(Dictionary<string, double> aggressorBonuses, string botDifficulty, string role)
|
||||
{
|
||||
if (!aggressorBonuses.TryGetValue(botDifficulty.ToLowerInvariant(), out var result))
|
||||
{
|
||||
logger.Warning(
|
||||
$"Unable to find aggressor bonus for kill value for: {role} {botDifficulty}, falling back to `normal`"
|
||||
);
|
||||
logger.Warning($"Unable to find aggressor bonus for kill value for: {role} {botDifficulty}, falling back to `normal`");
|
||||
|
||||
return aggressorBonuses["normal"];
|
||||
}
|
||||
@@ -465,11 +412,7 @@ public class BotGenerator(
|
||||
/// <param name="bot">Bot to adjust</param>
|
||||
/// <param name="appearance">Appearance settings to choose from</param>
|
||||
/// <param name="botGenerationDetails">Generation details</param>
|
||||
protected void SetBotAppearance(
|
||||
BotBase bot,
|
||||
Appearance appearance,
|
||||
BotGenerationDetails botGenerationDetails
|
||||
)
|
||||
protected void SetBotAppearance(BotBase bot, Appearance appearance, BotGenerationDetails botGenerationDetails)
|
||||
{
|
||||
// Choose random values by weight
|
||||
bot.Customization.Head = weightedRandomHelper.GetWeightedValue(appearance.Head);
|
||||
@@ -480,9 +423,7 @@ public class BotGenerator(
|
||||
var chosenBodyTemplate = databaseService.GetCustomization()[bot.Customization.Body.Value];
|
||||
|
||||
// Some bodies have matching hands, look up body to see if this is the case
|
||||
var chosenBody = bodyGlobalDictDb.FirstOrDefault(c =>
|
||||
c.Key == chosenBodyTemplate?.Name.Trim()
|
||||
);
|
||||
var chosenBody = bodyGlobalDictDb.FirstOrDefault(c => c.Key == chosenBodyTemplate?.Name.Trim());
|
||||
bot.Customization.Hands =
|
||||
chosenBody.Value?.IsNotRandom ?? false
|
||||
? chosenBody.Value.Hands // Has fixed hands for chosen body, update to match
|
||||
@@ -497,9 +438,7 @@ public class BotGenerator(
|
||||
/// <returns>Health object</returns>
|
||||
protected BotBaseHealth GenerateHealth(BotTypeHealth healthObj, bool playerScav = false)
|
||||
{
|
||||
var bodyParts = playerScav
|
||||
? GetLowestHpBodyPart(healthObj.BodyParts)
|
||||
: randomUtil.GetArrayValue(healthObj.BodyParts);
|
||||
var bodyParts = playerScav ? GetLowestHpBodyPart(healthObj.BodyParts) : randomUtil.GetArrayValue(healthObj.BodyParts);
|
||||
|
||||
BotBaseHealth health = new()
|
||||
{
|
||||
@@ -515,10 +454,7 @@ public class BotGenerator(
|
||||
},
|
||||
Temperature = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
healthObj.Temperature.Min,
|
||||
healthObj.Temperature.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(healthObj.Temperature.Min, healthObj.Temperature.Max),
|
||||
Maximum = healthObj.Temperature.Max,
|
||||
},
|
||||
BodyParts = new Dictionary<string, BodyPartHealth>
|
||||
@@ -540,10 +476,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.Chest.Min,
|
||||
bodyParts.Chest.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.Chest.Min, bodyParts.Chest.Max),
|
||||
Maximum = Math.Round(bodyParts.Chest.Max),
|
||||
},
|
||||
}
|
||||
@@ -554,10 +487,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.Stomach.Min,
|
||||
bodyParts.Stomach.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.Stomach.Min, bodyParts.Stomach.Max),
|
||||
Maximum = Math.Round(bodyParts.Stomach.Max),
|
||||
},
|
||||
}
|
||||
@@ -568,10 +498,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.LeftArm.Min,
|
||||
bodyParts.LeftArm.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.LeftArm.Min, bodyParts.LeftArm.Max),
|
||||
Maximum = Math.Round(bodyParts.LeftArm.Max),
|
||||
},
|
||||
}
|
||||
@@ -582,10 +509,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.RightArm.Min,
|
||||
bodyParts.RightArm.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.RightArm.Min, bodyParts.RightArm.Max),
|
||||
Maximum = Math.Round(bodyParts.RightArm.Max),
|
||||
},
|
||||
}
|
||||
@@ -596,10 +520,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.LeftLeg.Min,
|
||||
bodyParts.LeftLeg.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.LeftLeg.Min, bodyParts.LeftLeg.Max),
|
||||
Maximum = Math.Round(bodyParts.LeftLeg.Max),
|
||||
},
|
||||
}
|
||||
@@ -610,10 +531,7 @@ public class BotGenerator(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = randomUtil.GetDouble(
|
||||
bodyParts.RightLeg.Min,
|
||||
bodyParts.RightLeg.Max
|
||||
),
|
||||
Current = randomUtil.GetDouble(bodyParts.RightLeg.Min, bodyParts.RightLeg.Max),
|
||||
Maximum = Math.Round(bodyParts.RightLeg.Max),
|
||||
},
|
||||
}
|
||||
@@ -642,12 +560,7 @@ public class BotGenerator(
|
||||
.Select(bp => new
|
||||
{
|
||||
BodyPart = bp,
|
||||
TotalMaxHp = bp.Head.Max
|
||||
+ bp.Chest.Max
|
||||
+ bp.LeftArm.Max
|
||||
+ bp.RightArm.Max
|
||||
+ bp.LeftLeg.Max
|
||||
+ bp.RightLeg.Max,
|
||||
TotalMaxHp = bp.Head.Max + bp.Chest.Max + bp.LeftArm.Max + bp.RightArm.Max + bp.LeftLeg.Max + bp.RightLeg.Max,
|
||||
})
|
||||
.OrderBy(x => x.TotalMaxHp)
|
||||
.FirstOrDefault()
|
||||
@@ -676,9 +589,7 @@ public class BotGenerator(
|
||||
/// </summary>
|
||||
/// <param name="skills">Skills to randomise</param>
|
||||
/// <returns>Skills with randomised progress values as a collection</returns>
|
||||
protected List<CommonSkill> GetCommonSkillsWithRandomisedProgressValue(
|
||||
Dictionary<string, MinMax<double>> skills
|
||||
)
|
||||
protected List<CommonSkill> GetCommonSkillsWithRandomisedProgressValue(Dictionary<string, MinMax<double>> skills)
|
||||
{
|
||||
return skills
|
||||
.Select(kvp =>
|
||||
@@ -707,9 +618,7 @@ public class BotGenerator(
|
||||
/// </summary>
|
||||
/// <param name="masteringSkills">Skills to randomise</param>
|
||||
/// <returns>Skills with randomised progress values as a collection</returns>
|
||||
protected List<MasterySkill> GetMasteringSkillsWithRandomisedProgressValue(
|
||||
Dictionary<string, MinMax<double>>? masteringSkills
|
||||
)
|
||||
protected List<MasterySkill> GetMasteringSkillsWithRandomisedProgressValue(Dictionary<string, MinMax<double>>? masteringSkills)
|
||||
{
|
||||
if (masteringSkills is null)
|
||||
{
|
||||
@@ -727,11 +636,7 @@ public class BotGenerator(
|
||||
}
|
||||
|
||||
// All skills have id and progress props
|
||||
return new MasterySkill
|
||||
{
|
||||
Id = kvp.Key,
|
||||
Progress = randomUtil.GetDouble(skill.Min, skill.Max),
|
||||
};
|
||||
return new MasterySkill { Id = kvp.Key, Progress = randomUtil.GetDouble(skill.Min, skill.Max) };
|
||||
})
|
||||
.Where(baseSkill => baseSkill != null)
|
||||
.ToList();
|
||||
@@ -818,9 +723,7 @@ public class BotGenerator(
|
||||
break;
|
||||
default:
|
||||
// Everyone else gets a weighted randomised category
|
||||
botInfo.MemberCategory = weightedRandomHelper.GetWeightedValue(
|
||||
_pmcConfig.AccountTypeWeight
|
||||
);
|
||||
botInfo.MemberCategory = weightedRandomHelper.GetWeightedValue(_pmcConfig.AccountTypeWeight);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,7 @@ public class BotInventoryGenerator(
|
||||
|
||||
private readonly BotConfig _botConfig = configServer.GetConfig<BotConfig>();
|
||||
|
||||
private readonly FrozenSet<string> _slotsToCheck =
|
||||
[
|
||||
nameof(EquipmentSlots.Pockets),
|
||||
nameof(EquipmentSlots.SecuredContainer),
|
||||
];
|
||||
private readonly FrozenSet<string> _slotsToCheck = [nameof(EquipmentSlots.Pockets), nameof(EquipmentSlots.SecuredContainer)];
|
||||
|
||||
/// <summary>
|
||||
/// Add equipment/weapons/loot to bot
|
||||
@@ -86,9 +82,7 @@ public class BotInventoryGenerator(
|
||||
var botInventory = GenerateInventoryBase();
|
||||
|
||||
// Get generated raid details bot will be spawned in
|
||||
var raidConfig = profileActivityService
|
||||
.GetProfileActivityRaidData(sessionId)
|
||||
?.RaidConfiguration;
|
||||
var raidConfig = profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
|
||||
|
||||
GenerateAndAddEquipmentToBot(
|
||||
sessionId,
|
||||
@@ -115,15 +109,7 @@ public class BotInventoryGenerator(
|
||||
);
|
||||
|
||||
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
|
||||
botLootGenerator.GenerateLoot(
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
botGenerationDetails,
|
||||
isPmc,
|
||||
botRole,
|
||||
botInventory,
|
||||
botLevel
|
||||
);
|
||||
botLootGenerator.GenerateLoot(sessionId, botJsonTemplate, botGenerationDetails, isPmc, botRole, botInventory, botLevel);
|
||||
|
||||
return botInventory;
|
||||
}
|
||||
@@ -150,11 +136,7 @@ public class BotInventoryGenerator(
|
||||
new Item { Id = questRaidItemsId, Template = ItemTpl.STASH_QUESTRAID },
|
||||
new Item { Id = questStashItemsId, Template = ItemTpl.STASH_QUESTOFFLINE },
|
||||
new Item { Id = sortingTableId, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE },
|
||||
new Item
|
||||
{
|
||||
Id = hideoutCustomizationStashId,
|
||||
Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION,
|
||||
},
|
||||
new Item { Id = hideoutCustomizationStashId, Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION },
|
||||
],
|
||||
Equipment = equipmentId,
|
||||
Stash = stashId,
|
||||
@@ -192,10 +174,7 @@ public class BotInventoryGenerator(
|
||||
GetRaidConfigurationRequestData? raidConfig
|
||||
)
|
||||
{
|
||||
_botConfig.Equipment.TryGetValue(
|
||||
botGeneratorHelper.GetBotEquipmentRole(botRole),
|
||||
out var botEquipConfig
|
||||
);
|
||||
_botConfig.Equipment.TryGetValue(botGeneratorHelper.GetBotEquipmentRole(botRole), out var botEquipConfig);
|
||||
var randomistionDetails = botHelper.GetBotRandomizationDetails(botLevel, botEquipConfig);
|
||||
|
||||
// Apply nighttime changes if its nighttime + there's changes to make
|
||||
@@ -205,11 +184,7 @@ public class BotInventoryGenerator(
|
||||
&& weatherHelper.IsNightTime(raidConfig.TimeVariant, raidConfig.Location)
|
||||
)
|
||||
{
|
||||
foreach (
|
||||
var (equipment, weight) in randomistionDetails
|
||||
.NighttimeChanges
|
||||
.EquipmentModsModifiers
|
||||
)
|
||||
foreach (var (equipment, weight) in randomistionDetails.NighttimeChanges.EquipmentModsModifiers)
|
||||
// Never let mod chance go outside 0 - 100
|
||||
{
|
||||
var newWeight = weight + randomistionDetails.EquipmentMods[equipment];
|
||||
@@ -260,11 +235,7 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
RootEquipmentSlot = EquipmentSlots.Pockets,
|
||||
// Unheard profiles have unique sized pockets
|
||||
RootEquipmentPool = GetPocketPoolByGameEdition(
|
||||
chosenGameVersion,
|
||||
templateInventory,
|
||||
isPmc
|
||||
),
|
||||
RootEquipmentPool = GetPocketPoolByGameEdition(chosenGameVersion, templateInventory, isPmc),
|
||||
ModPool = templateInventory.Mods,
|
||||
SpawnChances = wornItemChances,
|
||||
BotData = new BotData
|
||||
@@ -425,10 +396,7 @@ public class BotInventoryGenerator(
|
||||
/// </summary>
|
||||
/// <param name="templateEquipment">Equipment to filter TacticalVest of</param>
|
||||
/// <param name="botRole">Role of bot vests are being filtered for</param>
|
||||
public void FilterRigsToThoseWithProtection(
|
||||
Dictionary<EquipmentSlots, Dictionary<MongoId, double>> templateEquipment,
|
||||
string botRole
|
||||
)
|
||||
public void FilterRigsToThoseWithProtection(Dictionary<EquipmentSlots, Dictionary<MongoId, double>> templateEquipment, string botRole)
|
||||
{
|
||||
var tacVestsWithArmor = templateEquipment[EquipmentSlots.TacticalVest]
|
||||
.Where(kvp => itemHelper.ItemHasSlots(kvp.Key))
|
||||
@@ -438,9 +406,7 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Unable to filter to only armored rigs as bot: {botRole} has none in pool"
|
||||
);
|
||||
logger.Debug($"Unable to filter to only armored rigs as bot: {botRole} has none in pool");
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -469,9 +435,7 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool"
|
||||
);
|
||||
logger.Debug($"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool");
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -489,18 +453,11 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
double? spawnChance = _slotsToCheck.Contains(settings.RootEquipmentSlot.ToString())
|
||||
? 100
|
||||
: settings.SpawnChances.EquipmentChances.GetValueOrDefault(
|
||||
settings.RootEquipmentSlot.ToString()
|
||||
);
|
||||
: settings.SpawnChances.EquipmentChances.GetValueOrDefault(settings.RootEquipmentSlot.ToString());
|
||||
|
||||
if (!spawnChance.HasValue)
|
||||
{
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-no_spawn_chance_defined_for_equipment_slot",
|
||||
settings.RootEquipmentSlot
|
||||
)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("bot-no_spawn_chance_defined_for_equipment_slot", settings.RootEquipmentSlot));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -522,19 +479,12 @@ public class BotInventoryGenerator(
|
||||
return false;
|
||||
}
|
||||
|
||||
var chosenItemTpl = weightedRandomHelper.GetWeightedValue(
|
||||
settings.RootEquipmentPool
|
||||
);
|
||||
var chosenItemTpl = weightedRandomHelper.GetWeightedValue(settings.RootEquipmentPool);
|
||||
var dbResult = itemHelper.GetItem(chosenItemTpl);
|
||||
|
||||
if (!dbResult.Key)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-missing_item_template",
|
||||
chosenItemTpl
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-missing_item_template", chosenItemTpl));
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug($"EquipmentSlot-> {settings.RootEquipmentSlot}");
|
||||
@@ -584,10 +534,7 @@ public class BotInventoryGenerator(
|
||||
Template = pickedItemDb.Id,
|
||||
ParentId = settings.Inventory.Equipment,
|
||||
SlotId = settings.RootEquipmentSlot.ToString(),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(
|
||||
pickedItemDb,
|
||||
settings.BotData.Role
|
||||
),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(pickedItemDb, settings.BotData.Role),
|
||||
};
|
||||
|
||||
var botEquipBlacklist = botEquipmentFilterService.GetBotEquipmentBlacklist(
|
||||
@@ -599,27 +546,17 @@ public class BotInventoryGenerator(
|
||||
if (
|
||||
_botConfig.Equipment.ContainsKey(settings.BotData.EquipmentRole)
|
||||
&& settings.RandomisationDetails?.RandomisedArmorSlots != null
|
||||
&& settings.RandomisationDetails.RandomisedArmorSlots.Contains(
|
||||
settings.RootEquipmentSlot.ToString()
|
||||
)
|
||||
&& settings.RandomisationDetails.RandomisedArmorSlots.Contains(settings.RootEquipmentSlot.ToString())
|
||||
)
|
||||
// Filter out mods from relevant blacklist
|
||||
{
|
||||
settings.ModPool[pickedItemDb.Id] = GetFilteredDynamicModsForItem(
|
||||
pickedItemDb.Id,
|
||||
botEquipBlacklist.Equipment
|
||||
);
|
||||
settings.ModPool[pickedItemDb.Id] = GetFilteredDynamicModsForItem(pickedItemDb.Id, botEquipBlacklist.Equipment);
|
||||
}
|
||||
|
||||
var itemIsOnGenerateModBlacklist =
|
||||
settings.GenerateModsBlacklist != null
|
||||
&& settings.GenerateModsBlacklist.Contains(pickedItemDb.Id);
|
||||
settings.GenerateModsBlacklist != null && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id);
|
||||
// Does item have slots for sub-mods to be inserted into
|
||||
if (
|
||||
pickedItemDb.Properties?.Slots is not null
|
||||
&& pickedItemDb.Properties.Slots.Any()
|
||||
&& !itemIsOnGenerateModBlacklist
|
||||
)
|
||||
if (pickedItemDb.Properties?.Slots is not null && pickedItemDb.Properties.Slots.Any() && !itemIsOnGenerateModBlacklist)
|
||||
{
|
||||
var childItemsToAdd = botEquipmentModGenerator.GenerateModsForEquipment(
|
||||
[item],
|
||||
@@ -667,18 +604,14 @@ public class BotInventoryGenerator(
|
||||
return modsForSlot;
|
||||
}
|
||||
|
||||
var filteredMods = modsForSlot
|
||||
.Where(mod => !blacklistedMods.Contains(mod))
|
||||
.ToHashSet();
|
||||
var filteredMods = modsForSlot.Where(mod => !blacklistedMods.Contains(mod)).ToHashSet();
|
||||
if (filteredMods.Any())
|
||||
{
|
||||
// There's at least one tpl remaining, send it
|
||||
return filteredMods;
|
||||
}
|
||||
|
||||
logger.Warning(
|
||||
$"Filtering: '{modSlot}' resulted in 0 mods. Reverting to original set for slot"
|
||||
);
|
||||
logger.Warning($"Filtering: '{modSlot}' resulted in 0 mods. Reverting to original set for slot");
|
||||
|
||||
// Return original
|
||||
return modsForSlot;
|
||||
@@ -712,9 +645,7 @@ public class BotInventoryGenerator(
|
||||
foreach (var desiredWeapons in weaponSlotsToFill)
|
||||
// Add weapon to bot if true and bot json has something to put into the slot
|
||||
{
|
||||
if (
|
||||
desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any()
|
||||
)
|
||||
if (desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any())
|
||||
{
|
||||
AddWeaponAndMagazinesToInventory(
|
||||
sessionId,
|
||||
@@ -738,31 +669,19 @@ public class BotInventoryGenerator(
|
||||
/// <returns>What slots bot should have weapons generated for</returns>
|
||||
public IEnumerable<DesiredWeapons> GetDesiredWeaponsForBot(Chances equipmentChances)
|
||||
{
|
||||
var shouldSpawnPrimary = randomUtil.GetChance100(
|
||||
equipmentChances.EquipmentChances["FirstPrimaryWeapon"]
|
||||
);
|
||||
var shouldSpawnPrimary = randomUtil.GetChance100(equipmentChances.EquipmentChances["FirstPrimaryWeapon"]);
|
||||
return
|
||||
[
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.FirstPrimaryWeapon,
|
||||
ShouldSpawn = shouldSpawnPrimary,
|
||||
},
|
||||
new DesiredWeapons { Slot = EquipmentSlots.FirstPrimaryWeapon, ShouldSpawn = shouldSpawnPrimary },
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.SecondPrimaryWeapon,
|
||||
ShouldSpawn =
|
||||
shouldSpawnPrimary
|
||||
&& randomUtil.GetChance100(
|
||||
equipmentChances.EquipmentChances["SecondPrimaryWeapon"]
|
||||
),
|
||||
ShouldSpawn = shouldSpawnPrimary && randomUtil.GetChance100(equipmentChances.EquipmentChances["SecondPrimaryWeapon"]),
|
||||
},
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.Holster,
|
||||
ShouldSpawn =
|
||||
!shouldSpawnPrimary
|
||||
|| randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]), // No primary = force pistol
|
||||
ShouldSpawn = !shouldSpawnPrimary || randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]), // No primary = force pistol
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -804,12 +723,7 @@ public class BotInventoryGenerator(
|
||||
|
||||
botInventory.Items.AddRange(generatedWeapon.Weapon);
|
||||
|
||||
botWeaponGenerator.AddExtraMagazinesToInventory(
|
||||
generatedWeapon,
|
||||
itemGenerationWeights.Items.Magazines,
|
||||
botInventory,
|
||||
botRole
|
||||
);
|
||||
botWeaponGenerator.AddExtraMagazinesToInventory(generatedWeapon, itemGenerationWeights.Items.Magazines, botInventory, botRole);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,7 @@ using SPTarkov.Server.Core.Utils;
|
||||
namespace SPTarkov.Server.Core.Generators;
|
||||
|
||||
[Injectable]
|
||||
public class BotLevelGenerator(
|
||||
ISptLogger<BotLevelGenerator> logger,
|
||||
RandomUtil randomUtil,
|
||||
DatabaseService databaseService
|
||||
)
|
||||
public class BotLevelGenerator(ISptLogger<BotLevelGenerator> logger, RandomUtil randomUtil, DatabaseService databaseService)
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a randomised bot level and exp value
|
||||
@@ -24,11 +20,7 @@ public class BotLevelGenerator(
|
||||
/// <param name="botGenerationDetails">Details to help generate a bot</param>
|
||||
/// <param name="bot">Bot the level is being generated for</param>
|
||||
/// <returns>IRandomisedBotLevelResult object</returns>
|
||||
public RandomisedBotLevelResult GenerateBotLevel(
|
||||
MinMax<int> levelDetails,
|
||||
BotGenerationDetails botGenerationDetails,
|
||||
BotBase bot
|
||||
)
|
||||
public RandomisedBotLevelResult GenerateBotLevel(MinMax<int> levelDetails, BotGenerationDetails botGenerationDetails, BotBase bot)
|
||||
{
|
||||
if (!botGenerationDetails.IsPmc)
|
||||
{
|
||||
@@ -36,18 +28,11 @@ public class BotLevelGenerator(
|
||||
}
|
||||
|
||||
var expTable = databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable;
|
||||
var botLevelRange = GetRelativePmcBotLevelRange(
|
||||
botGenerationDetails,
|
||||
levelDetails,
|
||||
expTable.Length
|
||||
);
|
||||
var botLevelRange = GetRelativePmcBotLevelRange(botGenerationDetails, levelDetails, expTable.Length);
|
||||
|
||||
// Get random level based on the exp table.
|
||||
var exp = 0;
|
||||
var level = int.Parse(
|
||||
ChooseBotLevel(botLevelRange.Min, botLevelRange.Max, 1, 1.15)
|
||||
.ToString(CultureInfo.InvariantCulture)
|
||||
); // TODO - nasty double to string to int conversion
|
||||
var level = int.Parse(ChooseBotLevel(botLevelRange.Min, botLevelRange.Max, 1, 1.15).ToString(CultureInfo.InvariantCulture)); // TODO - nasty double to string to int conversion
|
||||
for (var i = 0; i < level; i++)
|
||||
{
|
||||
exp += expTable[i].Experience;
|
||||
|
||||
@@ -51,11 +51,7 @@ public class BotLootGenerator(
|
||||
limitsForBotDict[limit.Key] = 0;
|
||||
}
|
||||
|
||||
return new ItemSpawnLimitSettings
|
||||
{
|
||||
CurrentLimits = limitsForBotDict,
|
||||
GlobalLimits = GetItemSpawnLimitsForBotType(botRole),
|
||||
};
|
||||
return new ItemSpawnLimitSettings { CurrentLimits = limitsForBotDict, GlobalLimits = GetItemSpawnLimitsForBotType(botRole) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,20 +91,14 @@ public class BotLootGenerator(
|
||||
|| itemCounts.Grenades.Weights is null
|
||||
)
|
||||
{
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText("bot-unable_to_generate_bot_loot", botRole)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("bot-unable_to_generate_bot_loot", botRole));
|
||||
return;
|
||||
}
|
||||
|
||||
var backpackLootCount = weightedRandomHelper.GetWeightedValue(
|
||||
itemCounts.BackpackLoot.Weights
|
||||
);
|
||||
var backpackLootCount = weightedRandomHelper.GetWeightedValue(itemCounts.BackpackLoot.Weights);
|
||||
var pocketLootCount = weightedRandomHelper.GetWeightedValue(itemCounts.PocketLoot.Weights);
|
||||
var vestLootCount = weightedRandomHelper.GetWeightedValue(itemCounts.VestLoot.Weights);
|
||||
var specialLootItemCount = weightedRandomHelper.GetWeightedValue(
|
||||
itemCounts.SpecialItems.Weights
|
||||
);
|
||||
var specialLootItemCount = weightedRandomHelper.GetWeightedValue(itemCounts.SpecialItems.Weights);
|
||||
var healingItemCount = weightedRandomHelper.GetWeightedValue(itemCounts.Healing.Weights);
|
||||
var drugItemCount = weightedRandomHelper.GetWeightedValue(itemCounts.Drugs.Weights);
|
||||
var foodItemCount = weightedRandomHelper.GetWeightedValue(itemCounts.Food.Weights);
|
||||
@@ -142,12 +132,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Special items
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.Special,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Special, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
specialLootItemCount,
|
||||
botInventory,
|
||||
@@ -158,12 +143,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Healing items / Meds
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.HealingItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.HealingItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
healingItemCount,
|
||||
botInventory,
|
||||
@@ -176,12 +156,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Drugs
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.DrugItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrugItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
drugItemCount,
|
||||
botInventory,
|
||||
@@ -194,12 +169,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Food
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.FoodItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.FoodItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
foodItemCount,
|
||||
botInventory,
|
||||
@@ -212,12 +182,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Drink
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.DrinkItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrinkItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
drinkItemCount,
|
||||
botInventory,
|
||||
@@ -230,12 +195,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Currency
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.CurrencyItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.CurrencyItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
currencyItemCount,
|
||||
botInventory,
|
||||
@@ -248,12 +208,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Stims
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.StimItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.StimItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
stimItemCount,
|
||||
botInventory,
|
||||
@@ -266,12 +221,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Grenades
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.GrenadeItems,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.GrenadeItems, botJsonTemplate),
|
||||
[EquipmentSlots.Pockets, EquipmentSlots.TacticalVest], // Can't use containersBotHasEquipped as we don't want grenades added to backpack
|
||||
grenadeCount,
|
||||
botInventory,
|
||||
@@ -304,20 +254,11 @@ public class BotLootGenerator(
|
||||
}
|
||||
|
||||
var backpackLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Backpack.GetRoubleValue(
|
||||
botLevel,
|
||||
botGenerationDetails.Location
|
||||
)
|
||||
? _pmcConfig.LootSettings.Backpack.GetRoubleValue(botLevel, botGenerationDetails.Location)
|
||||
: 0;
|
||||
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.Backpack,
|
||||
botJsonTemplate,
|
||||
itemPriceLimits?.Backpack
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Backpack, botJsonTemplate, itemPriceLimits?.Backpack),
|
||||
[EquipmentSlots.Backpack],
|
||||
backpackLootCount,
|
||||
botInventory,
|
||||
@@ -329,22 +270,14 @@ public class BotLootGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
var vestLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Vest.GetRoubleValue(botLevel, botGenerationDetails.Location)
|
||||
: 0;
|
||||
var vestLootRoubleTotal = isPmc ? _pmcConfig.LootSettings.Vest.GetRoubleValue(botLevel, botGenerationDetails.Location) : 0;
|
||||
|
||||
// TacticalVest - generate loot if they have one
|
||||
if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest))
|
||||
// Vest
|
||||
{
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.Vest,
|
||||
botJsonTemplate,
|
||||
itemPriceLimits?.Vest
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Vest, botJsonTemplate, itemPriceLimits?.Vest),
|
||||
[EquipmentSlots.TacticalVest],
|
||||
vestLootCount,
|
||||
botInventory,
|
||||
@@ -356,19 +289,11 @@ public class BotLootGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
var pocketLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Pocket.GetRoubleValue(botLevel, botGenerationDetails.Location)
|
||||
: 0;
|
||||
var pocketLootRoubleTotal = isPmc ? _pmcConfig.LootSettings.Pocket.GetRoubleValue(botLevel, botGenerationDetails.Location) : 0;
|
||||
|
||||
// Pockets
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.Pocket,
|
||||
botJsonTemplate,
|
||||
itemPriceLimits?.Pocket
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Pocket, botJsonTemplate, itemPriceLimits?.Pocket),
|
||||
[EquipmentSlots.Pockets],
|
||||
pocketLootCount,
|
||||
botInventory,
|
||||
@@ -385,12 +310,7 @@ public class BotLootGenerator(
|
||||
if (!isPmc || (isPmc && _pmcConfig.AddSecureContainerLootFromBotConfig))
|
||||
{
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
isPmc,
|
||||
LootCacheType.Secure,
|
||||
botJsonTemplate
|
||||
),
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate),
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
50,
|
||||
botInventory,
|
||||
@@ -423,17 +343,11 @@ public class BotLootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="botInventory">Bot to check</param>
|
||||
/// <returns>Array of available slots</returns>
|
||||
protected HashSet<EquipmentSlots> GetAvailableContainersBotCanStoreItemsIn(
|
||||
BotBaseInventory botInventory
|
||||
)
|
||||
protected HashSet<EquipmentSlots> GetAvailableContainersBotCanStoreItemsIn(BotBaseInventory botInventory)
|
||||
{
|
||||
HashSet<EquipmentSlots> result = [EquipmentSlots.Pockets];
|
||||
|
||||
if (
|
||||
(botInventory.Items ?? []).Any(item =>
|
||||
item.SlotId == nameof(EquipmentSlots.TacticalVest)
|
||||
)
|
||||
)
|
||||
if ((botInventory.Items ?? []).Any(item => item.SlotId == nameof(EquipmentSlots.TacticalVest)))
|
||||
{
|
||||
result.Add(EquipmentSlots.TacticalVest);
|
||||
}
|
||||
@@ -467,10 +381,7 @@ public class BotLootGenerator(
|
||||
|
||||
// AFAK
|
||||
AddLootFromPool(
|
||||
new Dictionary<MongoId, double>
|
||||
{
|
||||
{ ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT, 1 },
|
||||
},
|
||||
new Dictionary<MongoId, double> { { ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT, 1 } },
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
10,
|
||||
botInventory,
|
||||
@@ -527,17 +438,12 @@ public class BotLootGenerator(
|
||||
|
||||
if (!key)
|
||||
{
|
||||
logger.Warning(
|
||||
$"Unable to process item tpl: {weightedItemTpl} for slots: {equipmentSlots} on bot: {botRole}"
|
||||
);
|
||||
logger.Warning($"Unable to process item tpl: {weightedItemTpl} for slots: {equipmentSlots} on bot: {botRole}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
itemSpawnLimits is not null
|
||||
&& ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits)
|
||||
)
|
||||
if (itemSpawnLimits is not null && ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits))
|
||||
{
|
||||
// Remove item from pool to prevent it being picked again
|
||||
pool.Remove(weightedItemTpl);
|
||||
@@ -553,19 +459,14 @@ public class BotLootGenerator(
|
||||
{
|
||||
Id = newRootItemId,
|
||||
Template = itemToAddTemplate?.Id ?? MongoId.Empty(),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(
|
||||
itemToAddTemplate,
|
||||
botRole
|
||||
),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(itemToAddTemplate, botRole),
|
||||
},
|
||||
];
|
||||
|
||||
// Is Simple-Wallet / WZ wallet
|
||||
if (_botConfig.WalletLoot.WalletTplPool.Contains(weightedItemTpl))
|
||||
{
|
||||
var addCurrencyToWallet = randomUtil.GetChance100(
|
||||
_botConfig.WalletLoot.ChancePercent
|
||||
);
|
||||
var addCurrencyToWallet = randomUtil.GetChance100(_botConfig.WalletLoot.ChancePercent);
|
||||
if (addCurrencyToWallet)
|
||||
{
|
||||
// Create the currency items we want to add to wallet
|
||||
@@ -584,12 +485,7 @@ public class BotLootGenerator(
|
||||
// Add each currency to wallet
|
||||
foreach (var itemToAdd in itemsToAdd)
|
||||
{
|
||||
inventoryHelper.PlaceItemInContainer(
|
||||
containerGrid,
|
||||
itemToAdd,
|
||||
itemWithChildrenToAdd[0].Id,
|
||||
"main"
|
||||
);
|
||||
inventoryHelper.PlaceItemInContainer(containerGrid, itemToAdd, itemWithChildrenToAdd[0].Id, "main");
|
||||
}
|
||||
|
||||
itemWithChildrenToAdd.AddRange(itemsToAdd.SelectMany(x => x));
|
||||
@@ -618,9 +514,7 @@ public class BotLootGenerator(
|
||||
// Bot has no container to put item in, exit
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Unable to add: {totalItemCount} items to bot as it lacks a container to include them"
|
||||
);
|
||||
logger.Debug($"Unable to add: {totalItemCount} items to bot as it lacks a container to include them");
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -670,24 +564,17 @@ public class BotLootGenerator(
|
||||
List<List<Item>> result = [];
|
||||
|
||||
// Choose how many stacks of currency will be added to wallet
|
||||
var itemCount = randomUtil.GetInt(
|
||||
_botConfig.WalletLoot.ItemCount.Min,
|
||||
_botConfig.WalletLoot.ItemCount.Max
|
||||
);
|
||||
var itemCount = randomUtil.GetInt(_botConfig.WalletLoot.ItemCount.Min, _botConfig.WalletLoot.ItemCount.Max);
|
||||
for (var index = 0; index < itemCount; index++)
|
||||
{
|
||||
// Choose the size of the currency stack - default is 5k, 10k, 15k, 20k, 25k
|
||||
var chosenStackCount = weightedRandomHelper.GetWeightedValue(
|
||||
_botConfig.WalletLoot.StackSizeWeight
|
||||
);
|
||||
var chosenStackCount = weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.StackSizeWeight);
|
||||
List<Item> items =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = weightedRandomHelper.GetWeightedValue(
|
||||
_botConfig.WalletLoot.CurrencyWeight
|
||||
),
|
||||
Template = weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.CurrencyWeight),
|
||||
ParentId = walletId,
|
||||
Upd = new Upd { StackObjectsCount = int.Parse(chosenStackCount) },
|
||||
},
|
||||
@@ -705,12 +592,7 @@ public class BotLootGenerator(
|
||||
/// <param name="itemToAddChildrenTo">Item to add children to</param>
|
||||
/// <param name="isPmc">Is the item being generated for a pmc (affects money/ammo stack sizes)</param>
|
||||
/// <param name="botRole">role bot has that owns item</param>
|
||||
public void AddRequiredChildItemsToParent(
|
||||
TemplateItem? itemToAddTemplate,
|
||||
List<Item> itemToAddChildrenTo,
|
||||
bool isPmc,
|
||||
string botRole
|
||||
)
|
||||
public void AddRequiredChildItemsToParent(TemplateItem? itemToAddTemplate, List<Item> itemToAddChildrenTo, bool isPmc, string botRole)
|
||||
{
|
||||
// Fill ammo box
|
||||
if (itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO_BOX))
|
||||
@@ -792,9 +674,7 @@ public class BotLootGenerator(
|
||||
var weaponRootItem = generatedWeapon.Weapon?.FirstOrDefault();
|
||||
if (weaponRootItem is null)
|
||||
{
|
||||
logger.Error(
|
||||
$"Generated null loose weapon: {chosenWeaponType} for: {botRole} level: {botLevel}, skipping"
|
||||
);
|
||||
logger.Error($"Generated null loose weapon: {chosenWeaponType} for: {botRole} level: {botLevel}, skipping");
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -811,9 +691,7 @@ public class BotLootGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Failed to add additional weapon: {weaponRootItem.Id} to bot backpack, reason: {result.ToString()}"
|
||||
);
|
||||
logger.Debug($"Failed to add additional weapon: {weaponRootItem.Id} to bot backpack, reason: {result.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -826,11 +704,7 @@ public class BotLootGenerator(
|
||||
/// <param name="botRole">Bot type</param>
|
||||
/// <param name="itemSpawnLimits"></param>
|
||||
/// <returns>true if item has reached spawn limit</returns>
|
||||
protected bool ItemHasReachedSpawnLimit(
|
||||
TemplateItem? itemTemplate,
|
||||
string botRole,
|
||||
ItemSpawnLimitSettings? itemSpawnLimits
|
||||
)
|
||||
protected bool ItemHasReachedSpawnLimit(TemplateItem? itemTemplate, string botRole, ItemSpawnLimitSettings? itemSpawnLimits)
|
||||
{
|
||||
// PMCs and scavs have different sections of bot config for spawn limits
|
||||
if (itemSpawnLimits is not null && itemSpawnLimits.GlobalLimits?.Count == 0)
|
||||
@@ -861,10 +735,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Check if over limit
|
||||
var currentLimitCount = itemSpawnLimits.CurrentLimits[idToCheckFor.Value];
|
||||
if (
|
||||
itemSpawnLimits.CurrentLimits[idToCheckFor.Value]
|
||||
> itemSpawnLimits.GlobalLimits[idToCheckFor.Value]
|
||||
)
|
||||
if (itemSpawnLimits.CurrentLimits[idToCheckFor.Value] > itemSpawnLimits.GlobalLimits[idToCheckFor.Value])
|
||||
{
|
||||
// Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
|
||||
if (currentLimitCount > currentLimitCount * 10)
|
||||
@@ -911,9 +782,7 @@ public class BotLootGenerator(
|
||||
|
||||
itemHelper.AddUpdObjectToItem(moneyItem);
|
||||
|
||||
moneyItem.Upd.StackObjectsCount = int.Parse(
|
||||
weightedRandomHelper.GetWeightedValue(currencyWeight)
|
||||
);
|
||||
moneyItem.Upd.StackObjectsCount = int.Parse(weightedRandomHelper.GetWeightedValue(currencyWeight));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -948,12 +817,7 @@ public class BotLootGenerator(
|
||||
return _botConfig.ItemSpawnLimits[botRole.ToLowerInvariant()];
|
||||
}
|
||||
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-unable_to_find_spawn_limits_fallback_to_defaults",
|
||||
botRole
|
||||
)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("bot-unable_to_find_spawn_limits_fallback_to_defaults", botRole));
|
||||
|
||||
return [];
|
||||
}
|
||||
@@ -964,10 +828,7 @@ public class BotLootGenerator(
|
||||
/// <param name="itemTemplate">item we want to look for in spawn limits</param>
|
||||
/// <param name="spawnLimits">Limits to check for item</param>
|
||||
/// <returns>id as string, otherwise undefined</returns>
|
||||
public MongoId? GetMatchingIdFromSpawnLimits(
|
||||
TemplateItem itemTemplate,
|
||||
Dictionary<MongoId, double> spawnLimits
|
||||
)
|
||||
public MongoId? GetMatchingIdFromSpawnLimits(TemplateItem itemTemplate, Dictionary<MongoId, double> spawnLimits)
|
||||
{
|
||||
if (spawnLimits.ContainsKey(itemTemplate.Id))
|
||||
{
|
||||
|
||||
@@ -37,9 +37,7 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
protected const string _modMagazineSlotId = "mod_magazine";
|
||||
protected readonly BotConfig _botConfig = configServer.GetConfig<BotConfig>();
|
||||
protected readonly IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(
|
||||
inventoryMagGenComponents
|
||||
);
|
||||
protected readonly IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(inventoryMagGenComponents);
|
||||
protected readonly PmcConfig _pmcConfig = configServer.GetConfig<PmcConfig>();
|
||||
protected readonly RepairConfig _repairConfig = configServer.GetConfig<RepairConfig>();
|
||||
|
||||
@@ -93,10 +91,7 @@ public class BotWeaponGenerator(
|
||||
/// <param name="equipmentSlot">Primary/secondary/holster</param>
|
||||
/// <param name="botTemplateInventory">e.g. assault.json</param>
|
||||
/// <returns>Weapon template</returns>
|
||||
public MongoId PickWeightedWeaponTemplateFromPool(
|
||||
string equipmentSlot,
|
||||
BotTypeInventory botTemplateInventory
|
||||
)
|
||||
public MongoId PickWeightedWeaponTemplateFromPool(string equipmentSlot, BotTypeInventory botTemplateInventory)
|
||||
{
|
||||
if (!Enum.TryParse(equipmentSlot, out EquipmentSlots key))
|
||||
{
|
||||
@@ -146,23 +141,14 @@ public class BotWeaponGenerator(
|
||||
// Find ammo to use when filling magazines/chamber
|
||||
if (botTemplateInventory.Ammo is null)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("bot-no_ammo_found_in_bot_json", botRole)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-no_ammo_found_in_bot_json", botRole));
|
||||
logger.Error(serverLocalisationService.GetText("bot-generation_failed"));
|
||||
}
|
||||
|
||||
var ammoTpl = GetWeightedCompatibleAmmo(botTemplateInventory.Ammo, weaponItemTemplate);
|
||||
|
||||
// Create with just base weapon item
|
||||
var weaponWithModsArray = ConstructWeaponBaseList(
|
||||
weaponTpl,
|
||||
weaponParentId,
|
||||
slotName,
|
||||
weaponItemTemplate,
|
||||
botRole
|
||||
)
|
||||
.ToList();
|
||||
var weaponWithModsArray = ConstructWeaponBaseList(weaponTpl, weaponParentId, slotName, weaponItemTemplate, botRole).ToList();
|
||||
|
||||
// Chance to add randomised weapon enhancement
|
||||
if (isPmc && randomUtil.GetChance100(_pmcConfig.WeaponHasEnhancementChancePercent))
|
||||
@@ -198,28 +184,17 @@ public class BotWeaponGenerator(
|
||||
WeaponStats = new WeaponStats(),
|
||||
ConflictingItemTpls = [],
|
||||
};
|
||||
weaponWithModsArray = botEquipmentModGenerator.GenerateModsForWeapon(
|
||||
sessionId,
|
||||
generateWeaponModsRequest
|
||||
);
|
||||
weaponWithModsArray = botEquipmentModGenerator.GenerateModsForWeapon(sessionId, generateWeaponModsRequest);
|
||||
}
|
||||
|
||||
// Use weapon preset from globals.json if weapon isn't valid
|
||||
if (!IsWeaponValid(weaponWithModsArray, botRole))
|
||||
// Weapon is bad, fall back to weapons preset
|
||||
{
|
||||
weaponWithModsArray = GetPresetWeaponMods(
|
||||
weaponTpl,
|
||||
slotName,
|
||||
weaponParentId,
|
||||
weaponItemTemplate,
|
||||
botRole
|
||||
);
|
||||
weaponWithModsArray = GetPresetWeaponMods(weaponTpl, slotName, weaponParentId, weaponItemTemplate, botRole);
|
||||
}
|
||||
|
||||
var tempList = cloner.Clone(
|
||||
weaponWithModsArray.Where(item => item.SlotId == _modMagazineSlotId)
|
||||
);
|
||||
var tempList = cloner.Clone(weaponWithModsArray.Where(item => item.SlotId == _modMagazineSlotId));
|
||||
// Fill existing magazines to full and sync ammo type
|
||||
foreach (var magazine in tempList)
|
||||
{
|
||||
@@ -229,16 +204,11 @@ public class BotWeaponGenerator(
|
||||
// Add cartridge(s) to gun chamber(s)
|
||||
if (
|
||||
(weaponItemTemplate.Properties?.Chambers).Any()
|
||||
&& weaponItemTemplate
|
||||
.Properties.Chambers.FirstOrDefault()
|
||||
.Props.Filters.FirstOrDefault()
|
||||
.Filter.Contains(ammoTpl)
|
||||
&& weaponItemTemplate.Properties.Chambers.FirstOrDefault().Props.Filters.FirstOrDefault().Filter.Contains(ammoTpl)
|
||||
)
|
||||
{
|
||||
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
|
||||
var chamberSlotNames = weaponItemTemplate.Properties.Chambers.Select(chamberSlot =>
|
||||
chamberSlot.Name
|
||||
);
|
||||
var chamberSlotNames = weaponItemTemplate.Properties.Chambers.Select(chamberSlot => chamberSlot.Name);
|
||||
AddCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames.ToList());
|
||||
}
|
||||
|
||||
@@ -274,11 +244,7 @@ public class BotWeaponGenerator(
|
||||
/// <param name="weaponWithModsList">Weapon and mods</param>
|
||||
/// <param name="ammoTemplate">Cartridge to add to weapon</param>
|
||||
/// <param name="chamberSlotIds">Name of slots to create or add ammo to</param>
|
||||
protected void AddCartridgeToChamber(
|
||||
List<Item> weaponWithModsList,
|
||||
MongoId ammoTemplate,
|
||||
IEnumerable<string> chamberSlotIds
|
||||
)
|
||||
protected void AddCartridgeToChamber(List<Item> weaponWithModsList, MongoId ammoTemplate, IEnumerable<string> chamberSlotIds)
|
||||
{
|
||||
foreach (var slotId in chamberSlotIds)
|
||||
{
|
||||
@@ -332,10 +298,7 @@ public class BotWeaponGenerator(
|
||||
Template = weaponTemplate,
|
||||
ParentId = weaponParentId,
|
||||
SlotId = equipmentSlot,
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(
|
||||
weaponItemTemplate,
|
||||
botRole
|
||||
),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(weaponItemTemplate, botRole),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -359,10 +322,7 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
// Invalid weapon generated, fallback to preset
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-weapon_generated_incorrect_using_default",
|
||||
$"{weaponTemplate} - {itemTemplate.Name}"
|
||||
)
|
||||
serverLocalisationService.GetText("bot-weapon_generated_incorrect_using_default", $"{weaponTemplate} - {itemTemplate.Name}")
|
||||
);
|
||||
List<Item> weaponMods = [];
|
||||
|
||||
@@ -383,18 +343,13 @@ public class BotWeaponGenerator(
|
||||
var parentItem = preset.Items[0];
|
||||
parentItem.ParentId = weaponParentId;
|
||||
parentItem.SlotId = equipmentSlot;
|
||||
parentItem.Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(
|
||||
itemTemplate,
|
||||
botRole
|
||||
);
|
||||
parentItem.Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate, botRole);
|
||||
preset.Items[0] = parentItem;
|
||||
weaponMods.AddRange(preset.Items);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("bot-missing_weapon_preset", weaponTemplate)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-missing_weapon_preset", weaponTemplate));
|
||||
}
|
||||
|
||||
return weaponMods;
|
||||
@@ -417,16 +372,10 @@ public class BotWeaponGenerator(
|
||||
}
|
||||
|
||||
// Iterate over required slots in db item, check mod exists for that slot
|
||||
foreach (
|
||||
var modSlotTemplate in modTemplate.Properties.Slots?.Where(slot =>
|
||||
slot.Required.GetValueOrDefault(false)
|
||||
) ?? []
|
||||
)
|
||||
foreach (var modSlotTemplate in modTemplate.Properties.Slots?.Where(slot => slot.Required.GetValueOrDefault(false)) ?? [])
|
||||
{
|
||||
var slotName = modSlotTemplate.Name;
|
||||
var hasWeaponSlotItem = weaponItemList.Any(weaponItem =>
|
||||
weaponItem.ParentId == mod.Id && weaponItem.SlotId == slotName
|
||||
);
|
||||
var hasWeaponSlotItem = weaponItemList.Any(weaponItem => weaponItem.ParentId == mod.Id && weaponItem.SlotId == slotName);
|
||||
if (!hasWeaponSlotItem)
|
||||
{
|
||||
logger.Warning(
|
||||
@@ -467,18 +416,12 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
var weaponAndMods = generatedWeaponResult.Weapon;
|
||||
var weaponTemplate = generatedWeaponResult.WeaponTemplate;
|
||||
var magazineTpl = GetMagazineTemplateFromWeaponTemplate(
|
||||
weaponAndMods,
|
||||
weaponTemplate,
|
||||
botRole
|
||||
);
|
||||
var magazineTpl = GetMagazineTemplateFromWeaponTemplate(weaponAndMods, weaponTemplate, botRole);
|
||||
|
||||
var magTemplate = itemHelper.GetItem(magazineTpl.Value).Value;
|
||||
if (magTemplate is null)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("bot-unable_to_find_magazine_item", magazineTpl)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-unable_to_find_magazine_item", magazineTpl));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -487,36 +430,20 @@ public class BotWeaponGenerator(
|
||||
var ammoTemplate = itemHelper.GetItem(generatedWeaponResult.ChosenAmmoTemplate);
|
||||
if (!ammoTemplate.Key)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-unable_to_find_ammo_item",
|
||||
generatedWeaponResult.ChosenAmmoTemplate
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-unable_to_find_ammo_item", generatedWeaponResult.ChosenAmmoTemplate));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Has an UBGL
|
||||
if (
|
||||
generatedWeaponResult.ChosenUbglAmmoTemplate is not null
|
||||
&& !generatedWeaponResult.ChosenUbglAmmoTemplate.Value.IsEmpty()
|
||||
)
|
||||
if (generatedWeaponResult.ChosenUbglAmmoTemplate is not null && !generatedWeaponResult.ChosenUbglAmmoTemplate.Value.IsEmpty())
|
||||
{
|
||||
AddUbglGrenadesToBotInventory(weaponAndMods, generatedWeaponResult, inventory);
|
||||
}
|
||||
|
||||
var inventoryMagGenModel = new InventoryMagGen(
|
||||
magWeights,
|
||||
magTemplate,
|
||||
weaponTemplate,
|
||||
ammoTemplate.Value,
|
||||
inventory
|
||||
);
|
||||
var inventoryMagGenModel = new InventoryMagGen(magWeights, magTemplate, weaponTemplate, ammoTemplate.Value, inventory);
|
||||
|
||||
_inventoryMagGenComponents
|
||||
.FirstOrDefault(v => v.CanHandleInventoryMagGen(inventoryMagGenModel))
|
||||
.Process(inventoryMagGenModel);
|
||||
_inventoryMagGenComponents.FirstOrDefault(v => v.CanHandleInventoryMagGen(inventoryMagGenModel)).Process(inventoryMagGenModel);
|
||||
|
||||
// Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly)
|
||||
AddAmmoToSecureContainer(
|
||||
@@ -551,29 +478,14 @@ public class BotWeaponGenerator(
|
||||
};
|
||||
|
||||
// get ammo template from db
|
||||
var ubglAmmoDbTemplate = itemHelper
|
||||
.GetItem(generatedWeaponResult.ChosenUbglAmmoTemplate.Value)
|
||||
.Value;
|
||||
var ubglAmmoDbTemplate = itemHelper.GetItem(generatedWeaponResult.ChosenUbglAmmoTemplate.Value).Value;
|
||||
|
||||
// Add greandes to bot inventory
|
||||
var ubglAmmoGenModel = new InventoryMagGen(
|
||||
ubglMinMax,
|
||||
ubglDbTemplate,
|
||||
ubglDbTemplate,
|
||||
ubglAmmoDbTemplate,
|
||||
inventory
|
||||
);
|
||||
_inventoryMagGenComponents
|
||||
.FirstOrDefault(v => v.CanHandleInventoryMagGen(ubglAmmoGenModel))
|
||||
.Process(ubglAmmoGenModel);
|
||||
var ubglAmmoGenModel = new InventoryMagGen(ubglMinMax, ubglDbTemplate, ubglDbTemplate, ubglAmmoDbTemplate, inventory);
|
||||
_inventoryMagGenComponents.FirstOrDefault(v => v.CanHandleInventoryMagGen(ubglAmmoGenModel)).Process(ubglAmmoGenModel);
|
||||
|
||||
// Store extra grenades in secure container
|
||||
AddAmmoToSecureContainer(
|
||||
5,
|
||||
generatedWeaponResult.ChosenUbglAmmoTemplate.Value,
|
||||
20,
|
||||
inventory
|
||||
);
|
||||
AddAmmoToSecureContainer(5, generatedWeaponResult.ChosenUbglAmmoTemplate.Value, 20, inventory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -583,12 +495,7 @@ public class BotWeaponGenerator(
|
||||
/// <param name="ammoTpl">Ammo type to add.</param>
|
||||
/// <param name="stackSize">Size of the ammo stack to add.</param>
|
||||
/// <param name="inventory">Player inventory.</param>
|
||||
protected void AddAmmoToSecureContainer(
|
||||
int stackCount,
|
||||
MongoId ammoTpl,
|
||||
int stackSize,
|
||||
BotBaseInventory inventory
|
||||
)
|
||||
protected void AddAmmoToSecureContainer(int stackCount, MongoId ammoTpl, int stackSize, BotBaseInventory inventory)
|
||||
{
|
||||
var container = new HashSet<EquipmentSlots> { EquipmentSlots.SecuredContainer };
|
||||
for (var i = 0; i < stackCount; i++)
|
||||
@@ -618,11 +525,7 @@ public class BotWeaponGenerator(
|
||||
/// <param name="weaponTemplate">Weapon to get magazine template for.</param>
|
||||
/// <param name="botRole">The bot type we are getting the magazine for.</param>
|
||||
/// <returns>Magazine template string.</returns>
|
||||
protected MongoId? GetMagazineTemplateFromWeaponTemplate(
|
||||
IEnumerable<Item> weaponMods,
|
||||
TemplateItem weaponTemplate,
|
||||
string botRole
|
||||
)
|
||||
protected MongoId? GetMagazineTemplateFromWeaponTemplate(IEnumerable<Item> weaponMods, TemplateItem weaponTemplate, string botRole)
|
||||
{
|
||||
var magazine = weaponMods.FirstOrDefault(m => m.SlotId == _modMagazineSlotId);
|
||||
if (magazine is null)
|
||||
@@ -667,16 +570,10 @@ public class BotWeaponGenerator(
|
||||
/// <param name="cartridgePool">Dictionary of all cartridges keyed by type e.g. Caliber556x45NATO</param>
|
||||
/// <param name="weaponTemplate">Weapon details from database we want to pick ammo for</param>
|
||||
/// <returns>Ammo template that works with the desired gun</returns>
|
||||
protected MongoId GetWeightedCompatibleAmmo(
|
||||
Dictionary<string, Dictionary<MongoId, double>> cartridgePool,
|
||||
TemplateItem weaponTemplate
|
||||
)
|
||||
protected MongoId GetWeightedCompatibleAmmo(Dictionary<string, Dictionary<MongoId, double>> cartridgePool, TemplateItem weaponTemplate)
|
||||
{
|
||||
var desiredCaliber = GetWeaponCaliber(weaponTemplate);
|
||||
if (
|
||||
!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon)
|
||||
|| cartridgePoolForWeapon?.Count == 0
|
||||
)
|
||||
if (!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon) || cartridgePoolForWeapon?.Count == 0)
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
@@ -699,9 +596,7 @@ public class BotWeaponGenerator(
|
||||
}
|
||||
|
||||
// Get cartridges the weapons first chamber allow
|
||||
var compatibleCartridgesInTemplate = GetCompatibleCartridgesFromWeaponTemplate(
|
||||
weaponTemplate
|
||||
);
|
||||
var compatibleCartridgesInTemplate = GetCompatibleCartridgesFromWeaponTemplate(weaponTemplate);
|
||||
if (compatibleCartridgesInTemplate.Count == 0)
|
||||
// No chamber data found in weapon, send default
|
||||
{
|
||||
@@ -722,9 +617,7 @@ public class BotWeaponGenerator(
|
||||
if (!compatibleCartridges.Any())
|
||||
{
|
||||
// Get cartridges from the weapons first magazine in filters
|
||||
var compatibleCartridgesInMagazine = GetCompatibleCartridgesFromMagazineTemplate(
|
||||
weaponTemplate
|
||||
);
|
||||
var compatibleCartridgesInMagazine = GetCompatibleCartridgesFromMagazineTemplate(weaponTemplate);
|
||||
if (compatibleCartridgesInMagazine.Count == 0)
|
||||
{
|
||||
// No compatible cartridges found in magazine, use default
|
||||
@@ -732,9 +625,7 @@ public class BotWeaponGenerator(
|
||||
}
|
||||
|
||||
// Get the caliber data from the first compatible round in the magazine
|
||||
var magazineCaliberData = itemHelper
|
||||
.GetItem(compatibleCartridgesInMagazine.FirstOrDefault())
|
||||
.Value.Properties.Caliber;
|
||||
var magazineCaliberData = itemHelper.GetItem(compatibleCartridgesInMagazine.FirstOrDefault()).Value.Properties.Caliber;
|
||||
cartridgePoolForWeapon = cartridgePool[magazineCaliberData];
|
||||
|
||||
foreach (var cartridgeKvP in cartridgePoolForWeapon)
|
||||
@@ -760,16 +651,11 @@ public class BotWeaponGenerator(
|
||||
/// </summary>
|
||||
/// <param name="weaponTemplate">Weapon db template to get cartridges for</param>
|
||||
/// <returns>List of cartridge tpls</returns>
|
||||
protected HashSet<MongoId> GetCompatibleCartridgesFromWeaponTemplate(
|
||||
TemplateItem weaponTemplate
|
||||
)
|
||||
protected HashSet<MongoId> GetCompatibleCartridgesFromWeaponTemplate(TemplateItem weaponTemplate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(weaponTemplate);
|
||||
|
||||
var cartridges = weaponTemplate
|
||||
.Properties?.Chambers?.FirstOrDefault()
|
||||
?.Props?.Filters?.First()
|
||||
.Filter;
|
||||
var cartridges = weaponTemplate.Properties?.Chambers?.FirstOrDefault()?.Props?.Filters?.First().Filter;
|
||||
if (cartridges is not null)
|
||||
{
|
||||
return cartridges;
|
||||
@@ -785,24 +671,19 @@ public class BotWeaponGenerator(
|
||||
/// <param name="weaponTemplate">Weapon db template to get magazine cartridges for</param>
|
||||
/// <returns>Hashset of cartridge tpls</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when weaponTemplate is null.</exception>
|
||||
protected HashSet<MongoId> GetCompatibleCartridgesFromMagazineTemplate(
|
||||
TemplateItem weaponTemplate
|
||||
)
|
||||
protected HashSet<MongoId> GetCompatibleCartridgesFromMagazineTemplate(TemplateItem weaponTemplate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(weaponTemplate);
|
||||
|
||||
// Get the first magazine's template from the weapon
|
||||
var magazineSlot = weaponTemplate.Properties.Slots?.FirstOrDefault(slot =>
|
||||
slot.Name == "mod_magazine"
|
||||
);
|
||||
var magazineSlot = weaponTemplate.Properties.Slots?.FirstOrDefault(slot => slot.Name == "mod_magazine");
|
||||
if (magazineSlot is null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var magazineTemplate = itemHelper.GetItem(
|
||||
magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault()
|
||||
?? new MongoId(null)
|
||||
magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault() ?? new MongoId(null)
|
||||
);
|
||||
if (!magazineTemplate.Key)
|
||||
{
|
||||
@@ -811,14 +692,8 @@ public class BotWeaponGenerator(
|
||||
|
||||
// Try to get cartridges from slots array first, if none found, try Cartridges array
|
||||
var cartridges =
|
||||
magazineTemplate
|
||||
.Value.Properties.Slots.FirstOrDefault()
|
||||
?.Props?.Filters.FirstOrDefault()
|
||||
?.Filter
|
||||
?? magazineTemplate
|
||||
.Value.Properties.Cartridges.FirstOrDefault()
|
||||
?.Props?.Filters.FirstOrDefault()
|
||||
?.Filter;
|
||||
magazineTemplate.Value.Properties.Slots.FirstOrDefault()?.Props?.Filters.FirstOrDefault()?.Filter
|
||||
?? magazineTemplate.Value.Properties.Cartridges.FirstOrDefault()?.Props?.Filters.FirstOrDefault()?.Filter;
|
||||
|
||||
return cartridges ?? [];
|
||||
}
|
||||
@@ -838,18 +713,13 @@ public class BotWeaponGenerator(
|
||||
if (!string.IsNullOrEmpty(weaponTemplate.Properties.AmmoCaliber))
|
||||
// 9x18pmm has a typo, should be Caliber9x18PM
|
||||
{
|
||||
return weaponTemplate.Properties.AmmoCaliber == "Caliber9x18PMM"
|
||||
? "Caliber9x18PM"
|
||||
: weaponTemplate.Properties.AmmoCaliber;
|
||||
return weaponTemplate.Properties.AmmoCaliber == "Caliber9x18PMM" ? "Caliber9x18PM" : weaponTemplate.Properties.AmmoCaliber;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(weaponTemplate.Properties.LinkedWeapon))
|
||||
{
|
||||
var ammoInChamber = itemHelper.GetItem(
|
||||
weaponTemplate
|
||||
.Properties.Chambers.First()
|
||||
.Props.Filters.First()
|
||||
.Filter.FirstOrDefault()
|
||||
weaponTemplate.Properties.Chambers.First().Props.Filters.First().Filter.FirstOrDefault()
|
||||
);
|
||||
return !ammoInChamber.Key ? null : ammoInChamber.Value.Properties.Caliber;
|
||||
}
|
||||
@@ -863,21 +733,12 @@ public class BotWeaponGenerator(
|
||||
/// <param name="weaponMods">Weapon with children</param>
|
||||
/// <param name="magazine">Magazine item</param>
|
||||
/// <param name="cartridgeTemplate">Cartridge to insert into magazine</param>
|
||||
protected void FillExistingMagazines(
|
||||
List<Item> weaponMods,
|
||||
Item magazine,
|
||||
MongoId cartridgeTemplate
|
||||
)
|
||||
protected void FillExistingMagazines(List<Item> weaponMods, Item magazine, MongoId cartridgeTemplate)
|
||||
{
|
||||
var magazineTemplate = itemHelper.GetItem(magazine.Template).Value;
|
||||
if (magazineTemplate is null)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-unable_to_find_magazine_item",
|
||||
magazine.Template
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-unable_to_find_magazine_item", magazine.Template));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -894,12 +755,7 @@ public class BotWeaponGenerator(
|
||||
}
|
||||
else
|
||||
{
|
||||
AddOrUpdateMagazinesChildWithAmmo(
|
||||
weaponMods,
|
||||
magazine,
|
||||
cartridgeTemplate,
|
||||
magazineTemplate
|
||||
);
|
||||
AddOrUpdateMagazinesChildWithAmmo(weaponMods, magazine, cartridgeTemplate, magazineTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,9 +793,7 @@ public class BotWeaponGenerator(
|
||||
TemplateItem magazineTemplate
|
||||
)
|
||||
{
|
||||
var magazineCartridgeChildItem = weaponWithMods.FirstOrDefault(m =>
|
||||
m.ParentId == magazine.Id && m.SlotId == "cartridges"
|
||||
);
|
||||
var magazineCartridgeChildItem = weaponWithMods.FirstOrDefault(m => m.ParentId == magazine.Id && m.SlotId == "cartridges");
|
||||
if (magazineCartridgeChildItem is not null)
|
||||
{
|
||||
// Delete the existing cartridge object and create fresh below
|
||||
@@ -950,20 +804,13 @@ public class BotWeaponGenerator(
|
||||
List<Item> magazineWithCartridges = [magazine];
|
||||
|
||||
// Add cartridges as children to above mag array
|
||||
itemHelper.FillMagazineWithCartridge(
|
||||
magazineWithCartridges,
|
||||
magazineTemplate,
|
||||
chosenAmmoTpl,
|
||||
1
|
||||
);
|
||||
itemHelper.FillMagazineWithCartridge(magazineWithCartridges, magazineTemplate, chosenAmmoTpl, 1);
|
||||
|
||||
// Replace existing magazine with above array of mag + cartridge stacks
|
||||
var magazineIndex = weaponWithMods.FindIndex(i => i.Id == magazine.Id); // magazineWithCartridges
|
||||
if (magazineIndex == -1)
|
||||
{
|
||||
logger.Error(
|
||||
$"Unable to add cartridges: {chosenAmmoTpl} to magazine: {magazine.Id} as none found"
|
||||
);
|
||||
logger.Error($"Unable to add cartridges: {chosenAmmoTpl} to magazine: {magazine.Id} as none found");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -980,20 +827,12 @@ public class BotWeaponGenerator(
|
||||
/// <param name="weaponMods">Weapon mods to find and update camora mod(s) from</param>
|
||||
/// <param name="magazineId">Magazine id to find and add to</param>
|
||||
/// <param name="ammoTpl">Ammo template id to hydrate with</param>
|
||||
protected void FillCamorasWithAmmo(
|
||||
IEnumerable<Item> weaponMods,
|
||||
MongoId magazineId,
|
||||
MongoId ammoTpl
|
||||
)
|
||||
protected void FillCamorasWithAmmo(IEnumerable<Item> weaponMods, MongoId magazineId, MongoId ammoTpl)
|
||||
{
|
||||
// for CylinderMagazine we exchange the ammo in the "camoras".
|
||||
// This might not be necessary since we already filled the camoras with a random whitelisted and compatible ammo type,
|
||||
// but I'm not sure whether this is also used elsewhere
|
||||
var camoras = weaponMods
|
||||
.Where(x =>
|
||||
x.ParentId == magazineId && x.SlotId.StartsWith("camora", StringComparison.Ordinal)
|
||||
)
|
||||
.ToList();
|
||||
var camoras = weaponMods.Where(x => x.ParentId == magazineId && x.SlotId.StartsWith("camora", StringComparison.Ordinal)).ToList();
|
||||
|
||||
if (camoras.Count == 0)
|
||||
{
|
||||
|
||||
@@ -65,10 +65,7 @@ public class FenceBaseAssortGenerator(
|
||||
// Item base type blacklisted
|
||||
if (traderConfig.Fence.Blacklist.Count > 0)
|
||||
{
|
||||
if (
|
||||
traderConfig.Fence.Blacklist.Contains(itemId)
|
||||
|| itemHelper.IsOfBaseclasses(itemId, traderConfig.Fence.Blacklist)
|
||||
)
|
||||
if (traderConfig.Fence.Blacklist.Contains(itemId) || itemHelper.IsOfBaseclasses(itemId, traderConfig.Fence.Blacklist))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -131,9 +128,7 @@ public class FenceBaseAssortGenerator(
|
||||
// Create barter scheme (price)
|
||||
var barterSchemeToAdd = new BarterScheme
|
||||
{
|
||||
Count = Math.Round(
|
||||
(double)fenceService.GetItemPrice(itemId, itemWithChildrenToAdd)
|
||||
),
|
||||
Count = Math.Round((double)fenceService.GetItemPrice(itemId, itemWithChildrenToAdd)),
|
||||
Template = Money.ROUBLES,
|
||||
};
|
||||
|
||||
@@ -155,11 +150,7 @@ public class FenceBaseAssortGenerator(
|
||||
foreach (var defaultPreset in defaultPresets)
|
||||
{
|
||||
// Skip presets we've already added
|
||||
if (
|
||||
baseFenceAssort.Items.Any(item =>
|
||||
item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id
|
||||
)
|
||||
)
|
||||
if (baseFenceAssort.Items.Any(item => item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -168,9 +159,7 @@ public class FenceBaseAssortGenerator(
|
||||
var itemAndChildren = _cloner.Clone(defaultPreset.Items).ReplaceIDs();
|
||||
|
||||
// Find root item and add some properties to it
|
||||
var rootItem = itemAndChildren.FirstOrDefault(item =>
|
||||
string.IsNullOrEmpty(item.ParentId)
|
||||
);
|
||||
var rootItem = itemAndChildren.FirstOrDefault(item => string.IsNullOrEmpty(item.ParentId));
|
||||
rootItem.ParentId = "hideout";
|
||||
rootItem.SlotId = "hideout";
|
||||
rootItem.Upd = new Upd
|
||||
@@ -191,11 +180,7 @@ public class FenceBaseAssortGenerator(
|
||||
[
|
||||
new()
|
||||
{
|
||||
new BarterScheme
|
||||
{
|
||||
Template = Money.ROUBLES,
|
||||
Count = Math.Round(price * itemQualityModifier),
|
||||
},
|
||||
new BarterScheme { Template = Money.ROUBLES, Count = Math.Round(price * itemQualityModifier) },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -213,12 +198,7 @@ public class FenceBaseAssortGenerator(
|
||||
var ammoPenetrationPower = GetAmmoPenetrationPower(rootItemDb);
|
||||
if (ammoPenetrationPower == null)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText(
|
||||
"fence-unable_to_get_ammo_penetration_value",
|
||||
rootItemDb.Id
|
||||
)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("fence-unable_to_get_ammo_penetration_value", rootItemDb.Id));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,18 +215,13 @@ public class FenceBaseAssortGenerator(
|
||||
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
// Get the cartridge tpl found inside ammo box
|
||||
var cartridgeTplInBox = rootItemDb
|
||||
.Properties.StackSlots.First()
|
||||
.Props.Filters.First()
|
||||
.Filter.FirstOrDefault();
|
||||
var cartridgeTplInBox = rootItemDb.Properties.StackSlots.First().Props.Filters.First().Filter.FirstOrDefault();
|
||||
|
||||
// Look up cartridge tpl in db
|
||||
var ammoItemDb = itemHelper.GetItem(cartridgeTplInBox);
|
||||
if (!ammoItemDb.Key)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText("fence-ammo_not_found_in_db", cartridgeTplInBox)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("fence-ammo_not_found_in_db", cartridgeTplInBox));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -271,25 +246,20 @@ public class FenceBaseAssortGenerator(
|
||||
protected void AddChildrenToArmorModSlots(List<Item> armor, TemplateItem itemDbDetails)
|
||||
{
|
||||
// Armor has no mods, make no additions
|
||||
var hasMods =
|
||||
itemDbDetails.Properties?.Slots is not null && itemDbDetails.Properties.Slots.Any();
|
||||
var hasMods = itemDbDetails.Properties?.Slots is not null && itemDbDetails.Properties.Slots.Any();
|
||||
if (!hasMods)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for and add required soft inserts to armors
|
||||
var requiredSlots = itemDbDetails
|
||||
.Properties.Slots.Where(slot => slot.Required ?? false)
|
||||
.ToList();
|
||||
var requiredSlots = itemDbDetails.Properties.Slots.Where(slot => slot.Required ?? false).ToList();
|
||||
var hasRequiredSlots = requiredSlots.Count > 0;
|
||||
if (hasRequiredSlots)
|
||||
{
|
||||
foreach (var requiredSlot in requiredSlots)
|
||||
{
|
||||
var modItemDbDetails = itemHelper
|
||||
.GetItem(requiredSlot.Props.Filters.First().Plate.Value)
|
||||
.Value;
|
||||
var modItemDbDetails = itemHelper.GetItem(requiredSlot.Props.Filters.First().Plate.Value).Value;
|
||||
var plateTpl = requiredSlot.Props.Filters.First().Plate; // `Plate` property appears to be the 'default' item for slot
|
||||
if (plateTpl is null || plateTpl.Value.IsEmpty())
|
||||
// Some bsg plate properties are empty, skip mod
|
||||
@@ -318,9 +288,7 @@ public class FenceBaseAssortGenerator(
|
||||
}
|
||||
|
||||
// Check for and add plate items
|
||||
var plateSlots = itemDbDetails
|
||||
.Properties.Slots.Where(slot => itemHelper.IsRemovablePlateSlot(slot.Name))
|
||||
.ToList();
|
||||
var plateSlots = itemDbDetails.Properties.Slots.Where(slot => itemHelper.IsRemovablePlateSlot(slot.Name)).ToList();
|
||||
if (plateSlots.Count > 0)
|
||||
{
|
||||
foreach (var plateSlot in plateSlots)
|
||||
|
||||
@@ -33,8 +33,7 @@ public class LocationLootGenerator(
|
||||
)
|
||||
{
|
||||
protected readonly LocationConfig _locationConfig = _configServer.GetConfig<LocationConfig>();
|
||||
protected readonly SeasonalEventConfig _seasonalEventConfig =
|
||||
_configServer.GetConfig<SeasonalEventConfig>();
|
||||
protected readonly SeasonalEventConfig _seasonalEventConfig = _configServer.GetConfig<SeasonalEventConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Generate Loot for provided location ()
|
||||
@@ -68,9 +67,7 @@ public class LocationLootGenerator(
|
||||
}
|
||||
|
||||
// Create containers with loot
|
||||
result.AddRange(
|
||||
GenerateStaticContainers(locationId.ToLowerInvariant(), staticAmmoDistClone)
|
||||
);
|
||||
result.AddRange(GenerateStaticContainers(locationId.ToLowerInvariant(), staticAmmoDistClone));
|
||||
|
||||
// Add dynamic loot to output loot
|
||||
var dynamicSpawnPoints = GenerateDynamicLoot(
|
||||
@@ -82,15 +79,8 @@ public class LocationLootGenerator(
|
||||
// Merge dynamic spawns into result
|
||||
result.AddRange(dynamicSpawnPoints);
|
||||
|
||||
_logger.Success(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-dynamic_items_spawned_success",
|
||||
dynamicSpawnPoints.Count
|
||||
)
|
||||
);
|
||||
_logger.Success(
|
||||
_serverLocalisationService.GetText("location-generated_success", locationId)
|
||||
);
|
||||
_logger.Success(_serverLocalisationService.GetText("location-dynamic_items_spawned_success", dynamicSpawnPoints.Count));
|
||||
_logger.Success(_serverLocalisationService.GetText("location-generated_success", locationId));
|
||||
|
||||
// Clean up tracker
|
||||
counterTrackerHelper.Clear();
|
||||
@@ -115,40 +105,23 @@ public class LocationLootGenerator(
|
||||
var staticWeaponsOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticWeapons);
|
||||
if (staticWeaponsOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_find_static_weapon_for_map",
|
||||
locationId
|
||||
)
|
||||
);
|
||||
_logger.Error(_serverLocalisationService.GetText("location-unable_to_find_static_weapon_for_map", locationId));
|
||||
}
|
||||
|
||||
// Add mounted weapons to output loot
|
||||
result.AddRange(staticWeaponsOnMapClone);
|
||||
|
||||
var allStaticContainersOnMapClone = _cloner.Clone(
|
||||
mapData.StaticContainers.Value.StaticContainers
|
||||
);
|
||||
var allStaticContainersOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticContainers);
|
||||
if (allStaticContainersOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_find_static_container_for_map",
|
||||
locationId
|
||||
)
|
||||
);
|
||||
_logger.Error(_serverLocalisationService.GetText("location-unable_to_find_static_container_for_map", locationId));
|
||||
}
|
||||
|
||||
// Containers that MUST be added to map (e.g. quest containers)
|
||||
var staticForcedOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticForced);
|
||||
if (staticForcedOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_find_forced_static_data_for_map",
|
||||
locationId
|
||||
)
|
||||
);
|
||||
_logger.Error(_serverLocalisationService.GetText("location-unable_to_find_forced_static_data_for_map", locationId));
|
||||
}
|
||||
|
||||
// Remove christmas items from loot data
|
||||
@@ -159,9 +132,7 @@ public class LocationLootGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(
|
||||
allStaticContainersOnMapClone
|
||||
);
|
||||
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(allStaticContainersOnMapClone);
|
||||
|
||||
// Keep track of static loot count
|
||||
var staticContainerCount = 0;
|
||||
@@ -174,13 +145,7 @@ public class LocationLootGenerator(
|
||||
// Add loot to guaranteed containers and add to result
|
||||
foreach (
|
||||
var containerWithLoot in guaranteedContainers.Select(container =>
|
||||
AddLootToContainer(
|
||||
container,
|
||||
staticForcedOnMapClone,
|
||||
staticLootDist.Value,
|
||||
staticAmmoDist,
|
||||
locationId
|
||||
)
|
||||
AddLootToContainer(container, staticForcedOnMapClone, staticLootDist.Value, staticAmmoDist, locationId)
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -226,21 +191,13 @@ public class LocationLootGenerator(
|
||||
// Group containers by their groupId
|
||||
if (mapData.Statics is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_generate_static_loot",
|
||||
locationId
|
||||
)
|
||||
);
|
||||
_logger.Warning(_serverLocalisationService.GetText("location-unable_to_generate_static_loot", locationId));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// For each of the container groups, choose from the pool of containers, hydrate container with loot and add to result array
|
||||
var mapping = GetGroupIdToContainerMappings(
|
||||
mapData.Statics,
|
||||
staticRandomisableContainersOnMap
|
||||
);
|
||||
var mapping = GetGroupIdToContainerMappings(mapData.Statics, staticRandomisableContainersOnMap);
|
||||
foreach (var (key, data) in mapping)
|
||||
{
|
||||
// Count chosen was 0, skip
|
||||
@@ -253,9 +210,7 @@ public class LocationLootGenerator(
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping"
|
||||
);
|
||||
_logger.Debug($"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping");
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -271,9 +226,7 @@ public class LocationLootGenerator(
|
||||
{
|
||||
if (_randomUtil.GetChance100(containerIdsCopy[containerId.Key] * 100))
|
||||
{
|
||||
data.ContainerIdsWithProbability[containerId.Key] = containerIdsCopy[
|
||||
containerId.Key
|
||||
];
|
||||
data.ContainerIdsWithProbability[containerId.Key] = containerIdsCopy[containerId.Key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,16 +245,14 @@ public class LocationLootGenerator(
|
||||
foreach (var chosenContainerId in chosenContainerIds)
|
||||
{
|
||||
// Look up container object from full list of containers on map
|
||||
var containerObject = staticRandomisableContainersOnMap.FirstOrDefault(
|
||||
staticContainer => staticContainer.Template.Id == chosenContainerId
|
||||
var containerObject = staticRandomisableContainersOnMap.FirstOrDefault(staticContainer =>
|
||||
staticContainer.Template.Id == chosenContainerId
|
||||
);
|
||||
if (containerObject is null)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Container: {chosenContainerId} not found in staticRandomisableContainersOnMap, this is bad"
|
||||
);
|
||||
_logger.Debug($"Container: {chosenContainerId} not found in staticRandomisableContainersOnMap, this is bad");
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -323,12 +274,7 @@ public class LocationLootGenerator(
|
||||
}
|
||||
|
||||
_logger.Success($"A total of: {staticLootItemCount} static items spawned");
|
||||
_logger.Success(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-containers_generated_success",
|
||||
staticContainerCount
|
||||
)
|
||||
);
|
||||
_logger.Success(_serverLocalisationService.GetText("location-containers_generated_success", staticContainerCount));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -344,9 +290,7 @@ public class LocationLootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="staticContainers"></param>
|
||||
/// <returns>StaticContainerData array</returns>
|
||||
protected IEnumerable<StaticContainerData> GetRandomisableContainersOnMap(
|
||||
IEnumerable<StaticContainerData> staticContainers
|
||||
)
|
||||
protected IEnumerable<StaticContainerData> GetRandomisableContainersOnMap(IEnumerable<StaticContainerData> staticContainers)
|
||||
{
|
||||
return staticContainers.Where(staticContainer =>
|
||||
staticContainer.Probability != 1
|
||||
@@ -362,9 +306,7 @@ public class LocationLootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="staticContainersOnMap"></param>
|
||||
/// <returns>IStaticContainerData array</returns>
|
||||
protected IEnumerable<StaticContainerData> GetGuaranteedContainers(
|
||||
IEnumerable<StaticContainerData> staticContainersOnMap
|
||||
)
|
||||
protected IEnumerable<StaticContainerData> GetGuaranteedContainers(IEnumerable<StaticContainerData> staticContainersOnMap)
|
||||
{
|
||||
return staticContainersOnMap.Where(staticContainer =>
|
||||
staticContainer.Probability == 1
|
||||
@@ -382,10 +324,7 @@ public class LocationLootGenerator(
|
||||
/// <param name="groupId">Name of the group the containers are being collected for</param>
|
||||
/// <param name="containerData">Containers and probability values for a groupId</param>
|
||||
/// <returns>List of chosen container Ids</returns>
|
||||
protected List<string> GetContainersByProbability(
|
||||
string groupId,
|
||||
ContainerGroupCount containerData
|
||||
)
|
||||
protected List<string> GetContainersByProbability(string groupId, ContainerGroupCount containerData)
|
||||
{
|
||||
var chosenContainerIds = new List<string>();
|
||||
|
||||
@@ -437,16 +376,12 @@ public class LocationLootGenerator(
|
||||
(int)
|
||||
Math.Round(
|
||||
groupKvP.Value.MinContainers.Value
|
||||
* _locationConfig
|
||||
.ContainerRandomisationSettings
|
||||
.ContainerGroupMinSizeMultiplier
|
||||
* _locationConfig.ContainerRandomisationSettings.ContainerGroupMinSizeMultiplier
|
||||
),
|
||||
(int)
|
||||
Math.Round(
|
||||
groupKvP.Value.MaxContainers.Value
|
||||
* _locationConfig
|
||||
.ContainerRandomisationSettings
|
||||
.ContainerGroupMaxSizeMultiplier
|
||||
* _locationConfig.ContainerRandomisationSettings.ContainerGroupMaxSizeMultiplier
|
||||
)
|
||||
),
|
||||
};
|
||||
@@ -456,29 +391,17 @@ public class LocationLootGenerator(
|
||||
// Likely bad BSG data, will be fixed...eventually, example of the groupIds: `NEED_TO_BE_FIXED1`,`NEED_TO_BE_FIXED_SE02`, `NEED_TO_BE_FIXED_NW_01`
|
||||
mapping.Add(
|
||||
string.Empty,
|
||||
new ContainerGroupCount
|
||||
{
|
||||
ContainerIdsWithProbability = new Dictionary<string, double>(),
|
||||
ChosenCount = -1,
|
||||
}
|
||||
new ContainerGroupCount { ContainerIdsWithProbability = new Dictionary<string, double>(), ChosenCount = -1 }
|
||||
);
|
||||
|
||||
// Iterate over all containers and add to group keyed by groupId
|
||||
// Containers without a group go into a group with empty key ""
|
||||
foreach (var container in staticContainersOnMap)
|
||||
{
|
||||
if (
|
||||
!staticContainerGroupData.Containers.TryGetValue(
|
||||
container.Template.Id,
|
||||
out var groupData
|
||||
)
|
||||
)
|
||||
if (!staticContainerGroupData.Containers.TryGetValue(container.Template.Id, out var groupData))
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_find_container_in_statics_json",
|
||||
container.Template.Id
|
||||
)
|
||||
_serverLocalisationService.GetText("location-unable_to_find_container_in_statics_json", container.Template.Id)
|
||||
);
|
||||
|
||||
continue;
|
||||
@@ -498,17 +421,9 @@ public class LocationLootGenerator(
|
||||
|
||||
mapping.TryAdd(
|
||||
groupData.GroupId,
|
||||
new ContainerGroupCount
|
||||
{
|
||||
ChosenCount = 0d,
|
||||
ContainerIdsWithProbability = new Dictionary<string, double>(),
|
||||
}
|
||||
new ContainerGroupCount { ChosenCount = 0d, ContainerIdsWithProbability = new Dictionary<string, double>() }
|
||||
);
|
||||
mapping[groupData.GroupId]
|
||||
.ContainerIdsWithProbability.TryAdd(
|
||||
container.Template.Id,
|
||||
container.Probability.Value
|
||||
);
|
||||
mapping[groupData.GroupId].ContainerIdsWithProbability.TryAdd(container.Template.Id, container.Probability.Value);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
@@ -543,11 +458,7 @@ public class LocationLootGenerator(
|
||||
var containerMap = _itemHelper.GetContainerMapping(containerTpl);
|
||||
|
||||
// Choose count of items to add to container
|
||||
var itemCountToAdd = GetWeightedCountOfContainerItems(
|
||||
containerTpl,
|
||||
staticLootDist,
|
||||
locationName
|
||||
);
|
||||
var itemCountToAdd = GetWeightedCountOfContainerItems(containerTpl, staticLootDist, locationName);
|
||||
if (itemCountToAdd == 0)
|
||||
{
|
||||
return containerClone;
|
||||
@@ -595,10 +506,7 @@ public class LocationLootGenerator(
|
||||
: chosenItemWithChildren.Items;
|
||||
|
||||
// look for open slot to put chosen item into
|
||||
var result = containerMap.FindSlotForItem(
|
||||
chosenItemWithChildren.Width,
|
||||
chosenItemWithChildren.Height
|
||||
);
|
||||
var result = containerMap.FindSlotForItem(chosenItemWithChildren.Width, chosenItemWithChildren.Height);
|
||||
if (!result.Success.GetValueOrDefault(false))
|
||||
{
|
||||
if (failedToFitAttemptCount > _locationConfig.FitLootIntoContainerAttempts)
|
||||
@@ -628,9 +536,7 @@ public class LocationLootGenerator(
|
||||
{
|
||||
X = result.X,
|
||||
Y = result.Y,
|
||||
R = result.Rotation.GetValueOrDefault(false)
|
||||
? ItemRotation.Vertical
|
||||
: ItemRotation.Horizontal,
|
||||
R = result.Rotation.GetValueOrDefault(false) ? ItemRotation.Vertical : ItemRotation.Horizontal,
|
||||
};
|
||||
|
||||
// Add loot to container before returning
|
||||
@@ -673,16 +579,11 @@ public class LocationLootGenerator(
|
||||
{
|
||||
// Add each count of items into array
|
||||
itemCountArray.Add(
|
||||
new ProbabilityObject<int, float?>(
|
||||
itemCountDistribution.Count.Value,
|
||||
itemCountDistribution.RelativeProbability.Value,
|
||||
null
|
||||
)
|
||||
new ProbabilityObject<int, float?>(itemCountDistribution.Count.Value, itemCountDistribution.RelativeProbability.Value, null)
|
||||
);
|
||||
}
|
||||
|
||||
return (int)
|
||||
Math.Round(GetStaticLootMultiplierForLocation(locationName) * itemCountArray.Draw()[0]);
|
||||
return (int)Math.Round(GetStaticLootMultiplierForLocation(locationName) * itemCountArray.Draw()[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -705,12 +606,7 @@ public class LocationLootGenerator(
|
||||
var itemContainerDistribution = staticLootDist[containerTypeId]?.ItemDistribution;
|
||||
if (itemContainerDistribution is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-missing_item_distribution_data",
|
||||
containerTypeId
|
||||
)
|
||||
);
|
||||
_logger.Warning(_serverLocalisationService.GetText("location-missing_item_distribution_data", containerTypeId));
|
||||
|
||||
return itemDistribution;
|
||||
}
|
||||
@@ -729,9 +625,7 @@ public class LocationLootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
itemDistribution.Add(
|
||||
new ProbabilityObject<MongoId, float?>(icd.Tpl, icd.RelativeProbability.Value, null)
|
||||
);
|
||||
itemDistribution.Add(new ProbabilityObject<MongoId, float?>(icd.Tpl, icd.RelativeProbability.Value, null));
|
||||
}
|
||||
|
||||
return itemDistribution;
|
||||
@@ -780,26 +674,17 @@ public class LocationLootGenerator(
|
||||
|
||||
// Build the list of forced loot from both `SpawnpointsForced` and any point marked `IsAlwaysSpawn`
|
||||
dynamicForcedSpawnPoints.AddRange(dynamicLootDist.SpawnpointsForced);
|
||||
dynamicForcedSpawnPoints.AddRange(
|
||||
dynamicLootDist.Spawnpoints.Where(point =>
|
||||
point.Template.IsAlwaysSpawn.GetValueOrDefault()
|
||||
)
|
||||
);
|
||||
dynamicForcedSpawnPoints.AddRange(dynamicLootDist.Spawnpoints.Where(point => point.Template.IsAlwaysSpawn.GetValueOrDefault()));
|
||||
|
||||
loot.AddRange(GetForcedDynamicLoot(dynamicForcedSpawnPoints, locationName, staticAmmoDist));
|
||||
|
||||
// Draw from random distribution
|
||||
var desiredSpawnPointCount = Math.Round(
|
||||
GetLooseLootMultiplierForLocation(locationName)
|
||||
* _randomUtil.GetNormallyDistributedRandomNumber(
|
||||
dynamicLootDist.SpawnpointCount.Mean,
|
||||
dynamicLootDist.SpawnpointCount.Std
|
||||
)
|
||||
* _randomUtil.GetNormallyDistributedRandomNumber(dynamicLootDist.SpawnpointCount.Mean, dynamicLootDist.SpawnpointCount.Std)
|
||||
);
|
||||
|
||||
var blacklistedSpawnPoints = _locationConfig.LooseLootBlacklist.GetValueOrDefault(
|
||||
locationName
|
||||
);
|
||||
var blacklistedSpawnPoints = _locationConfig.LooseLootBlacklist.GetValueOrDefault(locationName);
|
||||
|
||||
// Init empty array to hold spawn points, letting us pick them pseudo-randomly
|
||||
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(_cloner);
|
||||
@@ -834,13 +719,7 @@ public class LocationLootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnPointArray.Add(
|
||||
new ProbabilityObject<string, Spawnpoint>(
|
||||
spawnPoint.Template.Id,
|
||||
spawnPoint.Probability ?? 0,
|
||||
spawnPoint
|
||||
)
|
||||
);
|
||||
spawnPointArray.Add(new ProbabilityObject<string, Spawnpoint>(spawnPoint.Template.Id, spawnPoint.Probability ?? 0, spawnPoint));
|
||||
}
|
||||
|
||||
// Select a number of spawn points to add loot to
|
||||
@@ -860,10 +739,7 @@ public class LocationLootGenerator(
|
||||
}
|
||||
|
||||
// Filter out duplicate locationIds // prob can be done better
|
||||
chosenSpawnPoints = chosenSpawnPoints
|
||||
.GroupBy(spawnPoint => spawnPoint.LocationId)
|
||||
.Select(group => group.First())
|
||||
.ToList();
|
||||
chosenSpawnPoints = chosenSpawnPoints.GroupBy(spawnPoint => spawnPoint.LocationId).Select(group => group.First()).ToList();
|
||||
|
||||
// Do we have enough items in pool to fulfill requirement
|
||||
var tooManySpawnPointsRequested = desiredSpawnPointCount - chosenSpawnPoints.Count > 0;
|
||||
@@ -893,29 +769,20 @@ public class LocationLootGenerator(
|
||||
// SpawnPoint is invalid, skip it
|
||||
if (spawnPoint.Template is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-missing_dynamic_template",
|
||||
spawnPoint.LocationId
|
||||
)
|
||||
);
|
||||
_logger.Warning(_serverLocalisationService.GetText("location-missing_dynamic_template", spawnPoint.LocationId));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure no blacklisted lootable items are in pool
|
||||
spawnPoint.Template.Items = spawnPoint
|
||||
.Template.Items.Where(item =>
|
||||
!_itemFilterService.IsLootableItemBlacklisted(item.Template)
|
||||
)
|
||||
.Template.Items.Where(item => !_itemFilterService.IsLootableItemBlacklisted(item.Template))
|
||||
.ToList();
|
||||
|
||||
// Ensure no seasonal items are in pool if not in-season
|
||||
if (!seasonalEventActive)
|
||||
{
|
||||
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(item =>
|
||||
!seasonalItemTplBlacklist.Contains(item.Template)
|
||||
);
|
||||
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(item => !seasonalItemTplBlacklist.Contains(item.Template));
|
||||
}
|
||||
|
||||
// Spawn point has no items after filtering, skip
|
||||
@@ -923,21 +790,14 @@ public class LocationLootGenerator(
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-spawnpoint_missing_items",
|
||||
spawnPoint.Template.Id
|
||||
)
|
||||
);
|
||||
_logger.Debug(_serverLocalisationService.GetText("location-spawnpoint_missing_items", spawnPoint.Template.Id));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get an array of allowed IDs after above filtering has occured
|
||||
var validComposedKeys = spawnPoint
|
||||
.Template.Items.Select(item => item.ComposedKey)
|
||||
.ToHashSet();
|
||||
var validComposedKeys = spawnPoint.Template.Items.Select(item => item.ComposedKey).ToHashSet();
|
||||
|
||||
// Construct container to hold above filtered items, letting us pick an item for the spot
|
||||
var itemArray = new ProbabilityObjectArray<string, double?>(_cloner);
|
||||
@@ -948,32 +808,19 @@ public class LocationLootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
itemArray.Add(
|
||||
new ProbabilityObject<string, double?>(
|
||||
itemDist.ComposedKey.Key,
|
||||
itemDist.RelativeProbability ?? 0,
|
||||
null
|
||||
)
|
||||
);
|
||||
itemArray.Add(new ProbabilityObject<string, double?>(itemDist.ComposedKey.Key, itemDist.RelativeProbability ?? 0, null));
|
||||
}
|
||||
|
||||
if (itemArray.Count == 0)
|
||||
{
|
||||
_logger.Warning(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-loot_pool_is_empty_skipping",
|
||||
spawnPoint.Template.Id
|
||||
)
|
||||
);
|
||||
_logger.Warning(_serverLocalisationService.GetText("location-loot_pool_is_empty_skipping", spawnPoint.Template.Id));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw a random item from the spawn points possible items
|
||||
var chosenComposedKey = itemArray.Draw().FirstOrDefault();
|
||||
var chosenItem = spawnPoint.Template.Items.FirstOrDefault(item =>
|
||||
item.ComposedKey == chosenComposedKey
|
||||
);
|
||||
var chosenItem = spawnPoint.Template.Items.FirstOrDefault(item => item.ComposedKey == chosenComposedKey);
|
||||
if (chosenItem is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
@@ -982,18 +829,10 @@ public class LocationLootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
var createItemResult = CreateDynamicLootItem(
|
||||
chosenItem,
|
||||
spawnPoint.Template.Items,
|
||||
staticAmmoDist
|
||||
);
|
||||
var createItemResult = CreateDynamicLootItem(chosenItem, spawnPoint.Template.Items, staticAmmoDist);
|
||||
|
||||
// If count reaches max, skip adding item to loot
|
||||
if (
|
||||
counterTrackerHelper.IncrementCount(
|
||||
createItemResult.Items.FirstOrDefault().Template
|
||||
)
|
||||
)
|
||||
if (counterTrackerHelper.IncrementCount(createItemResult.Items.FirstOrDefault().Template))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1047,14 +886,8 @@ public class LocationLootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
var chosenItem = forcedLootLocation.Template.Items.FirstOrDefault(item =>
|
||||
item.Id == rootItem.Id
|
||||
);
|
||||
var createItemResult = CreateDynamicLootItem(
|
||||
chosenItem,
|
||||
forcedLootLocation.Template.Items,
|
||||
staticAmmoDist
|
||||
);
|
||||
var chosenItem = forcedLootLocation.Template.Items.FirstOrDefault(item => item.Id == rootItem.Id);
|
||||
var createItemResult = CreateDynamicLootItem(chosenItem, forcedLootLocation.Template.Items, staticAmmoDist);
|
||||
|
||||
// Update root ID with the above dynamically generated ID
|
||||
forcedLootLocation.Template.Root = createItemResult.Items.FirstOrDefault().Id;
|
||||
@@ -1065,9 +898,7 @@ public class LocationLootGenerator(
|
||||
forcedLootLocation.Template.Items = convertedItems;
|
||||
|
||||
// Push forced location into array as long as it doesn't exist already
|
||||
var existingLocation = result.Any(spawnPoint =>
|
||||
spawnPoint.Id == locationTemplateToAdd.Id
|
||||
);
|
||||
var existingLocation = result.Any(spawnPoint => spawnPoint.Id == locationTemplateToAdd.Id);
|
||||
if (!existingLocation)
|
||||
{
|
||||
result.Add(locationTemplateToAdd);
|
||||
@@ -1116,10 +947,7 @@ public class LocationLootGenerator(
|
||||
var stackCount =
|
||||
itemDbTemplate.Properties.StackMaxSize == 1
|
||||
? 1
|
||||
: _randomUtil.GetInt(
|
||||
itemDbTemplate.Properties.StackMinRandom.Value,
|
||||
itemDbTemplate.Properties.StackMaxRandom.Value
|
||||
);
|
||||
: _randomUtil.GetInt(itemDbTemplate.Properties.StackMinRandom.Value, itemDbTemplate.Properties.StackMaxRandom.Value);
|
||||
|
||||
itemWithMods.Add(
|
||||
new Item
|
||||
@@ -1211,19 +1039,13 @@ public class LocationLootGenerator(
|
||||
rootItem.ParentId = parentId;
|
||||
}
|
||||
|
||||
if (
|
||||
_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MONEY)
|
||||
|| _itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO)
|
||||
)
|
||||
if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MONEY) || _itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO))
|
||||
{
|
||||
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
|
||||
var stackCount =
|
||||
itemTemplate.Properties.StackMaxSize == 1
|
||||
? 1
|
||||
: _randomUtil.GetInt(
|
||||
itemTemplate.Properties.StackMinRandom.Value,
|
||||
itemTemplate.Properties.StackMaxRandom.Value
|
||||
);
|
||||
: _randomUtil.GetInt(itemTemplate.Properties.StackMinRandom.Value, itemTemplate.Properties.StackMaxRandom.Value);
|
||||
|
||||
rootItem.Upd = new Upd { StackObjectsCount = stackCount };
|
||||
}
|
||||
@@ -1262,12 +1084,7 @@ public class LocationLootGenerator(
|
||||
};
|
||||
}
|
||||
|
||||
protected List<Item> GetArmorItems(
|
||||
string chosenTpl,
|
||||
Item? rootItem,
|
||||
List<Item> items,
|
||||
TemplateItem armorDbTemplate
|
||||
)
|
||||
protected List<Item> GetArmorItems(string chosenTpl, Item? rootItem, List<Item> items, TemplateItem armorDbTemplate)
|
||||
{
|
||||
var defaultPreset = _presetHelper.GetDefaultPreset(chosenTpl);
|
||||
if (defaultPreset is not null)
|
||||
@@ -1282,16 +1099,9 @@ public class LocationLootGenerator(
|
||||
else
|
||||
{
|
||||
// We make base item in calling method, no need to do it here
|
||||
if (
|
||||
armorDbTemplate.Properties?.Slots is not null
|
||||
&& armorDbTemplate.Properties.Slots.Any()
|
||||
)
|
||||
if (armorDbTemplate.Properties?.Slots is not null && armorDbTemplate.Properties.Slots.Any())
|
||||
{
|
||||
items = _itemHelper.AddChildSlotItems(
|
||||
items,
|
||||
armorDbTemplate,
|
||||
_locationConfig.EquipmentLootSettings.ModSpawnChancePercent
|
||||
);
|
||||
items = _itemHelper.AddChildSlotItems(items, armorDbTemplate, _locationConfig.EquipmentLootSettings.ModSpawnChancePercent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1322,10 +1132,7 @@ public class LocationLootGenerator(
|
||||
{
|
||||
try
|
||||
{
|
||||
children = _itemHelper.ReparentItemAndChildren(
|
||||
defaultPreset.Items.FirstOrDefault(),
|
||||
defaultPreset.Items
|
||||
);
|
||||
children = _itemHelper.ReparentItemAndChildren(defaultPreset.Items.FirstOrDefault(), defaultPreset.Items);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1361,16 +1168,9 @@ public class LocationLootGenerator(
|
||||
var rootItem = items.FirstOrDefault();
|
||||
if (rootItem is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-missing_root_item",
|
||||
new { tpl = chosenTpl, parentId }
|
||||
)
|
||||
);
|
||||
_logger.Error(_serverLocalisationService.GetText("location-missing_root_item", new { tpl = chosenTpl, parentId }));
|
||||
|
||||
throw new Exception(
|
||||
_serverLocalisationService.GetText("location-critical_error_see_log")
|
||||
);
|
||||
throw new Exception(_serverLocalisationService.GetText("location-critical_error_see_log"));
|
||||
}
|
||||
|
||||
try
|
||||
@@ -1382,12 +1182,7 @@ public class LocationLootGenerator(
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
"location-unable_to_reparent_item",
|
||||
new { tpl = chosenTpl, parentId }
|
||||
)
|
||||
);
|
||||
_logger.Error(_serverLocalisationService.GetText("location-unable_to_reparent_item", new { tpl = chosenTpl, parentId }));
|
||||
_logger.Error(e.StackTrace);
|
||||
|
||||
throw;
|
||||
|
||||
@@ -43,17 +43,12 @@ public class LootGenerator(
|
||||
var itemTypeCounts = InitItemLimitCounter(options.ItemLimits);
|
||||
|
||||
// Handle sealed weapon containers
|
||||
var sealedWeaponCrateCount = randomUtil.GetInt(
|
||||
options.WeaponCrateCount.Min,
|
||||
options.WeaponCrateCount.Max
|
||||
);
|
||||
var sealedWeaponCrateCount = randomUtil.GetInt(options.WeaponCrateCount.Min, options.WeaponCrateCount.Max);
|
||||
if (sealedWeaponCrateCount > 0)
|
||||
{
|
||||
// Get list of all sealed containers from db - they're all the same, just for flavor
|
||||
var itemsDb = databaseService.GetItems().Values;
|
||||
var sealedWeaponContainerPool = itemsDb.Where(item =>
|
||||
item.Name.Contains("event_container_airdrop")
|
||||
);
|
||||
var sealedWeaponContainerPool = itemsDb.Where(item => item.Name.Contains("event_container_airdrop"));
|
||||
|
||||
for (var index = 0; index < sealedWeaponCrateCount; index++)
|
||||
{
|
||||
@@ -84,20 +79,10 @@ public class LootGenerator(
|
||||
// Pool has items we could add as loot, proceed
|
||||
if (rewardPoolResults.ItemPool.Any())
|
||||
{
|
||||
var randomisedItemCount = randomUtil.GetInt(
|
||||
options.ItemCount.Min,
|
||||
options.ItemCount.Max
|
||||
);
|
||||
var randomisedItemCount = randomUtil.GetInt(options.ItemCount.Min, options.ItemCount.Max);
|
||||
for (var index = 0; index < randomisedItemCount; index++)
|
||||
{
|
||||
if (
|
||||
!FindAndAddRandomItemToLoot(
|
||||
rewardPoolResults.ItemPool,
|
||||
itemTypeCounts,
|
||||
options,
|
||||
result
|
||||
)
|
||||
)
|
||||
if (!FindAndAddRandomItemToLoot(rewardPoolResults.ItemPool, itemTypeCounts, options, result))
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
{
|
||||
index--;
|
||||
@@ -108,10 +93,7 @@ public class LootGenerator(
|
||||
var globalDefaultPresets = presetHelper.GetDefaultPresets().Values;
|
||||
|
||||
// Filter default presets to just weapons
|
||||
var randomisedWeaponPresetCount = randomUtil.GetInt(
|
||||
options.WeaponPresetCount.Min,
|
||||
options.WeaponPresetCount.Max
|
||||
);
|
||||
var randomisedWeaponPresetCount = randomUtil.GetInt(options.WeaponPresetCount.Min, options.WeaponPresetCount.Max);
|
||||
if (randomisedWeaponPresetCount > 0)
|
||||
{
|
||||
var weaponDefaultPresets = globalDefaultPresets.Where(preset =>
|
||||
@@ -122,14 +104,7 @@ public class LootGenerator(
|
||||
{
|
||||
for (var index = 0; index < randomisedWeaponPresetCount; index++)
|
||||
{
|
||||
if (
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
weaponDefaultPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result
|
||||
)
|
||||
)
|
||||
if (!FindAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, rewardPoolResults.Blacklist, result))
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
{
|
||||
index--;
|
||||
@@ -139,32 +114,18 @@ public class LootGenerator(
|
||||
}
|
||||
|
||||
// Filter default presets to just armors and then filter again by protection level
|
||||
var randomisedArmorPresetCount = randomUtil.GetInt(
|
||||
options.ArmorPresetCount.Min,
|
||||
options.ArmorPresetCount.Max
|
||||
);
|
||||
var randomisedArmorPresetCount = randomUtil.GetInt(options.ArmorPresetCount.Min, options.ArmorPresetCount.Max);
|
||||
if (randomisedArmorPresetCount > 0)
|
||||
{
|
||||
var armorDefaultPresets = globalDefaultPresets.Where(preset =>
|
||||
itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia.Value)
|
||||
);
|
||||
var levelFilteredArmorPresets = armorDefaultPresets.Where(armor =>
|
||||
IsArmorOfDesiredProtectionLevel(armor, options)
|
||||
);
|
||||
var armorDefaultPresets = globalDefaultPresets.Where(preset => itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia.Value));
|
||||
var levelFilteredArmorPresets = armorDefaultPresets.Where(armor => IsArmorOfDesiredProtectionLevel(armor, options));
|
||||
|
||||
// Add some armors to rewards
|
||||
if (levelFilteredArmorPresets.Any())
|
||||
{
|
||||
for (var index = 0; index < randomisedArmorPresetCount; index++)
|
||||
{
|
||||
if (
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
levelFilteredArmorPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result
|
||||
)
|
||||
)
|
||||
if (!FindAndAddRandomPresetToLoot(levelFilteredArmorPresets, itemTypeCounts, rewardPoolResults.Blacklist, result))
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
{
|
||||
index--;
|
||||
@@ -202,10 +163,7 @@ public class LootGenerator(
|
||||
for (var i = 0; i < randomisedItemCount; i++)
|
||||
{
|
||||
// Clone preset and alter Ids to be unique
|
||||
var presetWithUniqueIdsClone = cloner
|
||||
.Clone(preset.Items)
|
||||
.ReplaceIDs()
|
||||
.ToList();
|
||||
var presetWithUniqueIdsClone = cloner.Clone(preset.Items).ReplaceIDs().ToList();
|
||||
|
||||
// Add to results
|
||||
result.Add(presetWithUniqueIdsClone);
|
||||
@@ -262,9 +220,7 @@ public class LootGenerator(
|
||||
var itemTypeBlacklist = itemFilterService.GetItemRewardBaseTypeBlacklist();
|
||||
var itemsMatchingTypeBlacklist = itemsDb
|
||||
.Where(templateItem => !string.IsNullOrEmpty(templateItem.Parent)) // Ignore items without parents
|
||||
.Where(templateItem =>
|
||||
itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist)
|
||||
)
|
||||
.Where(templateItem => itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist))
|
||||
.Select(templateItem => templateItem.Id);
|
||||
|
||||
itemBlacklist.UnionWith([.. rewardItemBlacklist, .. itemsMatchingTypeBlacklist]);
|
||||
@@ -326,11 +282,7 @@ public class LootGenerator(
|
||||
var itemTypeCounts = new Dictionary<MongoId, ItemLimit>();
|
||||
foreach (var itemTypeId in limits)
|
||||
{
|
||||
itemTypeCounts[itemTypeId.Key] = new ItemLimit
|
||||
{
|
||||
Current = 0,
|
||||
Max = limits[itemTypeId.Key],
|
||||
};
|
||||
itemTypeCounts[itemTypeId.Key] = new ItemLimit { Current = 0, Max = limits[itemTypeId.Key] };
|
||||
}
|
||||
|
||||
return itemTypeCounts;
|
||||
@@ -353,10 +305,7 @@ public class LootGenerator(
|
||||
{
|
||||
var randomItem = randomUtil.GetArrayValue(items);
|
||||
|
||||
var itemLimitCount = itemTypeCounts.TryGetValue(
|
||||
randomItem.Parent,
|
||||
out var randomItemLimitCount
|
||||
);
|
||||
var itemLimitCount = itemTypeCounts.TryGetValue(randomItem.Parent, out var randomItemLimitCount);
|
||||
if (!itemLimitCount && randomItemLimitCount?.Current > randomItemLimitCount?.Max)
|
||||
{
|
||||
return false;
|
||||
@@ -444,12 +393,7 @@ public class LootGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"loot-chosen_preset_missing_encyclopedia_value",
|
||||
chosenPreset?.Id
|
||||
)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("loot-chosen_preset_missing_encyclopedia_value", chosenPreset?.Id));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -461,9 +405,7 @@ public class LootGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping"
|
||||
);
|
||||
logger.Debug($"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping");
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -478,21 +420,13 @@ public class LootGenerator(
|
||||
// Some custom mod items lack a parent property
|
||||
if (itemDbDetails.Value?.Parent is null)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"loot-item_missing_parentid",
|
||||
itemDbDetails.Value?.Name
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("loot-item_missing_parentid", itemDbDetails.Value?.Name));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check chosen preset hasn't exceeded spawn limit
|
||||
var hasItemLimitCount = itemTypeCounts.TryGetValue(
|
||||
itemDbDetails.Value.Parent,
|
||||
out var itemLimitCount
|
||||
);
|
||||
var hasItemLimitCount = itemTypeCounts.TryGetValue(itemDbDetails.Value.Parent, out var itemLimitCount);
|
||||
if (!hasItemLimitCount && itemLimitCount?.Current > itemLimitCount?.Max)
|
||||
{
|
||||
return false;
|
||||
@@ -521,27 +455,18 @@ public class LootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="containerSettings">sealed weapon container settings</param>
|
||||
/// <returns>List of items with children lists</returns>
|
||||
public List<List<Item>> GetSealedWeaponCaseLoot(
|
||||
SealedAirdropContainerSettings containerSettings
|
||||
)
|
||||
public List<List<Item>> GetSealedWeaponCaseLoot(SealedAirdropContainerSettings containerSettings)
|
||||
{
|
||||
List<List<Item>> itemsToReturn = [];
|
||||
|
||||
// Choose a weapon to give to the player (weighted)
|
||||
var chosenWeaponTpl = weightedRandomHelper.GetWeightedValue(
|
||||
containerSettings.WeaponRewardWeight
|
||||
);
|
||||
var chosenWeaponTpl = weightedRandomHelper.GetWeightedValue(containerSettings.WeaponRewardWeight);
|
||||
|
||||
// Get itemDb details of weapon
|
||||
var weaponDetailsDb = itemHelper.GetItem(chosenWeaponTpl);
|
||||
if (!weaponDetailsDb.Key)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"loot-non_item_picked_as_sealed_weapon_crate_reward",
|
||||
chosenWeaponTpl
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("loot-non_item_picked_as_sealed_weapon_crate_reward", chosenWeaponTpl));
|
||||
|
||||
return itemsToReturn;
|
||||
}
|
||||
@@ -554,12 +479,7 @@ public class LootGenerator(
|
||||
// No default preset found for weapon, choose a random one
|
||||
if (chosenWeaponPreset is null)
|
||||
{
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText(
|
||||
"loot-default_preset_not_found_using_random",
|
||||
chosenWeaponTpl
|
||||
)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("loot-default_preset_not_found_using_random", chosenWeaponTpl));
|
||||
chosenWeaponPreset = randomUtil.GetArrayValue(presetHelper.GetPresets(chosenWeaponTpl));
|
||||
}
|
||||
|
||||
@@ -572,18 +492,10 @@ public class LootGenerator(
|
||||
|
||||
// Get a random collection of weapon mods related to chosen weapon and add them to result array
|
||||
var linkedItemsToWeapon = ragfairLinkedItemService.GetLinkedDbItems(chosenWeaponTpl);
|
||||
itemsToReturn.AddRange(
|
||||
GetSealedContainerWeaponModRewards(
|
||||
containerSettings,
|
||||
linkedItemsToWeapon,
|
||||
chosenWeaponPreset
|
||||
)
|
||||
);
|
||||
itemsToReturn.AddRange(GetSealedContainerWeaponModRewards(containerSettings, linkedItemsToWeapon, chosenWeaponPreset));
|
||||
|
||||
// Handle non-weapon mod reward types
|
||||
itemsToReturn.AddRange(
|
||||
GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value)
|
||||
);
|
||||
itemsToReturn.AddRange(GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value));
|
||||
|
||||
return itemsToReturn;
|
||||
}
|
||||
@@ -621,9 +533,7 @@ public class LootGenerator(
|
||||
|
||||
// Need to find boxes that matches weapons caliber
|
||||
var weaponCaliber = weaponDetailsDb.Properties.AmmoCaliber;
|
||||
var ammoBoxesMatchingCaliber = ammoBoxesDetails.Where(x =>
|
||||
x.Properties.AmmoCaliber == weaponCaliber
|
||||
);
|
||||
var ammoBoxesMatchingCaliber = ammoBoxesDetails.Where(x => x.Properties.AmmoCaliber == weaponCaliber);
|
||||
if (!ammoBoxesMatchingCaliber.Any())
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
@@ -718,9 +628,7 @@ public class LootGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"No items found to fulfil reward type: {rewardKey} for weapon: {chosenWeaponPreset.Name}, skipping type"
|
||||
);
|
||||
logger.Debug($"No items found to fulfil reward type: {rewardKey} for weapon: {chosenWeaponPreset.Name}, skipping type");
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -784,17 +692,13 @@ public class LootGenerator(
|
||||
/// <returns>Single tpl</returns>
|
||||
protected MongoId PickRewardItem(RewardDetails rewardContainerDetails)
|
||||
{
|
||||
if (
|
||||
rewardContainerDetails.RewardTplPool is not null
|
||||
&& rewardContainerDetails.RewardTplPool.Count > 0
|
||||
)
|
||||
if (rewardContainerDetails.RewardTplPool is not null && rewardContainerDetails.RewardTplPool.Count > 0)
|
||||
{
|
||||
return weightedRandomHelper.GetWeightedValue(rewardContainerDetails.RewardTplPool);
|
||||
}
|
||||
|
||||
return randomUtil.GetArrayValue(
|
||||
GetItemRewardPool([], rewardContainerDetails.RewardTypePool, true, true, false)
|
||||
.ItemPool.Select(item => item.Id)
|
||||
GetItemRewardPool([], rewardContainerDetails.RewardTypePool, true, true, false).ItemPool.Select(item => item.Id)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,13 +58,7 @@ public class PMCLootGenerator(
|
||||
var pocketPriceOverrides = GetPMCPriceOverrides(pmcRole, "pocket");
|
||||
|
||||
// Generate loot and cache - Also pass check to ensure only 1x2 items are allowed (Unheard bots have big pockets, hence the need for 1x2)
|
||||
var pool = GenerateLootPool(
|
||||
pmcRole,
|
||||
allowedItemTypeWhitelist,
|
||||
blacklist,
|
||||
pocketPriceOverrides,
|
||||
ItemFitsInto1By2Slot
|
||||
);
|
||||
var pool = GenerateLootPool(pmcRole, allowedItemTypeWhitelist, blacklist, pocketPriceOverrides, ItemFitsInto1By2Slot);
|
||||
_pocketLootPool.TryAdd(pmcRole, pool);
|
||||
|
||||
return pool;
|
||||
@@ -97,13 +91,7 @@ public class PMCLootGenerator(
|
||||
var vestPriceOverrides = GetPMCPriceOverrides(pmcRole, "vest");
|
||||
|
||||
// Generate loot and cache - Also pass check to ensure items up to 2x2 are allowed, some vests have big slots
|
||||
var pool = GenerateLootPool(
|
||||
pmcRole,
|
||||
allowedItemTypeWhitelist,
|
||||
blacklist,
|
||||
vestPriceOverrides,
|
||||
ItemFitsInto2By2Slot
|
||||
);
|
||||
var pool = GenerateLootPool(pmcRole, allowedItemTypeWhitelist, blacklist, vestPriceOverrides, ItemFitsInto2By2Slot);
|
||||
_vestLootPool.TryAdd(pmcRole, pool);
|
||||
|
||||
return pool;
|
||||
@@ -133,13 +121,7 @@ public class PMCLootGenerator(
|
||||
var backpackPriceOverrides = GetPMCPriceOverrides(pmcRole, "vest");
|
||||
|
||||
// Generate loot and cache
|
||||
var pool = GenerateLootPool(
|
||||
pmcRole,
|
||||
allowedItemTypeWhitelist,
|
||||
blacklist,
|
||||
backpackPriceOverrides,
|
||||
null
|
||||
);
|
||||
var pool = GenerateLootPool(pmcRole, allowedItemTypeWhitelist, blacklist, backpackPriceOverrides, null);
|
||||
_backpackLootPool.TryAdd(pmcRole, pool);
|
||||
|
||||
return pool;
|
||||
@@ -225,9 +207,7 @@ public class PMCLootGenerator(
|
||||
/// <returns>Dictionary of overrides</returns>
|
||||
protected Dictionary<MongoId, double> GetPMCPriceOverrides(string pmcRole, string slot)
|
||||
{
|
||||
var pmcType = string.Equals(pmcRole, "pmcbear", StringComparison.OrdinalIgnoreCase)
|
||||
? "bear"
|
||||
: "usec";
|
||||
var pmcType = string.Equals(pmcRole, "pmcbear", StringComparison.OrdinalIgnoreCase) ? "bear" : "usec";
|
||||
|
||||
// the usec/bear.json item prices act as overrides we apply over what we dynamically generate
|
||||
if (databaseService.GetBots().Types.TryGetValue(pmcType, out var priceOverrides))
|
||||
@@ -253,15 +233,9 @@ public class PMCLootGenerator(
|
||||
/// <param name="tpl">Item tpl to get price of</param>
|
||||
/// <param name="pmcPriceOverrides"></param>
|
||||
/// <returns>Rouble price</returns>
|
||||
protected double GetItemPrice(
|
||||
MongoId tpl,
|
||||
Dictionary<MongoId, double>? pmcPriceOverrides = null
|
||||
)
|
||||
protected double GetItemPrice(MongoId tpl, Dictionary<MongoId, double>? pmcPriceOverrides = null)
|
||||
{
|
||||
if (
|
||||
pmcPriceOverrides is not null
|
||||
&& pmcPriceOverrides.TryGetValue(tpl, out var overridePrice)
|
||||
)
|
||||
if (pmcPriceOverrides is not null && pmcPriceOverrides.TryGetValue(tpl, out var overridePrice))
|
||||
{
|
||||
// There's a price override for this item, use override instead of default price
|
||||
return overridePrice;
|
||||
|
||||
@@ -36,8 +36,7 @@ public class PlayerScavGenerator(
|
||||
TimeUtil timeUtil
|
||||
)
|
||||
{
|
||||
protected readonly PlayerScavConfig _playerScavConfig =
|
||||
configServer.GetConfig<PlayerScavConfig>();
|
||||
protected readonly PlayerScavConfig _playerScavConfig = configServer.GetConfig<PlayerScavConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Update a player profile to include a new player scav profile
|
||||
@@ -62,9 +61,7 @@ public class PlayerScavGenerator(
|
||||
)
|
||||
)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("scav-missing_karma_settings", scavKarmaLevel)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("scav-missing_karma_settings", scavKarmaLevel));
|
||||
}
|
||||
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
@@ -110,13 +107,9 @@ public class PlayerScavGenerator(
|
||||
scavData.Info.Level = GetScavLevel(existingScavDataClone);
|
||||
scavData.Info.Experience = GetScavExperience(existingScavDataClone);
|
||||
scavData.Quests = existingScavDataClone.Quests ?? [];
|
||||
scavData.TaskConditionCounters =
|
||||
existingScavDataClone.TaskConditionCounters
|
||||
?? new Dictionary<MongoId, TaskConditionCounter>();
|
||||
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? new Dictionary<MongoId, TaskConditionCounter>();
|
||||
scavData.Notes = existingScavDataClone.Notes ?? new Notes { DataNotes = [] };
|
||||
scavData.WishList =
|
||||
existingScavDataClone.WishList
|
||||
?? new DictionaryOrList<MongoId, int>(new Dictionary<MongoId, int>(), []);
|
||||
scavData.WishList = existingScavDataClone.WishList ?? new DictionaryOrList<MongoId, int>(new Dictionary<MongoId, int>(), []);
|
||||
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? new Dictionary<MongoId, bool>();
|
||||
|
||||
// Add additional items to player scav as loot
|
||||
@@ -161,9 +154,7 @@ public class PlayerScavGenerator(
|
||||
var itemResult = itemHelper.GetItem(tpl.Key);
|
||||
if (!itemResult.Key)
|
||||
{
|
||||
logger.Warning(
|
||||
serverLocalisationService.GetText("scav-unable_to_add_item_to_player_scav", tpl)
|
||||
);
|
||||
logger.Warning(serverLocalisationService.GetText("scav-unable_to_add_item_to_player_scav", tpl));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -174,10 +165,7 @@ public class PlayerScavGenerator(
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = itemTemplate.Id,
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(
|
||||
itemTemplate,
|
||||
"assault"
|
||||
),
|
||||
Upd = botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate, "assault"),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -229,22 +217,13 @@ public class PlayerScavGenerator(
|
||||
/// </summary>
|
||||
/// <param name="karmaSettings">Values to modify the bot template with</param>
|
||||
/// <param name="baseBotNode">bot template to modify according to karma level settings</param>
|
||||
protected void AdjustBotTemplateWithKarmaSpecificSettings(
|
||||
KarmaLevel karmaSettings,
|
||||
BotType baseBotNode
|
||||
)
|
||||
protected void AdjustBotTemplateWithKarmaSpecificSettings(KarmaLevel karmaSettings, BotType baseBotNode)
|
||||
{
|
||||
// Adjust equipment chance values
|
||||
AdjustEquipmentWeights(
|
||||
karmaSettings.Modifiers.Equipment,
|
||||
baseBotNode.BotChances.EquipmentChances
|
||||
);
|
||||
AdjustEquipmentWeights(karmaSettings.Modifiers.Equipment, baseBotNode.BotChances.EquipmentChances);
|
||||
|
||||
// Adjust mod chance values
|
||||
AdjustWeaponModWeights(
|
||||
karmaSettings.Modifiers.Mod,
|
||||
baseBotNode.BotChances.WeaponModsChances
|
||||
);
|
||||
AdjustWeaponModWeights(karmaSettings.Modifiers.Mod, baseBotNode.BotChances.WeaponModsChances);
|
||||
|
||||
// Adjust item spawn quantity values
|
||||
AdjustItemWeights(karmaSettings.ItemLimits, baseBotNode.BotGeneration.Items);
|
||||
@@ -281,10 +260,7 @@ public class PlayerScavGenerator(
|
||||
/// <param name="key">e.g. "healing" / "looseLoot"</param>
|
||||
/// <param name="botItemWeights"></param>
|
||||
/// <returns>GenerationData</returns>
|
||||
protected GenerationData? GetKarmaLimitValuesByKey(
|
||||
string key,
|
||||
GenerationWeightingItems botItemWeights
|
||||
)
|
||||
protected GenerationData? GetKarmaLimitValuesByKey(string key, GenerationWeightingItems botItemWeights)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
@@ -318,10 +294,7 @@ public class PlayerScavGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
protected static void AdjustWeaponModWeights(
|
||||
Dictionary<string, double> modChangesToApply,
|
||||
Dictionary<string, double> weaponModChances
|
||||
)
|
||||
protected static void AdjustWeaponModWeights(Dictionary<string, double> modChangesToApply, Dictionary<string, double> weaponModChances)
|
||||
{
|
||||
foreach (var (modSlot, weight) in modChangesToApply)
|
||||
{
|
||||
@@ -416,26 +389,16 @@ public class PlayerScavGenerator(
|
||||
protected PmcData SetScavCooldownTimer(PmcData scavData, PmcData pmcData)
|
||||
{
|
||||
// Get sum of all scav cooldown reduction timer bonuses
|
||||
var modifier =
|
||||
1d
|
||||
+ pmcData
|
||||
.Bonuses.Where(x => x.Type == BonusType.ScavCooldownTimer)
|
||||
.Sum(bonus => (bonus?.Value ?? 1) / 100);
|
||||
var modifier = 1d + pmcData.Bonuses.Where(x => x.Type == BonusType.ScavCooldownTimer).Sum(bonus => (bonus?.Value ?? 1) / 100);
|
||||
|
||||
var fenceInfo = fenceService.GetFenceInfo(pmcData);
|
||||
modifier *= fenceInfo.SavageCooldownModifier;
|
||||
|
||||
// Make sure to apply ScavCooldownTimer bonus from Hideout if the player has it.
|
||||
var scavLockDuration =
|
||||
databaseService.GetGlobals().Configuration.SavagePlayCooldown * modifier;
|
||||
var scavLockDuration = databaseService.GetGlobals().Configuration.SavagePlayCooldown * modifier;
|
||||
|
||||
var fullProfile = profileHelper.GetFullProfile(pmcData.SessionId.Value);
|
||||
if (
|
||||
fullProfile?.ProfileInfo?.Edition?.StartsWith(
|
||||
AccountTypes.SPT_DEVELOPER,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
) ?? false
|
||||
)
|
||||
if (fullProfile?.ProfileInfo?.Edition?.StartsWith(AccountTypes.SPT_DEVELOPER, StringComparison.OrdinalIgnoreCase) ?? false)
|
||||
{
|
||||
// Force lock duration to 10seconds for dev profiles
|
||||
scavLockDuration = 10;
|
||||
|
||||
@@ -8,11 +8,7 @@ using SPTarkov.Server.Core.Services;
|
||||
namespace SPTarkov.Server.Core.Generators;
|
||||
|
||||
[Injectable]
|
||||
public class PmcWaveGenerator(
|
||||
ISptLogger<PmcWaveGenerator> logger,
|
||||
DatabaseService databaseService,
|
||||
ConfigServer configServer
|
||||
)
|
||||
public class PmcWaveGenerator(ISptLogger<PmcWaveGenerator> logger, DatabaseService databaseService, ConfigServer configServer)
|
||||
{
|
||||
protected readonly PmcConfig _pmcConfig = configServer.GetConfig<PmcConfig>();
|
||||
|
||||
@@ -58,12 +54,7 @@ public class PmcWaveGenerator(
|
||||
/// <param name="location"> Location Object </param>
|
||||
public void ApplyWaveChangesToMap(LocationBase location)
|
||||
{
|
||||
if (
|
||||
!_pmcConfig.CustomPmcWaves.TryGetValue(
|
||||
location.Id.ToLowerInvariant(),
|
||||
out var pmcWavesToAdd
|
||||
)
|
||||
)
|
||||
if (!_pmcConfig.CustomPmcWaves.TryGetValue(location.Id.ToLowerInvariant(), out var pmcWavesToAdd))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,11 +55,7 @@ public class RagfairAssortGenerator(
|
||||
IEnumerable<List<Item>> results = [];
|
||||
|
||||
// Get cloned items from db
|
||||
var dbItems = databaseService
|
||||
.GetItems()
|
||||
.Where(item =>
|
||||
!string.Equals(item.Value.Type, "Node", StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
var dbItems = databaseService.GetItems().Where(item => !string.Equals(item.Value.Type, "Node", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Store processed preset tpls so we don't add them when processing non-preset items
|
||||
HashSet<MongoId> processedArmorItems = [];
|
||||
@@ -96,11 +92,7 @@ public class RagfairAssortGenerator(
|
||||
}
|
||||
|
||||
// Skip seasonal items when not in-season
|
||||
if (
|
||||
RagfairConfig.Dynamic.RemoveSeasonalItemsWhenNotInEvent
|
||||
&& !seasonalEventActive
|
||||
&& seasonalItemTplBlacklist.Contains(id)
|
||||
)
|
||||
if (RagfairConfig.Dynamic.RemoveSeasonalItemsWhenNotInEvent && !seasonalEventActive && seasonalItemTplBlacklist.Contains(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -101,21 +101,13 @@ public class RagfairOfferGenerator(
|
||||
|
||||
// Hydrate ammo boxes with cartridges + ensure only 1 item is present (ammo box)
|
||||
// On offer refresh don't re-add cartridges to ammo box that already has cartridges
|
||||
if (
|
||||
itemsClone.Count == 1
|
||||
&& itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX)
|
||||
)
|
||||
if (itemsClone.Count == 1 && itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
itemHelper.AddCartridgesToAmmoBox(
|
||||
itemsClone,
|
||||
itemHelper.GetItem(rootItem.Template).Value
|
||||
);
|
||||
itemHelper.AddCartridgesToAmmoBox(itemsClone, itemHelper.GetItem(rootItem.Template).Value);
|
||||
}
|
||||
|
||||
var roubleListingPrice = Math.Round(ConvertOfferRequirementsIntoRoubles(offerRequirements));
|
||||
var singleItemListingPrice = details.SellInOnePiece
|
||||
? roubleListingPrice / details.Quantity
|
||||
: roubleListingPrice;
|
||||
var singleItemListingPrice = details.SellInOnePiece ? roubleListingPrice / details.Quantity : roubleListingPrice;
|
||||
|
||||
var offer = new RagfairOffer
|
||||
{
|
||||
@@ -124,10 +116,7 @@ public class RagfairOfferGenerator(
|
||||
User =
|
||||
details.Creator == OfferCreator.Player
|
||||
? CreatePlayerUserDataForFleaOffer(details.UserId)
|
||||
: CreateUserDataForFleaOffer(
|
||||
details.UserId,
|
||||
details.Creator == OfferCreator.Trader
|
||||
),
|
||||
: CreateUserDataForFleaOffer(details.UserId, details.Creator == OfferCreator.Trader),
|
||||
Root = rootItem.Id,
|
||||
Items = itemsClone,
|
||||
ItemsCost = Math.Round(handbookHelper.GetTemplatePrice(rootItem.Template)), // Handbook price
|
||||
@@ -167,10 +156,7 @@ public class RagfairOfferGenerator(
|
||||
Id = userId,
|
||||
MemberType = MemberCategory.Default,
|
||||
Nickname = botHelper.GetPmcNicknameOfMaxLength(botConfig.BotNameLengthLimit),
|
||||
Rating = randomUtil.GetDouble(
|
||||
ragfairConfig.Dynamic.Rating.Min,
|
||||
ragfairConfig.Dynamic.Rating.Max
|
||||
),
|
||||
Rating = randomUtil.GetDouble(ragfairConfig.Dynamic.Rating.Min, ragfairConfig.Dynamic.Rating.Max),
|
||||
IsRatingGrowing = randomUtil.GetBool(),
|
||||
Avatar = null,
|
||||
Aid = hashUtil.GenerateAccountId(),
|
||||
@@ -203,17 +189,14 @@ public class RagfairOfferGenerator(
|
||||
/// </summary>
|
||||
/// <param name="offerRequirements"> barter requirements for offer </param>
|
||||
/// <returns> rouble cost of offer </returns>
|
||||
protected double ConvertOfferRequirementsIntoRoubles(
|
||||
IEnumerable<OfferRequirement> offerRequirements
|
||||
)
|
||||
protected double ConvertOfferRequirementsIntoRoubles(IEnumerable<OfferRequirement> offerRequirements)
|
||||
{
|
||||
var roublePrice = 0d;
|
||||
foreach (var requirement in offerRequirements)
|
||||
{
|
||||
roublePrice += paymentHelper.IsMoneyTpl(requirement.TemplateId)
|
||||
? Math.Round(CalculateRoublePrice(requirement.Count.Value, requirement.TemplateId))
|
||||
: ragfairPriceService.GetFleaPriceForItem(requirement.TemplateId)
|
||||
* requirement.Count.Value; // Get flea price for barter offer items
|
||||
: ragfairPriceService.GetFleaPriceForItem(requirement.TemplateId) * requirement.Count.Value; // Get flea price for barter offer items
|
||||
}
|
||||
|
||||
return roublePrice;
|
||||
@@ -271,10 +254,7 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
// Generated pmc offer
|
||||
return randomUtil.GetDouble(
|
||||
ragfairConfig.Dynamic.Rating.Min,
|
||||
ragfairConfig.Dynamic.Rating.Max
|
||||
);
|
||||
return randomUtil.GetDouble(ragfairConfig.Dynamic.Rating.Min, ragfairConfig.Dynamic.Rating.Max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -289,13 +269,8 @@ public class RagfairOfferGenerator(
|
||||
if (creatorType == OfferCreator.Player)
|
||||
{
|
||||
// Player offer = current time + offerDurationTimeInHour;
|
||||
var offerDurationTimeHours = databaseService
|
||||
.GetGlobals()
|
||||
.Configuration.RagFair.OfferDurationTimeInHour;
|
||||
return (long)(
|
||||
timeUtil.GetTimeStamp()
|
||||
+ Math.Round((double)offerDurationTimeHours * TimeUtil.OneHourAsSeconds)
|
||||
);
|
||||
var offerDurationTimeHours = databaseService.GetGlobals().Configuration.RagFair.OfferDurationTimeInHour;
|
||||
return (long)(timeUtil.GetTimeStamp() + Math.Round((double)offerDurationTimeHours * TimeUtil.OneHourAsSeconds));
|
||||
}
|
||||
|
||||
if (creatorType == OfferCreator.Trader)
|
||||
@@ -304,10 +279,7 @@ public class RagfairOfferGenerator(
|
||||
return (long)databaseService.GetTrader(userID).Base.NextResupply;
|
||||
}
|
||||
|
||||
var randomSpread = randomUtil.GetDouble(
|
||||
ragfairConfig.Dynamic.EndTimeSeconds.Min,
|
||||
ragfairConfig.Dynamic.EndTimeSeconds.Max
|
||||
);
|
||||
var randomSpread = randomUtil.GetDouble(ragfairConfig.Dynamic.EndTimeSeconds.Min, ragfairConfig.Dynamic.EndTimeSeconds.Max);
|
||||
|
||||
// Fake-player offer
|
||||
return (long)Math.Round(time + randomSpread);
|
||||
@@ -323,15 +295,11 @@ public class RagfairOfferGenerator(
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
// get assort items from param if they exist, otherwise grab freshly generated assorts
|
||||
var assortItemsToProcess = replacingExpiredOffers
|
||||
? expiredOffers ?? []
|
||||
: ragfairAssortGenerator.GetAssortItems();
|
||||
var assortItemsToProcess = replacingExpiredOffers ? expiredOffers ?? [] : ragfairAssortGenerator.GetAssortItems();
|
||||
stopwatch.Stop();
|
||||
if (logger.IsLogEnabled(LogLevel.Debug) && stopwatch.ElapsedMilliseconds > 0)
|
||||
{
|
||||
logger.Debug(
|
||||
$"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count()} items"
|
||||
);
|
||||
logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count()} items");
|
||||
}
|
||||
|
||||
stopwatch.Restart();
|
||||
@@ -341,11 +309,7 @@ public class RagfairOfferGenerator(
|
||||
tasks.Add(
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
CreateOffersFromAssort(
|
||||
assortItemWithChildren,
|
||||
replacingExpiredOffers,
|
||||
ragfairConfig.Dynamic
|
||||
);
|
||||
CreateOffersFromAssort(assortItemWithChildren, replacingExpiredOffers, ragfairConfig.Dynamic);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -364,11 +328,7 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="assortItemWithChildren"> Item with its children to process into offers </param>
|
||||
/// <param name="isExpiredOffer"> Is an expired offer </param>
|
||||
/// <param name="config"> Ragfair dynamic config </param>
|
||||
protected void CreateOffersFromAssort(
|
||||
List<Item> assortItemWithChildren,
|
||||
bool isExpiredOffer,
|
||||
Dynamic config
|
||||
)
|
||||
protected void CreateOffersFromAssort(List<Item> assortItemWithChildren, bool isExpiredOffer, Dynamic config)
|
||||
{
|
||||
var rootItem = assortItemWithChildren.FirstOrDefault();
|
||||
var itemToSellDetails = itemHelper.GetItem(rootItem.Template);
|
||||
@@ -380,22 +340,15 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
// Armor presets can hold plates above the allowed flea level, remove if necessary
|
||||
var isPreset =
|
||||
rootItem?.Upd?.SptPresetId is not null
|
||||
&& presetHelper.IsPreset(rootItem.Upd.SptPresetId.Value);
|
||||
var isPreset = rootItem?.Upd?.SptPresetId is not null && presetHelper.IsPreset(rootItem.Upd.SptPresetId.Value);
|
||||
if (!isExpiredOffer && isPreset && ragfairConfig.Dynamic.Blacklist.EnableBsgList)
|
||||
{
|
||||
RemoveBannedPlatesFromPreset(
|
||||
assortItemWithChildren,
|
||||
ragfairConfig.Dynamic.Blacklist.ArmorPlate
|
||||
);
|
||||
RemoveBannedPlatesFromPreset(assortItemWithChildren, ragfairConfig.Dynamic.Blacklist.ArmorPlate);
|
||||
}
|
||||
|
||||
// Get number of offers to create
|
||||
// Limit to 1 offer when processing expired - like-for-like replacement
|
||||
var offerCount = isExpiredOffer
|
||||
? 1
|
||||
: ragfairServerHelper.GetOfferCountByBaseType(itemToSellDetails.Value.Parent);
|
||||
var offerCount = isExpiredOffer ? 1 : ragfairServerHelper.GetOfferCountByBaseType(itemToSellDetails.Value.Parent);
|
||||
|
||||
for (var index = 0; index < offerCount; index++)
|
||||
{
|
||||
@@ -424,10 +377,7 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="presetWithChildren"> Preset to check for plates </param>
|
||||
/// <param name="plateSettings"> Settings </param>
|
||||
/// <returns> True if plates removed </returns>
|
||||
protected bool RemoveBannedPlatesFromPreset(
|
||||
List<Item> presetWithChildren,
|
||||
ArmorPlateBlacklistSettings plateSettings
|
||||
)
|
||||
protected bool RemoveBannedPlatesFromPreset(List<Item> presetWithChildren, ArmorPlateBlacklistSettings plateSettings)
|
||||
{
|
||||
if (!itemHelper.ArmorItemCanHoldMods(presetWithChildren[0].Template))
|
||||
// Cant hold armor inserts, skip
|
||||
@@ -436,9 +386,7 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
var plateSlots = presetWithChildren
|
||||
.Where(item =>
|
||||
itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLowerInvariant())
|
||||
)
|
||||
.Where(item => itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLowerInvariant()))
|
||||
.ToList();
|
||||
if (plateSlots.Count == 0)
|
||||
// Has no plate slots e.g. "front_plate", exit
|
||||
@@ -487,10 +435,7 @@ public class RagfairOfferGenerator(
|
||||
var rootItem = itemWithChildren.FirstOrDefault();
|
||||
|
||||
// Get randomised amount to list on flea
|
||||
var desiredStackSize = ragfairServerHelper.CalculateDynamicStackCount(
|
||||
rootItem.Template,
|
||||
isPreset
|
||||
);
|
||||
var desiredStackSize = ragfairServerHelper.CalculateDynamicStackCount(rootItem.Template, isPreset);
|
||||
|
||||
// Reset stack count to 1 from whatever it was prior
|
||||
rootItem.Upd.StackObjectsCount = 1;
|
||||
@@ -506,36 +451,21 @@ public class RagfairOfferGenerator(
|
||||
!isBarterOffer
|
||||
&& randomUtil.GetChance100(ragfairConfig.Dynamic.Pack.ChancePercent)
|
||||
&& itemWithChildren.Count == 1
|
||||
&& itemHelper.IsOfBaseclasses(
|
||||
rootItem.Template,
|
||||
ragfairConfig.Dynamic.Pack.ItemTypeWhitelist
|
||||
);
|
||||
&& itemHelper.IsOfBaseclasses(rootItem.Template, ragfairConfig.Dynamic.Pack.ItemTypeWhitelist);
|
||||
|
||||
List<BarterScheme> barterScheme;
|
||||
if (isPackOffer)
|
||||
{
|
||||
// Set pack size
|
||||
desiredStackSize = randomUtil.GetInt(
|
||||
ragfairConfig.Dynamic.Pack.ItemCountMin,
|
||||
ragfairConfig.Dynamic.Pack.ItemCountMax
|
||||
);
|
||||
desiredStackSize = randomUtil.GetInt(ragfairConfig.Dynamic.Pack.ItemCountMin, ragfairConfig.Dynamic.Pack.ItemCountMax);
|
||||
|
||||
// Don't randomise pack items
|
||||
barterScheme = CreateCurrencyBarterScheme(
|
||||
itemWithChildren,
|
||||
isPackOffer,
|
||||
desiredStackSize
|
||||
);
|
||||
barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer, desiredStackSize);
|
||||
}
|
||||
else if (isBarterOffer)
|
||||
{
|
||||
// Apply randomised properties
|
||||
RandomiseOfferItemUpdProperties(
|
||||
sellerId,
|
||||
itemWithChildren,
|
||||
itemToSellDetails,
|
||||
offerCreator
|
||||
);
|
||||
RandomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails, offerCreator);
|
||||
barterScheme = CreateBarterBarterScheme(itemWithChildren, ragfairConfig.Dynamic.Barter);
|
||||
if (ragfairConfig.Dynamic.Barter.MakeSingleStackOnly)
|
||||
{
|
||||
@@ -550,12 +480,7 @@ public class RagfairOfferGenerator(
|
||||
{
|
||||
// Not barter or pack offer
|
||||
// Apply randomised properties
|
||||
RandomiseOfferItemUpdProperties(
|
||||
sellerId,
|
||||
itemWithChildren,
|
||||
itemToSellDetails,
|
||||
offerCreator
|
||||
);
|
||||
RandomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails, offerCreator);
|
||||
barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer);
|
||||
}
|
||||
|
||||
@@ -593,9 +518,7 @@ public class RagfairOfferGenerator(
|
||||
);
|
||||
|
||||
// Latest first, to ensure we don't move later items off by 1 each time we remove an item below it
|
||||
var indexesToRemove = offerItemPlatesToRemove
|
||||
.Select(plateItem => itemWithChildren.IndexOf(plateItem))
|
||||
.ToHashSet();
|
||||
var indexesToRemove = offerItemPlatesToRemove.Select(plateItem => itemWithChildren.IndexOf(plateItem)).ToHashSet();
|
||||
foreach (var index in indexesToRemove.OrderByDescending(x => x))
|
||||
{
|
||||
itemWithChildren.RemoveAt(index);
|
||||
@@ -618,19 +541,12 @@ public class RagfairOfferGenerator(
|
||||
// Trader assorts / assort items are missing
|
||||
if (assortsClone?.Items?.Count is null or 0)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"ragfair-no_trader_assorts_cant_generate_flea_offers",
|
||||
trader.Base.Nickname
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("ragfair-no_trader_assorts_cant_generate_flea_offers", trader.Base.Nickname));
|
||||
return;
|
||||
}
|
||||
|
||||
var blacklist = ragfairConfig.Dynamic.Blacklist;
|
||||
var childAssortItems = assortsClone
|
||||
.Items.Where(x => !string.Equals(x.ParentId, "hideout", StringComparison.Ordinal))
|
||||
.ToList();
|
||||
var childAssortItems = assortsClone.Items.Where(x => !string.Equals(x.ParentId, "hideout", StringComparison.Ordinal)).ToList();
|
||||
foreach (var item in assortsClone.Items)
|
||||
{
|
||||
// We only want to process 'base/root' items, no children
|
||||
@@ -646,17 +562,12 @@ public class RagfairOfferGenerator(
|
||||
var itemDetails = itemHelper.GetItem(item.Template);
|
||||
if (!itemDetails.Key)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText("ragfair-tpl_not_a_valid_item", item.Template)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("ragfair-tpl_not_a_valid_item", item.Template));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't include items that BSG has blacklisted from flea
|
||||
if (
|
||||
blacklist.EnableBsgList
|
||||
&& !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false)
|
||||
)
|
||||
if (blacklist.EnableBsgList && !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -731,11 +642,7 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
// Roll random chance to randomise item condition
|
||||
if (
|
||||
randomUtil.GetChance100(
|
||||
ragfairConfig.Dynamic.Condition[parentId.Value].ConditionChance * 100
|
||||
)
|
||||
)
|
||||
if (randomUtil.GetChance100(ragfairConfig.Dynamic.Condition[parentId.Value].ConditionChance * 100))
|
||||
{
|
||||
RandomiseItemCondition(parentId.Value, itemWithMods, itemDetails);
|
||||
}
|
||||
@@ -768,39 +675,25 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="conditionSettingsId"> Also the parentID of item being altered </param>
|
||||
/// <param name="itemWithMods"> Item to adjust condition details of </param>
|
||||
/// <param name="itemDetails"> DB Item details of first item in list </param>
|
||||
protected void RandomiseItemCondition(
|
||||
MongoId conditionSettingsId,
|
||||
IEnumerable<Item> itemWithMods,
|
||||
TemplateItem itemDetails
|
||||
)
|
||||
protected void RandomiseItemCondition(MongoId conditionSettingsId, IEnumerable<Item> itemWithMods, TemplateItem itemDetails)
|
||||
{
|
||||
var rootItem = itemWithMods.First();
|
||||
|
||||
var itemConditionValues = ragfairConfig.Dynamic.Condition[conditionSettingsId];
|
||||
var maxMultiplier = randomUtil.GetDouble(
|
||||
itemConditionValues.Max.Min,
|
||||
itemConditionValues.Max.Min
|
||||
);
|
||||
var currentMultiplier = randomUtil.GetDouble(
|
||||
itemConditionValues.Current.Min,
|
||||
itemConditionValues.Current.Max
|
||||
);
|
||||
var maxMultiplier = randomUtil.GetDouble(itemConditionValues.Max.Min, itemConditionValues.Max.Min);
|
||||
var currentMultiplier = randomUtil.GetDouble(itemConditionValues.Current.Min, itemConditionValues.Current.Max);
|
||||
|
||||
// Randomise armor + plates + armor related things
|
||||
if (
|
||||
itemHelper.ArmorItemCanHoldMods(rootItem.Template)
|
||||
|| itemHelper.IsOfBaseclasses(
|
||||
rootItem.Template,
|
||||
[BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]
|
||||
)
|
||||
|| itemHelper.IsOfBaseclasses(rootItem.Template, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
|
||||
)
|
||||
{
|
||||
RandomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
|
||||
|
||||
// Add hits to visor
|
||||
var visorMod = itemWithMods.FirstOrDefault(item =>
|
||||
item.ParentId == BaseClasses.ARMORED_EQUIPMENT.ToString()
|
||||
&& item.SlotId == "mod_equipment_000"
|
||||
item.ParentId == BaseClasses.ARMORED_EQUIPMENT.ToString() && item.SlotId == "mod_equipment_000"
|
||||
);
|
||||
if (randomUtil.GetChance100(25) && visorMod != null)
|
||||
{
|
||||
@@ -815,12 +708,7 @@ public class RagfairOfferGenerator(
|
||||
// Randomise Weapons
|
||||
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.WEAPON))
|
||||
{
|
||||
RandomiseWeaponDurability(
|
||||
itemWithMods.First(),
|
||||
itemDetails,
|
||||
maxMultiplier,
|
||||
currentMultiplier
|
||||
);
|
||||
RandomiseWeaponDurability(itemWithMods.First(), itemDetails, maxMultiplier, currentMultiplier);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -836,8 +724,7 @@ public class RagfairOfferGenerator(
|
||||
if (rootItem.Upd?.Key != null && itemDetails.Properties.MaximumNumberOfUsage > 1)
|
||||
{
|
||||
// Randomize key uses
|
||||
rootItem.Upd.Key.NumberOfUsages = (int?)
|
||||
Math.Round(itemDetails.Properties.MaximumNumberOfUsage.Value * (1 - maxMultiplier));
|
||||
rootItem.Upd.Key.NumberOfUsages = (int?)Math.Round(itemDetails.Properties.MaximumNumberOfUsage.Value * (1 - maxMultiplier));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -853,9 +740,7 @@ public class RagfairOfferGenerator(
|
||||
if (rootItem.Upd?.RepairKit != null)
|
||||
{
|
||||
// randomize repair kit (armor/weapon) uses
|
||||
var resource = Math.Round(
|
||||
(double)itemDetails.Properties.MaxRepairResource * maxMultiplier
|
||||
);
|
||||
var resource = Math.Round((double)itemDetails.Properties.MaxRepairResource * maxMultiplier);
|
||||
rootItem.Upd.RepairKit.Resource = resource == 0D ? 1D : resource;
|
||||
|
||||
return;
|
||||
@@ -865,11 +750,7 @@ public class RagfairOfferGenerator(
|
||||
{
|
||||
var totalCapacity = itemDetails.Properties.MaxResource;
|
||||
var remainingFuel = Math.Round((double)totalCapacity * maxMultiplier);
|
||||
rootItem.Upd.Resource = new UpdResource
|
||||
{
|
||||
UnitsConsumed = totalCapacity - remainingFuel,
|
||||
Value = remainingFuel,
|
||||
};
|
||||
rootItem.Upd.Resource = new UpdResource { UnitsConsumed = totalCapacity - remainingFuel, Value = remainingFuel };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,29 +761,18 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="itemDbDetails"> Item details from DB </param>
|
||||
/// <param name="maxMultiplier"> Value to multiply max durability by </param>
|
||||
/// <param name="currentMultiplier"> Value to multiply current durability by </param>
|
||||
protected void RandomiseWeaponDurability(
|
||||
Item item,
|
||||
TemplateItem itemDbDetails,
|
||||
double maxMultiplier,
|
||||
double currentMultiplier
|
||||
)
|
||||
protected void RandomiseWeaponDurability(Item item, TemplateItem itemDbDetails, double maxMultiplier, double currentMultiplier)
|
||||
{
|
||||
// Max
|
||||
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
|
||||
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
|
||||
var chosenMaxDurability = Math.Round(
|
||||
randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability)
|
||||
);
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability));
|
||||
|
||||
// Current
|
||||
var lowestCurrentDurability =
|
||||
randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
var chosenCurrentDurability = Math.Round(
|
||||
randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability)
|
||||
);
|
||||
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
var chosenCurrentDurability = Math.Round(randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability));
|
||||
|
||||
item.Upd.Repairable.Durability =
|
||||
chosenCurrentDurability == 0 ? 1D : chosenCurrentDurability; // Never var value become 0
|
||||
item.Upd.Repairable.Durability = chosenCurrentDurability == 0 ? 1D : chosenCurrentDurability; // Never var value become 0
|
||||
item.Upd.Repairable.MaxDurability = chosenMaxDurability;
|
||||
}
|
||||
|
||||
@@ -912,11 +782,7 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="armorWithMods"> Armor item with its child mods </param>
|
||||
/// <param name="currentMultiplier"> Chosen multiplier to use for current durability value </param>
|
||||
/// <param name="maxMultiplier"> Chosen multiplier to use for max durability value </param>
|
||||
protected void RandomiseArmorDurabilityValues(
|
||||
IEnumerable<Item> armorWithMods,
|
||||
double currentMultiplier,
|
||||
double maxMultiplier
|
||||
)
|
||||
protected void RandomiseArmorDurabilityValues(IEnumerable<Item> armorWithMods, double currentMultiplier, double maxMultiplier)
|
||||
{
|
||||
foreach (var armorItem in armorWithMods)
|
||||
{
|
||||
@@ -926,17 +792,11 @@ public class RagfairOfferGenerator(
|
||||
itemHelper.AddUpdObjectToItem(armorItem);
|
||||
|
||||
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
|
||||
var lowestMaxDurability =
|
||||
randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
|
||||
var chosenMaxDurability = Math.Round(
|
||||
randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability)
|
||||
);
|
||||
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability));
|
||||
|
||||
var lowestCurrentDurability =
|
||||
randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
var chosenCurrentDurability = Math.Round(
|
||||
randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability)
|
||||
);
|
||||
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
var chosenCurrentDurability = Math.Round(randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability));
|
||||
|
||||
armorItem.Upd.Repairable = new UpdRepairable
|
||||
{
|
||||
@@ -964,11 +824,7 @@ public class RagfairOfferGenerator(
|
||||
|
||||
if (isRepairable && props.Durability > 0)
|
||||
{
|
||||
item.Upd.Repairable = new UpdRepairable
|
||||
{
|
||||
Durability = props.Durability,
|
||||
MaxDurability = props.Durability,
|
||||
};
|
||||
item.Upd.Repairable = new UpdRepairable { Durability = props.Durability, MaxDurability = props.Durability };
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1007,17 +863,10 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="offerItems"> Items for sale in offer </param>
|
||||
/// <param name="barterConfig"> Barter config from ragfairConfig.Dynamic.barter </param>
|
||||
/// <returns> Barter scheme </returns>
|
||||
protected List<BarterScheme> CreateBarterBarterScheme(
|
||||
IEnumerable<Item> offerItems,
|
||||
BarterDetails barterConfig
|
||||
)
|
||||
protected List<BarterScheme> CreateBarterBarterScheme(IEnumerable<Item> offerItems, BarterDetails barterConfig)
|
||||
{
|
||||
// Get flea price of item being sold
|
||||
var priceOfOfferItem = ragfairPriceService.GetDynamicOfferPriceForOffer(
|
||||
offerItems,
|
||||
Money.ROUBLES,
|
||||
false
|
||||
);
|
||||
var priceOfOfferItem = ragfairPriceService.GetDynamicOfferPriceForOffer(offerItems, Money.ROUBLES, false);
|
||||
|
||||
// Don't make items under a designated rouble value into barter offers
|
||||
if (priceOfOfferItem < barterConfig.MinRoubleCostToBecomeBarter)
|
||||
@@ -1026,17 +875,13 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
// Get a randomised number of barter items to list offer for
|
||||
var barterItemCount = randomUtil.GetInt(
|
||||
barterConfig.ItemCountMin,
|
||||
barterConfig.ItemCountMax
|
||||
);
|
||||
var barterItemCount = randomUtil.GetInt(barterConfig.ItemCountMin, barterConfig.ItemCountMax);
|
||||
|
||||
// Get desired cost of individual item offer will be listed for e.g. offer = 15k, item count = 3, desired item cost = 5k
|
||||
var desiredItemCostRouble = Math.Round(priceOfOfferItem / barterItemCount);
|
||||
|
||||
// Rouble amount to go above/below when looking for an item (Wiggle cost of item a little)
|
||||
var offerCostVarianceRoubles =
|
||||
desiredItemCostRouble * barterConfig.PriceRangeVariancePercent / 100;
|
||||
var offerCostVarianceRoubles = desiredItemCostRouble * barterConfig.PriceRangeVariancePercent / 100;
|
||||
|
||||
// Dict of items and their flea price (cached on first use)
|
||||
var itemFleaPrices = GetFleaPricesAsArray();
|
||||
@@ -1047,9 +892,7 @@ public class RagfairOfferGenerator(
|
||||
var rootOfferItem = offerItems.FirstOrDefault();
|
||||
|
||||
var itemsInsidePriceBounds = itemFleaPrices.Where(itemAndPrice =>
|
||||
itemAndPrice.Price >= min
|
||||
&& itemAndPrice.Price <= max
|
||||
&& itemAndPrice.Tpl != rootOfferItem.Template // Don't allow the item being sold to be chosen
|
||||
itemAndPrice.Price >= min && itemAndPrice.Price <= max && itemAndPrice.Tpl != rootOfferItem.Template // Don't allow the item being sold to be chosen
|
||||
);
|
||||
|
||||
// No items on flea have a matching price, fall back to currency
|
||||
@@ -1096,19 +939,10 @@ public class RagfairOfferGenerator(
|
||||
/// <param name="isPackOffer"> Is the barter scheme being created for a pack offer </param>
|
||||
/// <param name="multiplier"> What to multiply the resulting price by </param>
|
||||
/// <returns> Barter scheme for offer </returns>
|
||||
protected List<BarterScheme> CreateCurrencyBarterScheme(
|
||||
IEnumerable<Item> offerWithChildren,
|
||||
bool isPackOffer,
|
||||
double multiplier = 1
|
||||
)
|
||||
protected List<BarterScheme> CreateCurrencyBarterScheme(IEnumerable<Item> offerWithChildren, bool isPackOffer, double multiplier = 1)
|
||||
{
|
||||
var currency = ragfairServerHelper.GetDynamicOfferCurrency();
|
||||
var price =
|
||||
ragfairPriceService.GetDynamicOfferPriceForOffer(
|
||||
offerWithChildren,
|
||||
currency,
|
||||
isPackOffer
|
||||
) * multiplier;
|
||||
var price = ragfairPriceService.GetDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) * multiplier;
|
||||
|
||||
return [new BarterScheme { Count = price, Template = currency }];
|
||||
}
|
||||
|
||||
+21
-83
@@ -60,25 +60,15 @@ public class CompletionQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
"Quest template null when attempting to create completion operational task."
|
||||
);
|
||||
logger.Error("Quest template null when attempting to create completion operational task.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter the items.json items to items the player must retrieve to complete quest: shouldn't be a quest item or "non-existent"
|
||||
var itemsToRetrievePool = GetItemsToRetrievePool(
|
||||
completionConfig,
|
||||
repeatableConfig.RewardBlacklist
|
||||
);
|
||||
var itemsToRetrievePool = GetItemsToRetrievePool(completionConfig, repeatableConfig.RewardBlacklist);
|
||||
|
||||
// Filter items within our budget
|
||||
var (hashSet, budget) = GetItemsWithinBudget(
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
roublesConfig,
|
||||
itemsToRetrievePool
|
||||
);
|
||||
var (hashSet, budget) = GetItemsWithinBudget(pmcLevel, levelsConfig, roublesConfig, itemsToRetrievePool);
|
||||
itemsToRetrievePool = hashSet;
|
||||
|
||||
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
|
||||
@@ -96,22 +86,12 @@ public class CompletionQuestGenerator(
|
||||
// Filtering too harsh
|
||||
if (itemsToRetrievePool.Count == 0)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive"));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var selectedItems = GenerateAvailableForFinish(
|
||||
quest,
|
||||
completionConfig,
|
||||
repeatableConfig,
|
||||
itemsToRetrievePool.ToList(),
|
||||
budget
|
||||
);
|
||||
var selectedItems = GenerateAvailableForFinish(quest, completionConfig, repeatableConfig, itemsToRetrievePool.ToList(), budget);
|
||||
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
@@ -131,10 +111,7 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="completionConfig">Completion quest type config</param>
|
||||
/// <param name="itemTplBlacklist">Item tpls to not add to pool</param>
|
||||
/// <returns>Set of item tpls</returns>
|
||||
protected HashSet<MongoId> GetItemsToRetrievePool(
|
||||
Completion completionConfig,
|
||||
HashSet<MongoId> itemTplBlacklist
|
||||
)
|
||||
protected HashSet<MongoId> GetItemsToRetrievePool(Completion completionConfig, HashSet<MongoId> itemTplBlacklist)
|
||||
{
|
||||
// Get seasonal items that should not be added to pool as seasonal event is not active
|
||||
var seasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
@@ -184,19 +161,12 @@ public class CompletionQuestGenerator(
|
||||
{
|
||||
// Be fair, don't value the items be more expensive than the reward
|
||||
var multiplier = randomUtil.GetDouble(0.5, 1);
|
||||
var roublesBudget = Math.Floor(
|
||||
mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier
|
||||
);
|
||||
var roublesBudget = Math.Floor(mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier);
|
||||
|
||||
// Make sure there is always a 5000 rouble budget available for selection
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
|
||||
return (
|
||||
itemsToRetrievePool
|
||||
.Where(itemTpl => itemHelper.GetItemPrice(itemTpl) < roublesBudget)
|
||||
.ToHashSet(),
|
||||
roublesBudget
|
||||
);
|
||||
return (itemsToRetrievePool.Where(itemTpl => itemHelper.GetItemPrice(itemTpl) < roublesBudget).ToHashSet(), roublesBudget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,14 +175,9 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="itemSelection">Item selection to filter</param>
|
||||
/// <param name="pmcLevel">Level of pmc</param>
|
||||
/// <returns>Filtered selection, or original if null or empty</returns>
|
||||
protected HashSet<MongoId> GetWhitelistedItemSelection(
|
||||
HashSet<MongoId> itemSelection,
|
||||
int pmcLevel
|
||||
)
|
||||
protected HashSet<MongoId> GetWhitelistedItemSelection(HashSet<MongoId> itemSelection, int pmcLevel)
|
||||
{
|
||||
var itemWhitelist = databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests?.Data?.Completion?.ItemsWhitelist;
|
||||
var itemWhitelist = databaseService.GetTemplates().RepeatableQuests?.Data?.Completion?.ItemsWhitelist;
|
||||
|
||||
// Whitelist doesn't exist or is empty, return original
|
||||
if (itemWhitelist is null || itemWhitelist.Count == 0)
|
||||
@@ -221,17 +186,13 @@ public class CompletionQuestGenerator(
|
||||
}
|
||||
|
||||
// Filter and concatenate items according to current player level
|
||||
var itemIdsWhitelisted = itemWhitelist
|
||||
.Where(p => p.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(x => x.ItemIds)
|
||||
.ToHashSet(); //.Aggregate((a, p) => a.Concat(p.ItemIds), []);
|
||||
var itemIdsWhitelisted = itemWhitelist.Where(p => p.MinPlayerLevel <= pmcLevel).SelectMany(x => x.ItemIds).ToHashSet(); //.Aggregate((a, p) => a.Concat(p.ItemIds), []);
|
||||
|
||||
var filteredSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
// Whitelist can contain item tpls and item base type ids
|
||||
return itemIdsWhitelisted.Any(v => itemHelper.IsOfBaseclass(x, v))
|
||||
|| itemIdsWhitelisted.Contains(x);
|
||||
return itemIdsWhitelisted.Any(v => itemHelper.IsOfBaseclass(x, v)) || itemIdsWhitelisted.Contains(x);
|
||||
})
|
||||
.ToHashSet();
|
||||
|
||||
@@ -248,14 +209,9 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="itemSelection">Item selection to filter</param>
|
||||
/// <param name="pmcLevel">Level of pmc</param>
|
||||
/// <returns>Filtered selection, or original if null or empty</returns>
|
||||
protected HashSet<MongoId> GetBlacklistedItemSelection(
|
||||
HashSet<MongoId> itemSelection,
|
||||
int pmcLevel
|
||||
)
|
||||
protected HashSet<MongoId> GetBlacklistedItemSelection(HashSet<MongoId> itemSelection, int pmcLevel)
|
||||
{
|
||||
var itemBlacklist = databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests?.Data?.Completion?.ItemsBlacklist;
|
||||
var itemBlacklist = databaseService.GetTemplates().RepeatableQuests?.Data?.Completion?.ItemsBlacklist;
|
||||
|
||||
// Blacklist doesn't exist or is empty, return original
|
||||
if (itemBlacklist is null || itemBlacklist.Count == 0)
|
||||
@@ -272,8 +228,7 @@ public class CompletionQuestGenerator(
|
||||
var filteredSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
return itemIdsBlacklisted.All(v => !itemHelper.IsOfBaseclass(x, v))
|
||||
|| !itemIdsBlacklisted.Contains(x);
|
||||
return itemIdsBlacklisted.All(v => !itemHelper.IsOfBaseclass(x, v)) || !itemIdsBlacklisted.Contains(x);
|
||||
})
|
||||
.ToHashSet();
|
||||
|
||||
@@ -323,10 +278,7 @@ public class CompletionQuestGenerator(
|
||||
if (!found)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_reward_item_found_in_price_range",
|
||||
new { minPrice = 0, roublesBudget }
|
||||
)
|
||||
localisationService.GetText("repeatable-no_reward_item_found_in_price_range", new { minPrice = 0, roublesBudget })
|
||||
);
|
||||
|
||||
return chosenRequirementItemsTpls;
|
||||
@@ -356,17 +308,13 @@ public class CompletionQuestGenerator(
|
||||
|
||||
// Push a CompletionCondition with the item and the amount of the item into quest
|
||||
chosenRequirementItemsTpls.Add(tplChosen);
|
||||
quest.Conditions.AvailableForFinish.Add(
|
||||
GenerateCondition(tplChosen, value, repeatableConfig.QuestConfig.Completion)
|
||||
);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCondition(tplChosen, value, repeatableConfig.QuestConfig.Completion));
|
||||
|
||||
// Is there budget left for more items
|
||||
if (roublesBudget > 0)
|
||||
{
|
||||
// Reduce item pool to fit budget
|
||||
itemSelection = itemSelection
|
||||
.Where(tpl => itemHelper.GetItemPrice(tpl) < roublesBudget)
|
||||
.ToList();
|
||||
itemSelection = itemSelection.Where(tpl => itemHelper.GetItemPrice(tpl) < roublesBudget).ToList();
|
||||
|
||||
if (itemSelection.Count == 0)
|
||||
{
|
||||
@@ -393,22 +341,12 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="value">Amount of items of this specific type to request</param>
|
||||
/// <param name="completionConfig">Completion config from quest.json</param>
|
||||
/// <returns>object of "Completion"-condition</returns>
|
||||
protected QuestCondition GenerateCondition(
|
||||
MongoId itemTpl,
|
||||
double value,
|
||||
Completion completionConfig
|
||||
)
|
||||
protected QuestCondition GenerateCondition(MongoId itemTpl, double value, Completion completionConfig)
|
||||
{
|
||||
var onlyFoundInRaid = completionConfig.RequiredItemsAreFiR;
|
||||
var minDurability = itemHelper.IsOfBaseclasses(
|
||||
itemTpl,
|
||||
[BaseClasses.WEAPON, BaseClasses.ARMOR]
|
||||
)
|
||||
var minDurability = itemHelper.IsOfBaseclasses(itemTpl, [BaseClasses.WEAPON, BaseClasses.ARMOR])
|
||||
? randomUtil.GetArrayValue(
|
||||
[
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Min,
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Max,
|
||||
]
|
||||
[completionConfig.RequiredItemMinDurabilityMinMax.Min, completionConfig.RequiredItemMinDurabilityMinMax.Max]
|
||||
)
|
||||
: 0;
|
||||
|
||||
|
||||
+49
-174
@@ -35,14 +35,13 @@ public class EliminationQuestGenerator(
|
||||
/// <summary>
|
||||
/// Body parts to present to the client as opposed to the body part information in quest data.
|
||||
/// </summary>
|
||||
private static readonly FrozenDictionary<string, List<string>> _bodyPartsToClient =
|
||||
new Dictionary<string, List<string>>
|
||||
{
|
||||
{ BodyParts.Arms, [BodyParts.LeftArm, BodyParts.RightArm] },
|
||||
{ BodyParts.Legs, [BodyParts.LeftLeg, BodyParts.RightLeg] },
|
||||
{ BodyParts.Head, [BodyParts.Head] },
|
||||
{ BodyParts.Chest, [BodyParts.Chest, BodyParts.Stomach] },
|
||||
}.ToFrozenDictionary();
|
||||
private static readonly FrozenDictionary<string, List<string>> _bodyPartsToClient = new Dictionary<string, List<string>>
|
||||
{
|
||||
{ BodyParts.Arms, [BodyParts.LeftArm, BodyParts.RightArm] },
|
||||
{ BodyParts.Legs, [BodyParts.LeftLeg, BodyParts.RightLeg] },
|
||||
{ BodyParts.Head, [BodyParts.Head] },
|
||||
{ BodyParts.Chest, [BodyParts.Chest, BodyParts.Stomach] },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// MaxDistDifficulty is defined by 2, this could be a tuning parameter if we don't like the reward generation
|
||||
@@ -83,9 +82,7 @@ public class EliminationQuestGenerator(
|
||||
var generationData = GetGenerationData(repeatableConfig, pmcLevel);
|
||||
if (generationData is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-eliminationQuestGenerationData-is-null")
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-eliminationQuestGenerationData-is-null"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,18 +111,13 @@ public class EliminationQuestGenerator(
|
||||
|
||||
// Target on bodyPart max. difficulty is that of the least probable element
|
||||
var maxTargetDifficulty = 1 / generationData.TargetsConfig.MinProbability();
|
||||
var maxBodyPartsDifficulty =
|
||||
generationData.EliminationConfig.MinKills
|
||||
/ generationData.BodyPartsConfig.MinProbability();
|
||||
var maxBodyPartsDifficulty = generationData.EliminationConfig.MinKills / generationData.BodyPartsConfig.MinProbability();
|
||||
|
||||
var maxKillDifficulty = generationData.EliminationConfig.MaxKills;
|
||||
var targetPool = questTypePool.Pool.Elimination;
|
||||
|
||||
// Get a random bot type to eliminate
|
||||
var (botTypeToEliminate, targetsConfig) = GetBotTypeToEliminate(
|
||||
generationData,
|
||||
questTypePool
|
||||
);
|
||||
var (botTypeToEliminate, targetsConfig) = GetBotTypeToEliminate(generationData, questTypePool);
|
||||
if (botTypeToEliminate is null || targetsConfig is null)
|
||||
{
|
||||
logger.Warning(localisationService.GetText("repeatable-no-bot-types-remain"));
|
||||
@@ -143,30 +135,18 @@ public class EliminationQuestGenerator(
|
||||
// Try and get a target location pool for this bot type
|
||||
if (!targetPool.Targets.TryGetValue(botTypeToEliminate, out var targetLocationPool))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-unable-get-target-pool", botTypeToEliminate)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable-get-target-pool", botTypeToEliminate));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try and get a location key for this quest
|
||||
if (
|
||||
!TryGetLocationKey(
|
||||
generationData,
|
||||
targetPool,
|
||||
botTypeToEliminate,
|
||||
targetLocationPool.Locations,
|
||||
out var locationKey
|
||||
) || locationKey is null
|
||||
!TryGetLocationKey(generationData, targetPool, botTypeToEliminate, targetLocationPool.Locations, out var locationKey)
|
||||
|| locationKey is null
|
||||
)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable-get-location-key",
|
||||
botTypeToEliminate
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable-get-location-key", botTypeToEliminate));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -174,9 +154,7 @@ public class EliminationQuestGenerator(
|
||||
// Generate a body part, make sure we ref the body part difficulty so it can be adjusted
|
||||
var bodyPartsToClient = new List<string>();
|
||||
var bodyPartDifficulty = 0d;
|
||||
var generateBodyParts = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.BodyPartChance
|
||||
);
|
||||
var generateBodyParts = randomUtil.GetChance100(generationData.EliminationConfig.BodyPartChance);
|
||||
if (generateBodyParts)
|
||||
{
|
||||
// draw the target body part and calculate the difficulty factor
|
||||
@@ -184,12 +162,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Draw a distance condition
|
||||
var isDistanceRequirementAllowed = IsDistanceRequirementAllowed(
|
||||
generationData,
|
||||
botTypeToEliminate,
|
||||
locationKey,
|
||||
targetsConfig
|
||||
);
|
||||
var isDistanceRequirementAllowed = IsDistanceRequirementAllowed(generationData, botTypeToEliminate, locationKey, targetsConfig);
|
||||
|
||||
int? distance = null;
|
||||
var distanceDifficulty = 0;
|
||||
@@ -204,9 +177,7 @@ public class EliminationQuestGenerator(
|
||||
|
||||
string? allowedWeaponsCategory = null;
|
||||
|
||||
var generateWeaponCategoryRequirement = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.WeaponCategoryRequirementChance
|
||||
);
|
||||
var generateWeaponCategoryRequirement = randomUtil.GetChance100(generationData.EliminationConfig.WeaponCategoryRequirementChance);
|
||||
|
||||
// Generate a weapon category requirement
|
||||
if (generateWeaponCategoryRequirement)
|
||||
@@ -217,9 +188,7 @@ public class EliminationQuestGenerator(
|
||||
// Only allow a specific weapon requirement if a weapon category was not chosen
|
||||
MongoId? allowedWeapon = null;
|
||||
|
||||
var generateWeaponRequirement = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.WeaponRequirementChance
|
||||
);
|
||||
var generateWeaponRequirement = randomUtil.GetChance100(generationData.EliminationConfig.WeaponRequirementChance);
|
||||
|
||||
// Generate a weapon requirement
|
||||
if (!generateWeaponCategoryRequirement && generateWeaponRequirement)
|
||||
@@ -228,11 +197,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Draw how many npm kills are required
|
||||
var desiredKillCount = GetEliminationKillCount(
|
||||
botTypeToEliminate,
|
||||
targetsConfig,
|
||||
generationData.EliminationConfig
|
||||
);
|
||||
var desiredKillCount = GetEliminationKillCount(botTypeToEliminate, targetsConfig, generationData.EliminationConfig);
|
||||
|
||||
var killDifficulty = desiredKillCount;
|
||||
|
||||
@@ -261,12 +226,7 @@ public class EliminationQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-quest_generation_failed_no_template",
|
||||
"elimination"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-quest_generation_failed_no_template", "elimination"));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -285,19 +245,11 @@ public class EliminationQuestGenerator(
|
||||
if (locationKey != "any")
|
||||
{
|
||||
var locationId = Enum.Parse<ELocationName>(locationKey);
|
||||
availableForFinishCondition.Counter.Conditions.Add(
|
||||
GenerateEliminationLocation(generationData.LocationsConfig[locationId])
|
||||
);
|
||||
availableForFinishCondition.Counter.Conditions.Add(GenerateEliminationLocation(generationData.LocationsConfig[locationId]));
|
||||
}
|
||||
|
||||
availableForFinishCondition.Counter.Conditions.Add(
|
||||
GenerateEliminationCondition(
|
||||
botTypeToEliminate,
|
||||
bodyPartsToClient,
|
||||
distance,
|
||||
allowedWeapon,
|
||||
allowedWeaponsCategory
|
||||
)
|
||||
GenerateEliminationCondition(botTypeToEliminate, bodyPartsToClient, distance, allowedWeapon, allowedWeaponsCategory)
|
||||
);
|
||||
availableForFinishCondition.Value = desiredKillCount;
|
||||
availableForFinishCondition.Id = new MongoId();
|
||||
@@ -316,15 +268,9 @@ public class EliminationQuestGenerator(
|
||||
return quest;
|
||||
}
|
||||
|
||||
protected EliminationQuestGenerationData? GetGenerationData(
|
||||
RepeatableQuestConfig repeatableConfig,
|
||||
int pmcLevel
|
||||
)
|
||||
protected EliminationQuestGenerationData? GetGenerationData(RepeatableQuestConfig repeatableConfig, int pmcLevel)
|
||||
{
|
||||
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(
|
||||
pmcLevel,
|
||||
repeatableConfig
|
||||
);
|
||||
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
|
||||
|
||||
if (eliminationConfig is null)
|
||||
{
|
||||
@@ -334,22 +280,13 @@ public class EliminationQuestGenerator(
|
||||
|
||||
var locationsConfig = repeatableConfig.Locations;
|
||||
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
cloner,
|
||||
eliminationConfig.BodyParts
|
||||
);
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(cloner, eliminationConfig.Targets);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(cloner, eliminationConfig.BodyParts);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponCategoryRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(cloner, eliminationConfig.WeaponRequirements);
|
||||
|
||||
return new EliminationQuestGenerationData(
|
||||
eliminationConfig,
|
||||
@@ -374,9 +311,7 @@ public class EliminationQuestGenerator(
|
||||
{
|
||||
var targetPool = questTypePool.Pool.Elimination;
|
||||
|
||||
var targetsConfig = generationData.TargetsConfig.Filter(x =>
|
||||
targetPool.Targets.ContainsKey(x.Key)
|
||||
);
|
||||
var targetsConfig = generationData.TargetsConfig.Filter(x => targetPool.Targets.ContainsKey(x.Key));
|
||||
|
||||
if (targetsConfig.Count != 0 && !targetsConfig.All(x => x.Data?.IsBoss ?? false))
|
||||
{
|
||||
@@ -407,9 +342,7 @@ public class EliminationQuestGenerator(
|
||||
out string? locationKey
|
||||
)
|
||||
{
|
||||
var useSpecificLocation = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.SpecificLocationChance
|
||||
);
|
||||
var useSpecificLocation = randomUtil.GetChance100(generationData.EliminationConfig.SpecificLocationChance);
|
||||
|
||||
switch (useSpecificLocation)
|
||||
{
|
||||
@@ -432,11 +365,7 @@ public class EliminationQuestGenerator(
|
||||
if (locations.Count == 0)
|
||||
{
|
||||
// Never should reach this if everything works out
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"quest-repeatable_elimination_generation_failed_please_report"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("quest-repeatable_elimination_generation_failed_please_report"));
|
||||
|
||||
locationKey = null;
|
||||
return false;
|
||||
@@ -459,9 +388,7 @@ public class EliminationQuestGenerator(
|
||||
var tmpKey = locationKey;
|
||||
|
||||
// Filter locations bot can be killed on to just those not chosen by key
|
||||
possibleLocationPool.Locations = possibleLocationPool
|
||||
.Locations?.Where(location => location != tmpKey)
|
||||
.ToList();
|
||||
possibleLocationPool.Locations = possibleLocationPool.Locations?.Where(location => location != tmpKey).ToList();
|
||||
|
||||
// None left after filtering
|
||||
if (possibleLocationPool.Locations?.Count is null or 0)
|
||||
@@ -480,10 +407,7 @@ public class EliminationQuestGenerator(
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <param name="bodyPartDifficulty">BodyPartDifficulty to modify based on selection</param>
|
||||
/// <returns>List of selected body parts</returns>
|
||||
protected List<string> GenerateBodyParts(
|
||||
EliminationQuestGenerationData generationData,
|
||||
ref double bodyPartDifficulty
|
||||
)
|
||||
protected List<string> GenerateBodyParts(EliminationQuestGenerationData generationData, ref double bodyPartDifficulty)
|
||||
{
|
||||
// if we add a bodyPart condition, we draw randomly one or two parts
|
||||
// each bodyPart of the BODYPARTS ProbabilityObjectArray includes the string(s)
|
||||
@@ -533,9 +457,7 @@ public class EliminationQuestGenerator(
|
||||
)
|
||||
{
|
||||
// This location is can be chosen for a distance requirement
|
||||
var whitelisted = !generationData.EliminationConfig.DistLocationBlacklist.Contains(
|
||||
locationKey
|
||||
);
|
||||
var whitelisted = !generationData.EliminationConfig.DistLocationBlacklist.Contains(locationKey);
|
||||
|
||||
// We're not whitelisted, exit early to avoid doing a roll for no reason
|
||||
if (!whitelisted)
|
||||
@@ -544,9 +466,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Are we allowed a distance condition by chance?
|
||||
var isAllowedByChance = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.DistanceProbability
|
||||
);
|
||||
var isAllowedByChance = randomUtil.GetChance100(generationData.EliminationConfig.DistanceProbability);
|
||||
|
||||
// Not allowed by chance, return early.
|
||||
// We now just assume we rolled this condition and don't take it into account anymore.
|
||||
@@ -567,25 +487,15 @@ public class EliminationQuestGenerator(
|
||||
.GetDictionary()
|
||||
.Select(x => x.Value)
|
||||
.Where(location => location.Base?.Id != null)
|
||||
.Select(location => new
|
||||
{
|
||||
location.Base.Id,
|
||||
BossSpawn = location.Base.BossLocationSpawn,
|
||||
});
|
||||
.Select(location => new { location.Base.Id, BossSpawn = location.Base.BossLocationSpawn });
|
||||
|
||||
// filter for the current boss to spawn on map
|
||||
var thisBossSpawns = bossSpawns
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
BossSpawn = x.BossSpawn.Where(e => e.BossName == botTypeToEliminate),
|
||||
})
|
||||
.Select(x => new { x.Id, BossSpawn = x.BossSpawn.Where(e => e.BossName == botTypeToEliminate) })
|
||||
.Where(x => x.BossSpawn.Any());
|
||||
|
||||
// remove blacklisted locations
|
||||
var allowedSpawns = thisBossSpawns.Where(x =>
|
||||
!generationData.EliminationConfig.DistLocationBlacklist.Contains(x.Id)
|
||||
);
|
||||
var allowedSpawns = thisBossSpawns.Where(x => !generationData.EliminationConfig.DistLocationBlacklist.Contains(x.Id));
|
||||
|
||||
// if the boss spawns on non-blacklisted locations and the current location is allowed,
|
||||
// we can generate a distance kill requirement
|
||||
@@ -603,19 +513,13 @@ public class EliminationQuestGenerator(
|
||||
var distance = (int)
|
||||
Math.Floor(
|
||||
Math.Abs(randomUtil.Random.NextDouble() - randomUtil.Random.NextDouble())
|
||||
* (
|
||||
1
|
||||
+ generationData.EliminationConfig.MaxDistance
|
||||
- generationData.EliminationConfig.MinDistance
|
||||
)
|
||||
* (1 + generationData.EliminationConfig.MaxDistance - generationData.EliminationConfig.MinDistance)
|
||||
+ generationData.EliminationConfig.MinDistance
|
||||
);
|
||||
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5d)) * 5;
|
||||
|
||||
var distanceDifficulty = (int)(
|
||||
MaxDistDifficulty * distance / generationData.EliminationConfig.MaxDistance
|
||||
);
|
||||
var distanceDifficulty = (int)(MaxDistDifficulty * distance / generationData.EliminationConfig.MaxDistance);
|
||||
|
||||
return (distance, distanceDifficulty);
|
||||
}
|
||||
@@ -626,10 +530,7 @@ public class EliminationQuestGenerator(
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <param name="distance">Distance to generate it for, pass null if not required</param>
|
||||
/// <returns>Weapon requirement category selected</returns>
|
||||
protected string? GenerateWeaponCategoryRequirement(
|
||||
EliminationQuestGenerationData generationData,
|
||||
int? distance
|
||||
)
|
||||
protected string? GenerateWeaponCategoryRequirement(EliminationQuestGenerationData generationData, int? distance)
|
||||
{
|
||||
switch (distance)
|
||||
{
|
||||
@@ -639,9 +540,7 @@ public class EliminationQuestGenerator(
|
||||
HashSet<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
|
||||
|
||||
// Filter out close range weapons from long distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category => weaponTypeBlacklist.Contains(category.Key));
|
||||
break;
|
||||
}
|
||||
// Filter out long range weapons from close distance requirement
|
||||
@@ -650,9 +549,7 @@ public class EliminationQuestGenerator(
|
||||
HashSet<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
|
||||
|
||||
// Filter out far range weapons from close distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category => weaponTypeBlacklist.Contains(category.Key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -669,20 +566,14 @@ public class EliminationQuestGenerator(
|
||||
/// </summary>
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <returns>Weapon to use</returns>
|
||||
protected MongoId GenerateSpecificWeaponRequirement(
|
||||
EliminationQuestGenerationData generationData
|
||||
)
|
||||
protected MongoId GenerateSpecificWeaponRequirement(EliminationQuestGenerationData generationData)
|
||||
{
|
||||
var weaponRequirement = generationData.WeaponRequirementConfig.Draw(1, false);
|
||||
var specificAllowedWeaponCategory = generationData.WeaponRequirementConfig.Data(
|
||||
weaponRequirement[0]
|
||||
);
|
||||
var specificAllowedWeaponCategory = generationData.WeaponRequirementConfig.Data(weaponRequirement[0]);
|
||||
|
||||
if (specificAllowedWeaponCategory?[0] is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-elimination-specific-weapon-null")
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-elimination-specific-weapon-null"));
|
||||
return MongoId.Empty();
|
||||
}
|
||||
|
||||
@@ -706,30 +597,18 @@ public class EliminationQuestGenerator(
|
||||
{
|
||||
if (targetsConfig.Data(targetKey)?.IsBoss ?? false)
|
||||
{
|
||||
return randomUtil.RandInt(
|
||||
eliminationConfig.MinBossKills,
|
||||
eliminationConfig.MaxBossKills + 1
|
||||
);
|
||||
return randomUtil.RandInt(eliminationConfig.MinBossKills, eliminationConfig.MaxBossKills + 1);
|
||||
}
|
||||
|
||||
if (targetsConfig.Data(targetKey)?.IsPmc ?? false)
|
||||
{
|
||||
return randomUtil.RandInt(
|
||||
eliminationConfig.MinPmcKills,
|
||||
eliminationConfig.MaxPmcKills + 1
|
||||
);
|
||||
return randomUtil.RandInt(eliminationConfig.MinPmcKills, eliminationConfig.MaxPmcKills + 1);
|
||||
}
|
||||
|
||||
return randomUtil.RandInt(eliminationConfig.MinKills, eliminationConfig.MaxKills + 1);
|
||||
}
|
||||
|
||||
protected double DifficultyWeighing(
|
||||
double target,
|
||||
double bodyPart,
|
||||
int dist,
|
||||
int kill,
|
||||
int weaponRequirement
|
||||
)
|
||||
protected double DifficultyWeighing(double target, double bodyPart, int dist, int kill, int weaponRequirement)
|
||||
{
|
||||
return Math.Sqrt(Math.Sqrt(target) + bodyPart + dist + weaponRequirement) * kill;
|
||||
}
|
||||
@@ -796,11 +675,7 @@ public class EliminationQuestGenerator(
|
||||
// Don't allow distance + melee requirement
|
||||
if (distance is not null && allowedWeaponCategory != "5b5f7a0886f77409407a7f96")
|
||||
{
|
||||
killConditionProps.Distance = new CounterConditionDistance
|
||||
{
|
||||
CompareMethod = ">=",
|
||||
Value = distance.Value,
|
||||
};
|
||||
killConditionProps.Distance = new CounterConditionDistance { CompareMethod = ">=", Value = distance.Value };
|
||||
}
|
||||
|
||||
// Has specific weapon requirement
|
||||
|
||||
+19
-94
@@ -54,20 +54,9 @@ public class ExplorationQuestGenerator(
|
||||
var explorationConfig = repeatableConfig.QuestConfig.Exploration;
|
||||
|
||||
// Try and get a location to generate for
|
||||
if (
|
||||
!TryGetLocationInfo(
|
||||
repeatableConfig,
|
||||
explorationConfig,
|
||||
questTypePool,
|
||||
out var locationInfo
|
||||
) || locationInfo is null
|
||||
)
|
||||
if (!TryGetLocationInfo(repeatableConfig, explorationConfig, questTypePool, out var locationInfo) || locationInfo is null)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_location_found_for_exploration_quest_generation"
|
||||
)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("repeatable-no_location_found_for_exploration_quest_generation"));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -82,12 +71,7 @@ public class ExplorationQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-quest_generation_failed_no_template",
|
||||
"exploration"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-quest_generation_failed_no_template", "exploration"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,45 +79,24 @@ public class ExplorationQuestGenerator(
|
||||
if (!TryGenerateAvailableForFinish(quest, locationInfo))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-available_for_finish_condition_failed_to_generate",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
localisationService.GetText("repeatable-available_for_finish_condition_failed_to_generate", locationInfo.LocationName)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we require a specific extract requirement, generate it
|
||||
if (
|
||||
locationInfo.RequiresSpecificExtract
|
||||
&& !TryGenerateSpecificExtractRequirement(quest, repeatableConfig, locationInfo)
|
||||
)
|
||||
if (locationInfo.RequiresSpecificExtract && !TryGenerateSpecificExtractRequirement(quest, repeatableConfig, locationInfo))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-specific_extract_condition_failed_to_generate",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
localisationService.GetText("repeatable-specific_extract_condition_failed_to_generate", locationInfo.LocationName)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Difficulty for exploration goes from 1 extract to maxExtracts
|
||||
// Difficulty for reward goes from 0.2...1 -> map
|
||||
var difficulty = mathUtil.MapToRange(
|
||||
locationInfo.NumOfExtractsRequired,
|
||||
1,
|
||||
explorationConfig.MaximumExtracts,
|
||||
0.2,
|
||||
1
|
||||
);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
difficulty,
|
||||
traderId,
|
||||
repeatableConfig,
|
||||
explorationConfig
|
||||
);
|
||||
var difficulty = mathUtil.MapToRange(locationInfo.NumOfExtractsRequired, 1, explorationConfig.MaximumExtracts, 0.2, 1);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(pmcLevel, difficulty, traderId, repeatableConfig, explorationConfig);
|
||||
|
||||
return quest;
|
||||
}
|
||||
@@ -168,18 +131,11 @@ public class ExplorationQuestGenerator(
|
||||
// Make the location info object
|
||||
var locationTarget = pool.Pool!.Exploration!.Locations![locationKey];
|
||||
|
||||
var requiresSpecificExtract = randomUtil.GetChance100(
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.Chance
|
||||
);
|
||||
var requiresSpecificExtract = randomUtil.GetChance100(repeatableConfig.QuestConfig.Exploration.SpecificExits.Chance);
|
||||
|
||||
var numExtracts = GetNumberOfExits(explorationConfig, requiresSpecificExtract);
|
||||
|
||||
locationInfo = new LocationInfo(
|
||||
locationKey,
|
||||
locationTarget.ToList(),
|
||||
requiresSpecificExtract,
|
||||
numExtracts
|
||||
);
|
||||
locationInfo = new LocationInfo(locationKey, locationTarget.ToList(), requiresSpecificExtract, numExtracts);
|
||||
|
||||
// Remove the location from the available pool
|
||||
pool.Pool.Exploration.Locations.Remove(locationKey);
|
||||
@@ -196,9 +152,7 @@ public class ExplorationQuestGenerator(
|
||||
protected int GetNumberOfExits(Exploration config, bool requiresSpecificExtract)
|
||||
{
|
||||
// Different max extract count when specific extract needed
|
||||
var exitTimesMax = requiresSpecificExtract
|
||||
? config.MaximumExtractsWithSpecificExit
|
||||
: config.MaximumExtracts + 1;
|
||||
var exitTimesMax = requiresSpecificExtract ? config.MaximumExtractsWithSpecificExit : config.MaximumExtracts + 1;
|
||||
|
||||
return randomUtil.RandInt(1, exitTimesMax);
|
||||
}
|
||||
@@ -209,10 +163,7 @@ public class ExplorationQuestGenerator(
|
||||
/// <param name="locationKey">Map id (e.g. factory4_day)</param>
|
||||
/// <param name="playerGroup">Pmc/Scav</param>
|
||||
/// <returns>List of Exit objects</returns>
|
||||
protected IEnumerable<Exit>? GetLocationExitsForSide(
|
||||
string locationKey,
|
||||
PlayerGroup playerGroup
|
||||
)
|
||||
protected IEnumerable<Exit>? GetLocationExitsForSide(string locationKey, PlayerGroup playerGroup)
|
||||
{
|
||||
var mapExtracts = databaseService.GetLocation(locationKey.ToLowerInvariant())?.AllExtracts;
|
||||
|
||||
@@ -235,18 +186,11 @@ public class ExplorationQuestGenerator(
|
||||
}
|
||||
|
||||
// Lookup the location
|
||||
var location = repeatableQuestHelper.GetQuestLocationByMapId(
|
||||
locationInfo.LocationName.ToString()
|
||||
);
|
||||
var location = repeatableQuestHelper.GetQuestLocationByMapId(locationInfo.LocationName.ToString());
|
||||
|
||||
if (location is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_to_find_location_id_for_location_name",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_to_find_location_id_for_location_name", locationInfo.LocationName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -267,11 +211,7 @@ public class ExplorationQuestGenerator(
|
||||
};
|
||||
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Id = new MongoId();
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Conditions =
|
||||
[
|
||||
exitStatusCondition,
|
||||
locationCondition,
|
||||
];
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Conditions = [exitStatusCondition, locationCondition];
|
||||
quest.Conditions.AvailableForFinish[0].Value = locationInfo.NumOfExtractsRequired;
|
||||
quest.Conditions.AvailableForFinish[0].Id = new MongoId();
|
||||
|
||||
@@ -294,19 +234,11 @@ public class ExplorationQuestGenerator(
|
||||
)
|
||||
{
|
||||
// Fetch extracts for the requested side
|
||||
var mapExits = GetLocationExitsForSide(
|
||||
locationInfo.LocationName.ToString(),
|
||||
repeatableConfig.Side
|
||||
);
|
||||
var mapExits = GetLocationExitsForSide(locationInfo.LocationName.ToString(), repeatableConfig.Side);
|
||||
|
||||
if (mapExits is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_to_find_exits_for_location",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_to_find_exits_for_location", locationInfo.LocationName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -315,19 +247,12 @@ public class ExplorationQuestGenerator(
|
||||
|
||||
// Exclude exits with a requirement to leave (e.g. car extracts)
|
||||
var possibleExits = exitPool.Where(exit =>
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains(
|
||||
"PassageRequirement"
|
||||
)
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains("PassageRequirement")
|
||||
);
|
||||
|
||||
if (!possibleExits.Any())
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_choose_exit_pool_empty",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_choose_exit_pool_empty", locationInfo.LocationName));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+5
-19
@@ -42,9 +42,7 @@ public class PickupQuestGenerator(
|
||||
sessionId
|
||||
);
|
||||
|
||||
var itemTypeToFetchWithCount = randomUtil.GetArrayValue(
|
||||
pickupConfig.ItemTypeToFetchWithMaxCount
|
||||
);
|
||||
var itemTypeToFetchWithCount = randomUtil.GetArrayValue(pickupConfig.ItemTypeToFetchWithMaxCount);
|
||||
|
||||
var itemCountToFetch = randomUtil.RandInt(
|
||||
itemTypeToFetchWithCount.MinimumPickupCount.Value,
|
||||
@@ -54,34 +52,22 @@ public class PickupQuestGenerator(
|
||||
// var locationKey: string = this.randomUtil.drawRandomFromDict(questTypePool.pool.Pickup.locations)[0];
|
||||
// var locationTarget = questTypePool.pool.Pickup.locations[locationKey];
|
||||
|
||||
var findCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x =>
|
||||
x.ConditionType == "FindItem"
|
||||
);
|
||||
var findCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x => x.ConditionType == "FindItem");
|
||||
findCondition.Target = new ListOrT<string>([itemTypeToFetchWithCount.ItemType], null);
|
||||
findCondition.Value = itemCountToFetch;
|
||||
|
||||
var counterCreatorCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x =>
|
||||
x.ConditionType == "CounterCreator"
|
||||
);
|
||||
var counterCreatorCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x => x.ConditionType == "CounterCreator");
|
||||
// var locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
|
||||
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
|
||||
|
||||
var equipmentCondition = counterCreatorCondition.Counter.Conditions.FirstOrDefault(x =>
|
||||
x.ConditionType == "Equipment"
|
||||
);
|
||||
var equipmentCondition = counterCreatorCondition.Counter.Conditions.FirstOrDefault(x => x.ConditionType == "Equipment");
|
||||
equipmentCondition.EquipmentInclusive =
|
||||
[
|
||||
[itemTypeToFetchWithCount.ItemType],
|
||||
];
|
||||
|
||||
// Add rewards
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
1,
|
||||
traderId,
|
||||
repeatableConfig,
|
||||
pickupConfig
|
||||
);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig);
|
||||
|
||||
return quest;
|
||||
}
|
||||
|
||||
+36
-158
@@ -68,11 +68,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
)
|
||||
{
|
||||
// Get vars to configure rewards with
|
||||
var rewardParams = GetQuestRewardValues(
|
||||
repeatableConfig.RewardScaling,
|
||||
difficulty,
|
||||
pmcLevel
|
||||
);
|
||||
var rewardParams = GetQuestRewardValues(repeatableConfig.RewardScaling, difficulty, pmcLevel);
|
||||
|
||||
// Get budget to spend on item rewards (copy of raw roubles given)
|
||||
var itemRewardBudget = rewardParams.RewardRoubles;
|
||||
@@ -112,13 +108,12 @@ public class RepeatableQuestRewardGenerator(
|
||||
rewardIndex++;
|
||||
|
||||
// Add GP coin reward
|
||||
rewards["Success"]
|
||||
.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
|
||||
rewards["Success"].Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
|
||||
rewardIndex++;
|
||||
|
||||
// Add preset weapon to reward if checks pass
|
||||
var traderWhitelistDetails = repeatableConfig.TraderWhitelist.FirstOrDefault(
|
||||
traderWhitelist => traderWhitelist.TraderId == traderId
|
||||
var traderWhitelistDetails = repeatableConfig.TraderWhitelist.FirstOrDefault(traderWhitelist =>
|
||||
traderWhitelist.TraderId == traderId
|
||||
);
|
||||
|
||||
if (traderWhitelistDetails is null)
|
||||
@@ -127,10 +122,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
traderWhitelistDetails.RewardCanBeWeapon
|
||||
&& randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent)
|
||||
)
|
||||
if (traderWhitelistDetails.RewardCanBeWeapon && randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent))
|
||||
{
|
||||
var chosenWeapon = GetRandomWeaponPresetWithinBudget(itemRewardBudget, rewardIndex);
|
||||
if (chosenWeapon is not null)
|
||||
@@ -143,17 +135,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
var inBudgetRewardItemPool = ChooseRewardItemsWithinBudget(
|
||||
repeatableConfig,
|
||||
itemRewardBudget,
|
||||
traderId
|
||||
);
|
||||
var inBudgetRewardItemPool = ChooseRewardItemsWithinBudget(repeatableConfig, itemRewardBudget, traderId);
|
||||
if (rewardTplBlacklist is not null)
|
||||
{
|
||||
// Filter reward pool of items from blacklist, only use if there's at least 1 item remaining
|
||||
var filteredRewardItemPool = inBudgetRewardItemPool
|
||||
.Where(item => !rewardTplBlacklist.Contains(item.Id))
|
||||
.ToList();
|
||||
var filteredRewardItemPool = inBudgetRewardItemPool.Where(item => !rewardTplBlacklist.Contains(item.Id)).ToList();
|
||||
|
||||
if (filteredRewardItemPool.Count != 0)
|
||||
{
|
||||
@@ -180,8 +166,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
// Add item rewards
|
||||
foreach (var itemReward in itemsToReward)
|
||||
{
|
||||
rewards["Success"]
|
||||
.Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex));
|
||||
rewards["Success"].Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex));
|
||||
rewardIndex++;
|
||||
}
|
||||
}
|
||||
@@ -205,9 +190,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Adding: {rewardParams.RewardReputation} {traderId.ToString()} trader reputation reward"
|
||||
);
|
||||
logger.Debug($"Adding: {rewardParams.RewardReputation} {traderId.ToString()} trader reputation reward");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,20 +213,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}"
|
||||
);
|
||||
logger.Debug($"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}");
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
protected QuestRewardValues GetQuestRewardValues(
|
||||
RewardScaling rewardScaling,
|
||||
double effectiveDifficulty,
|
||||
int pmcLevel
|
||||
)
|
||||
protected QuestRewardValues GetQuestRewardValues(RewardScaling rewardScaling, double effectiveDifficulty, int pmcLevel)
|
||||
{
|
||||
// difficulty could go from 0.2 ... -> for lowest difficulty receive 0.2*nominal reward
|
||||
var levelsConfig = rewardScaling.Levels;
|
||||
@@ -260,35 +237,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
SkillPointReward = mathUtil.Interp1(pmcLevel, levelsConfig, skillPointRewardConfig),
|
||||
SkillRewardChance = mathUtil.Interp1(pmcLevel, levelsConfig, skillRewardChanceConfig),
|
||||
RewardReputation = GetRewardRep(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
reputationConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardReputation = GetRewardRep(effectiveDifficulty, pmcLevel, levelsConfig, reputationConfig, rewardSpreadConfig),
|
||||
RewardNumItems = GetRewardNumItems(pmcLevel, levelsConfig, itemsConfig),
|
||||
RewardRoubles = GetRewardRoubles(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
roublesConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
GpCoinRewardCount = GetGpCoinRewardCount(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
gpCoinConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardXP = GetRewardXp(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
xpConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardRoubles = GetRewardRoubles(effectiveDifficulty, pmcLevel, levelsConfig, roublesConfig, rewardSpreadConfig),
|
||||
GpCoinRewardCount = GetGpCoinRewardCount(effectiveDifficulty, pmcLevel, levelsConfig, gpCoinConfig, rewardSpreadConfig),
|
||||
RewardXP = GetRewardXp(effectiveDifficulty, pmcLevel, levelsConfig, xpConfig, rewardSpreadConfig),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -336,11 +289,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return Math.Round(multiplier) / 100;
|
||||
}
|
||||
|
||||
protected int GetRewardNumItems(
|
||||
int pmcLevel,
|
||||
List<double> levelsConfig,
|
||||
List<double> itemsConfig
|
||||
)
|
||||
protected int GetRewardNumItems(int pmcLevel, List<double> levelsConfig, List<double> itemsConfig)
|
||||
{
|
||||
var interpolatedNumItems = mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig);
|
||||
|
||||
@@ -395,21 +344,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
if (itemHelper.IsOfBaseclass(chosenItemFromPool.Id, BaseClasses.AMMO))
|
||||
{
|
||||
// Don't reward ammo that stacks to less than what's allowed in config
|
||||
if (
|
||||
chosenItemFromPool.Properties.StackMaxSize
|
||||
< repeatableConfig.RewardAmmoStackMinSize
|
||||
)
|
||||
if (chosenItemFromPool.Properties.StackMaxSize < repeatableConfig.RewardAmmoStackMinSize)
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Choose the smallest value between budget, fitting size and stack max
|
||||
rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget(
|
||||
chosenItemFromPool,
|
||||
itemRewardBudget,
|
||||
maxItemCount
|
||||
);
|
||||
rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget(chosenItemFromPool, itemRewardBudget, maxItemCount);
|
||||
}
|
||||
|
||||
// 25% chance to double, triple or quadruple reward stack
|
||||
@@ -425,9 +367,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
var calculatedItemRewardBudget = itemRewardBudget - rewardItemStackCount * itemCost;
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}"
|
||||
);
|
||||
logger.Debug($"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}");
|
||||
}
|
||||
|
||||
// If we still have budget narrow down possible items
|
||||
@@ -444,9 +384,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining"
|
||||
);
|
||||
logger.Debug($"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,11 +404,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> Rouble budget </param>
|
||||
/// <param name="rewardNumItems"> Count of rewarded items </param>
|
||||
/// <returns> Count that fits budget (min 1) </returns>
|
||||
protected int CalculateAmmoStackSizeThatFitsBudget(
|
||||
TemplateItem itemSelected,
|
||||
double roublesBudget,
|
||||
int rewardNumItems
|
||||
)
|
||||
protected int CalculateAmmoStackSizeThatFitsBudget(TemplateItem itemSelected, double roublesBudget, int rewardNumItems)
|
||||
{
|
||||
// Calculate budget per reward item
|
||||
var stackRoubleBudget = roublesBudget / rewardNumItems;
|
||||
@@ -487,18 +421,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
return (int)Math.Clamp(stackSizeThatFitsBudget, 1, stackMaxCount);
|
||||
}
|
||||
|
||||
protected bool CanIncreaseRewardItemStackSize(
|
||||
TemplateItem item,
|
||||
int maxRoublePriceToStack,
|
||||
int randomChanceToPass = 100
|
||||
)
|
||||
protected bool CanIncreaseRewardItemStackSize(TemplateItem item, int maxRoublePriceToStack, int randomChanceToPass = 100)
|
||||
{
|
||||
var isEligibleForStackSizeIncrease =
|
||||
presetHelper.GetDefaultPresetOrItemPrice(item.Id) < maxRoublePriceToStack
|
||||
&& !itemHelper.IsOfBaseclasses(
|
||||
item.Id,
|
||||
[BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO]
|
||||
)
|
||||
&& !itemHelper.IsOfBaseclasses(item.Id, [BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO])
|
||||
&& !itemHelper.ItemRequiresSoftInserts(item.Id);
|
||||
|
||||
return isEligibleForStackSizeIncrease && randomUtil.GetChance100(randomChanceToPass);
|
||||
@@ -548,25 +475,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
var rewardableItemPool = GetRewardableItems(repeatableConfig, traderId);
|
||||
var minPrice = Math.Min(25000, 0.5 * roublesBudget.Value);
|
||||
|
||||
var rewardableItemPoolWithinBudget = FilterRewardPoolWithinBudget(
|
||||
rewardableItemPool,
|
||||
roublesBudget.Value,
|
||||
minPrice
|
||||
);
|
||||
var rewardableItemPoolWithinBudget = FilterRewardPoolWithinBudget(rewardableItemPool, roublesBudget.Value, minPrice);
|
||||
|
||||
if (rewardableItemPoolWithinBudget.Count == 0)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_reward_item_found_in_price_range",
|
||||
new { minPrice, roublesBudget }
|
||||
)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("repeatable-no_reward_item_found_in_price_range", new { minPrice, roublesBudget }));
|
||||
|
||||
// In case we don't find any items in the price range
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool
|
||||
.Where(x => itemHelper.GetItemPrice(x.Id) < roublesBudget)
|
||||
.ToList();
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool.Where(x => itemHelper.GetItemPrice(x.Id) < roublesBudget).ToList();
|
||||
}
|
||||
|
||||
return rewardableItemPoolWithinBudget;
|
||||
@@ -579,11 +495,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> The budget remaining for rewards </param>
|
||||
/// <param name="minPrice"> The minimum priced item to include </param>
|
||||
/// <returns> List of Items </returns>
|
||||
protected List<TemplateItem> FilterRewardPoolWithinBudget(
|
||||
List<TemplateItem> rewardItems,
|
||||
double roublesBudget,
|
||||
double minPrice
|
||||
)
|
||||
protected List<TemplateItem> FilterRewardPoolWithinBudget(List<TemplateItem> rewardItems, double roublesBudget, double minPrice)
|
||||
{
|
||||
return rewardItems
|
||||
.Where(item =>
|
||||
@@ -600,17 +512,10 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> Budget in roubles </param>
|
||||
/// <param name="rewardIndex"> Index of the reward </param>
|
||||
/// <returns> Dictionary of the reward and it's price, can return null. </returns>
|
||||
protected KeyValuePair<Reward, double>? GetRandomWeaponPresetWithinBudget(
|
||||
double roublesBudget,
|
||||
int rewardIndex
|
||||
)
|
||||
protected KeyValuePair<Reward, double>? GetRandomWeaponPresetWithinBudget(double roublesBudget, int rewardIndex)
|
||||
{
|
||||
// Add a random default preset weapon as reward
|
||||
var defaultPresetPool = new ExhaustableArray<Preset>(
|
||||
presetHelper.GetDefaultWeaponPresets().Values.ToList(),
|
||||
randomUtil,
|
||||
cloner
|
||||
);
|
||||
var defaultPresetPool = new ExhaustableArray<Preset>(presetHelper.GetDefaultWeaponPresets().Values.ToList(), randomUtil, cloner);
|
||||
|
||||
while (defaultPresetPool.HasValues())
|
||||
{
|
||||
@@ -631,12 +536,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
var chosenPreset = cloner.Clone(randomPreset);
|
||||
|
||||
return new KeyValuePair<Reward, double>(
|
||||
GeneratePresetReward(
|
||||
chosenPreset.Encyclopedia.Value,
|
||||
1,
|
||||
rewardIndex,
|
||||
chosenPreset.Items
|
||||
),
|
||||
GeneratePresetReward(chosenPreset.Encyclopedia.Value, 1, rewardIndex, chosenPreset.Items),
|
||||
presetPrice
|
||||
);
|
||||
}
|
||||
@@ -654,13 +554,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="preset"> Optional list of preset items </param>
|
||||
/// <param name="foundInRaid"> If generated Item is found in raid, default True </param>
|
||||
/// <returns> Object of "Reward"-item-type </returns>
|
||||
protected Reward GeneratePresetReward(
|
||||
MongoId tpl,
|
||||
int count,
|
||||
int index,
|
||||
List<Item>? preset,
|
||||
bool foundInRaid = true
|
||||
)
|
||||
protected Reward GeneratePresetReward(MongoId tpl, int count, int index, List<Item>? preset, bool foundInRaid = true)
|
||||
{
|
||||
var id = new MongoId();
|
||||
var questRewardItem = new Reward
|
||||
@@ -704,12 +598,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="index"> All rewards will be appended to a list, for unknown reasons the client wants the index</param>
|
||||
/// <param name="foundInRaid"> If generated Item is found in raid, default True </param>
|
||||
/// <returns> Object of "Reward"-item-type </returns>
|
||||
protected Reward GenerateItemReward(
|
||||
MongoId tpl,
|
||||
double count,
|
||||
int index,
|
||||
bool foundInRaid = true
|
||||
)
|
||||
protected Reward GenerateItemReward(MongoId tpl, double count, int index, bool foundInRaid = true)
|
||||
{
|
||||
var id = new MongoId();
|
||||
var questRewardItem = new Reward
|
||||
@@ -742,16 +631,10 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
// Determine currency based on trader
|
||||
// PK and Fence use Euros, everyone else is Roubles
|
||||
var currency =
|
||||
traderId == Traders.PEACEKEEPER || traderId == Traders.FENCE
|
||||
? Money.EUROS
|
||||
: Money.ROUBLES;
|
||||
var currency = traderId == Traders.PEACEKEEPER || traderId == Traders.FENCE ? Money.EUROS : Money.ROUBLES;
|
||||
|
||||
// Convert reward amount to Euros if necessary
|
||||
var rewardAmountToGivePlayer =
|
||||
currency == Money.EUROS
|
||||
? handbookHelper.FromRUB(rewardRoubles, Money.EUROS)
|
||||
: rewardRoubles;
|
||||
var rewardAmountToGivePlayer = currency == Money.EUROS ? handbookHelper.FromRUB(rewardRoubles, Money.EUROS) : rewardRoubles;
|
||||
|
||||
// Get chosen currency + amount and return
|
||||
return GenerateItemReward(currency, rewardAmountToGivePlayer, rewardIndex, false);
|
||||
@@ -767,10 +650,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="repeatableQuestConfig"> Config </param>
|
||||
/// <param name="traderId"> ID of trader who will give reward to player </param>
|
||||
/// <returns> List of rewardable items [[_tpl, itemTemplate],...] </returns>
|
||||
public List<TemplateItem> GetRewardableItems(
|
||||
RepeatableQuestConfig repeatableQuestConfig,
|
||||
MongoId traderId
|
||||
)
|
||||
public List<TemplateItem> GetRewardableItems(RepeatableQuestConfig repeatableQuestConfig, MongoId traderId)
|
||||
{
|
||||
// Get an array of seasonal items that should not be shown right now as seasonal event is not active
|
||||
var seasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
@@ -793,9 +673,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return false;
|
||||
}
|
||||
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader =>
|
||||
trader.TraderId == traderId
|
||||
);
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader => trader.TraderId == traderId);
|
||||
|
||||
return IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
|
||||
@@ -45,51 +45,25 @@ public class ScavCaseRewardGenerator(
|
||||
CacheDbItems();
|
||||
|
||||
// Get scavcase details from hideout/scavcase.json
|
||||
var scavCaseDetails = databaseService
|
||||
.GetHideout()
|
||||
.Production.ScavRecipes.FirstOrDefault(r => r.Id == recipeId);
|
||||
var scavCaseDetails = databaseService.GetHideout().Production.ScavRecipes.FirstOrDefault(r => r.Id == recipeId);
|
||||
var rewardItemCounts = GetScavCaseRewardCountsAndPrices(scavCaseDetails);
|
||||
|
||||
// Get items that fit the price criteria as set by the scavCase config
|
||||
var commonPricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Common);
|
||||
var rarePricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Rare);
|
||||
var superRarePricedItems = GetFilteredItemsByPrice(
|
||||
_dbItemsCache,
|
||||
rewardItemCounts.Superrare
|
||||
);
|
||||
var superRarePricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Superrare);
|
||||
|
||||
// Get randomly picked items from each item collection, the count range of which is defined in hideout/scavcase.json
|
||||
var randomlyPickedCommonRewards = PickRandomRewards(
|
||||
commonPricedItems,
|
||||
rewardItemCounts.Common,
|
||||
RewardRarity.Common
|
||||
);
|
||||
var randomlyPickedCommonRewards = PickRandomRewards(commonPricedItems, rewardItemCounts.Common, RewardRarity.Common);
|
||||
|
||||
var randomlyPickedRareRewards = PickRandomRewards(
|
||||
rarePricedItems,
|
||||
rewardItemCounts.Rare,
|
||||
RewardRarity.Rare
|
||||
);
|
||||
var randomlyPickedRareRewards = PickRandomRewards(rarePricedItems, rewardItemCounts.Rare, RewardRarity.Rare);
|
||||
|
||||
var randomlyPickedSuperRareRewards = PickRandomRewards(
|
||||
superRarePricedItems,
|
||||
rewardItemCounts.Superrare,
|
||||
RewardRarity.SuperRare
|
||||
);
|
||||
var randomlyPickedSuperRareRewards = PickRandomRewards(superRarePricedItems, rewardItemCounts.Superrare, RewardRarity.SuperRare);
|
||||
|
||||
// Add randomised stack sizes to ammo and money rewards
|
||||
var commonRewards = RandomiseContainerItemRewards(
|
||||
randomlyPickedCommonRewards,
|
||||
RewardRarity.Common
|
||||
);
|
||||
var rareRewards = RandomiseContainerItemRewards(
|
||||
randomlyPickedRareRewards,
|
||||
RewardRarity.Rare
|
||||
);
|
||||
var superRareRewards = RandomiseContainerItemRewards(
|
||||
randomlyPickedSuperRareRewards,
|
||||
RewardRarity.SuperRare
|
||||
);
|
||||
var commonRewards = RandomiseContainerItemRewards(randomlyPickedCommonRewards, RewardRarity.Common);
|
||||
var rareRewards = RandomiseContainerItemRewards(randomlyPickedRareRewards, RewardRarity.Rare);
|
||||
var superRareRewards = RandomiseContainerItemRewards(randomlyPickedSuperRareRewards, RewardRarity.SuperRare);
|
||||
|
||||
var result = commonRewards.Concat(rareRewards).Concat(superRareRewards);
|
||||
|
||||
@@ -142,21 +116,13 @@ public class ScavCaseRewardGenerator(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!_scavCaseConfig.AllowBossItemsAsRewards
|
||||
&& itemFilterService.IsBossItem(item.Id)
|
||||
)
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && itemFilterService.IsBossItem(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip item if parent id is blacklisted
|
||||
if (
|
||||
itemHelper.IsOfBaseclasses(
|
||||
item.Id,
|
||||
_scavCaseConfig.RewardItemParentBlacklist
|
||||
)
|
||||
)
|
||||
if (itemHelper.IsOfBaseclasses(item.Id, _scavCaseConfig.RewardItemParentBlacklist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -195,10 +161,7 @@ public class ScavCaseRewardGenerator(
|
||||
}
|
||||
|
||||
// Skip item if item id is on blacklist
|
||||
if (
|
||||
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id)
|
||||
|| itemFilterService.IsItemBlacklisted(item.Id)
|
||||
)
|
||||
if (_scavCaseConfig.RewardItemBlacklist.Contains(item.Id) || itemFilterService.IsItemBlacklisted(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -209,10 +172,7 @@ public class ScavCaseRewardGenerator(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!_scavCaseConfig.AllowBossItemsAsRewards
|
||||
&& itemFilterService.IsBossItem(item.Id)
|
||||
)
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && itemFilterService.IsBossItem(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -242,11 +202,7 @@ public class ScavCaseRewardGenerator(
|
||||
/// <param name="itemFilters">how the rewards should be filtered down (by item count)</param>
|
||||
/// <param name="rarity">Rarity of reward</param>
|
||||
/// <returns></returns>
|
||||
protected List<TemplateItem> PickRandomRewards(
|
||||
List<TemplateItem> items,
|
||||
RewardCountAndPriceDetails itemFilters,
|
||||
string rarity
|
||||
)
|
||||
protected List<TemplateItem> PickRandomRewards(List<TemplateItem> items, RewardCountAndPriceDetails itemFilters, string rarity)
|
||||
{
|
||||
List<TemplateItem> result = [];
|
||||
|
||||
@@ -327,10 +283,7 @@ public class ScavCaseRewardGenerator(
|
||||
// Is ammo handbook price between desired range
|
||||
var handbookPrice = ragfairPriceService.GetStaticPriceForItem(ammo.Id);
|
||||
if (
|
||||
_scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub.TryGetValue(
|
||||
rarity,
|
||||
out var matchingAmmoRewardForRarity
|
||||
)
|
||||
_scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub.TryGetValue(rarity, out var matchingAmmoRewardForRarity)
|
||||
&& handbookPrice >= matchingAmmoRewardForRarity.Min
|
||||
&& handbookPrice <= matchingAmmoRewardForRarity.Max
|
||||
)
|
||||
@@ -344,9 +297,7 @@ public class ScavCaseRewardGenerator(
|
||||
if (!possibleAmmoPool.Any())
|
||||
{
|
||||
// Filtered pool is empty
|
||||
logger.Warning(
|
||||
localisationService.GetText("scavcase-no_cartridges_found_matching_price")
|
||||
);
|
||||
logger.Warning(localisationService.GetText("scavcase-no_cartridges_found_matching_price"));
|
||||
}
|
||||
|
||||
// Get a random ammo and return it
|
||||
@@ -360,10 +311,7 @@ public class ScavCaseRewardGenerator(
|
||||
/// <param name="rewardItems">items to convert</param>
|
||||
/// <param name="rarity">The rarity desired ammo reward is for</param>
|
||||
/// <returns>Product array</returns>
|
||||
protected List<List<Item>> RandomiseContainerItemRewards(
|
||||
IEnumerable<TemplateItem> rewardItems,
|
||||
string rarity
|
||||
)
|
||||
protected List<List<Item>> RandomiseContainerItemRewards(IEnumerable<TemplateItem> rewardItems, string rarity)
|
||||
{
|
||||
// Each array is an item + children
|
||||
List<List<Item>> result = [];
|
||||
@@ -393,9 +341,7 @@ public class ScavCaseRewardGenerator(
|
||||
var preset = presetHelper.GetDefaultPreset(rewardItemDb.Id);
|
||||
if (preset is null)
|
||||
{
|
||||
logger.Warning(
|
||||
$"No preset for item: {rewardItemDb.Id} {rewardItemDb.Name}, skipping"
|
||||
);
|
||||
logger.Warning($"No preset for item: {rewardItemDb.Id} {rewardItemDb.Name}, skipping");
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -406,14 +352,9 @@ public class ScavCaseRewardGenerator(
|
||||
|
||||
resultItem = presetAndMods;
|
||||
}
|
||||
else if (
|
||||
itemHelper.IsOfBaseclasses(rewardItemDb.Id, [BaseClasses.AMMO, BaseClasses.MONEY])
|
||||
)
|
||||
else if (itemHelper.IsOfBaseclasses(rewardItemDb.Id, [BaseClasses.AMMO, BaseClasses.MONEY]))
|
||||
{
|
||||
rootItem.Upd = new Upd
|
||||
{
|
||||
StackObjectsCount = GetRandomAmountRewardForScavCase(rewardItemDb, rarity),
|
||||
};
|
||||
rootItem.Upd = new Upd { StackObjectsCount = GetRandomAmountRewardForScavCase(rewardItemDb, rarity) };
|
||||
}
|
||||
|
||||
result.Add(resultItem);
|
||||
@@ -427,19 +368,13 @@ public class ScavCaseRewardGenerator(
|
||||
/// <param name="dbItems">all items from the items.json</param>
|
||||
/// <param name="itemFilters">controls how the dbItems will be filtered and returned (handbook price)</param>
|
||||
/// <returns>filtered dbItems array</returns>
|
||||
protected List<TemplateItem> GetFilteredItemsByPrice(
|
||||
List<TemplateItem> dbItems,
|
||||
RewardCountAndPriceDetails itemFilters
|
||||
)
|
||||
protected List<TemplateItem> GetFilteredItemsByPrice(List<TemplateItem> dbItems, RewardCountAndPriceDetails itemFilters)
|
||||
{
|
||||
return dbItems
|
||||
.Where(item =>
|
||||
{
|
||||
var handbookPrice = ragfairPriceService.GetStaticPriceForItem(item.Id);
|
||||
if (
|
||||
handbookPrice >= itemFilters.MinPriceRub
|
||||
&& handbookPrice <= itemFilters.MaxPriceRub
|
||||
)
|
||||
if (handbookPrice >= itemFilters.MinPriceRub && handbookPrice <= itemFilters.MaxPriceRub)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -454,9 +389,7 @@ public class ScavCaseRewardGenerator(
|
||||
/// </summary>
|
||||
/// <param name="scavCaseDetails">production.json/scavRecipes object</param>
|
||||
/// <returns>ScavCaseRewardCountsAndPrices object</returns>
|
||||
protected ScavCaseRewardCountsAndPrices GetScavCaseRewardCountsAndPrices(
|
||||
ScavRecipe scavCaseDetails
|
||||
)
|
||||
protected ScavCaseRewardCountsAndPrices GetScavCaseRewardCountsAndPrices(ScavRecipe scavCaseDetails)
|
||||
{
|
||||
return new ScavCaseRewardCountsAndPrices
|
||||
{
|
||||
@@ -516,10 +449,7 @@ public class ScavCaseRewardGenerator(
|
||||
/// <returns>value to set stack count to</returns>
|
||||
protected int GetRandomisedAmmoRewardStackSize(TemplateItem itemToCalculate)
|
||||
{
|
||||
return randomUtil.GetInt(
|
||||
_scavCaseConfig.AmmoRewards.MinStackSize,
|
||||
itemToCalculate.Properties.StackMaxSize ?? 0
|
||||
);
|
||||
return randomUtil.GetInt(_scavCaseConfig.AmmoRewards.MinStackSize, itemToCalculate.Properties.StackMaxSize ?? 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+3
-4
@@ -6,10 +6,9 @@ using SPTarkov.Server.Core.Utils;
|
||||
namespace SPTarkov.Server.Core.Generators.WeaponGen.Implementations;
|
||||
|
||||
[Injectable]
|
||||
public class BarrelInventoryMagGen(
|
||||
RandomUtil randomUtil,
|
||||
BotWeaponGeneratorHelper botWeaponGeneratorHelper
|
||||
) : InventoryMagGen, IInventoryMagGen
|
||||
public class BarrelInventoryMagGen(RandomUtil randomUtil, BotWeaponGeneratorHelper botWeaponGeneratorHelper)
|
||||
: InventoryMagGen,
|
||||
IInventoryMagGen
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
|
||||
+6
-22
@@ -44,9 +44,7 @@ public class ExternalInventoryMagGen(
|
||||
var defaultMagazineTpl = weapon.GetWeaponsDefaultMagazineTpl();
|
||||
var isShotgun = itemHelper.IsOfBaseclass(weapon.Id, BaseClasses.SHOTGUN);
|
||||
|
||||
var randomizedMagazineCount = botWeaponGeneratorHelper.GetRandomizedMagazineCount(
|
||||
inventoryMagGen.GetMagCount()
|
||||
);
|
||||
var randomizedMagazineCount = botWeaponGeneratorHelper.GetRandomizedMagazineCount(inventoryMagGen.GetMagCount());
|
||||
for (var i = 0; i < randomizedMagazineCount; i++)
|
||||
{
|
||||
var magazineWithAmmo = botWeaponGeneratorHelper.CreateMagazineWithAmmo(
|
||||
@@ -77,9 +75,7 @@ public class ExternalInventoryMagGen(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Failed {fitAttempts} times to add magazine {magazineTpl} to bot inventory, stopping"
|
||||
);
|
||||
logger.Debug($"Failed {fitAttempts} times to add magazine {magazineTpl} to bot inventory, stopping");
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -115,12 +111,7 @@ public class ExternalInventoryMagGen(
|
||||
magTemplate = itemHelper.GetItem(magazineTpl).Value;
|
||||
if (magTemplate is null)
|
||||
{
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"bot-unable_to_find_default_magazine_item",
|
||||
magazineTpl
|
||||
)
|
||||
);
|
||||
logger.Error(serverLocalisationService.GetText("bot-unable_to_find_default_magazine_item", magazineTpl));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -174,15 +165,10 @@ public class ExternalInventoryMagGen(
|
||||
/// <param name="weaponTpl"> Weapon to get mag for </param>
|
||||
/// <param name="magazineBlacklist"> Blacklisted magazines </param>
|
||||
/// <returns> Item of chosen magazine </returns>
|
||||
public TemplateItem? GetRandomExternalMagazineForInternalMagazineGun(
|
||||
MongoId weaponTpl,
|
||||
HashSet<MongoId> magazineBlacklist
|
||||
)
|
||||
public TemplateItem? GetRandomExternalMagazineForInternalMagazineGun(MongoId weaponTpl, HashSet<MongoId> magazineBlacklist)
|
||||
{
|
||||
// The mag Slot data for the weapon
|
||||
var magSlot = itemHelper
|
||||
.GetItem(weaponTpl)
|
||||
.Value.Properties.Slots.FirstOrDefault(x => x.Name == "mod_magazine");
|
||||
var magSlot = itemHelper.GetItem(weaponTpl).Value.Properties.Slots.FirstOrDefault(x => x.Name == "mod_magazine");
|
||||
if (magSlot is null)
|
||||
{
|
||||
return null;
|
||||
@@ -199,9 +185,7 @@ public class ExternalInventoryMagGen(
|
||||
}
|
||||
|
||||
// Non-internal magazines that fit into the weapon
|
||||
var externalMagazineOnlyPool = magazinePool.Where(x =>
|
||||
x.Properties.ReloadMagType != ReloadMode.InternalMagazine
|
||||
);
|
||||
var externalMagazineOnlyPool = magazinePool.Where(x => x.Properties.ReloadMagType != ReloadMode.InternalMagazine);
|
||||
if (externalMagazineOnlyPool is null || !externalMagazineOnlyPool.Any())
|
||||
{
|
||||
return null;
|
||||
|
||||
+2
-5
@@ -5,9 +5,7 @@ using SPTarkov.Server.Core.Models.Enums;
|
||||
namespace SPTarkov.Server.Core.Generators.WeaponGen.Implementations;
|
||||
|
||||
[Injectable]
|
||||
public class InternalMagazineInventoryMagGen(BotWeaponGeneratorHelper botWeaponGeneratorHelper)
|
||||
: InventoryMagGen,
|
||||
IInventoryMagGen
|
||||
public class InternalMagazineInventoryMagGen(BotWeaponGeneratorHelper botWeaponGeneratorHelper) : InventoryMagGen, IInventoryMagGen
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
@@ -16,8 +14,7 @@ public class InternalMagazineInventoryMagGen(BotWeaponGeneratorHelper botWeaponG
|
||||
|
||||
public bool CanHandleInventoryMagGen(InventoryMagGen inventoryMagGen)
|
||||
{
|
||||
return inventoryMagGen.GetMagazineTemplate().Properties.ReloadMagType
|
||||
== ReloadMode.InternalMagazine;
|
||||
return inventoryMagGen.GetMagazineTemplate().Properties.ReloadMagType == ReloadMode.InternalMagazine;
|
||||
}
|
||||
|
||||
public void Process(InventoryMagGen inventoryMagGen)
|
||||
|
||||
+1
-3
@@ -5,9 +5,7 @@ using SPTarkov.Server.Core.Models.Enums;
|
||||
namespace SPTarkov.Server.Core.Generators.WeaponGen.Implementations;
|
||||
|
||||
[Injectable]
|
||||
public class UbglExternalMagGen(BotWeaponGeneratorHelper botWeaponGeneratorHelper)
|
||||
: InventoryMagGen,
|
||||
IInventoryMagGen
|
||||
public class UbglExternalMagGen(BotWeaponGeneratorHelper botWeaponGeneratorHelper) : InventoryMagGen, IInventoryMagGen
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
|
||||
@@ -69,19 +69,9 @@ public class WeatherGenerator(
|
||||
Pressure = GetRandomDouble(weatherValues.Pressure.Min, weatherValues.Pressure.Max),
|
||||
Temperature = 0,
|
||||
Fog = GetWeightedFog(weatherValues),
|
||||
RainIntensity =
|
||||
rain > 1
|
||||
? GetRandomDouble(
|
||||
weatherValues.RainIntensity.Min,
|
||||
weatherValues.RainIntensity.Max
|
||||
)
|
||||
: 0,
|
||||
RainIntensity = rain > 1 ? GetRandomDouble(weatherValues.RainIntensity.Min, weatherValues.RainIntensity.Max) : 0,
|
||||
Rain = rain,
|
||||
WindGustiness = GetRandomDouble(
|
||||
weatherValues.WindGustiness.Min,
|
||||
weatherValues.WindGustiness.Max,
|
||||
2
|
||||
),
|
||||
WindGustiness = GetRandomDouble(weatherValues.WindGustiness.Min, weatherValues.WindGustiness.Max, 2),
|
||||
WindDirection = GetWeightedWindDirection(weatherValues),
|
||||
WindSpeed = GetWeightedWindSpeed(weatherValues),
|
||||
Cloud = clouds,
|
||||
@@ -100,10 +90,7 @@ public class WeatherGenerator(
|
||||
|
||||
protected SeasonalValues GetWeatherValuesBySeason(Season currentSeason)
|
||||
{
|
||||
var result = _weatherConfig.Weather.SeasonValues.TryGetValue(
|
||||
currentSeason.ToString(),
|
||||
out var value
|
||||
);
|
||||
var result = _weatherConfig.Weather.SeasonValues.TryGetValue(currentSeason.ToString(), out var value);
|
||||
if (!result)
|
||||
{
|
||||
return _weatherConfig.Weather.SeasonValues["default"];
|
||||
@@ -122,9 +109,7 @@ public class WeatherGenerator(
|
||||
{
|
||||
// Convert timestamp to date so we can get current hour and check if its day or night
|
||||
var currentRaidTime = new DateTime(inRaidTimestamp);
|
||||
var minMax = weatherHelper.IsHourAtNightTime(currentRaidTime.Hour)
|
||||
? weather.Temp.Night
|
||||
: weather.Temp.Day;
|
||||
var minMax = weatherHelper.IsHourAtNightTime(currentRaidTime.Hour) ? weather.Temp.Night : weather.Temp.Day;
|
||||
|
||||
return Math.Round(randomUtil.GetDouble(minMax.Min, minMax.Max), 2);
|
||||
}
|
||||
@@ -136,15 +121,9 @@ public class WeatherGenerator(
|
||||
/// <param name="timestamp"> Optional, timestamp used </param>
|
||||
protected void SetCurrentDateTime(Weather weather, long? timestamp = null)
|
||||
{
|
||||
var inRaidTime = timestamp is null
|
||||
? weatherHelper.GetInRaidTime()
|
||||
: weatherHelper.GetInRaidTime(timestamp.Value);
|
||||
var inRaidTime = timestamp is null ? weatherHelper.GetInRaidTime() : weatherHelper.GetInRaidTime(timestamp.Value);
|
||||
var normalTime = inRaidTime.GetBsgFormattedWeatherTime();
|
||||
var formattedDate = (
|
||||
timestamp.HasValue
|
||||
? timeUtil.GetDateTimeFromTimeStamp(timestamp.Value)
|
||||
: DateTime.UtcNow
|
||||
).FormatToBsgDate();
|
||||
var formattedDate = (timestamp.HasValue ? timeUtil.GetDateTimeFromTimeStamp(timestamp.Value) : DateTime.UtcNow).FormatToBsgDate();
|
||||
var datetimeBsgFormat = $"{formattedDate} {normalTime}";
|
||||
|
||||
weather.Timestamp = timestamp ?? timeUtil.GetTimeStamp(); // matches weather.date
|
||||
@@ -155,23 +134,17 @@ public class WeatherGenerator(
|
||||
|
||||
protected WindDirection GetWeightedWindDirection(SeasonalValues weather)
|
||||
{
|
||||
return weightedRandomHelper
|
||||
.WeightedRandom(weather.WindDirection.Values, weather.WindDirection.Weights)
|
||||
.Item;
|
||||
return weightedRandomHelper.WeightedRandom(weather.WindDirection.Values, weather.WindDirection.Weights).Item;
|
||||
}
|
||||
|
||||
protected double GetWeightedClouds(SeasonalValues weather)
|
||||
{
|
||||
return weightedRandomHelper
|
||||
.WeightedRandom(weather.Clouds.Values, weather.Clouds.Weights)
|
||||
.Item;
|
||||
return weightedRandomHelper.WeightedRandom(weather.Clouds.Values, weather.Clouds.Weights).Item;
|
||||
}
|
||||
|
||||
protected double GetWeightedWindSpeed(SeasonalValues weather)
|
||||
{
|
||||
return weightedRandomHelper
|
||||
.WeightedRandom(weather.WindSpeed.Values, weather.WindSpeed.Weights)
|
||||
.Item;
|
||||
return weightedRandomHelper.WeightedRandom(weather.WindSpeed.Values, weather.WindSpeed.Weights).Item;
|
||||
}
|
||||
|
||||
protected double GetWeightedFog(SeasonalValues weather)
|
||||
|
||||
Reference in New Issue
Block a user