Add optional target argument to look command
look with no args still shows the room. With an argument, it inspects NPCs (showing stats/attitude), objects (description), exits (destination), players, or inventory items. Replaces the need to use examine for quick inspection while keeping full room view as the default. Made-with: Cursor
This commit is contained in:
136
src/commands.rs
136
src/commands.rs
@@ -89,7 +89,7 @@ pub async fn execute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let result = match cmd.as_str() {
|
let result = match cmd.as_str() {
|
||||||
"look" | "l" => cmd_look(player_id, state).await,
|
"look" | "l" => cmd_look(player_id, &args, state).await,
|
||||||
"go" => cmd_go(player_id, &args, state).await,
|
"go" => cmd_go(player_id, &args, state).await,
|
||||||
"north" | "south" | "east" | "west" | "up" | "down" | "n" | "s" | "e" | "w" | "u"
|
"north" | "south" | "east" | "west" | "up" | "down" | "n" | "s" | "e" | "w" | "u"
|
||||||
| "d" => cmd_go(player_id, resolve_dir(&cmd), state).await,
|
| "d" => cmd_go(player_id, resolve_dir(&cmd), state).await,
|
||||||
@@ -233,15 +233,139 @@ pub fn render_room_view(
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cmd_look(pid: usize, state: &SharedState) -> CommandResult {
|
async fn cmd_look(pid: usize, target: &str, state: &SharedState) -> CommandResult {
|
||||||
let st = state.lock().await;
|
let st = state.lock().await;
|
||||||
let rid = match st.players.get(&pid) {
|
let conn = match st.players.get(&pid) {
|
||||||
Some(c) => c.player.room_id.clone(),
|
Some(c) => c,
|
||||||
None => return simple("Error\r\n"),
|
None => return simple("Error\r\n"),
|
||||||
};
|
};
|
||||||
|
let rid = conn.player.room_id.clone();
|
||||||
|
let pname = conn.player.name.clone();
|
||||||
|
|
||||||
|
if !target.is_empty() {
|
||||||
|
let low = target.to_lowercase();
|
||||||
|
|
||||||
|
// Check NPCs in the room
|
||||||
|
if let Some(room) = st.world.get_room(&rid) {
|
||||||
|
for nid in &room.npcs {
|
||||||
|
if let Some(npc) = st.world.get_npc(nid) {
|
||||||
|
if npc.name.to_lowercase().contains(&low) {
|
||||||
|
let alive = st.npc_instances.get(nid).map(|i| i.alive).unwrap_or(true);
|
||||||
|
let att = st.npc_attitude_toward(nid, &pname);
|
||||||
|
let mut out = format!(
|
||||||
|
"\r\n{}\r\n {}\r\n",
|
||||||
|
ansi::bold(&npc.name),
|
||||||
|
npc.description
|
||||||
|
);
|
||||||
|
if !alive {
|
||||||
|
out.push_str(&format!(" {}\r\n", ansi::color(ansi::RED, "(dead)")));
|
||||||
|
} else if let Some(ref c) = npc.combat {
|
||||||
|
let hp = st.npc_instances.get(nid).map(|i| i.hp).unwrap_or(c.max_hp);
|
||||||
|
out.push_str(&format!(
|
||||||
|
" HP: {}/{} | ATK: {} | DEF: {}\r\n",
|
||||||
|
hp, c.max_hp, c.attack, c.defense
|
||||||
|
));
|
||||||
|
}
|
||||||
|
out.push_str(&format!(
|
||||||
|
" Attitude: {}\r\n",
|
||||||
|
ansi::color(attitude_color(att), att.label())
|
||||||
|
));
|
||||||
|
return CommandResult {
|
||||||
|
output: out,
|
||||||
|
broadcasts: Vec::new(),
|
||||||
|
kick_targets: Vec::new(),
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check objects in the room
|
||||||
|
for oid in &room.objects {
|
||||||
|
if let Some(obj) = st.world.get_object(oid) {
|
||||||
|
if obj.name.to_lowercase().contains(&low) {
|
||||||
|
return CommandResult {
|
||||||
|
output: format!(
|
||||||
|
"\r\n{}\r\n {}\r\n",
|
||||||
|
ansi::bold(&obj.name),
|
||||||
|
obj.description
|
||||||
|
),
|
||||||
|
broadcasts: Vec::new(),
|
||||||
|
kick_targets: Vec::new(),
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check exits
|
||||||
|
for (dir, dest_id) in &room.exits {
|
||||||
|
if dir.to_lowercase().contains(&low) {
|
||||||
|
let dest_name = st.world.get_room(dest_id)
|
||||||
|
.map(|r| r.name.as_str())
|
||||||
|
.unwrap_or("???");
|
||||||
|
return CommandResult {
|
||||||
|
output: format!(
|
||||||
|
"{} leads to {}.\r\n",
|
||||||
|
ansi::direction(dir),
|
||||||
|
ansi::room_name(dest_name),
|
||||||
|
),
|
||||||
|
broadcasts: Vec::new(),
|
||||||
|
kick_targets: Vec::new(),
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check inventory
|
||||||
|
for obj in &conn.player.inventory {
|
||||||
|
if obj.name.to_lowercase().contains(&low) {
|
||||||
|
return CommandResult {
|
||||||
|
output: format!(
|
||||||
|
"\r\n{}\r\n {}\r\n",
|
||||||
|
ansi::bold(&obj.name),
|
||||||
|
obj.description
|
||||||
|
),
|
||||||
|
broadcasts: Vec::new(),
|
||||||
|
kick_targets: Vec::new(),
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check other players in the room
|
||||||
|
for other in st.players_in_room(&rid, pid) {
|
||||||
|
if other.player.name.to_lowercase().contains(&low) {
|
||||||
|
let race_name = st.world.races.iter()
|
||||||
|
.find(|r| r.id == other.player.race_id)
|
||||||
|
.map(|r| r.name.as_str())
|
||||||
|
.unwrap_or("???");
|
||||||
|
let combat_str = if other.combat.is_some() { " [in combat]" } else { "" };
|
||||||
|
return CommandResult {
|
||||||
|
output: format!(
|
||||||
|
"\r\n{} — a {} {}{}\r\n",
|
||||||
|
ansi::player_name(&other.player.name),
|
||||||
|
race_name,
|
||||||
|
ansi::system_msg(&format!("(level {})", other.player.stats.level)),
|
||||||
|
ansi::color(ansi::RED, combat_str),
|
||||||
|
),
|
||||||
|
broadcasts: Vec::new(),
|
||||||
|
kick_targets: Vec::new(),
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return simple(&format!(
|
||||||
|
"{}\r\n",
|
||||||
|
ansi::error_msg(&format!("You don't see '{target}' here."))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No target — show the room
|
||||||
let mut out = render_room_view(&rid, pid, &st);
|
let mut out = render_room_view(&rid, pid, &st);
|
||||||
|
|
||||||
// Show combat status if in combat
|
|
||||||
if let Some(conn) = st.players.get(&pid) {
|
if let Some(conn) = st.players.get(&pid) {
|
||||||
if let Some(ref combat) = conn.combat {
|
if let Some(ref combat) = conn.combat {
|
||||||
if let Some(npc) = st.world.get_npc(&combat.npc_id) {
|
if let Some(npc) = st.world.get_npc(&combat.npc_id) {
|
||||||
@@ -1211,7 +1335,7 @@ async fn cmd_help(pid: usize, state: &SharedState) -> CommandResult {
|
|||||||
|
|
||||||
let mut out = format!("\r\n{}\r\n", ansi::bold("=== Commands ==="));
|
let mut out = format!("\r\n{}\r\n", ansi::bold("=== Commands ==="));
|
||||||
let cmds = [
|
let cmds = [
|
||||||
("look, l", "Look around the current room"),
|
("look [target], l", "Look at room, NPC, object, exit, or player"),
|
||||||
("go <dir>, n/s/e/w/u/d", "Move in a direction"),
|
("go <dir>, n/s/e/w/u/d", "Move in a direction"),
|
||||||
("say <msg>", "Say something to the room"),
|
("say <msg>", "Say something to the room"),
|
||||||
("who", "See who's online"),
|
("who", "See who's online"),
|
||||||
|
|||||||
Reference in New Issue
Block a user