From cfbe4098b76eeca08d54794343e1234076c9d053 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Wed, 27 Sep 2023 02:32:51 +0100 Subject: [PATCH] map memory --- src/main.rs | 96 +++++++++++++++------------------------- src/map/mod.rs | 11 +++++ src/visibility_system.rs | 39 ++++++++++++++++ 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/src/main.rs b/src/main.rs index cabed6c..b817e4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -154,7 +154,6 @@ const SHOW_BOUNDARIES: bool = false; // Config setting enum DrawType { None, Visible, - VisibleAndRemember, Telepathy, } @@ -170,12 +169,12 @@ struct DrawInfo { } fn draw_camera( + map: &Map, ecs: &World, draw: &mut Draw, atlas: &HashMap, font: ¬an::draw::Font ) { - let map = ecs.fetch::(); render_map_in_view(&*map, ecs, draw, atlas, false); { let bounds = crate::camera::get_screen_bounds(ecs, false); @@ -204,17 +203,8 @@ fn draw_camera( ) { let draw_type = if map.visible_tiles[idx] { - let is_prop = props.get(*e); - let is_item = items.get(*e); - if is_prop.is_some() || is_item.is_some() { - // If it's a static entity, we want to draw it, and - // also save it's location so that we remember where - // it was last seen after it leaves vision. - DrawType::VisibleAndRemember - } else { - // If it's anything else, just draw it. - DrawType::Visible - } + // If it's anything else, just draw it. + DrawType::Visible } else if map.telepath_tiles[idx] { let has_mind = minds.get(*e); if has_mind.is_some() { @@ -317,50 +307,6 @@ fn draw_camera( .v_align_middle(); } } - DrawType::VisibleAndRemember => { - // TODO: PUT THIS INTO A FUNCTION! - let renderable = renderables.get(entry.1.e).unwrap(); - if let Some(spriteinfo) = &renderable.sprite { - let id = if let Some(sprite) = atlas.get(&spriteinfo.id) { - sprite - } else { - panic!("No entity sprite found for ID: {}", spriteinfo.id); - }; - draw.image(id) - .position( - ((entry.0.x as f32) + spriteinfo.offset.0) * TILESIZE, - ((entry.0.y as f32) + spriteinfo.offset.1) * TILESIZE - ) - .color( - if spriteinfo.recolour { - Color::from_rgb( - renderable.fg.r, - renderable.fg.g, - renderable.fg.b - ) - } else { - Color::WHITE - } - ); - } else { - // Fallback to drawing text. - draw.text( - &font, - &format!("{}", bracket_lib::terminal::to_char(renderable.glyph as u8)) - ) - .position( - ((entry.0.x as f32) + 0.5) * TILESIZE, - ((entry.0.y as f32) + 0.5) * TILESIZE - ) - .color( - Color::from_rgb(renderable.fg.r, renderable.fg.g, renderable.fg.b) - ) - .size(FONTSIZE) - .h_align_center() - .v_align_middle(); - } - // TODO: Update map memory. - } _ => {} } } @@ -408,6 +354,34 @@ fn render_map_in_view( ) .color(tint); } + if !map.visible_tiles[idx] { + // Recall map memory. TODO: Improve this? Optimize? Do we need to remember more fields? + if let Some(memories) = map.memory.get(&idx) { + let mut sorted: Vec<_> = memories.iter().collect(); + sorted.sort_by(|a, b| a.render_order.cmp(&b.render_order)); + for memory in sorted.iter() { + let sprite = if let Some(sprite) = atlas.get(&memory.sprite) { + sprite + } else { + panic!("No sprite found for ID: {}", memory.sprite); + }; + draw.image(sprite) + .position( + (((x + bounds.x_offset) as f32) + memory.offset.0) * + TILESIZE, + (((y + bounds.y_offset) as f32) + memory.offset.1) * + TILESIZE + ) + .color( + if memory.recolour { + Color::from_rgb(memory.fg.r, memory.fg.g, memory.fg.b) + } else { + Color::from_rgb(0.75, 0.75, 0.75) + } + ); + } + } + } } } else if SHOW_BOUNDARIES { // TODO: Draw boundaries @@ -524,7 +498,8 @@ fn draw(_app: &mut App, gfx: &mut Graphics, gs: &mut State) { let mut draw = gfx.create_draw(); draw.clear(Color::BLACK); let mut log = false; - match *gs.ecs.fetch::() { + let runstate = *gs.ecs.fetch::(); + match runstate { | RunState::MainMenu { .. } | RunState::CharacterCreation { .. } | RunState::PreRun { .. } => {} @@ -541,13 +516,14 @@ fn draw(_app: &mut App, gfx: &mut Graphics, gs: &mut State) { } } _ => { + let map = gs.ecs.fetch::(); draw_bg(&gs.ecs, &mut draw, &gs.atlas); - draw_camera(&gs.ecs, &mut draw, &gs.atlas, &gs.font); + draw_camera(&*map, &gs.ecs, &mut draw, &gs.atlas, &gs.font); gui::draw_ui2(&gs.ecs, &mut draw, &gs.atlas, &gs.font); log = true; } } - match *gs.ecs.fetch::() { + match runstate { RunState::Farlook { x, y } => { gui::draw_farlook(x, y, &mut draw, &gs.atlas); //draw_tooltips(&gs.ecs, ctx, Some((x, y))); TODO: Put this in draw loop diff --git a/src/map/mod.rs b/src/map/mod.rs index a9582e8..f07aedd 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -26,6 +26,15 @@ use super::consts::visuals::{ // i.e. on a map size of 40*40, only entities to the left of the player are rendered. // on a map size of 42*42, the player can see entities up to 2 tiles to their right. +#[derive(Clone, Serialize, Deserialize)] +pub struct MapMemory { + pub sprite: String, + pub fg: RGB, + pub recolour: bool, + pub offset: (f32, f32), + pub render_order: i32, +} + #[derive(Default, Serialize, Deserialize, Clone)] pub struct Map { pub overmap: bool, @@ -33,6 +42,7 @@ pub struct Map { pub width: i32, pub height: i32, pub revealed_tiles: Vec, + pub memory: HashMap>, pub visible_tiles: Vec, pub lit_tiles: Vec, pub telepath_tiles: Vec, @@ -71,6 +81,7 @@ impl Map { width: width, height: height, revealed_tiles: vec![false; map_tile_count], + memory: HashMap::new(), visible_tiles: vec![false; map_tile_count], lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false. telepath_tiles: vec![false; map_tile_count], diff --git a/src/visibility_system.rs b/src/visibility_system.rs index 521eecd..3bd516e 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -10,6 +10,8 @@ use super::{ Telepath, Viewshed, Renderable, + Prop, + Item, gui::renderable_colour, tile_blocks_telepathy, }; @@ -35,6 +37,8 @@ impl<'a> System<'a> for VisibilitySystem { ReadStorage<'a, Blind>, ReadStorage<'a, BlocksVisibility>, ReadStorage<'a, Renderable>, + ReadStorage<'a, Prop>, + ReadStorage<'a, Item>, ); fn run(&mut self, data: Self::SystemData) { @@ -51,6 +55,8 @@ impl<'a> System<'a> for VisibilitySystem { blind_entities, blocks_visibility, renderables, + prop, + item, ) = data; map.view_blocked.clear(); @@ -59,6 +65,7 @@ impl<'a> System<'a> for VisibilitySystem { map.view_blocked.insert(idx); } + let mut player_was_dirty = false; for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { if viewshed.dirty { viewshed.dirty = false; @@ -81,6 +88,7 @@ impl<'a> System<'a> for VisibilitySystem { // If this is the player, reveal what they can see let _p: Option<&Player> = player.get(ent); if let Some(_p) = _p { + player_was_dirty = true; for t in map.visible_tiles.iter_mut() { *t = false; } @@ -139,6 +147,37 @@ impl<'a> System<'a> for VisibilitySystem { } } } + + if player_was_dirty { + // Refresh the memory of our visible tiles, by removing whatever + // was stored for every index we can currently see, and placing + // back in updated data. + let mut to_remove: Vec = Vec::new(); + for (i, &t) in map.visible_tiles.iter().enumerate() { + if t { + to_remove.push(i); + } + } + for idx in to_remove.iter() { + map.memory.remove(idx); + } + for (e, r, p, _h) in (&entities, &renderables, &pos, !&hidden).join() { + if prop.get(e).is_some() || item.get(e).is_some() { + let idx = map.xy_idx(p.x, p.y); + if map.visible_tiles[idx] { + if let Some(spriteinfo) = &r.sprite { + map.memory.entry(idx).or_insert(Vec::new()).push(crate::MapMemory { + sprite: spriteinfo.id.clone(), + fg: r.fg, + recolour: spriteinfo.recolour, + offset: spriteinfo.offset, + render_order: r.render_order, + }); + } + } + } + } + } } }