This commit is contained in:
Alex
2025-01-16 13:09:45 +00:00
20 changed files with 915 additions and 192 deletions
+2 -2
View File
@@ -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;
+15 -3
View File
@@ -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
/// <param name="fullProfile">Profile to check for dialog in</param>
private void CheckForAndRemoveUndefinedDialogues(SptProfile fullProfile)
{
throw new NotImplementedException();
if (fullProfile.DialogueRecords.TryGetValue("undefined", out var undefinedDialog))
{
fullProfile.DialogueRecords.Remove("undefined");
}
}
/// <summary>
@@ -579,7 +586,12 @@ public class GameController
/// <param name="fullProfile"></param>
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()
+457 -52
View File
@@ -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<string> SortModKeys(List<string> 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<string> 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
/// </summary>
/// <param name="request">Data used to choose an appropriate mod with</param>
/// <returns>itemHelper.getItem() result</returns>
public KeyValuePair<bool, TemplateItem>? ChooseModToPutIntoSlot(ModToSpawnRequest request) // TODO: type fuckery: [boolean, ITemplateItem] | undefined
public KeyValuePair<bool, TemplateItem>? 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<string> modPool, Slot parentSlot,
ModSpawn? choiceTypeEnum, List<Item> 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);
}
/// <summary>
@@ -931,9 +988,85 @@ public class BotEquipmentModGenerator
/// <param name="modSpawnType">How should the slot choice be handled - forced/normal etc</param>
/// <param name="weapon">Weapon mods at current time</param>
/// <returns>IChooseRandomCompatibleModResult</returns>
public ChooseRandomCompatibleModResult GetCompatibleModFromPool(List<string> modPool, ModSpawn modSpawnType, List<Item> weapon)
public ChooseRandomCompatibleModResult GetCompatibleModFromPool(List<string> modPool, ModSpawn? modSpawnType, List<Item> 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<T> CreateExhaustableArray<T>(List<T> itemsToAddToArray) // TODO: this wont likely be needed, reimplement for C#
@@ -980,7 +1113,75 @@ public class BotEquipmentModGenerator
public List<string> 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<bool, TemplateItem>? 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;
}
/// <summary>
@@ -1117,7 +1362,37 @@ public class BotEquipmentModGenerator
public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate, Dictionary<string, Dictionary<string, List<string>>> 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;
}
}
}
/// <summary>
@@ -1181,7 +1456,75 @@ public class BotEquipmentModGenerator
public void FillCamora(List<Item> items, Dictionary<string, Dictionary<string, List<string>>> 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<string> 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 });
}
}
/// <summary>
@@ -1212,6 +1555,68 @@ public class BotEquipmentModGenerator
/// <returns>Array of scope tpls that have been filtered to just ones allowed for that weapon type</returns>
public List<string> FilterSightsByWeaponType(Item weapon, List<string> scopes, Dictionary<string, List<string>> 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<string> 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<string> 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;
}
}
+40 -22
View File
@@ -113,7 +113,8 @@ public class BotLootGenerator
_logger.Warning(_localisationService.GetText("bot-unable_to_generate_bot_loot", botRole));
return;
}
var backpackLootCount = _weightedRandomHelper.GetWeightedValue<int>(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<string, int> pool,
List<EquipmentSlots> equipmentSlots,
int totalItemCount,
double totalItemCount,
BotBaseInventory inventoryToAddItemsTo, // TODO: type for containersIdFull was Set<string>
string botRole,
ItemSpawnLimitSettings itemSpawnLimits,
List<string> 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<string, double> modsChances,
string botRole,
bool isPmc,
+9 -9
View File
@@ -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,
}
)
);
+6 -6
View File
@@ -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
}
)
);
+102 -8
View File
@@ -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<BotWeaponGeneratorHelper> _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<string> _magCheck = ["CylinderMagazine", "SpringDrivenCylinder"];
public BotWeaponGeneratorHelper
(
ISptLogger<BotWeaponGeneratorHelper> 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;
}
/// <summary>
/// Get a randomized number of bullets for a specific magazine
/// </summary>
/// <param name="magCounts">Weights of magazines</param>
/// <param name="magTemplate">Magazine to generate bullet count for</param>
/// <returns>Bullet count number</returns>
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;
}
/// <summary>
@@ -25,7 +86,7 @@ public class BotWeaponGeneratorHelper
/// <returns>Numerical value of magazine count</returns>
public int GetRandomizedMagazineCount(GenerationData magCounts)
{
throw new NotImplementedException();
return (int)_weightedRandomHelper.GetWeightedValue<double>(magCounts.Weights);
}
/// <summary>
@@ -35,7 +96,7 @@ public class BotWeaponGeneratorHelper
/// <returns>True if it is cylinder related</returns>
public bool MagazineIsCylinderRelated(string magazineParentName)
{
throw new NotImplementedException();
return _magCheck.Contains(magazineParentName);
}
/// <summary>
@@ -47,7 +108,14 @@ public class BotWeaponGeneratorHelper
/// <returns>Item array</returns>
public List<Item> CreateMagazineWithAmmo(string magazineTpl, string ammoTpl, TemplateItem magTemplate)
{
throw new NotImplementedException();
List<Item> magazine =
[
new() { Id = _hashUtil.Generate(), Template = magazineTpl }
];
_itemHelper.FillMagazineWithCartridge(magazine, magTemplate, ammoTpl, 1);
return magazine;
}
/// <summary>
@@ -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<string> 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;
}
}
}
}
/// <summary>
@@ -74,6 +168,6 @@ public class BotWeaponGeneratorHelper
/// <returns>Tpl of magazine</returns>
public string GetWeaponsDefaultMagazineTpl(TemplateItem weaponTemplate)
{
throw new NotImplementedException();
return weaponTemplate.Properties.DefMagType;
}
}
+82 -5
View File
@@ -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<HideoutHelper> _logger;
protected TimeUtil _timeUtil;
private readonly LocalisationService _localisationService;
public HideoutHelper(
ISptLogger<HideoutHelper> logger,
TimeUtil timeUtil,
LocalisationService localisationService)
{
_logger = logger;
_timeUtil = timeUtil;
_localisationService = localisationService;
}
/// <summary>
/// Add production to profiles' Hideout.Production array
/// </summary>
@@ -52,9 +69,41 @@ public class HideoutHelper
/// </summary>
/// <param name="profileData">Profile to add bonus to</param>
/// <param name="bonus">Bonus to add to profile</param>
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);
}
/// <summary>
@@ -420,7 +469,22 @@ public class HideoutHelper
/// <param name="profileData">Profile to upgrade wall in</param>
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;
}
}
/// <summary>
@@ -439,7 +503,20 @@ public class HideoutHelper
/// <param name="profileData">Profile to adjust</param>
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;
}
}
}
/// <summary>
+53 -13
View File
@@ -11,7 +11,7 @@ public class PresetHelper
{
protected DatabaseService _databaseService;
protected ItemHelper _itemHelper;
protected ICloner _primaryCloner;
protected ICloner _cloner;
protected Dictionary<string, List<string>> _lookup = new();
protected Dictionary<string, Preset> _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<string, List<string>> 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<Preset> GetAllPresets()
{
throw new NotImplementedException();
return _cloner.Clone(_databaseService.GetGlobals().ItemPresets.Values.ToList());
}
public List<Preset> GetPresets(string templateId)
{
throw new NotImplementedException();
if (!HasPreset(templateId)) {
return [];
}
List<Preset> 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());
}
}
+4 -4
View File
@@ -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;
+2 -1
View File
@@ -331,6 +331,7 @@ public class TraderHelper
/// <returns>True if Traders enum has the param as a value</returns>
public bool TraderEnumHasValue(string traderId)
{
throw new NotImplementedException();
_logger.Error("HACK TraderEnumHasValue");
return Traders.TradersDictionary.ContainsValue(traderId);
}
}
+1 -1
View File
@@ -303,7 +303,7 @@ public class GenerationData
{
/** key: number of items, value: weighting */
[JsonPropertyName("weights")]
public Dictionary<int, double>? Weights { get; set; }
public Dictionary<double, double>? Weights { get; set; }
/** Array of item tpls */
[JsonPropertyName("whitelist")]
+3 -38
View File
@@ -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<StageBonus>? Bonuses { get; set; }
public List<Bonus>? 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<string>? 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; }
}
+31 -1
View File
@@ -13,4 +13,34 @@ public static class Traders
public const string LIGHTHOUSEKEEPER = "638f541a29ffd1183d187f57";
public const string BTR = "656f0f98d80a697f855d34b1";
public const string REF = "6617beeaa9cfa777ca915b7c";
}
public static Dictionary<TradersEnum, string> 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
}
@@ -43,7 +43,7 @@ public class GenerateWeaponRequest
/** Array of item tpls the weapon does not support */
[JsonPropertyName("conflictingItemTpls")]
public HashSet<string>? ConflictingItemTpls { get; set; }
public List<string>? ConflictingItemTpls { get; set; }
}
public class BotData
@@ -98,4 +98,4 @@ public class ItemCount
{
[JsonPropertyName("count")]
public int? Count { get; set; }
}
}
+2 -2
View File
@@ -74,8 +74,8 @@ public class ModToSpawnRequest
/// List of item tpls the weapon does not support
/// </summary>
[JsonPropertyName("conflictingItemTpls")]
public HashSet<string>? ConflictingItemTpls { get; set; }
public List<string>? ConflictingItemTpls { get; set; }
[JsonPropertyName("botData")]
public BotData? BotData { get; set; }
}
}
+8 -8
View File
@@ -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;
+19 -7
View File
@@ -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<string, List<WsNotificationEvent>> _messageQueue = new();
public Dictionary<string, List<object>> GetMessageQueue()
{
throw new NotImplementedException();
@@ -16,20 +18,20 @@ public class NotificationService
throw new NotImplementedException();
}
public void UpdateMessageOnQueue(string sessionId, List<object> value)
public void UpdateMessageOnQueue(string sessionId, List<WsNotificationEvent> value)
{
throw new NotImplementedException();
}
public bool Has(string sessionID)
{
throw new NotImplementedException();
return _messageQueue.ContainsKey(sessionID);
}
/// <summary>
/// Pop first message from queue.
/// </summary>
public object Pop(string sessionID)
public WsNotificationEvent Pop(string sessionID)
{
throw new NotImplementedException();
}
@@ -39,15 +41,25 @@ public class NotificationService
/// </summary>
public void Add(string sessionID, WsNotificationEvent message)
{
throw new NotImplementedException();
Get(sessionID).Add(message);
}
/// <summary>
/// Get message queue for session
/// </summary>
/// <param name="sessionID"></param>
public List<object> Get(string sessionID)
public List<WsNotificationEvent> Get(string sessionID)
{
throw new NotImplementedException();
if (sessionID is null)
{
throw new Exception("sessionID missing");
}
if (!_messageQueue.ContainsKey(sessionID))
{
_messageQueue[sessionID] = [];
}
return _messageQueue[sessionID];
}
}
+73 -4
View File
@@ -11,6 +11,10 @@ using Core.Utils;
using System.Text.RegularExpressions;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Models.Spt.Bots;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Reflection.Emit;
using System.Security.AccessControl;
namespace Core.Services;
@@ -23,6 +27,7 @@ public class ProfileFixerService
protected ItemHelper _itemHelper;
protected QuestRewardHelper _questRewardHelper;
protected TraderHelper _traderHelper;
protected HideoutHelper _hideoutHelper;
protected DatabaseService _databaseService;
protected LocalisationService _localisationService;
protected ConfigServer _configServer;
@@ -36,6 +41,7 @@ public class ProfileFixerService
ItemHelper itemHelper,
QuestRewardHelper questRewardHelper,
TraderHelper traderHelper,
HideoutHelper hideoutHelper,
DatabaseService databaseService,
LocalisationService localisationService,
ConfigServer configServer,
@@ -48,6 +54,7 @@ public class ProfileFixerService
_itemHelper = itemHelper;
_questRewardHelper = questRewardHelper;
_traderHelper = traderHelper;
_hideoutHelper = hideoutHelper;
_databaseService = databaseService;
_localisationService = localisationService;
_configServer = configServer;
@@ -484,7 +491,7 @@ public class ProfileFixerService
// Check each item in inventory to ensure item exists in itemdb
foreach (var item in inventoryItemsToCheck)
{
if (itemsDb[item.Template] is not null)
if (!itemsDb.ContainsKey(item.Template))
{
_logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
@@ -690,7 +697,54 @@ public class ProfileFixerService
*/
public void AddMissingHideoutBonusesToProfile(PmcData pmcProfile)
{
throw new NotImplementedException();
var dbHideoutAreas = _databaseService.GetHideout().Areas;
foreach (var profileArea in pmcProfile.Hideout.Areas) {
var areaType = profileArea.Type;
var level = profileArea.Level;
if (level.GetValueOrDefault(0) == 0)
{
continue;
}
// Get array of hideout area upgrade levels to check for bonuses
// Zero indexed
var areaLevelsToCheck = new List<string>();
for (var index = 0; index < level + 1; index++)
{
// Stage key is saved as string in db
areaLevelsToCheck.Add(index.ToString());
}
// Iterate over area levels, check for bonuses, add if needed
var dbArea = dbHideoutAreas.FirstOrDefault((area) => area.Type == areaType);
if (dbArea is null)
{
continue;
}
foreach (var areaLevel in areaLevelsToCheck) {
// Get areas level bonuses from db
var levelBonuses = dbArea.Stages[areaLevel]?.Bonuses;
if (levelBonuses is null || levelBonuses.Count == 0)
{
continue;
}
// Iterate over each bonus for the areas level
foreach (var bonus in levelBonuses) {
// Check if profile has bonus
var profileBonus = GetBonusFromProfile(pmcProfile.Bonuses, bonus);
if (profileBonus is null)
{
// no bonus, add to profile
_logger.Debug($"Profile has level {level} area {profileArea.Type} but no bonus found, adding { bonus.Type}");
_hideoutHelper.ApplyPlayerUpgradesBonuses(pmcProfile, bonus);
}
}
}
}
}
/**
@@ -698,9 +752,24 @@ public class ProfileFixerService
* @param bonus bonus to find
* @returns matching bonus
*/
protected Bonus GetBonusFromProfile(List<Bonus> profileBonuses, StageBonus bonus)
protected Bonus? GetBonusFromProfile(List<Bonus> profileBonuses, Bonus bonus)
{
throw new NotImplementedException();
// match by id first, used by "TextBonus" bonuses
if (bonus.Id is null)
{
return profileBonuses.FirstOrDefault((x) => x.Id == bonus.Id);
}
return bonus.Type switch
{
BonusType.StashSize => profileBonuses.FirstOrDefault(
(x) => x.Type == bonus.Type && x.TemplateId == bonus.TemplateId
),
BonusType.AdditionalSlots => profileBonuses.FirstOrDefault(
(x) => x.Type == bonus.Type && x.Value == bonus.Value && x.IsVisible == bonus.IsVisible
),
_ => profileBonuses.FirstOrDefault((x) => x.Type == bonus.Type && x.Value == bonus.Value)
};
}
public void CheckForAndRemoveInvalidTraders(SptProfile fullProfile)
+4 -4
View File
@@ -374,8 +374,8 @@ public class SeasonalEventService
_logger.Warning(
_localisationService.GetText("seasonal-missing_equipment_slot_on_bot", new
{
EquipmentSlot = equipmentSlotKey,
BotRole = botRole,
equipmentSlot = equipmentSlotKey,
botRole = botRole,
})
);
}
@@ -395,8 +395,8 @@ public class SeasonalEventService
_logger.Warning(
_localisationService.GetText("seasonal-missing_loot_container_slot_on_bot", new
{
LootContainer = lootContainerKey,
BotRole = botRole,
lootContainer = lootContainerKey,
botRole = botRole,
})
);
}