summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/battle/game-logic/btl_turn_actions.erl371
-rw-r--r--src/battle/game-logic/btl_turn_actions_attack.erl223
-rw-r--r--src/battle/game-logic/btl_turn_actions_move.erl132
-rw-r--r--src/battle/game-logic/btl_turn_actions_switch_weapon.erl67
-rw-r--r--src/battle/struct/btl_attack.erl101
-rw-r--r--src/battle/struct/btl_character_current_data.erl113
-rw-r--r--src/battle/struct/btl_character_turn_data.erl24
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