228 lines
5.9 KiB
Rust
228 lines
5.9 KiB
Rust
use crate::container_click::MouseClick;
|
|
use crate::player::PlayerInventory;
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
|
use pumpkin_world::item::ItemStack;
|
|
|
|
pub mod container_click;
|
|
pub mod player;
|
|
pub mod window_property;
|
|
|
|
/// https://wiki.vg/Inventory
|
|
#[derive(Debug, ToPrimitive, FromPrimitive, Clone, Eq, PartialEq)]
|
|
pub enum WindowType {
|
|
// not used
|
|
Generic9x1,
|
|
// not used
|
|
Generic9x2,
|
|
// General-purpose 3-row inventory. Used by Chest, minecart with chest, ender chest, and barrel
|
|
Generic9x3,
|
|
// not used
|
|
Generic9x4,
|
|
// not used
|
|
Generic9x5,
|
|
// Used by large chests
|
|
Generic9x6,
|
|
// General-purpose 3-by-3 square inventory, used by Dispenser and Dropper
|
|
Generic3x3,
|
|
// General-purpose 3-by-3 square inventory, used by the Crafter
|
|
Craft3x3,
|
|
Anvil,
|
|
Beacon,
|
|
BlastFurnace,
|
|
BrewingStand,
|
|
CraftingTable,
|
|
EnchantmentTable,
|
|
Furnace,
|
|
Grindstone,
|
|
// Hopper or minecart with hopper
|
|
Hopper,
|
|
Lectern,
|
|
Loom,
|
|
// Villager, Wandering Trader
|
|
Merchant,
|
|
ShulkerBox,
|
|
SmithingTable,
|
|
Smoker,
|
|
CartographyTable,
|
|
Stonecutter,
|
|
}
|
|
|
|
impl WindowType {
|
|
pub const fn default_title(&self) -> &'static str {
|
|
// TODO: Add titles here:
|
|
/*match self {
|
|
_ => "WINDOW TITLE",
|
|
}*/
|
|
"WINDOW TITLE"
|
|
}
|
|
}
|
|
|
|
pub trait Container {
|
|
fn window_type(&self) -> &WindowType;
|
|
|
|
fn handle_item_change(
|
|
&mut self,
|
|
carried_item: &mut Option<ItemStack>,
|
|
slot: usize,
|
|
mouse_click: MouseClick,
|
|
);
|
|
|
|
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>>;
|
|
}
|
|
|
|
pub fn handle_item_take(
|
|
carried_item: &mut Option<ItemStack>,
|
|
item_slot: &mut Option<ItemStack>,
|
|
mouse_click: MouseClick,
|
|
) {
|
|
let Some(item) = item_slot else {
|
|
return;
|
|
};
|
|
let mut new_item = *item;
|
|
|
|
match mouse_click {
|
|
MouseClick::Left => {
|
|
*item_slot = None;
|
|
}
|
|
MouseClick::Right => {
|
|
let half = item.item_count / 2;
|
|
item.item_count -= half;
|
|
new_item.item_count = half;
|
|
}
|
|
}
|
|
*carried_item = Some(new_item);
|
|
}
|
|
pub fn handle_item_change(
|
|
carried_slot: &mut Option<ItemStack>,
|
|
current_slot: &mut Option<ItemStack>,
|
|
mouse_click: MouseClick,
|
|
) {
|
|
match (current_slot.as_mut(), carried_slot.as_mut()) {
|
|
// Swap or combine current and carried
|
|
(Some(current), Some(carried)) => {
|
|
if current.item_id == carried.item_id {
|
|
combine_stacks(carried_slot, current, mouse_click);
|
|
} else if mouse_click == MouseClick::Left {
|
|
let carried = *carried;
|
|
*carried_slot = Some(current.to_owned());
|
|
*current_slot = Some(carried.to_owned());
|
|
}
|
|
}
|
|
// Put held stack into empty slot
|
|
(None, Some(carried)) => match mouse_click {
|
|
MouseClick::Left => {
|
|
*current_slot = Some(carried.to_owned());
|
|
*carried_slot = None;
|
|
}
|
|
MouseClick::Right => {
|
|
carried.item_count -= 1;
|
|
let mut new = *carried;
|
|
new.item_count = 1;
|
|
*current_slot = Some(new);
|
|
}
|
|
},
|
|
// Take stack into carried
|
|
(Some(_current), None) => handle_item_take(carried_slot, current_slot, mouse_click),
|
|
(None, None) => (),
|
|
}
|
|
}
|
|
|
|
pub fn combine_stacks(
|
|
carried_slot: &mut Option<ItemStack>,
|
|
slot: &mut ItemStack,
|
|
mouse_click: MouseClick,
|
|
) {
|
|
let Some(carried_item) = carried_slot else {
|
|
return;
|
|
};
|
|
|
|
let carried_change = match mouse_click {
|
|
MouseClick::Left => carried_item.item_count,
|
|
MouseClick::Right => 1,
|
|
};
|
|
|
|
// TODO: Check for item stack max size here
|
|
if slot.item_count + carried_change <= 64 {
|
|
slot.item_count += carried_change;
|
|
carried_item.item_count -= carried_change;
|
|
if carried_item.item_count == 0 {
|
|
*carried_slot = None;
|
|
}
|
|
} else {
|
|
let left_over = slot.item_count + carried_change - 64;
|
|
slot.item_count = 64;
|
|
carried_item.item_count = left_over;
|
|
}
|
|
}
|
|
|
|
pub struct ContainerStruct<const SLOTS: usize> {
|
|
slots: [Option<ItemStack>; SLOTS],
|
|
_state_id: usize,
|
|
_open_by: Option<Vec<i32>>,
|
|
window_type: WindowType,
|
|
}
|
|
|
|
impl<const T: usize> Container for ContainerStruct<T> {
|
|
fn window_type(&self) -> &WindowType {
|
|
&self.window_type
|
|
}
|
|
|
|
fn handle_item_change(
|
|
&mut self,
|
|
carried_slot: &mut Option<ItemStack>,
|
|
slot: usize,
|
|
mouse_click: MouseClick,
|
|
) {
|
|
let current_slot = &mut self.slots[slot];
|
|
handle_item_change(carried_slot, current_slot, mouse_click)
|
|
}
|
|
|
|
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
|
self.slots.iter_mut().collect()
|
|
}
|
|
}
|
|
|
|
pub struct OptionallyCombinedContainer<'a> {
|
|
container: Option<&'a mut Box<dyn Container>>,
|
|
inventory: &'a mut PlayerInventory,
|
|
}
|
|
impl<'a> OptionallyCombinedContainer<'a> {
|
|
pub fn new(
|
|
player_inventory: &'a mut PlayerInventory,
|
|
container: Option<&'a mut Box<dyn Container>>,
|
|
) -> Self {
|
|
Self {
|
|
inventory: player_inventory,
|
|
container,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Container for OptionallyCombinedContainer<'a> {
|
|
fn window_type(&self) -> &WindowType {
|
|
if let Some(container) = &self.container {
|
|
container.window_type()
|
|
} else {
|
|
&WindowType::Generic9x1
|
|
}
|
|
}
|
|
|
|
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
|
|
let mut slots = if let Some(container) = &mut self.container {
|
|
container.all_slots()
|
|
} else {
|
|
vec![]
|
|
};
|
|
slots.extend(self.inventory.all_slots());
|
|
slots
|
|
}
|
|
|
|
fn handle_item_change(
|
|
&mut self,
|
|
carried_item: &mut Option<ItemStack>,
|
|
slot: usize,
|
|
mouse_click: MouseClick,
|
|
) {
|
|
}
|
|
}
|