Apply limits on carrying capacity, and make roboporter strong.

This commit is contained in:
Condorra 2023-07-11 21:23:34 +10:00
parent e83cc19698
commit ea45530a39
18 changed files with 191 additions and 110 deletions

View File

@ -4,7 +4,7 @@ use super::{
}; };
use crate::{ use crate::{
models::item::Item, models::item::Item,
services::capacity::{check_item_capacity, CapacityLevel}, services::capacity::{check_item_capacity, check_item_ref_capacity, CapacityLevel},
static_content::possession_type::possession_data, static_content::possession_type::possession_data,
static_content::room, static_content::room,
}; };
@ -74,17 +74,17 @@ impl UserVerb for Verb {
)?; )?;
} }
user.credits -= stock.list_price; user.credits -= stock.list_price;
let player_item_str = format!("player/{}", &player_item.item_code); let player_item_str = player_item.refstr();
let item_code = ctx.trans.alloc_item_code().await?; let item_code = ctx.trans.alloc_item_code().await?;
let loc = match check_item_capacity( let loc = match check_item_capacity(
ctx.trans, ctx.trans,
&player_item_str, &player_item,
possession_type.weight, possession_type.weight,
) )
.await? .await?
{ {
CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => { CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => {
match check_item_capacity( match check_item_ref_capacity(
ctx.trans, ctx.trans,
&player_item.location, &player_item.location,
possession_type.weight, possession_type.weight,

View File

@ -194,7 +194,7 @@ impl QueueCommandHandler for QueueHandler {
} }
} }
match check_item_capacity(&ctx.trans, &ctx.item.refstr(), possession_data.weight).await? { match check_item_capacity(&ctx.trans, &ctx.item, possession_data.weight).await? {
CapacityLevel::AboveItemLimit | CapacityLevel::OverBurdened => { CapacityLevel::AboveItemLimit | CapacityLevel::OverBurdened => {
user_error("You have too much stuff to take that on!".to_owned())? user_error("You have too much stuff to take that on!".to_owned())?
} }

View File

@ -14,7 +14,7 @@ use crate::{
TaskHandler, TaskRunContext, TaskHandler, TaskRunContext,
}, },
services::{ services::{
capacity::{check_item_capacity, CapacityLevel}, capacity::{check_item_ref_capacity, CapacityLevel},
comms::broadcast_to_room, comms::broadcast_to_room,
}, },
static_content::possession_type::possession_data, static_content::possession_type::possession_data,
@ -200,7 +200,8 @@ impl QueueCommandHandler for QueueHandler {
Some(pd) => pd, Some(pd) => pd,
}; };
match check_item_capacity(ctx.trans, &ctx.item.location, possession_data.weight).await? { match check_item_ref_capacity(ctx.trans, &ctx.item.location, possession_data.weight).await?
{
CapacityLevel::AboveItemLimit => user_error(format!( CapacityLevel::AboveItemLimit => user_error(format!(
"You can't drop {}, because it is so cluttered here there is no where to put it!", "You can't drop {}, because it is so cluttered here there is no where to put it!",
&item.display_for_sentence(ctx.explicit().await?, 1, false) &item.display_for_sentence(ctx.explicit().await?, 1, false)

View File

@ -101,8 +101,7 @@ impl QueueCommandHandler for QueueHandler {
Some(pd) => pd, Some(pd) => pd,
}; };
let player_as_loc = format!("{}/{}", &ctx.item.item_type, &ctx.item.item_code); match check_item_capacity(ctx.trans, &ctx.item, possession_data.weight).await? {
match check_item_capacity(ctx.trans, &player_as_loc, possession_data.weight).await? {
CapacityLevel::AboveItemLimit => { CapacityLevel::AboveItemLimit => {
user_error("You just can't hold that many things!".to_owned())? user_error("You just can't hold that many things!".to_owned())?
} }
@ -136,7 +135,7 @@ impl QueueCommandHandler for QueueHandler {
) )
.await?; .await?;
let mut item_mut = (*item).clone(); let mut item_mut = (*item).clone();
item_mut.location = player_as_loc; item_mut.location = ctx.item.refstr();
item_mut.action_type = LocationActionType::Normal; item_mut.action_type = LocationActionType::Normal;
ctx.trans.save_item_model(&item_mut).await?; ctx.trans.save_item_model(&item_mut).await?;
Ok(()) Ok(())

View File

@ -40,6 +40,7 @@ impl UserVerb for Verb {
.map(|(_, g)| g.collect::<Vec<&Arc<Item>>>()) .map(|(_, g)| g.collect::<Vec<&Arc<Item>>>())
.collect::<Vec<Vec<&Arc<Item>>>>(); .collect::<Vec<Vec<&Arc<Item>>>>();
let mut response = String::new(); let mut response = String::new();
let mut total: u64 = 0;
for items in all_groups { for items in all_groups {
let item = items[0]; let item = items[0];
if item.item_type != "possession" { if item.item_type != "possession" {
@ -51,6 +52,7 @@ impl UserVerb for Verb {
.as_ref() .as_ref()
.unwrap_or(&PossessionType::AntennaWhip), .unwrap_or(&PossessionType::AntennaWhip),
) { ) {
total += items.len() as u64 * posdat.weight;
response.push_str(&format!( response.push_str(&format!(
"{} [{}]{}\n", "{} [{}]{}\n",
item.display_for_sentence( item.display_for_sentence(
@ -67,6 +69,11 @@ impl UserVerb for Verb {
)); ));
} }
} }
response.push_str(&format!(
"Total weight: {} ({} max)\n",
weight(total),
weight(player_item.max_carry())
));
if response == "" { if response == "" {
response.push_str("You aren't carrying anything.\n"); response.push_str("You aren't carrying anything.\n");
} }

View File

@ -6,7 +6,7 @@ use crate::{
db::ItemSearchParams, db::ItemSearchParams,
models::item::{ItemFlag, ItemSpecialData}, models::item::{ItemFlag, ItemSpecialData},
services::{ services::{
capacity::{check_item_capacity, CapacityLevel}, capacity::{check_item_capacity, check_item_ref_capacity, CapacityLevel},
comms::broadcast_to_room, comms::broadcast_to_room,
}, },
}; };
@ -76,14 +76,16 @@ impl UserVerb for Verb {
for item in items { for item in items {
if reverse { if reverse {
match check_item_capacity(&ctx.trans, &player_item.location, item.weight).await? { match check_item_ref_capacity(&ctx.trans, &player_item.location, item.weight)
.await?
{
CapacityLevel::AboveItemLimit => user_error( CapacityLevel::AboveItemLimit => user_error(
"There is not enough space here to unload another item.".to_owned(), "There is not enough space here to unload another item.".to_owned(),
)?, )?,
_ => {} _ => {}
} }
} else { } else {
match check_item_capacity(&ctx.trans, &npc.refstr(), item.weight).await? { match check_item_capacity(&ctx.trans, &npc, item.weight).await? {
CapacityLevel::AboveItemLimit | CapacityLevel::OverBurdened => { CapacityLevel::AboveItemLimit | CapacityLevel::OverBurdened => {
user_error("There is not enough capacity to load that item.".to_owned())? user_error("There is not enough capacity to load that item.".to_owned())?
} }

View File

@ -447,6 +447,10 @@ impl Item {
pub fn refstr(&self) -> String { pub fn refstr(&self) -> String {
format!("{}/{}", &self.item_type, &self.item_code) format!("{}/{}", &self.item_type, &self.item_code)
} }
pub fn max_carry(&self) -> u64 {
(50.0 * 2.0_f64.powf(*self.total_stats.get(&StatType::Brawn).unwrap_or(&0.0))).ceil() as u64
}
} }
impl Default for Item { impl Default for Item {

View File

@ -1,18 +1,19 @@
#[double]
use crate::db::DBTrans;
use crate::{ use crate::{
DResult,
models::item::Item,
models::consent::{Consent, ConsentType, ConsentStatus},
static_content::npc::npc_by_code,
message_handler::user_commands::drop::consider_expire_job_for_item, message_handler::user_commands::drop::consider_expire_job_for_item,
models::consent::{Consent, ConsentStatus, ConsentType},
models::item::Item,
static_content::npc::npc_by_code,
DResult,
}; };
use mockall_double::double; use mockall_double::double;
#[double] use crate::db::DBTrans;
pub mod comms;
pub mod combat;
pub mod skills;
pub mod capacity; pub mod capacity;
pub mod combat;
pub mod comms;
pub mod effect; pub mod effect;
pub mod skills;
fn check_one_consent(consent: &Consent, action: &str, target: &Item) -> bool { fn check_one_consent(consent: &Consent, action: &str, target: &Item) -> bool {
if let Some((loctype, loccode)) = target.location.split_once("/") { if let Some((loctype, loccode)) = target.location.split_once("/") {
@ -38,14 +39,17 @@ fn check_one_consent(consent: &Consent, action: &str, target: &Item) -> bool {
return false; return false;
} }
} }
true true
} }
pub async fn check_consent(trans: &DBTrans, action: &str, pub async fn check_consent(
consent_type: &ConsentType, trans: &DBTrans,
by: &Item, action: &str,
target: &Item) -> DResult<bool> { consent_type: &ConsentType,
by: &Item,
target: &Item,
) -> DResult<bool> {
// Consent is only a factor on actions by players towards other players or npcs. // Consent is only a factor on actions by players towards other players or npcs.
if by.item_type != "player" || (target.item_type != "player" && target.item_type != "npc") { if by.item_type != "player" || (target.item_type != "player" && target.item_type != "npc") {
return Ok(true); return Ok(true);
@ -53,51 +57,63 @@ pub async fn check_consent(trans: &DBTrans, action: &str,
if target.item_type == "npc" { if target.item_type == "npc" {
return Ok(match npc_by_code().get(target.item_code.as_str()) { return Ok(match npc_by_code().get(target.item_code.as_str()) {
None => false, None => false,
Some(npc) => npc.player_consents.contains(consent_type) Some(npc) => npc.player_consents.contains(consent_type),
}); });
} }
if target.item_code == by.item_code { if target.item_code == by.item_code {
return Ok(true) return Ok(true);
} }
trans.delete_expired_user_consent().await?; trans.delete_expired_user_consent().await?;
if let Some(consent) = trans.find_user_consent_by_parties_type( if let Some(consent) = trans
&target.item_code, &by.item_code, consent_type).await? { .find_user_consent_by_parties_type(&target.item_code, &by.item_code, consent_type)
.await?
{
if check_one_consent(&consent, action, &target) { if check_one_consent(&consent, action, &target) {
return Ok(true); return Ok(true);
} }
} }
trans.delete_expired_corp_consent().await?; trans.delete_expired_corp_consent().await?;
if let Some(consent) = trans.find_corp_consent_by_user_parties_type( if let Some(consent) = trans
&target.item_code, &by.item_code, consent_type).await? { .find_corp_consent_by_user_parties_type(&target.item_code, &by.item_code, consent_type)
.await?
{
if check_one_consent(&consent, action, &target) { if check_one_consent(&consent, action, &target) {
return Ok(true); return Ok(true);
} }
} }
Ok(false) Ok(false)
} }
pub async fn destroy_container(trans: &DBTrans, container: &Item) -> DResult<()> { pub async fn destroy_container(trans: &DBTrans, container: &Item) -> DResult<()> {
trans.delete_item(&container.item_type, &container.item_code).await?; trans
.delete_item(&container.item_type, &container.item_code)
for item in trans.find_items_by_location( .await?;
&container.refstr()
).await?.into_iter() { for item in trans
.find_items_by_location(&container.refstr())
.await?
.into_iter()
{
let mut item_mut = (*item).clone(); let mut item_mut = (*item).clone();
// We only update this to support consider_expire_job - it gets updated in bulk // We only update this to support consider_expire_job - it gets updated in bulk
// by transfer_all_possession below. // by transfer_all_possession below.
item_mut.location = container.location.clone(); item_mut.location = container.location.clone();
match capacity::check_item_capacity(trans, &container.location, item_mut.weight).await? { match capacity::check_item_ref_capacity(trans, &container.location, item_mut.weight).await?
capacity::CapacityLevel::OverBurdened | capacity::CapacityLevel::AboveItemLimit => {
trans.delete_item(&item_mut.item_type, &item_mut.item_code).await?, capacity::CapacityLevel::OverBurdened | capacity::CapacityLevel::AboveItemLimit => {
_ => consider_expire_job_for_item(trans, &item_mut).await? trans
.delete_item(&item_mut.item_type, &item_mut.item_code)
.await?
}
_ => consider_expire_job_for_item(trans, &item_mut).await?,
} }
} }
trans.transfer_all_possessions_code( trans
&container.refstr(), .transfer_all_possessions_code(&container.refstr(), &container.location)
&container.location).await?; .await?;
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use crate::DResult; #[double]
use crate::db::DBTrans;
use crate::{models::item::Item, DResult};
use mockall_double::double; use mockall_double::double;
#[double] use crate::db::DBTrans;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CapacityLevel { pub enum CapacityLevel {
@ -11,75 +11,118 @@ pub enum CapacityLevel {
OverBurdened, OverBurdened,
AboveItemLimit, AboveItemLimit,
} }
pub async fn check_item_capacity(trans: &DBTrans,
container: &str, pub async fn check_item_ref_capacity(
proposed_weight: u64) -> DResult<CapacityLevel> { trans: &DBTrans,
let stats = trans.get_location_stats( container: &str,
container proposed_weight: u64,
).await?; ) -> DResult<CapacityLevel> {
if let Some((item_type, item_code)) = container.split_once("/") {
if item_type != "player" && item_type != "npc" {
// Fast path...
let stats = trans.get_location_stats(&container).await?;
if stats.total_count >= 50 || proposed_weight > 0 && stats.total_count >= 49 {
Ok(CapacityLevel::AboveItemLimit)
} else {
Ok(CapacityLevel::Unburdened)
}
} else {
if let Some(item) = trans.find_item_by_type_code(item_type, item_code).await? {
check_item_capacity(trans, &item, proposed_weight).await
} else {
Err("Invalid item.")?
}
}
} else {
Err("Invalid item format.")?
}
}
pub async fn check_item_capacity(
trans: &DBTrans,
container: &Item,
proposed_weight: u64,
) -> DResult<CapacityLevel> {
let stats = trans.get_location_stats(&container.refstr()).await?;
if stats.total_count >= 50 || proposed_weight > 0 && stats.total_count >= 49 { if stats.total_count >= 50 || proposed_weight > 0 && stats.total_count >= 49 {
return Ok(CapacityLevel::AboveItemLimit); return Ok(CapacityLevel::AboveItemLimit);
} }
if let Some((item_type, _item_code)) = container.split_once("/") { if container.item_type == "player" || container.item_type == "npc" {
if item_type == "player" || item_type == "npc" { let max_weight = container.max_carry();
let max_weight = 20000; // TODO Calculate properly let new_weight = stats.total_weight + proposed_weight;
let new_weight = stats.total_weight + proposed_weight; if new_weight >= max_weight {
if new_weight >= max_weight { return Ok(CapacityLevel::OverBurdened);
return Ok(CapacityLevel::OverBurdened); } else if new_weight >= max_weight * 4 / 5 {
} else if new_weight >= max_weight * 4 / 5 { return Ok(CapacityLevel::HeavilyBurdened);
return Ok(CapacityLevel::HeavilyBurdened); } else if new_weight >= max_weight / 2 {
} else if new_weight >= max_weight / 2 { return Ok(CapacityLevel::SlightlyBurdened);
return Ok(CapacityLevel::SlightlyBurdened); }
}
}
} }
Ok(CapacityLevel::Unburdened) Ok(CapacityLevel::Unburdened)
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::db::{
MockDBTrans,
LocationStats
};
use super::*; use super::*;
use crate::db::{LocationStats, MockDBTrans};
#[tokio::test] #[tokio::test]
async fn check_item_capacity_should_say_above_item_limit_if_over() { async fn check_item_capacity_should_say_above_item_limit_if_over() {
let mut mock_db = MockDBTrans::new(); let mut mock_db = MockDBTrans::new();
mock_db.expect_get_location_stats() mock_db
.expect_get_location_stats()
.withf(|s| s == "player/foo") .withf(|s| s == "player/foo")
.returning(|_| Ok(LocationStats { .returning(|_| {
total_count: 49, Ok(LocationStats {
total_weight: 100, total_count: 49,
})); total_weight: 100,
assert_eq!(check_item_capacity(&mock_db, "player/foo", 10).await.unwrap(), })
CapacityLevel::AboveItemLimit); });
assert_eq!(
check_item_ref_capacity(&mock_db, "player/foo", 10)
.await
.unwrap(),
CapacityLevel::AboveItemLimit
);
} }
#[tokio::test] #[tokio::test]
async fn check_item_capacity_should_say_overburdened_if_over() { async fn check_item_capacity_should_say_overburdened_if_over() {
let mut mock_db = MockDBTrans::new(); let mut mock_db = MockDBTrans::new();
mock_db.expect_get_location_stats() mock_db
.expect_get_location_stats()
.withf(|s| s == "player/foo") .withf(|s| s == "player/foo")
.returning(|_| Ok(LocationStats { .returning(|_| {
total_count: 2, Ok(LocationStats {
total_weight: 100, total_count: 2,
})); total_weight: 100,
assert_eq!(check_item_capacity(&mock_db, "player/foo", 1000000).await.unwrap(), })
CapacityLevel::OverBurdened); });
assert_eq!(
check_item_ref_capacity(&mock_db, "player/foo", 1000000)
.await
.unwrap(),
CapacityLevel::OverBurdened
);
} }
#[tokio::test] #[tokio::test]
async fn check_item_capacity_should_say_unburdened_when_low_weight() { async fn check_item_capacity_should_say_unburdened_when_low_weight() {
let mut mock_db = MockDBTrans::new(); let mut mock_db = MockDBTrans::new();
mock_db.expect_get_location_stats() mock_db
.expect_get_location_stats()
.withf(|s| s == "player/foo") .withf(|s| s == "player/foo")
.returning(|_| Ok(LocationStats { .returning(|_| {
total_count: 2, Ok(LocationStats {
total_weight: 100, total_count: 2,
})); total_weight: 100,
assert_eq!(check_item_capacity(&mock_db, "player/foo", 50).await.unwrap(), })
CapacityLevel::Unburdened); });
assert_eq!(
check_item_ref_capacity(&mock_db, "player/foo", 50)
.await
.unwrap(),
CapacityLevel::Unburdened
);
} }
} }

View File

@ -13,7 +13,7 @@ use crate::{
}, },
models::{ models::{
consent::ConsentType, consent::ConsentType,
item::{Item, ItemFlag, Pronouns, SkillType}, item::{Item, ItemFlag, Pronouns, SkillType, StatType},
task::{Task, TaskDetails, TaskMeta, TaskRecurrence}, task::{Task, TaskDetails, TaskMeta, TaskRecurrence},
}, },
regular_tasks::{ regular_tasks::{
@ -99,6 +99,7 @@ pub struct NPC {
pub intrinsic_weapon: Option<PossessionType>, pub intrinsic_weapon: Option<PossessionType>,
pub total_xp: u64, pub total_xp: u64,
pub total_skills: BTreeMap<SkillType, f64>, pub total_skills: BTreeMap<SkillType, f64>,
pub total_stats: BTreeMap<StatType, f64>,
pub species: SpeciesType, pub species: SpeciesType,
pub wander_zones: Vec<&'static str>, pub wander_zones: Vec<&'static str>,
pub kill_bonus: Option<KillBonus>, pub kill_bonus: Option<KillBonus>,
@ -128,6 +129,7 @@ impl Default for NPC {
) )
}) })
.collect(), .collect(),
total_stats: vec![].into_iter().collect(),
aggression: 0, aggression: 0,
max_health: 24, max_health: 24,
intrinsic_weapon: None, intrinsic_weapon: None,
@ -202,6 +204,7 @@ pub fn npc_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
pronouns: c.pronouns.clone(), pronouns: c.pronouns.clone(),
total_xp: c.total_xp.clone(), total_xp: c.total_xp.clone(),
total_skills: c.total_skills.clone(), total_skills: c.total_skills.clone(),
total_stats: c.total_stats.clone(),
species: c.species.clone(), species: c.species.clone(),
health: c.max_health.clone(), health: c.max_health.clone(),
flags, flags,

View File

@ -6,7 +6,7 @@ use crate::{
user_commands::{rent::recursively_destroy_or_move_item, UResult}, user_commands::{rent::recursively_destroy_or_move_item, UResult},
ListenerSession, ListenerSession,
}, },
models::item::{FollowData, FollowState, Item, ItemFlag, Pronouns}, models::item::{FollowData, FollowState, Item, ItemFlag, Pronouns, StatType},
services::comms::broadcast_to_room, services::comms::broadcast_to_room,
static_content::{possession_type::PossessionType, species::SpeciesType}, static_content::{possession_type::PossessionType, species::SpeciesType},
DResult, DResult,
@ -120,6 +120,7 @@ macro_rules! roboporter {
price: 100, price: 100,
}), }),
extra_flags: vec![ItemFlag::CanLoad], extra_flags: vec![ItemFlag::CanLoad],
total_stats: vec![(StatType::Brawn, 20.0)].into_iter().collect(),
..Default::default() ..Default::default()
} }
} }

View File

@ -254,7 +254,7 @@ pub struct PossessionData {
pub charge_data: Option<ChargeData>, pub charge_data: Option<ChargeData>,
pub use_data: Option<UseData>, pub use_data: Option<UseData>,
pub becomes_on_spent: Option<PossessionType>, pub becomes_on_spent: Option<PossessionType>,
pub weight: u64, pub weight: u64, // should be realistic in grams.
pub install_handler: Option<&'static (dyn InstallHandler + Sync + Send)>, pub install_handler: Option<&'static (dyn InstallHandler + Sync + Send)>,
pub lockcheck_handler: Option<&'static (dyn LockcheckHandler + Sync + Send)>, pub lockcheck_handler: Option<&'static (dyn LockcheckHandler + Sync + Send)>,
pub sign_handler: Option<&'static (dyn ArglessHandler + Sync + Send)>, pub sign_handler: Option<&'static (dyn ArglessHandler + Sync + Send)>,
@ -273,7 +273,7 @@ impl Default for PossessionData {
details_less_explicit: None, details_less_explicit: None,
aliases: vec![], aliases: vec![],
max_health: 10, max_health: 10,
weight: 100, weight: 250,
charge_data: None, charge_data: None,
becomes_on_spent: None, becomes_on_spent: None,
use_data: None, use_data: None,

View File

@ -11,6 +11,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "rusty metal pot", display: "rusty metal pot",
details: "A metal pot that has rusted and is a bit dinged up - it looks like someone that way inclined could wear it as a serviceable helmet.", details: "A metal pot that has rusted and is a bit dinged up - it looks like someone that way inclined could wear it as a serviceable helmet.",
aliases: vec!("pot", "rusted", "rusty"), aliases: vec!("pot", "rusted", "rusty"),
weight: 600,
wear_data: Some(WearData { wear_data: Some(WearData {
covers_parts: vec!(BodyPart::Head), covers_parts: vec!(BodyPart::Head),
thickness: 4.0, thickness: 4.0,
@ -48,6 +49,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "hockey mask", display: "hockey mask",
details: "A white face-hugging fibreglass hockey mask, with small air holes across the face, but no specific hole for the mouth. It looks like it would give a degree of protection to the face, but it might also make someone look like a serial killer!", details: "A white face-hugging fibreglass hockey mask, with small air holes across the face, but no specific hole for the mouth. It looks like it would give a degree of protection to the face, but it might also make someone look like a serial killer!",
aliases: vec!("mask"), aliases: vec!("mask"),
weight: 300,
wear_data: Some(WearData { wear_data: Some(WearData {
covers_parts: vec!(BodyPart::Face), covers_parts: vec!(BodyPart::Face),
thickness: 4.0, thickness: 4.0,

View File

@ -58,7 +58,7 @@ impl InstallHandler for ScanLockInstall {
user_error("There's already a lock on that door - uninstall it first.".to_owned())?; user_error("There's already a lock on that door - uninstall it first.".to_owned())?;
} }
match check_item_capacity(&ctx.trans, &room.refstr(), LOCK_WEIGHT).await? { match check_item_capacity(&ctx.trans, &room, LOCK_WEIGHT).await? {
CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => user_error( CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => user_error(
"That room has so much stuff, you can't install anything new.".to_owned(), "That room has so much stuff, you can't install anything new.".to_owned(),
)?, )?,
@ -105,16 +105,15 @@ impl InstallHandler for ScanLockInstall {
let mut what_mut = (*what).clone(); let mut what_mut = (*what).clone();
let extra_text = let extra_text = match check_item_capacity(&ctx.trans, &player, LOCK_WEIGHT).await? {
match check_item_capacity(&ctx.trans, &player.refstr(), LOCK_WEIGHT).await? { CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => {
CapacityLevel::OverBurdened | CapacityLevel::AboveItemLimit => { ", dropping it on the floor since he can't hold it."
", dropping it on the floor since he can't hold it." }
} _ => {
_ => { what_mut.location = player.refstr();
what_mut.location = player.refstr(); ""
"" }
} };
};
what_mut.action_type = LocationActionType::Normal; what_mut.action_type = LocationActionType::Normal;
what_mut.owner = None; what_mut.owner = None;
ctx.trans.save_item_model(&what_mut).await?; ctx.trans.save_item_model(&what_mut).await?;

View File

@ -11,6 +11,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "pair of leather pants", display: "pair of leather pants",
details: "Black leather pants that looks like they would protect you from falling off a motorbike, or maybe even offer some protection against certain weapons", details: "Black leather pants that looks like they would protect you from falling off a motorbike, or maybe even offer some protection against certain weapons",
aliases: vec!("leather pants", "pants"), aliases: vec!("leather pants", "pants"),
weight: 500,
wear_data: Some(WearData { wear_data: Some(WearData {
covers_parts: vec!(BodyPart::Groin, BodyPart::Legs), covers_parts: vec!(BodyPart::Groin, BodyPart::Legs),
thickness: 4.0, thickness: 4.0,

View File

@ -18,7 +18,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
PossessionData { PossessionData {
display: "steak", display: "steak",
details: "A hunk of raw red meat, dripping with blood", details: "A hunk of raw red meat, dripping with blood",
weight: 100, weight: 250,
..Default::default() ..Default::default()
} }
), ),
@ -28,7 +28,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "severed head", display: "severed head",
aliases: vec!("head"), aliases: vec!("head"),
details: "A head that has been chopped clean from the body", details: "A head that has been chopped clean from the body",
weight: 250, weight: 1500,
..Default::default() ..Default::default()
} }
), ),

View File

@ -11,6 +11,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "leather jacket", display: "leather jacket",
details: "A black leather jacket that looks like it would protect you from falling off a motorbike, or maybe even offer some protection against certain weapons", details: "A black leather jacket that looks like it would protect you from falling off a motorbike, or maybe even offer some protection against certain weapons",
aliases: vec!("jacket"), aliases: vec!("jacket"),
weight: 600,
wear_data: Some(WearData { wear_data: Some(WearData {
covers_parts: vec!(BodyPart::Arms, covers_parts: vec!(BodyPart::Arms,
BodyPart::Chest, BodyPart::Chest,

View File

@ -11,6 +11,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
details: "A crudely fashioned whip made from a broken metal antenna. It looks a bit flimsy, but it \ details: "A crudely fashioned whip made from a broken metal antenna. It looks a bit flimsy, but it \
might do you until you get a better weapon!", might do you until you get a better weapon!",
aliases: vec!("whip"), aliases: vec!("whip"),
weight: 1000,
weapon_data: Some(WeaponData { weapon_data: Some(WeaponData {
uses_skill: SkillType::Whips, uses_skill: SkillType::Whips,
raw_min_to_learn: 0.0, raw_min_to_learn: 0.0,
@ -48,6 +49,7 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
display: "leather whip", display: "leather whip",
details: "A whip made from stitched together animal skins... it looks like a formidable weapon, and in the right hands will make someone look like Indiana Jones!", details: "A whip made from stitched together animal skins... it looks like a formidable weapon, and in the right hands will make someone look like Indiana Jones!",
aliases: vec!("whip"), aliases: vec!("whip"),
weight: 800,
weapon_data: Some(WeaponData { weapon_data: Some(WeaponData {
uses_skill: SkillType::Whips, uses_skill: SkillType::Whips,
raw_min_to_learn: 0.0, raw_min_to_learn: 0.0,