| summaryrefslogtreecommitdiff | 
diff options
| -rw-r--r-- | src/battle/mechanic/btl_attack.erl | 554 | ||||
| -rw-r--r-- | src/battle/mechanic/btl_turn_actions_management.erl | 50 | ||||
| -rw-r--r-- | src/battle/reply/btl_add_char.erl | 2 | ||||
| -rw-r--r-- | src/battle/struct/btl_action.erl | 102 | ||||
| -rw-r--r-- | src/battle/struct/btl_attack.erl | 28 | ||||
| -rw-r--r-- | src/battle/struct/btl_battle.erl | 112 | ||||
| -rw-r--r-- | src/battle/struct/btl_character.erl | 20 | ||||
| -rw-r--r-- | src/battle/struct/btl_character_turn_request.erl | 14 | ||||
| -rw-r--r-- | src/battle/struct/btl_character_turn_update.erl | 12 | ||||
| -rw-r--r-- | src/shared/struct/shr_character.erl | 7 | 
10 files changed, 767 insertions, 134 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} +   ]. diff --git a/src/battle/mechanic/btl_turn_actions_management.erl b/src/battle/mechanic/btl_turn_actions_management.erl index eefe812..0981ab4 100644 --- a/src/battle/mechanic/btl_turn_actions_management.erl +++ b/src/battle/mechanic/btl_turn_actions_management.erl @@ -37,15 +37,6 @@ deactivate_character (Update) ->     S1Update. --spec main_character_is_alive -   ( -      btl_character_turn_update:type() -   ) -   -> {boolean(), btl_character_turn_update:type()}. -main_character_is_alive (Update) -> -   {S0Update, MainCharacter} = btl_character_turn_update:get_character(Update), -   {btl_character:get_is_alive(MainCharacter), S0Update}. -  -spec handle_actions     (        list(btl_action:type()), @@ -54,8 +45,45 @@ main_character_is_alive (Update) ->     -> btl_character_turn_update:type().  handle_actions ([], Update) -> Update;  handle_actions ([BattleAction|FutureBattleActions], Update) -> -   {MainCharacterIsAlive, S0Update} = main_character_is_alive(Update), - +   case btl_action:get_actor_index(BattleAction) of +      -1 -> handle_actions(FutureBattleActions, S0Update); +      CharacterIX -> +         S0Battle = btl_character_turn_update:get_battle(S0Update), +         {Character, S1Battle} = +            btl_battle:get_resolved_character(CharacterIX, S0Battle), + +         S1Update = btl_character_turn_update:set_battle(S1Battle, S0Update), + +         case btl_character:is_alive(Character) of +            false -> handle_actions(FutureBattleActions, S1Update); +            true -> +               ActionResult = +                  case btl_action:get_category(BattleAction) of +                     move -> +                        btl_event_move:handle +                        ( +                           BattleAction, +                           Character, +                           S1Update +                        ); + +                     attack -> +                        case  +                        btl_event_attack:handle +                        ( +                           BattleAction, +                           Character, +                           S1Update +                        ); + +                     attack_of_opportunity -> +                        btl_event_attack_of_opportunity:handle +                        ( +                           BattleAction, +                           Character, +                           S1Update +                        ); +   end.     ActionResult =        case {MainCharacterIsAlive, btl_action:get_category(BattleAction)} of           {false, _} -> {ok, S0Update}; diff --git a/src/battle/reply/btl_add_char.erl b/src/battle/reply/btl_add_char.erl index d2fe255..556975c 100644 --- a/src/battle/reply/btl_add_char.erl +++ b/src/battle/reply/btl_add_char.erl @@ -26,7 +26,7 @@ rank_to_string (Rank) ->  -spec generate     (        non_neg_integer(), -      btl_character:type(), +      btl_character:either(),        non_neg_integer()     )     -> {list(any())}. diff --git a/src/battle/struct/btl_action.erl b/src/battle/struct/btl_action.erl index 52f41d8..174a063 100644 --- a/src/battle/struct/btl_action.erl +++ b/src/battle/struct/btl_action.erl @@ -7,15 +7,8 @@  (     move,     { +      actor_ix :: non_neg_integer(),        path :: list(shr_direction:enum()) -   } -). - --record -( -   interrupted_move, -   { -      path :: list(shr_direction:enum()),        movement_points :: non_neg_integer()     }  ). @@ -24,6 +17,7 @@  (     switch_weapon,     { +      actor_ix :: non_neg_integer()     }  ). @@ -31,35 +25,25 @@  (     attack,     { -      target_ix :: non_neg_integer() -   } -). - --record -( -   defend, -   { -      target_ix :: non_neg_integer() +      actor_ix :: non_neg_integer(), +      target_ix :: non_neg_integer(), +      is_opportunistic :: boolean()     }  ). -  -type category() ::     (        'move' -      | 'interrupted_move'        | 'switch_weapon'        | 'attack' -      | 'defend'        | 'nothing'     ).  -opaque type() ::     (        #move{} -      | #interrupted_move{}        | #switch_weapon{}        | #attack{} -      | #defend{} +      | #attack_of_opportunity{}     ).  -export_type([category/0, type/0]). @@ -71,9 +55,9 @@  (     [        from_map_marker/2, -      maybe_decode_move/1, -      maybe_decode_weapon_switch/1, -      maybe_decode_attack/1, +      maybe_decode_move/2, +      maybe_decode_weapon_switch/2, +      maybe_decode_attack/2,        can_follow/2     ]  ). @@ -90,7 +74,8 @@     [        get_path/1,        get_movement_points/1, -      get_target_ix/1, +      get_target_index/1, +      get_actor_index/1,        get_category/1     ]  ). @@ -102,20 +87,37 @@  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec maybe_decode_move (list(shr_direction:type())) -> list(type()). -maybe_decode_move ([]) -> []; -maybe_decode_move (PathInBinary) -> +-spec maybe_decode_move +   ( +      non_neg_integer(), +      list(shr_direction:type()) +   ) +   -> list(type()). +maybe_decode_move (_CharacterIX, []) -> []; +maybe_decode_move (CharacterIX, PathInBinary) ->     Path = lists:map(fun shr_direction:decode/1, PathInBinary), -   [#move{ path = Path }]. +   [#move{ actor_ix = CharacterIX, path = Path }]. --spec maybe_decode_attack (integer()) -> list(type()). -maybe_decode_attack (TargetIX) when (TargetIX < 0) -> []; -maybe_decode_attack (TargetIX) -> [#attack{ target_ix = TargetIX }]. +-spec maybe_decode_attack +   ( +      non_neg_integer(), +      integer() +   ) +   -> list(type()). +maybe_decode_attack (_CharacterIX, TargetIX) when (TargetIX < 0) -> []; +maybe_decode_attack (CharacterIX, TargetIX) -> +   [#attack{ actor_ix = CharacterIX, target_ix = TargetIX }]. --spec maybe_decode_weapon_switch (boolean()) -> list(type()). -maybe_decode_weapon_switch (false) -> []; -maybe_decode_weapon_switch (true) -> [#switch_weapon{}]. +-spec maybe_decode_weapon_switch +   ( +      non_neg_integer(), +      boolean() +   ) +   -> list(type()). +maybe_decode_weapon_switch (_CharacterIX, false) -> []; +maybe_decode_weapon_switch (CharacterIX, true) -> +   [#switch_weapon{ actor_ix = CharacterIX }].  -spec can_follow (category(), category()) -> boolean().  can_follow (nothing, attack) -> true; @@ -138,14 +140,26 @@ get_movement_points (Action) when is_record(Action, interrupted_move) ->     Action#interrupted_move.movement_points;  get_movement_points (_) -> 0. --spec get_target_ix (type()) -> non_neg_integer(). -get_target_ix (Action) when is_record(Action, attack) -> +-spec get_target_index (type()) -> non_neg_integer(). +get_target_index (Action) when is_record(Action, attack) ->     Action#attack.target_ix; -get_target_ix (Action) when is_record(Action, defend) -> -   Action#defend.target_ix; -get_target_ix (_) -> +get_target_index (Action) when is_record(Action, attack_of_opportunity) -> +   Action#attack_of_opportunity.target_ix; +get_target_index (_) ->     0. +-spec get_actor_index (type()) -> (non_neg_integer() | -1). +get_actor_index (Action) when is_record(Action, attack) -> +   Action#attack.actor_ix; +get_actor_index (Action) when is_record(Action, attack_of_opportunity) -> +   Action#attack_of_opportunity.actor_ix; +get_actor_index (Action) when is_record(Action, move) -> +   Action#move.actor_ix; +get_actor_index (Action) when is_record(Action, switch_weapon) -> +   Action#switch_weapon.actor_ix; +get_actor_index (_) -> +   -1. +  -spec new_interrupted_move     (        list(shr_direction:type()), @@ -161,8 +175,8 @@ get_category (Action) when is_record(Action, move) -> move;  get_category (Action) when is_record(Action, switch_weapon) -> switch_weapon;  get_category (Action) when is_record(Action, interrupted_move) ->     interrupted_move; -get_category (Action) when is_record(Action, defend) -> -  defend. +get_category (Action) when is_record(Action, attack_of_opportunity) -> +  attack_of_opportunity.  -spec from_map_marker     ( @@ -174,7 +188,7 @@ from_map_marker (_Character, Marker) ->     case shr_map_marker:get_category(Marker) of        matk ->           [ -            #defend +            #attack_of_opportunity              {                 target_ix = shr_map_marker:get_character_index(Marker)              } diff --git a/src/battle/struct/btl_attack.erl b/src/battle/struct/btl_attack.erl index 6d7f32e..baaa5a7 100644 --- a/src/battle/struct/btl_attack.erl +++ b/src/battle/struct/btl_attack.erl @@ -5,7 +5,7 @@  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --type order() :: ('first' | 'second' | 'counter' | 'opportunity'). +-type order() :: ('first' | 'second' | 'counter').  -type precision() :: ('misses' | 'grazes' | 'hits').  -record @@ -411,26 +411,8 @@ get_description_of        CanParry,        DefenderLuck,        AttackerLuck -   ); -get_description_of -( -   opportunity, -   Attacker, -   Defender, -   AttackerLuck, -   DefenderLuck -) -> -   effect_of_attack -   ( -      opportunity, -      Attacker, -      Defender, -      false, -      AttackerLuck, -      DefenderLuck     ). -  -spec apply_to_healths_and_lucks     (        maybe_type(), @@ -573,8 +555,12 @@ get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) ->        _ -> [First, Second]     end. --spec attack_of_opportunity () -> opportunity. -attack_of_opportunity () -> opportunity. +-spec attack_of_opportunity () -> list(step()). +attack_of_opportunity () -> +   [ +      {first, false}, +      {second, false} +   ].  -spec encode (type()) -> {list(any())}.  encode (Attack) -> diff --git a/src/battle/struct/btl_battle.erl b/src/battle/struct/btl_battle.erl index c1f4d68..435a99b 100644 --- a/src/battle/struct/btl_battle.erl +++ b/src/battle/struct/btl_battle.erl @@ -12,8 +12,7 @@        related_inventory :: shr_inventory:type(),        related_tile_ids :: ordsets:ordset(shr_tile:id()),        map :: shr_map:type(), -      characters :: -         orddict:orddict(non_neg_integer(), btl_character:unresolved()), +      characters :: orddict:orddict(non_neg_integer(), btl_character:either()),        players :: orddict:orddict(non_neg_integer(), btl_player:type()),        current_player_turn :: btl_player_turn:type()     } @@ -35,6 +34,7 @@        get_map/1,        get_characters/1,        get_character/2, +      get_resolved_character/2,        get_players/1,        get_player/2,        get_current_player_turn/1, @@ -130,12 +130,35 @@ get_map (Battle) -> Battle#battle.map.     (        type()     ) -   -> orddict:orddict(non_neg_integer(), btl_character:unresolved()). +   -> orddict:orddict(non_neg_integer(), btl_character:either()).  get_characters (Battle) -> Battle#battle.characters. --spec get_character (non_neg_integer(), type()) -> btl_character:unresolved(). -get_character (IX, Battle) -> -   orddict:fetch(IX, Battle#battle.characters). +-spec get_character (non_neg_integer(), type()) -> btl_character:either(). +get_character (IX, Battle) -> orddict:fetch(IX, Battle#battle.characters). + +-spec get_resolved_character +   ( +      non_neg_integer(), +      type() +   ) +   -> {btl_character:type(), type()}. +get_resolved_character (IX, Battle) -> +   Character = orddict:fetch(IX, Battle#battle.characters), + +   case btl_character:is_unresolved(Character) of +      true -> +         ResolvedCharacter = resolve_character(Character, Battle), +         { +            ResolvedCharacter, +            Battle#battle +            { +               characters = +                  orddict:set(IX, ResolvedCharacter, Battle#battle.characters) +            } +         }; + +      false -> {Character, Battle} +   end.  -spec get_players     ( @@ -207,7 +230,7 @@ ataxia_set_map (Map, MapUpdate, Battle) ->  -spec set_characters     ( -      orddict:orddict(non_neg_integer(), btl_character:unresolved()), +      orddict:orddict(non_neg_integer(), btl_character:either()),        type()     )     -> type(). @@ -219,16 +242,30 @@ set_characters (Characters, Battle) ->  -spec ataxia_set_characters     ( -      orddict:orddict(non_neg_integer(), btl_character:unresolved()), +      orddict:orddict(non_neg_integer(), btl_character:either()),        type()     )     -> {type(), ataxic:basic()}.  ataxia_set_characters (Characters, Battle) -> -   ataxia_set_characters(Characters, ataxic:constant(Characters), Battle). +   UnresolvedCharacters = +      orddict:map +      ( +         fun (_Key, Character) -> +            btl_character:to_unresolved(Character) +         end, +         Characters +      ), + +   ataxia_set_characters +   ( +      Characters, +      ataxic:constant(UnresolvedCharacters), +      Battle +   ).  -spec ataxia_set_characters     ( -      orddict:orddict(non_neg_integer(), btl_character:unresolved()), +      orddict:orddict(non_neg_integer(), btl_character:either()),        ataxic:basic(),        type()     ) @@ -246,7 +283,7 @@ ataxia_set_characters (Characters, CharactersUpdate, Battle) ->  -spec set_character     (        non_neg_integer(), -      btl_character:unresolved(), +      btl_character:either(),        type()     )     -> type(). @@ -258,7 +295,7 @@ set_character (IX, Character, Battle) ->  -spec add_character     ( -      btl_character:unresolved(), +      btl_character:either(),        type()     )     -> {non_neg_integer(), type()}. @@ -268,7 +305,7 @@ add_character (Character, Battle) ->  -spec ataxia_add_character     ( -      btl_character:unresolved(), +      btl_character:either(),        type()     )     -> {non_neg_integer(), type(), ataxic:basic()}. @@ -280,17 +317,23 @@ ataxia_add_character (Character, Battle) ->  -spec ataxia_set_character     (        non_neg_integer(), -      btl_character:unresolved(), +      btl_character:either(),        type()     )     -> {type(), ataxic:basic()}.  ataxia_set_character (IX, Character, Battle) -> -   ataxia_set_character(IX, Character, ataxic:constant(Character), Battle). +   ataxia_set_character +   ( +      IX, +      Character, +      ataxic:constant(btl_character:to_unresolved(Character)), +      Battle +   ).  -spec ataxia_set_character     (        non_neg_integer(), -      btl_character:unresolved(), +      btl_character:either(),        ataxic:basic(),        type()     ) @@ -477,29 +520,34 @@ new (Map) ->  -spec resolve_character     ( -      btl_character:unresolved(), +      btl_character:either(),        type()     )     -> btl_character:type(). -resolve_character (CharacterRef, Battle) -> -   btl_character:resolve -   ( -      shr_tile:get_omnimods -      ( -         shr_tile:from_id +resolve_character (Character, Battle) -> +   case btl_character:is_unresolved(Character) of +      true -> +         btl_character:resolve           ( -            shr_tile_instance:get_tile_id +            shr_tile:get_omnimods              ( -               shr_map:get_tile_instance +               shr_tile:from_id                 ( -                  btl_character:get_location(CharacterRef), -                  Battle#battle.map +                  shr_tile_instance:get_tile_id +                  ( +                     shr_map:get_tile_instance +                     ( +                        btl_character:get_location(Character), +                        Battle#battle.map +                     ) +                  )                 ) -            ) -         ) -      ), -      CharacterRef -   ). +            ), +            Character +         ); + +      false -> Character +   end.  -spec get_characters_field () -> non_neg_integer().  get_characters_field () -> #battle.characters. diff --git a/src/battle/struct/btl_character.erl b/src/battle/struct/btl_character.erl index cca53b3..790c809 100644 --- a/src/battle/struct/btl_character.erl +++ b/src/battle/struct/btl_character.erl @@ -92,6 +92,7 @@     [        new/4,        resolve/2, +      is_unresolved/1,        to_unresolved/1,        decode/1,        encode/1 @@ -149,7 +150,7 @@ get_location (#btl_char_ref{ location = R }) -> R.  get_current_health (#btl_char{ current_health = R }) -> R;  get_current_health (#btl_char_ref{ current_health = R }) -> R. --spec get_is_alive (type()) -> boolean(). +-spec get_is_alive (either()) -> boolean().  get_is_alive (#btl_char{ current_health = H, is_defeated = D }) ->     ((not D) and (H > 0));  get_is_alive (#btl_char_ref{ current_health = H, is_defeated = D }) -> @@ -439,8 +440,8 @@ new        base = Base     }. --spec resolve (shr_omnimods:type(), unresolved()) -> type(). -resolve (LocalOmnimods, CharRef) -> +-spec resolve (shr_omnimods:type(), either()) -> type(). +resolve (LocalOmnimods, CharRef) when is_record(CharRef, btl_char_ref) ->     #btl_char     {        player_ix = CharRef#btl_char_ref.player_ix, @@ -450,10 +451,11 @@ resolve (LocalOmnimods, CharRef) ->        is_active = CharRef#btl_char_ref.is_active,        is_defeated = CharRef#btl_char_ref.is_defeated,        base = shr_character:resolve(LocalOmnimods, CharRef#btl_char_ref.base) -   }. +   }; +resolve (_LocalOmnimods, Char) when is_record(Char, btl_char) -> Char. --spec to_unresolved (type()) -> unresolved(). -to_unresolved (Char) -> +-spec to_unresolved (either()) -> unresolved(). +to_unresolved (Char) when is_record(Char, btl_char) ->     #btl_char_ref     {        player_ix = Char#btl_char.player_ix, @@ -463,7 +465,11 @@ to_unresolved (Char) ->        is_active = Char#btl_char.is_active,        is_defeated = Char#btl_char.is_defeated,        base = shr_character:to_unresolved(Char#btl_char.base) -   }. +   }; +to_unresolved (CharRef) when is_record(CharRef, btl_char_ref) -> CharRef. + +-spec is_unresolved (either()) -> boolean(). +is_unresolved (Char) -> is_record(Char, btl_char_ref).  -spec get_rank_field() -> non_neg_integer().  get_rank_field () -> #btl_char_ref.rank. diff --git a/src/battle/struct/btl_character_turn_request.erl b/src/battle/struct/btl_character_turn_request.erl index 7246ca7..7d53bcd 100644 --- a/src/battle/struct/btl_character_turn_request.erl +++ b/src/battle/struct/btl_character_turn_request.erl @@ -52,12 +52,16 @@  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec decode_actions (map()) -> list(btl_action:type()). -decode_actions (Act) -> +-spec decode_actions (non_neg_integer(), map()) -> list(btl_action:type()). +decode_actions (CharacterIX, Act) ->     S0Result = [],     S1Result =        case -         btl_action:maybe_decode_move(maps:get(?ACTIONS_MOVE_FIELD, Act)) +         btl_action:maybe_decode_move +         ( +            CharacterIX, +            maps:get(?ACTIONS_MOVE_FIELD, Act) +         )        of           [] -> S0Result;           [Move] -> [Move|S0Result] @@ -67,6 +71,7 @@ decode_actions (Act) ->        case           btl_action:maybe_decode_attack           ( +            CharacterIX,              maps:get(?ACTIONS_ATTACK_FIELD, Act)           )        of @@ -78,6 +83,7 @@ decode_actions (Act) ->        case           btl_action:maybe_decode_weapon_switch           ( +            CharacterIX,              maps:get(?ACTIONS_WEAPON_SWITCH_FIELD, Act)           )        of @@ -94,7 +100,7 @@ decode_actions (Act) ->  decode (Map) ->     CharacterIX = maps:get(?CHAR_IX_FIELD, Map),     EncodedActions = maps:get(?ACTIONS_FIELD, Map), -   Actions = decode_actions(EncodedActions), +   Actions = decode_actions(CharacterIX, EncodedActions),     #type     { diff --git a/src/battle/struct/btl_character_turn_update.erl b/src/battle/struct/btl_character_turn_update.erl index 9f2c30a..f5f2d05 100644 --- a/src/battle/struct/btl_character_turn_update.erl +++ b/src/battle/struct/btl_character_turn_update.erl @@ -7,17 +7,9 @@  (     type,     { -      battle_is_outdated :: boolean(), -      character_is_outdated :: boolean(), -        battle :: btl_battle:type(), -      reversed_battle_updates :: list(ataxic:basic()), - -      character :: btl_character:type(), -      reversed_character_updates :: list(ataxic:basic()), -        character_ix :: non_neg_integer(), - +      reversed_battle_updates :: list(ataxic:basic()),        timeline :: list(any())     }  ). @@ -35,14 +27,12 @@        new/2,        get_battle/1, -      get_character/1,        get_character_ix/1,        get_battle_update/1,        get_timeline/1,        set_battle/3, -      set_character/2,        ataxia_set_battle/4,        ataxia_set_character/3, diff --git a/src/shared/struct/shr_character.erl b/src/shared/struct/shr_character.erl index 68e6cb6..87a9be8 100644 --- a/src/shared/struct/shr_character.erl +++ b/src/shared/struct/shr_character.erl @@ -397,14 +397,15 @@ resolve (LocalOmnimods, CharRef) ->        extra_omnimods = LocalOmnimods     }. --spec to_unresolved (type()) -> unresolved(). -to_unresolved (Char) -> +-spec to_unresolved (either()) -> unresolved(). +to_unresolved (Char) when is_record(Char, shr_char)->     #shr_char_ref     {        name = Char#shr_char.name,        equipment = shr_equipment:to_unresolved(Char#shr_char.equipment),        is_using_secondary = Char#shr_char.is_using_secondary -   }. +   }; +to_unresolved (CharRef) when is_record(CharRef, shr_char_ref) -> CharRef.  -spec decode (map()) -> unresolved().  decode (Map) -> | 


