summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/battle/mechanic/btl_attack.erl')
-rw-r--r-- | src/battle/mechanic/btl_attack.erl | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/src/battle/mechanic/btl_attack.erl b/src/battle/mechanic/btl_attack.erl new file mode 100644 index 0000000..cbc1baa --- /dev/null +++ b/src/battle/mechanic/btl_attack.erl @@ -0,0 +1,554 @@ +-module(btl_attack). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec roll_precision + ( + shr_statistics:type(), + shr_statistics:type(), + integer() + ) + -> {precision(), integer(), integer()}. +roll_precision +( + AttackerStatistics, + DefenderStatistics, + DefenderLuck +) -> + DefenderDodges = shr_statistics:get_dodges(DefenderStatistics), + AttackerAccuracy = shr_statistics:get_accuracy(AttackerStatistics), + MissChance = max(0, (DefenderDodges - AttackerAccuracy)), + + {Roll, _IsSuccess, PositiveModifier, NegativeModifier} = + shr_roll:percentage_with_luck(MissChance, DefenderLuck), + + { + case Roll of + X when (X =< MissChance) -> misses; + X when (X =< (MissChance * 2)) -> grazes; + _ -> hits + end, + PositiveModifier, + NegativeModifier + }. + +-spec roll_critical_hit + ( + shr_statistics:type(), + integer() + ) + -> {boolean(), integer(), integer()}. +roll_critical_hit (AttackerStatistics, AttackerLuck) -> + CriticalHitChance = shr_statistics:get_critical_hits(AttackerStatistics), + {_Roll, IsSuccess, PositiveModifier, NegativeModifier} = + shr_roll:percentage_with_luck(CriticalHitChance, AttackerLuck), + + {IsSuccess, PositiveModifier, NegativeModifier}. + +-spec roll_parry + ( + shr_statistics:type(), + integer() + ) + -> {boolean(), integer(), integer()}. +roll_parry (DefenderStatistics, DefenderLuck) -> + DefenderParryChance = shr_statistics:get_parries(DefenderStatistics), + {_Roll, IsSuccess, PositiveModifier, NegativeModifier} = + shr_roll:percentage_with_luck(DefenderParryChance, DefenderLuck), + + {IsSuccess, PositiveModifier, NegativeModifier}. + +-spec get_damage + ( + precision(), + boolean(), + float(), + shr_omnimods:type(), + shr_omnimods:type() + ) + -> non_neg_integer(). +get_damage +( + Precision, + IsCritical, + StartingDamageMultiplier, + AttackerOmnimods, + DefenderOmnimods +) -> + ActualDamageMultiplier = + ( + StartingDamageMultiplier + * + ( + case Precision of + misses -> 0; + grazes -> 0.5; + hits -> 1 + end + ) + * + ( + case IsCritical of + true -> 2; + _ -> 1 + end + ) + ), + + ActualDamage = + shr_omnimods:get_attack_damage + ( + ActualDamageMultiplier, + AttackerOmnimods, + DefenderOmnimods + ), + + ActualDamage. + +-spec effect_of_attack + ( + order(), + shr_character:type(), + shr_character:type(), + boolean(), + integer(), + integer() + ) + -> type(). +effect_of_attack +( + Order, + Attacker, + Defender, + CanParry, + AttackerLuck, + DefenderLuck +) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%% Roll parry to see if the roles have to be swapped. %%%%%%%%%%%%%%%%%%%%% + + DefenderStats = shr_character:get_statistics(Defender), + {ParryIsSuccessful, ParryPositiveLuckMod, ParryNegativeLuckMod} = + case CanParry of + true -> roll_parry(DefenderStats, DefenderLuck); + false -> {false, 0, 0} + end, + + { + ActualAttacker, + ActualDefender, + ActualAttackerLuck, + ActualDefenderLuck + } = + case ParryIsSuccessful of + true -> {Defender, Attacker, DefenderLuck, AttackerLuck}; + false -> {Attacker, Defender, AttackerLuck, DefenderLuck} + end, + + ActualAttackerStats = shr_character:get_statistics(ActualAttacker), + ActualAttackerOmnimods = shr_character:get_omnimods(ActualAttacker), + ActualDefenderStats = shr_character:get_statistics(ActualDefender), + ActualDefenderOmnimods = shr_character:get_omnimods(ActualDefender), + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + {Precision, PrecisionPositiveLuckMod, PrecisionNegativeLuckMod} = + roll_precision + ( + ActualAttackerStats, + ActualDefenderStats, + ActualDefenderLuck + ), + + + {IsCritical, CriticalPositiveLuckMod, CriticalNegativeLuckMod} = + roll_critical_hit(ActualAttackerStats, ActualAttackerLuck), + + ActualAttackerDamageModifier = + shr_statistics:get_damage_modifier(ActualAttackerStats), + + Damage = + get_damage + ( + Precision, + IsCritical, + ActualAttackerDamageModifier, + ActualAttackerOmnimods, + ActualDefenderOmnimods + ), + + {FinalAttackerLuckMod, FinalDefenderLuckMod} = + case {ParryIsSuccessful, Precision} of + {true, misses} -> + { + ( + % Attacker wasn't the one parrying + ParryNegativeLuckMod + % Attacker was the one evading + + PrecisionPositiveLuckMod + % miss -> no critical hit luck modifier + ), + ( + % Defender was the one parrying + ParryPositiveLuckMod + % Defender wasn't the one evading + + PrecisionNegativeLuckMod + % miss -> no critical hit luck modifier + ) + }; + + {true, _} -> + { + ( + % Attacker wasn't the one parrying + ParryNegativeLuckMod + % Attacker was the one evading + + PrecisionPositiveLuckMod + % Attacker wasn't the one doing a critical + + CriticalNegativeLuckMod + ), + ( + % Defender was the one parrying + ParryPositiveLuckMod + % Defender wasn't the one evading + + PrecisionNegativeLuckMod + % Defender was the one doing a critical + + CriticalPositiveLuckMod + ) + }; + + {false, misses} -> + { + ( + % Attacker wasn't the one parrying + ParryNegativeLuckMod + % Defender was the one evading + + PrecisionNegativeLuckMod + % miss -> no critical hit luck modifier + ), + ( + % Defender was the one parrying + ParryPositiveLuckMod + % Defender was the one evading + + PrecisionPositiveLuckMod + % miss -> no critical hit luck modifier + ) + }; + + {false, _} -> + { + ( + % Attacker wasn't the one parrying + ParryNegativeLuckMod + % Attacker wasn't the one evading + + PrecisionNegativeLuckMod + % Attacker was the one doing a critical + + CriticalPositiveLuckMod + ), + ( + % Defender was the one parrying + ParryPositiveLuckMod + % Defender was the one evading + + PrecisionPositiveLuckMod + % Defender wasn't the one doing a critical + + CriticalNegativeLuckMod + ) + } + end, + + #attack + { + order = Order, + precision = Precision, + is_critical = IsCritical, + is_parry = ParryIsSuccessful, + damage = Damage, + attacker_luck_mod = FinalAttackerLuckMod, + defender_luck_mod = FinalDefenderLuckMod + }. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec get_description_of + ( + step(), + shr_character:type(), + shr_character:type(), + integer(), + integer() + ) + -> maybe_type(). +get_description_of +( + {first, CanParry}, + Attacker, + Defender, + AttackerLuck, + DefenderLuck +) -> + effect_of_attack + ( + first, + Attacker, + Defender, + CanParry, + AttackerLuck, + DefenderLuck + ); +get_description_of +( + {second, CanParry}, + Attacker, + Defender, + AttackerLuck, + DefenderLuck +) -> + AttackerStats = shr_character:get_statistics(Attacker), + AttackerDoubleAttackChance = + shr_statistics:get_double_hits(AttackerStats), + {_Roll, IsSuccessful, PositiveModifier, NegativeModifier} = + shr_roll:percentage_with_luck(AttackerDoubleAttackChance, AttackerLuck), + + NewAttackerLuck = (AttackerLuck + PositiveModifier), + NewDefenderLuck = (DefenderLuck + NegativeModifier), + + case IsSuccessful of + true -> + Result = + effect_of_attack + ( + second, + Attacker, + Defender, + CanParry, + NewAttackerLuck, + NewDefenderLuck + ), + + Result#attack + { + attacker_luck_mod = + (Result#attack.attacker_luck_mod + PositiveModifier), + defender_luck_mod = + (Result#attack.defender_luck_mod + NegativeModifier) + }; + + _ -> {nothing, PositiveModifier, NegativeModifier} + end; +get_description_of +( + {counter, CanParry}, + Attacker, + Defender, + AttackerLuck, + DefenderLuck +) -> + effect_of_attack + ( + counter, + Defender, + Attacker, + CanParry, + DefenderLuck, + AttackerLuck + ). + +-spec apply_to_healths_and_lucks + ( + maybe_type(), + non_neg_integer(), + integer(), + non_neg_integer(), + integer() + ) + -> + { + maybe_type(), + non_neg_integer(), + integer(), + non_neg_integer(), + integer() + }. +apply_to_healths_and_lucks +( + _Attack, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck +) +when +( + (AttackerHealth =< 0) + or (DefenderHealth =< 0) +) -> + { + {nothing, 0, 0}, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck + }; +apply_to_healths_and_lucks +( + {nothing, AttackerLuckMod, DefenderLuckMod}, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck +) -> + { + {nothing, AttackerLuckMod, DefenderLuckMod}, + AttackerHealth, + (AttackerLuck + AttackerLuckMod), + DefenderHealth, + (DefenderLuck + DefenderLuckMod) + }; +apply_to_healths_and_lucks +( + Attack, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck +) +when +( + ( + (not Attack#attack.is_parry) + and ((Attack#attack.order == first) or (Attack#attack.order == second)) + ) + or + ( + Attack#attack.is_parry + and (Attack#attack.order == counter) + ) +) -> + Damage = Attack#attack.damage, + + { + Attack, + AttackerHealth, + (AttackerLuck + Attack#attack.attacker_luck_mod), + (DefenderHealth - Damage), + (DefenderLuck + Attack#attack.defender_luck_mod) + }; +apply_to_healths_and_lucks +( + Attack, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck +) +when +( + ( + (not Attack#attack.is_parry) + and (Attack#attack.order == counter) + ) + or + ( + Attack#attack.is_parry + and ((Attack#attack.order == first) or (Attack#attack.order == second)) + ) +) -> + Damage = Attack#attack.damage, + + { + Attack, + (AttackerHealth - Damage), + (AttackerLuck + Attack#attack.attacker_luck_mod), + DefenderHealth, + (DefenderLuck + Attack#attack.defender_luck_mod) + }. + +-spec get_sequence + ( + non_neg_integer(), + shr_weapon:type(), + shr_weapon:type() + ) + -> list(step()). +get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) -> + AttackerDefenseRange = shr_weapon:get_minimum_range(AttackerWeapon), + AttackerAttackRange = shr_weapon:get_maximum_range(AttackerWeapon), + DefenderDefenseRange = shr_weapon:get_minimum_range(DefenderWeapon), + DefenderAttackRange = shr_weapon:get_maximum_range(DefenderWeapon), + + AttackerCanAttack = (AttackRange =< AttackerAttackRange), + AttackerCanDefend = + (AttackerCanAttack and (AttackRange > AttackerDefenseRange)), + + true = (AttackerCanAttack == true), + + DefenderCanAttack = (AttackRange =< DefenderAttackRange), + DefenderCanDefend = + (DefenderCanAttack and (AttackRange > DefenderDefenseRange)), + + First = {first, DefenderCanDefend}, + Second = {second, DefenderCanDefend}, + Counter = {counter, AttackerCanDefend}, + + case DefenderCanDefend of + true -> [First, Counter, Second]; + _ -> [First, Second] + end. + +-spec standard + ( + non_neg_integer(), + btl_character:type(), + btl_player:type(), + btl_character:type() + btl_player:type() + ) + -> + { + % Attack Descriptions, + % Updated Attacker + % Attacker Ataxia Update + % Updated Defender + % Defender Ataxia Update + } + +-spec attack_of_opportunity + ( + btl_character:type(), + btl_player:type(), + btl_character:type() + btl_player:type(), + ) + -> + { + % Attack Descriptions, + % Updated Attacker + % Attacker Ataxia Update + % Updated Attacking Player + % Attacking Player Ataxia Update + % Updated Defender + % Defender Ataxia Update + % Updated Defending Player + % Attacking Player Ataxia Update + } + +attack_of_opportunity () -> + [ + {first, false}, + {second, false} + ]. |