Apply limits on carrying capacity, and make roboporter strong.
This commit is contained in:
parent
e83cc19698
commit
ea45530a39
@ -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,
|
||||||
|
@ -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())?
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(())
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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())?
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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("/") {
|
||||||
@ -42,10 +43,13 @@ fn check_one_consent(consent: &Consent, action: &str, target: &Item) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_consent(trans: &DBTrans, action: &str,
|
pub async fn check_consent(
|
||||||
|
trans: &DBTrans,
|
||||||
|
action: &str,
|
||||||
consent_type: &ConsentType,
|
consent_type: &ConsentType,
|
||||||
by: &Item,
|
by: &Item,
|
||||||
target: &Item) -> DResult<bool> {
|
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,25 +57,29 @@ 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);
|
||||||
}
|
}
|
||||||
@ -81,23 +89,31 @@ pub async fn check_consent(trans: &DBTrans, action: &str,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
.await?;
|
||||||
|
|
||||||
for item in trans.find_items_by_location(
|
for item in trans
|
||||||
&container.refstr()
|
.find_items_by_location(&container.refstr())
|
||||||
).await?.into_iter() {
|
.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(())
|
||||||
}
|
}
|
||||||
|
@ -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,18 +11,44 @@ pub enum CapacityLevel {
|
|||||||
OverBurdened,
|
OverBurdened,
|
||||||
AboveItemLimit,
|
AboveItemLimit,
|
||||||
}
|
}
|
||||||
pub async fn check_item_capacity(trans: &DBTrans,
|
|
||||||
|
pub async fn check_item_ref_capacity(
|
||||||
|
trans: &DBTrans,
|
||||||
container: &str,
|
container: &str,
|
||||||
proposed_weight: u64) -> DResult<CapacityLevel> {
|
proposed_weight: u64,
|
||||||
let stats = trans.get_location_stats(
|
) -> DResult<CapacityLevel> {
|
||||||
container
|
if let Some((item_type, item_code)) = container.split_once("/") {
|
||||||
).await?;
|
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);
|
||||||
@ -32,54 +58,71 @@ pub async fn check_item_capacity(trans: &DBTrans,
|
|||||||
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(|_| {
|
||||||
|
Ok(LocationStats {
|
||||||
total_count: 49,
|
total_count: 49,
|
||||||
total_weight: 100,
|
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(|_| {
|
||||||
|
Ok(LocationStats {
|
||||||
total_count: 2,
|
total_count: 2,
|
||||||
total_weight: 100,
|
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(|_| {
|
||||||
|
Ok(LocationStats {
|
||||||
total_count: 2,
|
total_count: 2,
|
||||||
total_weight: 100,
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,8 +105,7 @@ 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."
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user