4 using System.Collections.Generic;
13 None = 0,
Any = 1,
RightHand = 2,
LeftHand = 4,
Head = 8,
InnerClothes = 16,
OuterClothes = 32,
Headset = 64,
Card = 128,
Bag = 256,
HealthInterface = 512
66 return slotString ==
null ? Array.Empty<
string>() : slotString.Split(
',');
70 : base(character, ParseSlotTypes(element).Length)
72 this.character = character;
79 string[] slotTypeNames = ParseSlotTypes(element);
80 System.Diagnostics.Debug.Assert(slotTypeNames.Length ==
capacity);
85 slotTypeNames[i] = slotTypeNames[i].Trim();
86 if (!Enum.TryParse(slotTypeNames[i], out parsedSlotType))
88 DebugConsole.ThrowError(
"Error in the inventory config of \"" + character.
SpeciesName +
"\" - " + slotTypeNames[i] +
" is not a valid inventory slot type.",
101 InitProjSpecific(element);
103 var itemElements = element.
Elements().Where(e => e.Name.ToString().Equals(
"item", StringComparison.OrdinalIgnoreCase));
104 int itemCount = itemElements.Count();
107 DebugConsole.ThrowError($
"Character \"{character.SpeciesName}\" is configured to spawn with more items than it has inventory capacity for.");
112 DebugConsole.ThrowError(
113 $
"Character \"{character.SpeciesName}\" is configured to spawn with so many items it will have less than 2 free inventory slots. " +
114 "This can cause issues with talents that spawn extra loot in monsters' inventories."
115 +
" Consider increasing the inventory size.",
120 if (!spawnInitialItems) {
return; }
127 foreach (var subElement
in itemElements)
129 string itemIdentifier = subElement.GetAttributeString(
"identifier",
"");
132 DebugConsole.ThrowError(
"Error in character inventory \"" + character.SpeciesName +
"\" - item \"" + itemIdentifier +
"\" not found.",
137 string slotString = subElement.GetAttributeString(
"slot",
"None");
140 bool forceToSlot = subElement.GetAttributeBool(
"forcetoslot",
false);
141 int amount = subElement.GetAttributeInt(
"amount", 1);
142 for (
int i = 0; i < amount; i++)
148 string errorMsg = $
"Failed to spawn the initial item \"{item.Prefab.Identifier}\" in the inventory of \"{character.SpeciesName}\".";
149 DebugConsole.ThrowError(errorMsg, contentPackage: element.ContentPackage);
150 GameAnalyticsManager.AddErrorEventOnce(
"CharacterInventory:FailedToSpawnInitialItem", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
152 else if (!character.Enabled)
154 foreach (var heldItem in character.HeldItems)
156 if (item.body != null) { item.body.Enabled = false; }
164 partial
void InitProjSpecific(XElement element);
168 if (tag.IsEmpty) {
return null; }
169 for (
int i = 0; i < slots.Length; i++)
173 var item = slots[i].FirstOrDefault();
174 if (item !=
null && item.HasTag(tag)) {
return item; }
181 for (
int i = 0; i < slots.Length; i++)
183 if (SlotTypes[i] == limbSlot) {
return i; }
190 for (
int i = 0; i < slots.Length; i++)
192 if (SlotTypes[i] == limbSlot) {
return slots[i].FirstOrDefault(); }
202 int rightHandSlot = FindLimbSlot(
InvSlotType.RightHand);
203 int leftHandSlot = FindLimbSlot(
InvSlotType.LeftHand);
204 if (rightHandSlot > -1 && slots[rightHandSlot].Contains(item) &&
205 leftHandSlot > -1 && slots[leftHandSlot].Contains(item))
211 for (
int i = 0; i < slots.Length; i++)
213 if (SlotTypes[i] == limbSlot && slots[i].Contains(item)) {
return true; }
221 base.CanBePutInSlot(item, i, ignoreCondition) && item.
AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) &&
222 (SlotTypes[i] ==
InvSlotType.Any || slots[i].Items.Count < 1);
228 base.CanBePutInSlot(itemPrefab, i, condition, quality) &&
229 (SlotTypes[i] ==
InvSlotType.Any || slots[i].Items.Count < 1);
234 RemoveItem(item, tryEquipFromSameStack:
false);
239 if (!Contains(item)) {
return; }
241 bool wasEquipped = character.HasEquippedItem(item);
242 var indices = FindIndices(item);
244 base.RemoveItem(item);
249 character.SelectedItem?.GetComponent<CircuitBox>()?.OnViewUpdateProjSpecific();
254 if (tryEquipFromSameStack && wasEquipped)
256 int limbSlot = indices.Find(j => SlotTypes[j] !=
InvSlotType.Any);
257 foreach (
int i
in indices)
259 var itemInSameSlot = GetItemAt(i);
260 if (itemInSameSlot !=
null)
262 if (TryPutItem(itemInSameSlot, limbSlot, allowSwapping:
false, allowCombine:
false, character))
265 visualSlots[i].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.412f);
283 var wearable = item.GetComponent<
Wearable>();
284 if (wearable !=
null && !wearable.AutoEquipWhenFull && !IsAnySlotAvailable(item))
290 if (allowedSlots !=
null && allowedSlots.Any() && !allowedSlots.Contains(
InvSlotType.Any))
292 bool allSlotsTaken =
true;
293 foreach (var allowedSlot
in allowedSlots)
297 int rightHandSlot = FindLimbSlot(
InvSlotType.RightHand);
298 int leftHandSlot = FindLimbSlot(
InvSlotType.LeftHand);
299 if (rightHandSlot > -1 && slots[rightHandSlot].CanBePut(item) &&
300 leftHandSlot > -1 && slots[leftHandSlot].CanBePut(item))
302 allSlotsTaken =
false;
308 int slot = FindLimbSlot(allowedSlot);
309 if (slot > -1 && slots[slot].CanBePut(item))
311 allSlotsTaken =
false;
319 int slot = FindLimbSlot(allowedSlots.First());
320 if (slot > -1 && slots[slot].Items.Any(it => it != item) && slots[slot].First().AllowDroppingOnSwapWith(item))
322 foreach (
Item existingItem
in slots[slot].Items.ToList())
324 existingItem.
Drop(user);
331 return TryPutItem(item, user, allowedSlots, createNetworkEvent);
337 public override bool TryPutItem(
Item item,
Character user, IEnumerable<InvSlotType> allowedSlots =
null,
bool createNetworkEvent =
true,
bool ignoreCondition =
false)
339 if (allowedSlots ==
null || !allowedSlots.Any()) {
return false; }
343 throw new Exception(
"item null");
351 throw new Exception(
"Tried to put a removed item (" + item.
Name +
") in an inventory");
353 DebugConsole.ThrowError(
"Tried to put a removed item (" + item.
Name +
") in an inventory.\n" + Environment.StackTrace.CleanupStackTrace());
360 bool inSuitableSlot =
false;
361 bool inWrongSlot =
false;
362 int currentSlot = -1;
363 for (
int i = 0; i < capacity; i++)
365 if (slots[i].Contains(item))
368 if (allowedSlots.Any(a => a.HasFlag(SlotTypes[i])))
370 if ((SlotTypes[i] ==
InvSlotType.RightHand || SlotTypes[i] ==
InvSlotType.LeftHand) && !allowedSlots.Contains(SlotTypes[i]))
378 inSuitableSlot =
true;
381 else if (!allowedSlots.Any(a => a.HasFlag(SlotTypes[i])))
389 if (inSuitableSlot && !inWrongSlot) {
return true; }
394 int freeIndex = GetFreeAnySlot(item, inWrongSlot);
397 PutItem(item, freeIndex, user,
true, createNetworkEvent);
403 int placedInSlot = -1;
406 if (allowedSlot.HasFlag(
InvSlotType.RightHand) && character.AnimController.GetLimb(
LimbType.RightHand) ==
null) {
continue; }
407 if (allowedSlot.HasFlag(
InvSlotType.LeftHand) && character.AnimController.GetLimb(
LimbType.LeftHand) ==
null) {
continue; }
411 for (
int i = 0; i < capacity; i++)
413 if (allowedSlot.HasFlag(SlotTypes[i]) && item.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) && slots[i].Items.Any(it => it != item))
415 if (!slots[i].First().AllowedSlots.Contains(
InvSlotType.Any) || !TryPutItem(slots[i].FirstOrDefault(), character,
new List<InvSlotType> {
InvSlotType.Any },
true, ignoreCondition))
419 for (
int j = 0; j < capacity; j++)
421 if (visualSlots !=
null && slots[j] == slots[i]) { visualSlots[j].ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f); }
428 if (!free) {
continue; }
430 for (
int i = 0; i < capacity; i++)
432 if (allowedSlot.HasFlag(SlotTypes[i]) && item.GetComponents<
Pickable>().Any(p => p.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i]))) && slots[i].Empty())
434 bool removeFromOtherSlots = item.ParentInventory !=
this;
435 if (placedInSlot == -1 && inWrongSlot)
437 if (!slots[i].HideIfEmpty || SlotTypes[currentSlot] !=
InvSlotType.Any) { removeFromOtherSlots =
true; }
440 PutItem(item, i, user, removeFromOtherSlots, createNetworkEvent);
441 item.Equip(character);
445 if (placedInSlot > -1) {
break; }
448 return placedInSlot > -1;
453 private int GetFreeAnySlot(
Item item,
bool inWrongSlot)
456 for (
int i = 0; i < capacity; i++)
459 if (!slots[i].Empty() && CanBePutInSlot(item, i))
464 for (
int i = 0; i < capacity; i++)
467 if (slots[i].Contains(item))
472 for (
int i = 0; i < capacity; i++)
475 if (CanBePutInSlot(item, i))
480 for (
int i = 0; i < capacity; i++)
486 if (slots[i].
Any() && slots[i].Items.Any(it => it != item)) {
continue; }
490 if (!CanBePutInSlot(item, i)) {
continue; }
497 public override bool TryPutItem(
Item item,
int index,
bool allowSwapping,
bool allowCombine,
Character user,
bool createNetworkEvent =
true,
bool ignoreCondition =
false)
499 if (index < 0 || index >= slots.Length)
501 string errorMsg =
"CharacterInventory.TryPutItem failed: index was out of range(" + index +
").\n" + Environment.StackTrace.CleanupStackTrace();
502 GameAnalyticsManager.AddErrorEventOnce(
"CharacterInventory.TryPutItem:IndexOutOfRange", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
506 if (slots[index].
Any())
508 if (slots[index].Contains(item)) {
return false; }
509 return base.TryPutItem(item, index, allowSwapping, allowCombine, user, createNetworkEvent, ignoreCondition);
514 if (!item.GetComponents<
Pickable>().Any(p => p.AllowedSlots.Contains(
InvSlotType.Any))) {
return false; }
515 if (slots[index].
Any()) {
return slots[index].Contains(item); }
516 PutItem(item, index, user,
true, createNetworkEvent);
522 bool slotsFree =
true;
527 if (!allowedSlot.HasFlag(SlotTypes[index])) {
continue; }
528 for (
int i = 0; i < capacity; i++)
530 if (allowedSlot.HasFlag(SlotTypes[i]) && slots[i].Any() && !slots[i].Contains(item))
535 placeToSlots = allowedSlot;
540 if (!slotsFree) {
return false; }
542 return TryPutItem(item, user,
new List<InvSlotType>() { placeToSlots }, createNetworkEvent, ignoreCondition);
545 protected override void PutItem(
Item item,
int i,
Character user,
bool removeItem =
true,
bool createNetworkEvent =
true)
547 base.PutItem(item, i, user, removeItem, createNetworkEvent);
552 HintManager.OnObtainedItem(character, item);
553 character.SelectedItem?.GetComponent<CircuitBox>()?.OnViewUpdateProjSpecific();
static Character? Controlled
bool IsAnySlotAvailable(Item item)
override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition, int? quality=null)
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
AccessLevel
How much access other characters have to the inventory? Restricted = Only accessible when character i...
bool AccessibleWhenAlive
Can the inventory be accessed when the character is still alive
static bool IsHandSlotType(InvSlotType s)
override void CreateNetworkEvent(Range slotRange)
Item GetItemInLimbSlot(InvSlotType limbSlot)
void RemoveItem(Item item, bool tryEquipFromSameStack)
bool AccessibleByOwner
Can the inventory be accessed by the character itself when the character is still alive (only has an ...
int FindLimbSlot(InvSlotType limbSlot)
static readonly List< InvSlotType > AnySlot
override void RemoveItem(Item item)
override void PutItem(Item item, int i, Character user, bool removeItem=true, bool createNetworkEvent=true)
Item FindEquippedItemByTag(Identifier tag)
bool TryPutItemWithAutoEquipCheck(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true)
If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equippe...
override bool CanBePutInSlot(Item item, int i, bool ignoreCondition=false)
Can the item be put in the specified slot.
override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent=true, bool ignoreCondition=false)
CharacterInventory(ContentXElement element, Character character, bool spawnInitialItems)
bool IsInLimbSlot(Item item, InvSlotType limbSlot)
string? GetAttributeString(string key, string? def)
ContentPackage? ContentPackage
IEnumerable< ContentXElement > Elements()
bool GetAttributeBool(string key, bool def)
static EntitySpawner Spawner
void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition=null, int? quality=null, Action< Item > onSpawned=null)
static NetworkMember NetworkMember
readonly int capacity
Capacity, or the number of slots in the inventory.
readonly ItemSlot[] slots
virtual void RemoveItem(Item item)
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
void Unequip(Character character)
Inventory ParentInventory
IEnumerable< InvSlotType > AllowedSlots
void AssignCampaignInteractionType(CampaignMode.InteractionType interactionType, IEnumerable< Client > targetClients=null)
Character Equipper
Which character equipped this item? May not be the same character as the one who it's equipped on (yo...
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
CampaignMode.InteractionType CampaignInteractionType
static readonly PrefabCollection< ItemPrefab > Prefabs
List< InvSlotType > AllowedSlots