Init
This commit is contained in:
354
content/world/entities/inventory/itemstacks.en.md
Normal file
354
content/world/entities/inventory/itemstacks.en.md
Normal file
@@ -0,0 +1,354 @@
|
||||
---
|
||||
title: ItemStacks
|
||||
type: docs
|
||||
weight: 1
|
||||
---
|
||||
|
||||
ItemStack represents a stack of items in Hytale with quantity, durability, and metadata.
|
||||
|
||||
## Creating ItemStacks
|
||||
|
||||
ItemStacks are created using constructors with an item ID:
|
||||
|
||||
```java
|
||||
// Create by item ID
|
||||
ItemStack sword = new ItemStack("iron_sword");
|
||||
|
||||
// Create with quantity
|
||||
ItemStack materials = new ItemStack("wood_plank", 64);
|
||||
|
||||
// Create with quantity and metadata
|
||||
BsonDocument metadata = new BsonDocument();
|
||||
ItemStack customItem = new ItemStack("iron_sword", 1, metadata);
|
||||
|
||||
// Create with full parameters (durability)
|
||||
ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null);
|
||||
// Parameters: itemId, quantity, durability, maxDurability, metadata
|
||||
```
|
||||
|
||||
## ItemStack Properties
|
||||
|
||||
```java
|
||||
ItemStack stack = new ItemStack("iron_sword", 1);
|
||||
|
||||
// Get the item ID
|
||||
String itemId = stack.getItemId(); // "iron_sword"
|
||||
|
||||
// Get the Item asset
|
||||
Item item = stack.getItem();
|
||||
|
||||
// Get quantity (NOT getCount!)
|
||||
int quantity = stack.getQuantity();
|
||||
|
||||
// Check if empty
|
||||
boolean empty = stack.isEmpty();
|
||||
|
||||
// Check validity
|
||||
boolean valid = stack.isValid();
|
||||
|
||||
// Durability
|
||||
double durability = stack.getDurability();
|
||||
double maxDurability = stack.getMaxDurability();
|
||||
boolean unbreakable = stack.isUnbreakable(); // true if maxDurability <= 0
|
||||
boolean broken = stack.isBroken(); // true if durability == 0
|
||||
```
|
||||
|
||||
## Modifying ItemStacks
|
||||
|
||||
ItemStack uses a `with*` pattern that returns NEW instances:
|
||||
|
||||
```java
|
||||
ItemStack stack = new ItemStack("iron_sword", 1);
|
||||
|
||||
// Change quantity - returns NEW ItemStack or null if quantity is 0
|
||||
ItemStack moreItems = stack.withQuantity(32);
|
||||
|
||||
// Change durability
|
||||
ItemStack damaged = stack.withDurability(50.0);
|
||||
|
||||
// Increase durability
|
||||
ItemStack repaired = stack.withIncreasedDurability(25.0);
|
||||
|
||||
// Restore full durability
|
||||
ItemStack fullyRepaired = stack.withRestoredDurability(100.0);
|
||||
|
||||
// Change max durability
|
||||
ItemStack stronger = stack.withMaxDurability(200.0);
|
||||
|
||||
// Change state (for items with states)
|
||||
ItemStack newState = stack.withState("activated");
|
||||
|
||||
// Add/modify metadata
|
||||
ItemStack withMeta = stack.withMetadata(metadataDocument);
|
||||
|
||||
// Add specific metadata value
|
||||
ItemStack tagged = stack.withMetadata("CustomKey", Codec.STRING, "CustomValue");
|
||||
```
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
**Important:** `withQuantity(0)` returns `null`, not an empty ItemStack. Always check for null when decreasing quantity!
|
||||
{{< /callout >}}
|
||||
|
||||
## The EMPTY Constant
|
||||
|
||||
Use `ItemStack.EMPTY` for empty stacks:
|
||||
|
||||
```java
|
||||
// Static empty instance (singleton)
|
||||
ItemStack empty = ItemStack.EMPTY;
|
||||
|
||||
// Check for empty
|
||||
if (stack.isEmpty()) {
|
||||
// Stack is empty
|
||||
}
|
||||
|
||||
// Static helper method
|
||||
if (ItemStack.isEmpty(stack)) {
|
||||
// Handles null and empty stacks
|
||||
}
|
||||
```
|
||||
|
||||
## Comparing ItemStacks
|
||||
|
||||
```java
|
||||
ItemStack a = new ItemStack("iron_sword", 1);
|
||||
ItemStack b = new ItemStack("iron_sword", 5);
|
||||
ItemStack c = new ItemStack("diamond_sword", 1);
|
||||
|
||||
// Check if stackable (same itemId, durability, maxDurability, AND metadata)
|
||||
// Note: Different quantities can stack, but durability values must match exactly
|
||||
boolean canStack = a.isStackableWith(b);
|
||||
|
||||
// Check equivalent type (same itemId and metadata, ignores durability values)
|
||||
boolean sameType = a.isEquivalentType(b);
|
||||
|
||||
// Check same item type only (just itemId comparison)
|
||||
boolean sameItem = ItemStack.isSameItemType(a, c); // false
|
||||
|
||||
// Static helpers (handle nulls safely)
|
||||
ItemStack.isStackableWith(a, b);
|
||||
ItemStack.isEquivalentType(a, b);
|
||||
```
|
||||
|
||||
## Working with Metadata
|
||||
|
||||
ItemStack supports BSON metadata for custom data:
|
||||
|
||||
```java
|
||||
// Create metadata codec
|
||||
KeyedCodec<String> OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING);
|
||||
|
||||
// Add metadata
|
||||
ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName");
|
||||
|
||||
// Read metadata
|
||||
String owner = stack.getFromMetadataOrNull(OWNER_KEY);
|
||||
|
||||
// Read with key and codec
|
||||
Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER);
|
||||
|
||||
// Read with default from BuilderCodec
|
||||
MyData data = stack.getFromMetadataOrDefault("Data", MyData.CODEC);
|
||||
```
|
||||
|
||||
## Block Items
|
||||
|
||||
Check if an item can be placed as a block:
|
||||
|
||||
```java
|
||||
ItemStack stack = new ItemStack("stone", 1);
|
||||
|
||||
// Get associated block key (null if not a block item)
|
||||
String blockKey = stack.getBlockKey();
|
||||
if (blockKey != null) {
|
||||
// This item can be placed as a block
|
||||
}
|
||||
|
||||
// Check via Item asset
|
||||
Item item = stack.getItem();
|
||||
if (item.hasBlockType()) {
|
||||
String blockId = item.getBlockId();
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Consuming Items
|
||||
|
||||
```java
|
||||
public ItemStack consumeOne(ItemStack stack) {
|
||||
if (stack == null || stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int newQuantity = stack.getQuantity() - 1;
|
||||
|
||||
// withQuantity returns null if quantity is 0
|
||||
return stack.withQuantity(newQuantity);
|
||||
}
|
||||
|
||||
// Usage with container
|
||||
public void useItem(ItemContainer container, short slot) {
|
||||
ItemStack current = container.getItemStack(slot);
|
||||
ItemStack remaining = consumeOne(current);
|
||||
|
||||
// remaining may be null if stack was depleted
|
||||
container.setItemStackForSlot(slot, remaining);
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Item Type
|
||||
|
||||
```java
|
||||
public boolean isHoldingSword(ItemStack hand) {
|
||||
if (hand == null || hand.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check item type by ID
|
||||
return hand.getItemId().contains("sword");
|
||||
|
||||
// Or check via Item asset
|
||||
// return hand.getItem().getCategory().equals("weapon");
|
||||
}
|
||||
```
|
||||
|
||||
### Splitting Stacks
|
||||
|
||||
```java
|
||||
public ItemStack[] splitStack(ItemStack stack, int splitAmount) {
|
||||
if (stack == null || stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int currentQuantity = stack.getQuantity();
|
||||
if (splitAmount >= currentQuantity) {
|
||||
return new ItemStack[] { stack, null };
|
||||
}
|
||||
|
||||
// Create two stacks
|
||||
ItemStack remaining = stack.withQuantity(currentQuantity - splitAmount);
|
||||
ItemStack split = stack.withQuantity(splitAmount);
|
||||
|
||||
return new ItemStack[] { remaining, split };
|
||||
}
|
||||
```
|
||||
|
||||
### Merging Stacks
|
||||
|
||||
```java
|
||||
public ItemStack[] mergeStacks(ItemStack target, ItemStack source) {
|
||||
if (!target.isStackableWith(source)) {
|
||||
return new ItemStack[] { target, source }; // Can't merge
|
||||
}
|
||||
|
||||
int maxStack = target.getItem().getMaxStack();
|
||||
int totalQuantity = target.getQuantity() + source.getQuantity();
|
||||
|
||||
if (totalQuantity <= maxStack) {
|
||||
// Full merge
|
||||
return new ItemStack[] {
|
||||
target.withQuantity(totalQuantity),
|
||||
null
|
||||
};
|
||||
}
|
||||
|
||||
// Partial merge
|
||||
return new ItemStack[] {
|
||||
target.withQuantity(maxStack),
|
||||
source.withQuantity(totalQuantity - maxStack)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**Remember:**
|
||||
- Use `getQuantity()` not `getCount()` - Hytale uses "quantity"
|
||||
- `withQuantity(0)` returns `null` - check for this!
|
||||
- Use `ItemStack.isEmpty(stack)` to handle both null and empty
|
||||
- ItemStacks are mostly immutable - `with*` methods return new instances
|
||||
- Use `isStackableWith()` before attempting to merge stacks
|
||||
- `ResourceType.Id` must not be null when creating items via codecs
|
||||
{{< /callout >}}
|
||||
|
||||
```java
|
||||
// Good: Handle null from withQuantity
|
||||
ItemStack result = stack.withQuantity(newQuantity);
|
||||
if (result == null) {
|
||||
// Stack depleted, handle appropriately
|
||||
}
|
||||
|
||||
// Good: Safe empty check
|
||||
if (ItemStack.isEmpty(stack)) {
|
||||
// Handles both null and empty ItemStack.EMPTY
|
||||
}
|
||||
|
||||
// Bad: Ignoring the returned value
|
||||
stack.withQuantity(10); // Returns new stack, original unchanged!
|
||||
```
|
||||
|
||||
## ItemStack API Reference
|
||||
|
||||
### Instance Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getItemId()` | `String` | The item type identifier |
|
||||
| `getItem()` | `Item` | The Item asset (returns `Item.UNKNOWN` if not found) |
|
||||
| `getQuantity()` | `int` | Stack size |
|
||||
| `getDurability()` | `double` | Current durability |
|
||||
| `getMaxDurability()` | `double` | Maximum durability |
|
||||
| `isEmpty()` | `boolean` | True if itemId equals "Empty" |
|
||||
| `isUnbreakable()` | `boolean` | True if maxDurability <= 0 |
|
||||
| `isBroken()` | `boolean` | True if NOT unbreakable AND durability == 0 |
|
||||
| `isValid()` | `boolean` | True if empty OR item asset exists |
|
||||
| `isStackableWith(ItemStack)` | `boolean` | Same itemId, durability, maxDurability, metadata |
|
||||
| `isEquivalentType(ItemStack)` | `boolean` | Same itemId and metadata (ignores durability) |
|
||||
| `getBlockKey()` | `String?` | Block ID if item is placeable, null otherwise |
|
||||
| `getOverrideDroppedItemAnimation()` | `boolean` | Animation override flag |
|
||||
| `getMetadata()` | `BsonDocument?` | **Deprecated** - Returns cloned metadata |
|
||||
| `getFromMetadataOrNull(KeyedCodec)` | `T?` | Get typed metadata value |
|
||||
| `getFromMetadataOrNull(String, Codec)` | `T?` | Get typed metadata by key |
|
||||
| `getFromMetadataOrDefault(String, BuilderCodec)` | `T` | Get metadata with default |
|
||||
|
||||
### Modifier Methods (return new ItemStack)
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `withQuantity(int)` | `ItemStack?` | **Returns null if quantity is 0** |
|
||||
| `withDurability(double)` | `ItemStack` | Clamped to [0, maxDurability] |
|
||||
| `withMaxDurability(double)` | `ItemStack` | Also clamps current durability |
|
||||
| `withIncreasedDurability(double)` | `ItemStack` | Add to current durability |
|
||||
| `withRestoredDurability(double)` | `ItemStack` | Set both durability and max |
|
||||
| `withState(String)` | `ItemStack` | Change item state |
|
||||
| `withMetadata(BsonDocument)` | `ItemStack` | Replace all metadata |
|
||||
| `withMetadata(KeyedCodec, T)` | `ItemStack` | Set typed metadata |
|
||||
| `withMetadata(String, Codec, T)` | `ItemStack` | Set metadata by key |
|
||||
| `withMetadata(String, BsonValue)` | `ItemStack` | Set raw BSON value |
|
||||
| `setOverrideDroppedItemAnimation(boolean)` | `void` | **Mutates in place** |
|
||||
|
||||
### Static Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `EMPTY` | `ItemStack` | Singleton empty stack (itemId = "Empty") |
|
||||
| `EMPTY_ARRAY` | `ItemStack[]` | Empty array constant |
|
||||
| `CODEC` | `BuilderCodec<ItemStack>` | Serialization codec |
|
||||
|
||||
### Static Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `isEmpty(ItemStack)` | `boolean` | Null-safe empty check |
|
||||
| `isStackableWith(ItemStack, ItemStack)` | `boolean` | Null-safe stackable check |
|
||||
| `isEquivalentType(ItemStack, ItemStack)` | `boolean` | Null-safe type check |
|
||||
| `isSameItemType(ItemStack, ItemStack)` | `boolean` | Compare itemId only |
|
||||
| `fromPacket(ItemQuantity)` | `ItemStack?` | Create from network packet |
|
||||
|
||||
### Nested Classes
|
||||
|
||||
| Class | Description |
|
||||
|-------|-------------|
|
||||
| `ItemStack.Metadata` | Contains `BLOCK_STATE` constant for block state metadata key |
|
||||
Reference in New Issue
Block a user