finish off botGenHelper
This commit is contained in:
@@ -325,7 +325,158 @@ public class BotGeneratorHelper
|
||||
/// <returns>false if no incompatibilities, also has incompatibility reason</returns>
|
||||
public ChooseRandomCompatibleModResult IsItemIncompatibleWithCurrentItems(List<Item> itemsEquipped, string tplToCheck, string equipmentSlot)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Skip slots that have no incompatibilities
|
||||
List<string> slotsToCheck = ["Scabbard", "Backpack", "SecureContainer", "Holster", "ArmBand"];
|
||||
if (slotsToCheck.Contains(equipmentSlot))
|
||||
{
|
||||
return new() { Incompatible = false, Found = false, Reason = "" };
|
||||
}
|
||||
|
||||
// TODO: Can probably be optimized to cache itemTemplates as items are added to inventory
|
||||
var equippedItemsDb = itemsEquipped.Select((equippedItem) => _itemHelper.GetItem(equippedItem.Template).Value);
|
||||
var itemToEquipDb = _itemHelper.GetItem(tplToCheck);
|
||||
var itemToEquip = itemToEquipDb.Value;
|
||||
|
||||
if (!itemToEquipDb.Key)
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService.GetText(
|
||||
"bot-invalid_item_compatibility_check",
|
||||
new
|
||||
{
|
||||
itemTpl = tplToCheck,
|
||||
slot = equipmentSlot,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return new() { Incompatible = true, Found = false, Reason = $"item: {tplToCheck} does not exist in the database" };
|
||||
}
|
||||
|
||||
if (itemToEquip.Properties is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService.GetText(
|
||||
"bot-compatibility_check_missing_props",
|
||||
new
|
||||
{
|
||||
id = itemToEquip.Id,
|
||||
name = itemToEquip.Name,
|
||||
slot = equipmentSlot,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return new() { Incompatible = true, Found = false, Reason = $"item: {tplToCheck} does not have a _props field" };
|
||||
}
|
||||
|
||||
// Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover
|
||||
var blockingItem = equippedItemsDb.FirstOrDefault(
|
||||
(item) => item.Properties.GetType().GetProperties().FirstOrDefault(x => x.Name.ToLower() == $"blocks{equipmentSlot}").GetValue(item) is not null
|
||||
);
|
||||
if (blockingItem is not null)
|
||||
{
|
||||
// this.logger.warning(`1 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._name} - ${equipmentSlot}`);
|
||||
|
||||
return new()
|
||||
{
|
||||
Incompatible = true, Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} in slot: {equipmentSlot} blocked by: {blockingItem.Id} {blockingItem.Name}", SlotBlocked = true
|
||||
};
|
||||
}
|
||||
|
||||
// Check if any of the current inventory templates have the incoming item defined as incompatible
|
||||
blockingItem = equippedItemsDb.FirstOrDefault((x) => x.Properties.ConflictingItems?.Contains(tplToCheck) ?? false);
|
||||
if (blockingItem is not null)
|
||||
{
|
||||
// this.logger.warning(`2 incompatibility found between - ${itemToEquip[1]._name} and ${blockingItem._props.Name} - ${equipmentSlot}`);
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} in slot: {equipmentSlot} blocked by: {blockingItem.Id} {blockingItem.Name}",
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip.Properties.BlocksHeadwear ?? false)
|
||||
{
|
||||
var existingHeadwear = itemsEquipped.FirstOrDefault((x) => x.SlotId == "Headwear");
|
||||
if (existingHeadwear is not null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingHeadwear.Template} in slot: {existingHeadwear.SlotId}",
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip.Properties.BlocksFaceCover ?? false)
|
||||
{
|
||||
var existingFaceCover = itemsEquipped.FirstOrDefault((item) => item.SlotId == "FaceCover");
|
||||
if (existingFaceCover is not null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingFaceCover.Template} in slot: {existingFaceCover.SlotId}",
|
||||
SlotBlocked = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip.Properties.BlocksEarpiece ?? false)
|
||||
{
|
||||
var existingEarpiece = itemsEquipped.FirstOrDefault((item) => item.SlotId == "Earpiece");
|
||||
if (existingEarpiece is not null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingEarpiece.Template} in slot: {existingEarpiece.SlotId}",
|
||||
SlotBlocked = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip.Properties.BlocksArmorVest is not null)
|
||||
{
|
||||
var existingArmorVest = itemsEquipped.FirstOrDefault((item) => item.SlotId == "ArmorVest");
|
||||
if (existingArmorVest is not null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingArmorVest.Template} in slot: {existingArmorVest.SlotId}",
|
||||
SlotBlocked = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the incoming item has any inventory items defined as incompatible
|
||||
var blockingInventoryItem = itemsEquipped.FirstOrDefault((x) => itemToEquip.Properties.ConflictingItems?.Contains(x.Template) ?? false);
|
||||
if (blockingInventoryItem is not null)
|
||||
{
|
||||
// this.logger.warning(`3 incompatibility found between - ${itemToEquip[1]._name} and ${blockingInventoryItem._tpl} - ${equipmentSlot}`)
|
||||
return new()
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} blocks existing item {blockingInventoryItem.Template} in slot {blockingInventoryItem.SlotId}",
|
||||
};
|
||||
}
|
||||
|
||||
return new() { Incompatible = false, Reason = "" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -360,7 +511,143 @@ public class BotGeneratorHelper
|
||||
BotBaseInventory inventory,
|
||||
HashSet<string> containersIdFull = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
/** Track how many containers are unable to be found */
|
||||
var missingContainerCount = 0;
|
||||
foreach (var equipmentSlotId in equipmentSlots)
|
||||
{
|
||||
if (containersIdFull?.Contains(equipmentSlotId) ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get container to put item into
|
||||
var container = inventory.Items.FirstOrDefault((item) => item.SlotId == equipmentSlotId);
|
||||
if (container is null)
|
||||
{
|
||||
missingContainerCount++;
|
||||
if (missingContainerCount == equipmentSlots.Count)
|
||||
{
|
||||
// Bot doesnt have any containers we want to add item to
|
||||
_logger.Debug(
|
||||
$"Unable to add item: {itemWithChildren[0].Template} to bot as it lacks the following containers: {string.Join(",", equipmentSlots)}"
|
||||
);
|
||||
|
||||
return ItemAddedResult.NO_CONTAINERS;
|
||||
}
|
||||
|
||||
// No container of desired type found, skip to next container type
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get container details from db
|
||||
var containerTemplate = _itemHelper.GetItem(container.Template);
|
||||
if (!containerTemplate.Key)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("bot-missing_container_with_tpl", container.Template));
|
||||
|
||||
// Bad item, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!containerTemplate.Value.Properties.Grids?.Any() ?? false)
|
||||
{
|
||||
// Container has no slots to hold items
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get x/y grid size of item
|
||||
var itemSize = _inventoryHelper.GetItemSize(rootItemTplId, rootItemId, itemWithChildren);
|
||||
|
||||
// Iterate over each grid in the container and look for a big enough space for the item to be placed in
|
||||
var currentGridCount = 1;
|
||||
var totalSlotGridCount = containerTemplate.Value.Properties.Grids.Count;
|
||||
foreach (var slotGrid in containerTemplate.Value.Properties.Grids)
|
||||
{
|
||||
// Grid is empty, skip or item size is bigger than grid
|
||||
if (slotGrid.Props.CellsH == 0 || slotGrid.Props.CellsV == 0 || itemSize[0] * itemSize[1] > slotGrid.Props.CellsV * slotGrid.Props.CellsH)
|
||||
continue;
|
||||
|
||||
// Can't put item type in grid, skip all grids as we're assuming they have the same rules
|
||||
if (!ItemAllowedInContainer(slotGrid, rootItemTplId))
|
||||
{
|
||||
// Multiple containers, maybe next one allows item, only break out of loop for this containers grids
|
||||
break;
|
||||
}
|
||||
|
||||
// Get all root items in found container
|
||||
var existingContainerItems = inventory.Items.Where(
|
||||
(item) => item.ParentId == container.Id && item.SlotId == slotGrid.Name
|
||||
)
|
||||
.ToList();
|
||||
|
||||
// Get root items in container we can iterate over to find out what space is free
|
||||
var containerItemsToCheck = existingContainerItems.Where((x) => x.SlotId == slotGrid.Name);
|
||||
foreach (var item in containerItemsToCheck)
|
||||
{
|
||||
// Look for children on items, insert into array if found
|
||||
// (used later when figuring out how much space weapon takes up)
|
||||
var itemWithChildrens = _itemHelper.FindAndReturnChildrenAsItems(inventory.Items, item.Id);
|
||||
if (itemWithChildrens.Count > 1)
|
||||
{
|
||||
existingContainerItems.Remove(item);
|
||||
existingContainerItems.AddRange(itemWithChildrens);
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of items free/used spots in current grid
|
||||
var slotGridMap = _inventoryHelper.GetContainerMap(
|
||||
slotGrid.Props.CellsH.GetValueOrDefault(),
|
||||
slotGrid.Props.CellsV.GetValueOrDefault(),
|
||||
existingContainerItems,
|
||||
container.Id
|
||||
);
|
||||
|
||||
// Try to fit item into grid
|
||||
var findSlotResult = _containerHelper.FindSlotForItem(slotGridMap, itemSize[0], itemSize[1]);
|
||||
|
||||
// Open slot found, add item to inventory
|
||||
if (findSlotResult.Success ?? false)
|
||||
{
|
||||
var parentItem = itemWithChildren.FirstOrDefault((i) => i.Id == rootItemId);
|
||||
|
||||
// Set items parent to container id
|
||||
parentItem.ParentId = container.Id;
|
||||
parentItem.SlotId = slotGrid.Name;
|
||||
parentItem.Location = new ItemLocation()
|
||||
{
|
||||
X = findSlotResult.X,
|
||||
Y = findSlotResult.Y,
|
||||
R = (findSlotResult.Rotation ?? false) ? 1 : 0,
|
||||
}
|
||||
;
|
||||
|
||||
inventory.Items.AddRange(itemWithChildren);
|
||||
|
||||
return ItemAddedResult.SUCCESS;
|
||||
}
|
||||
|
||||
// If we've checked all grids in container and reached this point, there's no space for item
|
||||
if (currentGridCount >= totalSlotGridCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentGridCount++;
|
||||
// No space in this grid, move to next container grid and try again
|
||||
}
|
||||
|
||||
// if we got to this point, the item couldnt be placed on the container
|
||||
if (containersIdFull is not null)
|
||||
{
|
||||
// if the item was a one by one, we know it must be full. Or if the maps cant find a slot for a one by one
|
||||
if (itemSize[0] == 1 && itemSize[1] == 1)
|
||||
{
|
||||
containersIdFull.Add(equipmentSlotId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ItemAddedResult.NO_SPACE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -371,6 +658,40 @@ public class BotGeneratorHelper
|
||||
/// <returns>True if allowed</returns>
|
||||
protected bool ItemAllowedInContainer(Grid slotGrid, string itemTpl)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var propFilters = slotGrid.Props.Filters;
|
||||
var excludedFilter = propFilters[0]?.ExcludedFilter ?? [];
|
||||
var filter = propFilters[0]?.Filter ?? [];
|
||||
|
||||
if (propFilters.Count == 0)
|
||||
{
|
||||
// no filters, item is fine to add
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if item base type is excluded
|
||||
if (excludedFilter is not null || filter is not null)
|
||||
{
|
||||
var itemDetails = _itemHelper.GetItem(itemTpl).Value;
|
||||
|
||||
// if item to add is found in exclude filter, not allowed
|
||||
if (excludedFilter?.Contains(itemDetails.Parent) ?? false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If Filter array only contains 1 filter and its for basetype 'item', allow it
|
||||
if (filter?.Count == 1 && filter.Contains(BaseClasses.ITEM))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If allowed filter has something in it + filter doesnt have basetype 'item', not allowed
|
||||
if (filter?.Count > 0 && !filter.Contains(itemDetails.Parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public class InventoryHelper
|
||||
/// <param name="itemList">Players inventory items</param>
|
||||
/// <param name="containerId">Id of the container</param>
|
||||
/// <returns>Two-dimensional representation of container</returns>
|
||||
public List<List<int>> GetContainerMap(int containerH, int containerV, List<Item> itemList, string containerId)
|
||||
public List<List<int>> GetContainerMap(double containerH, double containerV, List<Item> itemList, string containerId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ public class Item
|
||||
|
||||
public class ItemLocation
|
||||
{
|
||||
public float? X { get; set; }
|
||||
public float? Y { get; set; }
|
||||
public double? X { get; set; }
|
||||
public double? Y { get; set; }
|
||||
public object? R { get; set; } // TODO: Can be string or number
|
||||
public bool? IsSearched { get; set; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user