Browse Source

basic item drop logic

pbs
jake 5 years ago
parent
commit
4a526e7673
  1. 7
      src/entity/item/mod.rs
  2. 64
      src/ship/items.rs
  3. 33
      src/ship/packet/builder/message.rs
  4. 1
      src/ship/packet/builder/mod.rs
  5. 62
      src/ship/packet/handler/direct_message.rs
  6. 2
      src/ship/packet/handler/message.rs
  7. 7
      src/ship/ship.rs

7
src/entity/item/mod.rs

@ -7,6 +7,7 @@ pub mod unit;
pub mod mag; pub mod mag;
use crate::entity::character::CharacterEntityId; use crate::entity::character::CharacterEntityId;
use crate::ship::map::MapArea;
#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq)] #[derive(PartialEq, Copy, Clone, Debug, Hash, Eq)]
pub struct ItemEntityId(pub u32); pub struct ItemEntityId(pub u32);
@ -27,8 +28,10 @@ pub enum ItemLocation {
slot: BankName, slot: BankName,
}, },
Floor { Floor {
// floor: eventually
// x y z: ?????
map_area: MapArea,
x: f32,
y: f32,
z: f32,
}, },
/*Destroyed { /*Destroyed {
// marks an item that has been consumed in some way // marks an item that has been consumed in some way

64
src/ship/items.rs

@ -11,7 +11,9 @@ use crate::entity::item::shield::Shield;
use crate::entity::item::unit::Unit; use crate::entity::item::unit::Unit;
use crate::entity::item::tool::Tool; use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag; 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)] #[derive(Debug)]
@ -22,12 +24,12 @@ enum ItemInstance {
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ActiveItemId(u32);
pub struct ActiveItemId(pub u32);
#[derive(Debug)] #[derive(Debug)]
pub struct ActiveItem { pub struct ActiveItem {
id: ActiveItemId,
pub id: ActiveItemId,
item: ItemInstance, 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<ItemEntity>) -> Vec<ItemInstance> { fn stack_items(items: Vec<ItemEntity>) -> Vec<ItemInstance> {
let mut stacks = HashMap::new(); let mut stacks = HashMap::new();
@ -138,8 +148,6 @@ pub struct ActiveItemDatabase {
id: u32, id: u32,
} }
impl ActiveItemDatabase { impl ActiveItemDatabase {
pub fn new() -> ActiveItemDatabase { pub fn new() -> ActiveItemDatabase {
ActiveItemDatabase { ActiveItemDatabase {
@ -155,7 +163,7 @@ impl ActiveItemDatabase {
} }
} }
// deactivate item
// TODO: deactivate item
pub fn get_character_inventory<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> ActiveInventory { pub fn get_character_inventory<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> ActiveInventory {
let items = entity_gateway.get_items_by_character(&character); 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)); let activated = stacked.into_iter().map(|i| self.activate_item(i));
ActiveInventory(activated.take(30).collect()) ActiveInventory(activated.take(30).collect())
} }
// TODO: Result
pub fn activate_item_drop<EG: EntityGateway>(&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)] #[cfg(test)]

33
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<ItemDrop, ShipError> {
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,
})
}

1
src/ship/packet/builder/mod.rs

@ -1,4 +1,5 @@
pub mod lobby; pub mod lobby;
pub mod message;
pub mod room; pub mod room;
use libpso::character::character::Inventory; use libpso::character::character::Inventory;

62
src/ship/packet/handler/direct_message.rs

@ -6,10 +6,14 @@ use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients, Rooms}; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients, Rooms};
use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder}; 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 libpso::character::character;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use libpso::{utf8_to_array, utf8_to_utf16_array}; 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) fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> { -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
@ -20,8 +24,6 @@ fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location:
})) }))
} }
pub fn guildcard_send(id: ClientId, pub fn guildcard_send(id: ClientId,
guildcard_send: &GuildcardSend, guildcard_send: &GuildcardSend,
target: u32, target: u32,
@ -46,3 +48,57 @@ pub fn guildcard_send(id: ClientId,
}; };
send_to_client(id, target as u8, msg, &client_location) send_to_client(id, target as u8, msg, &client_location)
} }
pub fn request_item<EG>(id: ClientId,
request_item: &RequestItem,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
rooms: &mut Rooms,
active_items: &mut ActiveItemDatabase)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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::<Vec<_>>(); // TODO: can EntityGateway be Sync?
Ok(Box::new(item_drop_packets.into_iter()))
}

2
src/ship/packet/handler/message.rs

@ -19,7 +19,7 @@ pub fn request_exp(id: ClientId,
match client_location.get_area(id).unwrap() { match client_location.get_area(id).unwrap() {
RoomLobby::Room(room) => { RoomLobby::Room(room) => {
let r = rooms[room.0].as_ref().unwrap(); 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);
}, },
_ => {} _ => {}
}; };

7
src/ship/ship.rs

@ -42,6 +42,8 @@ pub enum ShipError {
ClientLocationError(#[from] ClientLocationError), ClientLocationError(#[from] ClientLocationError),
MapsError(#[from] MapsError), MapsError(#[from] MapsError),
InvalidRoom(u32), InvalidRoom(u32),
MonsterAlreadyDroppedItem(ClientId, u16),
SliceError(#[from] std::array::TryFromSliceError),
} }
#[derive(Debug)] #[derive(Debug)]
@ -147,6 +149,7 @@ pub struct ClientState {
//guildcard: GuildCard, //guildcard: GuildCard,
pub inventory: items::ActiveInventory, pub inventory: items::ActiveInventory,
//bank: Bank, //bank: Bank,
pub floor_items: Vec<items::ActiveItemOnFloor>,
pub block: u32, pub block: u32,
} }
@ -159,6 +162,7 @@ impl ClientState {
session: session, session: session,
inventory: inventory, inventory: inventory,
//bank: bank, //bank: bank,
floor_items: Vec::new(),
block: 1, block: 1,
} }
} }
@ -209,6 +213,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
GameMessage::GuildcardSend(guildcard_send) => { GameMessage::GuildcardSend(guildcard_send) => {
handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients) 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(); let cmsg = msg.clone();
Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter() Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()

Loading…
Cancel
Save