diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index fa27c81..520d73b 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -78,10 +78,12 @@ pub fn spell_trigger(creator: Option, spell: Entity, targets: &Targets, } } event_trigger(creator, spell, &targeting, ecs); - if self_destruct && creator.is_some() { - ecs.entities() - .delete(creator.unwrap()) - .expect("Unable to delete owner"); + if self_destruct { + if let Some(creator) = creator { + ecs.entities() + .delete(creator) + .expect("Unable to delete owner"); + } } } diff --git a/src/raws.rs b/src/raws.rs index 83d3655..0436e57 100644 --- a/src/raws.rs +++ b/src/raws.rs @@ -6,6 +6,7 @@ mod prop_structs; mod rawmaster; mod spawn_table_structs; mod spell_structs; +mod weapon_traits; use std::sync::Mutex; @@ -19,6 +20,7 @@ use prop_structs::*; pub use rawmaster::*; use spawn_table_structs::*; pub use spell_structs::Spell; +pub use weapon_traits::*; #[derive(Deserialize, Default, Debug)] pub struct Raws { @@ -29,6 +31,7 @@ pub struct Raws { pub loot_tables: Vec, pub faction_table: Vec, pub spells: Vec, + pub weapon_traits: Vec, } embedded_resource!(RAW_FILE, "../raws/spawns.json"); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 66b8f80..964157c 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -5,11 +5,11 @@ use ::rltk::{console, RandomNumberGenerator, RGB}; use ::specs::prelude::*; use ::specs::saveload::{MarkedBuilder, SimpleMarker}; +use super::{Raws, Reaction, SpawnTableEntry, RAWS}; use crate::components::*; use crate::gamesystem::{mana_at_level, npc_hp}; use crate::map::MasterDungeonMap; use crate::random_table::{MasterTable, RandomTable}; -use crate::raws::{Raws, Reaction, RAWS}; pub fn parse_dice_string(dice: &str) -> (i32, i32, i32) { lazy_static! { @@ -51,15 +51,148 @@ pub struct RawMaster { spell_index: HashMap, } +struct NewMagicItem { + name: String, + bonus: i32, +} + impl RawMaster { pub fn empty() -> RawMaster { RawMaster::default() } + fn append_magic_template(items_to_build: &mut Vec, item: &super::Item) { + if let Some(template) = &item.template_magic { + if item.weapon.is_some() || item.wearable.is_some() { + if template.include_cursed { + items_to_build.push(NewMagicItem { + name: item.name.clone(), + bonus: -1, + }); + } + + for bonus in template.bonus_min..=template.bonus_max { + items_to_build.push(NewMagicItem { + name: item.name.clone(), + bonus, + }); + } + } else { + console::log(format!( + "{} is marked as templated, but isn't a weapon or armor.", + item.name + )); + } + } + } + + fn build_base_magic_item(&self, nmw: &NewMagicItem) -> super::Item { + let base_item_index = self.item_index[&nmw.name]; + let mut base_item_copy = self.raws.items[base_item_index].clone(); + base_item_copy.vendor_category = None; // Don't sell magic items! + + if nmw.bonus == -1 { + base_item_copy.name = format!("{} -1", nmw.name); + } else { + base_item_copy.name = format!("{} +{}", nmw.name, nmw.bonus); + } + + base_item_copy.magic = Some(super::MagicItem { + class: match nmw.bonus { + 2 | 3 | 4 => "rare", + 5 => "legendary", + _ => "common", + } + .to_string(), + naming: base_item_copy + .template_magic + .as_ref() + .unwrap() + .unidentified_name + .clone(), + cursed: if nmw.bonus == -1 { Some(true) } else { None }, + }); + + if let Some(initiative_penalty) = base_item_copy.initiative_penalty.as_mut() { + *initiative_penalty -= nmw.bonus as f32; + } + if let Some(base_value) = base_item_copy.base_value.as_mut() { + *base_value += (nmw.bonus as f32 + 1.0) * 50.0; + } + if let Some(mut weapon) = base_item_copy.weapon.as_mut() { + weapon.hit_bonus += nmw.bonus; + let (n, die, plus) = parse_dice_string(&weapon.base_damage); + let final_bonus = plus + nmw.bonus; + if final_bonus > 0 { + weapon.base_damage = format!("{}d{}+{}", n, die, final_bonus); + } else { + weapon.base_damage = format!("{}d{}-{}", n, die, i32::abs(final_bonus)); + } + } + if let Some(mut armor) = base_item_copy.wearable.as_mut() { + armor.armor_class += nmw.bonus as f32; + } + + base_item_copy + } + + fn build_magic_weapon_or_armor(&mut self, items_to_build: &[NewMagicItem]) { + for nmw in items_to_build.iter() { + let base_item_copy = self.build_base_magic_item(nmw); + + let real_name = base_item_copy.name.clone(); + self.raws.items.push(base_item_copy); + self.item_index + .insert(real_name.clone(), self.raws.items.len() - 1); + + self.raws.spawn_table.push(SpawnTableEntry { + name: real_name.clone(), + weight: 10 - i32::abs(nmw.bonus), + min_depth: 1 + i32::abs((nmw.bonus - 1) * 3), + max_depth: 100, + add_map_depth_to_weight: None, + }); + } + } + + fn build_traited_weapons(&mut self, items_to_build: &[NewMagicItem]) { + items_to_build + .iter() + .filter(|i| i.bonus > 0) + .for_each(|nmw| { + for wt in self.raws.weapon_traits.iter() { + let mut base_item_copy = self.build_base_magic_item(nmw); + if let Some(mut weapon) = base_item_copy.weapon.as_mut() { + base_item_copy.name = format!("{} {}", wt.name, base_item_copy.name); + if let Some(base_value) = base_item_copy.base_value.as_mut() { + *base_value *= 2.0; + } + weapon.proc_chance = Some(0.25); + weapon.proc_effects = Some(wt.effects.clone()); + + let real_name = base_item_copy.name.clone(); + self.raws.items.push(base_item_copy); + self.item_index + .insert(real_name.clone(), self.raws.items.len() - 1); + + self.raws.spawn_table.push(SpawnTableEntry { + name: real_name.clone(), + weight: 9 - i32::abs(nmw.bonus), + min_depth: 2 + i32::abs((nmw.bonus - 1) * 3), + max_depth: 100, + add_map_depth_to_weight: None, + }); + } + } + }); + } + pub fn load(&mut self, raws: Raws) { self.raws = raws; self.item_index = HashMap::new(); let mut used_names: HashSet = HashSet::new(); + let mut items_to_build: Vec = Vec::new(); + for (i, item) in self.raws.items.iter().enumerate() { if used_names.contains(&item.name) { console::log(format!( @@ -69,6 +202,8 @@ impl RawMaster { } self.item_index.insert(item.name.clone(), i); used_names.insert(item.name.clone()); + + RawMaster::append_magic_template(&mut items_to_build, item); } for (i, mob) in self.raws.mobs.iter().enumerate() { if used_names.contains(&mob.name) { @@ -94,7 +229,7 @@ impl RawMaster { for spawn in self.raws.spawn_table.iter() { if !used_names.contains(&spawn.name) { console::log(format!( - "WARNING - Spawn tables references unspecified entity {}", + "WARNING - Spawn tables references unspecified entity [{}]", spawn.name )); } @@ -122,6 +257,9 @@ impl RawMaster { for (i, spell) in self.raws.spells.iter().enumerate() { self.spell_index.insert(spell.name.clone(), i); } + + self.build_magic_weapon_or_armor(&items_to_build); + self.build_traited_weapons(&items_to_build); } } @@ -813,8 +951,6 @@ pub fn spawn_named_spell(raws: &RawMaster, ecs: &mut World, key: &str) -> Option } pub fn get_spawn_table_for_depth(raws: &RawMaster, depth: i32) -> MasterTable { - use super::SpawnTableEntry; - let available_options: Vec<&SpawnTableEntry> = raws .raws .spawn_table diff --git a/src/raws/weapon_traits.rs b/src/raws/weapon_traits.rs new file mode 100644 index 0000000..9c23a60 --- /dev/null +++ b/src/raws/weapon_traits.rs @@ -0,0 +1,9 @@ +use std::collections::HashMap; + +use ::serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct WeaponTrait { + pub name: String, + pub effects: HashMap, +}