fastfov and telepathy
currently no way to *gain* telepathy, but it works
This commit is contained in:
parent
58ab2e9aa5
commit
ec9127573c
12 changed files with 126 additions and 45 deletions
|
|
@ -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<rltk::Point>,
|
||||
|
|
@ -44,6 +47,13 @@ pub struct Viewshed {
|
|||
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)]
|
||||
pub struct Name {
|
||||
pub name: String,
|
||||
|
|
|
|||
|
|
@ -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<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>> {
|
||||
return LOG.lock().unwrap().clone();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
||||
|
|
|
|||
12
src/gui.rs
12
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::<CombatStats>();
|
||||
let players = ecs.read_storage::<Player>();
|
||||
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::<Map>();
|
||||
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")),
|
||||
|
|
|
|||
40
src/main.rs
40
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::<Position>();
|
||||
let renderables = self.ecs.read_storage::<Renderable>();
|
||||
let minds = self.ecs.read_storage::<Mind>();
|
||||
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));
|
||||
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::<Renderable>();
|
||||
gs.ecs.register::<Player>();
|
||||
gs.ecs.register::<Monster>();
|
||||
gs.ecs.register::<Mind>();
|
||||
gs.ecs.register::<Viewshed>();
|
||||
gs.ecs.register::<Telepath>();
|
||||
gs.ecs.register::<Name>();
|
||||
gs.ecs.register::<BlocksTile>();
|
||||
gs.ecs.register::<CombatStats>();
|
||||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub struct Map {
|
|||
pub height: i32,
|
||||
pub revealed_tiles: Vec<bool>,
|
||||
pub visible_tiles: Vec<bool>,
|
||||
pub telepath_tiles: Vec<bool>,
|
||||
pub red_offset: Vec<u8>,
|
||||
pub green_offset: Vec<u8>,
|
||||
pub blue_offset: Vec<u8>,
|
||||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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::<Position>();
|
||||
let mut players = ecs.write_storage::<Player>();
|
||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||
let mut telepaths = ecs.write_storage::<Telepath>();
|
||||
let combat_stats = ecs.read_storage::<CombatStats>();
|
||||
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.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::<Point>();
|
||||
ppos.x = pos.x;
|
||||
ppos.y = pos.y;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<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);
|
||||
|
||||
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(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::<SimpleMarker<SerializeMe>>()
|
||||
.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 {
|
||||
|
|
|
|||
|
|
@ -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<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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue