122 lines
4.5 KiB
Markdown
122 lines
4.5 KiB
Markdown
# Adding an Item Behavior
|
|
|
|
This guide details how to implement custom interactive mechanics for an item in Pumpkin.
|
|
|
|
---
|
|
|
|
## When Is an Item Behavior Needed?
|
|
|
|
- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`.
|
|
- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns.
|
|
|
|
---
|
|
|
|
## Step 1: Define the Item Struct & Metadata
|
|
|
|
Create a new file under `crates/pumpkin/src/item/items/<item_name>.rs` (e.g., `items/snowball.rs`):
|
|
|
|
```rust
|
|
use std::sync::Arc;
|
|
use pumpkin_data::entity::EntityType;
|
|
use pumpkin_data::item::Item;
|
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
|
use crate::entity::Entity;
|
|
use crate::entity::player::Player;
|
|
use crate::entity::projectile::snowball::SnowballEntity;
|
|
use crate::item::{ItemBehaviour, ItemMetadata};
|
|
|
|
pub struct SnowBallItem;
|
|
|
|
// 1. Declare the associated numeric Item ID(s)
|
|
impl ItemMetadata for SnowBallItem {
|
|
fn ids() -> Box<[u16]> {
|
|
[Item::SNOWBALL.id].into()
|
|
}
|
|
}
|
|
|
|
// 2. Implement the interaction behavior
|
|
impl ItemBehaviour for SnowBallItem {
|
|
fn normal_use(&self, _item: &Item, player: &Player) {
|
|
let position = player.position();
|
|
let world = player.world();
|
|
|
|
// Play throw sound effect
|
|
world.play_sound(
|
|
Sound::EntitySnowballThrow,
|
|
SoundCategory::Neutral,
|
|
&position,
|
|
);
|
|
|
|
// Construct and launch the projectile entity
|
|
let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL);
|
|
let snowball = SnowballEntity::new_shot(entity, player.get_entity());
|
|
let (yaw, pitch) = player.rotation();
|
|
snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0);
|
|
world.spawn_entity(Arc::new(snowball));
|
|
|
|
// Safely decrement held item unless in Creative mode
|
|
let mut held = player.inventory.held_item();
|
|
held.decrement_unless_creative(player.gamemode.load(), 1);
|
|
player.inventory.set_held_item(held);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2: `ItemBehaviour` Hook Reference
|
|
|
|
The `ItemBehaviour` trait provides comprehensive hooks for different interaction models:
|
|
|
|
### Instant Single-Use Hooks
|
|
|
|
| Hook | Signature | Description |
|
|
| :--- | :--- | :--- |
|
|
| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. |
|
|
| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. |
|
|
| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3<f32>, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). |
|
|
| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc<dyn EntityBase>)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). |
|
|
|
|
### Channeled & Timed Hooks
|
|
|
|
| Hook | Signature | Description |
|
|
| :--- | :--- | :--- |
|
|
| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). |
|
|
| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. |
|
|
| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). |
|
|
|
|
---
|
|
|
|
## Step 3: Durability & Cooldowns
|
|
|
|
### Decrementing Stacks Safely
|
|
Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them:
|
|
|
|
```rust
|
|
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
|
```
|
|
|
|
### Applying Cooldowns
|
|
To prevent action spamming (e.g., Ender Pearls, Chorus Fruit):
|
|
|
|
```rust
|
|
// Start a 20-tick (1-second) cooldown on Ender Pearls
|
|
player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20);
|
|
```
|
|
|
|
---
|
|
|
|
## Step 4: Registering the Item
|
|
|
|
1. Export your module in `crates/pumpkin/src/item/items/mod.rs`:
|
|
```rust
|
|
pub mod snowball;
|
|
```
|
|
|
|
2. Register the struct in `crates/pumpkin/src/item/registry.rs`:
|
|
```rust
|
|
items.register(SnowBallItem);
|
|
```
|
|
|
|
When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance.
|