From f21f574ebbd4631f40e2587eb1448f37fa8399ca Mon Sep 17 00:00:00 2001 From: Shagnor Date: Fri, 3 Feb 2023 23:26:24 +1100 Subject: [PATCH] Implement capacity limit system. --- blastmud_game/src/db.rs | 13 +++++++ .../src/message_handler/user_commands/buy.rs | 28 +++++++++------ blastmud_game/src/models/item.rs | 2 ++ blastmud_game/src/services.rs | 2 +- blastmud_game/src/services/capacity.rs | 36 +++++++++++++++++++ .../src/static_content/possession_type.rs | 18 ++++++++++ 6 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 blastmud_game/src/services/capacity.rs diff --git a/blastmud_game/src/db.rs b/blastmud_game/src/db.rs index 10c6b1d..d6931fa 100644 --- a/blastmud_game/src/db.rs +++ b/blastmud_game/src/db.rs @@ -61,6 +61,12 @@ pub struct OnlineInfo { pub time: Option> } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LocationStats { + pub total_count: u64, + pub total_weight: u64, +} + impl DBPool { pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> { self.get_conn().await?.execute( @@ -621,6 +627,13 @@ impl DBTrans { .filter_map(|row| serde_json::from_value(row.get(0)).ok()) .collect()) } + + pub async fn get_location_stats(&self, location: &str) -> DResult { + Ok(serde_json::from_value(self.pg_trans()?.query_one( + "SELECT COUNT(*) AS total_count, SUM(details->>'weight') AS total_weight \ + FROM items WHERE location = $1", &[&location] + ).await?.get(0))?) + } pub async fn commit(mut self: Self) -> DResult<()> { let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None)); diff --git a/blastmud_game/src/message_handler/user_commands/buy.rs b/blastmud_game/src/message_handler/user_commands/buy.rs index bf1ca0f..6fde871 100644 --- a/blastmud_game/src/message_handler/user_commands/buy.rs +++ b/blastmud_game/src/message_handler/user_commands/buy.rs @@ -7,6 +7,7 @@ use crate::{ static_content::room, static_content::possession_type::possession_data, models::item::Item, + services::capacity::{check_item_capacity, CapacityLevel}, }; use async_trait::async_trait; use ansi::ansi; @@ -51,19 +52,26 @@ impl UserVerb for Verb { user_error("You don't have enough credits to buy that!".to_owned())?; } user.credits -= stock.list_price; + let player_item_str = format!("player/{}", &player_item.item_code); let item_code = ctx.trans.alloc_item_code().await?; + let loc = match check_item_capacity(ctx.trans, &player_item_str, + possession_type.weight).await? { + CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => { + match check_item_capacity(ctx.trans, &player_item.location, + possession_type.weight).await? { + CapacityLevel::AboveItemLimit => + user_error( + "You can't carry it, and there is too much stuff \ + here already".to_owned())?, + _ => &player_item.location + } + } + _ => &player_item_str + }; let new_item = Item { - item_type: "possession".to_owned(), item_code: format!("{}", item_code), - possession_type: Some(stock.possession_type.clone()), - display: possession_type.display.to_owned(), - display_less_explicit: possession_type.display_less_explicit.map(|d| d.to_owned()), - details: Some(possession_type.details.to_owned()), - details_less_explicit: possession_type.details_less_explicit.map(|d| d.to_owned()), - aliases: possession_type.aliases.iter().map(|al| (*al).to_owned()).collect(), - location: format!("player/{}", &player_item.item_code), - health: possession_type.max_health, - ..Default::default() + location: loc.to_owned(), + ..stock.possession_type.clone().into() }; ctx.trans.create_item(&new_item).await?; ctx.trans.queue_for_session( diff --git a/blastmud_game/src/models/item.rs b/blastmud_game/src/models/item.rs index 9b4ce23..cc428c8 100644 --- a/blastmud_game/src/models/item.rs +++ b/blastmud_game/src/models/item.rs @@ -301,6 +301,7 @@ pub struct Item { pub flags: Vec, pub sex: Option, pub active_combat: Option, + pub weight: u64, } impl Item { @@ -375,6 +376,7 @@ impl Default for Item { flags: vec!(), sex: None, active_combat: Some(Default::default()), + weight: 0, } } } diff --git a/blastmud_game/src/services.rs b/blastmud_game/src/services.rs index 63910b9..7fdef42 100644 --- a/blastmud_game/src/services.rs +++ b/blastmud_game/src/services.rs @@ -6,7 +6,7 @@ use crate::{ pub mod skills; pub mod combat; - +pub mod capacity; pub async fn broadcast_to_room(trans: &DBTrans, location: &str, from_item: Option<&Item>, message_explicit_ok: &str, message_nonexplicit: Option<&str>) -> DResult<()> { for item in trans.find_items_by_location(location).await? { diff --git a/blastmud_game/src/services/capacity.rs b/blastmud_game/src/services/capacity.rs new file mode 100644 index 0000000..a97bc4c --- /dev/null +++ b/blastmud_game/src/services/capacity.rs @@ -0,0 +1,36 @@ +use crate::{ + db::DBTrans, + DResult, +}; + +pub enum CapacityLevel { + Unburdened, + SlightlyBurdened, + HeavilyBurdened, + OverBurdened, + AboveItemLimit, +} +pub async fn check_item_capacity(trans: &DBTrans, + container: &str, + proposed_weight: u64) -> DResult { + let stats = trans.get_location_stats( + container + ).await?; + if stats.total_count >= 50 || proposed_weight > 0 && stats.total_count >= 49 { + return Ok(CapacityLevel::AboveItemLimit); + } + if let Some((item_type, _item_code)) = container.split_once("/") { + if item_type == "player" || item_type == "npc" { + let max_weight = 20000; // TODO Calculate properly + let new_weight = stats.total_weight + proposed_weight; + if new_weight >= max_weight { + return Ok(CapacityLevel::OverBurdened); + } else if new_weight >= max_weight * 4 / 5 { + return Ok(CapacityLevel::HeavilyBurdened); + } else if new_weight >= max_weight / 2 { + return Ok(CapacityLevel::SlightlyBurdened); + } + } + } + Ok(CapacityLevel::Unburdened) +} diff --git a/blastmud_game/src/static_content/possession_type.rs b/blastmud_game/src/static_content/possession_type.rs index 38ac549..b0013f0 100644 --- a/blastmud_game/src/static_content/possession_type.rs +++ b/blastmud_game/src/static_content/possession_type.rs @@ -110,6 +110,24 @@ pub enum PossessionType { AntennaWhip, } +impl Into for PossessionType { + fn into(self) -> Item { + let possession_dat = possession_data().get(&self).unwrap(); + Item { + item_type: "possession".to_owned(), + possession_type: Some(self.clone()), + display: possession_dat.display.to_owned(), + display_less_explicit: possession_dat.display_less_explicit.map(|d| d.to_owned()), + details: Some(possession_dat.details.to_owned()), + details_less_explicit: possession_dat.details_less_explicit.map(|d| d.to_owned()), + aliases: possession_dat.aliases.iter().map(|al| (*al).to_owned()).collect(), + health: possession_dat.max_health, + weight: possession_dat.weight, + ..Default::default() + } + } +} + pub fn fist() -> &'static WeaponData { static FIST_WEAPON: OnceCell = OnceCell::new(); FIST_WEAPON.get_or_init(|| {