steel_core/player/player_inventory/
mod.rs1use std::sync::Arc;
4
5use text_components::TextComponent;
6
7use crate::{inventory::menu::Menu, player::Player, world::World};
8
9mod container;
10mod core;
11mod equipment;
12mod player_handlers;
13
14pub use container::InvalidHotbarSlot;
15pub(crate) use container::armor_equipment;
16pub use core::PlayerInventory;
17pub use equipment::EquipmentSwapResult;
18
19pub struct MenuOpenContext<'a> {
21 pub container_id: u8,
23 pub player: &'a Player,
25 pub world: &'a Arc<World>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[must_use]
32pub enum MenuRemovalStatus {
33 Complete,
35 Pending,
38}
39
40#[derive(Clone, Copy, PartialEq, Eq)]
41pub(super) enum MenuItemDisposition {
42 ReturnToInventory,
43 Drop,
44}
45
46impl MenuItemDisposition {
47 const fn combine(self, other: Self) -> Self {
48 if matches!(self, Self::Drop) || matches!(other, Self::Drop) {
49 Self::Drop
50 } else {
51 Self::ReturnToInventory
52 }
53 }
54}
55
56pub(super) struct OpenMenuState {
57 menu: Option<Menu>,
58 dispatch: Option<OpenMenuDispatch>,
59 terminal_removal: Option<TerminalMenuRemoval>,
60 active_open_operations: usize,
61}
62
63pub(super) struct PlayerInventorySyncState {
64 pending_slots: [bool; PlayerInventory::CONTAINER_SIZE],
65}
66
67impl PlayerInventorySyncState {
68 pub(super) const fn new() -> Self {
69 Self {
70 pending_slots: [false; PlayerInventory::CONTAINER_SIZE],
71 }
72 }
73
74 fn request(&mut self, slots: impl IntoIterator<Item = usize>) {
75 for slot in slots {
76 assert!(
77 slot < PlayerInventory::CONTAINER_SIZE,
78 "logical player inventory slot {slot} is out of bounds"
79 );
80 self.pending_slots[slot] = true;
81 }
82 }
83
84 fn take_ready(&mut self, overrides_player_slots: bool) -> Vec<usize> {
85 self.pending_slots
86 .iter_mut()
87 .enumerate()
88 .filter_map(|(slot, pending)| {
89 if !*pending || (overrides_player_slots && slot < PlayerInventory::INVENTORY_SIZE) {
90 return None;
91 }
92 *pending = false;
93 Some(slot)
94 })
95 .collect()
96 }
97}
98
99struct OpenMenuDispatch {
100 container_id: u8,
101 overrides_player_slots: bool,
102 actions: Vec<DeferredMenuAction>,
103}
104
105struct TerminalMenuRemoval {
106 disposition: MenuItemDisposition,
107 main_cleanup_complete: bool,
108 pending_cleanup_in_progress: bool,
109 pending_menus: Vec<Menu>,
110}
111
112enum DeferredMenuAction {
113 Close { send_packet: bool },
114 Open(Box<PendingMenuOpen>),
115 Install(Box<PreparedMenu>),
116}
117
118type MenuFactory = Box<dyn for<'a> FnOnce(MenuOpenContext<'a>) -> Menu + Send + 'static>;
119
120struct PendingMenuOpen {
121 title: TextComponent,
122 create: MenuFactory,
123}
124
125struct PreparedMenu {
126 title: TextComponent,
127 menu: Menu,
128}
129
130enum OpenMenuUnavailable {
131 Closed,
132 Unavailable,
133}
134
135impl OpenMenuState {
136 pub(super) const fn new() -> Self {
137 Self {
138 menu: None,
139 dispatch: None,
140 terminal_removal: None,
141 active_open_operations: 0,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests;