summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/battle/game-logic/btl_turn_actions.erl | 371 | ||||
-rw-r--r-- | src/battle/game-logic/btl_turn_actions_attack.erl | 223 | ||||
-rw-r--r-- | src/battle/game-logic/btl_turn_actions_move.erl | 132 | ||||
-rw-r--r-- | src/battle/game-logic/btl_turn_actions_switch_weapon.erl | 67 | ||||
-rw-r--r-- | src/battle/struct/btl_attack.erl | 101 | ||||
-rw-r--r-- | src/battle/struct/btl_character_current_data.erl | 113 | ||||
-rw-r--r-- | src/battle/struct/btl_character_turn_data.erl | 24 |
7 files changed, 645 insertions, 386 deletions
diff --git a/src/battle/game-logic/btl_turn_actions.erl b/src/battle/game-logic/btl_turn_actions.erl index caf2e26..6ff09aa 100644 --- a/src/battle/game-logic/btl_turn_actions.erl +++ b/src/battle/game-logic/btl_turn_actions.erl @@ -9,139 +9,39 @@ -export ( [ - handle/2 + handle/2, + handle_max_health_changes/2 ] ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%% SWITCHING WEAPON %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec handle_switch_weapon +-spec mod_current_health ( + non_neg_integer(), + non_neg_integer(), btl_character_turn_update:type() ) -> btl_character_turn_update:type(). -handle_switch_weapon (Update) -> +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), - CharacterAttributes = btl_character:get_attributes(Character), - ArmorID = btl_character:get_armor_id(Character), - {PrimaryWeaponID, SecondaryWeaponID} = btl_character:get_weapon_ids(Character), + PreviousHealth = btl_character:get_current_health(Character), - UpdatedWeaponIDs = {SecondaryWeaponID, PrimaryWeaponID}, - UpdatedCharacterStatistics = - shr_statistics:new(CharacterAttributes, UpdatedWeaponIDs, ArmorID), - UpdatedCharacter = - btl_character:set_statistics + PreviousHealthRatio = (PreviousHealth / PreviousMaxHealth), + NewHealth = + min ( - UpdatedCharacterStatistics, - btl_character:set_weapon_ids(UpdatedWeaponIDs, Character) - ), - - TimelineItem = btl_turn_result:new_character_switched_weapons(CharacterIX), - - DBQuery = - shr_db_query:update_indexed - ( - btl_battle:get_characters_field(), - CharacterIX, - [ - shr_db_query:set_field - ( - btl_character:get_weapons_field(), - UpdatedWeaponIDs - ), - shr_db_query:set_field - ( - btl_character:get_statistics_field(), - UpdatedCharacterStatistics - ) - ] + 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), - btl_character_turn_update:add_to_timeline(TimelineItem, DBQuery, S0Update). - -%%%% MOVING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --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 = - array:foldl - ( - fun (IX, Char, Prev) -> - IsAlive = btl_character:get_is_alive(Char), - if - (IX == CharacterIX) -> Prev; - (not IsAlive) -> Prev; - true -> [btl_character:get_location(Char)|Prev] - end - end, - [], - btl_battle:get_characters(Battle) - ), - - {NewLocation, Cost} = - btl_movement: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) -> - Character = btl_character_turn_data:get_character(Data), - CharacterStatistics = btl_character:get_statistics(Character), - CharacterMovementPoints = - shr_statistics:get_movement_points(CharacterStatistics), - - true = (Cost =< CharacterMovementPoints), - - ok. - --spec commit_move - ( - btl_character_turn_update:type(), - list(btl_direction:type()), - btl_location:type() - ) - -> btl_character_turn_update:type(). -commit_move (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), - - UpdatedData = btl_character_turn_data:set_character(UpdatedCharacter, Data), - - TimelineItem = - btl_turn_result:new_character_moved(CharacterIX, Path, NewLocation), - DBQuery = shr_db_query:update_indexed ( @@ -150,229 +50,16 @@ commit_move (Update, Path, NewLocation) -> [ shr_db_query:set_field ( - btl_character:get_location_field(), - NewLocation - ) - ] - ), - - S0Update = - btl_character_turn_update:add_to_timeline - ( - TimelineItem, - DBQuery, - Update - ), - - btl_character_turn_update:set_data(UpdatedData, S0Update). - --spec handle_move - ( - btl_battle_action:type(), - btl_character_turn_update:type() - ) - -> btl_character_turn_update:type(). -handle_move (BattleAction, Update) -> - Data = btl_character_turn_update:get_data(Update), - Path = btl_battle_action:get_path(BattleAction), - - {PathCost, NewLocation} = get_path_cost_and_destination(Data, Path), - assert_character_can_move(Data, PathCost), - - commit_move(Update, Path, NewLocation). - -%%%% ATTACKING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec handle_attack_sequence - ( - btl_character:type(), - btl_character:type(), - list(btl_attack:step()) - ) - -> {list(btl_attack:type()), non_neg_integer(), non_neg_integer()}. -handle_attack_sequence -( - Character, - TargetCharacter, - AttackSequence -) -> - AttackPlannedEffects = - lists:map - ( - fun (AttackStep) -> - btl_attack:get_description_of - ( - AttackStep, - Character, - TargetCharacter - ) - 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, - { - [], - btl_character:get_current_health(Character), - btl_character:get_current_health(TargetCharacter) - }, - 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). - - --spec handle_attack - ( - btl_battle_action:type(), - btl_character_turn_update:type() - ) - -> btl_character_turn_update:type(). -handle_attack (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), - TargetIX = btl_battle_action:get_target_ix(BattleAction), - TargetCharacter = btl_battle:get_character(TargetIX, Battle), - - true = btl_character:get_is_alive(TargetCharacter), - - AttackSequence = get_attack_sequence(Character, TargetCharacter), - - {AttackEffects, RemainingAttackerHealth, RemainingDefenderHealth} = - handle_attack_sequence(Character, 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 = - shr_db_query:update_indexed - ( - btl_battle:get_characters_field(), - TargetIX, - [ - shr_db_query:set_field - ( - btl_character:get_current_health_field(), - RemainingDefenderHealth - ) - ] - ), - - DBQuery1 = - shr_db_query:update_indexed - ( - btl_battle:get_characters_field(), - CharacterIX, - [ - shr_db_query:set_field - ( btl_character:get_current_health_field(), - RemainingAttackerHealth + NewHealth ) ] ), - S0Update = - btl_character_turn_update:add_to_timeline - ( - TimelineItem, - DBQuery0, - Update - ), + S1Update = btl_character_turn_update:add_to_db(DBQuery, S0Update), - S1Update = - btl_character_turn_update:add_to_db - ( - DBQuery1, - S0Update - ), + S1Update. - 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. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -385,7 +72,27 @@ handle_attack (BattleAction, Update) -> -> btl_character_turn_update:type(). handle (BattleAction, Update) -> case btl_battle_action:get_category(BattleAction) of - move -> handle_move(BattleAction, Update); - switch_weapon -> handle_switch_weapon(Update); - attack -> handle_attack(BattleAction, Update) + 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. + +-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/game-logic/btl_turn_actions_attack.erl b/src/battle/game-logic/btl_turn_actions_attack.erl new file mode 100644 index 0000000..355c791 --- /dev/null +++ b/src/battle/game-logic/btl_turn_actions_attack.erl @@ -0,0 +1,223 @@ +-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 = + shr_db_query:update_indexed + ( + btl_battle:get_characters_field(), + TargetIX, + [ + shr_db_query:set_field + ( + btl_character:get_current_health_field(), + RemainingDefenderHealth + ) + ] + ), + + DBQuery1 = + shr_db_query:update_indexed + ( + btl_battle:get_characters_field(), + CharacterIX, + [ + shr_db_query:set_field + ( + btl_character:get_current_health_field(), + 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/game-logic/btl_turn_actions_move.erl b/src/battle/game-logic/btl_turn_actions_move.erl new file mode 100644 index 0000000..5669b75 --- /dev/null +++ b/src/battle/game-logic/btl_turn_actions_move.erl @@ -0,0 +1,132 @@ +-module(btl_turn_actions_move). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + handle/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-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 = + array:foldl + ( + fun (IX, Char, Prev) -> + IsAlive = btl_character:get_is_alive(Char), + if + (IX == CharacterIX) -> Prev; + (not IsAlive) -> Prev; + true -> [btl_character:get_location(Char)|Prev] + end + end, + [], + btl_battle:get_characters(Battle) + ), + + {NewLocation, Cost} = + btl_movement: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) -> + Character = btl_character_turn_data:get_character(Data), + CharacterStatistics = btl_character:get_statistics(Character), + CharacterMovementPoints = + shr_statistics:get_movement_points(CharacterStatistics), + + true = (Cost =< CharacterMovementPoints), + + ok. + +-spec commit_move + ( + btl_character_turn_update:type(), + list(btl_direction:type()), + btl_location:type() + ) + -> btl_character_turn_update:type(). +commit_move (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), + + UpdatedData = btl_character_turn_data:set_character(UpdatedCharacter, Data), + + TimelineItem = + btl_turn_result:new_character_moved(CharacterIX, Path, NewLocation), + + DBQuery = + shr_db_query:update_indexed + ( + btl_battle:get_characters_field(), + CharacterIX, + [ + shr_db_query:set_field + ( + btl_character:get_location_field(), + NewLocation + ) + ] + ), + + S0Update = + btl_character_turn_update:add_to_timeline + ( + TimelineItem, + DBQuery, + Update + ), + + btl_character_turn_update:set_data(UpdatedData, S0Update). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 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), + Path = btl_battle_action:get_path(BattleAction), + + {PathCost, NewLocation} = get_path_cost_and_destination(Data, Path), + assert_character_can_move(Data, PathCost), + + commit_move(Update, Path, NewLocation). diff --git a/src/battle/game-logic/btl_turn_actions_switch_weapon.erl b/src/battle/game-logic/btl_turn_actions_switch_weapon.erl new file mode 100644 index 0000000..6872ff4 --- /dev/null +++ b/src/battle/game-logic/btl_turn_actions_switch_weapon.erl @@ -0,0 +1,67 @@ +-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:handle_max_health_changes + ( + CharacterCurrentData, + S0Update + ), + + TimelineItem = btl_turn_result:new_character_switched_weapons(CharacterIX), + + DBQuery = + shr_db_query:update_indexed + ( + btl_battle:get_characters_field(), + CharacterIX, + [ + shr_db_query:set_field + ( + btl_character:get_weapons_field(), + UpdatedWeaponIDs + ) + ] + ), + + btl_character_turn_update:add_to_timeline(TimelineItem, DBQuery, S1Update). diff --git a/src/battle/struct/btl_attack.erl b/src/battle/struct/btl_attack.erl index 04302b3..2411529 100644 --- a/src/battle/struct/btl_attack.erl +++ b/src/battle/struct/btl_attack.erl @@ -30,7 +30,7 @@ ( [ get_sequence/3, - get_description_of/5, + get_description_of/3, apply_to_healths/3 ] ). @@ -71,34 +71,15 @@ roll_parry (DefenderStatistics) -> DefenderParryChance = shr_statistics:get_parries(DefenderStatistics), (shr_roll:percentage() =< DefenderParryChance). --spec effect_of_attack +-spec get_damage ( - order(), - shr_statistics:type(), + precision(), + boolean(), shr_omnimods:type(), - shr_statistics:type(), - shr_omnimods:type(), - boolean() + shr_omnimods:type() ) - -> type(). -effect_of_attack (Order, AtkStats, AtkOmni, DefStats, DefOmni, CanParry) -> - ParryIsSuccessful = (CanParry and roll_parry(DefStats)), - - {ActualAtkStats, ActualDefStats} = - case ParryIsSuccessful of - true -> {DefStats, AtkStats}; - false -> {AtkStats, DefStats} - end, - - {ActualAtkOmni, ActualDefOmni} = - case ParryIsSuccessful of - true -> {DefOmni, AtkOmni}; - false -> {AtkOmni, DefOmni} - end, - - Precision = roll_precision(ActualAtkStats, ActualDefStats), - IsCritical = roll_critical_hit(ActualAtkStats), - + -> non_neg_integer(). +get_damage (Precision, IsCritical, ActualAtkOmni, ActualDefOmni) -> S0DamageMultiplier = case Precision of misses -> 0; @@ -109,7 +90,7 @@ effect_of_attack (Order, AtkStats, AtkOmni, DefStats, DefOmni, CanParry) -> S1DamageMultiplier = case IsCritical of true -> (S0DamageMultiplier * 2); - false -> S0DamageMultiplier + _ -> S0DamageMultiplier end, ActualDamage = @@ -120,13 +101,43 @@ effect_of_attack (Order, AtkStats, AtkOmni, DefStats, DefOmni, CanParry) -> ActualDefOmni ), + ActualDamage. + +-spec effect_of_attack + ( + order(), + btl_character_current_data:type(), + btl_character_current_data:type(), + boolean() + ) + -> type(). +effect_of_attack (Order, AtkCurrData, DefCurrData, CanParry) -> + DefStats = btl_character_current_data:get_statistics(DefCurrData), + + ParryIsSuccessful = (CanParry and roll_parry(DefStats)), + + {ActualAtkData, ActualDefData} = + case ParryIsSuccessful of + true -> {DefCurrData, AtkCurrData}; + false -> {AtkCurrData, DefCurrData} + end, + + ActualAtkStats = btl_character_current_data:get_statistics(ActualAtkData), + ActualAtkOmni = btl_character_current_data:get_omnimods(ActualAtkData), + ActualDefStats = btl_character_current_data:get_statistics(ActualDefData), + ActualDefOmni = btl_character_current_data:get_omnimods(ActualDefData), + + Precision = roll_precision(ActualAtkStats, ActualDefStats), + IsCritical = roll_critical_hit(ActualAtkStats), + Damage = get_damage(Precision, IsCritical, ActualAtkOmni, ActualDefOmni), + #attack { order = Order, precision = Precision, is_critical = IsCritical, is_parry = ParryIsSuccessful, - damage = ActualDamage + damage = Damage }. -spec encode_order (order()) -> binary(). @@ -146,42 +157,26 @@ encode_precision (misses) -> <<"m">>. -spec get_description_of ( step(), - shr_statistics:type(), - shr_omnimods:type(), - shr_statistics:type(), - shr_omnimods:type() + btl_character_current_data:type(), + btl_character_current_data:type() ) -> maybe_type(). -get_description_of ({first, CanParry}, AtkStats, AtkOmni, DefStats, DefOmni) -> - effect_of_attack(first, AtkStats, AtkOmni, DefStats, DefOmni, CanParry); -get_description_of ({second, CanParry}, AtkStats, AtkOmni, DefStats, DefOmni) -> +get_description_of ({first, CanParry}, AtkCurrData, DefCurrData) -> + effect_of_attack(first, AtkCurrData, DefCurrData, CanParry); +get_description_of ({second, CanParry}, AtkCurrData, DefCurrData) -> + AtkStats = btl_character_current_data:get_statistics(AtkCurrData), AttackerDoubleAttackChange = shr_statistics:get_double_hits(AtkStats), case shr_roll:percentage() of X when (X =< AttackerDoubleAttackChange) -> - effect_of_attack - ( - second, - AtkStats, - AtkOmni, - DefStats, - DefOmni, - CanParry - ); + effect_of_attack(second, AtkCurrData, DefCurrData, CanParry); _ -> nothing end; -get_description_of -( - {counter, CanParry}, - AtkStats, - AtkOmni, - DefStats, - DefOmni -) -> - effect_of_attack(counter, DefStats, DefOmni, AtkStats, AtkOmni, CanParry). +get_description_of ({counter, CanParry}, AtkCurrData, DefCurrData) -> + effect_of_attack(counter, DefCurrData, AtkCurrData, CanParry). -spec apply_to_healths ( diff --git a/src/battle/struct/btl_character_current_data.erl b/src/battle/struct/btl_character_current_data.erl new file mode 100644 index 0000000..60bfdbc --- /dev/null +++ b/src/battle/struct/btl_character_current_data.erl @@ -0,0 +1,113 @@ +-module(btl_character_current_data). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + character_current_data, + { + attributes :: shr_attributes:type(), + statistics :: shr_statistics:type(), + omnimods :: shr_omnimods:type() + } +). + +-opaque type() :: #character_current_data{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_attributes/1, + get_statistics/1, + get_omnimods/1 + ] +). + +-export +( + [ + new/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec location_to_omnimods + ( + {non_neg_integer(), non_neg_integer()}, + btl_map:type() + ) + -> shr_omnimods:type(). +location_to_omnimods (Location, Map) -> + TileInstance = btl_map:get_tile_instance(Location, Map), + TileClassID = shr_tile:extract_main_class_id(TileInstance), + Tile = shr_tile:from_class_id(TileClassID), + + shr_tile:get_omnimods(Tile). + +-spec weapon_id_to_omnimods (shr_weapon:id()) -> shr_omnimods:type(). +weapon_id_to_omnimods (WeaponID) -> + Weapon = shr_weapon:from_id(WeaponID), + + shr_weapon:get_omnimods(Weapon). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_omnimods (type()) -> shr_omnimods:type(). +get_omnimods (Char) -> Char#character_current_data.omnimods. + +-spec get_attributes (type()) -> shr_attributes:type(). +get_attributes (Char) -> Char#character_current_data.attributes. + +-spec get_statistics (type()) -> shr_statistics:type(). +get_statistics (Char) -> Char#character_current_data.statistics. + +%%%% Utils +-spec new (btl_character:type(), btl_map:type()) -> type(). +new (Character, Map) -> + PermanentOmnimods = btl_character:get_permanent_omnimods(Character), + + {WeaponID, _} = btl_character:get_weapon_ids(Character), + WeaponOmnimods = weapon_id_to_omnimods(WeaponID), + + Location = btl_character:get_location(Character), + TileOmnimods = location_to_omnimods(Location, Map), + + CurrentOmnimods = + shr_omnimods:merge + ( + shr_omnimods:merge(WeaponOmnimods, TileOmnimods), + PermanentOmnimods + ), + + CurrentAttributes = + shr_omnimods:apply_to_attributes + ( + CurrentOmnimods, + shr_attributes:default() + ), + + CurrentStatistics = + shr_omnimods:apply_to_statistics + ( + CurrentOmnimods, + shr_statistics:new_raw(CurrentAttributes) + ), + + #character_current_data + { + attributes = CurrentAttributes, + statistics = CurrentStatistics, + omnimods = CurrentOmnimods + }. + diff --git a/src/battle/struct/btl_character_turn_data.erl b/src/battle/struct/btl_character_turn_data.erl index 64df29f..6742d1c 100644 --- a/src/battle/struct/btl_character_turn_data.erl +++ b/src/battle/struct/btl_character_turn_data.erl @@ -10,6 +10,7 @@ dirty :: boolean(), battle :: btl_battle:type(), character :: btl_character:type(), + character_current_data :: btl_character_current_data:type(), character_ix :: non_neg_integer() } ). @@ -29,10 +30,12 @@ get_battle_is_dirty/1, get_battle/1, get_character/1, + get_character_current_data/1, get_character_ix/1, set_battle/2, - set_character/2 + set_character/2, + refresh_character_current_data/1 ] ). @@ -54,12 +57,15 @@ -spec new (btl_battle:type(), non_neg_integer()) -> type(). new (Battle, CharacterIX) -> Character = btl_battle:get_character(CharacterIX, Battle), + Map = btl_battle:get_map(Battle), + CharacterCurrentData = btl_character_current_data:new(Character, Map), #type { dirty = false, battle = Battle, character = Character, + character_current_data = CharacterCurrentData, character_ix = CharacterIX }. @@ -72,6 +78,9 @@ get_battle (Data) -> Data#type.battle. -spec get_character (type()) -> btl_character:type(). get_character (Data) -> Data#type.character. +-spec get_character_current_data (type()) -> btl_character_current_data:type(). +get_character_current_data (Data) -> Data#type.character_current_data. + -spec get_character_ix (type()) -> non_neg_integer(). get_character_ix (Data) -> Data#type.character_ix. @@ -87,6 +96,19 @@ set_character (Character, Data) -> character = Character }. +-spec refresh_character_current_data (type()) -> type(). +refresh_character_current_data (Data) -> + Battle = Data#type.battle, + Character = Data#type.character, + Map = btl_battle:get_map(Battle), + CharacterCurrentData = btl_character_current_data:new(Character, Map), + + Data#type + { + character_current_data = CharacterCurrentData + }. + + -spec clean_battle (type()) -> type(). clean_battle (Data) -> Data#type |