diff --git a/raws/items.json b/raws/items.json index 3edc981..ffb6d8d 100644 --- a/raws/items.json +++ b/raws/items.json @@ -192,7 +192,7 @@ "weight": 6, "value": 10, "flags": ["EQUIP_SHIELD"], - "effects": { "ac": "2" } + "effects": { "ac": "2", "to_hit": "-1" } }, { "id": "equip_largeshield", @@ -201,7 +201,7 @@ "weight": 12, "value": 35, "flags": ["EQUIP_SHIELD"], - "effects": { "ac": "3" } + "effects": { "ac": "3", "to_hit": "-2" } }, { "id": "equip_body_weakleather", diff --git a/src/components.rs b/src/components.rs index 7974f07..750a16b 100644 --- a/src/components.rs +++ b/src/components.rs @@ -316,6 +316,11 @@ pub struct ArmourClassBonus { pub amount: i32, } +#[derive(Component, ConvertSaveload, Clone)] +pub struct ToHitBonus { + pub amount: i32, +} + #[derive(Component, Serialize, Deserialize, Clone)] pub struct Equippable { pub slot: EquipmentSlot, diff --git a/src/gamesystem.rs b/src/gamesystem.rs index dc67d9e..f1150fa 100644 --- a/src/gamesystem.rs +++ b/src/gamesystem.rs @@ -17,7 +17,7 @@ pub fn hp_per_level(rng: &mut rltk::RandomNumberGenerator, constitution: i32) -> #[allow(dead_code)] /// Returns a total HP roll for a player, based on a given constitution score and level. pub fn player_hp_at_level(rng: &mut rltk::RandomNumberGenerator, constitution: i32, level: i32) -> i32 { - let mut total = 10 + attr_bonus(constitution); + let mut total = 8 + attr_bonus(constitution); for _i in 0..level { total += hp_per_level(rng, constitution); } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 5166ed8..ee81f60 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -489,7 +489,7 @@ pub fn obfuscate_name_ecs(ecs: &World, item: Entity) -> (String, String) { if has_beatitude.known { let prefix = match has_beatitude.buc { BUC::Cursed => Some("cursed "), - BUC::Uncursed => None, + BUC::Uncursed => Some("uncursed "), BUC::Blessed => Some("blessed "), }; if prefix.is_some() { diff --git a/src/main.rs b/src/main.rs index f237e59..9c07018 100644 --- a/src/main.rs +++ b/src/main.rs @@ -611,6 +611,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/melee_combat_system.rs b/src/melee_combat_system.rs index b3fb4dd..530680a 100644 --- a/src/melee_combat_system.rs +++ b/src/melee_combat_system.rs @@ -3,7 +3,8 @@ use super::{ gamelog, gamesystem, gui::renderable_colour, ArmourClassBonus, Attributes, EquipmentSlot, Equipped, HungerClock, HungerState, MeleeWeapon, MultiAttack, Name, - NaturalAttacks, ParticleBuilder, Pools, Position, Renderable, Skill, Skills, WantsToMelee, WeaponAttribute, + NaturalAttacks, ParticleBuilder, Pools, Position, Renderable, Skill, Skills, ToHitBonus, WantsToMelee, + WeaponAttribute, }; use rltk::prelude::*; use specs::prelude::*; @@ -26,6 +27,7 @@ impl<'a> System<'a> for MeleeCombatSystem { ReadStorage<'a, MeleeWeapon>, ReadStorage<'a, NaturalAttacks>, ReadStorage<'a, ArmourClassBonus>, + ReadStorage<'a, ToHitBonus>, ReadStorage<'a, HungerClock>, ReadStorage<'a, MultiAttack>, WriteExpect<'a, rltk::RandomNumberGenerator>, @@ -47,6 +49,7 @@ impl<'a> System<'a> for MeleeCombatSystem { melee_weapons, natural_attacks, ac, + to_hit, hunger_clock, multi_attackers, mut rng, @@ -96,10 +99,10 @@ impl<'a> System<'a> for MeleeCombatSystem { attacks.push(( MeleeWeapon { attribute: WeaponAttribute::Strength, - hit_bonus: 0, damage_n_dice: 1, damage_die_type: 4, damage_bonus: 0, + hit_bonus: 0, }, "punches".to_string(), )); @@ -124,7 +127,12 @@ impl<'a> System<'a> for MeleeCombatSystem { let d20 = rng.roll_dice(1, 20); let attribute_hit_bonus = attacker_attributes.dexterity.bonus; let skill_hit_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills); - let weapon_hit_bonus = weapon_info.hit_bonus; + let mut equipment_hit_bonus = weapon_info.hit_bonus; + for (wielded, to_hit) in (&equipped, &to_hit).join() { + if wielded.owner == entity { + equipment_hit_bonus += to_hit.amount; + } + } let mut status_hit_bonus = 0; let hc = hunger_clock.get(entity); if let Some(hc) = hc { @@ -141,8 +149,13 @@ impl<'a> System<'a> for MeleeCombatSystem { _ => {} } } - let attacker_bonuses = - attacker_pools.level + attribute_hit_bonus + skill_hit_bonus + weapon_hit_bonus + status_hit_bonus; + // Total to-hit bonus + let attacker_bonuses = 1 // +1 for being in melee combat + + attacker_pools.level // + level + + attribute_hit_bonus // +- str/dex bonus depending on weapon used + + skill_hit_bonus // +- relevant skill modifier + + equipment_hit_bonus // +- any other to-hit modifiers from equipment + + status_hit_bonus; // +- any to-hit modifiers from status effects // Get armour class let bac = target_pools.bac; @@ -164,17 +177,21 @@ impl<'a> System<'a> for MeleeCombatSystem { armour_class_roll = -armour_class_roll; } - let target_number = 10 + armour_class_roll + attacker_bonuses; + // Monster attacks receive a +10 to-hit bonus against the player. + let monster_v_player_bonus = if wants_melee.target == *player_entity { 10 } else { 0 }; + + let target_number = monster_v_player_bonus + armour_class_roll + attacker_bonuses; let target_name = names.get(wants_melee.target).unwrap(); if COMBAT_LOGGING { rltk::console::log(format!( - "ATTACKLOG: {} *{}* {}: rolled ({}) 1d20 vs. {} (10 + {}AC + {}to-hit)", + "ATTACKLOG: {} *{}* {}: rolled ({}) 1d20 vs. {} ({} + {}AC + {}to-hit)", &name.name, attack_verb, &target_name.name, d20, target_number, + monster_v_player_bonus, armour_class_roll, attacker_bonuses )); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index ee04493..4aaa0a1 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -29,6 +29,7 @@ macro_rules! apply_effects { "aoe" => $eb = $eb.with(AOE { radius: effect.1.parse::().unwrap() }), "confusion" => $eb = $eb.with(Confusion { turns: effect.1.parse::().unwrap() }), "ac" => $eb = $eb.with(ArmourClassBonus { amount: effect.1.parse::().unwrap() }), + "to_hit" => $eb = $eb.with(ToHitBonus { amount: effect.1.parse::().unwrap() }), "particle_line" => $eb = $eb.with(parse_particle_line(&effect.1)), "particle_burst" => $eb = $eb.with(parse_particle_burst(&effect.1)), "particle" => $eb = $eb.with(parse_particle(&effect.1)), @@ -230,7 +231,7 @@ pub fn spawn_named_item( ) -> Option { if raws.item_index.contains_key(key) { let item_template = &raws.raws.items[raws.item_index[key]]; - let dm = ecs.fetch::(); + let mut dm = ecs.fetch_mut::(); let scroll_names = dm.scroll_map.clone(); let potion_names = dm.potion_map.clone(); let wand_names = dm.wand_map.clone(); @@ -256,6 +257,9 @@ pub fn spawn_named_item( } _ => false, }; + if known_beatitude && !identified_items.contains(&item_template.name.name) { + dm.identified_items.insert(item_template.name.name.clone()); + } std::mem::drop(player_entity); std::mem::drop(dm); // -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT --- diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 8084d3a..59a00d2 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -110,6 +110,7 @@ pub fn save_game(ecs: &mut World) { SpawnParticleSimple, TakingTurn, Telepath, + ToHitBonus, Viewshed, Charges, WantsToApproach, @@ -234,6 +235,7 @@ pub fn load_game(ecs: &mut World) { SpawnParticleSimple, TakingTurn, Telepath, + ToHitBonus, Viewshed, Charges, WantsToApproach,