summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornsensfel <SpamShield0@noot-noot.org>2018-02-28 17:46:41 +0100
committernsensfel <SpamShield0@noot-noot.org>2018-02-28 17:46:41 +0100
commit1b59bdfc0d923a1ebfcebf4d6efceb2f2f4579a4 (patch)
treed000a796fd61b27d8031cbdf691f1be73fdb5cb0 /src/battlemap
parent5235345620c0d4a6669ccc6badc387902ea8c92a (diff)
Moved the mess from 'character_turn' into 'attack'.
Diffstat (limited to 'src/battlemap')
-rw-r--r--src/battlemap/attack.erl279
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.