diff --git a/raws/items.json b/raws/items.json index 118c940..b1302a6 100644 --- a/raws/items.json +++ b/raws/items.json @@ -6,7 +6,7 @@ "weight": 1, "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "healing": "4d4+2" }, + "effects": { "heal": "4d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, { @@ -16,7 +16,7 @@ "weight": 1, "value": 25, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "healing": "2d4+2" }, + "effects": { "heal": "2d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, { @@ -44,7 +44,7 @@ "weight": 0.5, "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "particle_line": "*;-;#53f06d;75.0;#f9ff9f;100.0", "ranged": "12", "healing": "1d4+2" }, + "effects": { "particle_line": "*;-;#53f06d;75.0;#f9ff9f;100.0", "ranged": "12", "heal": "1d4+2" }, "magic": { "class": "uncommon", "naming": "scroll" } }, { @@ -54,7 +54,7 @@ "weight": 0.5, "value": 200, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "particle": "*;#53f06d;200.0", "ranged": "12", "aoe": "3", "healing": "1d4+2" }, + "effects": { "particle": "*;#53f06d;200.0", "ranged": "12", "aoe": "3", "heal": "1d4+2" }, "magic": { "class": "rare", "naming": "scroll" } }, { diff --git a/src/components.rs b/src/components.rs index 7af71f2..3ab3c10 100644 --- a/src/components.rs +++ b/src/components.rs @@ -237,7 +237,7 @@ pub enum BUC { Blessed, } -#[derive(Component, Debug, Serialize, Deserialize, Clone)] +#[derive(Component, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)] pub struct Beatitude { pub buc: BUC, pub known: bool, diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 6663a99..e7b157a 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -572,6 +572,23 @@ pub fn obfuscate_name_ecs(ecs: &World, item: Entity) -> (String, String) { return (singular, plural); } +pub fn unobf_name_ecs(ecs: &World, item: Entity) -> (String, String) { + let (mut singular, mut plural) = ("nameless (bug)".to_string(), "nameless (bug)".to_string()); + if let Some(name) = ecs.read_storage::().get(item) { + (singular, plural) = (name.name.clone(), name.plural.clone()); + } + if let Some(has_beatitude) = ecs.read_storage::().get(item) { + let prefix = match has_beatitude.buc { + BUC::Cursed => "cursed ", + BUC::Uncursed => "uncursed ", + BUC::Blessed => "blessed ", + }; + singular.insert_str(0, prefix); + plural.insert_str(0, prefix); + } + return (singular, plural); +} + /// Gets renderable colour as tuple of u8 pub fn renderable_colour(renderables: &ReadStorage, entity: Entity) -> (u8, u8, u8) { return if let Some(renderable) = renderables.get(entity) { diff --git a/src/morgue.rs b/src/morgue.rs index 5da74d5..a541247 100644 --- a/src/morgue.rs +++ b/src/morgue.rs @@ -1,8 +1,11 @@ use std::fs::{ File, create_dir_all }; use std::io::{ self, Write }; use std::time::SystemTime; +use crate::components::*; +use crate::gui::{ Class, Ancestry, unobf_name_ecs }; use specs::prelude::*; use rltk::prelude::*; +use std::collections::HashMap; #[cfg(target_arch = "wasm32")] pub fn create_morgue_file(_ecs: &World) {} @@ -19,14 +22,152 @@ pub fn create_morgue_file(ecs: &World) { } fn write_morgue_file(ecs: &World, morgue_dir: &str) -> Result<(), io::Error> { - let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); - let morgue_info = format!( - r#"╔══════════════════════════════════════════════════════════════╗ -║ Level 1, human wizard ║ -╚══════════════════════════════════════════════════════════════╝"# + // Initialise default + let mut morgue_info: String = Default::default(); + + let e = ecs.fetch::(); + let class = match ecs.read_storage::().get(*e).unwrap().name { + Class::Fighter => "fighter", + Class::Wizard => "wizard", + Class::Rogue => "rogue", + Class::Villager => "villager", + }; + let ancestry = match ecs.read_storage::().get(*e).unwrap().name { + Ancestry::Human => "human", + Ancestry::Elf => "elf", + Ancestry::Dwarf => "dwarf", + Ancestry::Gnome => "gnome", + Ancestry::Catfolk => "catfolk", + Ancestry::NULL => "NULL", + }; + let pools = ecs.read_storage::(); + let pool = pools.get(*e).unwrap(); + let attrs = ecs.read_storage::(); + let attr = attrs.get(*e).unwrap(); + let header = format!("{} {}, level {}/{}", &ancestry, &class, &pool.level, &pool.xp); + morgue_info.push_str(&create_boxed_text(header.as_str(), None)); + morgue_info.push_str(&draw_tombstone(header.len())); + morgue_info.push_str( + format!( + "HP {}/{} MP {}/{}\n", + pool.hit_points.current, + pool.hit_points.max, + pool.mana.current, + pool.mana.max + ).as_str() ); - let file_name = format!("{}/{}-{}-{}.txt", morgue_dir, "human", "wizard", timestamp); + morgue_info.push_str(&draw_attributes(attr)); + morgue_info.push_str(&create_boxed_text("Equipment", None)); + morgue_info.push_str(&draw_equipment(ecs)); + morgue_info.push_str(&create_boxed_text("Backpack", None)); + morgue_info.push_str(&draw_backpack(ecs)); + + // Save to file + let file_name = format!("{}/lv{}-{}-{}-{}.txt", morgue_dir, &pool.level, &ancestry, &class, get_timestamp()); let mut file = File::create(&file_name)?; // Open/create morgue file file.write_all(morgue_info.as_bytes())?; Ok(()) } + +fn get_timestamp() -> String { + return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs().to_string(); +} + +fn create_boxed_text(content: &str, width: Option) -> String { + let width = if width.is_some() { width.unwrap() } else { content.len() + 2 }; + let horizontal = format!("{:═^w$}", "", w = width); + return format!("╔{h}╗\n║ {c} ║\n╚{h}╝\n", h = horizontal, c = content); +} + +fn draw_tombstone(len: usize) -> String { + let pad = (len - 17) / 2; + return format!( + "\n{:^p$} .-'~~~`-.\n{:^p$} .' `.\n{:^p$} | rest |\n{:^p$} | in |\n{:^p$} | peace |\n{:^p$}\\\\| |//\n{:^p$}^^^^^^^^^^^^^^^^^{:^p$}\n\n", + "", + "", + "", + "", + "", + "", + "", + "", + p = pad + ); +} + +fn draw_attributes(attr: &Attributes) -> String { + return format!( + "\nSTR {:>2} ({:+}) CON {:>2} ({:+}) WIS {:>2} ({:+})\nDEX {:>2} ({:+}) INT {:>2} ({:+}) CHA {:>2} ({:+})\n\n", + attr.strength.base + attr.strength.modifiers, + attr.strength.bonus, + attr.constitution.base + attr.constitution.modifiers, + attr.constitution.bonus, + attr.wisdom.base + attr.wisdom.modifiers, + attr.wisdom.bonus, + attr.dexterity.base + attr.dexterity.modifiers, + attr.dexterity.bonus, + attr.intelligence.base + attr.intelligence.modifiers, + attr.intelligence.bonus, + attr.charisma.base + attr.charisma.modifiers, + attr.charisma.bonus + ); +} + +fn draw_equipment(ecs: &World) -> String { + // Get all of the player's equipment. + let mut equip: HashMap = HashMap::new(); + let equipped = ecs.read_storage::(); + for (entity, _e, _n) in (&ecs.entities(), &equipped, &ecs.read_storage::()) + .join() + .filter(|item| item.1.owner == *ecs.fetch::()) { + equip + .entry(entity) + .and_modify(|count| { + *count += 1; + }) + .or_insert(1); + } + let mut result: String = Default::default(); + for item in equip { + let slot = match equipped.get(item.0).unwrap().slot { + EquipmentSlot::Melee => "l-hand -", + EquipmentSlot::Shield => "r-hand -", + EquipmentSlot::Head => "head -", + EquipmentSlot::Body => "body -", + EquipmentSlot::Feet => "feet -", + EquipmentSlot::Hands => "hands -", + EquipmentSlot::Back => "back -", + EquipmentSlot::Neck => "neck -", + }; + let name = if item.1 != 1 { unobf_name_ecs(ecs, item.0).1 } else { unobf_name_ecs(ecs, item.0).0 }; + result.push_str(&format!("{:>8} {}\n", slot, name)); + } + result.push_str("\n"); + return result; +} + +fn draw_backpack(ecs: &World) -> String { + // Get all of the player's backpack. + let mut pack: HashMap<(String, String), (i32, Entity)> = HashMap::new(); + for (entity, _bp, _n) in (&ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::()) + .join() + .filter(|item| item.1.owner == *ecs.fetch::()) { + pack.entry(unobf_name_ecs(ecs, entity)) + .and_modify(|(count, _e)| { + *count += 1; + }) + .or_insert((1, entity)); + } + let mut result: String = Default::default(); + for item in pack { + let name = if item.1.0 != 1 { + format!("{} {}", item.1.0, item.0.1) + } else { + // TODO: Get correct article (a/an/some) here, write a fn for it. + item.0.0 + }; + result.push_str(&format!("- {}\n", name)); + } + result.push_str("\n"); + return result; +} diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 3bd5df7..2483a88 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -17,7 +17,7 @@ macro_rules! apply_effects { for effect in $effects.iter() { let effect_name = effect.0.as_str(); match effect_name { - "healing" => { + "heal" => { let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str()); $eb = $eb.with(ProvidesHealing { n_dice, sides, modifier }) }