From dcef56dcace2142ddef7b183331f726cc2613beb Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 20:39:23 -0600 Subject: [PATCH 01/14] add get_room/get_lobby to ClientLocation --- src/ship/location.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/ship/location.rs b/src/ship/location.rs index e3b1c78..bc15201 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -52,6 +52,8 @@ pub enum JoinLobbyError { #[derive(Error, Debug, PartialEq)] #[error("")] pub enum GetAreaError { + NotInRoom, + NotInLobby, InvalidClient, } @@ -331,6 +333,24 @@ impl ClientLocation { .map(Clone::clone) } + pub fn get_room(&self, id: ClientId) -> Result { + if let RoomLobby::Room(room) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { + Ok(*room) + } + else { + Err(GetAreaError::NotInRoom) + } + } + + pub fn get_lobby(&self, id: ClientId) -> Result { + if let RoomLobby::Lobby(lobby) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { + Ok(*lobby) + } + else { + Err(GetAreaError::NotInLobby) + } + } + pub fn remove_client_from_area(&mut self, id: ClientId) -> Result<(), ClientRemovalError> { let area = self.client_location.get_mut(&id).ok_or(ClientRemovalError::ClientNotInArea)?; let client_list = match area { From d773002eaaf39747d5b9c881f94f2ff285463fb7 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:50:34 -0600 Subject: [PATCH 02/14] rawmapenemy unknown3 -> map_area --- src/ship/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ship/map.rs b/src/ship/map.rs index d548056..7119976 100644 --- a/src/ship/map.rs +++ b/src/ship/map.rs @@ -16,7 +16,7 @@ struct RawMapEnemy { id: u32, _unknown1: u16, children: u16, - _unknown3: u16, + map_area: u16, _unknown4: u16, section: u16, wave_idd: u16, @@ -42,7 +42,7 @@ impl RawMapEnemy { id: cursor.read_u32::()?, _unknown1: cursor.read_u16::()?, children: cursor.read_u16::()?, - _unknown3: cursor.read_u16::()?, + map_area: cursor.read_u16::()?, _unknown4: cursor.read_u16::()?, section: cursor.read_u16::()?, wave_idd: cursor.read_u16::()?, From 0e3b305f95b68623f64b8bdfa4a79a2eeee09d21 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:53:11 -0600 Subject: [PATCH 03/14] add some vars to MapEnemy, error types to misc map functions, and MapArea -> u8 --- src/ship/map.rs | 103 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/src/ship/map.rs b/src/ship/map.rs index 7119976..4b60645 100644 --- a/src/ship/map.rs +++ b/src/ship/map.rs @@ -7,6 +7,7 @@ use std::fs::File; use byteorder::{LittleEndian, ReadBytesExt}; use rand::Rng; +use thiserror::Error; use crate::ship::monster::MonsterType; use crate::ship::room::Episode; @@ -65,18 +66,22 @@ impl RawMapEnemy { } -#[derive(Debug)] +#[derive(Error, Debug)] +#[error("")] enum MapEnemyError { UnknownEnemyId(u32), + MapAreaError(#[from] MapAreaError), } #[derive(Debug, Copy, Clone)] pub struct MapEnemy { pub monster: MonsterType, + pub map_area: MapArea, hp: u32, - // other stats from bp.n - dead: bool, + // TODO: other stats from battleparam + pub dropped_item: bool, + pub gave_exp: bool, } impl MapEnemy { @@ -169,16 +174,20 @@ impl MapEnemy { Ok(MapEnemy { monster: monster, + map_area: MapArea::from_value(&episode, enemy.map_area as u32)?, hp: 0, - dead: false, + dropped_item: false, + gave_exp: false, }) } - fn new(monster: MonsterType) -> MapEnemy { + fn new(monster: MonsterType, map_area: MapArea) -> MapEnemy { MapEnemy { monster: monster, + map_area: map_area, hp: 0, - dead: false, + dropped_item: false, + gave_exp: false, } } } @@ -340,7 +349,7 @@ enum MapVariantMode { Offline, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum MapArea { Pioneer2Ep1, Forest1, @@ -359,12 +368,14 @@ pub enum MapArea { DarkFalz, } +#[derive(Error, Debug)] +#[error("")] pub enum MapAreaError { UnknownMapArea(u32), } impl MapArea { - pub fn from_value(episode: Episode, area: u32) -> Result { + pub fn from_value(episode: &Episode, area: u32) -> Result { match (episode, area) { (Episode::One, 0) => Ok(MapArea::Pioneer2Ep1), (Episode::One, 1) => Ok(MapArea::Forest1), @@ -404,6 +415,26 @@ impl MapArea { _ => None } } + + pub fn area_value(&self) -> u8 { + match self { + MapArea::Pioneer2Ep1 => 0, + MapArea::Forest1 => 1, + MapArea::Forest2 => 2, + MapArea::Caves1 => 3, + MapArea::Caves2 => 4, + MapArea::Caves3 => 5, + MapArea::Mines1 => 6, + MapArea::Mines2 => 7, + MapArea::Ruins1 => 8, + MapArea::Ruins2 => 9, + MapArea::Ruins3 => 10, + MapArea::Dragon => 11, + MapArea::DeRolLe => 12, + MapArea::VolOpt => 13, + MapArea::DarkFalz => 14, + } + } } @@ -516,76 +547,76 @@ fn enemy_data_from_map_data(path: PathBuf, episode: &Episode) -> Vec { for _ in 0..30 { - monsters.push(Some(MapEnemy::new(MonsterType::Mothmant))); + monsters.push(Some(MapEnemy::new(MonsterType::Mothmant, monster.map_area))); } }, MonsterType::PofuillySlime => { for _ in 0..4 { - monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime))); + monsters.push(Some(MapEnemy::new(MonsterType::PofuillySlime, monster.map_area))); } }, MonsterType::PanArms => { - monsters.push(Some(MapEnemy::new(MonsterType::Hidoom))); - monsters.push(Some(MapEnemy::new(MonsterType::Migium))); + monsters.push(Some(MapEnemy::new(MonsterType::Hidoom, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::Migium, monster.map_area))); }, MonsterType::SinowBeat => { for _ in 0..4 { - monsters.push(Some(MapEnemy::new(MonsterType::SinowBeat))); + monsters.push(Some(MapEnemy::new(MonsterType::SinowBeat, monster.map_area))); } }, MonsterType::SinowGold => { for _ in 0..4 { - monsters.push(Some(MapEnemy::new(MonsterType::SinowGold))); + monsters.push(Some(MapEnemy::new(MonsterType::SinowGold, monster.map_area))); } }, MonsterType::Canane => { for _ in 0..8 { - monsters.push(Some(MapEnemy::new(MonsterType::RingCanadine))); + monsters.push(Some(MapEnemy::new(MonsterType::RingCanadine, monster.map_area))); } }, MonsterType::ChaosSorcerer => { - monsters.push(Some(MapEnemy::new(MonsterType::BeeR))); - monsters.push(Some(MapEnemy::new(MonsterType::BeeL))); + monsters.push(Some(MapEnemy::new(MonsterType::BeeR, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::BeeL, monster.map_area))); }, MonsterType::Bulclaw => { for _ in 0..4 { - monsters.push(Some(MapEnemy::new(MonsterType::Claw))); + monsters.push(Some(MapEnemy::new(MonsterType::Claw, monster.map_area))); } }, MonsterType::DeRolLe => { for _ in 0..10 { - monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeBody))); + monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeBody, monster.map_area))); } for _ in 0..9 { - monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeMine))); + monsters.push(Some(MapEnemy::new(MonsterType::DeRolLeMine, monster.map_area))); } }, MonsterType::VolOptPartA => { for _ in 0..6 { - monsters.push(Some(MapEnemy::new(MonsterType::VolOptPillar))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptPillar, monster.map_area))); } for _ in 0..24 { - monsters.push(Some(MapEnemy::new(MonsterType::VolOptMonitor))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptMonitor, monster.map_area))); } for _ in 0..2 { - monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); } - monsters.push(Some(MapEnemy::new(MonsterType::VolOptAmp))); - monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore))); - monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptAmp, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); }, // TOOD: this cares about difficulty (theres an ult-specific darvant?) MonsterType::DarkFalz => { for _ in 0..509 { - monsters.push(Some(MapEnemy::new(MonsterType::Darvant))); + monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area))); } - monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz3))); - monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz2))); - monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz1))); + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz3, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz2, monster.map_area))); + monsters.push(Some(MapEnemy::new(MonsterType::DarkFalz1, monster.map_area))); }, _ => { for _ in 0..enemy.children { - monsters.push(Some(MapEnemy::new(monster.monster))); + monsters.push(Some(MapEnemy::new(monster.monster, monster.map_area))); } } } @@ -596,6 +627,12 @@ fn enemy_data_from_map_data(path: PathBuf, episode: &Episode) -> Vec MapEnemy { - self.enemy_data[id].unwrap() + pub fn enemy_by_id(&self, id: usize) -> Result { + self.enemy_data[id].ok_or(MapsError::InvalidMonsterId(id)) } pub fn map_headers(&self) -> [u32; 0x20] { From 0eb9035badedb54d11c0b2ecb067ebcd76f6bf77 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:54:01 -0600 Subject: [PATCH 04/14] make TechniqueDisk and Tool Copy --- src/entity/item/tech.rs | 2 +- src/entity/item/tool.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/item/tech.rs b/src/entity/item/tech.rs index 8976904..b7faeaa 100644 --- a/src/entity/item/tech.rs +++ b/src/entity/item/tech.rs @@ -25,7 +25,7 @@ pub enum Technique { } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct TechniqueDisk { pub tech: Technique, pub level: u32, diff --git a/src/entity/item/tool.rs b/src/entity/item/tool.rs index 3583a63..dbde12b 100644 --- a/src/entity/item/tool.rs +++ b/src/entity/item/tool.rs @@ -394,7 +394,7 @@ impl ToolType { } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Tool { pub tool: ToolType, } From 160fc7f10b7e9d8f0facddaf2c0c873e895e58aa Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:55:12 -0600 Subject: [PATCH 05/14] pub ItemDrop --- src/ship/drops/mod.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 294eed4..0f2e7c3 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -1,3 +1,9 @@ +// TODO: there is some structure duplication that occurs here: +// the rare and box tables instantiate their own copies of the +// generic drop tables as they need them to apply their modifiers +// to their drops + + mod drop_table; mod rare_drop_table; mod generic_weapon; @@ -78,16 +84,18 @@ pub enum ItemDropType { Shield(shield::Shield), Unit(unit::Unit), Tool(tool::Tool), + //Tools(Vec), TechniqueDisk(tech::TechniqueDisk), Mag(mag::Mag), Meseta(u32), } -struct ItemDrop { - x: f32, - y: f32, - z: f32, - item: ItemDropType, +pub struct ItemDrop { + pub map_area: MapArea, + pub x: f32, + pub y: f32, + pub z: f32, + pub item: ItemDropType, } impl ItemDrop { From 375c97c95dca29a92ffa017d142e855c0cd87e9d Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:57:19 -0600 Subject: [PATCH 06/14] add MapsError to ShipError --- src/ship/ship.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 8936afd..4d31242 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -25,6 +25,7 @@ use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOM use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; use crate::ship::items; use crate::ship::room; +use crate::ship::map::MapsError; use crate::ship::packet::handler; pub const SHIP_PORT: u16 = 23423; @@ -39,6 +40,7 @@ pub enum ShipError { InvalidSlot(ClientId, u32), TooManyClients, ClientLocationError(#[from] ClientLocationError), + MapsError(#[from] MapsError), InvalidRoom(u32), } From 663797e55b694386abb4e858f0a27124bf0cdaf3 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 21:58:43 -0600 Subject: [PATCH 07/14] remove unneeded PartialEq derive --- src/entity/item/mod.rs | 6 +++--- src/ship/items.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 303edf4..0ee351f 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -15,7 +15,7 @@ pub struct ItemId(u32); #[derive(Debug, Clone, PartialEq, Eq)] pub struct BankName(String); -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum ItemLocation { Inventory { character_id: CharacterEntityId, @@ -96,13 +96,13 @@ impl ItemDetail { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct NewItemEntity { pub location: ItemLocation, pub item: ItemDetail, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct ItemEntity { pub id: ItemEntityId, pub location: ItemLocation, diff --git a/src/ship/items.rs b/src/ship/items.rs index 9dcd068..52de888 100644 --- a/src/ship/items.rs +++ b/src/ship/items.rs @@ -14,8 +14,8 @@ use crate::entity::item::mag::Mag; use crate::entity::item::Meseta; -#[derive(Debug, PartialEq)] -pub enum ItemInstance { +#[derive(Debug)] +enum ItemInstance { Individual(ItemEntity), Stacked(Vec), Meseta(Meseta), From 4a526e76735e4616cf9b2c1a3f4c4f7aa9132f00 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 22:01:05 -0600 Subject: [PATCH 08/14] basic item drop logic --- src/entity/item/mod.rs | 7 ++- src/ship/items.rs | 64 ++++++++++++++++++++--- src/ship/packet/builder/message.rs | 33 ++++++++++++ src/ship/packet/builder/mod.rs | 1 + src/ship/packet/handler/direct_message.rs | 62 ++++++++++++++++++++-- src/ship/packet/handler/message.rs | 2 +- src/ship/ship.rs | 7 +++ 7 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 src/ship/packet/builder/message.rs diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 0ee351f..8e3731d 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -7,6 +7,7 @@ pub mod unit; pub mod mag; use crate::entity::character::CharacterEntityId; +use crate::ship::map::MapArea; #[derive(PartialEq, Copy, Clone, Debug, Hash, Eq)] pub struct ItemEntityId(pub u32); @@ -27,8 +28,10 @@ pub enum ItemLocation { slot: BankName, }, Floor { - // floor: eventually - // x y z: ????? + map_area: MapArea, + x: f32, + y: f32, + z: f32, }, /*Destroyed { // marks an item that has been consumed in some way diff --git a/src/ship/items.rs b/src/ship/items.rs index 52de888..575c1ef 100644 --- a/src/ship/items.rs +++ b/src/ship/items.rs @@ -11,7 +11,9 @@ use crate::entity::item::shield::Shield; use crate::entity::item::unit::Unit; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; -use crate::entity::item::Meseta; +use crate::entity::item::{Meseta, NewItemEntity}; +use crate::ship::map::MapArea; +use crate::ship::drops::{ItemDrop, ItemDropType}; #[derive(Debug)] @@ -22,12 +24,12 @@ enum ItemInstance { } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct ActiveItemId(u32); +pub struct ActiveItemId(pub u32); #[derive(Debug)] pub struct ActiveItem { - id: ActiveItemId, + pub id: ActiveItemId, item: ItemInstance, } @@ -108,6 +110,14 @@ fn inventory_item_index(item: &ItemInstance) -> usize { } } +pub struct ActiveItemOnFloor { + pub map_area: MapArea, + pub x: f32, + pub y: f32, + pub z: f32, + pub item: ActiveItem, +} + fn stack_items(items: Vec) -> Vec { let mut stacks = HashMap::new(); @@ -138,8 +148,6 @@ pub struct ActiveItemDatabase { id: u32, } - - impl ActiveItemDatabase { pub fn new() -> ActiveItemDatabase { ActiveItemDatabase { @@ -155,7 +163,7 @@ impl ActiveItemDatabase { } } - // deactivate item + // TODO: deactivate item pub fn get_character_inventory(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> ActiveInventory { let items = entity_gateway.get_items_by_character(&character); @@ -173,6 +181,50 @@ impl ActiveItemDatabase { let activated = stacked.into_iter().map(|i| self.activate_item(i)); ActiveInventory(activated.take(30).collect()) } + + // TODO: Result + pub fn activate_item_drop(&mut self, entity_gateway: &mut EG, item_drop: ItemDrop) -> ActiveItemOnFloor { + let item_detail = match item_drop.item { + ItemDropType::Weapon(w) => Some(ItemDetail::Weapon(w)), + ItemDropType::Armor(w) => Some(ItemDetail::Armor(w)), + ItemDropType::Shield(w) => Some(ItemDetail::Shield(w)), + ItemDropType::Unit(w) => Some(ItemDetail::Unit(w)), + ItemDropType::Tool(w) => Some(ItemDetail::Tool(w)), + ItemDropType::TechniqueDisk(w) => Some(ItemDetail::TechniqueDisk(w)), + ItemDropType::Mag(w) => Some(ItemDetail::Mag(w)), + ItemDropType::Meseta(_) => None + }; + let item_instance = match item_detail { + Some(item) => { + let item_entity = entity_gateway.create_item(NewItemEntity { + item: item, + location: ItemLocation::Floor { + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + } + }).unwrap(); + stack_items(vec![item_entity]).pop().unwrap() + }, + None => { + let meseta = match item_drop.item { + ItemDropType::Meseta(m) => m, + _ => panic!(), + }; + ItemInstance::Meseta(Meseta(meseta)) + } + }; + let active_item = self.activate_item(item_instance); + + ActiveItemOnFloor { + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + item: active_item, + } + } } #[cfg(test)] diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs new file mode 100644 index 0000000..e724fc0 --- /dev/null +++ b/src/ship/packet/builder/message.rs @@ -0,0 +1,33 @@ +use std::collections::HashMap; +use libpso::packet::ship::*; +use libpso::packet::messages::*; +use crate::common::serverstate::ClientId; +use crate::common::leveltable::CharacterLevelTable; +use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; +use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; +use crate::ship::location::{ClientLocation, LobbyId, AreaClient, ClientLocationError}; +use crate::entity::character::CharacterEntity; +use crate::ship::items::{ActiveInventory, ActiveItemOnFloor}; +use crate::ship::packet::builder::{player_header, player_info}; +use std::convert::TryInto; +use libpso::character::character::{Inventory, InventoryItem}; +use libpso::utf8_to_utf16_array; + + +pub fn item_drop(client: u8, target: u8, item_drop: &ActiveItemOnFloor) -> Result { + let item_bytes = item_drop.item.as_client_bytes(); + Ok(ItemDrop { + client: client, + target: target, + area: item_drop.map_area.area_value(), + variety: 0, + unknown: 0, + x: item_drop.x, + z: item_drop.z, + y: item_drop.y, + item_bytes: item_bytes[0..12].try_into()?, + item_id: item_drop.item.id.0, + item_bytes2: item_bytes[12..16].try_into()?, + unknown2: 0, + }) +} diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index 19120f1..2e8866e 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -1,4 +1,5 @@ pub mod lobby; +pub mod message; pub mod room; use libpso::character::character::Inventory; diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index a824684..4afafea 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -6,10 +6,14 @@ use crate::common::serverstate::ClientId; use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients, Rooms}; use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; -use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOMS}; +use crate::ship::location::{ClientLocation, LobbyId, RoomId, RoomLobby, MAX_ROOMS, ClientLocationError}; +use crate::ship::room::RoomState; +use crate::ship::drops::{ItemDrop, ItemDropType}; +use crate::ship::items::ActiveItemDatabase; use libpso::character::character; use crate::entity::gateway::EntityGateway; use libpso::{utf8_to_array, utf8_to_utf16_array}; +use crate::ship::packet::builder; fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation) -> Box + Send> { @@ -20,8 +24,6 @@ fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: })) } - - pub fn guildcard_send(id: ClientId, guildcard_send: &GuildcardSend, target: u32, @@ -46,3 +48,57 @@ pub fn guildcard_send(id: ClientId, }; send_to_client(id, target as u8, msg, &client_location) } + +pub fn request_item(id: ClientId, + request_item: &RequestItem, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + rooms: &mut Rooms, + active_items: &mut ActiveItemDatabase) + -> Result + Send>, ShipError> +where EG: EntityGateway { + let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let mut room = rooms.get_mut(room_id.0) + .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? + .as_mut() + .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?; + + let monster = room.maps.enemy_by_id(request_item.enemy_id as usize)?; + if monster.dropped_item { + return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id)) + } + + let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + + let item_drop_packets = clients_in_area.into_iter() + .filter_map(|area_client| { + room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| { + warn!("drop is? {:?}", item_drop_type); + (area_client, item_drop_type) + }) + }) + .map(|(area_client, item_drop_type)| -> Result<_, ShipError> { + let item_drop = ItemDrop { + map_area: monster.map_area, + x: request_item.x, + y: request_item.y, + z: request_item.z, + item: item_drop_type, + }; + + let activated_item = active_items.activate_item_drop(entity_gateway, item_drop); + let mut client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; + let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &activated_item)?; + client.floor_items.push(activated_item); + Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))) + }) + .filter_map(|item_drop_pkt| { + // TODO: log errors here + item_drop_pkt.ok() + }) + .collect::>(); // TODO: can EntityGateway be Sync? + + Ok(Box::new(item_drop_packets.into_iter())) +} diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index a58de7c..d597768 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -19,7 +19,7 @@ pub fn request_exp(id: ClientId, match client_location.get_area(id).unwrap() { RoomLobby::Room(room) => { let r = rooms[room.0].as_ref().unwrap(); - warn!("killed a {:?}", r.maps.enemy_by_id(request_exp.enemy_id as usize).monster); + warn!("killed a {:?}", r.maps.enemy_by_id(request_exp.enemy_id as usize).unwrap().monster); }, _ => {} }; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 4d31242..a468dfd 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -42,6 +42,8 @@ pub enum ShipError { ClientLocationError(#[from] ClientLocationError), MapsError(#[from] MapsError), InvalidRoom(u32), + MonsterAlreadyDroppedItem(ClientId, u16), + SliceError(#[from] std::array::TryFromSliceError), } #[derive(Debug)] @@ -147,6 +149,7 @@ pub struct ClientState { //guildcard: GuildCard, pub inventory: items::ActiveInventory, //bank: Bank, + pub floor_items: Vec, pub block: u32, } @@ -159,6 +162,7 @@ impl ClientState { session: session, inventory: inventory, //bank: bank, + floor_items: Vec::new(), block: 1, } } @@ -209,6 +213,9 @@ impl ShipServerState { GameMessage::GuildcardSend(guildcard_send) => { handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients) }, + GameMessage::RequestItem(request_item) => { + handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_database).unwrap() + }, _ => { let cmsg = msg.clone(); Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter() From bd195a2ffd21ccb9ac0d065323a22c6c20796b40 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 22:23:59 -0600 Subject: [PATCH 09/14] remove unwrap --- src/ship/items.rs | 10 +++++----- src/ship/packet/handler/direct_message.rs | 2 +- src/ship/ship.rs | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ship/items.rs b/src/ship/items.rs index 575c1ef..ebce01e 100644 --- a/src/ship/items.rs +++ b/src/ship/items.rs @@ -14,6 +14,7 @@ use crate::entity::item::mag::Mag; use crate::entity::item::{Meseta, NewItemEntity}; use crate::ship::map::MapArea; use crate::ship::drops::{ItemDrop, ItemDropType}; +use crate::ship::ship::ShipError; #[derive(Debug)] @@ -182,8 +183,7 @@ impl ActiveItemDatabase { ActiveInventory(activated.take(30).collect()) } - // TODO: Result - pub fn activate_item_drop(&mut self, entity_gateway: &mut EG, item_drop: ItemDrop) -> ActiveItemOnFloor { + pub fn activate_item_drop(&mut self, entity_gateway: &mut EG, item_drop: ItemDrop) -> Result { let item_detail = match item_drop.item { ItemDropType::Weapon(w) => Some(ItemDetail::Weapon(w)), ItemDropType::Armor(w) => Some(ItemDetail::Armor(w)), @@ -205,7 +205,7 @@ impl ActiveItemDatabase { z: item_drop.z, } }).unwrap(); - stack_items(vec![item_entity]).pop().unwrap() + stack_items(vec![item_entity]).pop().ok_or(ShipError::ItemError)? }, None => { let meseta = match item_drop.item { @@ -217,13 +217,13 @@ impl ActiveItemDatabase { }; let active_item = self.activate_item(item_instance); - ActiveItemOnFloor { + Ok(ActiveItemOnFloor { map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, item: active_item, - } + }) } } diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 4afafea..03855cb 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -88,7 +88,7 @@ where EG: EntityGateway { item: item_drop_type, }; - let activated_item = active_items.activate_item_drop(entity_gateway, item_drop); + let activated_item = active_items.activate_item_drop(entity_gateway, item_drop)?; let mut client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &activated_item)?; client.floor_items.push(activated_item); diff --git a/src/ship/ship.rs b/src/ship/ship.rs index a468dfd..10d2db5 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -44,6 +44,7 @@ pub enum ShipError { InvalidRoom(u32), MonsterAlreadyDroppedItem(ClientId, u16), SliceError(#[from] std::array::TryFromSliceError), + ItemError, // TODO: refine this } #[derive(Debug)] From 91c44628eae6f631a279ce030e01375b3b5d1430 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Apr 2020 22:51:09 -0600 Subject: [PATCH 10/14] fix meseta drops --- src/entity/item/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 8e3731d..58e09b6 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -49,6 +49,7 @@ pub struct Meseta(pub u32); impl Meseta { pub fn as_bytes(&self) -> [u8; 16] { let mut result = [0; 16]; + result[0] = 4; result[12..16].copy_from_slice(&u32::to_le_bytes(self.0)); result } From 02c9cdc276fabf9197f166530f573120c814fff2 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 27 Apr 2020 06:55:18 -0600 Subject: [PATCH 11/14] drop techs --- src/entity/character.rs | 25 ++----------------------- src/entity/item/tech.rs | 32 +++++++++++++++++++++++++++++++- src/ship/drops/tech_table.rs | 2 +- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/entity/character.rs b/src/entity/character.rs index 5d898b0..4c7f140 100644 --- a/src/entity/character.rs +++ b/src/entity/character.rs @@ -153,29 +153,8 @@ impl CharacterTechniques { pub fn as_bytes(&self) -> [u8; 20] { self.techs.iter() .fold([0xFF; 20], |mut techlist, (tech, level)| { - let index = match tech { - Technique::Foie => 0, - Technique::Gifoie => 1, - Technique::Rafoie => 2, - Technique::Barta => 3, - Technique::Gibarta => 4, - Technique::Rabarta => 5, - Technique::Zonde => 6, - Technique::Gizonde => 7, - Technique::Razonde => 8, - Technique::Grants => 9, - Technique::Deband => 10, - Technique::Jellen => 11, - Technique::Zalure => 12, - Technique::Shifta => 13, - Technique::Ryuker => 14, - Technique::Resta => 15, - Technique::Anti => 16, - Technique::Reverser => 17, - Technique::Megid => 18, - }; - - techlist[index] = level.0 - 1; + let index = tech.as_value(); + techlist[index as usize] = level.0 - 1; techlist }) } diff --git a/src/entity/item/tech.rs b/src/entity/item/tech.rs index b7faeaa..326aab7 100644 --- a/src/entity/item/tech.rs +++ b/src/entity/item/tech.rs @@ -24,6 +24,31 @@ pub enum Technique { Megid, } +impl Technique { + pub fn as_value(&self) -> u8 { + match self { + Technique::Foie => 0, + Technique::Gifoie => 1, + Technique::Rafoie => 2, + Technique::Barta => 3, + Technique::Gibarta => 4, + Technique::Rabarta => 5, + Technique::Zonde => 6, + Technique::Gizonde => 7, + Technique::Razonde => 8, + Technique::Grants => 9, + Technique::Deband => 10, + Technique::Jellen => 11, + Technique::Zalure => 12, + Technique::Shifta => 13, + Technique::Ryuker => 14, + Technique::Resta => 15, + Technique::Anti => 16, + Technique::Reverser => 17, + Technique::Megid => 18, + } + } +} #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct TechniqueDisk { @@ -33,6 +58,11 @@ pub struct TechniqueDisk { impl TechniqueDisk { pub fn as_bytes(&self) -> [u8; 16] { - [0; 16] + let mut result = [0; 16]; + result[0] = 3; + result[1] = 2; + result[2] = self.level as u8 - 1; + result[4] = self.tech.as_value(); + result } } diff --git a/src/ship/drops/tech_table.rs b/src/ship/drops/tech_table.rs index 370f9c5..7e40ae6 100644 --- a/src/ship/drops/tech_table.rs +++ b/src/ship/drops/tech_table.rs @@ -100,7 +100,7 @@ impl TechniqueTable { let tech_weights = WeightedIndex::new(tech_rates.clone().map(|(_, stat)| stat.rate)).unwrap(); let (tech, stat) = tech_rates.nth(tech_weights.sample(rng)).unwrap(); - let level = rng.gen_range(stat.min, stat.max+1); + let level = rng.gen_range(stat.min, stat.max+1) + 1; Some(ItemDropType::TechniqueDisk(TechniqueDisk { tech: *tech, From a32a7130f424712a2e0d4e7aef58fb958a6a2bd2 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 27 Apr 2020 06:55:37 -0600 Subject: [PATCH 12/14] cleanup --- src/ship/drops/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 0f2e7c3..65aa1e2 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -98,13 +98,6 @@ pub struct ItemDrop { pub item: ItemDropType, } -impl ItemDrop { - pub fn as_client_bytes(&self) -> u8 { - 0 - } -} - - pub struct DropTable { monster_stats: HashMap, @@ -118,7 +111,6 @@ pub struct DropTable { rng: R, } - impl DropTable { pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable { let monster_stats: HashMap = load_data_file(episode, difficulty, section_id, "monster_dar.toml"); From b22ce086f7c25cb19ca85d0f09fefbfce23e9289 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 27 Apr 2020 07:07:09 -0600 Subject: [PATCH 13/14] guess I needed this PartialEq for the tests --- src/entity/item/mod.rs | 4 ++-- src/ship/items.rs | 2 +- src/ship/map.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 58e09b6..e3c801d 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -16,7 +16,7 @@ pub struct ItemId(u32); #[derive(Debug, Clone, PartialEq, Eq)] pub struct BankName(String); -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum ItemLocation { Inventory { character_id: CharacterEntityId, @@ -106,7 +106,7 @@ pub struct NewItemEntity { pub item: ItemDetail, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ItemEntity { pub id: ItemEntityId, pub location: ItemLocation, diff --git a/src/ship/items.rs b/src/ship/items.rs index ebce01e..a00a1b1 100644 --- a/src/ship/items.rs +++ b/src/ship/items.rs @@ -17,7 +17,7 @@ use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::ship::ShipError; -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum ItemInstance { Individual(ItemEntity), Stacked(Vec), diff --git a/src/ship/map.rs b/src/ship/map.rs index 4b60645..52e297e 100644 --- a/src/ship/map.rs +++ b/src/ship/map.rs @@ -349,7 +349,7 @@ enum MapVariantMode { Offline, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum MapArea { Pioneer2Ep1, Forest1, From 6458fad89ff2c87851b7c84978d522fc2d2ed1cd Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 27 Apr 2020 07:07:51 -0600 Subject: [PATCH 14/14] fix tech drop tests --- src/ship/drops/tech_table.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ship/drops/tech_table.rs b/src/ship/drops/tech_table.rs index 7e40ae6..f576598 100644 --- a/src/ship/drops/tech_table.rs +++ b/src/ship/drops/tech_table.rs @@ -118,10 +118,10 @@ mod test { let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]); let tt = TechniqueTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); - let tech_tests = vec![(MapArea::Forest1, Technique::Resta, 13), - (MapArea::Caves3, Technique::Foie, 24), - (MapArea::Mines2, Technique::Gibarta, 20), - (MapArea::DarkFalz, Technique::Razonde, 22)]; + let tech_tests = vec![(MapArea::Forest1, Technique::Resta, 14), + (MapArea::Caves3, Technique::Foie, 25), + (MapArea::Mines2, Technique::Gibarta, 21), + (MapArea::DarkFalz, Technique::Razonde, 23)]; for (area, tech, level) in tech_tests { assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk(