use crate::ship::items::ClientItemId; use crate::entity::item::{ItemEntityId, ItemDetail}; use crate::entity::item::Meseta; use crate::entity::item::tool::Tool; use crate::ship::map::MapArea; use crate::ship::items::inventory::{IndividualInventoryItem, StackedInventoryItem, InventoryItemHandle}; #[derive(Debug, Clone)] pub struct IndividualFloorItem { pub entity_id: ItemEntityId, pub item_id: ClientItemId, pub item: ItemDetail, pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, } #[derive(Debug, Clone)] pub struct StackedFloorItem { pub entity_ids: Vec, pub item_id: ClientItemId, pub tool: Tool, pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, } impl StackedFloorItem { pub fn count(&self) -> usize { self.entity_ids.len() } pub fn as_client_bytes(&self) -> [u8; 16] { self.tool.as_stacked_bytes(self.count()) } } #[derive(Debug, Clone)] pub struct MesetaFloorItem { pub item_id: ClientItemId, pub meseta: Meseta, pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, } #[derive(Debug, Clone)] pub enum FloorItem { Individual(IndividualFloorItem), Stacked(StackedFloorItem), Meseta(MesetaFloorItem), } impl FloorItem { pub fn item_id(&self) -> ClientItemId { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.item_id }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.item_id }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.item_id } } } pub fn x(&self) -> f32 { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.x }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.x }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.x } } } pub fn y(&self) -> f32 { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.y }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.y }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.y } } } pub fn z(&self) -> f32 { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.z }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.z }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.z } } } pub fn map_area(&self) -> MapArea { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.map_area }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.map_area }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.map_area } } } pub fn as_client_bytes(&self) -> [u8; 16] { match self { FloorItem::Individual(individual_floor_item) => { individual_floor_item.item.as_client_bytes() }, FloorItem::Stacked(stacked_floor_item) => { stacked_floor_item.as_client_bytes() }, FloorItem::Meseta(meseta_floor_item) => { meseta_floor_item.meseta.as_bytes() } } } } pub struct FloorItemHandle<'a> { floor: &'a mut RoomFloorItems, index: usize, } impl<'a> FloorItemHandle<'a> { pub fn item(&'a self) -> Option<&'a FloorItem> { self.floor.0.get(self.index) } pub fn remove_from_floor(self) { self.floor.0.remove(self.index); } } // TODO: floors should keep track of their own item_ids #[derive(Debug, Default)] pub struct RoomFloorItems(Vec); impl RoomFloorItems { pub fn add_item(&mut self, item: FloorItem) { self.0.push(item); } pub fn remove_item(&mut self, item_id: &ClientItemId) { self.0.retain(|item| item.item_id() != *item_id); } // TODO: &ClientItemId pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> { self.0.iter().find(|item| item.item_id() == item_id) } // TODO: &ClientItemId pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option { let index = self.0.iter().position(|item| item.item_id() == item_id)?; Some(FloorItemHandle { floor: self, index, }) } pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option { self.0 .drain_filter(|i| i.item_id() == item_id) .next() } pub fn drop_individual_inventory_item(&mut self, individual_inventory_item: IndividualInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &IndividualFloorItem { self.0.push(FloorItem::Individual(IndividualFloorItem { entity_id: individual_inventory_item.entity_id, item_id: individual_inventory_item.item_id, item: individual_inventory_item.item, map_area: item_drop_location.0, x: item_drop_location.1, y: item_drop_location.2, z: item_drop_location.3, })); match self.0.last().unwrap() { FloorItem::Individual(item) => item, _ => unreachable!(), } } pub fn drop_stacked_inventory_item(&mut self, stacked_inventory_item: StackedInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &StackedFloorItem { self.0.push(FloorItem::Stacked(StackedFloorItem { entity_ids: stacked_inventory_item.entity_ids, item_id: stacked_inventory_item.item_id, tool: stacked_inventory_item.tool, map_area: item_drop_location.0, x: item_drop_location.1, y: item_drop_location.2, z: item_drop_location.3, })); match self.0.last().unwrap() { FloorItem::Stacked(item) => item, _ => unreachable!(), } } // TODO: Result // TODO: if consumed_item is not a tool items do not get placed back into inventory (should I care?) pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> { let consumed_item = inventory_item.consume(amount).ok()?; if let ItemDetail::Tool(tool) = consumed_item.item() { self.0.push(FloorItem::Stacked(StackedFloorItem { entity_ids: consumed_item.entity_ids(), item_id: new_item_id, tool, map_area: item_drop_location.0, x: item_drop_location.1, y: item_drop_location.2, z: item_drop_location.3, })) } else { return None } match self.0.last().unwrap() { FloorItem::Stacked(item) => Some(item), _ => unreachable!(), } } }