Implement capacity limit system.

This commit is contained in:
Condorra 2023-02-03 23:26:24 +11:00
parent 99ffe45479
commit f21f574ebb
6 changed files with 88 additions and 11 deletions

View File

@ -61,6 +61,12 @@ pub struct OnlineInfo {
pub time: Option<DateTime<Utc>>
}
#[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<LocationStats> {
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));

View File

@ -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(

View File

@ -301,6 +301,7 @@ pub struct Item {
pub flags: Vec<ItemFlag>,
pub sex: Option<Sex>,
pub active_combat: Option<ActiveCombat>,
pub weight: u64,
}
impl Item {
@ -375,6 +376,7 @@ impl Default for Item {
flags: vec!(),
sex: None,
active_combat: Some(Default::default()),
weight: 0,
}
}
}

View File

@ -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? {

View File

@ -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<CapacityLevel> {
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)
}

View File

@ -110,6 +110,24 @@ pub enum PossessionType {
AntennaWhip,
}
impl Into<Item> 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<WeaponData> = OnceCell::new();
FIST_WEAPON.get_or_init(|| {