Improve get targeting with 'all' option.
This commit is contained in:
parent
ddd0f33cb5
commit
d4fd71d839
@ -235,6 +235,8 @@ pub struct ItemSearchParams<'l> {
|
||||
pub include_loc_contents: bool,
|
||||
pub include_active_players: bool,
|
||||
pub include_all_players: bool,
|
||||
pub item_type_only: Option<&'l str>,
|
||||
pub limit: u8,
|
||||
pub dead_first: bool,
|
||||
}
|
||||
|
||||
@ -247,6 +249,8 @@ impl ItemSearchParams<'_> {
|
||||
include_active_players: false,
|
||||
include_all_players: 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,
|
||||
search.from_item.item_code);
|
||||
|
||||
let (offset, query) = parse_offset(search.query);
|
||||
let mut param_no: usize = 5;
|
||||
let (offset, mut query) = parse_offset(search.query);
|
||||
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("\\", "\\\\")
|
||||
.replace("_", "\\_")
|
||||
.replace("%", "")
|
||||
@ -530,11 +556,24 @@ impl DBTrans {
|
||||
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_len = query.len() as i32;
|
||||
let limit = search.limit as i64;
|
||||
let mut params: Vec<&(dyn ToSql + Sync)> = vec!(
|
||||
&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 {
|
||||
ctes.push(format!("contents AS (\
|
||||
SELECT details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\
|
||||
@ -567,14 +606,15 @@ impl DBTrans {
|
||||
ctes.push(format!("relevant_items AS ({})", include_tables.join(" UNION ")));
|
||||
|
||||
let cte_str: String = ctes.join(", ");
|
||||
|
||||
|
||||
Ok(Arc::new(self.pg_trans()?.query(
|
||||
&format!(
|
||||
"WITH {} SELECT details, aliases FROM relevant_items WHERE (lower(details->>'display') LIKE $1) \
|
||||
OR (lower(details ->>'display_less_explicit') LIKE $1) \
|
||||
OR aliases @> $4 \
|
||||
ORDER BY ABS(length(details->>'display')-$3) ASC \
|
||||
LIMIT 1 OFFSET $2", &cte_str),
|
||||
"WITH {} SELECT details, aliases FROM relevant_items WHERE \
|
||||
((lower(details->>'display') LIKE $1) \
|
||||
OR (lower(details ->>'display_less_explicit') LIKE $1) \
|
||||
OR aliases @> $4) {} \
|
||||
ORDER BY {} ABS(length(details->>'display')-$3) ASC \
|
||||
LIMIT $5 OFFSET $2", &cte_str, &extra_where, &extra_order),
|
||||
¶ms
|
||||
).await?.into_iter()
|
||||
.filter_map(|i| serde_json::from_value(i.get("details")).ok())
|
||||
|
@ -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 {
|
||||
use crate::db::MockDBTrans;
|
||||
|
||||
|
@ -22,6 +22,7 @@ impl UserVerb for Verb {
|
||||
|
||||
let attack_whom = search_item_for_user(ctx, &ItemSearchParams {
|
||||
include_loc_contents: true,
|
||||
limit: 1,
|
||||
..ItemSearchParams::base(&player_item, remaining)
|
||||
}).await?;
|
||||
|
||||
|
@ -6,7 +6,8 @@ use super::{
|
||||
ItemSearchParams,
|
||||
user_error,
|
||||
get_player_item_or_fail,
|
||||
search_item_for_user,
|
||||
search_items_for_user,
|
||||
parsing::parse_count
|
||||
};
|
||||
use crate::{
|
||||
static_content::possession_type::possession_data,
|
||||
@ -115,20 +116,32 @@ impl QueueCommandHandler for QueueHandler {
|
||||
pub struct Verb;
|
||||
#[async_trait]
|
||||
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?;
|
||||
// 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,
|
||||
item_type_only: Some("possession"),
|
||||
limit: get_limit.unwrap_or(100),
|
||||
..ItemSearchParams::base(&player_item, &remaining)
|
||||
}).await?;
|
||||
if player_item.is_dead {
|
||||
user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?;
|
||||
}
|
||||
if target.item_type != "possession" {
|
||||
user_error("You can't get that!".to_owned())?;
|
||||
for target in targets {
|
||||
if target.item_type != "possession" {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -219,6 +219,7 @@ impl UserVerb for Verb {
|
||||
&ItemSearchParams {
|
||||
include_contents: true,
|
||||
include_loc_contents: true,
|
||||
limit: 1,
|
||||
..ItemSearchParams::base(&player_item, &rem_trim)
|
||||
}
|
||||
).await?
|
||||
|
@ -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> {
|
||||
const CATCHALL_ERROR: &'static str = "Must only contain alphanumeric characters or _";
|
||||
fn parse_valid(input: &str) -> IResult<&str, (), VerboseError<&str>> {
|
||||
|
@ -22,6 +22,7 @@ impl UserVerb for Verb {
|
||||
}
|
||||
let to_whom = search_item_for_user(ctx, &ItemSearchParams {
|
||||
include_loc_contents: true,
|
||||
limit: 1,
|
||||
..ItemSearchParams::base(&player_item, &to_whom_name)
|
||||
}).await?;
|
||||
|
||||
|
@ -117,6 +117,7 @@ impl UserVerb for Verb {
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
let weapon = search_item_for_user(ctx, &ItemSearchParams {
|
||||
include_contents: true,
|
||||
limit: 1,
|
||||
..ItemSearchParams::base(&player_item, &remaining)
|
||||
}).await?;
|
||||
if player_item.is_dead {
|
||||
|
Loading…
Reference in New Issue
Block a user