fastfov and telepathy

currently no way to *gain* telepathy, but it works
This commit is contained in:
Llywelwyn 2023-07-11 11:12:11 +01:00
parent 58ab2e9aa5
commit ec9127573c
12 changed files with 126 additions and 45 deletions

View file

@ -37,6 +37,9 @@ pub struct Player {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Monster {} pub struct Monster {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Mind {}
#[derive(Component, ConvertSaveload, Clone)] #[derive(Component, ConvertSaveload, Clone)]
pub struct Viewshed { pub struct Viewshed {
pub visible_tiles: Vec<rltk::Point>, pub visible_tiles: Vec<rltk::Point>,
@ -44,6 +47,13 @@ pub struct Viewshed {
pub dirty: bool, pub dirty: bool,
} }
#[derive(Component, ConvertSaveload, Clone)]
pub struct Telepath {
pub telepath_tiles: Vec<rltk::Point>,
pub range: i32,
pub dirty: bool,
}
#[derive(Component, Debug, ConvertSaveload, Clone)] #[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct Name { pub struct Name {
pub name: String, pub name: String,

View file

@ -1,4 +1,4 @@
use super::LogFragment; use super::{events, LogFragment, Logger};
use rltk::prelude::*; use rltk::prelude::*;
use std::sync::Mutex; use std::sync::Mutex;
@ -37,6 +37,21 @@ pub fn print_log(console: &mut Box<dyn Console>, 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<Vec<crate::gamelog::LogFragment>> { pub fn clone_log() -> Vec<Vec<crate::gamelog::LogFragment>> {
return LOG.lock().unwrap().clone(); return LOG.lock().unwrap().clone();
} }

View file

@ -4,7 +4,7 @@ mod builder;
pub use builder::*; pub use builder::*;
mod logstore; mod logstore;
use 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; mod events;
pub use events::*; pub use events::*;

View file

@ -6,29 +6,29 @@ use rltk::{Rltk, VirtualKeyCode, RGB};
use specs::prelude::*; use specs::prelude::*;
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { 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 // Render stats
let combat_stats = ecs.read_storage::<CombatStats>(); let combat_stats = ecs.read_storage::<CombatStats>();
let players = ecs.read_storage::<Player>(); let players = ecs.read_storage::<Player>();
for (_player, stats) in (&players, &combat_stats).join() { for (_player, stats) in (&players, &combat_stats).join() {
let health = format!(" HP {}/{} ", stats.hp, stats.max_hp); 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.print_color_right(36, 43, 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.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. // 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 // Render depth
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
let depth = format!(" D{} ", map.depth); 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 // Render turn
ctx.print_color_right( ctx.print_color_right(
78, 78,
59, 50,
RGB::named(rltk::YELLOW), RGB::named(rltk::YELLOW),
RGB::named(rltk::BLACK), RGB::named(rltk::BLACK),
&format!(" T{} ", crate::gamelog::get_event_count("Turn")), &format!(" T{} ", crate::gamelog::get_event_count("Turn")),

View file

@ -220,6 +220,8 @@ impl State {
if let Some(vs) = vs { if let Some(vs) = vs {
vs.dirty = true; vs.dirty = true;
} }
gamelog::setup_log();
} }
} }
@ -242,21 +244,28 @@ impl GameState for State {
{ {
let positions = self.ecs.read_storage::<Position>(); let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>(); let renderables = self.ecs.read_storage::<Renderable>();
let minds = self.ecs.read_storage::<Mind>();
let map = self.ecs.fetch::<Map>(); let map = self.ecs.fetch::<Map>();
let entities = self.ecs.entities();
let mut data = (&positions, &renderables).join().collect::<Vec<_>>(); let mut data = (&positions, &renderables, &entities).join().collect::<Vec<_>>();
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order)); 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 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 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); let mut bg = render.bg.add(RGB::from_u8(26, 45, 45)).add(offsets);
//bg = bg.add(offsets);
if map.bloodstains.contains(&idx) { if map.bloodstains.contains(&idx) {
bg = bg.add(RGB::from_f32(0.6, 0., 0.)); bg = bg.add(RGB::from_f32(0.6, 0., 0.));
} }
if map.visible_tiles[idx] { if map.visible_tiles[idx] {
ctx.set(pos.x, pos.y, render.fg, bg, render.glyph); 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); gui::draw_ui(&self.ecs, ctx);
} }
@ -450,17 +459,14 @@ impl GameState for State {
} }
const DISPLAYWIDTH: i32 = 80; const DISPLAYWIDTH: i32 = 80;
const DISPLAYHEIGHT: i32 = 60; const DISPLAYHEIGHT: i32 = 51;
fn main() -> rltk::BError { fn main() -> rltk::BError {
use rltk::RltkBuilder; use rltk::RltkBuilder;
let mut context = RltkBuilder::new() let mut context = RltkBuilder::simple(DISPLAYWIDTH, DISPLAYHEIGHT)
.unwrap()
.with_title("rust-rl") .with_title("rust-rl")
.with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT)
.with_tile_dimensions(16, 16) .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") //.with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg")
.build()?; .build()?;
context.with_post_scanlines(false); context.with_post_scanlines(false);
@ -472,7 +478,9 @@ fn main() -> rltk::BError {
gs.ecs.register::<Renderable>(); gs.ecs.register::<Renderable>();
gs.ecs.register::<Player>(); gs.ecs.register::<Player>();
gs.ecs.register::<Monster>(); gs.ecs.register::<Monster>();
gs.ecs.register::<Mind>();
gs.ecs.register::<Viewshed>(); gs.ecs.register::<Viewshed>();
gs.ecs.register::<Telepath>();
gs.ecs.register::<Name>(); gs.ecs.register::<Name>();
gs.ecs.register::<BlocksTile>(); gs.ecs.register::<BlocksTile>();
gs.ecs.register::<CombatStats>(); gs.ecs.register::<CombatStats>();
@ -521,18 +529,8 @@ fn main() -> rltk::BError {
gs.ecs.insert(Point::new(player_x, player_y)); gs.ecs.insert(Point::new(player_x, player_y));
gs.ecs.insert(player_entity); gs.ecs.insert(player_entity);
gamelog::clear_log(); gamelog::setup_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();
gs.ecs.insert(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }); gs.ecs.insert(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame });
gs.ecs.insert(particle_system::ParticleBuilder::new()); gs.ecs.insert(particle_system::ParticleBuilder::new());
gs.ecs.insert(rex_assets::RexAssets::new()); gs.ecs.insert(rex_assets::RexAssets::new());

View file

@ -26,6 +26,7 @@ pub struct Map {
pub height: i32, pub height: i32,
pub revealed_tiles: Vec<bool>, pub revealed_tiles: Vec<bool>,
pub visible_tiles: Vec<bool>, pub visible_tiles: Vec<bool>,
pub telepath_tiles: Vec<bool>,
pub red_offset: Vec<u8>, pub red_offset: Vec<u8>,
pub green_offset: Vec<u8>, pub green_offset: Vec<u8>,
pub blue_offset: Vec<u8>, pub blue_offset: Vec<u8>,
@ -100,6 +101,7 @@ impl Map {
height: MAPHEIGHT as i32, height: MAPHEIGHT as i32,
revealed_tiles: vec![false; MAPCOUNT], revealed_tiles: vec![false; MAPCOUNT],
visible_tiles: vec![false; MAPCOUNT], visible_tiles: vec![false; MAPCOUNT],
telepath_tiles: vec![false; MAPCOUNT],
red_offset: vec![0; MAPCOUNT], red_offset: vec![0; MAPCOUNT],
green_offset: vec![0; MAPCOUNT], green_offset: vec![0; MAPCOUNT],
blue_offset: vec![0; MAPCOUNT], blue_offset: vec![0; MAPCOUNT],

View file

@ -1,5 +1,5 @@
use super::{ 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, WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH,
}; };
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; 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::<Position>(); let mut positions = ecs.write_storage::<Position>();
let mut players = ecs.write_storage::<Player>(); let mut players = ecs.write_storage::<Player>();
let mut viewsheds = ecs.write_storage::<Viewshed>(); let mut viewsheds = ecs.write_storage::<Viewshed>();
let mut telepaths = ecs.write_storage::<Telepath>();
let combat_stats = ecs.read_storage::<CombatStats>(); let combat_stats = ecs.read_storage::<CombatStats>();
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
@ -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.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)); 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; 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::<Point>(); let mut ppos = ecs.write_resource::<Point>();
ppos.x = pos.x; ppos.x = pos.x;
ppos.y = pos.y; ppos.y = pos.y;

View file

@ -51,7 +51,9 @@ pub fn save_game(ecs: &mut World) {
Renderable, Renderable,
Player, Player,
Viewshed, Viewshed,
Telepath,
Monster, Monster,
Mind,
Name, Name,
BlocksTile, BlocksTile,
CombatStats, CombatStats,
@ -133,7 +135,9 @@ pub fn load_game(ecs: &mut World) {
Renderable, Renderable,
Player, Player,
Viewshed, Viewshed,
Telepath,
Monster, Monster,
Mind,
Name, Name,
BlocksTile, BlocksTile,
CombatStats, CombatStats,

View file

@ -1,7 +1,7 @@
use super::{ use super::{
random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible,
EquipmentSlot, Equippable, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Monster, Name, Player, Position, EquipmentSlot, Equippable, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Mind, Monster, Name, Player,
ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Viewshed, AOE, MAPWIDTH, Position, ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Telepath, Viewshed, AOE, MAPWIDTH,
}; };
use rltk::{console, RandomNumberGenerator, RGB}; use rltk::{console, RandomNumberGenerator, RGB};
use specs::prelude::*; use specs::prelude::*;
@ -27,7 +27,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32, player_name: String
.build() .build()
} }
fn monster<S: ToString>(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharType, name: S, hit_die: i32) { fn monster<S: ToString>(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); let rolled_hp = roll_hit_dice(ecs, 1, hit_die);
ecs.create_entity() ecs.create_entity()
@ -35,23 +35,24 @@ fn monster<S: ToString>(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(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(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true })
.with(Monster {}) .with(Monster {})
.with(Mind {})
.with(Name { name: name.to_string() }) .with(Name { name: name.to_string() })
.with(BlocksTile {}) .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::<SimpleMarker<SerializeMe>>() .marked::<SimpleMarker<SerializeMe>>()
.build(); .build();
} }
fn orc(ecs: &mut World, x: i32, y: i32) { 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) { 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) { 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 { pub fn roll_hit_dice(ecs: &mut World, n: i32, d: i32) -> i32 {

View file

@ -1,4 +1,4 @@
use super::{Map, Player, Position, Viewshed}; use super::{Map, Player, Position, Telepath, Viewshed};
use rltk::{FieldOfViewAlg::SymmetricShadowcasting, Point}; use rltk::{FieldOfViewAlg::SymmetricShadowcasting, Point};
use specs::prelude::*; use specs::prelude::*;
@ -9,12 +9,13 @@ impl<'a> System<'a> for VisibilitySystem {
WriteExpect<'a, Map>, WriteExpect<'a, Map>,
Entities<'a>, Entities<'a>,
WriteStorage<'a, Viewshed>, WriteStorage<'a, Viewshed>,
WriteStorage<'a, Telepath>,
WriteStorage<'a, Position>, WriteStorage<'a, Position>,
ReadStorage<'a, Player>, ReadStorage<'a, Player>,
); );
fn run(&mut self, data: Self::SystemData) { 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() { for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if viewshed.dirty { 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<Point> {
let mut visible_tiles: Vec<Point> = 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
}

View file

@ -801,16 +801,16 @@ function __wbg_get_imports() {
const ret = wasm.memory; const ret = wasm.memory;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper429 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper533 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 73, __wbg_adapter_20); const ret = makeMutClosure(arg0, arg1, 86, __wbg_adapter_20);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper1249 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper1314 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 270, __wbg_adapter_23); const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper1251 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper1316 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 270, __wbg_adapter_23); const ret = makeMutClosure(arg0, arg1, 284, __wbg_adapter_23);
return addHeapObject(ret); return addHeapObject(ret);
}; };

Binary file not shown.