Browse Source

move partial drops over to item_state

pull/112/head
jake 3 years ago
committed by andy
parent
commit
646f75d00d
  1. 153
      src/ship/items/actions.rs
  2. 85
      src/ship/items/state.rs
  3. 13
      src/ship/packet/builder/message.rs
  4. 10
      src/ship/packet/handler/message.rs
  5. 2
      src/ship/ship.rs

153
src/ship/items/actions.rs

@ -1,12 +1,12 @@
use crate::ship::items::ClientItemId; use crate::ship::items::ClientItemId;
use crate::entity::item::ItemNote;
use crate::entity::item::{Meseta, ItemNote};
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::character::{CharacterEntity, CharacterEntityId};
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult};
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail};
pub enum TriggerCreateItem {ItemAction, pub enum TriggerCreateItem {ItemAction,
Yes, Yes,
@ -112,7 +112,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem
} }
fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: ClientItemId, map_area: MapArea, drop_position: (f32, f32, f32))
fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32))
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem) -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>> -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{ {
@ -134,6 +134,7 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id:
let mut floor = item_state.floor(&character_id)?; let mut floor = item_state.floor(&character_id)?;
floor.add_inventory_item(inventory_item, map_area, drop_position); floor.add_inventory_item(inventory_item, map_area, drop_position);
item_state.set_floor(floor);
Ok(((item_state, transaction), ())) Ok(((item_state, transaction), ()))
}) })
@ -155,7 +156,151 @@ where
let item_state_proxy = ItemStateProxy::new(item_state); let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default() let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(take_item_from_inventory(character.id, *item_id)) .act(take_item_from_inventory(character.id, *item_id))
.act(add_inventory_item_to_shared_floor(character.id, *item_id, map_area, drop_position))
.act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn take_partial_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), StackedItemDetail), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, mut transaction), _| {
Box::pin(async move {
let mut inventory = item_state.inventory(&character_id)?;
let item = inventory.take_partial_item(&item_id, amount).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?;
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
item_state.set_inventory(inventory);
Ok(((item_state, transaction), item))
})
}
}
fn add_partial_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32))
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), StackedItemDetail)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, transaction), stacked_item| {
Box::pin(async move {
let floor_item = FloorItem {
item_id: item_state.new_item_id()?,
item: FloorItemDetail::Stacked(stacked_item),
map_area,
x: drop_position.0,
y: 0.0,
z: drop_position.1,
};
let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| {
async move {
if let Ok(transaction) = &mut transaction {
transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop {
character_id,
map_area,
x: drop_position.0,
y: 0.0,
z: drop_position.1,
}).await?;
}
transaction
}}).await?;
let mut floor = item_state.floor(&character_id)?;
let floor_item = floor.add_item(floor_item).clone();
item_state.set_floor(floor);
Ok(((item_state, transaction), floor_item))
})
}
}
pub async fn drop_partial_item<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(take_partial_item_from_inventory(character.id, *item_id, amount))
.act(add_partial_inventory_item_to_shared_floor(character.id, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), u32), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, mut transaction), _| {
Box::pin(async move {
let mut inventory = item_state.inventory(&character_id)?;
inventory.remove_meseta(amount)?;
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
Ok(((item_state, transaction), amount))
})
}
}
fn add_meseta_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32))
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), u32)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, transaction), amount| {
Box::pin(async move {
let floor_item = FloorItem {
item_id: item_state.new_item_id()?,
item: FloorItemDetail::Meseta(Meseta(amount)),
map_area: map_area,
x: drop_position.0,
y: 0.0,
z: drop_position.1,
};
let mut floor = item_state.floor(&character_id)?;
let floor_item = floor.add_item(floor_item).clone();
item_state.set_floor(floor);
Ok(((item_state, transaction), floor_item))
})
}
}
pub async fn drop_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(take_meseta_from_inventory(character.id, amount))
.act(add_meseta_to_shared_floor(character.id, map_area, drop_position))
.commit((item_state_proxy, transaction)) .commit((item_state_proxy, transaction))
.await?; .await?;
item_state_proxy.commit(); item_state_proxy.commit();

