(Feat) Item Dqta

This commit is contained in:
2026-06-25 14:40:21 +02:00
parent 3597bacb1a
commit 931bc9c9e2
10 changed files with 372 additions and 20 deletions
+171 -1
View File
@@ -104,7 +104,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Sprite: {fileID: 21300000, guid: 2237e4ae38354ff459a9b61781694de6, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
@@ -114,6 +114,171 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5573310796372301174
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6703316971934351753}
- component: {fileID: 4480407019944074660}
- component: {fileID: 2465922160796796964}
m_Layer: 5
m_Name: Image (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6703316971934351753
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5573310796372301174}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5909651071786952382}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
m_AnchoredPosition: {x: 0, y: -0.00010586}
m_SizeDelta: {x: 80.3081, y: 8.2002}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &4480407019944074660
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5573310796372301174}
m_CullTransparentMesh: 1
--- !u!114 &2465922160796796964
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5573310796372301174}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 0.4862745, g: 0.9882354, b: 0, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 89cc97401eb588a408315f5fa0148929, type: 3}
m_Type: 3
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 0
m_FillAmount: 0.76
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 2.8
--- !u!1 &5983078061427925415
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5909651071786952382}
- component: {fileID: 4758672237807855567}
- component: {fileID: 6423672678987582984}
- component: {fileID: 5188521778652969812}
m_Layer: 5
m_Name: Mask Durability
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &5909651071786952382
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5983078061427925415}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6703316971934351753}
m_Father: {fileID: 4729973786354734962}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
m_AnchoredPosition: {x: 0, y: 9.200001}
m_SizeDelta: {x: 80.3081, y: 8.2001}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &4758672237807855567
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5983078061427925415}
m_CullTransparentMesh: 1
--- !u!114 &6423672678987582984
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5983078061427925415}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 0.3056603, g: 0.3056603, b: 0.3056603, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: b3f0d976f6d6802479d6465d11b3aa68, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 7.79
--- !u!114 &5188521778652969812
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5983078061427925415}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Mask
m_ShowMaskGraphic: 1
--- !u!1 &6747954976634286854
GameObject:
m_ObjectHideFlags: 0
@@ -219,6 +384,7 @@ RectTransform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 850401354156706093}
- {fileID: 5909651071786952382}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -241,6 +407,10 @@ MonoBehaviour:
iconImage: {fileID: 6241670606430176389}
quantityText: {fileID: 2578968024406161774}
highlightImage: {fileID: 9078470808336705615}
usageBarRoot: {fileID: 5983078061427925415}
usageBarFill: {fileID: 2465922160796796964}
normalTint: {r: 0.4862745, g: 0.9882354, b: 0, a: 1}
depletedTint: {r: 1, g: 0, b: 0, a: 1}
--- !u!1 &8681194172915985056
GameObject:
m_ObjectHideFlags: 0
+49 -1
View File
@@ -1,31 +1,63 @@
namespace Ashwild.Inventory
{
/// <summary>
/// A single inventory cell: the item it holds, how many, and — for items that track uses
/// (tool durability, multi-bite food) — the remaining uses of this specific instance. Items
/// with uses never stack, so a slot's uses always describe one concrete instance.
/// </summary>
[System.Serializable]
public class InventorySlot
{
private ItemData itemData;
private int quantity;
private int currentUses;
public ItemData ItemData => itemData;
public int Quantity => quantity;
public int CurrentUses => currentUses;
public bool IsEmpty => itemData == null;
/// <summary>
/// Whether this slot holds a uses-tracked item whose uses have run out — kept in the
/// inventory (shown red and unusable) when the item is not destroyed on depletion.
/// </summary>
public bool IsDepleted => itemData != null && itemData.HasUses && currentUses <= 0;
/// <summary>
/// Places an item in the slot, resetting its uses to full for uses-tracked items.
/// </summary>
public void Set(ItemData data, int qty)
{
itemData = data;
quantity = qty;
currentUses = (data != null && data.HasUses) ? data.MaxUses : 0;
}
/// <summary>
/// Places an item with an explicit remaining-uses value, used when restoring a partially
/// used instance (e.g. picking a dropped tool back up). A negative value means "full".
/// </summary>
public void Set(ItemData data, int qty, int uses)
{
itemData = data;
quantity = qty;
if (data != null && data.HasUses)
currentUses = uses < 0 ? data.MaxUses : uses;
else
currentUses = 0;
}
public void Clear()
{
itemData = null;
quantity = 0;
currentUses = 0;
}
public int AddQuantity(int amount)
{
if (itemData == null || !itemData.IsStackable)
if (itemData == null || !itemData.IsStackable || itemData.HasUses)
return amount;
int space = itemData.MaxStackSize - quantity;
@@ -41,9 +73,25 @@ namespace Ashwild.Inventory
Clear();
}
/// <summary>
/// Spends uses on this instance (a tool hit, a food bite), clamped at 0. Has no effect on
/// items that do not track uses.
/// </summary>
public void ConsumeUse(int amount = 1)
{
if (itemData == null || !itemData.HasUses) return;
currentUses -= amount;
if (currentUses < 0) currentUses = 0;
}
/// <summary>
/// Uses-tracked items are non-stackable, so they never merge into an existing slot —
/// every instance keeps its own uses bar.
/// </summary>
public bool CanAccept(ItemData data)
{
if (IsEmpty) return true;
if (data != null && data.HasUses) return false;
return itemData == data && itemData.IsStackable && quantity < itemData.MaxStackSize;
}
}
+21
View File
@@ -32,6 +32,12 @@ namespace Ashwild.Inventory
[Header("Type")]
[SerializeField] private ItemType itemType;
[Header("Uses / Durability")]
[Tooltip("How many times this item can be used before it is depleted (a tool's hits, a food's bites). 0 = no usage tracking — a normal item without a uses bar.")]
[SerializeField] private int maxUses = 0;
[Tooltip("When the uses reach 0: checked = the item stays in the inventory, shown red and unusable (repairable tool, refillable container); unchecked = it is destroyed.")]
[SerializeField] private bool keepWhenDepleted = false;
[Header("Tool")]
[SerializeField] private HarvestType harvestType = HarvestType.None;
[SerializeField] private float toolPower = 1f;
@@ -59,6 +65,21 @@ namespace Ashwild.Inventory
public bool IsStackable => isStackable;
public int MaxStackSize => maxStackSize;
public ItemType ItemType => itemType;
public int MaxUses => maxUses;
/// <summary>
/// When uses run out: true keeps the item (shown red, unusable until repaired/refilled),
/// false destroys it. Repairable tools and refillable containers set this true.
/// </summary>
public bool KeepWhenDepleted => keepWhenDepleted;
/// <summary>
/// Whether this item tracks per-instance uses (a tool's durability, a food's bites). Items
/// with uses get a slot bar and are treated as non-stackable so each instance keeps its own value.
/// </summary>
public bool HasUses => maxUses > 0;
public HarvestType HarvestType => harvestType;
public float ToolPower => toolPower;
public float HealthRestore => healthRestore;
+78 -15
View File
@@ -131,7 +131,13 @@ namespace Ashwild.Inventory
/// Server-side: sends a granted item to this inventory's owning client, where it is added.
/// Called after the server authorises a pickup.
/// </summary>
public void GrantItemFromServer(ItemData item, int quantity)
public void GrantItemFromServer(ItemData item, int quantity) => GrantItemFromServer(item, quantity, -1);
/// <summary>
/// Server-side grant that also restores a specific remaining-uses value, used when a partially
/// used instance is picked back up off the ground. A negative value means "grant at full uses".
/// </summary>
public void GrantItemFromServer(ItemData item, int quantity, int uses)
{
if (item == null) return;
@@ -142,14 +148,15 @@ namespace Ashwild.Inventory
return;
}
TargetGrantItem(Owner, id, quantity);
TargetGrantItem(Owner, id, quantity, uses);
}
/// <summary>
/// Runs on the owning client: resolves the granted item and adds it locally.
/// Runs on the owning client: resolves the granted item and adds it locally, restoring its
/// remaining uses when one was carried across the drop.
/// </summary>
[TargetRpc]
private void TargetGrantItem(NetworkConnection conn, ushort itemId, int quantity)
private void TargetGrantItem(NetworkConnection conn, ushort itemId, int quantity, int uses)
{
ItemData item = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetItem(itemId) : null;
if (item == null)
@@ -158,7 +165,7 @@ namespace Ashwild.Inventory
return;
}
AddItem(item, quantity);
AddItem(item, quantity, uses);
}
#endregion
@@ -173,7 +180,12 @@ namespace Ashwild.Inventory
public InventorySlot GetSelectedSlot() => slots[selectedHotbarIndex];
public bool AddItem(ItemData item, int quantity = 1)
/// <summary>
/// Adds an item, stacking into existing slots first. Uses-tracked items never stack: each unit
/// takes its own slot with its own uses bar. The optional uses value restores a partially used
/// instance (negative = full); it only applies to uses-tracked items.
/// </summary>
public bool AddItem(ItemData item, int quantity = 1, int uses = -1)
{
if (item == null)
{
@@ -183,21 +195,32 @@ namespace Ashwild.Inventory
int remaining = quantity;
for (int i = 0; i < inventorySize && remaining > 0; i++)
if (!item.HasUses)
{
if (!slots[i].IsEmpty && slots[i].ItemData == item && slots[i].CanAccept(item))
for (int i = 0; i < inventorySize && remaining > 0; i++)
{
remaining = slots[i].AddQuantity(remaining);
NotifySlotChanged(i);
if (!slots[i].IsEmpty && slots[i].ItemData == item && slots[i].CanAccept(item))
{
remaining = slots[i].AddQuantity(remaining);
NotifySlotChanged(i);
}
}
}
for (int i = 0; i < inventorySize && remaining > 0; i++)
{
if (slots[i].IsEmpty)
{
int toPlace = (item.IsStackable && remaining > item.MaxStackSize) ? item.MaxStackSize : remaining;
slots[i].Set(item, toPlace);
remaining -= toPlace;
if (item.HasUses)
{
slots[i].Set(item, 1, uses);
remaining -= 1;
}
else
{
int toPlace = (item.IsStackable && remaining > item.MaxStackSize) ? item.MaxStackSize : remaining;
slots[i].Set(item, toPlace);
remaining -= toPlace;
}
NotifySlotChanged(i);
}
}
@@ -257,6 +280,12 @@ namespace Ashwild.Inventory
NotifySlotChanged(indexB);
}
/// <summary>
/// Consumes one use of a consumable: applies its restores once (per bite, for multi-use food)
/// then either spends a use or removes one unit. A multi-use item is kept at 0 uses (red,
/// unusable) when KeepWhenDepleted is set, otherwise it is destroyed. Ignores an already
/// depleted item.
/// </summary>
public void UseItem(int index)
{
if (index < 0 || index >= inventorySize) return;
@@ -264,6 +293,7 @@ namespace Ashwild.Inventory
ItemData item = slots[index].ItemData;
if (item.ItemType != ItemType.Consumable) return;
if (slots[index].IsDepleted) return;
if (PlayerStats.Instance != null)
{
@@ -273,7 +303,39 @@ namespace Ashwild.Inventory
}
PlayerEvents.RaiseItemConsumed(item);
RemoveItem(index, 1);
if (item.HasUses)
{
slots[index].ConsumeUse(1);
if (slots[index].IsDepleted && !item.KeepWhenDepleted)
RemoveItem(index, 1);
else
NotifySlotChanged(index);
}
else
{
RemoveItem(index, 1);
}
}
/// <summary>
/// Spends durability on the currently selected hotbar item after a successful tool hit. The
/// tool is kept at 0 uses (red, unusable until repaired) when KeepWhenDepleted is set, otherwise
/// it is destroyed. Returns false when the selected slot holds a depleted repairable tool, so
/// the caller can block the swing; true otherwise (including non-uses items).
/// </summary>
public bool ConsumeSelectedToolUse(int amount = 1)
{
InventorySlot slot = slots[selectedHotbarIndex];
if (slot.IsEmpty || !slot.ItemData.HasUses) return true;
if (slot.IsDepleted) return false;
ItemData item = slot.ItemData;
slot.ConsumeUse(amount);
if (slot.IsDepleted && !item.KeepWhenDepleted)
RemoveItem(selectedHotbarIndex, 1);
else
NotifySlotChanged(selectedHotbarIndex);
return true;
}
public void DropItem(int index, int quantity = 1)
@@ -282,13 +344,14 @@ namespace Ashwild.Inventory
if (slots[index].IsEmpty) return;
ItemData item = slots[index].ItemData;
int uses = item.HasUses ? slots[index].CurrentUses : -1;
Transform origin = dropOrigin != null ? dropOrigin : transform;
if (item.WorldPrefab != null && origin != null && PickableRegistry.Instance != null)
{
Vector3 dropPos = origin.position + origin.forward * dropForwardDistance;
ushort id = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetId(item) : (ushort)0;
if (id != 0)
PickableRegistry.Instance.RequestDropServerRpc(id, quantity, dropPos, origin.rotation);
PickableRegistry.Instance.RequestDropServerRpc(id, quantity, uses, dropPos, origin.rotation);
else
Debug.LogWarning($"[PlayerInventory] '{item.ItemName}' is not in the ItemDatabase — drop not spawned. Run Rebuild Item Database.", this);
}
+36
View File
@@ -13,6 +13,16 @@ namespace Ashwild.Inventory
[SerializeField] private TextMeshProUGUI quantityText;
[SerializeField] private Image highlightImage;
[Header("Uses Bar")]
[Tooltip("Root object of the uses/durability bar — shown only for items that track uses.")]
[SerializeField] private GameObject usageBarRoot;
[Tooltip("Filled image whose fillAmount maps to the remaining uses (Image Type = Filled).")]
[SerializeField] private Image usageBarFill;
[Tooltip("Icon tint applied when the item still has uses left.")]
[SerializeField] private Color normalTint = Color.white;
[Tooltip("Icon (and bar) tint applied when the item is depleted — a broken repairable tool / empty container.")]
[SerializeField] private Color depletedTint = Color.red;
private int slotIndex;
private Action<int, int> onSwapRequested;
private Action<int, bool> onClicked;
@@ -40,12 +50,17 @@ namespace Ashwild.Inventory
onClicked = clickCallback;
}
/// <summary>
/// Redraws the slot: icon, quantity, and the uses bar. Uses-tracked items (tools, multi-bite
/// food) show a fill bar for their remaining uses and turn red once depleted.
/// </summary>
public void UpdateVisual(InventorySlot slot)
{
if (slot == null || slot.IsEmpty)
{
iconImage.gameObject.SetActive(false);
quantityText.gameObject.SetActive(false);
if (usageBarRoot != null) usageBarRoot.SetActive(false);
}
else
{
@@ -55,9 +70,30 @@ namespace Ashwild.Inventory
quantityText.gameObject.SetActive(showQty);
if (showQty)
quantityText.text = slot.Quantity.ToString();
UpdateUsageBar(slot);
}
}
/// <summary>
/// Shows the uses bar and tints the icon red when the item is depleted; hides the bar entirely
/// for items that do not track uses. Preserves the current icon alpha so a mid-drag fade stays.
/// </summary>
private void UpdateUsageBar(InventorySlot slot)
{
bool hasUses = slot.ItemData.HasUses;
if (usageBarRoot != null)
usageBarRoot.SetActive(hasUses);
Color tint = (hasUses && slot.IsDepleted) ? depletedTint : normalTint;
tint.a = iconImage.color.a;
iconImage.color = tint;
if (hasUses && usageBarFill != null)
usageBarFill.fillAmount = (float)slot.CurrentUses / slot.ItemData.MaxUses;
}
public void SetSelected(bool selected)
{
if (highlightImage != null)
@@ -24,6 +24,7 @@ namespace Ashwild.Network
{
public ushort itemId;
public int quantity;
public int uses;
public Vector3 position;
public Quaternion rotation;
}
@@ -135,7 +136,7 @@ namespace Ashwild.Network
ItemData item = ItemDatabase.Instance != null ? ItemDatabase.Instance.GetItem(record.itemId) : null;
activeDrops.Remove(id);
if (item != null) inventory.GrantItemFromServer(item, record.quantity);
if (item != null) inventory.GrantItemFromServer(item, record.quantity, record.uses);
}
}
@@ -143,7 +144,7 @@ namespace Ashwild.Network
/// Owner-side entry: asks the server to spawn a dropped item into the world.
/// </summary>
[ServerRpc(RequireOwnership = false)]
public void RequestDropServerRpc(ushort itemId, int quantity, Vector3 position, Quaternion rotation, NetworkConnection conn = null)
public void RequestDropServerRpc(ushort itemId, int quantity, int uses, Vector3 position, Quaternion rotation, NetworkConnection conn = null)
{
if (ItemDatabase.Instance == null || ItemDatabase.Instance.GetItem(itemId) == null) return;
@@ -152,6 +153,7 @@ namespace Ashwild.Network
{
itemId = itemId,
quantity = quantity,
uses = uses,
position = position,
rotation = rotation
});
@@ -102,12 +102,14 @@ namespace Ashwild.Player
/// <summary>
/// Swings on use input (gated by lock, setup and cooldown) and harvests the
/// aimed target.
/// aimed target. A depleted but non-destroyed tool (a worn-out repairable axe) is
/// unusable: the swing is blocked until it is repaired.
/// </summary>
private void HandleAttackPressed()
{
if (PlayerEvents.InputLocked) return;
if (raycastOrigin == null) return;
if (PlayerInventory.Instance != null && PlayerInventory.Instance.GetSelectedSlot().IsDepleted) return;
if (Time.time < nextSwingTime) return;
nextSwingTime = Time.time + swingCooldown;
@@ -160,6 +162,10 @@ namespace Ashwild.Player
HarvestableRegistry.Instance.RequestHitServerRpc(harvestable.Id, damage, toolId, hitDirection);
PlayerEvents.RaiseHarvestableHit(harvestable, damage);
// Wear the tool only on a real connecting hit, never on a swing into the air or a blocked clunk.
if (PlayerInventory.Instance != null)
PlayerInventory.Instance.ConsumeSelectedToolUse(1);
}
/// <summary>
@@ -18,6 +18,8 @@ MonoBehaviour:
isStackable: 0
maxStackSize: 64
itemType: 1
maxUses: 60
keepWhenDepleted: 1
harvestType: 0
toolPower: 1
healthRestore: 0
@@ -18,6 +18,8 @@ MonoBehaviour:
isStackable: 0
maxStackSize: 64
itemType: 1
maxUses: 100
keepWhenDepleted: 1
harvestType: 2
toolPower: 1
healthRestore: 0
@@ -18,6 +18,8 @@ MonoBehaviour:
isStackable: 0
maxStackSize: 64
itemType: 1
maxUses: 80
keepWhenDepleted: 1
harvestType: 1
toolPower: 1
healthRestore: 0