diff --git a/blastmud_game/src/message_handler/user_commands.rs b/blastmud_game/src/message_handler/user_commands.rs index c526832a..5aac543b 100644 --- a/blastmud_game/src/message_handler/user_commands.rs +++ b/blastmud_game/src/message_handler/user_commands.rs @@ -139,7 +139,11 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! { "read" => look::VERB, "list" => list::VERB, + + "lm" => map::VERB, "lmap" => map::VERB, + "gm" => map::VERB, + "gmap" => map::VERB, "p" => page::VERB, "page" => page::VERB, diff --git a/blastmud_game/src/message_handler/user_commands/look.rs b/blastmud_game/src/message_handler/user_commands/look.rs index cdf865c7..1335a3ee 100644 --- a/blastmud_game/src/message_handler/user_commands/look.rs +++ b/blastmud_game/src/message_handler/user_commands/look.rs @@ -8,95 +8,19 @@ use crate::{ Item, LocationActionType, Subattack, ItemFlag, ItemSpecialData }}, static_content::{ - room::{self, Direction, GridCoords}, + room::{self, Direction}, dynzone::self, possession_type::possession_data, }, language, services::combat::max_health, }; +use super::map::{render_map, render_map_dyn}; use itertools::Itertools; use std::sync::Arc; use mockall_double::double; #[double] use crate::db::DBTrans; -pub fn render_map(room: &room::Room, width: usize, height: usize) -> String { - let mut buf = String::new(); - let my_loc = &room.grid_coords; - let min_x = my_loc.x - (width as i64) / 2; - let max_x = min_x + (width as i64); - let min_y = my_loc.y - (height as i64) / 2; - let max_y = min_y + (height as i64); - for y in min_y..max_y { - for x in min_x..max_x { - if my_loc.x == x && my_loc.y == y { - buf.push_str(ansi!("()")) - } else { - buf.push_str(room::room_map_by_zloc() - .get(&(&room.zone, &room::GridCoords { x, y, z: my_loc.z })) - .map(|r| if room.zone == r.zone { - r.short - } else { - r.secondary_zones.iter() - .find(|sz| sz.zone == room.zone) - .map(|sz| sz.short) - .expect("Secondary zone missing") - }) - .unwrap_or(" ")); - } - } - buf.push('\n'); - } - buf -} - -pub fn render_map_dyn(dynzone: &dynzone::Dynzone, - dynroom: &dynzone::Dynroom, - width: usize, height: usize) -> String { - let mut buf = String::new(); - let my_loc = &dynroom.grid_coords; - let min_x = my_loc.x - (width as i64) / 2; - let max_x = min_x + (width as i64); - let min_y = my_loc.y - (height as i64) / 2; - let max_y = min_y + (height as i64); - - let main_exit: Option = dynzone.dyn_rooms - .iter() - .flat_map(|(_, dr)| - dr.exits.iter() - .filter(|ex| match ex.target { - dynzone::ExitTarget::ExitZone => true, - _ => false - }) - .map(|ex| dr.grid_coords.apply(&ex.direction)) - ).next(); - - for y in min_y..max_y { - for x in min_x..max_x { - if my_loc.x == x && my_loc.y == y { - buf.push_str(ansi!("()")) - } else { - buf.push_str(dynzone.dyn_rooms.iter() - .find( - |(_, dr)| dr.grid_coords.x == x && - dr.grid_coords.y == y && - dr.grid_coords.z == my_loc.z) - .map(|(_, r)| r.short) - .or_else(|| main_exit.as_ref().and_then( - |ex_pos| - if ex_pos.x == x && ex_pos.y == y && - ex_pos.z == my_loc.z { - Some("<<") - } else { - None - })) - .unwrap_or(" ")); - } - } - buf.push('\n'); - } - buf -} pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> { let mut contents_desc = String::new(); diff --git a/blastmud_game/src/message_handler/user_commands/map.rs b/blastmud_game/src/message_handler/user_commands/map.rs index a4abc31e..1b029122 100644 --- a/blastmud_game/src/message_handler/user_commands/map.rs +++ b/blastmud_game/src/message_handler/user_commands/map.rs @@ -11,6 +11,84 @@ use crate::{ }; use std::sync::Arc; +pub fn render_map(room: &room::Room, width: usize, height: usize) -> String { + let mut buf = String::new(); + let my_loc = &room.grid_coords; + let min_x = my_loc.x - (width as i64) / 2; + let max_x = min_x + (width as i64); + let min_y = my_loc.y - (height as i64) / 2; + let max_y = min_y + (height as i64); + for y in min_y..max_y { + for x in min_x..max_x { + if my_loc.x == x && my_loc.y == y { + buf.push_str(ansi!("()")) + } else { + buf.push_str(room::room_map_by_zloc() + .get(&(&room.zone, &room::GridCoords { x, y, z: my_loc.z })) + .map(|r| if room.zone == r.zone { + r.short + } else { + r.secondary_zones.iter() + .find(|sz| sz.zone == room.zone) + .map(|sz| sz.short) + .expect("Secondary zone missing") + }) + .unwrap_or(" ")); + } + } + buf.push('\n'); + } + buf +} + +pub fn render_map_dyn(dynzone: &dynzone::Dynzone, + dynroom: &dynzone::Dynroom, + width: usize, height: usize) -> String { + let mut buf = String::new(); + let my_loc = &dynroom.grid_coords; + let min_x = my_loc.x - (width as i64) / 2; + let max_x = min_x + (width as i64); + let min_y = my_loc.y - (height as i64) / 2; + let max_y = min_y + (height as i64); + + let main_exit: Option = dynzone.dyn_rooms + .iter() + .flat_map(|(_, dr)| + dr.exits.iter() + .filter(|ex| match ex.target { + dynzone::ExitTarget::ExitZone => true, + _ => false + }) + .map(|ex| dr.grid_coords.apply(&ex.direction)) + ).next(); + + for y in min_y..max_y { + for x in min_x..max_x { + if my_loc.x == x && my_loc.y == y { + buf.push_str(ansi!("()")) + } else { + buf.push_str(dynzone.dyn_rooms.iter() + .find( + |(_, dr)| dr.grid_coords.x == x && + dr.grid_coords.y == y && + dr.grid_coords.z == my_loc.z) + .map(|(_, r)| r.short) + .or_else(|| main_exit.as_ref().and_then( + |ex_pos| + if ex_pos.x == x && ex_pos.y == y && + ex_pos.z == my_loc.z { + Some("<<") + } else { + None + })) + .unwrap_or(" ")); + } + } + buf.push('\n'); + } + buf +} + pub fn render_lmap(room: &room::Room, width: usize, height: usize, captions_needed: &mut Vec<(usize, &'static str, &'static str)>) -> String { let mut buf = String::new(); @@ -234,67 +312,121 @@ pub fn caption_lmap<'l>(captions: &Vec<(usize, &'l str, &'l str)>, width: usize, buf } -pub async fn lmap_room(ctx: &VerbContext<'_>, - room: &room::Room) -> UResult<()> { - let mut captions: Vec<(usize, &'static str, &'static str)> = Vec::new(); - ctx.trans.queue_for_session( - ctx.session, - Some(&flow_around(&render_lmap(room, 9, 7, &mut captions), 45, ansi!(" "), - &caption_lmap(&captions, 14, 27), 31 - )) - ).await?; - Ok(()) +#[async_trait] +trait MapType { + async fn map_room(&self, ctx: &VerbContext<'_>, + room: &room::Room) -> UResult<()>; + async fn map_room_dyn<'a>( + &self, + ctx: &VerbContext<'_>, + zone: &'a dynzone::Dynzone, + room: &'a dynzone::Dynroom, + zoneref: &str + ) -> UResult<()>; } -pub async fn lmap_room_dyn<'a>( - ctx: &VerbContext<'_>, - zone: &'a dynzone::Dynzone, - room: &'a dynzone::Dynroom, - zoneref: &str -) -> UResult<()> { - let mut captions: Vec<(usize, &str, &str)> = Vec::new(); - let connectwhere_name_opt: Option = match zoneref.split_once("/") { - None => None, - Some((zone_t, zone_c)) => { - let zone_item: Option> = ctx.trans.find_item_by_type_code(zone_t, zone_c).await?; - match zone_item.as_ref().map(|v| v.as_ref()) { - Some(Item { special_data: Some(ItemSpecialData::DynzoneData { zone_exit: Some(zone_exit), ..}), - ..}) => - match zone_exit.split_once("/") { - None => None, - Some((ex_t, ex_c)) => - match ctx.trans.find_item_by_type_code(ex_t, ex_c).await?.as_ref() { - Some(dest_item) => Some( - dest_item.display_for_sentence( - !ctx.session_dat.less_explicit_mode, 1, true - )), - None => None - } - }, - _ => None, +pub struct LmapType; + +#[async_trait] +impl MapType for LmapType { + async fn map_room(&self, ctx: &VerbContext<'_>, + room: &room::Room) -> UResult<()> { + let mut captions: Vec<(usize, &'static str, &'static str)> = Vec::new(); + ctx.trans.queue_for_session( + ctx.session, + Some(&flow_around(&render_lmap(room, 9, 7, &mut captions), 45, ansi!(" "), + &caption_lmap(&captions, 14, 27), 31 + )) + ).await?; + Ok(()) + } + + async fn map_room_dyn<'a>( + &self, + ctx: &VerbContext<'_>, + zone: &'a dynzone::Dynzone, + room: &'a dynzone::Dynroom, + zoneref: &str + ) -> UResult<()> { + let mut captions: Vec<(usize, &str, &str)> = Vec::new(); + let connectwhere_name_opt: Option = match zoneref.split_once("/") { + None => None, + Some((zone_t, zone_c)) => { + let zone_item: Option> = ctx.trans.find_item_by_type_code(zone_t, zone_c).await?; + match zone_item.as_ref().map(|v| v.as_ref()) { + Some(Item { special_data: Some(ItemSpecialData::DynzoneData { zone_exit: Some(zone_exit), ..}), + ..}) => + match zone_exit.split_once("/") { + None => None, + Some((ex_t, ex_c)) => + match ctx.trans.find_item_by_type_code(ex_t, ex_c).await?.as_ref() { + Some(dest_item) => Some( + dest_item.display_for_sentence( + !ctx.session_dat.less_explicit_mode, 1, true + )), + None => None + } + }, + _ => None, + } } - } - }; - let lmap_str = - render_lmap_dynroom(zone, room, 9, 7, &mut captions, - connectwhere_name_opt.as_ref().map(|v| v.as_str())); - ctx.trans.queue_for_session( - ctx.session, - Some(&flow_around(&lmap_str, - 45, ansi!(" "), - &caption_lmap(&captions, 14, 27), 31 - )) - ).await?; - Ok(()) + }; + let lmap_str = + render_lmap_dynroom(zone, room, 9, 7, &mut captions, + connectwhere_name_opt.as_ref().map(|v| v.as_str())); + ctx.trans.queue_for_session( + ctx.session, + Some(&flow_around(&lmap_str, + 45, ansi!(" "), + &caption_lmap(&captions, 14, 27), 31 + )) + ).await?; + Ok(()) + } +} + +pub struct GmapType; + +#[async_trait] +impl MapType for GmapType { + async fn map_room(&self, ctx: &VerbContext<'_>, + room: &room::Room) -> UResult<()> { + ctx.trans.queue_for_session( + ctx.session, + Some(&render_map(room, 16, 9)) + ).await?; + Ok(()) + } + + async fn map_room_dyn<'a>( + &self, + ctx: &VerbContext<'_>, + zone: &'a dynzone::Dynzone, + room: &'a dynzone::Dynroom, + _zoneref: &str + ) -> UResult<()> { + ctx.trans.queue_for_session( + ctx.session, + Some(&render_map_dyn(zone, room, 16, 9)) + ).await?; + Ok(()) + } } 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, remaining: &str) -> UResult<()> { if remaining.trim() != "" { user_error("map commands don't take anything after them".to_owned())?; } + + let map_type: Box = match verb { + "lmap" | "lm" => Box::new(LmapType), + "gmap" | "gm" => Box::new(GmapType), + _ => user_error("I don't know how to show that map type.".to_owned())? + }; + let player_item = get_player_item_or_fail(ctx).await?; let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen")); let room_item: Arc = ctx.trans.find_item_by_type_code(heretype, herecode).await? @@ -303,7 +435,7 @@ impl UserVerb for Verb { let room = room::room_map_by_code().get(room_item.item_code.as_str()) .ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?; - lmap_room(ctx, &room).await?; + map_type.map_room(ctx, &room).await?; } else if room_item.item_type == "dynroom" { let (dynzone, dynroom) = match &room_item.special_data { Some(ItemSpecialData::DynroomData { dynzone_code, dynroom_code }) => { @@ -315,7 +447,7 @@ impl UserVerb for Verb { }, _ => user_error("Expected dynroom to have DynroomData".to_owned())? }; - lmap_room_dyn(ctx, &dynzone, &dynroom, &room_item.location).await?; + map_type.map_room_dyn(ctx, &dynzone, &dynroom, &room_item.location).await?; } else { user_error("Can't map here".to_owned())?; }