diff --git a/src/commands.rs b/src/commands.rs index 8a38470..9f2481a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -89,7 +89,7 @@ pub async fn execute( } 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, "north" | "south" | "east" | "west" | "up" | "down" | "n" | "s" | "e" | "w" | "u" | "d" => cmd_go(player_id, resolve_dir(&cmd), state).await, @@ -233,15 +233,139 @@ pub fn render_room_view( 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 rid = match st.players.get(&pid) { - Some(c) => c.player.room_id.clone(), + let conn = match st.players.get(&pid) { + Some(c) => c, 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); - // Show combat status if in combat if let Some(conn) = st.players.get(&pid) { if let Some(ref combat) = conn.combat { 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 cmds = [ - ("look, l", "Look around the current room"), + ("look [target], l", "Look at room, NPC, object, exit, or player"), ("go