Merge branch 'gamelog_module'

This commit is contained in:
Llywelwyn 2023-07-10 13:09:17 +01:00
commit a780d13733
16 changed files with 295 additions and 80 deletions

1
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -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<Vec<crate::gamelog::LogFragment>>,
pub events: HashMap<String, i32>,
}
#[derive(Component, ConvertSaveload, Clone)]

View file

@ -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::<Name>();
let items = ecs.read_storage::<Item>();
let entities = ecs.entities();
let mut log = ecs.write_resource::<GameLog>();
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)

View file

@ -1,3 +0,0 @@
pub struct GameLog {
pub entries: Vec<String>,
}

42
src/gamelog/builder.rs Normal file
View file

@ -0,0 +1,42 @@
use super::{append_entry, LogFragment};
use rltk::prelude::*;
pub struct Logger {
current_colour: RGB,
fragments: Vec<LogFragment>,
}
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<T: ToString>(mut self, text: T) -> Self {
self.fragments.push(LogFragment { colour: self.current_colour, text: text.to_string() });
return self;
}
pub fn log(self) {
return append_entry(self.fragments);
}
pub fn npc_name<T: ToString>(mut self, text: T) -> Self {
self.fragments.push(LogFragment { colour: RGB::named(rltk::YELLOW), text: text.to_string() });
return self;
}
pub fn item_name<T: ToString>(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;
}
}

43
src/gamelog/events.rs Normal file
View file

@ -0,0 +1,43 @@
use std::collections::HashMap;
use std::sync::Mutex;
lazy_static! {
static ref EVENTS: Mutex<HashMap<String, i32>> = Mutex::new(HashMap::new());
}
pub fn clear_events() {
EVENTS.lock().unwrap().clear();
}
pub fn record_event<T: ToString>(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<T: ToString>(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<String, i32> {
EVENTS.lock().unwrap().clone()
}
pub fn load_events(events: HashMap<String, i32>) {
EVENTS.lock().unwrap().clear();
events.iter().for_each(|(k, v)| {
EVENTS.lock().unwrap().insert(k.to_string(), *v);
});
}

43
src/gamelog/logstore.rs Normal file
View file

@ -0,0 +1,43 @@
use super::LogFragment;
use rltk::prelude::*;
use std::sync::Mutex;
lazy_static! {
static ref LOG: Mutex<Vec<Vec<LogFragment>>> = Mutex::new(Vec::new());
}
#[allow(dead_code)]
pub fn append_fragment(fragment: LogFragment) {
LOG.lock().unwrap().push(vec![fragment]);
}
pub fn append_entry(fragments: Vec<LogFragment>) {
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;
}
pub fn clone_log() -> Vec<Vec<crate::gamelog::LogFragment>> {
return LOG.lock().unwrap().clone();
}
pub fn restore_log(log: &mut Vec<Vec<crate::gamelog::LogFragment>>) {
LOG.lock().unwrap().clear();
LOG.lock().unwrap().append(log);
}

16
src/gamelog/mod.rs Normal file
View file

@ -0,0 +1,16 @@
use rltk::prelude::*;
mod builder;
pub use builder::*;
mod logstore;
use logstore::*;
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,
}

View file

@ -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,19 +18,23 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
}
// Render message log
let log = ecs.fetch::<GameLog>();
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::<Map>();
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();

View file

@ -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<Entity> = 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();
}
}

View file

@ -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");
@ -155,8 +157,7 @@ impl State {
}
// Notify player, restore health up to a point.
let mut gamelog = self.ecs.fetch_mut::<gamelog::GameLog>();
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::<CombatStats>();
let player_health = player_health_store.get_mut(*player_entity);
if let Some(player_health) = player_health {
@ -213,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();
@ -375,7 +379,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)));
@ -429,9 +433,16 @@ 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!["<pretend i wrote a paragraph explaining why you're here>".to_string()],
});
gamelog::clear_log();
gamelog::clear_events();
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());

View file

@ -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() // <name> hits the <name>!
.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);
}
}

View file

@ -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;

View file

@ -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>();
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::<Item>();
let positions = ecs.read_storage::<Position>();
let mut gamelog = ecs.fetch_mut::<GameLog>();
let mut target_item: Option<Entity> = 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::<WantsToPickupItem>();
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>();
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::<Entity>();
let viewshed_components = ecs.read_storage::<Viewshed>();
let monsters = ecs.read_storage::<Monster>();
let mut wait_message = "You wait a turn.";
let worldmap_resource = ecs.fetch::<Map>();
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::<CombatStats>();
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>();
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;
}

View file

@ -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::<super::map::Map>().unwrap().clone();
let savehelper =
ecs.create_entity().with(SerializationHelper { map: mapcopy }).marked::<SimpleMarker<SerializeMe>>().build();
let savehelper = ecs
.create_entity()
.with(SerializationHelper {
map: mapcopy,
log: crate::gamelog::clone_log(),
events: crate::gamelog::clone_events(),
})
.marked::<SimpleMarker<SerializeMe>>()
.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::<rltk::Point>();