diff --git a/Core/Controllers/CustomizationController.cs b/Core/Controllers/CustomizationController.cs
index 81dc2b20..87da63fb 100644
--- a/Core/Controllers/CustomizationController.cs
+++ b/Core/Controllers/CustomizationController.cs
@@ -97,8 +97,8 @@ public class CustomizationController
var suitDetails = _databaseService.GetCustomization()[suitId];
_logger.Error(_localisationService.GetText("customisation-item_already_purchased", new
{
- ItemId = suitDetails?.Id,
- ItemName = suitDetails?.Name,
+ itemId = suitDetails?.Id,
+ itemName = suitDetails?.Name,
}));
return output;
diff --git a/Core/Controllers/GameController.cs b/Core/Controllers/GameController.cs
index ec0d79da..3ea2616b 100644
--- a/Core/Controllers/GameController.cs
+++ b/Core/Controllers/GameController.cs
@@ -3,6 +3,7 @@ using Core.Context;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Game;
+using Core.Models.Eft.Match;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
using Core.Models.External;
@@ -12,6 +13,8 @@ using Core.Servers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+using System.Diagnostics;
namespace Core.Controllers;
@@ -222,7 +225,7 @@ public class GameController
var profile = _profileHelper.GetPmcProfile(sessionId);
var gameTime = profile?.Stats?.Eft?.OverallCounters?.Items?.FirstOrDefault(c =>
c.Key.Contains("LifeTime") &&
- c.Key.Contains("Pmc")).Value ?? 0D;
+ c.Key.Contains("Pmc"))?.Value ?? 0D;
var config = new GameConfigResponse
{
@@ -570,7 +573,11 @@ public class GameController
/// Profile to check for dialog in
private void CheckForAndRemoveUndefinedDialogues(SptProfile fullProfile)
{
- throw new NotImplementedException();
+
+ if (fullProfile.DialogueRecords.TryGetValue("undefined", out var undefinedDialog))
+ {
+ fullProfile.DialogueRecords.Remove("undefined");
+ }
}
///
@@ -579,7 +586,12 @@ public class GameController
///
private void LogProfileDetails(SptProfile fullProfile)
{
- throw new NotImplementedException();
+ _logger.Error("NOT IMPLEMENTED LogProfileDetails");
+ _logger.Debug($"Profile made with: ${ fullProfile.SptData.Version}");
+ _logger.Debug($"{fullProfile.SptData.Mods.Count} Mods used");
+ //_logger.Debug($"Server version: ${ ProgramStatics.SPT_VERSION || _coreConfig.SptVersion} ${ ProgramStatics.COMMIT}");
+ //_logger.Debug($"Debug enabled: ${ ProgramStatics.DEBUG}");
+ //_logger.Debug($"Mods enabled: ${ ProgramStatics.MODS}");
}
public void Load()
diff --git a/Core/Generators/BotEquipmentModGenerator.cs b/Core/Generators/BotEquipmentModGenerator.cs
index 423eecb2..d14f238c 100644
--- a/Core/Generators/BotEquipmentModGenerator.cs
+++ b/Core/Generators/BotEquipmentModGenerator.cs
@@ -299,9 +299,9 @@ public class BotEquipmentModGenerator
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot",
new
{
- WeaponName = request.ParentTemplate.Name,
- WeaponId = request.ParentTemplate.Id,
- BotRole = request.BotData.Role,
+ weaponName = request.ParentTemplate.Name,
+ weaponId = request.ParentTemplate.Id,
+ botRole = request.BotData.Role,
}
)
);
@@ -332,10 +332,10 @@ public class BotEquipmentModGenerator
"bot-weapon_missing_mod_slot",
new
{
- ModSlot = modSlot,
- WeaponId = request.ParentTemplate.Id,
- WeaponName = request.ParentTemplate.Name,
- BotRole = request.BotData.Role,
+ modSlot = modSlot,
+ weaponId = request.ParentTemplate.Id,
+ weaponName = request.ParentTemplate.Name,
+ botRole = request.BotData.Role,
}
)
);
@@ -659,9 +659,11 @@ public class BotEquipmentModGenerator
public List SortModKeys(List unsortedSlotKeys, string itemTplWithKeysToSort)
{
// No need to sort with only 1 item in array
- if (unsortedSlotKeys.Count <= 1) {
+ if (unsortedSlotKeys.Count <= 1)
+ {
return unsortedSlotKeys;
}
+
var isMount = _itemHelper.IsOfBaseclass(itemTplWithKeysToSort, BaseClasses.MOUNT);
List sortedKeys = [];
@@ -677,63 +679,78 @@ public class BotEquipmentModGenerator
var modScope000Key = "mod_scope_000";
// Mounts are a special case, they need scopes first before more mounts
- if (isMount) {
- if (unsortedSlotKeys.Contains(modScope000Key)) {
+ if (isMount)
+ {
+ if (unsortedSlotKeys.Contains(modScope000Key))
+ {
sortedKeys.Add(modScope000Key);
unsortedSlotKeys.Remove(modScope000Key);
}
- if (unsortedSlotKeys.Contains(modScopeKey)) {
+ if (unsortedSlotKeys.Contains(modScopeKey))
+ {
sortedKeys.Add(modScopeKey);
unsortedSlotKeys.Remove(modScopeKey);
}
- if (unsortedSlotKeys.Contains(modMountKey)) {
+ if (unsortedSlotKeys.Contains(modMountKey))
+ {
sortedKeys.Add(modMountKey);
unsortedSlotKeys.Remove(modMountKey);
}
- } else {
- if (unsortedSlotKeys.Contains(modHandguardKey)) {
+ }
+ else
+ {
+ if (unsortedSlotKeys.Contains(modHandguardKey))
+ {
sortedKeys.Add(modHandguardKey);
unsortedSlotKeys.Remove(modHandguardKey);
}
- if (unsortedSlotKeys.Contains(modBarrelKey)) {
+ if (unsortedSlotKeys.Contains(modBarrelKey))
+ {
sortedKeys.Add(modBarrelKey);
unsortedSlotKeys.Remove(modBarrelKey);
}
- if (unsortedSlotKeys.Contains(modMount001Key)) {
+ if (unsortedSlotKeys.Contains(modMount001Key))
+ {
sortedKeys.Add(modMount001Key);
unsortedSlotKeys.Remove(modMount001Key);
}
- if (unsortedSlotKeys.Contains(modRecieverKey)) {
+ if (unsortedSlotKeys.Contains(modRecieverKey))
+ {
sortedKeys.Add(modRecieverKey);
unsortedSlotKeys.Remove(modRecieverKey);
}
- if (unsortedSlotKeys.Contains(modPistolGrip)) {
+ if (unsortedSlotKeys.Contains(modPistolGrip))
+ {
sortedKeys.Add(modPistolGrip);
unsortedSlotKeys.Remove(modPistolGrip);
}
- if (unsortedSlotKeys.Contains(modGasBlockKey)) {
+ if (unsortedSlotKeys.Contains(modGasBlockKey))
+ {
sortedKeys.Add(modGasBlockKey);
unsortedSlotKeys.Remove(modGasBlockKey);
}
- if (unsortedSlotKeys.Contains(modStockKey)) {
+ if (unsortedSlotKeys.Contains(modStockKey))
+ {
sortedKeys.Add(modStockKey);
unsortedSlotKeys.Remove(modStockKey);
}
- if (unsortedSlotKeys.Contains(modMountKey)) {
+ if (unsortedSlotKeys.Contains(modMountKey))
+ {
sortedKeys.Add(modMountKey);
unsortedSlotKeys.Remove(modMountKey);
}
- if (unsortedSlotKeys.Contains(modScopeKey)) {
+ if (unsortedSlotKeys.Contains(modScopeKey))
+ {
sortedKeys.Add(modScopeKey);
unsortedSlotKeys.Remove(modScopeKey);
}
@@ -783,8 +800,9 @@ public class BotEquipmentModGenerator
return ModSpawn.SPAWN;
}
- var spawnMod = _probabilityHelper.RollChance(modSpawnChances[modSlotName]);
- if (!spawnMod && (slotRequired.GetValueOrDefault(false) || botEquipConfig.WeaponSlotIdsToMakeRequired.Contains(modSlotName)))
+
+ var spawnMod = _probabilityHelper.RollChance(modSpawnChances.GetValueOrDefault(modSlotName));
+ if (!spawnMod && (slotRequired.GetValueOrDefault(false) || (botEquipConfig.WeaponSlotIdsToMakeRequired?.Contains(modSlotName) ?? false)))
{
// Edge case: Mod is required but spawn chance roll failed, choose default mod spawn for slot
return ModSpawn.DEFAULT_MOD;
@@ -798,48 +816,59 @@ public class BotEquipmentModGenerator
///
/// Data used to choose an appropriate mod with
/// itemHelper.getItem() result
- public KeyValuePair? ChooseModToPutIntoSlot(ModToSpawnRequest request) // TODO: type fuckery: [boolean, ITemplateItem] | undefined
+ public KeyValuePair? ChooseModToPutIntoSlot(ModToSpawnRequest request)
{
/** Slot mod will fill */
var parentSlot = request.ParentTemplate.Properties.Slots?.FirstOrDefault((i) => i.Name == request.ModSlot);
var weaponTemplate = _itemHelper.GetItem(request.Weapon[0].Template).Value;
// It's ammo, use predefined ammo parameter
- if (GetAmmoContainers().Contains(request.ModSlot) && request.ModSlot != "mod_magazine") {
+ if (GetAmmoContainers().Contains(request.ModSlot) && request.ModSlot != "mod_magazine")
+ {
return _itemHelper.GetItem(request.AmmoTpl);
}
// Ensure there's a pool of mods to pick from
var modPool = GetModPoolForSlot(request, weaponTemplate);
- if (modPool is null && !(parentSlot?.Required ?? false)) {
+ if (modPool is null && !(parentSlot?.Required ?? false))
+ {
// Nothing in mod pool + item not required
_logger.Debug($"Mod pool for optional slot: {request.ModSlot} on item: {request.ParentTemplate.Name} was empty, skipping mod");
return null;
}
// Filter out non-whitelisted scopes, use full modpool if filtered pool would have no elements
- if (request.ModSlot.Contains("mod_scope") && request.BotWeaponSightWhitelist is not null) {
+ if (request.ModSlot.Contains("mod_scope") && request.BotWeaponSightWhitelist is not null)
+ {
// scope pool has more than one scope
- if (modPool.Count > 1) {
+ if (modPool.Count > 1)
+ {
modPool = FilterSightsByWeaponType(request.Weapon[0], modPool, request.BotWeaponSightWhitelist);
}
}
- if (request.ModSlot == "mod_gas_block") {
- if (request.WeaponStats.HasOptic ?? false && modPool.Count > 1) {
+ if (request.ModSlot == "mod_gas_block")
+ {
+ 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.Count() > 0) {
+ if (onlyLowProfileGasBlocks.Count() > 0)
+ {
modPool = onlyLowProfileGasBlocks.ToList();
}
- } else if (request.WeaponStats.HasRearIronSight ?? false && modPool.Count() > 1) {
+ }
+ 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)
);
- if (onlyHighProfileGasBlocks.Count() > 0) {
+ if (onlyHighProfileGasBlocks.Count() > 0)
+ {
modPool = onlyHighProfileGasBlocks.ToList();
}
}
@@ -850,7 +879,8 @@ public class BotEquipmentModGenerator
request?.ModSlot == "mod_magazine" &&
(request?.IsRandomisableSlot ?? false) &&
request.RandomisationSettings.MinimumMagazineSize is not null
- ) {
+ )
+ {
modPool = GetFilterdMagazinePoolByCapacity(request, modPool);
}
@@ -863,30 +893,38 @@ public class BotEquipmentModGenerator
request.Weapon,
request.ModSlot
);
- if (chosenModResult.SlotBlocked ?? false && !(parentSlot.Required ?? false)) {
+ if (chosenModResult.SlotBlocked ?? false && !(parentSlot.Required ?? false))
+ {
// Don't bother trying to fit mod, slot is completely blocked
return null;
}
// Log if mod chosen was incompatible
- if (chosenModResult.Incompatible ?? false && !(parentSlot.Required ?? false)) {
+ if (chosenModResult.Incompatible ?? false && !(parentSlot.Required ?? false))
+ {
_logger.Debug(chosenModResult.Reason);
}
// 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.Found = true;
}
// Compatible item not found + not required
- if (!(chosenModResult.Found ?? false) && parentSlot != null && (!parentSlot.Required ?? false)) {
+ if (!(chosenModResult.Found ?? false) && parentSlot != null && (!parentSlot.Required ?? false))
+ {
return null;
}
- if (!(chosenModResult.Found ?? false) && parentSlot != null) {
- if (parentSlot.Required ?? false) {
- _logger.Warning($"Required slot unable to be filled, {request.ModSlot} on {request.ParentTemplate.Name} {request.ParentTemplate.Id} for weapon: {request.Weapon[0].Template}");
+ if (!(chosenModResult.Found ?? false) && parentSlot != null)
+ {
+ if (parentSlot.Required ?? false)
+ {
+ _logger.Warning(
+ $"Required slot unable to be filled, {request.ModSlot} on {request.ParentTemplate.Name} {request.ParentTemplate.Id} for weapon: {request.Weapon[0].Template}"
+ );
}
return null;
@@ -921,7 +959,26 @@ public class BotEquipmentModGenerator
public ChooseRandomCompatibleModResult GetCompatibleWeaponModTplForSlotFromPool(ModToSpawnRequest request, List modPool, Slot parentSlot,
ModSpawn? choiceTypeEnum, List- weapon, string modSlotName)
{
- throw new NotImplementedException();
+ // Filter out incompatible mods from pool
+ var preFilteredModPool = GetFilteredModPool(modPool, request.ConflictingItemTpls);
+ if (preFilteredModPool.Count == 0)
+ {
+ return new()
+ {
+ Incompatible = true,
+ Found = false,
+ Reason = $"Unable to add mod to {choiceTypeEnum.ToString()} slot: {modSlotName}. All: {modPool.Count()} had conflicts"
+ };
+ }
+
+ // Filter mod pool to only items that appear in parents allowed list
+ preFilteredModPool = preFilteredModPool.Where((tpl) => parentSlot.Props.Filters[0].Filter.Contains(tpl)).ToList();
+ if (preFilteredModPool.Count() == 0)
+ {
+ return new() { Incompatible = true, Found = false, Reason = "No mods found in parents allowed list" };
+ }
+
+ return GetCompatibleModFromPool(preFilteredModPool, choiceTypeEnum, weapon);
}
///
@@ -931,9 +988,85 @@ public class BotEquipmentModGenerator
/// How should the slot choice be handled - forced/normal etc
/// Weapon mods at current time
/// IChooseRandomCompatibleModResult
- public ChooseRandomCompatibleModResult GetCompatibleModFromPool(List modPool, ModSpawn modSpawnType, List
- weapon)
+ public ChooseRandomCompatibleModResult GetCompatibleModFromPool(List modPool, ModSpawn? modSpawnType, List
- weapon)
{
- throw new NotImplementedException();
+ // Create exhaustable pool to pick mod item from
+ var exhaustableModPool = CreateExhaustableArray(modPool);
+
+ // Create default response if no compatible item is found below
+ ChooseRandomCompatibleModResult chosenModResult = new()
+ {
+ Incompatible = true,
+ Found = false,
+ Reason = "unknown",
+ };
+
+ // Limit how many attempts to find a compatible mod can occur before giving up
+ var maxBlockedAttempts = Math.Round(modPool.Count() * 0.75); // 75% of pool size
+ var blockedAttemptCount = 0;
+ string chosenTpl = null;
+ while (exhaustableModPool.HasValues())
+ {
+ chosenTpl = exhaustableModPool.GetRandomValue();
+ var pickedItemDetails = _itemHelper.GetItem(chosenTpl);
+ if (!pickedItemDetails.Key)
+ {
+ // Not valid item, try again
+ continue;
+ }
+
+ if (pickedItemDetails.Value.Properties is null)
+ {
+ // no props data, try again
+ continue;
+ }
+
+ // Success - Default wanted + only 1 item in pool
+ if (modSpawnType == ModSpawn.DEFAULT_MOD && modPool.Count() == 1)
+ {
+ chosenModResult.Found = true;
+ chosenModResult.Incompatible = false;
+ chosenModResult.ChosenTemplate = chosenTpl;
+
+ break;
+ }
+
+ // Check if existing weapon mods are incompatible with chosen item
+ var existingItemBlockingChoice = weapon.FirstOrDefault(
+ (item) =>
+ pickedItemDetails.Value.Properties.ConflictingItems?.Contains(item.Template) ?? false
+ );
+ if (existingItemBlockingChoice is not null)
+ {
+ // Give max of x attempts of picking a mod if blocked by another
+ if (blockedAttemptCount > maxBlockedAttempts)
+ {
+ blockedAttemptCount = 0; // reset
+ break;
+ }
+
+ blockedAttemptCount++;
+
+ // Not compatible - Try again
+ continue;
+ }
+
+ // Edge case- Some mod combos will never work, make sure this isnt the case
+ if (WeaponModComboIsIncompatible(weapon, chosenTpl))
+ {
+ chosenModResult.Reason = $"Chosen weapon mod: {chosenTpl} can never be compatible with existing weapon mods";
+ break;
+ }
+
+ // Success
+ chosenModResult.Found = true;
+ chosenModResult.Incompatible = false;
+ chosenModResult.ChosenTemplate = chosenTpl;
+
+ break;
+ }
+
+ return chosenModResult;
}
public ExhaustableArray CreateExhaustableArray(List itemsToAddToArray) // TODO: this wont likely be needed, reimplement for C#
@@ -980,7 +1113,75 @@ public class BotEquipmentModGenerator
public List GetModPoolForDefaultSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
{
- throw new NotImplementedException();
+ var matchingModFromPreset = GetMatchingModFromPreset(request, weaponTemplate);
+ if (matchingModFromPreset is null)
+ {
+ if (request.ItemModPool[request.ModSlot]?.Count > 1)
+ {
+ _logger.Debug($"{request.BotData.Role} No default: {request.ModSlot} mod found for: {weaponTemplate.Name}, using existing pool");
+ }
+
+ // Couldnt find default in globals, use existing mod pool data
+ return request.ItemModPool[request.ModSlot];
+ }
+
+ // Only filter mods down to single default item if it already exists in existing itemModPool, OR the default item has no children
+ // Filtering mod pool to item that wasnt 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[request.ModSlot].Contains(matchingModFromPreset.Template))
+ {
+ // Found mod on preset + it already exists in mod pool
+ return [matchingModFromPreset.Template];
+ }
+
+ // Get an array of items that are allowed in slot from parent item
+ // Check the filter of the slot to ensure a chosen mod fits
+ var parentSlotCompatibleItems = request.ParentTemplate.Properties.Slots?.FirstOrDefault(
+ (slot) => slot.Name.ToLower() == request.ModSlot.ToLower()
+ )
+ ?.Props.Filters[0].Filter;
+
+ // Mod isnt in existing pool, only add if it has no children and exists inside parent filter
+ if (
+ parentSlotCompatibleItems?.Contains(matchingModFromPreset.Template) ??
+ false &&
+ _itemHelper.GetItem(matchingModFromPreset.Template).Value.Properties.Slots?.Count == 0
+ )
+ {
+ // Chosen mod has no conflicts + no children + is in parent compat list
+ if (!request.ConflictingItemTpls.Contains(matchingModFromPreset.Template))
+ {
+ return [matchingModFromPreset.Template];
+ }
+
+ // Above chosen mod had conflicts with existing weapon mods
+ _logger.Debug(
+ $"{request.BotData.Role} Chosen default: {request.ModSlot} mod found for: {weaponTemplate.Name} weapon conflicts with item on weapon, cannot use default"
+ );
+
+ var existingModPool = request.ItemModPool[request.ModSlot];
+ if (existingModPool.Count == 1)
+ {
+ // The only item in pool isn't compatible
+ _logger.Debug(
+ $"{request.BotData.Role} {request.ModSlot} Mod pool for: {weaponTemplate.Name} weapon has only incompatible items, using parent list instead"
+ );
+
+ // Last ditch, use full pool of items minus conflicts
+ var newListOfModsForSlot = parentSlotCompatibleItems.Where((tpl) => !request.ConflictingItemTpls.Contains(tpl));
+ if (newListOfModsForSlot.Count() > 0)
+ {
+ return newListOfModsForSlot.ToList();
+ }
+ }
+
+ // Return full mod pool
+ return request.ItemModPool[request.ModSlot];
+ }
+
+ // Tried everything, return mod pool
+ return request.ItemModPool[request.ModSlot];
}
public Item GetMatchingModFromPreset(ModToSpawnRequest request, TemplateItem weaponTemplate)
@@ -1104,7 +1305,51 @@ public class BotEquipmentModGenerator
public bool IsModValidForSlot(KeyValuePair? modToAdd, Slot slotAddedToTemplate, string modSlot, TemplateItem parentTemplate,
string botRole)
{
- throw new NotImplementedException();
+ var modBeingAddedDbTemplate = modToAdd.Value;
+
+ // Mod lacks db template object
+ if (modBeingAddedDbTemplate.Value is null)
+ {
+ _logger.Error(
+ _localisationService.GetText(
+ "bot-no_item_template_found_when_adding_mod",
+ new
+ {
+ modId = modBeingAddedDbTemplate.Value?.Id ?? "UNKNOWN",
+ modSlot = modSlot,
+ }
+ )
+ );
+ _logger.Debug($"Item -> {parentTemplate?.Id}; Slot -> {modSlot}");
+
+ return false;
+ }
+
+ // Mod has invalid db item
+ if (!modToAdd.HasValue)
+ {
+ // Parent slot must be filled but db object is invalid, show warning and return false
+ if (slotAddedToTemplate.Required ?? false)
+ {
+ _logger.Warning(
+ _localisationService.GetText(
+ "bot-unable_to_add_mod_item_invalid",
+ new
+ {
+ itemName = modBeingAddedDbTemplate.Value?.Name ?? "UNKNOWN",
+ iodSlot = modSlot,
+ parentItemName = parentTemplate.Name,
+ botRole = botRole,
+ }
+ )
+ );
+ }
+
+ return false;
+ }
+
+ // Mod was found in db
+ return true;
}
///
@@ -1117,7 +1362,37 @@ public class BotEquipmentModGenerator
public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate, Dictionary>> modPool,
EquipmentFilterDetails botEquipBlacklist)
{
- throw new NotImplementedException();
+ var desiredSlotObject = modTemplate.Properties.Slots?.FirstOrDefault((slot) => slot.Name.Contains(desiredSlotName));
+ if (desiredSlotObject is not null)
+ {
+ var supportedSubMods = desiredSlotObject.Props.Filters[0].Filter;
+ if (supportedSubMods is not null)
+ {
+ // Filter mods
+ var filteredMods = FilterModsByBlacklist(supportedSubMods, botEquipBlacklist, desiredSlotName);
+ if (filteredMods.Count() == 0)
+ {
+ _logger.Warning(
+ _localisationService.GetText(
+ "bot-unable_to_filter_mods_all_blacklisted",
+ new
+ {
+ slotName = desiredSlotObject.Name,
+ itemName = modTemplate.Name,
+ }
+ )
+ );
+ filteredMods = supportedSubMods;
+ }
+
+ if (modPool[modTemplate.Id] is null)
+ {
+ modPool[modTemplate.Id] = new();
+ }
+
+ modPool[modTemplate.Id][desiredSlotObject.Name] = supportedSubMods;
+ }
+ }
}
///
@@ -1181,7 +1456,75 @@ public class BotEquipmentModGenerator
public void FillCamora(List
- items, Dictionary>> modPool, string cylinderMagParentId,
TemplateItem cylinderMagTemplate)
{
- throw new NotImplementedException();
+ var itemModPool = modPool[cylinderMagTemplate.Id];
+ if (itemModPool is null)
+ {
+ _logger.Warning(
+ _localisationService.GetText(
+ "bot-unable_to_fill_camora_slot_mod_pool_empty",
+ new
+ {
+ weaponId = cylinderMagTemplate.Id,
+ weaponName = cylinderMagTemplate.Name,
+ }
+ )
+ );
+ var camoraSlots = cylinderMagTemplate.Properties.Slots.Where((slot) => slot.Name.StartsWith("camora"));
+
+ // Attempt to generate camora slots for item
+ modPool[cylinderMagTemplate.Id] = new();
+ foreach (var camora in camoraSlots)
+ {
+ modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters[0].Filter;
+ }
+
+ itemModPool = modPool[cylinderMagTemplate.Id];
+ }
+
+ ExhaustableArray exhaustableModPool = null;
+ var modSlot = "cartridges";
+ var camoraFirstSlot = "camora_000";
+ if (itemModPool[modSlot] is not null)
+ {
+ exhaustableModPool = CreateExhaustableArray(itemModPool[modSlot]);
+ }
+ else if (itemModPool[camoraFirstSlot] is not null)
+ {
+ modSlot = camoraFirstSlot;
+ exhaustableModPool = CreateExhaustableArray(MergeCamoraPools(itemModPool));
+ }
+ else
+ {
+ _logger.Error(_localisationService.GetText("bot-missing_cartridge_slot", cylinderMagTemplate.Id));
+
+ return;
+ }
+
+ string modTpl = null;
+ bool found = false;
+ while (exhaustableModPool.HasValues())
+ {
+ modTpl = exhaustableModPool.GetRandomValue();
+ if (!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(items, modTpl, modSlot).Incompatible.GetValueOrDefault(false))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ _logger.Error(_localisationService.GetText("bot-no_compatible_camora_ammo_found", modSlot));
+
+ return;
+ }
+
+ foreach (var slot in cylinderMagTemplate.Properties.Slots)
+ {
+ var modSlotId = slot.Name;
+ var modId = _hashUtil.Generate();
+ items.Add(new() { Id = modId, Template = modTpl, ParentId = cylinderMagParentId, SlotId = modSlotId });
+ }
}
///
@@ -1212,6 +1555,68 @@ public class BotEquipmentModGenerator
/// Array of scope tpls that have been filtered to just ones allowed for that weapon type
public List FilterSightsByWeaponType(Item weapon, List scopes, Dictionary> botWeaponSightWhitelist)
{
- throw new NotImplementedException();
+ var weaponDetails = _itemHelper.GetItem(weapon.Template);
+
+ // Return original scopes array if whitelist not found
+ var whitelistedSightTypes = botWeaponSightWhitelist[weaponDetails.Value.Parent];
+ if (whitelistedSightTypes is null)
+ {
+ _logger.Debug($"Unable to find whitelist for weapon type: {weaponDetails.Value.Parent} {weaponDetails.Value.Name}, skipping sight filtering");
+
+ return scopes;
+ }
+
+ // Filter items that are not directly scopes OR mounts that do not hold the type of scope we allow for this weapon type
+ List filteredScopesAndMods = [];
+ foreach (var item in scopes)
+ {
+ // Mods is a scope, check base class is allowed
+ if (_itemHelper.IsOfBaseclasses(item, whitelistedSightTypes))
+ {
+ // Add mod to allowed list
+ filteredScopesAndMods.Add(item);
+ continue;
+ }
+
+ // Edge case, what if item is a mount for a scope and not directly a scope?
+ // Check item is mount + has child items
+ var itemDetails = _itemHelper.GetItem(item).Value;
+ if (_itemHelper.IsOfBaseclass(item, BaseClasses.MOUNT) && itemDetails.Properties.Slots.Count() > 0)
+ {
+ // 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
+ List filter = ["mod_scope", "mod_scope_000"];
+ 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[0]
+ .Filter.All(
+ (tpl) =>
+ _itemHelper.IsOfBaseclasses(tpl, whitelistedSightTypes) ||
+ _itemHelper.IsOfBaseclass(tpl, BaseClasses.MOUNT)
+ )
+ ) ??
+ false)
+ {
+ // Add mod to allowed list
+ filteredScopesAndMods.Add(item);
+ }
+ }
+ }
+
+ // No mods added to return list after filtering has occurred, send back the original mod list
+ if (filteredScopesAndMods is null || filteredScopesAndMods.Count() == 0)
+ {
+ _logger.Debug($"Scope whitelist too restrictive for: {weapon.Template} {weaponDetails.Value.Name}, skipping filter");
+
+ return scopes;
+ }
+
+ return filteredScopesAndMods;
}
}
diff --git a/Core/Generators/BotLootGenerator.cs b/Core/Generators/BotLootGenerator.cs
index 759f992f..470dea35 100644
--- a/Core/Generators/BotLootGenerator.cs
+++ b/Core/Generators/BotLootGenerator.cs
@@ -113,7 +113,8 @@ public class BotLootGenerator
_logger.Warning(_localisationService.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);
@@ -156,7 +157,8 @@ public class BotLootGenerator
botInventory,
botRole,
botItemLimits,
- containersIdFull);
+ containersIdFull
+ );
// Healing items / Meds
AddLootFromPool(
@@ -168,7 +170,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Drugs
AddLootFromPool(
@@ -180,7 +183,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Food
AddLootFromPool(
@@ -192,7 +196,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Drink
AddLootFromPool(
@@ -204,7 +209,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Currency
AddLootFromPool(
@@ -216,7 +222,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Stims
AddLootFromPool(
@@ -228,7 +235,8 @@ public class BotLootGenerator
botItemLimits,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
// Grenades
AddLootFromPool(
@@ -240,7 +248,8 @@ public class BotLootGenerator
null,
containersIdFull,
0,
- isPmc);
+ isPmc
+ );
var itemPriceLimits = GetSingleItemLootPriceLimits(botLevel, isPmc);
@@ -259,7 +268,8 @@ public class BotLootGenerator
botRole,
isPmc,
botLevel,
- containersIdFull);
+ containersIdFull
+ );
}
var backpackLootRoubleTotal = GetBackpackRoubleTotalByLevel(botLevel, isPmc);
@@ -269,7 +279,8 @@ public class BotLootGenerator
isPmc,
LootCacheType.Backpack,
botJsonTemplate,
- itemPriceLimits?.Backpack),
+ itemPriceLimits?.Backpack
+ ),
[EquipmentSlots.Backpack],
backpackLootCount,
botInventory,
@@ -277,7 +288,8 @@ public class BotLootGenerator
botItemLimits,
containersIdFull,
backpackLootRoubleTotal,
- isPmc);
+ isPmc
+ );
}
// TacticalVest - generate loot if they have one
@@ -290,7 +302,8 @@ public class BotLootGenerator
isPmc,
LootCacheType.Vest,
botJsonTemplate,
- itemPriceLimits?.Vest),
+ itemPriceLimits?.Vest
+ ),
[EquipmentSlots.TacticalVest],
vestLootCount,
botInventory,
@@ -298,7 +311,8 @@ public class BotLootGenerator
botItemLimits,
containersIdFull,
_pmcConfig.MaxVestLootTotalRub,
- isPmc);
+ isPmc
+ );
}
// Pockets
@@ -308,7 +322,8 @@ public class BotLootGenerator
isPmc,
LootCacheType.Pocket,
botJsonTemplate,
- itemPriceLimits?.Pocket),
+ itemPriceLimits?.Pocket
+ ),
[EquipmentSlots.Pockets],
pocketLootCount,
botInventory,
@@ -316,7 +331,8 @@ public class BotLootGenerator
botItemLimits,
containersIdFull,
_pmcConfig.MaxPocketLootTotalRub,
- isPmc);
+ isPmc
+ );
// Secure
@@ -331,8 +347,9 @@ public class BotLootGenerator
botRole,
null,
containersIdFull,
- - 1,
- isPmc);
+ -1,
+ isPmc
+ );
}
}
@@ -342,7 +359,8 @@ public class BotLootGenerator
if (isPmc)
{
var matchingValue = _pmcConfig.LootItemLimitsRub.FirstOrDefault(
- (minMaxValue) => botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max);
+ (minMaxValue) => botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
+ );
return matchingValue;
}
@@ -399,12 +417,12 @@ public class BotLootGenerator
public void AddLootFromPool(
Dictionary pool,
List equipmentSlots,
- int totalItemCount,
+ double totalItemCount,
BotBaseInventory inventoryToAddItemsTo, // TODO: type for containersIdFull was Set
string botRole,
ItemSpawnLimitSettings itemSpawnLimits,
List containersIdFull,
- int totalValueLimitRub = 0,
+ double totalValueLimitRub = 0,
bool isPmc = false)
{
throw new NotImplementedException();
@@ -448,7 +466,7 @@ public class BotLootGenerator
public void AddLooseWeaponsToInventorySlot(string sessionId,
BotBaseInventory botInventory,
EquipmentSlots equipmentSlot,
- BotTypeInventory templateInventory,
+ BotTypeInventory templateInventory,
Dictionary modsChances,
string botRole,
bool isPmc,
diff --git a/Core/Generators/BotWeaponGenerator.cs b/Core/Generators/BotWeaponGenerator.cs
index c71f6509..a2709f82 100644
--- a/Core/Generators/BotWeaponGenerator.cs
+++ b/Core/Generators/BotWeaponGenerator.cs
@@ -396,10 +396,10 @@ public class BotWeaponGenerator
"bot-weapons_required_slot_missing_item",
new
{
- ModSlot = modSlotTemplate.Name,
- ModName = modTemplate.Name,
- SlotId = mod.SlotId,
- BotRole = botRole,
+ modSlot = modSlotTemplate.Name,
+ modName = modTemplate.Name,
+ slotId = mod.SlotId,
+ botRole = botRole,
}
)
);
@@ -561,8 +561,8 @@ public class BotWeaponGenerator
"bot-weapon_missing_magazine_or_chamber",
new
{
- WeaponId = weaponTemplate.Id,
- BotRole = botRole,
+ weaponId = weaponTemplate.Id,
+ botRole = botRole,
}
)
);
@@ -597,9 +597,9 @@ public class BotWeaponGenerator
"bot-no_caliber_data_for_weapon_falling_back_to_default",
new
{
- WeaponId = weaponTemplate.Id,
- WeaponName = weaponTemplate.Name,
- DefaultAmmo = weaponTemplate.Properties.DefAmmo,
+ weaponId = weaponTemplate.Id,
+ weaponName = weaponTemplate.Name,
+ defaultAmmo = weaponTemplate.Properties.DefAmmo,
}
)
);
diff --git a/Core/Helpers/BotGeneratorHelper.cs b/Core/Helpers/BotGeneratorHelper.cs
index 2c6748ed..d316beff 100644
--- a/Core/Helpers/BotGeneratorHelper.cs
+++ b/Core/Helpers/BotGeneratorHelper.cs
@@ -237,9 +237,9 @@ public class BotGeneratorHelper
"bot-missing_equipment_settings",
new
{
- BotRole = botRole,
- Setting = setting,
- DefaultValue = defaultValue
+ botRole = botRole,
+ setting = setting,
+ defaultValue = defaultValue
}
)
);
@@ -257,9 +257,9 @@ public class BotGeneratorHelper
"bot-missing_equipment_settings_property",
new
{
- BotRole = botRole,
- Setting = setting,
- DefaultValue = defaultValue
+ botRole = botRole,
+ setting = setting,
+ defaultValue = defaultValue
}
)
);
diff --git a/Core/Helpers/BotWeaponGeneratorHelper.cs b/Core/Helpers/BotWeaponGeneratorHelper.cs
index 2ef93210..d9a81053 100644
--- a/Core/Helpers/BotWeaponGeneratorHelper.cs
+++ b/Core/Helpers/BotWeaponGeneratorHelper.cs
@@ -1,21 +1,82 @@
using Core.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
+using Core.Models.Spt.Bots;
+using Core.Models.Utils;
+using Core.Servers;
+using Core.Services;
+using Core.Utils;
namespace Core.Helpers;
[Injectable]
public class BotWeaponGeneratorHelper
{
+ private readonly ISptLogger _logger;
+ private readonly DatabaseServer _databaseServer;
+ private readonly ItemHelper _itemHelper;
+ private readonly RandomUtil _randomUtil;
+ private readonly HashUtil _hashUtil;
+ private readonly WeightedRandomHelper _weightedRandomHelper;
+ private readonly BotGeneratorHelper _botGeneratorHelper;
+ private readonly LocalisationService _localisationService;
+
+ private readonly List _magCheck = ["CylinderMagazine", "SpringDrivenCylinder"];
+
+ public BotWeaponGeneratorHelper
+ (
+ ISptLogger logger,
+ DatabaseServer databaseServer,
+ ItemHelper itemHelper,
+ RandomUtil randomUtil,
+ HashUtil hashUtil,
+ WeightedRandomHelper weightedRandomHelper,
+ BotGeneratorHelper botGeneratorHelper,
+ LocalisationService localisationService
+ )
+ {
+ _logger = logger;
+ _databaseServer = databaseServer;
+ _itemHelper = itemHelper;
+ _randomUtil = randomUtil;
+ _hashUtil = hashUtil;
+ _weightedRandomHelper = weightedRandomHelper;
+ _botGeneratorHelper = botGeneratorHelper;
+ _localisationService = localisationService;
+ }
+
///
/// Get a randomized number of bullets for a specific magazine
///
/// Weights of magazines
/// Magazine to generate bullet count for
/// Bullet count number
- public int GetRandomizedBulletCount(GenerationData magCounts, TemplateItem magTemplate)
+ public double? GetRandomizedBulletCount(GenerationData magCounts, TemplateItem magTemplate)
{
- throw new NotImplementedException();
+ var randomizedMagazineCount = GetRandomizedMagazineCount(magCounts);
+ var parentItem = _itemHelper.GetItem(magTemplate.Parent).Value;
+ double? chamberBulletCount = 0;
+ if (MagazineIsCylinderRelated(parentItem.Name))
+ {
+ var firstSlotAmmoTpl = magTemplate.Properties.Cartridges[0].Props.Filters[0].Filter[0];
+ var ammoMaxStackSize = _itemHelper.GetItem(firstSlotAmmoTpl).Value?.Properties?.StackMaxSize ?? 1;
+ chamberBulletCount = ammoMaxStackSize == 1
+ ? 1 // Rotating grenade launcher
+ : magTemplate.Properties.Slots.Count; // Shotguns/revolvers. We count the number of camoras as the _max_count of the magazine is 0
+ }
+ else if (parentItem.Id == BaseClasses.UBGL)
+ {
+ // Underbarrel launchers can only have 1 chambered grenade
+ chamberBulletCount = 1;
+ }
+ else
+ {
+ chamberBulletCount = magTemplate.Properties.Cartridges[0].MaxCount;
+ }
+
+ // Get the amount of bullets that would fit in the internal magazine
+ // and multiply by how many magazines were supposed to be created
+ return chamberBulletCount * randomizedMagazineCount;
}
///
@@ -25,7 +86,7 @@ public class BotWeaponGeneratorHelper
/// Numerical value of magazine count
public int GetRandomizedMagazineCount(GenerationData magCounts)
{
- throw new NotImplementedException();
+ return (int)_weightedRandomHelper.GetWeightedValue(magCounts.Weights);
}
///
@@ -35,7 +96,7 @@ public class BotWeaponGeneratorHelper
/// True if it is cylinder related
public bool MagazineIsCylinderRelated(string magazineParentName)
{
- throw new NotImplementedException();
+ return _magCheck.Contains(magazineParentName);
}
///
@@ -47,7 +108,14 @@ public class BotWeaponGeneratorHelper
/// Item array
public List
- CreateMagazineWithAmmo(string magazineTpl, string ammoTpl, TemplateItem magTemplate)
{
- throw new NotImplementedException();
+ List
- magazine =
+ [
+ new() { Id = _hashUtil.Generate(), Template = magazineTpl }
+ ];
+
+ _itemHelper.FillMagazineWithCartridge(magazine, magTemplate, ammoTpl, 1);
+
+ return magazine;
}
///
@@ -61,10 +129,36 @@ public class BotWeaponGeneratorHelper
string ammoTpl,
int cartridgeCount,
BotBaseInventory inventory,
- object equipmentSlotsToAddTo // TODO: EquipmentSlots[] equipmentSlotsToAddTo = new EquipmentSlots[] { EquipmentSlots.TACTICAL_VEST, EquipmentSlots.POCKETS }
+ List equipmentSlotsToAddTo
)
{
- throw new NotImplementedException();
+ if (equipmentSlotsToAddTo is null)
+ equipmentSlotsToAddTo = [EquipmentSlots.TacticalVest.ToString(), EquipmentSlots.Pockets.ToString()];
+
+ var ammoItems = _itemHelper.SplitStack(new () {
+ Id = _hashUtil.Generate(),
+ Template = ammoTpl,
+ Upd = new () { StackObjectsCount = cartridgeCount },
+ });
+
+ foreach (var ammoItem in ammoItems) {
+ var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
+ equipmentSlotsToAddTo,
+ ammoItem.Id,
+ ammoItem.Template,
+ [ammoItem],
+ inventory
+ );
+
+ if (result != ItemAddedResult.SUCCESS) {
+ _logger.Debug($"Unable to add ammo: {ammoItem.Template} to bot inventory, {result.ToString()}");
+
+ if (result == ItemAddedResult.NO_SPACE || result == ItemAddedResult.NO_CONTAINERS) {
+ // If there's no space for 1 stack or no containers to hold item, there's no space for the others
+ break;
+ }
+ }
+ }
}
///
@@ -74,6 +168,6 @@ public class BotWeaponGeneratorHelper
/// Tpl of magazine
public string GetWeaponsDefaultMagazineTpl(TemplateItem weaponTemplate)
{
- throw new NotImplementedException();
+ return weaponTemplate.Properties.DefMagType;
}
}
diff --git a/Core/Helpers/HideoutHelper.cs b/Core/Helpers/HideoutHelper.cs
index fe4f82a8..ea26eac3 100644
--- a/Core/Helpers/HideoutHelper.cs
+++ b/Core/Helpers/HideoutHelper.cs
@@ -1,15 +1,32 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Hideout;
using Core.Models.Eft.ItemEvent;
using Core.Models.Enums;
+using Core.Models.Utils;
+using Core.Services;
+using Core.Utils;
namespace Core.Helpers;
[Injectable]
public class HideoutHelper
{
+ private readonly ISptLogger _logger;
+ protected TimeUtil _timeUtil;
+ private readonly LocalisationService _localisationService;
+
+ public HideoutHelper(
+ ISptLogger logger,
+ TimeUtil timeUtil,
+ LocalisationService localisationService)
+ {
+ _logger = logger;
+ _timeUtil = timeUtil;
+ _localisationService = localisationService;
+ }
+
///
/// Add production to profiles' Hideout.Production array
///
@@ -52,9 +69,41 @@ public class HideoutHelper
///
/// Profile to add bonus to
/// Bonus to add to profile
- public void ApplyPlayerUpgradesBonuses(PmcData profileData, StageBonus bonus)
+ public void ApplyPlayerUpgradesBonuses(PmcData profileData, Bonus bonus)
{
- throw new NotImplementedException();
+ // Handle additional changes some bonuses need before being added
+ var bonusToAdd = new Bonus();
+ switch (bonus.Type)
+ {
+ case BonusType.StashSize:
+ {
+ // Find stash item and adjust tpl to new tpl from bonus
+ var stashItem = profileData.Inventory.Items.FirstOrDefault((x) => x.Id == profileData.Inventory.Stash);
+ if (stashItem is null)
+ {
+ _logger.Warning(_localisationService.GetText("hideout-unable_to_apply_stashsize_bonus_no_stash_found", profileData.Inventory.Stash));
+ }
+
+ stashItem.Template = bonus.TemplateId;
+
+ break;
+ }
+ case BonusType.MaximumEnergyReserve:
+ // Amend max energy in profile
+ profileData.Health.Energy.Maximum += bonus.Value;
+ break;
+ case BonusType.TextBonus:
+ // Delete values before they're added to profile
+ bonus.IsPassive = null;
+ bonus.IsProduction = null;
+ bonus.IsVisible = null;
+ break;
+ }
+
+ // Add bonus to player bonuses array in profile
+ // EnergyRegeneration, HealthRegeneration, RagfairCommission, ScavCooldownTimer, SkillGroupLevelingBoost, ExperienceRate, QuestMoneyReward etc
+ _logger.Debug($"Adding bonus: {bonus.Type} to profile, value: {bonus.Value}");
+ profileData.Bonuses.Add(bonus);
}
///
@@ -420,7 +469,22 @@ public class HideoutHelper
/// Profile to upgrade wall in
public void UnlockHideoutWallInProfile(PmcData profileData)
{
- throw new NotImplementedException();
+ var profileHideoutAreas = profileData.Hideout.Areas;
+ var waterCollector = profileHideoutAreas.FirstOrDefault((x) => x.Type == HideoutAreas.WATER_COLLECTOR);
+ var medStation = profileHideoutAreas.FirstOrDefault((x) => x.Type == HideoutAreas.MEDSTATION);
+ var wall = profileHideoutAreas.FirstOrDefault((x) => x.Type == HideoutAreas.EMERGENCY_WALL);
+
+ // No collector or med station, skip
+ if ((waterCollector is null && medStation is null))
+ {
+ return;
+ }
+
+ // If med-station > level 1 AND water collector > level 1 AND wall is level 0
+ if (waterCollector?.Level >= 1 && medStation?.Level >= 1 && wall?.Level <= 0)
+ {
+ wall.Level = 3;
+ }
}
///
@@ -439,7 +503,20 @@ public class HideoutHelper
/// Profile to adjust
public void SetHideoutImprovementsToCompleted(PmcData profileData)
{
- throw new NotImplementedException();
+ foreach (var improvementId in profileData.Hideout.Improvements)
+ {
+ if (!profileData.Hideout.Improvements.TryGetValue(improvementId.Key, out var improvementDetails))
+ {
+ continue;
+ }
+
+ if (improvementDetails.Completed == false
+ && improvementDetails.ImproveCompleteTimestamp < _timeUtil.GetTimeStamp()
+ )
+ {
+ improvementDetails.Completed = true;
+ }
+ }
}
///
diff --git a/Core/Helpers/PresetHelper.cs b/Core/Helpers/PresetHelper.cs
index 08bee62d..b8b82f32 100644
--- a/Core/Helpers/PresetHelper.cs
+++ b/Core/Helpers/PresetHelper.cs
@@ -11,7 +11,7 @@ public class PresetHelper
{
protected DatabaseService _databaseService;
protected ItemHelper _itemHelper;
- protected ICloner _primaryCloner;
+ protected ICloner _cloner;
protected Dictionary> _lookup = new();
protected Dictionary _defaultEquipmentPresets;
@@ -21,12 +21,12 @@ public class PresetHelper
(
DatabaseService databaseService,
ItemHelper itemHelper,
- ICloner primaryCloner
+ ICloner cloner
)
{
_databaseService = databaseService;
_itemHelper = itemHelper;
- _primaryCloner = primaryCloner;
+ _cloner = cloner;
}
public void HydratePresetStore(Dictionary> input)
@@ -91,29 +91,40 @@ public class PresetHelper
* @param baseClass The BaseClasses enum to check against
* @returns True if the preset is of the given base class, false otherwise
*/
- public bool IsPresetBaseClass(string id, BaseClasses baseClass)
+ public bool IsPresetBaseClass(string id, string baseClass)
{
- throw new NotImplementedException();
+ return IsPreset(id) && _itemHelper.IsOfBaseclass(GetPreset(id).Encyclopedia, baseClass);
}
public bool HasPreset(string templateId)
{
- throw new NotImplementedException();
+ return _lookup.ContainsKey(templateId) ;
}
public Preset GetPreset(string id)
{
- throw new NotImplementedException();
+ return _cloner.Clone(_databaseService.GetGlobals().ItemPresets[id]);
}
public List GetAllPresets()
{
- throw new NotImplementedException();
+ return _cloner.Clone(_databaseService.GetGlobals().ItemPresets.Values.ToList());
}
public List GetPresets(string templateId)
{
- throw new NotImplementedException();
+ if (!HasPreset(templateId)) {
+ return [];
+ }
+
+ List presets = [];
+ var ids = _lookup[templateId];
+
+ foreach (var id in ids) {
+ presets.Add(GetPreset(id));
+ }
+
+ return presets;
}
/**
@@ -121,14 +132,36 @@ public class PresetHelper
* @param templateId Item tpl to get preset for
* @returns null if no default preset, otherwise Preset
*/
- public Preset GetDefaultPreset(string templateId)
+ public Preset? GetDefaultPreset(string templateId)
{
- throw new NotImplementedException();
+ if (!HasPreset(templateId)) {
+ return null;
+ }
+
+ var allPresets = GetPresets(templateId);
+
+ foreach (var preset in allPresets) {
+ if (preset.Encyclopedia is not null) {
+ return preset;
+ }
+ }
+
+ return allPresets[0];
}
public string GetBaseItemTpl(string presetId)
{
- throw new NotImplementedException();
+ if (IsPreset(presetId)) {
+ var preset = GetPreset(presetId);
+
+ foreach (var item in preset.Items) {
+ if (preset.Parent == item.Id) {
+ return item.Template;
+ }
+ }
+ }
+
+ return "";
}
/**
@@ -138,6 +171,13 @@ public class PresetHelper
*/
public decimal GetDefaultPresetOrItemPrice(string tpl)
{
- throw new NotImplementedException();
+ // Get default preset if it exists
+ var defaultPreset = GetDefaultPreset(tpl);
+
+ // Bundle up tpls we want price for
+ var tpls = defaultPreset is not null ? defaultPreset.Items.Select((item) => item.Template) : [tpl];
+
+ // Get price of tpls
+ return _itemHelper.GetItemAndChildrenPrice(tpls.ToList());
}
}
diff --git a/Core/Helpers/QuestRewardHelper.cs b/Core/Helpers/QuestRewardHelper.cs
index 2508d0b4..07c47250 100644
--- a/Core/Helpers/QuestRewardHelper.cs
+++ b/Core/Helpers/QuestRewardHelper.cs
@@ -152,8 +152,8 @@ public class QuestRewardHelper
default:
_logger.Error(_localisationService.GetText("quest-reward_type_not_handled", new
{
- RewardType = reward.Type,
- QuestId = questId,
+ rewardType = reward.Type,
+ questId = questId,
questName = questDetails.QuestName
}));
break;
@@ -280,8 +280,8 @@ public class QuestRewardHelper
{
_logger.Error(_localisationService.GetText("quest-unable_to_find_matching_hideout_production", new
{
- QuestName = questDetails.QuestName,
- MatchCount = matchingProductions.Count
+ questName = questDetails.QuestName,
+ matchCount = matchingProductions.Count
}));
return;
diff --git a/Core/Helpers/TraderHelper.cs b/Core/Helpers/TraderHelper.cs
index c3c0bb11..0036621e 100644
--- a/Core/Helpers/TraderHelper.cs
+++ b/Core/Helpers/TraderHelper.cs
@@ -331,6 +331,7 @@ public class TraderHelper
/// True if Traders enum has the param as a value
public bool TraderEnumHasValue(string traderId)
{
- throw new NotImplementedException();
+ _logger.Error("HACK TraderEnumHasValue");
+ return Traders.TradersDictionary.ContainsValue(traderId);
}
}
diff --git a/Core/Models/Eft/Common/Tables/BotType.cs b/Core/Models/Eft/Common/Tables/BotType.cs
index ccc92bd2..17ab603c 100644
--- a/Core/Models/Eft/Common/Tables/BotType.cs
+++ b/Core/Models/Eft/Common/Tables/BotType.cs
@@ -303,7 +303,7 @@ public class GenerationData
{
/** key: number of items, value: weighting */
[JsonPropertyName("weights")]
- public Dictionary? Weights { get; set; }
+ public Dictionary? Weights { get; set; }
/** Array of item tpls */
[JsonPropertyName("whitelist")]
diff --git a/Core/Models/Eft/Hideout/HideoutArea.cs b/Core/Models/Eft/Hideout/HideoutArea.cs
index 04e7fe39..99ea612f 100644
--- a/Core/Models/Eft/Hideout/HideoutArea.cs
+++ b/Core/Models/Eft/Hideout/HideoutArea.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
namespace Core.Models.Eft.Hideout;
@@ -9,7 +10,7 @@ public class HideoutArea
public string? Id { get; set; }
[JsonPropertyName("type")]
- public int? Type { get; set; }
+ public HideoutAreas? Type { get; set; }
[JsonPropertyName("enabled")]
public bool? IsEnabled { get; set; }
@@ -57,7 +58,7 @@ public class Stage
public bool? AutoUpgrade { get; set; }
[JsonPropertyName("bonuses")]
- public List? Bonuses { get; set; }
+ public List? Bonuses { get; set; }
[JsonPropertyName("constructionTime")]
public double? ConstructionTime { get; set; }
@@ -181,39 +182,3 @@ public class StageRequirement : RequirementBase
[JsonPropertyName("skillLevel")]
public int? SkillLevel { get; set; }
}
-
-public class StageBonus
-{
- [JsonPropertyName("value")]
- public int? Value { get; set; }
-
- [JsonPropertyName("passive")]
- public bool? Passive { get; set; }
-
- [JsonPropertyName("production")]
- public bool? Production { get; set; }
-
- [JsonPropertyName("visible")]
- public bool? Visible { get; set; }
-
- [JsonPropertyName("skillType")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
- public BonusSkillType? SkillType { get; set; }
-
- [JsonPropertyName("type")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
- public BonusType? Type { get; set; }
-
- [JsonPropertyName("filter")]
- public List? Filter { get; set; }
-
- [JsonPropertyName("icon")]
- public string? Icon { get; set; }
-
- /** CHANGES PER DUMP */
- [JsonPropertyName("id")]
- public string? Id { get; set; }
-
- [JsonPropertyName("templateId")]
- public string? TemplateId { get; set; }
-}
\ No newline at end of file
diff --git a/Core/Models/Enums/Traders.cs b/Core/Models/Enums/Traders.cs
index 7d6204aa..3d66acbd 100644
--- a/Core/Models/Enums/Traders.cs
+++ b/Core/Models/Enums/Traders.cs
@@ -13,4 +13,34 @@ public static class Traders
public const string LIGHTHOUSEKEEPER = "638f541a29ffd1183d187f57";
public const string BTR = "656f0f98d80a697f855d34b1";
public const string REF = "6617beeaa9cfa777ca915b7c";
-}
\ No newline at end of file
+
+ public static Dictionary TradersDictionary { get; set; } = new()
+ {
+ {TradersEnum.Prapor, "54cb50c76803fa8b248b4571"},
+ {TradersEnum.Therapist, "54cb57776803fa99248b456e"},
+ {TradersEnum.Fence, "579dc571d53a0658a154fbec"},
+ {TradersEnum.Skier, "58330581ace78e27b8b10cee"},
+ {TradersEnum.Peacekeeper, "5935c25fb3acc3127c3d8cd9"},
+ {TradersEnum.Mechanic, "5a7c2eca46aef81a7ca2145d"},
+ {TradersEnum.Ragman, "5ac3b934156ae10c4430e83c"},
+ {TradersEnum.Jaeger, "5c0647fdd443bc2504c2d371"},
+ {TradersEnum.LighthouseKeeper, "638f541a29ffd1183d187f57"},
+ {TradersEnum.Btr, "656f0f98d80a697f855d34b1"},
+ {TradersEnum.Ref, "6617beeaa9cfa777ca915b7c"}
+ };
+}
+
+public enum TradersEnum
+{
+ Prapor,
+ Therapist,
+ Fence,
+ Skier,
+ Peacekeeper,
+ Mechanic,
+ Ragman,
+ Jaeger,
+ LighthouseKeeper,
+ Btr,
+ Ref
+}
diff --git a/Core/Models/Spt/Bots/GenerateWeaponRequest.cs b/Core/Models/Spt/Bots/GenerateWeaponRequest.cs
index 128077fb..54af2fec 100644
--- a/Core/Models/Spt/Bots/GenerateWeaponRequest.cs
+++ b/Core/Models/Spt/Bots/GenerateWeaponRequest.cs
@@ -43,7 +43,7 @@ public class GenerateWeaponRequest
/** Array of item tpls the weapon does not support */
[JsonPropertyName("conflictingItemTpls")]
- public HashSet? ConflictingItemTpls { get; set; }
+ public List? ConflictingItemTpls { get; set; }
}
public class BotData
@@ -98,4 +98,4 @@ public class ItemCount
{
[JsonPropertyName("count")]
public int? Count { get; set; }
-}
\ No newline at end of file
+}
diff --git a/Core/Models/Spt/Bots/ModToSpawnRequest.cs b/Core/Models/Spt/Bots/ModToSpawnRequest.cs
index 3c45078a..0d8dc81d 100644
--- a/Core/Models/Spt/Bots/ModToSpawnRequest.cs
+++ b/Core/Models/Spt/Bots/ModToSpawnRequest.cs
@@ -74,8 +74,8 @@ public class ModToSpawnRequest
/// List of item tpls the weapon does not support
///
[JsonPropertyName("conflictingItemTpls")]
- public HashSet? ConflictingItemTpls { get; set; }
+ public List? ConflictingItemTpls { get; set; }
[JsonPropertyName("botData")]
public BotData? BotData { get; set; }
-}
\ No newline at end of file
+}
diff --git a/Core/Services/MailSendService.cs b/Core/Services/MailSendService.cs
index 0cb4238e..c501d205 100644
--- a/Core/Services/MailSendService.cs
+++ b/Core/Services/MailSendService.cs
@@ -81,8 +81,8 @@ public class MailSendService
{
_logger.Error(_localisationService.GetText("mailsend-missing_trader", new
{
- MessageType = messageType,
- SessionId = sessionId,
+ messageType = messageType,
+ sessionId = sessionId,
}));
return;
@@ -138,8 +138,8 @@ public class MailSendService
{
_logger.Error(_localisationService.GetText("mailsend-missing_trader", new
{
- MessageType = messageType,
- SessionId = sessionId,
+ messageType = messageType,
+ sessionId = sessionId,
}));
return;
@@ -408,8 +408,8 @@ public class MailSendService
{
_localisationService.GetText("mailsend-missing_parent", new
{
- TraderId = messageDetails.Trader,
- Sender = messageDetails.Sender,
+ traderId = messageDetails.Trader,
+ sender = messageDetails.Sender,
});
return itemsToSendToPlayer;
@@ -436,8 +436,8 @@ public class MailSendService
{
_logger.Error(_localisationService.GetText("dialog-missing_item_template", new
{
- Tpl = reward.Template,
- Type = dialogType,
+ tpl = reward.Template,
+ type = dialogType,
}));
continue;
diff --git a/Core/Services/NotificationService.cs b/Core/Services/NotificationService.cs
index 2ba21dae..b436bbf2 100644
--- a/Core/Services/NotificationService.cs
+++ b/Core/Services/NotificationService.cs
@@ -1,4 +1,4 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Ws;
namespace Core.Services;
@@ -6,6 +6,8 @@ namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class NotificationService
{
+ protected Dictionary> _messageQueue = new();
+
public Dictionary> GetMessageQueue()
{
throw new NotImplementedException();
@@ -16,20 +18,20 @@ public class NotificationService
throw new NotImplementedException();
}
- public void UpdateMessageOnQueue(string sessionId, List
public void Add(string sessionID, WsNotificationEvent message)
{
- throw new NotImplementedException();
+ Get(sessionID).Add(message);
}
///
/// Get message queue for session
///
///
- public List