diff --git a/Core/Callbacks/GameCallbacks.cs b/Core/Callbacks/GameCallbacks.cs
index c5a5a8e6..5a1a5f16 100644
--- a/Core/Callbacks/GameCallbacks.cs
+++ b/Core/Callbacks/GameCallbacks.cs
@@ -67,10 +67,9 @@ public class GameCallbacks : OnLoad
///
public string GameStart(string url, EmptyRequestData info, string sessionID)
{
- var today = _timeUtil.GetDate();
- var startTimestampMS = _timeUtil.GetTimeStamp();
- _gameController.GameStart(url, info, sessionID, startTimestampMS);
- return _httpResponseUtil.GetBody(new GameStartResponse() { UtcTime = startTimestampMS / 1000 });
+ var startTimestampSec = _timeUtil.GetTimeStamp();
+ _gameController.GameStart(url, info, sessionID, startTimestampSec);
+ return _httpResponseUtil.GetBody(new GameStartResponse() { UtcTime = startTimestampSec });
}
///
diff --git a/Core/Generators/BotEquipmentModGenerator.cs b/Core/Generators/BotEquipmentModGenerator.cs
index d14f238c..03cea0e6 100644
--- a/Core/Generators/BotEquipmentModGenerator.cs
+++ b/Core/Generators/BotEquipmentModGenerator.cs
@@ -775,11 +775,11 @@ public class BotEquipmentModGenerator
case "patron_in_weapon":
case "patron_in_weapon_000":
case "patron_in_weapon_001":
- return parentTemplate.Properties.Chambers.FirstOrDefault((chamber) => chamber.Name.Contains(modSlotLower));
+ return parentTemplate?.Properties?.Chambers?.FirstOrDefault((chamber) => chamber.Name.Contains(modSlotLower));
case "cartridges":
- return parentTemplate.Properties.Cartridges.FirstOrDefault((c) => c.Name.ToLower() == modSlotLower);
+ return parentTemplate?.Properties?.Cartridges?.FirstOrDefault((c) => c.Name?.ToLower() == modSlotLower);
default:
- return parentTemplate.Properties.Slots.FirstOrDefault((s) => s.Name.ToLower() == modSlotLower);
+ return parentTemplate?.Properties?.Slots?.FirstOrDefault((s) => s.Name?.ToLower() == modSlotLower);
}
}
diff --git a/Core/Generators/BotWeaponGenerator.cs b/Core/Generators/BotWeaponGenerator.cs
index a2709f82..0a34195e 100644
--- a/Core/Generators/BotWeaponGenerator.cs
+++ b/Core/Generators/BotWeaponGenerator.cs
@@ -224,8 +224,9 @@ public class BotWeaponGenerator
);
}
+ var tempList = _cloner.Clone(weaponWithModsArray.Where((item) => item.SlotId == _modMagazineSlotId));
// Fill existing magazines to full and sync ammo type
- foreach (var magazine in weaponWithModsArray.Where((item) => item.SlotId == _modMagazineSlotId))
+ foreach (var magazine in tempList)
{
FillExistingMagazines(weaponWithModsArray, magazine, ammoTpl);
}
diff --git a/Core/Generators/WeaponGen/Implementations/BarrelInvetoryMagGen.cs b/Core/Generators/WeaponGen/Implementations/BarrelInvetoryMagGen.cs
index 467f451b..0571f149 100644
--- a/Core/Generators/WeaponGen/Implementations/BarrelInvetoryMagGen.cs
+++ b/Core/Generators/WeaponGen/Implementations/BarrelInvetoryMagGen.cs
@@ -1,12 +1,23 @@
using Core.Annotations;
+using Core.Helpers;
+using Core.Utils;
namespace Core.Generators.WeaponGen.Implementations;
[Injectable]
public class BarrelInvetoryMagGen : InventoryMagGen, IInventoryMagGen
{
- public BarrelInvetoryMagGen()
+ private readonly RandomUtil _randomUtil;
+ private readonly BotWeaponGeneratorHelper _botWeaponGeneratorHelper;
+
+ public BarrelInvetoryMagGen
+ (
+ RandomUtil randomUtil,
+ BotWeaponGeneratorHelper botWeaponGeneratorHelper
+ )
{
+ _randomUtil = randomUtil;
+ _botWeaponGeneratorHelper = botWeaponGeneratorHelper;
}
public int GetPriority()
@@ -16,11 +27,31 @@ public class BarrelInvetoryMagGen : InventoryMagGen, IInventoryMagGen
public bool CanHandleInventoryMagGen(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ return inventoryMagGen.GetWeaponTemplate().Properties.ReloadMode == "OnlyBarrel";
}
public void Process(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ // Can't be done by _props.ammoType as grenade launcher shoots grenades with ammoType of "buckshot"
+ double? randomisedAmmoStackSize = null;
+ if (inventoryMagGen.GetAmmoTemplate().Properties.StackMaxRandom == 1)
+ {
+ // doesnt stack
+ randomisedAmmoStackSize = _randomUtil.GetInt(3, 6);
+ }
+ else
+ {
+ randomisedAmmoStackSize = _randomUtil.GetInt(
+ (int)inventoryMagGen.GetAmmoTemplate().Properties.StackMinRandom,
+ (int)inventoryMagGen.GetAmmoTemplate().Properties.StackMaxRandom
+ );
+ }
+
+ _botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
+ inventoryMagGen.GetAmmoTemplate().Id,
+ (int)randomisedAmmoStackSize,
+ inventoryMagGen.GetPmcInventory(),
+ null
+ );
}
}
diff --git a/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs b/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
index d5867ee9..8e78f281 100644
--- a/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
+++ b/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
@@ -1,13 +1,39 @@
using Core.Annotations;
+using Core.Helpers;
using Core.Models.Eft.Common.Tables;
+using Core.Models.Enums;
+using Core.Models.Utils;
+using Core.Services;
+using Core.Utils;
namespace Core.Generators.WeaponGen.Implementations;
[Injectable]
public class ExternalInventoryMagGen : InventoryMagGen, IInventoryMagGen
{
- public ExternalInventoryMagGen()
+ private readonly ISptLogger _logger;
+ private readonly ItemHelper _itemHelper;
+ private readonly LocalisationService _localisationService;
+ private readonly BotWeaponGeneratorHelper _botWeaponGeneratorHelper;
+ private readonly BotGeneratorHelper _botGeneratorHelper;
+ private readonly RandomUtil _randomUtil;
+
+ public ExternalInventoryMagGen
+ (
+ ISptLogger logger,
+ ItemHelper itemHelper,
+ LocalisationService localisationService,
+ BotWeaponGeneratorHelper botWeaponGeneratorHelper,
+ BotGeneratorHelper botGeneratorHelper,
+ RandomUtil randomUtil
+ )
{
+ _logger = logger;
+ _itemHelper = itemHelper;
+ _localisationService = localisationService;
+ _botWeaponGeneratorHelper = botWeaponGeneratorHelper;
+ _botGeneratorHelper = botGeneratorHelper;
+ _randomUtil = randomUtil;
}
public int GetPriority()
@@ -17,16 +43,124 @@ public class ExternalInventoryMagGen : InventoryMagGen, IInventoryMagGen
public bool CanHandleInventoryMagGen(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ return true; // Fallback, if code reaches here it means no other implementation can handle this type of magazine
}
public void Process(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ // Cout of attempts to fit a magazine into bot inventory
+ var fitAttempts = 0;
+
+ // Magazine Db template
+ var magTemplate = inventoryMagGen.GetMagazineTemplate();
+ var magazineTpl = magTemplate.Id;
+ var weapon = inventoryMagGen.GetWeaponTemplate();
+ List attemptedMagBlacklist = [];
+ var defaultMagazineTpl = _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weapon);
+ var randomizedMagazineCount = _botWeaponGeneratorHelper.GetRandomizedMagazineCount(inventoryMagGen.GetMagCount());
+ for (var i = 0; i < randomizedMagazineCount; i++) {
+ var magazineWithAmmo = _botWeaponGeneratorHelper.CreateMagazineWithAmmo(
+ magazineTpl,
+ inventoryMagGen.GetAmmoTemplate().Id,
+ magTemplate
+ );
+
+ var fitsIntoInventory = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
+ [EquipmentSlots.TacticalVest.ToString(), EquipmentSlots.Pockets.ToString()],
+ magazineWithAmmo[0].Id,
+ magazineTpl,
+ magazineWithAmmo,
+ inventoryMagGen.GetPmcInventory()
+ );
+
+ if (fitsIntoInventory == ItemAddedResult.NO_CONTAINERS) {
+ // No containers to fit magazines, stop trying
+ break;
+ }
+
+ // No space for magazine and we haven't reached desired magazine count
+ if (fitsIntoInventory == ItemAddedResult.NO_SPACE && i < randomizedMagazineCount) {
+ // Prevent infinite loop by only allowing 5 attempts at fitting a magazine into inventory
+ if (fitAttempts > 5) {
+ _logger.Debug($"Failed {fitAttempts} times to add magazine {magazineTpl} to bot inventory, stopping");
+
+ break;
+ }
+
+ /* We were unable to fit at least the minimum amount of magazines,
+ * so we fallback to default magazine and try again.
+ * Temporary workaround to Killa spawning with no extra mags if he spawns with a drum mag */
+
+ if (magazineTpl == defaultMagazineTpl) {
+ // We were already on default - stop here to prevent infinite looping
+ break;
+ }
+
+ // Add failed magazine tpl to blacklist
+ attemptedMagBlacklist.Add(magazineTpl);
+
+ // Set chosen magazine tpl to the weapons default magazine tpl and try to fit into inventory next loop
+ magazineTpl = defaultMagazineTpl;
+ magTemplate = _itemHelper.GetItem(magazineTpl).Value;
+ if (magTemplate is null) {
+ _logger.Error(
+ _localisationService.GetText("bot-unable_to_find_default_magazine_item", magazineTpl)
+ );
+
+ break;
+ }
+
+ // Edge case - some weapons (SKS) have an internal magazine as default, choose random non-internal magazine to add to bot instead
+ if (magTemplate.Properties.ReloadMagType == "InternalMagazine") {
+ var result = GetRandomExternalMagazineForInternalMagazineGun(
+ inventoryMagGen.GetWeaponTemplate().Id,
+ attemptedMagBlacklist
+ );
+ if (result?.Id is null) {
+ _logger.Debug($"Unable to add additional magazine into bot inventory for weapon: {weapon.Name}, attempted: {fitAttempts} times");
+
+ break;
+ }
+
+ magazineTpl = result.Id;
+ magTemplate = result;
+ fitAttempts++;
+ }
+
+ // Reduce loop counter by 1 to ensure we get full cout of desired magazines
+ i--;
+ }
+
+ if (fitsIntoInventory == ItemAddedResult.SUCCESS) {
+ // Reset fit counter now it succeeded
+ fitAttempts = 0;
+ }
+ }
}
public TemplateItem? GetRandomExternalMagazineForInternalMagazineGun(string weaponTpl, List magazineBlacklist)
{
- throw new NotImplementedException();
+ // The mag Slot data for the weapon
+ var magSlot = _itemHelper.GetItem(weaponTpl).Value.Properties.Slots.FirstOrDefault((x) => x.Name == "mod_magazine");
+ if (magSlot is null) {
+ return null;
+ }
+
+ // All possible mags that fit into the weapon excluding blacklisted
+ var magazinePool = magSlot.Props.Filters[0].Filter.Where((x) => !magazineBlacklist.Contains(x)).Select(
+ (x) => _itemHelper.GetItem(x).Value
+ );
+ if (magazinePool is null) {
+ return null;
+ }
+
+ // Non-internal magazines that fit into the weapon
+ var externalMagazineOnlyPool = magazinePool.Where((x) => x.Properties.ReloadMagType != "InternalMagazine");
+ if (externalMagazineOnlyPool is null || externalMagazineOnlyPool?.Count() == 0) {
+ return null;
+ }
+
+ // Randomly chosen external magazine
+ return _randomUtil.GetArrayValue(externalMagazineOnlyPool);
}
}
diff --git a/Core/Generators/WeaponGen/Implementations/InternalMagazineInventoryMagGen.cs b/Core/Generators/WeaponGen/Implementations/InternalMagazineInventoryMagGen.cs
index 7d09eeb7..76991706 100644
--- a/Core/Generators/WeaponGen/Implementations/InternalMagazineInventoryMagGen.cs
+++ b/Core/Generators/WeaponGen/Implementations/InternalMagazineInventoryMagGen.cs
@@ -1,12 +1,19 @@
using Core.Annotations;
+using Core.Helpers;
namespace Core.Generators.WeaponGen.Implementations;
[Injectable]
public class InternalMagazineInventoryMagGen : InventoryMagGen, IInventoryMagGen
{
- public InternalMagazineInventoryMagGen()
+ private readonly BotWeaponGeneratorHelper _botWeaponGeneratorHelper;
+
+ public InternalMagazineInventoryMagGen
+ (
+ BotWeaponGeneratorHelper botWeaponGeneratorHelper
+ )
{
+ _botWeaponGeneratorHelper = botWeaponGeneratorHelper;
}
public int GetPriority()
@@ -16,11 +23,20 @@ public class InternalMagazineInventoryMagGen : InventoryMagGen, IInventoryMagGen
public bool CanHandleInventoryMagGen(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ return inventoryMagGen.GetMagazineTemplate().Properties.ReloadMagType == "InternalMagazine";
}
public void Process(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ var bulletCount = _botWeaponGeneratorHelper.GetRandomizedBulletCount(
+ inventoryMagGen.GetMagCount(),
+ inventoryMagGen.GetMagazineTemplate()
+ );
+ _botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
+ inventoryMagGen.GetAmmoTemplate().Id,
+ (int)bulletCount,
+ inventoryMagGen.GetPmcInventory(),
+ null
+ );
}
}
diff --git a/Core/Generators/WeaponGen/Implementations/UbglExternalMagGen.cs b/Core/Generators/WeaponGen/Implementations/UbglExternalMagGen.cs
index 65ff35cc..8d756817 100644
--- a/Core/Generators/WeaponGen/Implementations/UbglExternalMagGen.cs
+++ b/Core/Generators/WeaponGen/Implementations/UbglExternalMagGen.cs
@@ -1,12 +1,20 @@
using Core.Annotations;
+using Core.Helpers;
+using Core.Models.Enums;
namespace Core.Generators.WeaponGen.Implementations;
[Injectable]
public class UbglExternalMagGen : InventoryMagGen, IInventoryMagGen
{
- public UbglExternalMagGen()
+ private readonly BotWeaponGeneratorHelper _botWeaponGeneratorHelper;
+
+ public UbglExternalMagGen
+ (
+ BotWeaponGeneratorHelper botWeaponGeneratorHelper
+ )
{
+ _botWeaponGeneratorHelper = botWeaponGeneratorHelper;
}
public int GetPriority()
@@ -16,11 +24,20 @@ public class UbglExternalMagGen : InventoryMagGen, IInventoryMagGen
public bool CanHandleInventoryMagGen(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ return inventoryMagGen.GetWeaponTemplate().Parent == BaseClasses.UBGL;
}
public void Process(InventoryMagGen inventoryMagGen)
{
- throw new NotImplementedException();
+ var bulletCount = _botWeaponGeneratorHelper.GetRandomizedBulletCount(
+ inventoryMagGen.GetMagCount(),
+ inventoryMagGen.GetMagazineTemplate()
+ );
+ _botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
+ inventoryMagGen.GetAmmoTemplate().Id,
+ (int)bulletCount,
+ inventoryMagGen.GetPmcInventory(),
+ [EquipmentSlots.TacticalVest.ToString()]
+ );
}
}