From 124cc7160d80bc9fcafd1a1592fa046ef44d6ef7 Mon Sep 17 00:00:00 2001 From: Nathanael Sensfelder Date: Tue, 4 Jun 2019 14:53:34 +0200 Subject: ... --- .../btl_turn_actions_opportunity_attack.erl | 308 +++++++++++++++++++++ src/battle/struct/btl_attack.erl | 5 +- 2 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 src/battle/mechanic/turn_action/btl_turn_actions_opportunity_attack.erl diff --git a/src/battle/mechanic/turn_action/btl_turn_actions_opportunity_attack.erl b/src/battle/mechanic/turn_action/btl_turn_actions_opportunity_attack.erl new file mode 100644 index 0000000..7d6a1c5 --- /dev/null +++ b/src/battle/mechanic/turn_action/btl_turn_actions_opportunity_attack.erl @@ -0,0 +1,308 @@ +-module(btl_turn_actions_opportunity_attack). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + handle/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec handle_attack_sequence + ( + btl_character:type(), + non_neg_integer(), + btl_character:type(), + non_neg_integer(), + integer(), + integer(), + list(btl_attack:step()), + list(btl_attack:type()) + ) + -> + { + list(btl_attack:type()), + non_neg_integer(), + non_neg_integer(), + integer(), + integer() + }. +handle_attack_sequence +( + _Character, + CharacterCurrentHealth, + _TargetCharacter, + TargetCurrentHealth, + AttackerLuck, + DefenderLuck, + AttackSequence, + Result +) +when +( + (CharacterCurrentHealth == 0) + or (TargetCurrentHealth == 0) + or (AttackSequence == []) +) -> + { + lists:reverse(Result), + CharacterCurrentHealth, + TargetCurrentHealth, + AttackerLuck, + DefenderLuck + }; +handle_attack_sequence +( + Character, + AttackerHealth, + TargetCharacter, + DefenderHealth, + AttackerLuck, + DefenderLuck, + [NextAttack | AttackSequence], + Result +) -> + AttackEffect = + btl_attack:get_description_of + ( + NextAttack, + btl_character:get_base_character(Character), + btl_character:get_base_character(TargetCharacter), + AttackerLuck, + DefenderLuck + ), + + { + AttackResult, + NewAttackerHealth, + NewAttackerLuck, + NewDefenderHealth, + NewDefenderLuck + } = + btl_attack:apply_to_healths_and_lucks + ( + AttackEffect, + AttackerHealth, + AttackerLuck, + DefenderHealth, + DefenderLuck + ), + + NextResult = + case AttackResult of + {nothing, _, _} -> Result; + _ -> [AttackResult|Result] + end, + + handle_attack_sequence + ( + Character, + NewAttackerHealth, + TargetCharacter, + NewDefenderHealth, + NewAttackerLuck, + NewDefenderLuck, + AttackSequence, + NextResult + ). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle + ( + btl_action:type(), + btl_character_turn_update:type() + ) + -> {ok, btl_character_turn_update:type()}. +handle (BattleAction, Update) -> + {S0Update, Battle} = btl_character_turn_update:get_battle(Update), + {S1Update, Character} = btl_character_turn_update:get_character(S0Update), + + DefendingPlayerIX = btl_character:get_player_index(Character), + DefendingPlayer = btl_battle:get_player(DefendingPlayerIX, Battle), + DefendingPlayerLuck = btl_player:get_luck(DefendingPlayer), + + ActorIX = btl_action:get_target_ix(BattleAction), + Map = btl_battle:get_map(Battle), + ActorCharacterRef = btl_battle:get_character(ActorIX, Battle), + ActorCharacter = + btl_character:resolve + ( + shr_tile:get_omnimods + ( + shr_tile:from_id + ( + shr_tile_instance:get_tile_id + ( + shr_map:get_tile_instance + ( + btl_character:get_location(ActorCharacterRef), + Map + ) + ) + ) + ), + ActorCharacterRef + ), + + AttackingPlayerIX = btl_character:get_player_index(ActorCharacter), + AttackingPlayer = btl_battle:get_player(AttackingPlayerIX, Battle), + AttackingPlayerLuck = btl_player:get_luck(AttackingPlayer), + + AttackSequence = btl_attack:attack_of_opportunity(), + + { + AttackEffects, + RemainingAttackerHealth, + RemainingDefenderHealth, + NewAttackerLuck, + NewDefenderLuck + } = + handle_attack_sequence + ( + Character, + btl_character:get_current_health(Character), + ActorCharacter, + btl_character:get_current_health(ActorCharacter), + AttackingPlayerLuck, + DefendingPlayerLuck, + AttackSequence, + [] + ), + + S0NewAttackerLuck = + case {(NewAttackerLuck =< -2), (NewAttackerLuck >= 2)} of + {true, _} -> (NewAttackerLuck + 2); + {_, true} -> (NewAttackerLuck - 2); + _ -> 0 + end, + + S0NewDefenderLuck = + case {(NewDefenderLuck =< -2), (NewDefenderLuck >= 2)} of + {true, _} -> (NewDefenderLuck + 2); + {_, true} -> (NewDefenderLuck - 2); + _ -> 0 + end, + + {UpdatedAttackingPlayer, AttackingPlayerAtaxiaUpdate} = + btl_player:ataxia_set_luck(S0NewAttackerLuck, AttackingPlayer), + + {UpdatedDefendingPlayer, DefendingPlayerAtaxiaUpdate} = + btl_player:ataxia_set_luck(S0NewDefenderLuck, DefendingPlayer), + + {UpdatedCharacter, CharacterAtaxiaUpdate} = + btl_character:ataxia_set_current_health + ( + RemainingAttackerHealth, + Character + ), + + {UpdatedTargetCharacterRef, TargetCharacterRefAtaxiaUpdate} = + btl_character:ataxia_set_current_health + ( + RemainingDefenderHealth, + TargetCharacterRef + ), + + {S0Battle, BattleAtaxiaUpdate0} = + btl_battle:ataxia_set_player + ( + AttackingPlayerIX, + UpdatedAttackingPlayer, + AttackingPlayerAtaxiaUpdate, + Battle + ), + + {S1Battle, BattleAtaxiaUpdate1} = + btl_battle:ataxia_set_player + ( + DefendingPlayerIX, + UpdatedDefendingPlayer, + DefendingPlayerAtaxiaUpdate, + S0Battle + ), + + {S2Battle, BattleAtaxiaUpdate2} = + btl_battle:ataxia_set_character + ( + TargetIX, + UpdatedTargetCharacterRef, + TargetCharacterRefAtaxiaUpdate, + S1Battle + ), + + % Potential danger ahead: we're going to update both the 'character' and + % 'battle' members of a btl_character_turn_update. + % 'S1Update' is sure to have both up to date (as it's the result of 'get' + % requests for both) and there is no risk of the 'battle' update influencing + % 'character', making what follows safe. + + S2Update = + btl_character_turn_update:ataxia_set_battle + ( + S2Battle, + false, + ataxic:optimize + ( + ataxic:sequence + ( + [ + BattleAtaxiaUpdate0, + BattleAtaxiaUpdate1, + BattleAtaxiaUpdate2 + ] + ) + ), + S1Update + ), + + S3Update = + btl_character_turn_update:ataxia_set_character + ( + UpdatedCharacter, + CharacterAtaxiaUpdate, + S2Update + ), + + TimelineItem = + btl_turn_result:new_character_attacked + ( + btl_character_turn_update:get_character_ix(S3Update), + TargetIX, + AttackEffects, + S0NewAttackerLuck, + S0NewDefenderLuck + ), + + S4Update = btl_character_turn_update:add_to_timeline(TimelineItem, S3Update), + + S5Update = + case (RemainingAttackerHealth > 0) of + true -> S4Update; + false -> + btl_victory_progression:handle_character_loss(Character, S4Update) + end, + + S6Update = + case (RemainingAttackerHealth > 0) of + true -> S5Update; + false -> + btl_victory_progression:handle_character_loss + ( + TargetCharacterRef, + S5Update + ) + end, + + {ok, S6Update}. diff --git a/src/battle/struct/btl_attack.erl b/src/battle/struct/btl_attack.erl index 481e07f..189cb0e 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'). +-type order() :: ('first' | 'second' | 'counter' | 'opportunity'). -type precision() :: ('misses' | 'grazes' | 'hits'). -record @@ -35,7 +35,8 @@ [ get_sequence/3, get_description_of/5, - apply_to_healths_and_lucks/5 + apply_to_healths_and_lucks/5, + new_opportunity/2, ] ). -- cgit v1.2.3-70-g09d2