85
src/ship/items/state.rs

@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use crate::ship::items::ClientItemId; use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity};
@ -7,7 +8,6 @@ use crate::ship::map::MapArea;
use crate::ship::location::RoomId; use crate::ship::location::RoomId;
use crate::entity::character::CharacterEntityId; use crate::entity::character::CharacterEntityId;
use crate::entity::gateway::GatewayError; use crate::entity::gateway::GatewayError;
use crate::entity::gateway::entitygateway::EntityGatewayTransaction;
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::ship::drops::ItemDrop; use crate::ship::drops::ItemDrop;
@ -33,6 +33,9 @@ pub enum ItemStateError {
#[error("gateway")] #[error("gateway")]
GatewayError(#[from] GatewayError), GatewayError(#[from] GatewayError),
#[error("tried to drop more meseta than in inventory: {0}")]
InvalidMesetaDrop(u32),
} }
@ -163,7 +166,7 @@ where
type Start = T; type Start = T;
type Error = E; type Error = E;
async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> {
async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> {
Ok((s, ())) Ok((s, ()))
} }
@ -252,12 +255,12 @@ impl FloorItemDetail {
#[derive(Clone)] #[derive(Clone)]
pub struct FloorItem { pub struct FloorItem {
item_id: ClientItemId,
item: FloorItemDetail,
map_area: MapArea,
x: f32,
y: f32,
z: f32,
pub item_id: ClientItemId,
pub item: FloorItemDetail,
pub map_area: MapArea,
pub x: f32,
pub y: f32,
pub z: f32,
} }
impl FloorItem { impl FloorItem {
@ -293,6 +296,20 @@ impl FloorItem {
} }
param param
} }
pub fn as_client_bytes(&self) -> [u8; 16] {
match &self.item {
FloorItemDetail::Individual(individual_floor_item) => {
individual_floor_item.item.as_client_bytes()
},
FloorItemDetail::Stacked(stacked_floor_item) => {
stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len())
},
FloorItemDetail::Meseta(meseta_floor_item) => {
meseta_floor_item.as_bytes()
}
}
}
} }
@ -326,7 +343,7 @@ pub struct RoomFloorItems(Vec<FloorItem>);
pub struct InventoryState { pub struct InventoryState {
character_id: CharacterEntityId, character_id: CharacterEntityId,
inventory: Inventory, inventory: Inventory,
meseta: Meseta,
pub meseta: Meseta,
} }
impl InventoryState { impl InventoryState {
@ -394,6 +411,40 @@ impl InventoryState {
.next() .next()
} }
pub fn take_partial_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<StackedItemDetail> {
let amount = amount as usize;
let (idx, _, stacked_item) = self.inventory.0
.iter_mut()
.enumerate()
.filter_map(|(k, item)| {
match item.item {
InventoryItemDetail::Stacked(ref mut stacked_item) => Some((k, item.item_id, stacked_item)),
_ => None
}
})
.find(|(_, id, _)| *id == *item_id)?;
let remove_all = match stacked_item.entity_ids.len().cmp(&amount) {
Ordering::Equal => true,
Ordering::Greater => false,
Ordering::Less => return None,
};
if remove_all {
let stacked_item = stacked_item.clone();
self.inventory.0.remove(idx);
Some(stacked_item)
}
else {
let entity_ids = stacked_item.entity_ids.drain(..amount).collect();
Some(StackedItemDetail {
entity_ids: entity_ids,
tool: stacked_item.tool,
})
}
}
pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity {
InventoryEntity { InventoryEntity {
items: self.inventory.0.iter() items: self.inventory.0.iter()
@ -420,6 +471,14 @@ impl InventoryState {
.collect() .collect()
} }
} }
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if amount > self.meseta.0 {
return Err(ItemStateError::InvalidMesetaDrop(amount))
}
self.meseta.0 -= amount;
Ok(())
}
} }
pub struct FloorState { pub struct FloorState {
@ -444,7 +503,7 @@ impl FloorState {
}) })
} }
pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) {
pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem {
let floor_item = FloorItem { let floor_item = FloorItem {
item_id: inventory_item.item_id, item_id: inventory_item.item_id,
item: match inventory_item.item { item: match inventory_item.item {
@ -458,6 +517,12 @@ impl FloorState {
}; };
self.shared.0.push(floor_item); self.shared.0.push(floor_item);
&self.shared.0[self.shared.0.len()-1]
}
pub fn add_item(&mut self, floor_item: FloorItem) -> &FloorItem {
self.shared.0.push(floor_item);
&self.shared.0[self.shared.0.len()-1]
} }
} }

13
src/ship/packet/builder/message.rs

@ -4,6 +4,7 @@ use crate::entity::item;
use crate::common::leveltable::CharacterStats; use crate::common::leveltable::CharacterStats;
use crate::ship::ship::{ShipError}; use crate::ship::ship::{ShipError};
use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank};
use crate::ship::items::state::FloorItem as FloorItem2;
use crate::ship::location::AreaClient; use crate::ship::location::AreaClient;
use std::convert::TryInto; use std::convert::TryInto;
use crate::ship::shops::ShopItem; use crate::ship::shops::ShopItem;
@ -89,7 +90,7 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu
}) })
} }
pub fn drop_split_stack(area_client: AreaClient, item: &StackedFloorItem) -> Result<DropSplitStack, ShipError> {
pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result<DropSplitStack, ShipError> {
let item_bytes = item.as_client_bytes(); let item_bytes = item.as_client_bytes();
Ok(DropSplitStack { Ok(DropSplitStack {
client: area_client.local_client.id(), client: area_client.local_client.id(),
@ -106,18 +107,18 @@ pub fn drop_split_stack(area_client: AreaClient, item: &StackedFloorItem) -> Res
}) })
} }
pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> {
pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem2) -> Result<DropSplitStack, ShipError> {
let item_bytes = item.as_client_bytes(); let item_bytes = item.as_client_bytes();
Ok(DropSplitStack { Ok(DropSplitStack {
client: area_client.local_client.id(), client: area_client.local_client.id(),
target: 0, target: 0,
variety: 0, variety: 0,
unknown1: 0, unknown1: 0,
map_area: item.map_area().area_value(),
x: item.x(),
z: item.z(),
map_area: item.map_area.area_value(),
x: item.x,
z: item.z,
item_bytes: item_bytes[0..12].try_into()?, item_bytes: item_bytes[0..12].try_into()?,
item_id: item.item_id().0,
item_id: item.item_id.0,
item_bytes2: item_bytes[12..16].try_into()?, item_bytes2: item_bytes[12..16].try_into()?,
unknown2: 0, unknown2: 0,
}) })

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

@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::items::{ItemManager, ClientItemId};
use crate::ship::packet::builder; use crate::ship::packet::builder;
use crate::ship::items::state::ItemState; use crate::ship::items::state::ItemState;
use crate::ship::items::actions::{drop_item, pick_up_item};
use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta};
pub async fn request_exp<EG: EntityGateway>(id: ClientId, pub async fn request_exp<EG: EntityGateway>(id: ClientId,
request_exp: &RequestExp, request_exp: &RequestExp,
@ -120,7 +120,7 @@ pub async fn no_longer_has_item<EG>(id: ClientId,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager)
item_state: &mut ItemState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where where
EG: EntityGateway EG: EntityGateway
@ -134,7 +134,7 @@ where
} }
if no_longer_has_item.item_id == 0xFFFFFFFF { if no_longer_has_item.item_id == 0xFFFFFFFF {
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
let dropped_meseta = drop_meseta(item_state, entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32); let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32);
@ -158,9 +158,9 @@ where
)) ))
} }
else { else {
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
let dropped_item = drop_partial_item(item_state, entity_gateway, &client.character, &drop_location.item_id, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?;
client.item_drop_location = None; client.item_drop_location = None;
let clients_in_area = client_location.get_clients_in_room(room_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() })?;

2
src/ship/ship.rs

@ -490,7 +490,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
}, },
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
let block = self.blocks.with_client(id, &self.clients)?; let block = self.blocks.with_client(id, &self.clients)?;
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
}, },
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |

Loading…
Cancel
Save