From 85881db62fac4d37552dd63b822c1122b9949553 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Mon, 10 Jul 2023 11:46:21 +0100 Subject: [PATCH 1/3] pre-api removal --- Cargo.lock | 1 + Cargo.toml | 1 + src/gamelog.rs | 3 --- src/gamelog/builder.rs | 27 +++++++++++++++++++++++++++ src/gamelog/logstore.rs | 33 +++++++++++++++++++++++++++++++++ src/gamelog/mod.rs | 16 ++++++++++++++++ src/gui.rs | 17 ++++++----------- src/main.rs | 12 +++++++++++- 8 files changed, 95 insertions(+), 15 deletions(-) delete mode 100644 src/gamelog.rs create mode 100644 src/gamelog/builder.rs create mode 100644 src/gamelog/logstore.rs create mode 100644 src/gamelog/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 817d22f..856b572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2277,6 +2277,7 @@ version = "0.1.0" dependencies = [ "bracket-lib 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", "criterion", + "lazy_static", "rltk", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 33a049c..995b367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ specs = { version = "0.16.1", features = ["serde"] } specs-derive = "0.4.1" serde = { version = "1.0.93", features = ["derive"]} serde_json = "1.0.39" +lazy_static = "1.4.0" [dev-dependencies] criterion = { version = "^0.5" } diff --git a/src/gamelog.rs b/src/gamelog.rs deleted file mode 100644 index dbe28c0..0000000 --- a/src/gamelog.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct GameLog { - pub entries: Vec, -} diff --git a/src/gamelog/builder.rs b/src/gamelog/builder.rs new file mode 100644 index 0000000..0a7e481 --- /dev/null +++ b/src/gamelog/builder.rs @@ -0,0 +1,27 @@ +use super::{append_entry, LogFragment}; +use rltk::prelude::*; + +pub struct Logger { + current_colour: RGB, + fragments: Vec, +} + +impl Logger { + pub fn new() -> Self { + Logger { current_colour: RGB::named(rltk::WHITE), fragments: Vec::new() } + } + + pub fn colour(mut self, colour: (u8, u8, u8)) -> Self { + self.current_colour = RGB::named(colour); + return self; + } + + pub fn append(mut self, text: T) -> Self { + self.fragments.push(LogFragment { colour: self.current_colour, text: text.to_string() }); + return self; + } + + pub fn log(self) { + append_entry(self.fragments) + } +} diff --git a/src/gamelog/logstore.rs b/src/gamelog/logstore.rs new file mode 100644 index 0000000..02afbf0 --- /dev/null +++ b/src/gamelog/logstore.rs @@ -0,0 +1,33 @@ +use super::LogFragment; +use rltk::prelude::*; +use std::sync::Mutex; + +lazy_static! { + static ref LOG: Mutex>> = Mutex::new(Vec::new()); +} + +pub fn append_fragment(fragment: LogFragment) { + LOG.lock().unwrap().push(vec![fragment]); +} + +pub fn append_entry(fragments: Vec) { + LOG.lock().unwrap().push(fragments); +} + +pub fn clear_log() { + LOG.lock().unwrap().clear(); +} + +pub fn log_display() -> TextBuilder { + let mut buf = TextBuilder::empty(); + + LOG.lock().unwrap().iter().rev().take(12).for_each(|log| { + log.iter().for_each(|frag| { + buf.fg(frag.colour); + buf.line_wrap(&frag.text); + }); + buf.ln(); + }); + + return buf; +} diff --git a/src/gamelog/mod.rs b/src/gamelog/mod.rs new file mode 100644 index 0000000..def0086 --- /dev/null +++ b/src/gamelog/mod.rs @@ -0,0 +1,16 @@ +use rltk::prelude::*; + +mod builder; +pub use builder::*; +mod logstore; +use logstore::*; +pub use logstore::{clear_log, log_display}; + +pub struct GameLog { + pub entries: Vec, +} + +pub struct LogFragment { + pub colour: RGB, + pub text: String, +} diff --git a/src/gui.rs b/src/gui.rs index 84d9989..2325247 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,8 +1,8 @@ use super::{ - gamelog::GameLog, rex_assets::RexAssets, CombatStats, InBackpack, Map, Name, Player, Point, Position, RunState, - State, Viewshed, + gamelog, rex_assets::RexAssets, CombatStats, InBackpack, Map, Name, Player, Point, Position, RunState, State, + Viewshed, }; -use rltk::{Rltk, VirtualKeyCode, RGB}; +use rltk::{Rltk, TextBlock, VirtualKeyCode, RGB}; use specs::prelude::*; pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { @@ -18,14 +18,9 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { } // Render message log - let log = ecs.fetch::(); - let mut y = 44; - for s in log.entries.iter().rev() { - if y < 49 { - ctx.print(2, y, s); - } - y += 1; - } + let mut block = TextBlock::new(1, 44, 78, 5); + let _ = block.print(&gamelog::log_display()); + block.render(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console); // Render depth let map = ecs.fetch::(); diff --git a/src/main.rs b/src/main.rs index 8148dcb..90aefef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,8 @@ mod particle_system; use particle_system::{ParticleBuilder, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME}; mod random_table; mod rex_assets; +#[macro_use] +extern crate lazy_static; // Embedded resources for use in wasm build rltk::embedded_resource!(TERMINAL8X8, "../resources/terminal8x8.jpg"); @@ -375,7 +377,7 @@ fn main() -> rltk::BError { .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()?; context.with_post_scanlines(false); //context.screen_burn_color(RGB::named((150, 255, 255))); @@ -432,6 +434,14 @@ fn main() -> rltk::BError { gs.ecs.insert(gamelog::GameLog { entries: vec!["".to_string()], }); + gamelog::clear_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(particle_system::ParticleBuilder::new()); gs.ecs.insert(rex_assets::RexAssets::new()); From 4f899d329ecbd8c1cd6876001d36ed20eb18bd11 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Mon, 10 Jul 2023 12:48:56 +0100 Subject: [PATCH 2/3] initial --- src/damage_system.rs | 17 ++++++--- src/gamelog/builder.rs | 17 ++++++++- src/gamelog/logstore.rs | 1 + src/gamelog/mod.rs | 4 --- src/inventory_system.rs | 72 +++++++++++++++++++++++++++----------- src/main.rs | 7 ++-- src/melee_combat_system.rs | 33 +++++++++-------- src/monster_ai_system.rs | 14 ++++---- src/player.rs | 23 ++++++------ 9 files changed, 121 insertions(+), 67 deletions(-) diff --git a/src/damage_system.rs b/src/damage_system.rs index b46333f..633e708 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -1,4 +1,4 @@ -use super::{gamelog::GameLog, CombatStats, Entities, Item, Map, Name, Player, Position, SufferDamage}; +use super::{gamelog, CombatStats, Entities, Item, Map, Name, Player, Position, SufferDamage}; use specs::prelude::*; pub struct DamageSystem {} @@ -37,7 +37,6 @@ pub fn delete_the_dead(ecs: &mut World) { let names = ecs.read_storage::(); let items = ecs.read_storage::(); let entities = ecs.entities(); - let mut log = ecs.write_resource::(); for (entity, stats) in (&entities, &combat_stats).join() { if stats.hp < 1 { let player = players.get(entity); @@ -47,9 +46,19 @@ pub fn delete_the_dead(ecs: &mut World) { if let Some(victim_name) = victim_name { let item = items.get(entity); if let Some(_item) = item { - log.entries.push(format!("{} was destroyed!", &victim_name.name)); + gamelog::Logger::new() + .append("The") + .npc_name(&victim_name.name) + .colour(rltk::WHITE) + .append("was destroyed.") + .log(); } else { - log.entries.push(format!("The {} died!", &victim_name.name)); + gamelog::Logger::new() + .append("The") + .npc_name(&victim_name.name) + .colour(rltk::WHITE) + .append("died.") + .log(); } } dead.push(entity) diff --git a/src/gamelog/builder.rs b/src/gamelog/builder.rs index 0a7e481..3570041 100644 --- a/src/gamelog/builder.rs +++ b/src/gamelog/builder.rs @@ -22,6 +22,21 @@ impl Logger { } pub fn log(self) { - append_entry(self.fragments) + return append_entry(self.fragments); + } + + pub fn npc_name(mut self, text: T) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::YELLOW), text: text.to_string() }); + return self; + } + + pub fn item_name(mut self, text: T) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::CYAN), text: text.to_string() }); + return self; + } + + pub fn damage(mut self, damage: i32) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::RED), text: format!("{}", damage).to_string() }); + return self; } } diff --git a/src/gamelog/logstore.rs b/src/gamelog/logstore.rs index 02afbf0..7ff1015 100644 --- a/src/gamelog/logstore.rs +++ b/src/gamelog/logstore.rs @@ -6,6 +6,7 @@ lazy_static! { static ref LOG: Mutex>> = Mutex::new(Vec::new()); } +#[allow(dead_code)] pub fn append_fragment(fragment: LogFragment) { LOG.lock().unwrap().push(vec![fragment]); } diff --git a/src/gamelog/mod.rs b/src/gamelog/mod.rs index def0086..31beb4f 100644 --- a/src/gamelog/mod.rs +++ b/src/gamelog/mod.rs @@ -6,10 +6,6 @@ mod logstore; use logstore::*; pub use logstore::{clear_log, log_display}; -pub struct GameLog { - pub entries: Vec, -} - pub struct LogFragment { pub colour: RGB, pub text: String, diff --git a/src/inventory_system.rs b/src/inventory_system.rs index b516a25..56e7585 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,6 +1,6 @@ use super::{ - gamelog::GameLog, CombatStats, Confusion, Consumable, Cursed, Destructible, InBackpack, InflictsDamage, - MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing, RunState, SufferDamage, WantsToDropItem, + gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, InBackpack, InflictsDamage, MagicMapper, Map, + Name, ParticleBuilder, Point, Position, ProvidesHealing, RunState, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, }; use specs::prelude::*; @@ -11,7 +11,6 @@ impl<'a> System<'a> for ItemCollectionSystem { #[allow(clippy::type_complexity)] type SystemData = ( ReadExpect<'a, Entity>, - WriteExpect<'a, GameLog>, WriteStorage<'a, WantsToPickupItem>, WriteStorage<'a, Position>, ReadStorage<'a, Name>, @@ -19,14 +18,17 @@ impl<'a> System<'a> for ItemCollectionSystem { ); fn run(&mut self, data: Self::SystemData) { - let (player_entity, mut gamelog, mut wants_pickup, mut positions, names, mut backpack) = data; + let (player_entity, mut wants_pickup, mut positions, names, mut backpack) = data; for pickup in wants_pickup.join() { positions.remove(pickup.item); backpack.insert(pickup.item, InBackpack { owner: pickup.collected_by }).expect("Unable to pickup item."); if pickup.collected_by == *player_entity { - gamelog.entries.push(format!("You pick up the {}.", names.get(pickup.item).unwrap().name)); + gamelog::Logger::new() + .append("You pick up the") + .item_name(format!("{}.", &names.get(pickup.item).unwrap().name)) + .log(); } } @@ -39,7 +41,6 @@ impl<'a> System<'a> for ItemUseSystem { #[allow(clippy::type_complexity)] type SystemData = ( ReadExpect<'a, Entity>, - WriteExpect<'a, GameLog>, ReadExpect<'a, Map>, Entities<'a>, WriteStorage<'a, WantsToUseItem>, @@ -62,7 +63,6 @@ impl<'a> System<'a> for ItemUseSystem { fn run(&mut self, data: Self::SystemData) { let ( player_entity, - mut gamelog, map, entities, mut wants_to_use, @@ -89,7 +89,7 @@ impl<'a> System<'a> for ItemUseSystem { let is_cursed = cursed_items.get(wants_to_use.item); - gamelog.entries.push(format!("You use the {}.", item_being_used.name)); + gamelog::Logger::new().append("You use the").item_name(format!("{}.", &item_being_used.name)).log(); // TARGETING let mut targets: Vec = Vec::new(); @@ -117,7 +117,12 @@ impl<'a> System<'a> for ItemUseSystem { if let Some(pos) = pos { target = Point::new(pos.x, pos.y); } - gamelog.entries.push(format!("The {} disobeys!", item_being_used.name)); + gamelog::Logger::new() + .append("The") + .item_name(&item_being_used.name) + .colour(rltk::WHITE) + .append("disobeys!") + .log(); } } // AOE @@ -153,7 +158,13 @@ impl<'a> System<'a> for ItemUseSystem { if let Some(stats) = stats { stats.hp = i32::min(stats.max_hp, stats.hp + heal.amount); if entity == *player_entity { - gamelog.entries.push(format!("Quaffing, you heal {} hp.", heal.amount)); + gamelog::Logger::new() + .append("Quaffing, you heal") + .colour(rltk::GREEN) + .append(heal.amount) + .colour(rltk::WHITE) + .append("hit points.") + .log(); } let pos = positions.get(entity); if let Some(pos) = pos { @@ -194,14 +205,25 @@ impl<'a> System<'a> for ItemUseSystem { None => { SufferDamage::new_damage(&mut suffer_damage, *mob, damage.amount); if entity == *player_entity { - gamelog.entries.push(format!( - "{} takes {} damage from the {}!", - entity_name.name, damage.amount, item_being_used.name - )); + gamelog::Logger::new() + .append("The") + .npc_name(&entity_name.name) + .colour(rltk::WHITE) + .append("takes") + .damage(damage.amount) + .colour(rltk::WHITE) + .append("damage from the") + .item_name(format!("{}.", &item_being_used.name)) + .log(); } } Some(_destructible) => { - gamelog.entries.push(format!("{} is destroyed!", entity_name.name)); + gamelog::Logger::new() + .append("The") + .item_name(&entity_name.name) + .colour(rltk::WHITE) + .append("is destroyed!") + .log(); entities.delete(*mob).expect("Delete failed"); } } @@ -237,11 +259,19 @@ impl<'a> System<'a> for ItemUseSystem { used_item = true; match is_cursed { None => { - gamelog.entries.push("You feel a sense of acuity to your surroundings.".to_string()); + gamelog::Logger::new() + .append("You feel") + .colour(rltk::GREEN) + .append("a sense of acuity towards your surroundings.") + .log(); *runstate = RunState::MagicMapReveal { row: 0, cursed: false }; } Some(_) => { - gamelog.entries.push("You forget where you just were!".to_string()); + gamelog::Logger::new() + .append("You") + .colour(rltk::RED) + .append("forget where you last were.") + .log(); *runstate = RunState::MagicMapReveal { row: 0, cursed: true }; } } @@ -269,7 +299,6 @@ impl<'a> System<'a> for ItemDropSystem { #[allow(clippy::type_complexity)] type SystemData = ( ReadExpect<'a, Entity>, - WriteExpect<'a, GameLog>, Entities<'a>, WriteStorage<'a, WantsToDropItem>, ReadStorage<'a, Name>, @@ -278,7 +307,7 @@ impl<'a> System<'a> for ItemDropSystem { ); fn run(&mut self, data: Self::SystemData) { - let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data; + let (player_entity, entities, mut wants_drop, names, mut positions, mut backpack) = data; for (entity, to_drop) in (&entities, &wants_drop).join() { let mut dropper_pos: Position = Position { x: 0, y: 0 }; @@ -293,7 +322,10 @@ impl<'a> System<'a> for ItemDropSystem { backpack.remove(to_drop.item); if entity == *player_entity { - gamelog.entries.push(format!("You drop the {}.", names.get(to_drop.item).unwrap().name)); + gamelog::Logger::new() + .append("You drop the") + .item_name(format!("{}.", &names.get(to_drop.item).unwrap().name)) + .log(); } } diff --git a/src/main.rs b/src/main.rs index 90aefef..1525678 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,8 +157,7 @@ impl State { } // Notify player, restore health up to a point. - let mut gamelog = self.ecs.fetch_mut::(); - gamelog.entries.push("You descend the stairwell, and take a moment to recover your strength.".to_string()); + gamelog::Logger::new().append("You descend the stairwell, and take a moment to gather your strength.").log(); let mut player_health_store = self.ecs.write_storage::(); let player_health = player_health_store.get_mut(*player_entity); if let Some(player_health) = player_health { @@ -431,9 +430,7 @@ fn main() -> rltk::BError { gs.ecs.insert(map); gs.ecs.insert(Point::new(player_x, player_y)); gs.ecs.insert(player_entity); - gs.ecs.insert(gamelog::GameLog { - entries: vec!["".to_string()], - }); + gamelog::clear_log(); gamelog::Logger::new() .append("Welcome!") diff --git a/src/melee_combat_system.rs b/src/melee_combat_system.rs index 5f6431d..2211ba9 100644 --- a/src/melee_combat_system.rs +++ b/src/melee_combat_system.rs @@ -1,4 +1,4 @@ -use super::{gamelog::GameLog, CombatStats, Name, ParticleBuilder, Position, SufferDamage, WantsToMelee}; +use super::{gamelog, CombatStats, Name, ParticleBuilder, Position, SufferDamage, WantsToMelee}; use specs::prelude::*; pub struct MeleeCombatSystem {} @@ -6,7 +6,6 @@ pub struct MeleeCombatSystem {} impl<'a> System<'a> for MeleeCombatSystem { type SystemData = ( Entities<'a>, - WriteExpect<'a, GameLog>, WriteStorage<'a, WantsToMelee>, ReadStorage<'a, Name>, ReadStorage<'a, CombatStats>, @@ -16,16 +15,8 @@ impl<'a> System<'a> for MeleeCombatSystem { ); fn run(&mut self, data: Self::SystemData) { - let ( - entities, - mut log, - mut wants_melee, - names, - combat_stats, - mut inflict_damage, - mut particle_builder, - positions, - ) = data; + let (entities, mut wants_melee, names, combat_stats, mut inflict_damage, mut particle_builder, positions) = + data; for (_entity, wants_melee, name, stats) in (&entities, &wants_melee, &names, &combat_stats).join() { if stats.hp <= 0 { @@ -51,9 +42,23 @@ impl<'a> System<'a> for MeleeCombatSystem { let damage = i32::max(0, stats.power - target_stats.defence); if damage == 0 { - log.entries.push(format!("{} is unable to hurt {}.", &name.name, &target_name.name)); + gamelog::Logger::new() + .append("The") + .npc_name(&name.name) + .colour(rltk::WHITE) + .append("attempts to strike") + .npc_name(&target_name.name) + .colour(rltk::WHITE) + .append("- but fails.") + .log(); } else { - log.entries.push(format!("{} hits {} for {} damage.", &name.name, &target_name.name, damage)); + gamelog::Logger::new() // hits the ! + .append("The") + .npc_name(&name.name) + .colour(rltk::WHITE) + .append("hits the") + .npc_name(format!("{}.", &target_name.name)) + .log(); SufferDamage::new_damage(&mut inflict_damage, wants_melee.target, damage); } } diff --git a/src/monster_ai_system.rs b/src/monster_ai_system.rs index 69dd326..1433673 100644 --- a/src/monster_ai_system.rs +++ b/src/monster_ai_system.rs @@ -1,6 +1,4 @@ -use super::{ - gamelog::GameLog, Confusion, Map, Monster, Name, ParticleBuilder, Position, RunState, Viewshed, WantsToMelee, -}; +use super::{gamelog, Confusion, Map, Monster, Name, ParticleBuilder, Position, RunState, Viewshed, WantsToMelee}; use rltk::Point; use specs::prelude::*; @@ -10,7 +8,6 @@ impl<'a> System<'a> for MonsterAI { #[allow(clippy::type_complexity)] type SystemData = ( WriteExpect<'a, Map>, - WriteExpect<'a, GameLog>, ReadExpect<'a, Point>, ReadExpect<'a, Entity>, ReadExpect<'a, RunState>, @@ -27,7 +24,6 @@ impl<'a> System<'a> for MonsterAI { fn run(&mut self, data: Self::SystemData) { let ( mut map, - mut gamelog, player_pos, player_entity, runstate, @@ -57,11 +53,15 @@ impl<'a> System<'a> for MonsterAI { let mut glyph = rltk::to_cp437('?'); if i_am_confused.turns < 1 { confused.remove(entity); - gamelog.entries.push(format!("{} snaps out of its confusion!", entity_name.name)); + gamelog::Logger::new() + .npc_name(&entity_name.name) + .colour(rltk::WHITE) + .append("snaps out of it.") + .log(); fg = rltk::RGB::named(rltk::MEDIUMSLATEBLUE); glyph = rltk::to_cp437('!'); } else { - gamelog.entries.push(format!("{} is confused.", entity_name.name)); + gamelog::Logger::new().npc_name(&entity_name.name).colour(rltk::WHITE).append("is confused.").log(); } particle_builder.request(pos.x, pos.y, fg, rltk::RGB::named(rltk::BLACK), glyph, 200.0); can_act = false; diff --git a/src/player.rs b/src/player.rs index 5a43b43..a031c5c 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,5 +1,5 @@ use super::{ - gamelog::GameLog, CombatStats, Item, Map, Monster, Name, Player, Position, RunState, State, TileType, Viewshed, + gamelog, CombatStats, Item, Map, Monster, Name, Player, Position, RunState, State, TileType, Viewshed, WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH, }; use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; @@ -48,8 +48,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) { } if tile_content != "You see " { tile_content.push_str("."); - let mut gamelog = ecs.write_resource::(); - gamelog.entries.push(tile_content); + gamelog::Logger::new().append(tile_content).log() } 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)); @@ -67,7 +66,6 @@ fn get_item(ecs: &mut World) { let entities = ecs.entities(); let items = ecs.read_storage::(); let positions = ecs.read_storage::(); - let mut gamelog = ecs.fetch_mut::(); let mut target_item: Option = None; for (item_entity, _item, position) in (&entities, &items, &positions).join() { @@ -77,7 +75,7 @@ fn get_item(ecs: &mut World) { } match target_item { - None => gamelog.entries.push("There is nothing to pick up.".to_string()), + None => gamelog::Logger::new().append("There is nothing to pick up.").log(), Some(item) => { let mut pickup = ecs.write_storage::(); pickup @@ -142,8 +140,7 @@ pub fn try_next_level(ecs: &mut World) -> bool { if map.tiles[player_idx] == TileType::DownStair { return true; } else { - let mut gamelog = ecs.fetch_mut::(); - gamelog.entries.push("You don't see a way down.".to_string()); + gamelog::Logger::new().append("You don't see a way down.").log(); return false; } } @@ -152,8 +149,6 @@ fn skip_turn(ecs: &mut World) -> RunState { let player_entity = ecs.fetch::(); let viewshed_components = ecs.read_storage::(); let monsters = ecs.read_storage::(); - let mut wait_message = "You wait a turn."; - let worldmap_resource = ecs.fetch::(); let mut can_heal = true; @@ -171,6 +166,7 @@ fn skip_turn(ecs: &mut World) -> RunState { } } + let mut did_heal = false; if can_heal { let mut health_components = ecs.write_storage::(); let player_hp = health_components.get_mut(*player_entity).unwrap(); @@ -178,12 +174,15 @@ fn skip_turn(ecs: &mut World) -> RunState { let roll = rng.roll_dice(1, 6); if (roll == 6) && player_hp.hp < player_hp.max_hp { player_hp.hp += 1; - wait_message = "You wait a turn, and recover a hit point."; + did_heal = true; } } - let mut gamelog = ecs.fetch_mut::(); - gamelog.entries.push(wait_message.to_string()); + if did_heal { + gamelog::Logger::new().append("You wait a turn, and").colour(rltk::GREEN).append("recover a hit point.").log(); + } else { + gamelog::Logger::new().append("You wait a turn.").log(); + } return RunState::PlayerTurn; } From c10eda695abb255cd698952038221088fd87f3da Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Mon, 10 Jul 2023 13:09:01 +0100 Subject: [PATCH 3/3] logs + events refactor --- src/components.rs | 3 +++ src/gamelog/events.rs | 43 +++++++++++++++++++++++++++++++++++++++++ src/gamelog/logstore.rs | 9 +++++++++ src/gamelog/mod.rs | 6 +++++- src/gui.rs | 11 ++++++++++- src/main.rs | 4 ++++ src/saveload_system.rs | 13 +++++++++++-- 7 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/gamelog/events.rs diff --git a/src/components.rs b/src/components.rs index 2c57635..6f29d63 100644 --- a/src/components.rs +++ b/src/components.rs @@ -4,6 +4,7 @@ use specs::error::NoError; use specs::prelude::*; use specs::saveload::{ConvertSaveload, Marker}; use specs_derive::*; +use std::collections::HashMap; // Serialization helper code. We need to implement ConvertSaveload for each type that contains an // Entity. @@ -12,6 +13,8 @@ pub struct SerializeMe; #[derive(Component, Serialize, Deserialize, Clone)] pub struct SerializationHelper { pub map: super::map::Map, + pub log: Vec>, + pub events: HashMap, } #[derive(Component, ConvertSaveload, Clone)] diff --git a/src/gamelog/events.rs b/src/gamelog/events.rs new file mode 100644 index 0000000..f7c7b4f --- /dev/null +++ b/src/gamelog/events.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; +use std::sync::Mutex; + +lazy_static! { + static ref EVENTS: Mutex> = Mutex::new(HashMap::new()); +} + +pub fn clear_events() { + EVENTS.lock().unwrap().clear(); +} + +pub fn record_event(event: T, n: i32) { + let event_name = event.to_string(); + let mut events_lock = EVENTS.lock(); + let mut events = events_lock.as_mut().unwrap(); + if let Some(e) = events.get_mut(&event_name) { + *e += n; + } else { + events.insert(event_name, n); + } +} + +pub fn get_event_count(event: T) -> i32 { + let event_name = event.to_string(); + let events_lock = EVENTS.lock(); + let events = events_lock.unwrap(); + if let Some(e) = events.get(&event_name) { + *e + } else { + 0 + } +} + +pub fn clone_events() -> HashMap { + EVENTS.lock().unwrap().clone() +} + +pub fn load_events(events: HashMap) { + EVENTS.lock().unwrap().clear(); + events.iter().for_each(|(k, v)| { + EVENTS.lock().unwrap().insert(k.to_string(), *v); + }); +} diff --git a/src/gamelog/logstore.rs b/src/gamelog/logstore.rs index 7ff1015..9d8ec10 100644 --- a/src/gamelog/logstore.rs +++ b/src/gamelog/logstore.rs @@ -32,3 +32,12 @@ pub fn log_display() -> TextBuilder { return buf; } + +pub fn clone_log() -> Vec> { + return LOG.lock().unwrap().clone(); +} + +pub fn restore_log(log: &mut Vec>) { + LOG.lock().unwrap().clear(); + LOG.lock().unwrap().append(log); +} diff --git a/src/gamelog/mod.rs b/src/gamelog/mod.rs index 31beb4f..aad720f 100644 --- a/src/gamelog/mod.rs +++ b/src/gamelog/mod.rs @@ -4,8 +4,12 @@ mod builder; pub use builder::*; mod logstore; use logstore::*; -pub use logstore::{clear_log, log_display}; +pub use logstore::{clear_log, clone_log, log_display, restore_log}; +mod events; +pub use events::*; +use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, Clone)] pub struct LogFragment { pub colour: RGB, pub text: String, diff --git a/src/gui.rs b/src/gui.rs index 2325247..4ef7066 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -25,7 +25,16 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { // Render depth let map = ecs.fetch::(); let depth = format!(" D{} ", map.depth); - ctx.print_color(74, 43, 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, + 49, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + &format!(" T{} ", crate::gamelog::get_event_count("Turn")), + ); // Render mouse cursor let mouse_pos = ctx.mouse_pos(); diff --git a/src/main.rs b/src/main.rs index 1525678..491f469 100644 --- a/src/main.rs +++ b/src/main.rs @@ -214,6 +214,9 @@ impl GameState for State { } RunState::AwaitingInput => { new_runstate = player_input(self, ctx); + if new_runstate != RunState::AwaitingInput { + gamelog::record_event("Turn", 1); + } } RunState::PlayerTurn => { self.run_systems(); @@ -432,6 +435,7 @@ fn main() -> rltk::BError { gs.ecs.insert(player_entity); gamelog::clear_log(); + gamelog::clear_events(); gamelog::Logger::new() .append("Welcome!") .colour(rltk::CYAN) diff --git a/src/saveload_system.rs b/src/saveload_system.rs index ab75d70..c53e899 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -27,8 +27,15 @@ pub fn save_game(_ecs: &mut World) {} pub fn save_game(ecs: &mut World) { // Create helper let mapcopy = ecs.get_mut::().unwrap().clone(); - let savehelper = - ecs.create_entity().with(SerializationHelper { map: mapcopy }).marked::>().build(); + let savehelper = ecs + .create_entity() + .with(SerializationHelper { + map: mapcopy, + log: crate::gamelog::clone_log(), + events: crate::gamelog::clone_events(), + }) + .marked::>() + .build(); // Actually serialize { @@ -156,6 +163,8 @@ pub fn load_game(ecs: &mut World) { *worldmap = h.map.clone(); worldmap.tile_content = vec![Vec::new(); super::map::MAPCOUNT]; deleteme = Some(e); + crate::gamelog::restore_log(&mut h.log.clone()); + crate::gamelog::load_events(h.events.clone()); } for (e, _p, pos) in (&entities, &player, &position).join() { let mut ppos = ecs.write_resource::();