Add drop command, and show inventory on look.

This commit is contained in:
Condorra 2023-02-20 17:38:35 +11:00
parent 25cfda033b
commit d5314c981e
4 changed files with 185 additions and 9 deletions

View File

@ -14,6 +14,7 @@ use std::sync::Arc;
mod agree;
pub mod attack;
mod buy;
pub mod drop;
pub mod get;
mod describe;
mod help;
@ -107,6 +108,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
// Other commands (alphabetical except aliases grouped):
"attack" => attack::VERB,
"buy" => buy::VERB,
"drop" => drop::VERB,
"get" => get::VERB,
"inventory" => inventory::VERB,
"inv" => inventory::VERB,

View File

@ -0,0 +1,133 @@
use super::{
VerbContext,
UserVerb,
UserVerbRef,
UResult,
ItemSearchParams,
user_error,
get_player_item_or_fail,
search_item_for_user,
};
use crate::{
static_content::possession_type::possession_data,
regular_tasks::queued_command::{
QueueCommandHandler,
QueueCommand,
queue_command
},
services::{
broadcast_to_room,
capacity::{
check_item_capacity,
CapacityLevel,
}
},
models::item::LocationActionType,
};
use async_trait::async_trait;
use std::time;
pub struct QueueHandler;
#[async_trait]
impl QueueCommandHandler for QueueHandler {
async fn start_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
-> UResult<time::Duration> {
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.is_dead {
user_error("You try to drop it, but your ghostly hands slip through it uselessly".to_owned())?;
}
let item_id = match command {
QueueCommand::Drop { possession_id } => possession_id,
_ => user_error("Unexpected command".to_owned())?
};
let item = match ctx.trans.find_item_by_type_code("possession", &item_id).await? {
None => user_error("Item not found".to_owned())?,
Some(it) => it
};
if item.location != format!("{}/{}", &player_item.item_type, &player_item.item_code) {
user_error(
format!("You try to drop {} but realise you no longer have it",
item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)
)
)?
}
let msg_exp = format!("{} prepares to drop {}\n",
&player_item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false));
let msg_nonexp = format!("{} prepares to drop {}\n",
&player_item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false));
broadcast_to_room(ctx.trans, &player_item.location, None, &msg_exp, Some(&msg_nonexp)).await?;
Ok(time::Duration::from_secs(1))
}
#[allow(unreachable_patterns)]
async fn finish_command(&self, ctx: &mut VerbContext<'_>, command: &QueueCommand)
-> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?;
if player_item.is_dead {
user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?;
}
let item_id = match command {
QueueCommand::Drop { possession_id } => possession_id,
_ => user_error("Unexpected command".to_owned())?
};
let item = match ctx.trans.find_item_by_type_code("possession", &item_id).await? {
None => user_error("Item not found".to_owned())?,
Some(it) => it
};
if item.location != format!("{}/{}", &player_item.item_type, &player_item.item_code) {
user_error(format!("You try to drop {} but realise you no longer have it!",
&item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)))?
}
let possession_data = match item.possession_type.as_ref().and_then(|pt| possession_data().get(&pt)) {
None => user_error("That item no longer exists in the game so can't be handled".to_owned())?,
Some(pd) => pd
};
match check_item_capacity(ctx.trans, &player_item.location, possession_data.weight).await? {
CapacityLevel::AboveItemLimit => user_error(
format!("You can't drop {}, because it is so cluttered here there is no where to put it!",
&item.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, false)
),
)?,
_ => ()
}
let msg_exp = format!("{} drops {}\n",
&player_item.display_for_sentence(true, 1, true),
&item.display_for_sentence(true, 1, false));
let msg_nonexp = format!("{} drops {}\n",
&player_item.display_for_sentence(false, 1, true),
&item.display_for_sentence(false, 1, false));
broadcast_to_room(ctx.trans, &player_item.location, None, &msg_exp, Some(&msg_nonexp)).await?;
let mut item_mut = (*item).clone();
item_mut.location = player_item.location.clone();
item_mut.action_type = LocationActionType::Normal;
ctx.trans.save_item_model(&item_mut).await?;
Ok(())
}
}
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?;
let target = search_item_for_user(ctx, &ItemSearchParams {
include_contents: true,
..ItemSearchParams::base(&player_item, &remaining)
}).await?;
if player_item.is_dead {
user_error("You try to drop it, but your ghostly hands slip through it uselessly".to_owned())?;
}
if target.item_type != "possession" {
user_error("You can't drop that!".to_owned())?;
}
queue_command(ctx, &QueueCommand::Drop { possession_id: target.item_code.clone() }).await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -2,9 +2,12 @@ use super::{VerbContext, UserVerb, UserVerbRef, UResult, UserError, user_error,
get_player_item_or_fail, search_item_for_user};
use async_trait::async_trait;
use ansi::{ansi, flow_around, word_wrap};
use crate::db::ItemSearchParams;
use crate::models::{item::{Item, LocationActionType, Subattack, ItemFlag}};
use crate::static_content::room::{self, Direction};
use crate::{
db::ItemSearchParams,
models::{item::{Item, LocationActionType, Subattack, ItemFlag}},
static_content::room::{self, Direction},
language,
};
use itertools::Itertools;
use std::sync::Arc;
@ -39,11 +42,45 @@ pub fn render_map(room: &room::Room, width: usize, height: usize) -> String {
}
pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> {
let mut contents_desc = String::new();
let mut items = ctx.trans.find_items_by_location(&format!("{}/{}",
item.item_type, item.item_code)).await?;
items.sort_unstable_by(|it1, it2| (&it1.display).cmp(&it2.display));
let all_groups: Vec<Vec<&Arc<Item>>> = items
.iter()
.group_by(|i| i.display_for_sentence(true, 1, false))
.into_iter()
.map(|(_, g)|g.collect::<Vec<&Arc<Item>>>())
.collect::<Vec<Vec<&Arc<Item>>>>();
if all_groups.len() > 0 {
contents_desc.push_str(&(language::caps_first(&item.pronouns.subject)));
if item.item_type == "player" || item.item_type == "npc" {
contents_desc.push_str("'s carrying ");
} else {
contents_desc.push_str(" contains ");
}
let mut phrases = Vec::<String>::new();
for group_items in all_groups {
let head = &group_items[0];
let mut details = head.display_for_sentence(!ctx.session_dat.less_explicit_mode,
group_items.len(), false);
if head.action_type == LocationActionType::Wielded {
details.push_str(" (wielded)");
}
phrases.push(details);
}
let phrases_str: Vec<&str> = phrases.iter().map(|p| p.as_str()).collect();
contents_desc.push_str(&(language::join_words(&phrases_str) + ".\n"));
}
ctx.trans.queue_for_session(
ctx.session,
Some(&format!("{}\n{}\n",
Some(&format!("{}\n{}\n{}",
&item.display_for_session(&ctx.session_dat),
item.details_for_session(&ctx.session_dat).unwrap_or("")
item.details_for_session(&ctx.session_dat).unwrap_or(""),
contents_desc,
))
).await?;
Ok(())
@ -73,7 +110,7 @@ pub async fn describe_room(ctx: &VerbContext<'_>, item: &Item,
Ok(())
}
async fn list_item_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> UResult<String> {
async fn list_room_contents<'l>(ctx: &'l VerbContext<'_>, item: &'l Item) -> UResult<String> {
if item.flags.contains(&ItemFlag::NoSeeContents) {
return Ok(" It is too foggy to see who or what else is here.".to_owned());
}
@ -192,7 +229,7 @@ impl UserVerb for Verb {
let room =
room::room_map_by_code().get(item.item_code.as_str())
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
describe_room(ctx, &item, &room, &list_item_contents(ctx, &item).await?).await?;
describe_room(ctx, &item, &room, &list_room_contents(ctx, &item).await?).await?;
}
Ok(())
}

View File

@ -14,9 +14,10 @@ use crate::message_handler::user_commands::{
VerbContext,
CommandHandlingError,
UResult,
get,
drop,
movement,
wield,
get,
user_error,
get_user_or_fail
};
@ -28,6 +29,7 @@ pub enum QueueCommand {
Movement { direction: Direction },
Wield { possession_id: String },
Get { possession_id: String },
Drop { possession_id: String },
}
impl QueueCommand {
pub fn name(&self) -> &'static str {
@ -36,6 +38,7 @@ impl QueueCommand {
Movement {..} => "Movement",
Wield {..} => "Wield",
Get {..} => "Get",
Drop {..} => "Drop",
}
}
}
@ -50,9 +53,10 @@ fn queue_command_registry() -> &'static BTreeMap<&'static str, &'static (dyn Que
static REGISTRY: OnceCell<BTreeMap<&'static str, &'static (dyn QueueCommandHandler + Sync + Send)>> =
OnceCell::new();
REGISTRY.get_or_init(|| vec!(
("Drop", &drop::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
("Get", &get::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
("Movement", &movement::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
("Wield", &wield::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
("Get", &get::QueueHandler as &(dyn QueueCommandHandler + Sync + Send)),
).into_iter().collect())
}