1a0be06c3b
24 Plätze mit je bis zu 99 Gegenständen. Hinzufügen füllt zuerst vorhandene Stapel und meldet, was nicht passt; Entfernen nimmt alles oder nichts. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
158 lines
4.3 KiB
Rust
158 lines
4.3 KiB
Rust
//! Item stacks carried by a player.
|
|
|
|
use crate::codec::{DecodeError, Reader, Writer};
|
|
|
|
/// Identifies a kind of item. The value zero is reserved for empty slots.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct ItemId(pub u16);
|
|
|
|
/// Stone, gained by breaking rock.
|
|
pub const ITEM_STONE: ItemId = ItemId(1);
|
|
|
|
/// Number of slots in an inventory.
|
|
pub const INVENTORY_SLOTS: usize = 24;
|
|
/// Largest number of items in one slot.
|
|
pub const STACK_LIMIT: u32 = 99;
|
|
|
|
/// Items of one kind in one slot.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub struct Stack {
|
|
pub item: ItemId,
|
|
pub count: u32,
|
|
}
|
|
|
|
/// Fixed number of slots, each empty or holding one stack.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Inventory {
|
|
slots: [Option<Stack>; INVENTORY_SLOTS],
|
|
}
|
|
|
|
impl Default for Inventory {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Inventory {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
slots: [None; INVENTORY_SLOTS],
|
|
}
|
|
}
|
|
|
|
pub fn slots(&self) -> &[Option<Stack>] {
|
|
&self.slots
|
|
}
|
|
|
|
/// Total number of `item` across all slots.
|
|
pub fn count(&self, item: ItemId) -> u32 {
|
|
self.slots
|
|
.iter()
|
|
.flatten()
|
|
.filter(|stack| stack.item == item)
|
|
.map(|stack| stack.count)
|
|
.sum()
|
|
}
|
|
|
|
/// How many more of `item` fit.
|
|
pub fn free_space(&self, item: ItemId) -> u32 {
|
|
self.slots
|
|
.iter()
|
|
.map(|slot| match slot {
|
|
None => STACK_LIMIT,
|
|
Some(stack) if stack.item == item => STACK_LIMIT - stack.count,
|
|
Some(_) => 0,
|
|
})
|
|
.sum()
|
|
}
|
|
|
|
/// Adds up to `count` of `item`. Existing stacks are topped up before
|
|
/// empty slots are used. Returns how many did not fit.
|
|
///
|
|
/// # Panics
|
|
/// Panics if `item` is the reserved id zero.
|
|
pub fn add(&mut self, item: ItemId, count: u32) -> u32 {
|
|
assert!(item.0 != 0, "item id zero is reserved for empty slots");
|
|
let mut left = count;
|
|
for slot in self.slots.iter_mut() {
|
|
if left == 0 {
|
|
break;
|
|
}
|
|
let Some(stack) = slot else { continue };
|
|
if stack.item != item {
|
|
continue;
|
|
}
|
|
let moved = left.min(STACK_LIMIT - stack.count);
|
|
stack.count += moved;
|
|
left -= moved;
|
|
}
|
|
for slot in self.slots.iter_mut() {
|
|
if left == 0 {
|
|
break;
|
|
}
|
|
if slot.is_none() {
|
|
let moved = left.min(STACK_LIMIT);
|
|
*slot = Some(Stack { item, count: moved });
|
|
left -= moved;
|
|
}
|
|
}
|
|
left
|
|
}
|
|
|
|
/// Removes exactly `count` of `item`, emptying the last slots first.
|
|
/// Returns `false` and changes nothing if fewer are present.
|
|
pub fn remove(&mut self, item: ItemId, count: u32) -> bool {
|
|
if self.count(item) < count {
|
|
return false;
|
|
}
|
|
let mut left = count;
|
|
for slot in self.slots.iter_mut().rev() {
|
|
if left == 0 {
|
|
break;
|
|
}
|
|
let Some(stack) = slot else { continue };
|
|
if stack.item != item {
|
|
continue;
|
|
}
|
|
let moved = left.min(stack.count);
|
|
stack.count -= moved;
|
|
left -= moved;
|
|
if stack.count == 0 {
|
|
*slot = None;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
pub fn encode(&self, w: &mut Writer) {
|
|
for slot in &self.slots {
|
|
match slot {
|
|
None => w.u16(0),
|
|
Some(stack) => {
|
|
w.u16(stack.item.0);
|
|
w.u32(stack.count);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
|
|
let mut slots = [None; INVENTORY_SLOTS];
|
|
for slot in slots.iter_mut() {
|
|
let item = r.u16()?;
|
|
if item == 0 {
|
|
continue;
|
|
}
|
|
let count = r.u32()?;
|
|
if count == 0 || count > STACK_LIMIT {
|
|
return Err(DecodeError::InvalidValue("stack count"));
|
|
}
|
|
*slot = Some(Stack {
|
|
item: ItemId(item),
|
|
count,
|
|
});
|
|
}
|
|
Ok(Self { slots })
|
|
}
|
|
}
|