diff --git a/assets/common/items/debug/admin_black_hole.ron b/assets/common/items/debug/admin_black_hole.ron index 8cc8cd3f12..c565e9aa5b 100644 --- a/assets/common/items/debug/admin_black_hole.ron +++ b/assets/common/items/debug/admin_black_hole.ron @@ -9,5 +9,5 @@ ItemDef( ), quality: Debug, tags: [], - slots: 900, + slots: 1, ) diff --git a/client/src/lib.rs b/client/src/lib.rs index 23ad15aa00..fb72e1efc3 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1119,6 +1119,13 @@ impl Client { pub fn swap_slots(&mut self, a: Slot, b: Slot) { match (a, b) { + (Slot::Overflow(o), Slot::Inventory(inv)) + | (Slot::Inventory(inv), Slot::Overflow(o)) => { + self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( + InventoryEvent::OverflowMove(o, inv), + ))); + }, + (Slot::Overflow(_), _) | (_, Slot::Overflow(_)) => {}, (Slot::Equip(equip), slot) | (slot, Slot::Equip(equip)) => self.control_action( ControlAction::InventoryAction(InventoryAction::Swap(equip, slot)), ), @@ -1138,6 +1145,9 @@ impl Client { Slot::Inventory(inv) => self.send_msg(ClientGeneral::ControlEvent( ControlEvent::InventoryEvent(InventoryEvent::Drop(inv)), )), + Slot::Overflow(o) => self.send_msg(ClientGeneral::ControlEvent( + ControlEvent::InventoryEvent(InventoryEvent::OverflowDrop(o)), + )), } } @@ -1173,6 +1183,7 @@ impl Client { InventoryEvent::SplitSwap(inv1, inv2), ))) }, + (Slot::Overflow(_), _) | (_, Slot::Overflow(_)) => {}, } } @@ -1184,6 +1195,9 @@ impl Client { Slot::Inventory(inv) => self.send_msg(ClientGeneral::ControlEvent( ControlEvent::InventoryEvent(InventoryEvent::SplitDrop(inv)), )), + Slot::Overflow(o) => self.send_msg(ClientGeneral::ControlEvent( + ControlEvent::InventoryEvent(InventoryEvent::OverflowSplitDrop(o)), + )), } } @@ -1393,6 +1407,7 @@ impl Client { if let Some(item) = match item { Slot::Equip(equip_slot) => inv.equipped(equip_slot), Slot::Inventory(invslot) => inv.get(invslot), + Slot::Overflow(_) => None, } { item.has_durability() } else { diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 285ca705e1..6122d3d05d 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -31,6 +31,9 @@ pub enum InventoryEvent { craft_event: CraftEvent, craft_sprite: Option, }, + OverflowMove(usize, InvSlotId), + OverflowDrop(usize), + OverflowSplitDrop(usize), } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -86,6 +89,11 @@ impl From for InventoryManip { craft_event, craft_sprite, }, + InventoryEvent::OverflowMove(o, inv) => { + Self::Swap(Slot::Overflow(o), Slot::Inventory(inv)) + }, + InventoryEvent::OverflowDrop(o) => Self::Drop(Slot::Overflow(o)), + InventoryEvent::OverflowSplitDrop(o) => Self::SplitDrop(Slot::Overflow(o)), } } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 3ede192829..5fe9484834 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -524,11 +524,38 @@ impl Inventory { *self.slot_mut(b).unwrap() = slot_a; } + /// Moves an item from an overflow slot to an inventory slot + pub fn move_overflow_item(&mut self, overflow: usize, inv_slot: InvSlotId) { + match self.slot(inv_slot) { + Some(Some(_)) => { + warn!("Attempted to move from overflow slot to a filled inventory slot"); + return; + }, + None => { + warn!("Attempted to move from overflow slot to a non-existent inventory slot"); + return; + }, + Some(None) => {}, + }; + + let item = self.overflow_items.remove(overflow); + *self.slot_mut(inv_slot).unwrap() = Some(item); + } + /// Remove an item from the slot pub fn remove(&mut self, inv_slot_id: InvSlotId) -> Option { self.slot_mut(inv_slot_id).and_then(|item| item.take()) } + /// Remove an item from an overflow slot + pub fn overflow_remove(&mut self, overflow_slot: usize) -> Option { + if overflow_slot < self.overflow_items.len() { + Some(self.overflow_items.remove(overflow_slot)) + } else { + None + } + } + /// Remove just one item from the slot pub fn take( &mut self, @@ -579,6 +606,34 @@ impl Inventory { } } + /// Takes half of the items from an overflow slot + pub fn overflow_take_half( + &mut self, + overflow_slot: usize, + ability_map: &AbilityMap, + msm: &MaterialStatManifest, + ) -> Option { + if let Some(item) = self.overflow_items.get_mut(overflow_slot) { + if item.is_stackable() && item.amount() > 1 { + let mut return_item = item.duplicate(ability_map, msm); + let returning_amount = item.amount() / 2; + item.decrease_amount(returning_amount).ok()?; + return_item.set_amount(returning_amount).expect( + "return_item.amount() = item.amount() / 2 < item.amount() (since \ + item.amount() ≥ 1) ≤ item.max_amount() = return_item.max_amount(), since \ + return_item is a duplicate of item", + ); + Some(return_item) + } else if overflow_slot < self.overflow_items.len() { + Some(self.overflow_items.remove(overflow_slot)) + } else { + None + } + } else { + None + } + } + /// Takes all items from the inventory pub fn drain(&mut self) -> impl Iterator + '_ { self.slots_mut() @@ -781,6 +836,15 @@ impl Inventory { self.loadout.swap_slots(slot_a, slot_b, time); Vec::new() }, + (Slot::Overflow(overflow_slot), Slot::Inventory(inv_slot)) + | (Slot::Inventory(inv_slot), Slot::Overflow(overflow_slot)) => { + self.move_overflow_item(overflow_slot, inv_slot); + Vec::new() + }, + // Items from overflow slots cannot be equipped until moved into a real inventory slot + (Slot::Overflow(_), Slot::Equip(_)) | (Slot::Equip(_), Slot::Overflow(_)) => Vec::new(), + // Items cannot be moved between overflow slots + (Slot::Overflow(_), Slot::Overflow(_)) => Vec::new(), } } @@ -976,6 +1040,8 @@ impl Inventory { self.loadout .repair_item_at_slot(equip_slot, ability_map, msm); }, + // Items in overflow slots cannot be repaired until they are moved to a real slot + Slot::Overflow(_) => {}, } } diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index 37ca0c981d..1fe38fd41b 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -15,6 +15,7 @@ pub enum SlotError { pub enum Slot { Inventory(InvSlotId), Equip(EquipSlot), + Overflow(usize), } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -111,6 +112,7 @@ impl Slot { match (self, item_kind) { (Self::Inventory(_), _) => true, (Self::Equip(slot), item_kind) => slot.can_hold(item_kind), + (Self::Overflow(_), _) => true, } } } diff --git a/common/src/recipe.rs b/common/src/recipe.rs index ca500c68a4..c6ecdbb5d0 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1030,6 +1030,8 @@ impl RepairRecipeBook { if let Some(item) = match item { Slot::Equip(slot) => inv.equipped(slot), Slot::Inventory(slot) => inv.get(slot), + // Items in overflow slots cannot be repaired until item is moved to a real slot + Slot::Overflow(_) => None, } { if let Some(repair_recipe) = self.repair_recipe(item) { repair_recipe diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 67958d22b4..038fb49a68 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1141,6 +1141,9 @@ pub fn handle_manipulate_loadout( let inv_manip = InventoryManip::Use(slot); output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)); }, + InventoryAction::Use(Slot::Overflow(_)) => { + // Items in overflow slots cannot be used until moved to a real slot + }, InventoryAction::ToggleSpriteLight(pos, enable) => { if matches!(pos.kind, Volume::Terrain) { let sprite_interact = sprite_interact::SpriteInteractKind::ToggleLight(enable); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 74c9e92ecc..152a28107b 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -559,6 +559,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } Some(InventoryUpdateEvent::Used) }, + // Items in overflow slots cannot be used + Slot::Overflow(_) => None, }; drop(inventories); @@ -663,6 +665,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let item = match slot { Slot::Inventory(slot) => inventory.take_half(slot, &ability_map, &msm), Slot::Equip(_) => None, + Slot::Overflow(_) => None, }; if let Some(item) = item { @@ -686,6 +689,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let item = match slot { Slot::Inventory(slot) => inventory.remove(slot), Slot::Equip(slot) => inventory.replace_loadout_item(slot, None, time), + Slot::Overflow(slot) => inventory.overflow_remove(slot), }; // FIXME: We should really require the drop and write to be atomic! @@ -717,6 +721,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let item = match slot { Slot::Inventory(slot) => inventory.take_half(slot, ability_map, &msm), Slot::Equip(_) => None, + Slot::Overflow(o) => inventory.overflow_take_half(o, ability_map, &msm), }; // FIXME: We should really require the drop and write to be atomic! diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index af7439ba8b..6f81791c84 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -1576,6 +1576,7 @@ impl<'a> Widget for Crafting<'a> { modifier: craft_slot_2.and_then(|slot| match slot { Slot::Inventory(slot) => Some(slot), Slot::Equip(_) => None, + Slot::Overflow(_) => None, }), }); } @@ -1736,6 +1737,7 @@ impl<'a> Widget for Crafting<'a> { .and_then(|slot| match slot { Slot::Inventory(slot) => self.inventory.get(slot), Slot::Equip(_) => None, + Slot::Overflow(_) => None, }) .and_then(|item| item.item_definition_id().itemdef_id().map(String::from)) { @@ -1746,6 +1748,7 @@ impl<'a> Widget for Crafting<'a> { .and_then(|slot| match slot { Slot::Inventory(slot) => self.inventory.get(slot), Slot::Equip(_) => None, + Slot::Overflow(_) => None, }) .and_then(|item| { item.item_definition_id().itemdef_id().map(String::from) @@ -1769,6 +1772,7 @@ impl<'a> Widget for Crafting<'a> { if let Some(item) = match craft_slot_1 { Some(Slot::Inventory(slot)) => self.inventory.get(slot), Some(Slot::Equip(slot)) => self.inventory.equipped(slot), + Some(Slot::Overflow(_)) => None, None => None, } { if let Some(recipe) = self.client.repair_recipe_book().repair_recipe(item) { diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 3174d845b7..91527cd6fb 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -286,6 +286,7 @@ impl CraftSlot { match self.slot { Some(Slot::Inventory(slot)) => inv.get(slot), Some(Slot::Equip(slot)) => inv.equipped(slot), + Some(Slot::Overflow(_)) => None, None => None, } } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 12213465cd..cfb577db6c 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1753,6 +1753,7 @@ impl PlayState for SessionState { move_allowed = false; } }, + Slot::Overflow(_) => {}, } }; } @@ -2021,6 +2022,7 @@ impl PlayState for SessionState { let item = match item { Slot::Equip(slot) => inventory.equipped(slot), Slot::Inventory(slot) => inventory.get(slot), + Slot::Overflow(_) => None, }?; let repair_recipe = client.repair_recipe_book().repair_recipe(item)?;