From 6f9aa5489686e90556caa817aea9b17b79345269 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Mon, 10 Jan 2022 10:21:19 -0500 Subject: [PATCH] Add lighting system --- src/components.rs | 6 ++++ src/lighting_system.rs | 45 ++++++++++++++++++++++++++++ src/main.rs | 6 ++++ src/map.rs | 5 ++++ src/map/themes.rs | 3 ++ src/map_builders/limestone_cavern.rs | 3 +- src/saveload_system.rs | 2 ++ src/spawner.rs | 4 +++ 8 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/lighting_system.rs diff --git a/src/components.rs b/src/components.rs index 5ffe155..ae3a6bf 100644 --- a/src/components.rs +++ b/src/components.rs @@ -285,6 +285,12 @@ pub struct OtherLevelPosition { pub depth: i32, } +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct LightSource { + pub color: RGB, + pub range: i32, +} + // Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an // Entity. diff --git a/src/lighting_system.rs b/src/lighting_system.rs new file mode 100644 index 0000000..1918e96 --- /dev/null +++ b/src/lighting_system.rs @@ -0,0 +1,45 @@ +use ::rltk::{Point, RGB}; +use ::specs::prelude::*; +use rltk::DistanceAlg; + +use super::{LightSource, Map, Position, Viewshed}; + +pub struct LightingSystem {} + +impl<'a> System<'a> for LightingSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteExpect<'a, Map>, + ReadStorage<'a, Viewshed>, + ReadStorage<'a, Position>, + ReadStorage<'a, LightSource>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (mut map, viewshed, positions, lighting) = data; + + if map.outdoors { + return; + } + + let black = RGB::from_f32(0., 0., 0.); + for l in map.light.iter_mut() { + *l = black; + } + + for (viewshed, pos, light) in (&viewshed, &positions, &lighting).join() { + let light_point = Point::from(*pos); + let range_f = light.range as f32; + + for t in viewshed.visible_tiles.iter() { + if t.x > 0 && t.x < map.width && t.y > 0 && t.y < map.height { + let idx = map.xy_idx(t.x, t.y); + let distance = DistanceAlg::Pythagoras.distance2d(light_point, *t); + let intensity = (range_f - distance) / range_f; + + map.light[idx] = map.light[idx] + (light.color * intensity); + } + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 2454220..31bf79e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod gamesystem; mod gui; mod hunger_system; mod inventory_system; +mod lighting_system; mod map; pub mod map_builders; mod map_indexing_system; @@ -38,6 +39,7 @@ pub use game_log::GameLog; use gui::{show_cheat_mode, CheatMenuResult}; use hunger_system::HungerSystem; use inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem}; +use lighting_system::LightingSystem; pub use map::*; use map_indexing_system::MapIndexingSystem; use melee_combat_system::MeleeCombatSystem; @@ -152,6 +154,9 @@ impl State { let mut particles = ParticleSpawnSystem {}; particles.run_now(&self.ecs); + let mut lighting = LightingSystem {}; + lighting.run_now(&self.ecs); + self.ecs.maintain(); } } @@ -468,6 +473,7 @@ fn main() -> ::rltk::BError { InBackpack, InflictsDamage, Item, + LightSource, LootTable, MagicMapper, MeleeWeapon, diff --git a/src/map.rs b/src/map.rs index a73a45f..e0d2b31 100644 --- a/src/map.rs +++ b/src/map.rs @@ -8,6 +8,7 @@ use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::serde::{Deserialize, Serialize}; use ::specs::prelude::*; pub use dungeon::*; +use rltk::RGB; pub use themes::*; pub use tiletype::{tile_opaque, tile_walkable, TileType}; @@ -25,6 +26,8 @@ pub struct Map { pub bloodstains: HashSet, pub view_blocked: HashSet, pub name: String, + pub outdoors: bool, + pub light: Vec, #[serde(skip_serializing)] #[serde(skip_deserializing)] @@ -74,6 +77,8 @@ impl Map { bloodstains: HashSet::new(), view_blocked: HashSet::new(), name: name.to_string(), + outdoors: true, + light: vec![RGB::from_f32(0., 0., 0.); map_tile_count], } } } diff --git a/src/map/themes.rs b/src/map/themes.rs index 9c31d16..220e1d5 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -16,6 +16,9 @@ pub fn tile_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { if !map.visible_tiles[idx] { fg = fg.to_greyscale(); bg = RGB::from_f32(0., 0., 0.); + } else if !map.outdoors { + fg = fg * map.light[idx]; + bg = bg * map.light[idx]; } (glyph, fg, bg) diff --git a/src/map_builders/limestone_cavern.rs b/src/map_builders/limestone_cavern.rs index c0fc635..ff69d2f 100644 --- a/src/map_builders/limestone_cavern.rs +++ b/src/map_builders/limestone_cavern.rs @@ -4,7 +4,7 @@ use super::{ AreaStartingPosition, BuilderChain, BuilderMap, CullUnreachable, DistantExit, DrunkardsWalkBuilder, MetaMapBuilder, VoronoiSpawning, XStart, YStart, }; -use crate::map::{self, TileType}; +use crate::map::TileType; pub fn limestone_cavern_builder( new_depth: i32, @@ -82,5 +82,6 @@ impl CaveDecorator { } build_data.take_snapshot(); + build_data.map.outdoors = false; } } diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 1017764..2258dfc 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -80,6 +80,7 @@ pub fn save_game(ecs: &mut World) { InBackpack, InflictsDamage, Item, + LightSource, LootTable, MagicMapper, MeleeWeapon, @@ -184,6 +185,7 @@ pub fn load_game(ecs: &mut World) { InBackpack, InflictsDamage, Item, + LightSource, LootTable, MagicMapper, MeleeWeapon, diff --git a/src/spawner.rs b/src/spawner.rs index c9c1ddc..2b7ad49 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -50,6 +50,10 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { xp: 0, level: 1, }) + .with(LightSource { + color: RGB::from_f32(1.0, 1.0, 0.5), + range: 8, + }) .marked::>() .build();