diff --git a/Core/Helpers/ItemHelper.cs b/Core/Helpers/ItemHelper.cs index 9fd605ef..f73341df 100644 --- a/Core/Helpers/ItemHelper.cs +++ b/Core/Helpers/ItemHelper.cs @@ -173,7 +173,7 @@ public class ItemHelper // @returns Does item have the possibility ot need soft inserts public bool ArmorItemCanHoldMods(string itemTpl) { - throw new NotImplementedException(); + return IsOfBaseclasses(itemTpl, [BaseClasses.HEADWEAR, BaseClasses.VEST, BaseClasses.ARMOR]); } // Does the provided item tpl need soft/removable inserts to function @@ -274,17 +274,23 @@ public class ItemHelper /// List of TemplateItem objects public List GetItems() { - throw new NotImplementedException(); + return _cloner.Clone(_databaseService.GetItems().Values).ToList(); } /** - * Gets item data from items.json - * @param tpl items template id to look up - * @returns bool - is valid + template item object as array - */ - public KeyValuePair GetItem(string tpl) + * Gets item data from items.json + * @param tpl items template id to look up + * @returns bool - is valid + template item object as array + */ + public KeyValuePair GetItem(string tpl) { - throw new NotImplementedException(); + // -> Gets item from + if (_databaseService.GetItems().Keys.Contains(tpl)) + { + return new(true, _databaseService.GetItems()[tpl]); + } + + return new(false, null); } /** @@ -294,7 +300,7 @@ public class ItemHelper */ public bool ItemHasSlots(string itemTpl) { - throw new NotImplementedException(); + return GetItem(itemTpl).Value.Properties.Slots?.Count() > 0; } /** @@ -354,7 +360,19 @@ public class ItemHelper */ public List FindAndReturnChildrenByItems(List items, string baseItemId) { - throw new NotImplementedException(); + List list = []; + + foreach (var childitem in items) + { + if (childitem.ParentId == baseItemId) + { + list.AddRange(FindAndReturnChildrenByItems(items, childitem.Id)); + } + } + + list.Add(baseItemId); // Required, push original item id onto array + + return list; } /** @@ -366,7 +384,30 @@ public class ItemHelper */ public List FindAndReturnChildrenAsItems(List items, string baseItemId, bool modsOnly = false) { - throw new NotImplementedException(); + List list = []; + foreach (var childItem in items) + { + // Include itself + if (childItem.Id == baseItemId) + { + list.Insert(0, childItem); + continue; + } + + // Is stored in parent and disallowed + if (modsOnly && childItem.Location is not null) + { + continue; + } + + // Items parentid matches root item AND returned items doesnt contain current child + if (childItem.ParentId == baseItemId && !list.Any((item) => childItem.Id == item.Id)) + { + list.AddRange(FindAndReturnChildrenAsItems(items, childItem.Id)); + } + } + + return list; } /** @@ -407,7 +448,14 @@ public class ItemHelper /// SlotId OR slotid, locationX, locationY. public string GetChildId(Item item) { - throw new NotImplementedException(); + if (item.Location is null) + { + return item.SlotId; + } + + var LocationTyped = (ItemLocation)item.Location; + + return $"{item.SlotId},{LocationTyped.X},{LocationTyped.Y}"; } /// @@ -427,7 +475,36 @@ public class ItemHelper /// List of root item + children. public List SplitStack(Item itemToSplit) { - throw new NotImplementedException(); + if (itemToSplit?.Upd?.StackObjectsCount is null) + { + return [itemToSplit]; + } + + var maxStackSize = GetItem(itemToSplit.Template).Value.Properties.StackMaxSize; + var remainingCount = itemToSplit.Upd.StackObjectsCount; + List rootAndChildren = []; + + // If the current count is already equal or less than the max + // return the item as is. + if (remainingCount <= maxStackSize) + { + rootAndChildren.Add(_cloner.Clone(itemToSplit)); + + return rootAndChildren; + } + + while (remainingCount.Value != 0) + { + var amount = Math.Min(remainingCount ?? 0, maxStackSize ?? 0); + var newStackClone = _cloner.Clone(itemToSplit); + + newStackClone.Id = _hashUtil.Generate(); + newStackClone.Upd.StackObjectsCount = amount; + remainingCount -= amount; + rootAndChildren.Add(newStackClone); + } + + return rootAndChildren; } /// @@ -682,10 +759,12 @@ public class ItemHelper // Check to see if the slot that the item is attached to is marked as required in the parent item's template. var isRequiredSlot = false; if (parentTemplate.Key && parentTemplate.Value?.Properties?.Slots != null) - isRequiredSlot = parentTemplate.Value?.Properties?.Slots?.Any(slot => - slot?.Name == item?.SlotId && - (slot?.Required ?? false) - ) ?? false; + isRequiredSlot = parentTemplate.Value?.Properties?.Slots?.Any( + slot => + slot?.Name == item?.SlotId && + (slot?.Required ?? false) + ) ?? + false; return itemTemplate.Key && parentTemplate.Key && (isNotRaidModdable || isRequiredSlot); } @@ -720,11 +799,11 @@ public class ItemHelper } /** - * Determines if an item is an attachment that is currently attached to its parent item. - * - * @param item The item to check. - * @returns true if the item is attached attachment, otherwise false. - */ + * Determines if an item is an attachment that is currently attached to its parent item. + * + * @param item The item to check. + * @returns true if the item is attached attachment, otherwise false. + */ public bool IsAttachmentAttached(Item item) { // TODO: actually implement @@ -779,7 +858,48 @@ public class ItemHelper */ public void AddCartridgesToAmmoBox(List ammoBox, TemplateItem ammoBoxDetails) { - throw new NotImplementedException(); + var ammoBoxMaxCartridgeCount = ammoBoxDetails.Properties.StackSlots[0].MaxCount; + var cartridgeTpl = ammoBoxDetails.Properties.StackSlots[0].Props.Filters[0].Filter[0]; + var cartridgeDetails = GetItem(cartridgeTpl); + var cartridgeMaxStackSize = cartridgeDetails.Value.Properties.StackMaxSize; + + // Exit if ammo already exists in box + if (ammoBox.Any((item) => item.Template == cartridgeTpl)) + { + return; + } + + // Add new stack-size-correct items to ammo box + double? currentStoredCartridgeCount = 0; + var maxPerStack = Math.Min(ammoBoxMaxCartridgeCount ?? 0, cartridgeMaxStackSize ?? 0); + // Find location based on Max ammo box size + var location = Math.Ceiling(ammoBoxMaxCartridgeCount / maxPerStack ?? 0) - 1; + + while (currentStoredCartridgeCount < ammoBoxMaxCartridgeCount) + { + var remainingSpace = ammoBoxMaxCartridgeCount - currentStoredCartridgeCount; + var cartridgeCountToAdd = remainingSpace < maxPerStack ? remainingSpace : maxPerStack; + + // Add cartridge item into items array + var cartridgeItemToAdd = CreateCartridges( + ammoBox[0].Id, + cartridgeTpl, + cartridgeCountToAdd ?? 0, + location, + ammoBox[0].Upd?.SpawnedInSession ?? false + ); + + // In live no ammo box has the first cartridge item with a location + if (location == 0) + { + cartridgeItemToAdd.Location = null; + } + + ammoBox.Add(cartridgeItemToAdd); + + currentStoredCartridgeCount += cartridgeCountToAdd; + location--; + } } /** @@ -840,7 +960,78 @@ public class ItemHelper double minSizeMultiplier = 0.25 ) { - throw new NotImplementedException(); + // Get cartridge properties and max allowed stack size + var cartridgeDetails = GetItem(cartridgeTpl); + if (!cartridgeDetails.Key) + { + _logger.Error(_localisationService.GetText("item-invalid_tpl_item", cartridgeTpl)); + } + + var cartridgeMaxStackSize = cartridgeDetails.Value.Properties?.StackMaxSize; + if (cartridgeMaxStackSize is null) + { + _logger.Error($"Item with tpl: {cartridgeTpl} lacks a _props or StackMaxSize property"); + } + + // Get max number of cartridges in magazine, choose random value between min/max + var magazineCartridgeMaxCount = IsOfBaseclass(magTemplate.Id, BaseClasses.SPRING_DRIVEN_CYLINDER) + ? magTemplate.Properties.Slots.Count() // Edge case for rotating grenade launcher magazine + : magTemplate.Properties.Cartridges[0]?.MaxCount; + + if (magazineCartridgeMaxCount is null) + { + _logger.Warning($"Magazine: {magTemplate.Id} {magTemplate.Name} lacks a Cartridges array, unable to fill magazine with ammo"); + + return; + } + + var desiredStackCount = _randomUtil.GetInt( + (int) + Math.Round(minSizeMultiplier * magazineCartridgeMaxCount ?? 0), + (int)magazineCartridgeMaxCount + ); + + if (magazineWithChildCartridges.Count() > 1) + { + _logger.Warning($"Magazine {magTemplate.Name} already has cartridges defined, this may cause issues"); + } + + // Loop over cartridge count and add stacks to magazine + double? currentStoredCartridgeCount = 0; + var location = 0; + while (currentStoredCartridgeCount < desiredStackCount) + { + // Get stack size of cartridges + var cartridgeCountToAdd = + desiredStackCount <= cartridgeMaxStackSize ? desiredStackCount : cartridgeMaxStackSize; + + // Ensure we don't go over the max stackcount size + var remainingSpace = desiredStackCount - currentStoredCartridgeCount; + if (cartridgeCountToAdd > remainingSpace) + { + cartridgeCountToAdd = remainingSpace; + } + + // Add cartridge item object into items array + magazineWithChildCartridges.Add( + CreateCartridges( + magazineWithChildCartridges[0].Id, + cartridgeTpl, + cartridgeCountToAdd ?? 0, + location, + magazineWithChildCartridges[0].Upd?.SpawnedInSession ?? false + ) + ); + + currentStoredCartridgeCount += cartridgeCountToAdd; + location++; + } + + // Only one cartridge stack added, remove location property as its only used for 2 or more stacks + if (location == 1) + { + magazineWithChildCartridges[1].Location = null; + } } /// @@ -883,12 +1074,19 @@ public class ItemHelper public Item CreateCartridges( string parentId, string ammoTpl, - int stackCount, - int location, + double stackCount, + double location, bool foundInRaid = false ) { - throw new NotImplementedException(); + return new () { + Id = _hashUtil.Generate(), + Template = ammoTpl, + ParentId = parentId, + SlotId = "cartridges", + Location = location, + Upd = new () { StackObjectsCount = stackCount, SpawnedInSession = foundInRaid }, + }; } /// @@ -961,16 +1159,16 @@ public class ItemHelper } // Get a list of slot names that hold removable plates -// Returns Array of slot ids (e.g. front_plate) + // Returns Array of slot ids (e.g. front_plate) public List GetRemovablePlateSlotIds() { throw new NotImplementedException(); } -// Generate new unique ids for child items while preserving hierarchy -// Base/primary item -// Primary item + children of primary item -// Returns Item array with updated IDs + // Generate new unique ids for child items while preserving hierarchy + // Base/primary item + // Primary item + children of primary item + // Returns Item array with updated IDs public List ReparentItemAndChildren(Item rootItem, List itemWithChildren) { throw new NotImplementedException(); @@ -999,35 +1197,35 @@ public class ItemHelper throw new NotImplementedException(); } -// Populate a Map object of items for quick lookup using their ID. -// -// An array of Items that should be added to a Map. -// Returns A Map where the keys are the item IDs and the values are the corresponding Item objects. + // Populate a Map object of items for quick lookup using their ID. + // + // An array of Items that should be added to a Map. + // Returns A Map where the keys are the item IDs and the values are the corresponding Item objects. public Dictionary GenerateItemsMap(List items) { throw new NotImplementedException(); } -// Add a blank upd object to passed in item if it does not exist already -// item to add upd to -// text to write to log when upd object was not found -// Returns True when upd object was added + // Add a blank upd object to passed in item if it does not exist already + // item to add upd to + // text to write to log when upd object was not found + // Returns True when upd object was added public bool AddUpdObjectToItem(Item item, string warningMessageWhenMissing = null) { throw new NotImplementedException(); } -// Return all tpls from Money enum -// Returns string tpls + // Return all tpls from Money enum + // Returns string tpls public List GetMoneyTpls() { throw new NotImplementedException(); } -// Get a randomised stack size for the passed in ammo -// Ammo to get stack size for -// Default: Limit to 60 to prevent crazy values when players use stack increase mods -// Returns number + // Get a randomised stack size for the passed in ammo + // Ammo to get stack size for + // Default: Limit to 60 to prevent crazy values when players use stack increase mods + // Returns number public int GetRandomisedAmmoStackSize(TemplateItem ammoItemTemplate, int maxLimit = 60) { throw new NotImplementedException(); @@ -1038,8 +1236,8 @@ public class ItemHelper throw new NotImplementedException(); } -// Remove FiR status from passed in items -// Items to update FiR status of + // Remove FiR status from passed in items + // Items to update FiR status of public void RemoveSpawnedInSessionPropertyFromItems(List items) { throw new NotImplementedException(); @@ -1050,7 +1248,7 @@ public class ItemHelper return _itemBaseClassService.ItemHasBaseClass(tpl, [baseClassTpl]); } - public bool isOfBaseclasses(string tpl, List baseClassTpls) + public bool IsOfBaseclasses(string tpl, List baseClassTpls) { return _itemBaseClassService.ItemHasBaseClass(tpl, baseClassTpls); }