Implement resolving objects from player's perspective.

This commit is contained in:
Condorra 2022-12-30 00:41:46 +11:00
parent dc71999591
commit 79fea1f3b5
4 changed files with 87 additions and 9 deletions

View File

@ -334,6 +334,66 @@ impl DBTrans {
} }
pub async fn resolve_items_by_display_name_for_player(
self: &Self,
from_item: &Item,
query: &str,
include_contents: bool,
include_loc_contents: bool,
include_active_players: bool,
include_all_players: bool
) -> DResult<Vec<Item>> {
let mut ctes: Vec<String> = Vec::new();
let mut include_tables: Vec<&'static str> = Vec::new();
let player_loc = &from_item.location;
let player_desig = format!("{}/{}", from_item.item_type,
from_item.item_code);
if include_contents {
ctes.push("contents AS (\
SELECT details FROM items WHERE details->>'location' = $1
)".to_owned());
include_tables.push("SELECT details FROM contents");
}
if include_loc_contents {
ctes.push("loc_contents AS (\
SELECT details FROM items WHERE details->>'location' = $2
)".to_owned());
include_tables.push("SELECT details FROM loc_contents");
}
if include_active_players {
ctes.push("active_players AS (\
SELECT details FROM items WHERE details->>'item_type' = 'player' \
AND current_session IS NOT NULL \
)".to_owned());
include_tables.push("SELECT details FROM active_players");
}
if include_all_players {
ctes.push("all_players AS (\
SELECT details FROM items WHERE details->>'item_type' = 'player'
)".to_owned());
include_tables.push("SELECT details FROM all_players");
}
ctes.push(format!("relevant_items AS ({})", include_tables.join(" UNION ")));
let cte_str: String = ctes.join(", ");
Ok(self.pg_trans()?.query(
&format!(
"WITH {} SELECT details FROM relevant_items WHERE (lower(details->>'display') LIKE $3) \
OR (lower(details ->>'display_less_explicit') LIKE $3) \
ORDER BY length(details->>'display') DESC \
LIMIT 2", &cte_str),
&[&player_desig, &player_loc,
&(query.replace("\\", "\\\\")
.replace("_", "\\_")
.replace("%", "")
.to_lowercase() + "%")]
).await?.into_iter()
.filter_map(|i| serde_json::from_value(i.get("details")).ok())
.collect())
}
pub async fn commit(mut self: Self) -> DResult<()> { pub async fn commit(mut self: Self) -> DResult<()> {
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None)); let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
if let Some(trans) = trans_opt { if let Some(trans) = trans_opt {

View File

@ -42,7 +42,7 @@ pub fn render_map(room: &room::Room, width: usize, height: usize) -> String {
pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> { pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> {
ctx.trans.queue_for_session( ctx.trans.queue_for_session(
ctx.session, ctx.session,
Some(&format!("{}\n{}", Some(&format!("{}\n{}\n",
explicit_if_allowed( explicit_if_allowed(
ctx, ctx,
&item.display, &item.display,
@ -147,10 +147,11 @@ 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, remaining: &str) -> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?; let player_item = get_player_item_or_fail(ctx).await?;
let rem_trim = remaining.trim().to_lowercase();
let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen")); let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
let (itype, icode) = if remaining == "" { let (itype, icode): (String, String) = if rem_trim == "" {
Ok((heretype, herecode)) Ok((heretype.to_owned(), herecode.to_owned()))
} else if let Some(dir) = Direction::parse(remaining) { } else if let Some(dir) = Direction::parse(&rem_trim) {
if heretype != "room" { if heretype != "room" {
// Fix this when we have planes / boats / roomkits. // Fix this when we have planes / boats / roomkits.
user_error("Navigating outside rooms not yet supported.".to_owned()) user_error("Navigating outside rooms not yet supported.".to_owned())
@ -161,7 +162,7 @@ impl UserVerb for Verb {
Some(exit) => { Some(exit) => {
match room::resolve_exit(room, exit) { match room::resolve_exit(room, exit) {
None => user_error("There is nothing in that direction".to_owned()), None => user_error("There is nothing in that direction".to_owned()),
Some(room2) => Ok(("room", room2.code)) Some(room2) => Ok(("room".to_owned(), room2.code.to_owned()))
} }
} }
} }
@ -169,16 +170,30 @@ impl UserVerb for Verb {
user_error("Can't find your current location".to_owned()) user_error("Can't find your current location".to_owned())
} }
} }
} else if rem_trim == "me" || rem_trim == "self" {
Ok((player_item.item_type.clone(), player_item.item_code.clone()))
} else { } else {
user_error("Sorry, I don't understand what you want to look at.".to_owned()) match &ctx.trans.resolve_items_by_display_name_for_player(
&player_item,
&rem_trim,
true, true, false, false
).await?[..] {
[] => user_error("Sorry, I couldn't find anything matching.".to_owned()),
[match_it] => Ok((match_it.item_type.clone(), match_it.item_code.clone())),
[item1, ..] if item1.display.to_lowercase() == rem_trim ||
item1.display_less_explicit.as_ref().map(|x|x.to_lowercase()) == Some(rem_trim) =>
Ok((item1.item_type.clone(), item1.item_code.clone())),
_ => user_error("Sorry, that name is ambiguous, please be more specific.".to_owned())
}
}?; }?;
let item = ctx.trans.find_item_by_type_code(itype, icode).await? let item = ctx.trans.find_item_by_type_code(&itype, &icode).await?
.ok_or_else(|| UserError("Sorry, that no longer exists".to_owned()))?; .ok_or_else(|| UserError("Sorry, that no longer exists".to_owned()))?;
if itype != "room" { if itype != "room" {
describe_normal_item(ctx, &item).await?; describe_normal_item(ctx, &item).await?;
} else { } else {
let room = let room =
room::room_map_by_code().get(icode).ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?; room::room_map_by_code().get(icode.as_str())
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
describe_room(ctx, &room, &list_item_contents(ctx, &item).await?).await?; describe_room(ctx, &room, &list_item_contents(ctx, &item).await?).await?;
} }
Ok(()) Ok(())

View File

@ -34,6 +34,7 @@ impl UserVerb for Verb {
item_type: "player".to_owned(), item_type: "player".to_owned(),
item_code: username.to_lowercase(), item_code: username.to_lowercase(),
display: username.to_owned(), display: username.to_owned(),
details: Some("A non-descript individual".to_owned()),
location: "room/repro_xv_chargen".to_owned(), location: "room/repro_xv_chargen".to_owned(),
..Item::default() ..Item::default()
}).await?; }).await?;

View File

@ -24,6 +24,8 @@ CREATE TABLE items (
CREATE UNIQUE INDEX item_index ON items ((details->>'item_type'), (details->>'item_code')); CREATE UNIQUE INDEX item_index ON items ((details->>'item_type'), (details->>'item_code'));
CREATE INDEX item_by_loc ON items ((details->>'location')); CREATE INDEX item_by_loc ON items ((details->>'location'));
CREATE INDEX item_by_static ON items ((cast(details->>'is_static' as boolean))); CREATE INDEX item_by_static ON items ((cast(details->>'is_static' as boolean)));
CREATE INDEX item_by_display ON items (lower(details->>'display'));
CREATE INDEX item_by_display_less_explicit ON items (lower(details->>'display_less_explicit'));
CREATE TABLE users ( CREATE TABLE users (
-- Username here is all lower case, but details has correct case version. -- Username here is all lower case, but details has correct case version.