forked from blasthavers/blastmud
Add gmap (giant map) command.
This commit is contained in:
parent
b6ed5ea487
commit
284c49b4a1
@ -139,7 +139,11 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
|||||||
"read" => look::VERB,
|
"read" => look::VERB,
|
||||||
|
|
||||||
"list" => list::VERB,
|
"list" => list::VERB,
|
||||||
|
|
||||||
|
"lm" => map::VERB,
|
||||||
"lmap" => map::VERB,
|
"lmap" => map::VERB,
|
||||||
|
"gm" => map::VERB,
|
||||||
|
"gmap" => map::VERB,
|
||||||
|
|
||||||
"p" => page::VERB,
|
"p" => page::VERB,
|
||||||
"page" => page::VERB,
|
"page" => page::VERB,
|
||||||
|
@ -8,95 +8,19 @@ use crate::{
|
|||||||
Item, LocationActionType, Subattack, ItemFlag, ItemSpecialData
|
Item, LocationActionType, Subattack, ItemFlag, ItemSpecialData
|
||||||
}},
|
}},
|
||||||
static_content::{
|
static_content::{
|
||||||
room::{self, Direction, GridCoords},
|
room::{self, Direction},
|
||||||
dynzone::self,
|
dynzone::self,
|
||||||
possession_type::possession_data,
|
possession_type::possession_data,
|
||||||
},
|
},
|
||||||
language,
|
language,
|
||||||
services::combat::max_health,
|
services::combat::max_health,
|
||||||
};
|
};
|
||||||
|
use super::map::{render_map, render_map_dyn};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use mockall_double::double;
|
use mockall_double::double;
|
||||||
#[double] use crate::db::DBTrans;
|
#[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!("<bgblue><red>()<reset>"))
|
|
||||||
} 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<GridCoords> = 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!("<bgblue><red>()<reset>"))
|
|
||||||
} 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<()> {
|
pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> {
|
||||||
let mut contents_desc = String::new();
|
let mut contents_desc = String::new();
|
||||||
|
@ -11,6 +11,84 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use std::sync::Arc;
|
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!("<bgblue><red>()<reset>"))
|
||||||
|
} 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<GridCoords> = 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!("<bgblue><red>()<reset>"))
|
||||||
|
} 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,
|
pub fn render_lmap(room: &room::Room, width: usize, height: usize,
|
||||||
captions_needed: &mut Vec<(usize, &'static str, &'static str)>) -> String {
|
captions_needed: &mut Vec<(usize, &'static str, &'static str)>) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
@ -234,67 +312,121 @@ pub fn caption_lmap<'l>(captions: &Vec<(usize, &'l str, &'l str)>, width: usize,
|
|||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn lmap_room(ctx: &VerbContext<'_>,
|
#[async_trait]
|
||||||
room: &room::Room) -> UResult<()> {
|
trait MapType {
|
||||||
let mut captions: Vec<(usize, &'static str, &'static str)> = Vec::new();
|
async fn map_room(&self, ctx: &VerbContext<'_>,
|
||||||
ctx.trans.queue_for_session(
|
room: &room::Room) -> UResult<()>;
|
||||||
ctx.session,
|
async fn map_room_dyn<'a>(
|
||||||
Some(&flow_around(&render_lmap(room, 9, 7, &mut captions), 45, ansi!("<reset> "),
|
&self,
|
||||||
&caption_lmap(&captions, 14, 27), 31
|
ctx: &VerbContext<'_>,
|
||||||
))
|
zone: &'a dynzone::Dynzone,
|
||||||
).await?;
|
room: &'a dynzone::Dynroom,
|
||||||
Ok(())
|
zoneref: &str
|
||||||
|
) -> UResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn lmap_room_dyn<'a>(
|
pub struct LmapType;
|
||||||
ctx: &VerbContext<'_>,
|
|
||||||
zone: &'a dynzone::Dynzone,
|
#[async_trait]
|
||||||
room: &'a dynzone::Dynroom,
|
impl MapType for LmapType {
|
||||||
zoneref: &str
|
async fn map_room(&self, ctx: &VerbContext<'_>,
|
||||||
) -> UResult<()> {
|
room: &room::Room) -> UResult<()> {
|
||||||
let mut captions: Vec<(usize, &str, &str)> = Vec::new();
|
let mut captions: Vec<(usize, &'static str, &'static str)> = Vec::new();
|
||||||
let connectwhere_name_opt: Option<String> = match zoneref.split_once("/") {
|
ctx.trans.queue_for_session(
|
||||||
None => None,
|
ctx.session,
|
||||||
Some((zone_t, zone_c)) => {
|
Some(&flow_around(&render_lmap(room, 9, 7, &mut captions), 45, ansi!("<reset> "),
|
||||||
let zone_item: Option<Arc<Item>> = ctx.trans.find_item_by_type_code(zone_t, zone_c).await?;
|
&caption_lmap(&captions, 14, 27), 31
|
||||||
match zone_item.as_ref().map(|v| v.as_ref()) {
|
))
|
||||||
Some(Item { special_data: Some(ItemSpecialData::DynzoneData { zone_exit: Some(zone_exit), ..}),
|
).await?;
|
||||||
..}) =>
|
Ok(())
|
||||||
match zone_exit.split_once("/") {
|
}
|
||||||
None => None,
|
|
||||||
Some((ex_t, ex_c)) =>
|
async fn map_room_dyn<'a>(
|
||||||
match ctx.trans.find_item_by_type_code(ex_t, ex_c).await?.as_ref() {
|
&self,
|
||||||
Some(dest_item) => Some(
|
ctx: &VerbContext<'_>,
|
||||||
dest_item.display_for_sentence(
|
zone: &'a dynzone::Dynzone,
|
||||||
!ctx.session_dat.less_explicit_mode, 1, true
|
room: &'a dynzone::Dynroom,
|
||||||
)),
|
zoneref: &str
|
||||||
None => None
|
) -> UResult<()> {
|
||||||
}
|
let mut captions: Vec<(usize, &str, &str)> = Vec::new();
|
||||||
},
|
let connectwhere_name_opt: Option<String> = match zoneref.split_once("/") {
|
||||||
_ => None,
|
None => None,
|
||||||
|
Some((zone_t, zone_c)) => {
|
||||||
|
let zone_item: Option<Arc<Item>> = 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 =
|
||||||
let lmap_str =
|
render_lmap_dynroom(zone, room, 9, 7, &mut captions,
|
||||||
render_lmap_dynroom(zone, room, 9, 7, &mut captions,
|
connectwhere_name_opt.as_ref().map(|v| v.as_str()));
|
||||||
connectwhere_name_opt.as_ref().map(|v| v.as_str()));
|
ctx.trans.queue_for_session(
|
||||||
ctx.trans.queue_for_session(
|
ctx.session,
|
||||||
ctx.session,
|
Some(&flow_around(&lmap_str,
|
||||||
Some(&flow_around(&lmap_str,
|
45, ansi!("<reset> "),
|
||||||
45, ansi!("<reset> "),
|
&caption_lmap(&captions, 14, 27), 31
|
||||||
&caption_lmap(&captions, 14, 27), 31
|
))
|
||||||
))
|
).await?;
|
||||||
).await?;
|
Ok(())
|
||||||
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;
|
pub struct Verb;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UserVerb for Verb {
|
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() != "" {
|
if remaining.trim() != "" {
|
||||||
user_error("map commands don't take anything after them".to_owned())?;
|
user_error("map commands don't take anything after them".to_owned())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let map_type: Box<dyn MapType + Sync + Send> = 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 player_item = get_player_item_or_fail(ctx).await?;
|
||||||
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 room_item: Arc<Item> = ctx.trans.find_item_by_type_code(heretype, herecode).await?
|
let room_item: Arc<Item> = ctx.trans.find_item_by_type_code(heretype, herecode).await?
|
||||||
@ -303,7 +435,7 @@ impl UserVerb for Verb {
|
|||||||
let room =
|
let room =
|
||||||
room::room_map_by_code().get(room_item.item_code.as_str())
|
room::room_map_by_code().get(room_item.item_code.as_str())
|
||||||
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
|
.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" {
|
} else if room_item.item_type == "dynroom" {
|
||||||
let (dynzone, dynroom) = match &room_item.special_data {
|
let (dynzone, dynroom) = match &room_item.special_data {
|
||||||
Some(ItemSpecialData::DynroomData { dynzone_code, dynroom_code }) => {
|
Some(ItemSpecialData::DynroomData { dynzone_code, dynroom_code }) => {
|
||||||
@ -315,7 +447,7 @@ impl UserVerb for Verb {
|
|||||||
},
|
},
|
||||||
_ => user_error("Expected dynroom to have DynroomData".to_owned())?
|
_ => 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 {
|
} else {
|
||||||
user_error("Can't map here".to_owned())?;
|
user_error("Can't map here".to_owned())?;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user