@ -1,27 +1,34 @@
#![ allow(dead_code) ]
use std ::collections ::{ HashMap , BTreeMap } ;
use libpso ::character ::character ; //::InventoryItem;
use thiserror ::Error ;
use crate ::entity ::gateway ::EntityGateway ;
use crate ::entity ::character ::CharacterEntity ;
use crate ::entity ::character ::{ CharacterEntity , CharacterEntityId } ;
use crate ::entity ::item ::{ ItemEntityId , ItemEntity , ItemDetail , ItemLocation } ;
use crate ::entity ::item ::{ Meseta , NewItemEntity } ;
use crate ::entity ::item ::tool ::Tool ;
use crate ::ship ::map ::MapArea ;
use crate ::ship ::drops ::{ ItemDrop , ItemDropType } ;
use crate ::ship ::ship ::ClientState ;
use crate ::ship ::location ::{ AreaClient , RoomId } ;
#[ derive(Debug, Copy, Clone, Hash, PartialEq, Eq) ]
pub struct ActiveItemId ( pub u32 ) ;
#[ derive(Debug) ]
#[ derive(Debug, Copy, Clone, Hash, PartialEq, Eq) ]
struct RoomItemId ( RoomId , u32 ) ;
#[ derive(Debug, Copy, Clone, Hash, PartialEq, Eq) ]
pub struct ClientItemId ( pub u32 ) ;
#[ derive(Debug, Clone) ]
enum ActiveItemEntityId {
Individual ( ItemEntityId ) ,
Stacked ( Vec < ItemEntityId > ) ,
Meseta ( Meseta ) ,
}
#[ derive(Debug) ]
#[ derive(Debug, Clone) ]
enum HeldItemType {
Individual ( ItemDetail ) ,
Stacked ( Tool , usize ) ,
@ -48,21 +55,7 @@ impl HeldItemType {
}
}
#[ derive(Debug) ]
pub struct InventoryItem {
id : ActiveItemId ,
item : HeldItemType ,
//slot: usize,
equipped : bool ,
}
#[ derive(Debug) ]
pub struct BankItem {
id : ActiveItemId ,
item : HeldItemType ,
}
#[ derive(Debug) ]
#[ derive(Debug, Clone) ]
pub enum FloorItemType {
Individual ( ItemDetail ) ,
Stacked ( Tool , usize ) ,
@ -93,9 +86,18 @@ impl FloorItemType {
}
}
#[ derive(Debug) ]
#[ derive(Debug, Clone) ]
pub struct InventoryItem {
entity_id : ActiveItemEntityId ,
item_id : ClientItemId ,
item : HeldItemType ,
equipped : bool ,
}
#[ derive(Debug, Clone) ]
pub struct FloorItem {
pub id : ActiveItemId ,
entity_id : ActiveItemEntityId ,
pub item_id : ClientItemId ,
pub item : FloorItemType ,
pub map_area : MapArea ,
pub x : f32 ,
@ -104,12 +106,15 @@ pub struct FloorItem {
}
#[ derive(Debug) ]
pub enum InventoryError {
pub struct BankItem {
id : ActiveItemId ,
item : HeldItemType ,
}
pub struct CharacterInventory ( Vec < InventoryItem > ) ;
impl CharacterInventory {
pub struct CharacterInventory < 'a > ( & 'a Vec < InventoryItem > ) ;
impl < 'a > CharacterInventory < 'a > {
pub fn as_client_inventory_items ( & self ) -> [ character ::InventoryItem ; 30 ] {
self . 0. iter ( )
. enumerate ( )
@ -117,7 +122,7 @@ impl CharacterInventory {
let bytes = item . item . as_client_bytes ( ) ;
inventory [ slot ] . data1 . copy_from_slice ( & bytes [ 0 . . 12 ] ) ;
inventory [ slot ] . data2 . copy_from_slice ( & bytes [ 12 . . 16 ] ) ;
inventory [ slot ] . item_id = item . id . 0 ;
inventory [ slot ] . item_id = item . item_i d . 0 ;
// does this do anything?
inventory [ slot ] . equipped = if item . equipped { 1 } else { 0 } ;
// because this actually equips the item
@ -126,42 +131,60 @@ impl CharacterInventory {
} )
}
pub fn add_item ( & mut self , item : InventoryItem ) -> Result < usize , InventoryError > {
self . 0. push ( item ) ;
Ok ( self . count ( ) - 1 )
}
pub fn count ( & self ) -> usize {
self . 0. len ( )
}
}
#[ derive(Debug )]
#[ derive(Error, Debug) ]
#[ error( " " ) ]
pub enum ItemManagerError {
EntityGatewayError ,
NoSuchItemId ( ClientItemId ) ,
NoCharacter ( CharacterEntityId ) ,
CouldNotAddToInventory ( FloorItem ) ,
//ItemBelongsToOtherPlayer,
Idunnoman ,
}
pub struct ItemManager {
id : usize ,
active_to_entity : HashMap < ActiveItemId , ActiveItemEntityId > ,
id_counter : u32 ,
character_inventory : HashMap < CharacterEntityId , Vec < InventoryItem > > ,
character_floor : HashMap < CharacterEntityId , Vec < FloorItem > > ,
character_item_id_counter : HashMap < CharacterEntityId , u32 > ,
character_room : HashMap < CharacterEntityId , RoomId > ,
room_floor : HashMap < RoomId , Vec < FloorItem > > ,
room_item_id_counter : HashMap < RoomId , u32 > ,
}
impl ItemManager {
pub fn new ( ) -> ItemManager {
ItemManager {
id : 0 ,
active_to_entity : HashMap ::new ( )
id_counter : 0 ,
character_inventory : HashMap ::new ( ) ,
character_floor : HashMap ::new ( ) ,
character_item_id_counter : HashMap ::new ( ) ,
character_room : HashMap ::new ( ) ,
room_floor : HashMap ::new ( ) ,
room_item_id_counter : HashMap ::new ( ) ,
}
}
fn next_id ( & mut self ) -> ActiveItemId {
self . id + = 1 ;
ActiveItemId ( self . id as u32 )
pub fn next_global_item_id ( & mut self ) -> ClientItemId {
self . id_counter + = 1 ;
ClientItemId ( self . id_counter )
}
pub fn next_drop_item_id ( & mut self , room_id : RoomId ) -> ClientItemId {
let next_id = self . room_item_id_counter . entry ( room_id ) . or_insert ( 0xF0000000 ) ;
* next_id + = 1 ;
ClientItemId ( * next_id )
}
pub fn get_character_inventory < EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , character : & CharacterEntity ) -> CharacterInventory {
// TODO: Result
pub fn load_character < EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , character : & CharacterEntity ) {
let items = entity_gateway . get_items_by_character ( & character ) ;
let inventory_items = items . into_iter ( )
. filter_map ( | item | {
@ -190,18 +213,151 @@ impl ItemManager {
} )
. into_iter ( )
. map ( | ( _slot , ( held_item , entity_id , equipped ) ) | {
let id = self . next_id ( ) ;
self . active_to_entity . insert ( id , entity_id ) ;
let id = self . next_global_item_id ( ) ;
InventoryItem {
id : id ,
entity_id : entity_id ,
item_id : id ,
item : held_item ,
equipped : equipped ,
}
} ) ;
CharacterInventory ( inventory_items . take ( 30 ) . collect ( ) )
let k = inventory_items . take ( 30 ) . collect ( ) ;
self . character_inventory . insert ( character . id , k ) ;
}
pub fn add_character_to_room ( & mut self , room_id : RoomId , character : & CharacterEntity , area_client : AreaClient ) {
let base_id = ( ( area_client . local_client . id ( ) as u32 ) < < 21 ) | 0x10000 ;
let inventory = self . character_inventory . get_mut ( & character . id ) . unwrap ( ) ;
for ( i , item ) in inventory . iter_mut ( ) . enumerate ( ) {
item . item_id = ClientItemId ( base_id + i as u32 ) ;
}
self . character_room . insert ( character . id , room_id ) ;
self . character_floor . insert ( character . id , Vec ::new ( ) ) ;
self . room_floor . entry ( room_id ) . or_insert ( Vec ::new ( ) ) ;
self . character_item_id_counter . insert ( character . id , base_id + inventory . len ( ) as u32 ) ;
}
pub fn get_character_inventory ( & self , character : & CharacterEntity ) -> Result < CharacterInventory , ItemManagerError > {
Ok ( CharacterInventory ( self . character_inventory . get ( & character . id )
. ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ) )
}
pub fn remove_character_from_room ( & mut self , character : & CharacterEntity ) {
self . character_inventory . remove ( & character . id ) ;
self . character_floor . remove ( & character . id ) ;
self . character_room . remove ( & character . id )
. as_ref ( )
. map ( | room | {
if self . character_room . iter ( ) . find ( | ( _ , r ) | * r = = room ) . is_none ( ) {
self . room_floor . remove ( room ) ;
}
} ) ;
}
pub fn get_inventory_item_by_id ( & self , character : & CharacterEntity , item_id : ClientItemId ) -> Result < InventoryItem , ItemManagerError > {
let inventory = self . character_inventory . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
inventory . iter ( )
. filter ( | item | {
item . item_id = = item_id
} )
. nth ( 0 )
. ok_or ( ItemManagerError ::NoSuchItemId ( item_id ) )
. map ( Clone ::clone )
}
pub fn get_floor_item_by_id ( & self , character : & CharacterEntity , item_id : ClientItemId ) -> Result < FloorItem , ItemManagerError > {
let floor = self . character_floor . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let room = self . character_room . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let shared_floor = self . room_floor . get ( room ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
floor . iter ( )
. chain ( shared_floor . iter ( ) )
. filter ( | item | {
item . item_id = = item_id
} )
. nth ( 0 )
. ok_or ( ItemManagerError ::NoSuchItemId ( item_id ) )
. map ( Clone ::clone )
}
pub fn character_picks_up_item < EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , character : & mut CharacterEntity , floor_item : FloorItem ) -> Result < ( ) , ItemManagerError > {
let local_floor = self . character_floor . get_mut ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let inventory = self . character_inventory . get_mut ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let room_id = self . character_room . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let shared_floor = self . room_floor . get_mut ( & room_id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
if let Some ( _ ) = local_floor . iter ( ) . find ( | i | i . item_id = = floor_item . item_id ) {
local_floor . retain ( | item | {
item . item_id ! = floor_item . item_id
} ) ;
}
else if let Some ( _ ) = shared_floor . iter ( ) . find ( | i | i . item_id = = floor_item . item_id ) {
shared_floor . retain ( | item | {
item . item_id ! = floor_item . item_id
} ) ;
}
else {
return Err ( ItemManagerError ::NoSuchItemId ( floor_item . item_id ) )
}
if inventory . len ( ) > = 30 {
return Err ( ItemManagerError ::CouldNotAddToInventory ( floor_item ) ) ;
}
match floor_item . item {
FloorItemType ::Individual ( item ) = > {
let inventory_item = InventoryItem {
entity_id : floor_item . entity_id ,
item_id : floor_item . item_id ,
item : HeldItemType ::Individual ( item . clone ( ) ) ,
equipped : false ,
} ;
if let ActiveItemEntityId ::Individual ( item_id ) = & inventory_item . entity_id {
entity_gateway . save_item ( & ItemEntity {
id : * item_id ,
item : item ,
location : ItemLocation ::Inventory {
character_id : character . id ,
slot : inventory . len ( ) ,
equipped : false ,
} ,
} ) ; // TODO: error check
inventory . push ( inventory_item ) ;
} // else something went very wrong TODO: log it
} ,
FloorItemType ::Stacked ( tool , usize ) = > {
let inventory_item = InventoryItem {
entity_id : floor_item . entity_id ,
item_id : floor_item . item_id ,
item : HeldItemType ::Stacked ( tool , usize ) ,
equipped : false ,
} ;
if let ActiveItemEntityId ::Stacked ( item_ids ) = & inventory_item . entity_id {
for item_id in item_ids {
entity_gateway . save_item ( & ItemEntity {
id : * item_id ,
item : ItemDetail ::Tool ( tool ) ,
location : ItemLocation ::Inventory {
character_id : character . id ,
slot : inventory . len ( ) ,
equipped : false ,
} ,
} ) ; // TODO: error check
} ;
inventory . push ( inventory_item ) ;
} // else something went very wrong TODO: log it
} ,
FloorItemType ::Meseta ( meseta ) = > {
character . meseta + = meseta . 0 ;
entity_gateway . save_character ( & character ) ;
}
}
Ok ( ( ) )
}
pub fn drop_item_on_local_floor < EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , character : & CharacterEntity , item_drop : ItemDrop ) -> Result < FloorItem , ItemManagerError > {
pub fn enemy_ drop_item_on_local_floor< EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , character : & CharacterEntity , item_drop : ItemDrop ) -> Result < & FloorItem , ItemManagerError > {
let item = match item_drop . item {
ItemDropType ::Weapon ( w ) = > FloorItemType ::Individual ( ItemDetail ::Weapon ( w ) ) ,
ItemDropType ::Armor ( w ) = > FloorItemType ::Individual ( ItemDetail ::Armor ( w ) ) ,
@ -214,8 +370,8 @@ impl ItemManager {
//ItemDropType::Tool(t) if !t.is_stackable() => FloorItemType::Individual(ItemDetail::Tool(w)),
ItemDropType ::Meseta ( m ) = > FloorItemType ::Meseta ( Meseta ( m ) )
} ;
let active_ entity_ids = match & item {
let entity_id = match & item {
FloorItemType ::Individual ( i ) = > {
let entity = entity_gateway . create_item ( NewItemEntity {
item : i . clone ( ) ,
@ -250,70 +406,86 @@ impl ItemManager {
FloorItemType ::Meseta ( m ) = > ActiveItemEntityId ::Meseta ( m . clone ( ) ) ,
} ;
let id = self . next_id ( ) ;
self . active_to_entity . insert ( id , active_entity_ids ) ;
Ok ( FloorItem {
id : id ,
let room_id = * self . character_room . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let item_id = self . next_drop_item_id ( room_id ) ;
let floor_item = FloorItem {
entity_id : entity_id ,
item_id : item_id ,
item : item ,
map_area : item_drop . map_area ,
x : item_drop . x ,
y : item_drop . y ,
z : item_drop . z ,
} )
} ;
self . character_floor . entry ( character . id ) . or_insert ( Vec ::new ( ) ) . push ( floor_item ) ;
self . character_floor . get ( & character . id ) . ok_or ( ItemManagerError ::Idunnoman ) ? . last ( ) . ok_or ( ItemManagerError ::Idunnoman )
}
pub fn move_item_from_floor_to_inventory < EG : EntityGateway > ( & mut self , entity_gateway : & mut EG , client : & mut ClientState , floor_item : FloorItem ) -> Result < ( ) , ItemManagerError > {
match floor_item . item {
FloorItemType ::Individual ( item ) = > {
let inventory_item = InventoryItem {
id : floor_item . id ,
item : HeldItemType ::Individual ( item . clone ( ) ) ,
equipped : false ,
} ;
pub fn player_drop_item_on_shared_floor < EG : EntityGateway > ( & mut self ,
entity_gateway : & mut EG ,
character : & CharacterEntity ,
inventory_item : InventoryItem ,
item_drop_location : ( MapArea , f32 , f32 , f32 ) )
-> Result < ( ) , ItemManagerError > {
let inventory = self . character_inventory . get_mut ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let room_id = self . character_room . get ( & character . id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
let shared_floor = self . room_floor . get_mut ( & room_id ) . ok_or ( ItemManagerError ::NoCharacter ( character . id ) ) ? ;
inventory
. drain_filter ( | i | i . item_id = = inventory_item . item_id )
. nth ( 0 )
. ok_or ( ItemManagerError ::NoSuchItemId ( inventory_item . item_id ) ) ? ;
let item_entity_id = self . active_to_entity . get ( & floor_item . id ) . unwrap ( ) ; // TODO: unwrap
if let ActiveItemEntityId ::Individual ( item_id ) = item_entity_id {
let slot = client . inventory . add_item ( inventory_item ) . unwrap ( ) ; // TODO: unwrap
let room_floor_item = FloorItem {
entity_id : inventory_item . entity_id ,
item_id : inventory_item . item_id ,
item : match inventory_item . item {
HeldItemType ::Individual ( item ) = > FloorItemType ::Individual ( item ) ,
HeldItemType ::Stacked ( tool , count ) = > FloorItemType ::Stacked ( tool , count ) ,
} ,
map_area : item_drop_location . 0 ,
x : item_drop_location . 1 ,
y : item_drop_location . 2 ,
z : item_drop_location . 3 ,
} ;
match & room_floor_item . item {
FloorItemType ::Individual ( item ) = > {
if let ActiveItemEntityId ::Individual ( item_id ) = & room_floor_item . entity_id {
entity_gateway . save_item ( & ItemEntity {
id : * item_id ,
item : item ,
location : ItemLocation ::Inventory {
character_id : client . character . id ,
slot : slot ,
equipped : false ,
} ,
item : item . clone ( ) ,
location : ItemLocation ::SharedFloor {
map_area : item_drop_location . 0 ,
x : item_drop_location . 1 ,
y : item_drop_location . 2 ,
z : item_drop_location . 3 ,
}
} ) ; // TODO: error check
} // else something went very wrong TODO: log it
} // else something went very wrong: TODO: log it
} ,
FloorItemType ::Stacked ( tool , usize ) = > {
let inventory_item = InventoryItem {
id : floor_item . id ,
item : HeldItemType ::Stacked ( tool , usize ) ,
equipped : false ,
} ;
let item_entity_id = self . active_to_entity . get ( & floor_item . id ) . unwrap ( ) ; // TODO: unwrap
if let ActiveItemEntityId ::Stacked ( item_ids ) = item_entity_id {
let slot = client . inventory . add_item ( inventory_item ) . unwrap ( ) ; // TODO: unwrap
FloorItemType ::Stacked ( tool , _count ) = > {
if let ActiveItemEntityId ::Stacked ( item_ids ) = & room_floor_item . entity_id {
for item_id in item_ids {
entity_gateway . save_item ( & ItemEntity {
id : * item_id ,
item : ItemDetail ::Tool ( tool ) ,
location : ItemLocation ::Inventory {
character_id : client . character . id ,
slot : slot ,
equipped : false ,
item : ItemDetail ::Tool ( * tool ) ,
location : ItemLocation ::SharedFloor {
map_area : item_drop_location . 0 ,
x : item_drop_location . 1 ,
y : item_drop_location . 2 ,
z : item_drop_location . 3 ,
} ,
} ) ; // TODO: error check
}
} // else something went very wrong TODO: log it
} ,
FloorItemType ::Meseta ( meseta ) = > {
client . character . meseta + = meseta . 0 ;
entity_gateway . save_character ( & client . character ) ;
}
_ = > { } , // can meseta get here?
}
shared_floor . push ( room_floor_item ) ;
Ok ( ( ) )
}
}