Improve get targeting with 'all' option.

This commit is contained in:
Condorra 2023-02-20 22:27:43 +11:00
parent ddd0f33cb5
commit d4fd71d839
8 changed files with 92 additions and 17 deletions

View File

@ -235,6 +235,8 @@ pub struct ItemSearchParams<'l> {
pub include_loc_contents: bool, pub include_loc_contents: bool,
pub include_active_players: bool, pub include_active_players: bool,
pub include_all_players: bool, pub include_all_players: bool,
pub item_type_only: Option<&'l str>,
pub limit: u8,
pub dead_first: bool, pub dead_first: bool,
} }
@ -247,6 +249,8 @@ impl ItemSearchParams<'_> {
include_active_players: false, include_active_players: false,
include_all_players: false, include_all_players: false,
dead_first: false, dead_first: false,
limit: 100,
item_type_only: None,
} }
} }
} }
@ -521,8 +525,30 @@ impl DBTrans {
let player_desig = format!("{}/{}", search.from_item.item_type, let player_desig = format!("{}/{}", search.from_item.item_type,
search.from_item.item_code); search.from_item.item_code);
let (offset, query) = parse_offset(search.query); let (offset, mut query) = parse_offset(search.query);
let mut param_no: usize = 5; let mut param_no: usize = 6;
let mut extra_order: String = String::new();
let mut extra_where: String = String::new();
let mut dead_only = false;
if query.starts_with("dead") {
query = query[4..].trim();
dead_only = true;
} else if query.starts_with("corpse of") {
query = query[9..].trim();
dead_only = true;
} else if query.starts_with("corpse") {
query = query[6..].trim();
dead_only = true;
}
if dead_only {
extra_where.push_str(" AND COALESCE(CAST(details->>'is_dead' AS boolean), false) = true");
} else if search.dead_first {
extra_order.push_str(" COALESCE(CAST(details->>'is_dead' AS boolean), false) DESC,");
} else {
extra_order.push_str(" COALESCE(CAST(details->>'is_dead' AS boolean), false) ASC,");
}
let query_wildcard = query.replace("\\", "\\\\") let query_wildcard = query.replace("\\", "\\\\")
.replace("_", "\\_") .replace("_", "\\_")
.replace("%", "") .replace("%", "")
@ -530,10 +556,23 @@ impl DBTrans {
let offset_sql = offset.map(|x| (if x >= 1 { x - 1 } else { x}) as i64).unwrap_or(0); let offset_sql = offset.map(|x| (if x >= 1 { x - 1 } else { x}) as i64).unwrap_or(0);
let query_json = serde_json::to_value(query.to_lowercase())?; let query_json = serde_json::to_value(query.to_lowercase())?;
let query_len = query.len() as i32; let query_len = query.len() as i32;
let limit = search.limit as i64;
let mut params: Vec<&(dyn ToSql + Sync)> = vec!( let mut params: Vec<&(dyn ToSql + Sync)> = vec!(
&query_wildcard, &query_wildcard,
&offset_sql, &query_len, &query_json); &offset_sql,
&query_len,
&query_json,
&limit
);
match search.item_type_only {
None => {}
Some(ref item_type) => {
extra_where.push_str(&format!(" AND details->>'item_type' = ${}", param_no));
param_no += 1;
params.push(item_type);
}
}
if search.include_contents { if search.include_contents {
ctes.push(format!("contents AS (\ ctes.push(format!("contents AS (\
@ -570,11 +609,12 @@ impl DBTrans {
Ok(Arc::new(self.pg_trans()?.query( Ok(Arc::new(self.pg_trans()?.query(
&format!( &format!(
"WITH {} SELECT details, aliases FROM relevant_items WHERE (lower(details->>'display') LIKE $1) \ "WITH {} SELECT details, aliases FROM relevant_items WHERE \
((lower(details->>'display') LIKE $1) \
OR (lower(details ->>'display_less_explicit') LIKE $1) \ OR (lower(details ->>'display_less_explicit') LIKE $1) \
OR aliases @> $4 \ OR aliases @> $4) {} \
ORDER BY ABS(length(details->>'display')-$3) ASC \ ORDER BY {} ABS(length(details->>'display')-$3) ASC \
LIMIT 1 OFFSET $2", &cte_str), LIMIT $5 OFFSET $2", &cte_str, &extra_where, &extra_order),
&params &params
).await?.into_iter() ).await?.into_iter()
.filter_map(|i| serde_json::from_value(i.get("details")).ok()) .filter_map(|i| serde_json::from_value(i.get("details")).ok())

View File

@ -242,6 +242,14 @@ pub async fn search_item_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l Item
}) })
} }
pub async fn search_items_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l ItemSearchParams<'l>) ->
UResult<Vec<Arc<Item>>> {
Ok(match &ctx.trans.resolve_items_by_display_name_for_player(search).await?[..] {
[] => user_error("Sorry, I couldn't find anything matching.".to_owned())?,
v => v.into_iter().map(|it| it.clone()).collect(),
})
}
#[cfg(test)] mod test { #[cfg(test)] mod test {
use crate::db::MockDBTrans; use crate::db::MockDBTrans;

View File

@ -22,6 +22,7 @@ impl UserVerb for Verb {
let attack_whom = search_item_for_user(ctx, &ItemSearchParams { let attack_whom = search_item_for_user(ctx, &ItemSearchParams {
include_loc_contents: true, include_loc_contents: true,
limit: 1,
..ItemSearchParams::base(&player_item, remaining) ..ItemSearchParams::base(&player_item, remaining)
}).await?; }).await?;

View File

@ -6,7 +6,8 @@ use super::{
ItemSearchParams, ItemSearchParams,
user_error, user_error,
get_player_item_or_fail, get_player_item_or_fail,
search_item_for_user, search_items_for_user,
parsing::parse_count
}; };
use crate::{ use crate::{
static_content::possession_type::possession_data, static_content::possession_type::possession_data,
@ -115,20 +116,32 @@ impl QueueCommandHandler for QueueHandler {
pub struct Verb; pub struct Verb;
#[async_trait] #[async_trait]
impl UserVerb for Verb { impl UserVerb for Verb {
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> { async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, mut remaining: &str) -> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?; let player_item = get_player_item_or_fail(ctx).await?;
// TODO: Parse "get target from container" variant // TODO: Parse "get target from container" variant
let target = search_item_for_user(ctx, &ItemSearchParams { let mut get_limit = Some(1);
if remaining == "all" || remaining.starts_with("all ") {
remaining = remaining[3..].trim();
get_limit = None;
} else if let (Some(n), remaining2) = parse_count(remaining) {
get_limit = Some(n);
remaining = remaining2;
}
let targets = search_items_for_user(ctx, &ItemSearchParams {
include_loc_contents: true, include_loc_contents: true,
item_type_only: Some("possession"),
limit: get_limit.unwrap_or(100),
..ItemSearchParams::base(&player_item, &remaining) ..ItemSearchParams::base(&player_item, &remaining)
}).await?; }).await?;
if player_item.is_dead { if player_item.is_dead {
user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?; user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?;
} }
for target in targets {
if target.item_type != "possession" { if target.item_type != "possession" {
user_error("You can't get that!".to_owned())?; user_error("You can't get that!".to_owned())?;
} }
queue_command(ctx, &QueueCommand::Get { possession_id: target.item_code.clone() }).await?; queue_command(ctx, &QueueCommand::Get { possession_id: target.item_code.clone() }).await?;
}
Ok(()) Ok(())
} }
} }

View File

@ -219,6 +219,7 @@ impl UserVerb for Verb {
&ItemSearchParams { &ItemSearchParams {
include_contents: true, include_contents: true,
include_loc_contents: true, include_loc_contents: true,
limit: 1,
..ItemSearchParams::base(&player_item, &rem_trim) ..ItemSearchParams::base(&player_item, &rem_trim)
} }
).await? ).await?

View File

@ -45,6 +45,16 @@ pub fn parse_offset(input: &str) -> (Option<u8>, &str) {
} }
} }
pub fn parse_count(input: &str) -> (Option<u8>, &str) {
fn parser(input: &str) -> IResult<&str, u8> {
terminated(u8, char(' '))(input)
}
match parser(input) {
Err(_) => (None, input),
Ok((rest, result)) => (Some(result), rest)
}
}
pub fn parse_username(input: &str) -> Result<(&str, &str), &'static str> { pub fn parse_username(input: &str) -> Result<(&str, &str), &'static str> {
const CATCHALL_ERROR: &'static str = "Must only contain alphanumeric characters or _"; const CATCHALL_ERROR: &'static str = "Must only contain alphanumeric characters or _";
fn parse_valid(input: &str) -> IResult<&str, (), VerboseError<&str>> { fn parse_valid(input: &str) -> IResult<&str, (), VerboseError<&str>> {

View File

@ -22,6 +22,7 @@ impl UserVerb for Verb {
} }
let to_whom = search_item_for_user(ctx, &ItemSearchParams { let to_whom = search_item_for_user(ctx, &ItemSearchParams {
include_loc_contents: true, include_loc_contents: true,
limit: 1,
..ItemSearchParams::base(&player_item, &to_whom_name) ..ItemSearchParams::base(&player_item, &to_whom_name)
}).await?; }).await?;

View File

@ -117,6 +117,7 @@ impl UserVerb for Verb {
let player_item = get_player_item_or_fail(ctx).await?; let player_item = get_player_item_or_fail(ctx).await?;
let weapon = search_item_for_user(ctx, &ItemSearchParams { let weapon = search_item_for_user(ctx, &ItemSearchParams {
include_contents: true, include_contents: true,
limit: 1,
..ItemSearchParams::base(&player_item, &remaining) ..ItemSearchParams::base(&player_item, &remaining)
}).await?; }).await?;
if player_item.is_dead { if player_item.is_dead {