| summaryrefslogtreecommitdiff | 
diff options
| author | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2018-03-02 15:43:23 +0100 | 
|---|---|---|
| committer | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2018-03-02 15:43:23 +0100 | 
| commit | dc119102df0076e45ee6c484e361c3fff3e6e2dd (patch) | |
| tree | ff9d0ad5787ceac83decf5066e9969ea01388a45 /src/battle/attack.erl | |
| parent | 45434f444962ef9c852e8122fe86ae65c9c49436 (diff) | |
Still working on it...
Diffstat (limited to 'src/battle/attack.erl')
| -rw-r--r-- | src/battle/attack.erl | 279 | 
1 files changed, 279 insertions, 0 deletions
| diff --git a/src/battle/attack.erl b/src/battle/attack.erl new file mode 100644 index 0000000..7384f78 --- /dev/null +++ b/src/battle/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. | 


