| summaryrefslogtreecommitdiff | 
diff options
Diffstat (limited to 'src/battle/mechanic')
| -rw-r--r-- | src/battle/mechanic/btl_next_turn.erl | 184 | ||||
| -rw-r--r-- | src/battle/mechanic/btl_turn_actions.erl | 83 | ||||
| -rw-r--r-- | src/battle/mechanic/btl_victory.erl | 208 | ||||
| -rw-r--r-- | src/battle/mechanic/turn_action/btl_turn_actions_attack.erl | 225 | ||||
| -rw-r--r-- | src/battle/mechanic/turn_action/btl_turn_actions_move.erl | 186 | ||||
| -rw-r--r-- | src/battle/mechanic/turn_action/btl_turn_actions_stats_change.erl | 85 | ||||
| -rw-r--r-- | src/battle/mechanic/turn_action/btl_turn_actions_switch_weapon.erl | 68 | 
7 files changed, 1039 insertions, 0 deletions
| diff --git a/src/battle/mechanic/btl_next_turn.erl b/src/battle/mechanic/btl_next_turn.erl new file mode 100644 index 0000000..886916d --- /dev/null +++ b/src/battle/mechanic/btl_next_turn.erl @@ -0,0 +1,184 @@ +-module(btl_next_turn). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      update_if_needed/1 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec set_player_turn_to_next (btl_battle:type()) +   -> {btl_battle:type(), ataxic:basic()}. +set_player_turn_to_next (Battle) -> +   Players = btl_battle:get_players(Battle), +   CurrentPlayerTurn = btl_battle:get_current_player_turn(Battle), + +   NextPlayerTurn = btl_player_turn:next(Players, CurrentPlayerTurn), + +   UpdatedBattle = btl_battle:set_current_player_turn(NextPlayerTurn, Battle), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_current_player_turn_field(), +         ataxic:constant(NextPlayerTurn) +      ), + +   {UpdatedBattle, DBQuery}. + +-spec reset_next_player_timeline (btl_battle:type()) +   -> {btl_battle:type(), btl_player:type(), ataxic:basic()}. +reset_next_player_timeline (Battle) -> +   NextPlayerTurn = btl_battle:get_current_player_turn(Battle), +   NextPlayerIX = btl_player_turn:get_player_ix(NextPlayerTurn), +   NextPlayer = btl_battle:get_player(NextPlayerIX, Battle), + +   UpdatedNextPlayer = btl_player:reset_timeline(NextPlayer), +   UpdatedBattle = +      btl_battle:set_player(NextPlayerIX, UpdatedNextPlayer, Battle), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_players_field(), +         ataxic_sugar:update_orddict_element +         ( +            NextPlayerIX, +            ataxic:update_field +            ( +               btl_player:get_timeline_field(), +               ataxic:constant([]) +            ) +         ) +      ), + +   {UpdatedBattle, UpdatedNextPlayer, DBQuery}. + + +-spec activate_next_players_characters (btl_battle:type(), btl_player:type()) +   -> {btl_battle:type(), ataxic:basic()}. +activate_next_players_characters (Battle, NextPlayer) -> +   NextPlayerIX = btl_player:get_index(NextPlayer), +   Characters = btl_battle:get_characters(Battle), + +   {UpdatedCharacters, AtaxicUpdates} = +      orddict:fold +      ( +         fun (IX, Character, {Prev, Updates}) -> +            case (btl_character:get_player_index(Character) == NextPlayerIX) of +               true -> +                  { +                     orddict:store +                     ( +                        IX, +                        btl_character:set_is_active(true, Character), +                        Prev +                     ), +                     [ +                        ataxic_sugar:update_orddict_element +                        ( +                           IX, +                           ataxic:update_field +                           ( +                              btl_character:get_is_active_field(), +                              ataxic:constant(true) +                           ) +                        )|Updates +                     ] +                  }; + +               false -> {Prev, Updates} +            end +         end, +         {Characters, []}, +         Characters +      ), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic:sequence(AtaxicUpdates) +      ), + +   UpdatedBattle = btl_battle:set_characters(UpdatedCharacters, Battle), + +   {UpdatedBattle, DBQuery}. + +-spec update +   ( +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +update (Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Battle = btl_character_turn_data:get_battle(Data), + +   {S0Battle, DBQuery0} = set_player_turn_to_next(Battle), +   {S1Battle, NextPlayer, DBQuery1} = reset_next_player_timeline(S0Battle), +   {S2Battle, DBQuery2} = +      activate_next_players_characters(S1Battle, NextPlayer), + +   S0Data = btl_character_turn_data:set_battle(S2Battle, Data), +   S0Update = +      btl_character_turn_update:add_to_timeline +      ( +         btl_turn_result:new_player_turn_started +         ( +            btl_player:get_index(NextPlayer) +         ), +         DBQuery0, +         Update +      ), + +   S1Update = btl_character_turn_update:set_data(S0Data, S0Update), + +   S2Update = +      lists:foldl +      ( +         fun btl_character_turn_update:add_to_db/2, +         S1Update, +         [DBQuery1,DBQuery2] +      ), + +   S2Update. + +-spec requires_update (btl_character_turn_update:type()) -> boolean(). +requires_update (Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Battle = btl_character_turn_data:get_battle(Data), +   Characters = btl_battle:get_characters(Battle), + +   (not +      (lists:any +         ( +            fun ({_IX, Char}) -> +               btl_character:get_is_active(Char) +            end, +            orddict:to_list(Characters) +         ) +      ) +   ). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec update_if_needed +   ( +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +update_if_needed (Update) -> +   case requires_update(Update) of +      true -> update(Update); +      _ -> Update +   end. diff --git a/src/battle/mechanic/btl_turn_actions.erl b/src/battle/mechanic/btl_turn_actions.erl new file mode 100644 index 0000000..d4a81fc --- /dev/null +++ b/src/battle/mechanic/btl_turn_actions.erl @@ -0,0 +1,83 @@ +-module(btl_turn_actions). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      apply_requested_actions/2 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Move elsewhere + +%%%% TODO: move this elsewhere +-spec finalize_character +   ( +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +finalize_character (Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Character = btl_character_turn_data:get_character(Data), + +   DisabledCharacter = btl_character:set_is_active(false, Character), +   UpdatedData = btl_character_turn_data:set_character(DisabledCharacter, Data), +   FinalizedData = btl_character_turn_data:clean_battle(UpdatedData), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            btl_character_turn_data:get_character_ix(Data), +            ataxic:update_field +            ( +               btl_character:get_is_active_field(), +               ataxic:constant(false) +            ) +         ) +      ), + +   S0Update = btl_character_turn_update:set_data(FinalizedData, Update), +   S1Update = btl_character_turn_update:add_to_db(DBQuery, S0Update), + +   S1Update. + +-spec handle +( +   btl_battle_action:type(), +   btl_character_turn_update:type() +) +-> btl_character_turn_update:type(). +handle (BattleAction, Update) -> +   case btl_battle_action:get_category(BattleAction) of +      move -> btl_turn_actions_move:handle(BattleAction, Update); +      switch_weapon -> btl_turn_actions_switch_weapon:handle(Update); +      attack -> btl_turn_actions_attack:handle(BattleAction, Update) +   end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec apply_requested_actions +   ( +      btl_character_turn_data:type(), +      btl_character_turn_request:type() +   ) +   -> btl_character_turn_update:type(). +apply_requested_actions (Data, Request) -> +   Actions = btl_character_turn_request:get_actions(Request), + +   EmptyUpdate = btl_character_turn_update:new(Data), +   PostActionsUpdate = lists:foldl(fun handle/2, EmptyUpdate, Actions), + +   finalize_character(PostActionsUpdate). diff --git a/src/battle/mechanic/btl_victory.erl b/src/battle/mechanic/btl_victory.erl new file mode 100644 index 0000000..089af81 --- /dev/null +++ b/src/battle/mechanic/btl_victory.erl @@ -0,0 +1,208 @@ +-module(btl_victory). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      handle_character_lost_health/3 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec mark_players_characters_as_defeated +   ( +      non_neg_integer(), +      orddict:orddict(non_neg_integer(), btl_character:type()) +   ) +   -> +   { +      orddict:orddict(non_neg_integer(), btl_character:type()), +      list(ataxic:basic()) +   }. +mark_players_characters_as_defeated (PlayerIX, Characters) -> +   orddict:fold +   ( +      fun (IX, Character, {Dict, Updates}) -> +         case (btl_character:get_player_index(Character) == PlayerIX) of +            false -> {Dict, Updates}; +            true -> +               { +                  orddict:store +                  ( +                     IX, +                     btl_character:set_is_defeated(true, Character), +                     Dict +                  ), +                  [ +                     ataxic_sugar:update_orddict_element +                     ( +                        IX, +                        ataxic:update_field +                        ( +                           btl_character:get_is_defeated_field(), +                           ataxic:constant(true) +                        ) +                     ) +                  ] +               } +         end +      end, +      {Characters, []}, +      Characters +   ). + +-spec handle_player_defeat +   ( +      non_neg_integer(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle_player_defeat (PlayerIX, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Battle = btl_character_turn_data:get_battle(Data), +   Characters = btl_battle:get_characters(Battle), + +   %% FIXME [FUNCTION: battle][MEDIUM]: The controlled character might slip +   %% through. +   {UpdatedCharacters, AtaxicUpdates} = +      mark_players_characters_as_defeated(PlayerIX, Characters), + +   S0Battle = btl_battle:set_characters(UpdatedCharacters, Battle), +   S1Battle = +      btl_battle:set_player +      ( +         PlayerIX, +         btl_player:set_is_active +         ( +            false, +            btl_battle:get_player(PlayerIX, S0Battle) +         ), +         S0Battle +      ), + +   UpdatedData = btl_character_turn_data:set_battle(S1Battle, Data), +   S0Update = btl_character_turn_update:set_data(UpdatedData, Update), + +   DBQuery = +      ataxic:sequence +      ( +         [ +            ataxic:update_field +            ( +               btl_battle:get_players_field(), +               ataxic_sugar:update_orddict_element +               ( +                  PlayerIX, +                  ataxic:update_field +                  ( +                     btl_player:get_is_active_field(), +                     ataxic:constant(false) +                  ) +               ) +            ), +            ataxic:update_field +            ( +               btl_battle:get_characters_field(), +               ataxic:sequence(AtaxicUpdates) +            ) +         ] +      ), + +   S1Update = +      btl_character_turn_update:add_to_timeline +      ( +         btl_turn_result:new_player_lost(PlayerIX), +         DBQuery, +         S0Update +      ), + +   S1Update. + + +-spec actually_handle_character_lost_health +   ( +      non_neg_integer(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +actually_handle_character_lost_health (CharIX, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Battle = btl_character_turn_data:get_battle(Data), +   Character = btl_battle:get_character(CharIX, Battle), +   Characters = btl_battle:get_characters(Battle), +   CharacterPlayerIX = btl_character:get_player_index(Character), + +   case btl_character:get_rank(Character) of +      optional -> +         %% Let's not assume there is a commander +         StillHasAliveChar = +            lists:any +            ( +               fun ({IX, Char}) -> +                  ( +                     (CharacterPlayerIX == btl_character:get_player_index(Char)) +                     and (IX /= CharIX) +                     and btl_character:get_is_alive(Char) +                  ) +               end, +               orddict:to_list(Characters) +            ), + +         case StillHasAliveChar of +            true -> Update; +            _ -> handle_player_defeat(CharacterPlayerIX, Update) +         end; + +      commander -> handle_player_defeat(CharacterPlayerIX, Update); + +      target -> +         StillHasAliveChar = +            lists:any +            ( +               fun ({IX, Char}) -> +                  ( +                     (CharacterPlayerIX == btl_character:get_player_index(Char)) +                     and (IX /= CharIX) +                     and btl_character:get_is_alive(Char) +                  ) +               end, +               orddict:to_list(Characters) +            ), + +         case StillHasAliveChar of +            true -> Update; +            _ -> handle_player_defeat(CharacterPlayerIX, Update) +         end +   end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle_character_lost_health +   ( +      non_neg_integer(), +      integer(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle_character_lost_health (_, Health, Update) when (Health > 0) -> Update; +handle_character_lost_health (CharIX, _Health, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   S1Data = btl_character_turn_data:clean_battle(Data), +   S1Update = btl_character_turn_update:set_data(S1Data, Update), + +   S2Update = actually_handle_character_lost_health(CharIX, S1Update), + +   S2Data = btl_character_turn_update:get_data(S2Update), +   S3Data = btl_character_turn_data:refreshr_character(S2Data), +   S3Update = btl_character_turn_update:set_data(S3Data, S2Update), + +   S3Update. diff --git a/src/battle/mechanic/turn_action/btl_turn_actions_attack.erl b/src/battle/mechanic/turn_action/btl_turn_actions_attack.erl new file mode 100644 index 0000000..52dd3fb --- /dev/null +++ b/src/battle/mechanic/turn_action/btl_turn_actions_attack.erl @@ -0,0 +1,225 @@ +-module(btl_turn_actions_attack). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      handle/2 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle_attack_sequence +   ( +      btl_character_current_data:type(), +      non_neg_integer(), +      btl_character_current_data:type(), +      non_neg_integer(), +      list(btl_attack:step()) +   ) +   -> {list(btl_attack:type()), non_neg_integer(), non_neg_integer()}. +handle_attack_sequence +( +   CharacterCurrentData, +   CharacterCurrentHealth, +   TargetCurrentData, +   TargetCurrentHealth, +   AttackSequence +) -> +   AttackPlannedEffects = +      lists:map +      ( +         fun (AttackStep) -> +            btl_attack:get_description_of +            ( +               AttackStep, +               CharacterCurrentData, +               TargetCurrentData +            ) +         end, +         AttackSequence +      ), + +   lists:foldl +   ( +      fun +      ( +         AttackEffectCandidate, +         {AttackValidEffects, AttackerHealth, DefenderHealth} +      ) -> +         {AttackResult, NewAttackerHealth, NewDefenderHealth} = +            btl_attack:apply_to_healths +            ( +               AttackEffectCandidate, +               AttackerHealth, +               DefenderHealth +            ), +         case AttackResult of +            nothing -> {AttackValidEffects, AttackerHealth, DefenderHealth}; +            _ -> +               { +                  (AttackValidEffects ++ [AttackResult]), +                  NewAttackerHealth, +                  NewDefenderHealth +               } +         end +      end, +      { +         [], +         CharacterCurrentHealth, +         TargetCurrentHealth +      }, +      AttackPlannedEffects +   ). + +-spec get_attack_sequence +   ( +      btl_character:type(), +      btl_character:type() +   ) +   -> list(btl_attack:step()). +get_attack_sequence (Character, TargetCharacter) -> +   Range = +      btl_location:dist +      ( +         btl_character:get_location(Character), +         btl_character:get_location(TargetCharacter) +      ), + +   {AttackingWeaponID, _} = btl_character:get_weapon_ids(Character), +   {DefendingWeaponID, _} = btl_character:get_weapon_ids(TargetCharacter), + +   AttackingWeapon = shr_weapon:from_id(AttackingWeaponID), +   DefendingWeapon = shr_weapon:from_id(DefendingWeaponID), + +   btl_attack:get_sequence(Range, AttackingWeapon, DefendingWeapon). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle +   ( +      btl_battle_action:type(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle (BattleAction, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Battle = btl_character_turn_data:get_battle(Data), +   Character = btl_character_turn_data:get_character(Data), +   CharacterIX = btl_character_turn_data:get_character_ix(Data), +   CharacterCurrentData = +      btl_character_turn_data:get_character_current_data(Data), + +   Map = btl_battle:get_map(Battle), +   TargetIX = btl_battle_action:get_target_ix(BattleAction), +   TargetCharacter = btl_battle:get_character(TargetIX, Battle), +   TargetCurrentData = btl_character_current_data:new(TargetCharacter, Map), + +   true = btl_character:get_is_alive(TargetCharacter), + +   AttackSequence = get_attack_sequence(Character, TargetCharacter), + +   {AttackEffects, RemainingAttackerHealth, RemainingDefenderHealth} = +      handle_attack_sequence +      ( +         CharacterCurrentData, +         btl_character:get_current_health(Character), +         TargetCurrentData, +         btl_character:get_current_health(TargetCharacter), +         AttackSequence +      ), + +   UpdatedCharacter = +      btl_character:set_current_health(RemainingAttackerHealth, Character), + +   UpdatedBattle = +      btl_battle:set_character +      ( +         TargetIX, +         btl_character:set_current_health +         ( +            RemainingDefenderHealth, +            TargetCharacter +         ), +         Battle +      ), + +   S0Data = btl_character_turn_data:set_battle(UpdatedBattle, Data), +   S1Data = btl_character_turn_data:set_character(UpdatedCharacter, S0Data), + +   TimelineItem = +      btl_turn_result:new_character_attacked +      ( +         CharacterIX, +         TargetIX, +         AttackEffects +      ), + +   DBQuery0 = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            TargetIX, +            ataxic:update_field +            ( +               btl_character:get_current_health_field(), +               ataxic:constant(RemainingDefenderHealth) +            ) +         ) +      ), + +   DBQuery1 = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            CharacterIX, +            ataxic:update_field +            ( +               btl_character:get_current_health_field(), +               ataxic:constant(RemainingAttackerHealth) +            ) +         ) +      ), + +   S0Update = +      btl_character_turn_update:add_to_timeline +      ( +         TimelineItem, +         DBQuery0, +         Update +      ), + +   S1Update = btl_character_turn_update:add_to_db(DBQuery1, S0Update), +   S2Update = btl_character_turn_update:set_data(S1Data, S1Update), + +   S3Update = +      btl_victory:handle_character_lost_health +      ( +         CharacterIX, +         RemainingAttackerHealth, +         S2Update +      ), + +   S4Update = +      btl_victory:handle_character_lost_health +      ( +         TargetIX, +         RemainingDefenderHealth, +         S3Update +      ), + +   S4Update. diff --git a/src/battle/mechanic/turn_action/btl_turn_actions_move.erl b/src/battle/mechanic/turn_action/btl_turn_actions_move.erl new file mode 100644 index 0000000..bf023d5 --- /dev/null +++ b/src/battle/mechanic/turn_action/btl_turn_actions_move.erl @@ -0,0 +1,186 @@ +-module(btl_turn_actions_move). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      handle/2 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec cross +   ( +      btl_map:type(), +      list(btl_location:type()), +      list(btl_direction:enum()), +      non_neg_integer(), +      btl_location:type() +   ) +   -> {btl_location:type(), non_neg_integer()}. +cross (_Map, _ForbiddenLocations, [], Cost, Location) -> +   {Location, Cost}; +cross (Map, ForbiddenLocations, [Step|NextSteps], Cost, Location) -> +   NextLocation = btl_location:apply_direction(Step, Location), +   NextTileInstance = btl_map:get_tile_instance(NextLocation, Map), +   NextTileClassID = shr_tile:extract_main_class_id(NextTileInstance), +   NextTile = shr_tile:from_class_id(NextTileClassID), +   NextCost = (Cost + shr_tile:get_cost(NextTile)), +   IsForbidden = +      lists:foldl +      ( +         fun (ForbiddenLocation, Prev) -> +            (Prev or (NextLocation == ForbiddenLocation)) +         end, +         false, +         ForbiddenLocations +      ), + +   IsForbidden = false, + +   cross(Map, ForbiddenLocations, NextSteps, NextCost, NextLocation). + +-spec cross +   ( +      btl_map:type(), +      list(btl_location:type()), +      list(btl_direction:enum()), +      btl_location:type() +   ) +   -> {btl_location:type(), non_neg_integer()}. +cross (Map, ForbiddenLocations, Path, Location) -> +   cross(Map, ForbiddenLocations, Path, 0, Location). + +-spec get_path_cost_and_destination +   ( +      btl_character_turn_data:type(), +      list(btl_direction:type()) +   ) +   -> {non_neg_integer(), btl_location:type()}. +get_path_cost_and_destination (Data, Path) -> +   Character = btl_character_turn_data:get_character(Data), +   CharacterIX = btl_character_turn_data:get_character_ix(Data), +   Battle = btl_character_turn_data:get_battle(Data), +   Map = btl_battle:get_map(Battle), + +   ForbiddenLocations = +      orddict:fold +      ( +         fun (IX, Char, Prev) -> +            IsAlive = btl_character:get_is_alive(Char), +            if +               (IX == CharacterIX) -> Prev; +               (not IsAlive) -> Prev; +               true -> +                  ordsets:add_element(btl_character:get_location(Char), Prev) +            end +         end, +         ordsets:new(), +         btl_battle:get_characters(Battle) +      ), + +   {NewLocation, Cost} = +      cross +      ( +         Map, +         ForbiddenLocations, +         Path, +         btl_character:get_location(Character) +      ), + +   {Cost, NewLocation}. + +-spec assert_character_can_move +   ( +      btl_character_turn_data:type(), +      non_neg_integer() +   ) +   -> 'ok'. +assert_character_can_move (Data, Cost) -> +   CharacterData = btl_character_turn_data:get_character_current_data(Data), +   CharacterStats= btl_character_current_data:get_statistics(CharacterData), +   CharacterMovementPoints = shr_statistics:get_movement_points(CharacterStats), + +   true = (Cost =< CharacterMovementPoints), + +   ok. + +-spec commit_move +   ( +      btl_character_current_data:type(), +      btl_character_turn_update:type(), +      list(btl_direction:type()), +      btl_location:type() +   ) +   -> btl_character_turn_update:type(). +commit_move (PreviousCurrentData, Update, Path, NewLocation) -> +   Data = btl_character_turn_update:get_data(Update), +   Character = btl_character_turn_data:get_character(Data), +   CharacterIX = btl_character_turn_data:get_character_ix(Data), + +   UpdatedCharacter = btl_character:set_location(NewLocation, Character), +   S0Data = btl_character_turn_data:set_character(UpdatedCharacter, Data), +   S1Data = btl_character_turn_data:refresh_character_current_data(S0Data), + +   S0Update = btl_character_turn_update:set_data(S1Data, Update), +   S1Update = +      btl_turn_actions_stats_change:handle_max_health_changes +      ( +         PreviousCurrentData, +         S0Update +      ), + +   TimelineItem = +      btl_turn_result:new_character_moved(CharacterIX, Path, NewLocation), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            CharacterIX, +            ataxic:update_field +            ( +               btl_character:get_location_field(), +               ataxic:constant(NewLocation) +            ) +         ) +      ), + +   S2Update = +      btl_character_turn_update:add_to_timeline +      ( +         TimelineItem, +         DBQuery, +         S1Update +      ), + +   S2Update. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle +   ( +      btl_battle_action:type(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle (BattleAction, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   CharacterCurrentData = +      btl_character_turn_data:get_character_current_data(Data), +   Path = btl_battle_action:get_path(BattleAction), + +   {PathCost, NewLocation} = get_path_cost_and_destination(Data, Path), +   assert_character_can_move(Data, PathCost), + +   commit_move(CharacterCurrentData, Update, Path, NewLocation). diff --git a/src/battle/mechanic/turn_action/btl_turn_actions_stats_change.erl b/src/battle/mechanic/turn_action/btl_turn_actions_stats_change.erl new file mode 100644 index 0000000..5fe5444 --- /dev/null +++ b/src/battle/mechanic/turn_action/btl_turn_actions_stats_change.erl @@ -0,0 +1,85 @@ +-module(btl_turn_actions_stats_change). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      handle_max_health_changes/2 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec mod_current_health +   ( +      non_neg_integer(), +      non_neg_integer(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +mod_current_health (CurrentMaxHealth, PreviousMaxHealth, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Character = btl_character_turn_data:get_character(Data), +   CharacterIX = btl_character_turn_data:get_character_ix(Data), +   PreviousHealth = btl_character:get_current_health(Character), + +   PreviousHealthRatio = (PreviousHealth / PreviousMaxHealth), +   NewHealth = +      min +      ( +         CurrentMaxHealth, +         max(1, round(PreviousHealthRatio * CurrentMaxHealth)) +      ), + +   UpdatedCharacter = btl_character:set_current_health(NewHealth, Character), +   UpdatedData = btl_character_turn_data:set_character(UpdatedCharacter, Data), +   S0Update = btl_character_turn_update:set_data(UpdatedData, Update), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            CharacterIX, +            ataxic:update_field +            ( +               btl_character:get_current_health_field(), +               ataxic:constant(NewHealth) +            ) +         ) +      ), + +   S1Update =  btl_character_turn_update:add_to_db(DBQuery, S0Update), + +   S1Update. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle_max_health_changes +   ( +      btl_character_current_data:type(), +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle_max_health_changes (PreviousData, Update) -> +   Data = btl_character_turn_update:get_data(Update), +   CurrentData = btl_character_turn_data:get_character_current_data(Data), +   CurrentStats =  btl_character_current_data:get_statistics(CurrentData), +   PreviousStats = btl_character_current_data:get_statistics(PreviousData), + +   CurrentMaxHealth = shr_statistics:get_health(CurrentStats), +   PreviousMaxHealth = shr_statistics:get_health(PreviousStats), + +   case (CurrentMaxHealth == PreviousMaxHealth) of +      true -> Update; +      _ -> mod_current_health(CurrentMaxHealth, PreviousMaxHealth, Update) +   end. diff --git a/src/battle/mechanic/turn_action/btl_turn_actions_switch_weapon.erl b/src/battle/mechanic/turn_action/btl_turn_actions_switch_weapon.erl new file mode 100644 index 0000000..119dbe7 --- /dev/null +++ b/src/battle/mechanic/turn_action/btl_turn_actions_switch_weapon.erl @@ -0,0 +1,68 @@ +-module(btl_turn_actions_switch_weapon). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( +   [ +      handle/1 +   ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle +   ( +      btl_character_turn_update:type() +   ) +   -> btl_character_turn_update:type(). +handle (Update) -> +   Data = btl_character_turn_update:get_data(Update), +   Character = btl_character_turn_data:get_character(Data), +   CharacterCurrentData = +      btl_character_turn_data:get_character_current_data(Data), +   CharacterIX = btl_character_turn_data:get_character_ix(Data), + +   {PrimaryWeaponID, SecondaryWeaponID} = btl_character:get_weapon_ids(Character), + +   UpdatedWeaponIDs = {SecondaryWeaponID, PrimaryWeaponID}, +   UpdatedCharacter = btl_character:set_weapon_ids(UpdatedWeaponIDs, Character), + +   S0Data = btl_character_turn_data:set_character(UpdatedCharacter, Data), +   S1Data = btl_character_turn_data:refresh_character_current_data(S0Data), + +   S0Update = btl_character_turn_update:set_data(S1Data, Update), +   S1Update = +      btl_turn_actions_stats_change:handle_max_health_changes +      ( +         CharacterCurrentData, +         S0Update +      ), + +   TimelineItem = btl_turn_result:new_character_switched_weapons(CharacterIX), + +   DBQuery = +      ataxic:update_field +      ( +         btl_battle:get_characters_field(), +         ataxic_sugar:update_orddict_element +         ( +            CharacterIX, +            ataxic:update_field +            ( +               btl_character:get_weapons_field(), +               ataxic:constant(UpdatedWeaponIDs) +            ) +         ) +      ), + +   btl_character_turn_update:add_to_timeline(TimelineItem, DBQuery, S1Update). | 


