summaryrefslogtreecommitdiff |
diff options
author | nsensfel <SpamShield0@noot-noot.org> | 2018-02-28 17:46:41 +0100 |
---|---|---|
committer | nsensfel <SpamShield0@noot-noot.org> | 2018-02-28 17:46:41 +0100 |
commit | 1b59bdfc0d923a1ebfcebf4d6efceb2f2f4579a4 (patch) | |
tree | d000a796fd61b27d8031cbdf691f1be73fdb5cb0 /src/battlemap/attack.erl | |
parent | 5235345620c0d4a6669ccc6badc387902ea8c92a (diff) |
Moved the mess from 'character_turn' into 'attack'.
Diffstat (limited to 'src/battlemap/attack.erl')
-rw-r--r-- | src/battlemap/attack.erl | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/battlemap/attack.erl b/src/battlemap/attack.erl new file mode 100644 index 0000000..7384f78 --- /dev/null +++ b/src/battlemap/attack.erl @@ -0,0 +1,279 @@ +-module(attack). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% TODO: find better names for those types. +-type hits() :: ('misses' | 'grazes' | 'hits'). +-type critical() :: ('critical' | 'basic'). +-type attack_order() :: 'first' | 'second' | 'counter'. +-type attack_order_with_parry() :: {attack_order(), boolean()}. +-type attack_category() :: + ( + attack_order() + | {attack_order(), 'parry'} + ). +-type attack_effect() :: {hits(), critical(), non_neg_integer()}. +-type attack_desc() :: + ( + {attack_category(), attack_effect()} + | 'nothing' + ). + +-export_type +( + [ + hits/0, + critical/0, + attack_category/0, + attack_effect/0, + attack_desc/0 + ] +). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + get_sequence/3, + get_description_of/3, + apply_to_healths/3 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec roll_hits + ( + statistics:struct(), + statistics:struct() + ) + -> hits(). +roll_hits (AttackerStatistics, DefenderStatistics) -> + DefenderDodges = statistics:get_dodges(DefenderStatistics), + AttackerAccuracy = statistics:get_accuracy(AttackerStatistics), + MissChance = max(0, (DefenderDodges - AttackerAccuracy)), + case roll:percentage() of + X when (X =< MissChance) -> misses; + X when (X =< (MissChance * 2)) -> grazes; + _ -> hits + end. + +-spec roll_damage + ( + statistics:struct(), + statistics:struct() + ) + -> {critical(), non_neg_integer()}. +roll_damage (AttackerStatistics, _DefenderStatistics) -> + {MinimumDamage, MaximumDamage} = statistics:get_damages(AttackerStatistics), + MaximumRoll = max(1, MaximumDamage - MinimumDamage), + BaseDamage = MinimumDamage + (rand:uniform(MaximumRoll) - 1), + CriticalHitChance = statistics:get_critical_hits(AttackerStatistics), + case roll:percentage() of + X when (X =< CriticalHitChance) -> {critical, (BaseDamage * 2)}; + _ -> {basic, BaseDamage} + end. + +-spec effect_of_attack + ( + statistics:struct(), + statistics:struct() + ) + -> attack_effect(). +effect_of_attack (AttackerStatistics, DefenderStatistics) -> + Hits = roll_hits(AttackerStatistics, DefenderStatistics), + {Critical, Damage} = roll_damage(AttackerStatistics, DefenderStatistics), + case Hits of + misses -> {Hits, Critical, 0}; + grazes -> {Hits, Critical, trunc(Damage / 2)}; + hits -> {Hits, Critical, Damage} + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec get_description_of + ( + attack_order_with_parry(), + statistics:struct(), + statistics:struct() + ) + -> attack_desc(). +get_description_of +( + {first, DefenderCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + DefenderParryChance = statistics:get_parries(DefenderStatistics), + ParryRoll = + case DefenderCanParry of + true -> roll:percentage(); + _ -> 101 + end, + if + (ParryRoll =< DefenderParryChance) -> + { + {first, parry}, + effect_of_attack(DefenderStatistics, AttackerStatistics) + }; + + true -> + {first, effect_of_attack(AttackerStatistics, DefenderStatistics)} + end; +get_description_of +( + {second, DefenderCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + DefenderParryChance = statistics:get_parries(DefenderStatistics), + ParryRoll = + case DefenderCanParry of + true -> roll:percentage(); + _ -> 101 + end, + AttackerDoubleAttackChange = statistics:get_double_hits(AttackerStatistics), + DoubleAttackRoll = roll:percentage(), + if + (DoubleAttackRoll > AttackerDoubleAttackChange) -> + nothing; + + (ParryRoll =< DefenderParryChance) -> + { + {second, parry}, + effect_of_attack(DefenderStatistics, AttackerStatistics) + }; + + true -> + {second, effect_of_attack(AttackerStatistics, DefenderStatistics)} + end; +get_description_of +( + {counter, AttackerCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + AttackerParryChance = statistics:get_parries(AttackerStatistics), + ParryRoll = + case AttackerCanParry of + true -> roll:percentage(); + _ -> 101 + end, + if + (ParryRoll =< AttackerParryChance) -> + { + {counter, parry}, + effect_of_attack(AttackerStatistics, DefenderStatistics) + }; + + true -> + {counter, effect_of_attack(DefenderStatistics, AttackerStatistics)} + end. + +-spec apply_to_healths + ( + attack_desc(), + non_neg_integer(), + non_neg_integer() + ) + -> {attack_desc(), non_neg_integer(), non_neg_integer()}. +apply_to_healths +( + nothing, + AttackerHealth, + DefenderHealth +) -> + {nothing, AttackerHealth, DefenderHealth}; +apply_to_healths +( + {Attack, Effect}, + AttackerHealth, + DefenderHealth +) +when +( + (Attack == first) + or (Attack == second) + or (Attack == {counter, parry}) +) -> + {_Hits, _Critical, Damage} = Effect, + case AttackerHealth of + 0 -> + {nothing, AttackerHealth, DefenderHealth}; + + _ -> + { + {Attack, Effect}, + AttackerHealth, + max(0, (DefenderHealth - Damage)) + } + end; +apply_to_healths +( + {Attack, Effect}, + AttackerHealth, + DefenderHealth +) +when +( + (Attack == {first, parry}) + or (Attack == {second, parry}) + or (Attack == counter) +) -> + {_Hits, _Critical, Damage} = Effect, + case DefenderHealth of + 0 -> + {nothing, AttackerHealth, DefenderHealth}; + + _ -> + { + {Attack, Effect}, + max(0, (AttackerHealth - Damage)), + DefenderHealth + } + end. + +-spec get_sequence + ( + non_neg_integer(), + weapon:struct(), + weapon:struct() + ) + -> list(attack_order_with_parry()). +get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) -> + {AttackerDefenseRange, AttackerAttackRange} = + weapon:get_ranges(AttackerWeapon), + {DefenderDefenseRange, DefenderAttackRange} = + weapon:get_ranges(DefenderWeapon), + + AttackerCanAttack = (AttackRange =< AttackerAttackRange), + AttackerCanDefend = + (AttackerCanAttack and (AttackRange > AttackerDefenseRange)), + AttackerCanParry = + (AttackerCanDefend and weapon:can_parry(AttackerWeapon)), + + DefenderCanAttack = (AttackRange =< DefenderAttackRange), + DefenderCanDefend = + (DefenderCanAttack and (AttackRange > DefenderDefenseRange)), + DefenderCanParry = + (DefenderCanDefend and weapon:can_parry(DefenderWeapon)), + + First = {first, DefenderCanParry}, + Second = {second, DefenderCanParry}, + Counter = {counter, AttackerCanParry}, + + if + (not AttackerCanAttack) -> + []; + + (not DefenderCanDefend) -> + [First, Second]; + + true -> + [First, Counter, Second] + end. |