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). |