adds damage types and mods (weak/resist/immune), for all damage events
This commit is contained in:
parent
66013667d8
commit
8a44c94272
12 changed files with 131 additions and 10 deletions
|
|
@ -1,6 +1,7 @@
|
|||
## v0.1.4
|
||||
### added
|
||||
- **overmap**: bare, but exists. player now starts on the overworld, and can move to local maps (like the old starting town) via >. can leave local maps back to the overmap by walking out of the map boundaries.
|
||||
- **damage types**: immunities, weaknesses, and resistances
|
||||
- **full keyboard support**: examining and targeting can now be done via keyboard only
|
||||
- **a config file** read at runtime, unfortunately not compatible with WASM builds yet
|
||||
- **morgue files**: y/n prompt to write a morgue file on death to /morgue/foo.txt, or to the console for WASM builds
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@ pub enum WeaponAttribute {
|
|||
|
||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||
pub struct MeleeWeapon {
|
||||
pub damage_type: DamageType,
|
||||
pub attribute: WeaponAttribute,
|
||||
pub damage_n_dice: i32,
|
||||
pub damage_die_type: i32,
|
||||
|
|
@ -326,6 +327,7 @@ pub struct MeleeWeapon {
|
|||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NaturalAttack {
|
||||
pub name: String,
|
||||
pub damage_type: DamageType,
|
||||
pub damage_n_dice: i32,
|
||||
pub damage_die_type: i32,
|
||||
pub damage_bonus: i32,
|
||||
|
|
@ -365,8 +367,55 @@ pub struct ProvidesHealing {
|
|||
pub modifier: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum DamageType {
|
||||
Physical,
|
||||
Magic,
|
||||
Forced, // Bypasses any immunities. e.g. Hunger ticks.
|
||||
}
|
||||
|
||||
impl DamageType {
|
||||
pub fn is_magic(&self) -> bool {
|
||||
match self {
|
||||
DamageType::Magic => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum DamageModifier {
|
||||
None,
|
||||
Weakness,
|
||||
Resistance,
|
||||
Immune,
|
||||
}
|
||||
|
||||
impl DamageModifier {
|
||||
pub fn multiplier(&self) -> f32 {
|
||||
match self {
|
||||
DamageModifier::None => 1.0,
|
||||
DamageModifier::Weakness => 10.0,
|
||||
DamageModifier::Resistance => 0.5,
|
||||
DamageModifier::Immune => 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct HasDamageModifiers {
|
||||
pub modifiers: HashMap<DamageType, DamageModifier>,
|
||||
}
|
||||
|
||||
impl HasDamageModifiers {
|
||||
pub fn modifier(&self, damage_type: &DamageType) -> &DamageModifier {
|
||||
self.modifiers.get(damage_type).unwrap_or(&DamageModifier::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct InflictsDamage {
|
||||
pub damage_type: DamageType,
|
||||
pub n_dice: i32,
|
||||
pub sides: i32,
|
||||
pub modifier: i32,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
HungerClock,
|
||||
HungerState,
|
||||
Bleeds,
|
||||
HasDamageModifiers,
|
||||
};
|
||||
use crate::gui::with_article;
|
||||
use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME };
|
||||
|
|
@ -27,8 +28,15 @@ pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
|
|||
let mut pools = ecs.write_storage::<Pools>();
|
||||
if let Some(target_pool) = pools.get_mut(target) {
|
||||
if !target_pool.god {
|
||||
if let EffectType::Damage { amount } = damage.effect_type {
|
||||
target_pool.hit_points.current -= amount;
|
||||
if let EffectType::Damage { amount, damage_type } = damage.effect_type {
|
||||
let mult = if
|
||||
let Some(modifiers) = ecs.read_storage::<HasDamageModifiers>().get(target)
|
||||
{
|
||||
modifiers.modifier(&damage_type).multiplier()
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
target_pool.hit_points.current -= ((amount as f32) * mult) as i32;
|
||||
let bleeders = ecs.read_storage::<Bleeds>();
|
||||
if let Some(bleeds) = bleeders.get(target) {
|
||||
add_effect(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use bracket_lib::prelude::*;
|
|||
use specs::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Mutex;
|
||||
use crate::components::DamageType;
|
||||
|
||||
mod damage;
|
||||
mod hunger;
|
||||
|
|
@ -24,6 +25,7 @@ lazy_static! {
|
|||
pub enum EffectType {
|
||||
Damage {
|
||||
amount: i32,
|
||||
damage_type: DamageType,
|
||||
},
|
||||
Healing {
|
||||
amount: i32,
|
||||
|
|
|
|||
|
|
@ -246,7 +246,11 @@ fn handle_damage(
|
|||
if let Some(damage_item) = ecs.read_storage::<InflictsDamage>().get(event.entity) {
|
||||
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||
let roll = rng.roll_dice(damage_item.n_dice, damage_item.sides) + damage_item.modifier;
|
||||
add_effect(event.source, EffectType::Damage { amount: roll }, event.target.clone());
|
||||
add_effect(
|
||||
event.source,
|
||||
EffectType::Damage { amount: roll, damage_type: damage_item.damage_type },
|
||||
event.target.clone()
|
||||
);
|
||||
for target in get_entity_targets(&event.target) {
|
||||
if ecs.read_storage::<Prop>().get(target).is_some() {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use super::{
|
|||
HungerClock,
|
||||
HungerState,
|
||||
TakingTurn,
|
||||
DamageType,
|
||||
};
|
||||
use bracket_lib::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
|
@ -78,7 +79,7 @@ impl<'a> System<'a> for HungerSystem {
|
|||
if hunger_clock.state == HungerState::Starving {
|
||||
add_effect(
|
||||
None,
|
||||
EffectType::Damage { amount: 1 },
|
||||
EffectType::Damage { amount: 1, damage_type: DamageType::Forced },
|
||||
Targets::Entity { target: entity }
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ fn main() -> BError {
|
|||
gs.ecs.register::<SpawnParticleSimple>();
|
||||
gs.ecs.register::<SpawnParticleBurst>();
|
||||
gs.ecs.register::<SpawnParticleLine>();
|
||||
gs.ecs.register::<HasDamageModifiers>();
|
||||
gs.ecs.register::<SimpleMarker<SerializeMe>>();
|
||||
gs.ecs.register::<SerializationHelper>();
|
||||
gs.ecs.register::<DMSerializationHelper>();
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
} else {
|
||||
attacks.push((
|
||||
MeleeWeapon {
|
||||
damage_type: crate::DamageType::Physical,
|
||||
attribute: WeaponAttribute::Strength,
|
||||
damage_n_dice: 1,
|
||||
damage_die_type: 4,
|
||||
|
|
@ -301,7 +302,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
}
|
||||
add_effect(
|
||||
Some(entity),
|
||||
EffectType::Damage { amount: damage },
|
||||
EffectType::Damage { amount: damage, damage_type: weapon_info.damage_type },
|
||||
Targets::Entity { target: wants_melee.target }
|
||||
);
|
||||
if entity == *player_entity {
|
||||
|
|
@ -392,6 +393,7 @@ fn get_natural_attacks(
|
|||
for a in nat.attacks.iter() {
|
||||
attacks.push((
|
||||
MeleeWeapon {
|
||||
damage_type: a.damage_type,
|
||||
attribute: WeaponAttribute::Strength,
|
||||
hit_bonus: a.hit_bonus,
|
||||
damage_n_dice: a.damage_n_dice,
|
||||
|
|
@ -409,6 +411,7 @@ fn get_natural_attacks(
|
|||
};
|
||||
attacks.push((
|
||||
MeleeWeapon {
|
||||
damage_type: nat.attacks[attack_index].damage_type,
|
||||
attribute: WeaponAttribute::Strength,
|
||||
hit_bonus: nat.attacks[attack_index].hit_bonus,
|
||||
damage_n_dice: nat.attacks[attack_index].damage_n_dice,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use super::{
|
|||
WantsToPickupItem,
|
||||
get_dest,
|
||||
Destination,
|
||||
DamageType,
|
||||
};
|
||||
use bracket_lib::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
|
@ -290,7 +291,7 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
|||
if rng.roll_dice(1, 20) == 20 {
|
||||
add_effect(
|
||||
None,
|
||||
EffectType::Damage { amount: 1 },
|
||||
EffectType::Damage { amount: 1, damage_type: DamageType::Physical },
|
||||
Targets::Entity { target: entity }
|
||||
);
|
||||
gamelog::Logger
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ macro_rules! apply_effects {
|
|||
}
|
||||
"ranged" => $eb = $eb.with(Ranged { range: effect.1.parse::<i32>().unwrap() }),
|
||||
"damage" => {
|
||||
let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str());
|
||||
$eb = $eb.with(InflictsDamage { n_dice, sides, modifier })
|
||||
let (damage_type, dice) = parse_damage_string(effect.1.as_str());
|
||||
$eb = $eb.with(InflictsDamage { damage_type, n_dice: dice.0, sides: dice.1, modifier: dice.2 })
|
||||
}
|
||||
"aoe" => $eb = $eb.with(AOE { radius: effect.1.parse::<i32>().unwrap() }),
|
||||
"confusion" => $eb = $eb.with(Confusion { turns: effect.1.parse::<i32>().unwrap() }),
|
||||
|
|
@ -45,6 +45,7 @@ macro_rules! apply_effects {
|
|||
/// flags are components that have no parameters to modify.
|
||||
macro_rules! apply_flags {
|
||||
($flags:expr, $eb:expr) => {
|
||||
let mut damage_modifiers: HashMap<DamageType, DamageModifier> = HashMap::new();
|
||||
for flag in $flags.iter() {
|
||||
match flag.as_str() {
|
||||
// --- PROP FLAGS BEGIN HERE ---
|
||||
|
|
@ -89,6 +90,13 @@ macro_rules! apply_flags {
|
|||
"NEUTRAL" => $eb = $eb.with(Faction { name: "neutral".to_string() }),
|
||||
"HERBIVORE" => $eb = $eb.with(Faction { name: "herbivore".to_string() }),
|
||||
"CARNIVORE" => $eb = $eb.with(Faction { name: "carnivore".to_string() }),
|
||||
// --- DAMAGE MODIFIERS ---
|
||||
"MAGIC_IMMUNITY" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Immune); }
|
||||
"MAGIC_WEAKNESS" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Weakness); }
|
||||
"MAGIC_RESISTANCE" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Resistance); }
|
||||
"PHYSICAL_IMMUNITY" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Immune); }
|
||||
"PHYSICAL_WEAKNESS" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Weakness); }
|
||||
"PHYSICAL_RESISTANCE" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Resistance); }
|
||||
// --- MOVEMENT MODES --- ( defaults to WANDER )
|
||||
"STATIC" => $eb = $eb.with(MoveMode { mode: Movement::Static }),
|
||||
"RANDOM_PATH" => $eb = $eb.with(MoveMode { mode: Movement::RandomWaypoint { path: None } }),
|
||||
|
|
@ -102,6 +110,9 @@ macro_rules! apply_flags {
|
|||
_ => console::log(format!("Unrecognised flag: {}", flag.as_str())),
|
||||
}
|
||||
}
|
||||
if damage_modifiers.len() > 0 {
|
||||
$eb = $eb.with(HasDamageModifiers { modifiers: damage_modifiers });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -340,13 +351,16 @@ pub fn spawn_named_item(
|
|||
}
|
||||
|
||||
if let Some(weapon) = &item_template.equip {
|
||||
let (n_dice, die_type, bonus) = parse_dice_string(weapon.damage.as_str());
|
||||
let (damage_type, (n_dice, die_type, bonus)) = parse_damage_string(
|
||||
weapon.damage.as_str()
|
||||
);
|
||||
let weapon_attribute = match weapon.flag.as_str() {
|
||||
"DEXTERITY" => WeaponAttribute::Dexterity,
|
||||
"FINESSE" => WeaponAttribute::Finesse,
|
||||
_ => WeaponAttribute::Strength,
|
||||
};
|
||||
let wpn = MeleeWeapon {
|
||||
damage_type,
|
||||
attribute: weapon_attribute,
|
||||
damage_n_dice: n_dice,
|
||||
damage_die_type: die_type,
|
||||
|
|
@ -526,9 +540,10 @@ pub fn spawn_named_mob(
|
|||
if let Some(natural_attacks) = &mob_template.attacks {
|
||||
let mut natural = NaturalAttacks { attacks: Vec::new() };
|
||||
for na in natural_attacks.iter() {
|
||||
let (n, d, b) = parse_dice_string(&na.damage);
|
||||
let (damage_type, (n, d, b)) = parse_damage_string(&na.damage);
|
||||
let attack = NaturalAttack {
|
||||
name: na.name.clone(),
|
||||
damage_type,
|
||||
hit_bonus: na.hit_bonus,
|
||||
damage_n_dice: n,
|
||||
damage_die_type: d,
|
||||
|
|
@ -1039,3 +1054,18 @@ fn parse_particle_burst(n: &str) -> SpawnParticleBurst {
|
|||
trail_lifetime_ms: tokens[7].parse::<f32>().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_damage_string(n: &str) -> (DamageType, (i32, i32, i32)) {
|
||||
let tokens: Vec<_> = n.split(';').collect();
|
||||
let damage_type = if tokens.len() > 1 {
|
||||
match tokens[1] {
|
||||
"physical" => DamageType::Physical,
|
||||
"magic" => DamageType::Magic,
|
||||
_ => panic!("Unrecognised damage type in raws: {}", tokens[1]),
|
||||
}
|
||||
} else {
|
||||
DamageType::Physical
|
||||
};
|
||||
let dice = parse_dice_string(tokens[0]);
|
||||
return (damage_type, dice);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ pub fn save_game(ecs: &mut World) {
|
|||
GrantsXP,
|
||||
HasAncestry,
|
||||
HasClass,
|
||||
HasDamageModifiers,
|
||||
Hidden,
|
||||
HungerClock,
|
||||
IdentifiedBeatitude,
|
||||
|
|
@ -218,6 +219,7 @@ pub fn load_game(ecs: &mut World) {
|
|||
GrantsXP,
|
||||
HasAncestry,
|
||||
HasClass,
|
||||
HasDamageModifiers,
|
||||
Hidden,
|
||||
HungerClock,
|
||||
IdentifiedBeatitude,
|
||||
|
|
|
|||
19
tests/components_test.rs
Normal file
19
tests/components_test.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// tests/components_test.rs
|
||||
use rust_rl::components::*;
|
||||
|
||||
#[test]
|
||||
fn damagetype_equality() {
|
||||
let dt1 = DamageType::Physical;
|
||||
let dt2 = DamageType::Physical;
|
||||
assert_eq!(dt1, dt2);
|
||||
let dt3 = DamageType::Magic;
|
||||
assert_ne!(dt1, dt3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn damagetype_ismagic() {
|
||||
let dt1 = DamageType::Physical;
|
||||
let dt2 = DamageType::Magic;
|
||||
assert!(!dt1.is_magic());
|
||||
assert!(dt2.is_magic());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue