diff --git a/src/components.rs b/src/components.rs index d4f9eee..49800dc 100644 --- a/src/components.rs +++ b/src/components.rs @@ -37,6 +37,9 @@ pub struct Player {} #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Monster {} +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Mind {} + #[derive(Component, ConvertSaveload, Clone)] pub struct Viewshed { pub visible_tiles: Vec, @@ -44,6 +47,13 @@ pub struct Viewshed { pub dirty: bool, } +#[derive(Component, ConvertSaveload, Clone)] +pub struct Telepath { + pub telepath_tiles: Vec, + pub range: i32, + pub dirty: bool, +} + #[derive(Component, Debug, ConvertSaveload, Clone)] pub struct Name { pub name: String, diff --git a/src/gamelog/logstore.rs b/src/gamelog/logstore.rs index dc05a02..32e63c2 100644 --- a/src/gamelog/logstore.rs +++ b/src/gamelog/logstore.rs @@ -1,4 +1,4 @@ -use super::LogFragment; +use super::{events, LogFragment, Logger}; use rltk::prelude::*; use std::sync::Mutex; @@ -37,6 +37,21 @@ pub fn print_log(console: &mut Box, pos: Point, descending: bool, l }); } +pub fn setup_log() { + clear_log(); + events::clear_events(); + for _ in 0..5 { + Logger::new().log(); + } + Logger::new() + .append("Welcome!") + .colour(rltk::CYAN) + .append("(") + .append("pretend i wrote a paragraph explaining why you're here") + .append(")") + .log(); +} + pub fn clone_log() -> Vec> { return LOG.lock().unwrap().clone(); } diff --git a/src/gamelog/mod.rs b/src/gamelog/mod.rs index 4ba9ad8..e83d6c3 100644 --- a/src/gamelog/mod.rs +++ b/src/gamelog/mod.rs @@ -4,7 +4,7 @@ mod builder; pub use builder::*; mod logstore; use logstore::*; -pub use logstore::{clear_log, clone_log, print_log, restore_log}; +pub use logstore::{clear_log, clone_log, print_log, restore_log, setup_log}; mod events; pub use events::*; diff --git a/src/gui.rs b/src/gui.rs index 4758a54..539d731 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -6,29 +6,29 @@ use rltk::{Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { - ctx.draw_hollow_box_double(0, 45, 79, 14, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.draw_hollow_box_double(0, 43, 79, 7, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Render stats let combat_stats = ecs.read_storage::(); let players = ecs.read_storage::(); for (_player, stats) in (&players, &combat_stats).join() { let health = format!(" HP {}/{} ", stats.hp, stats.max_hp); - ctx.print_color_right(36, 45, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health); - ctx.draw_bar_horizontal(38, 45, 34, stats.hp, stats.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); + ctx.print_color_right(36, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health); + ctx.draw_bar_horizontal(38, 43, 34, stats.hp, stats.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); } // Render the message log at [1, 46], descending, with 6 lines. - gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console, Point::new(1, 46), true, 13); + gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console, Point::new(1, 44), true, 6); // Render depth let map = ecs.fetch::(); let depth = format!(" D{} ", map.depth); - ctx.print_color_right(78, 45, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &depth); + ctx.print_color_right(78, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &depth); // Render turn ctx.print_color_right( 78, - 59, + 50, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &format!(" T{} ", crate::gamelog::get_event_count("Turn")), diff --git a/src/main.rs b/src/main.rs index c10fc7f..adefbbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,6 +220,8 @@ impl State { if let Some(vs) = vs { vs.dirty = true; } + + gamelog::setup_log(); } } @@ -242,21 +244,28 @@ impl GameState for State { { let positions = self.ecs.read_storage::(); let renderables = self.ecs.read_storage::(); + let minds = self.ecs.read_storage::(); let map = self.ecs.fetch::(); + let entities = self.ecs.entities(); - let mut data = (&positions, &renderables).join().collect::>(); + let mut data = (&positions, &renderables, &entities).join().collect::>(); data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order)); - for (pos, render) in data.iter() { + for (pos, render, ent) in data.iter() { let idx = map.xy_idx(pos.x, pos.y); let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]); let mut bg = render.bg.add(RGB::from_u8(26, 45, 45)).add(offsets); - //bg = bg.add(offsets); if map.bloodstains.contains(&idx) { bg = bg.add(RGB::from_f32(0.6, 0., 0.)); } if map.visible_tiles[idx] { ctx.set(pos.x, pos.y, render.fg, bg, render.glyph); } + if map.telepath_tiles[idx] { + let has_mind = minds.get(*ent); + if let Some(_) = has_mind { + ctx.set(pos.x, pos.y, render.fg, RGB::named(rltk::BLACK), render.glyph); + } + } } gui::draw_ui(&self.ecs, ctx); } @@ -450,17 +459,14 @@ impl GameState for State { } const DISPLAYWIDTH: i32 = 80; -const DISPLAYHEIGHT: i32 = 60; +const DISPLAYHEIGHT: i32 = 51; fn main() -> rltk::BError { use rltk::RltkBuilder; - let mut context = RltkBuilder::new() + let mut context = RltkBuilder::simple(DISPLAYWIDTH, DISPLAYHEIGHT) + .unwrap() .with_title("rust-rl") - .with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT) .with_tile_dimensions(16, 16) - .with_resource_path("resources/") - .with_font("terminal8x8.jpg", 8, 8) - .with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg") //.with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg") .build()?; context.with_post_scanlines(false); @@ -472,7 +478,9 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); @@ -521,18 +529,8 @@ fn main() -> rltk::BError { gs.ecs.insert(Point::new(player_x, player_y)); gs.ecs.insert(player_entity); - gamelog::clear_log(); - gamelog::clear_events(); - for _ in 0..5 { - gamelog::Logger::new().log(); - } - gamelog::Logger::new() - .append("Welcome!") - .colour(rltk::CYAN) - .append("(") - .append("pretend i wrote a paragraph explaining why you're here") - .append(")") - .log(); + gamelog::setup_log(); + gs.ecs.insert(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }); gs.ecs.insert(particle_system::ParticleBuilder::new()); gs.ecs.insert(rex_assets::RexAssets::new()); diff --git a/src/map.rs b/src/map.rs index 6bedfa3..8895404 100644 --- a/src/map.rs +++ b/src/map.rs @@ -26,6 +26,7 @@ pub struct Map { pub height: i32, pub revealed_tiles: Vec, pub visible_tiles: Vec, + pub telepath_tiles: Vec, pub red_offset: Vec, pub green_offset: Vec, pub blue_offset: Vec, @@ -100,6 +101,7 @@ impl Map { height: MAPHEIGHT as i32, revealed_tiles: vec![false; MAPCOUNT], visible_tiles: vec![false; MAPCOUNT], + telepath_tiles: vec![false; MAPCOUNT], red_offset: vec![0; MAPCOUNT], green_offset: vec![0; MAPCOUNT], blue_offset: vec![0; MAPCOUNT], diff --git a/src/player.rs b/src/player.rs index 69e7cea..e3c768d 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,5 +1,5 @@ use super::{ - gamelog, CombatStats, Item, Map, Monster, Name, Player, Position, RunState, State, TileType, Viewshed, + gamelog, CombatStats, Item, Map, Monster, Name, Player, Position, RunState, State, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH, }; use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; @@ -10,6 +10,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) { let mut positions = ecs.write_storage::(); let mut players = ecs.write_storage::(); let mut viewsheds = ecs.write_storage::(); + let mut telepaths = ecs.write_storage::(); let combat_stats = ecs.read_storage::(); let map = ecs.fetch::(); @@ -62,7 +63,14 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) { } pos.x = min((MAPWIDTH as i32) - 1, max(0, pos.x + delta_x)); pos.y = min((MAPHEIGHT as i32) - 1, max(0, pos.y + delta_y)); + + // Dirty viewsheds, and check only now if telepath viewshed exists viewshed.dirty = true; + + let is_telepath = telepaths.get_mut(entity); + if let Some(telepathy) = is_telepath { + telepathy.dirty = true; + } let mut ppos = ecs.write_resource::(); ppos.x = pos.x; ppos.y = pos.y; diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 0058e9b..4c2820c 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -51,7 +51,9 @@ pub fn save_game(ecs: &mut World) { Renderable, Player, Viewshed, + Telepath, Monster, + Mind, Name, BlocksTile, CombatStats, @@ -133,7 +135,9 @@ pub fn load_game(ecs: &mut World) { Renderable, Player, Viewshed, + Telepath, Monster, + Mind, Name, BlocksTile, CombatStats, diff --git a/src/spawner.rs b/src/spawner.rs index e9d41db..47884f0 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,7 +1,7 @@ use super::{ random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, - EquipmentSlot, Equippable, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Monster, Name, Player, Position, - ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Viewshed, AOE, MAPWIDTH, + EquipmentSlot, Equippable, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Mind, Monster, Name, Player, + Position, ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Telepath, Viewshed, AOE, MAPWIDTH, }; use rltk::{console, RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -27,7 +27,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32, player_name: String .build() } -fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharType, name: S, hit_die: i32) { +fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharType, name: S, hit_die: i32, power: i32) { let rolled_hp = roll_hit_dice(ecs, 1, hit_die); ecs.create_entity() @@ -35,23 +35,24 @@ fn monster(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharTy .with(Renderable { glyph: glyph, fg: RGB::named(rltk::GREEN), bg: RGB::named(rltk::BLACK), render_order: 1 }) .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Monster {}) + .with(Mind {}) .with(Name { name: name.to_string() }) .with(BlocksTile {}) - .with(CombatStats { max_hp: rolled_hp, hp: rolled_hp, defence: 0, power: 2 }) + .with(CombatStats { max_hp: rolled_hp, hp: rolled_hp, defence: 0, power: power }) .marked::>() .build(); } fn orc(ecs: &mut World, x: i32, y: i32) { - monster(ecs, x, y, rltk::to_cp437('o'), "orc", 8); + monster(ecs, x, y, rltk::to_cp437('o'), "orc", 8, 3); } fn goblin(ecs: &mut World, x: i32, y: i32) { - monster(ecs, x, y, rltk::to_cp437('g'), "goblin", 6); + monster(ecs, x, y, rltk::to_cp437('g'), "goblin", 6, 2); } fn goblin_chieftain(ecs: &mut World, x: i32, y: i32) { - monster(ecs, x, y, rltk::to_cp437('G'), "goblin chieftain", 8); + monster(ecs, x, y, rltk::to_cp437('G'), "goblin chieftain", 8, 3); } pub fn roll_hit_dice(ecs: &mut World, n: i32, d: i32) -> i32 { diff --git a/src/visibility_system.rs b/src/visibility_system.rs index a851c18..dbcc030 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -1,4 +1,4 @@ -use super::{Map, Player, Position, Viewshed}; +use super::{Map, Player, Position, Telepath, Viewshed}; use rltk::{FieldOfViewAlg::SymmetricShadowcasting, Point}; use specs::prelude::*; @@ -9,12 +9,13 @@ impl<'a> System<'a> for VisibilitySystem { WriteExpect<'a, Map>, Entities<'a>, WriteStorage<'a, Viewshed>, + WriteStorage<'a, Telepath>, WriteStorage<'a, Position>, ReadStorage<'a, Player>, ); fn run(&mut self, data: Self::SystemData) { - let (mut map, entities, mut viewshed, pos, player) = data; + let (mut map, entities, mut viewshed, mut telepath, pos, player) = data; for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { if viewshed.dirty { @@ -40,5 +41,47 @@ impl<'a> System<'a> for VisibilitySystem { } } } + + for (ent, telepath, pos) in (&entities, &mut telepath, &pos).join() { + if telepath.dirty { + telepath.dirty = false; + + telepath.telepath_tiles = fast_fov(pos.x, pos.y, telepath.range); + telepath.telepath_tiles.retain(|p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height); + + // If this is the player, reveal what they can see + let _p: Option<&Player> = player.get(ent); + if let Some(_p) = _p { + for t in map.telepath_tiles.iter_mut() { + *t = false; + } + for vis in telepath.telepath_tiles.iter() { + let idx = map.xy_idx(vis.x, vis.y); + map.telepath_tiles[idx] = true; + } + } + } + } } } + +pub fn fast_fov(p_x: i32, p_y: i32, r: i32) -> Vec { + let mut visible_tiles: Vec = Vec::new(); + + let mut i = 0; + while i <= 360 { + let x: f32 = f32::cos(i as f32 * 0.01745 as f32); + let y: f32 = f32::sin(i as f32 * 0.01745 as f32); + + let mut ox: f32 = p_x as f32 + 0.5 as f32; + let mut oy: f32 = p_y as f32 + 0.5 as f32; + for _i in 0..r { + visible_tiles.push(Point::new(ox as i32, oy as i32)); + ox += x; + oy += y; + } + i += 4; + } + + visible_tiles +} diff --git a/wasm/rust-llyrl.js b/wasm/rust-llyrl.js index 81af90e..63a489c 100644 --- a/wasm/rust-llyrl.js +++ b/wasm/rust-llyrl.js @@ -801,16 +801,16 @@ function __wbg_get_imports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper429 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 73, __wbg_adapter_20); + imports.wbg.__wbindgen_closure_wrapper533 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 86, __wbg_adapter_20); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper1249 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 270, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper1314 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper1251 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 270, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper1316 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23); return addHeapObject(ret); }; diff --git a/wasm/rust-llyrl_bg.wasm b/wasm/rust-llyrl_bg.wasm index f4800de..560e1f8 100644 Binary files a/wasm/rust-llyrl_bg.wasm and b/wasm/rust-llyrl_bg.wasm differ