From dc119102df0076e45ee6c484e361c3fff3e6e2dd Mon Sep 17 00:00:00 2001 From: Nathanael Sensfelder Date: Fri, 2 Mar 2018 15:43:23 +0100 Subject: Still working on it... --- src/battle/attack.erl | 279 ++++++++++++++++++++++++++++++++++++++ src/battle/battle_turn.erl | 135 ++++++++++++++++++ src/battle/movement.erl | 58 ++++++++ src/battle/roll.erl | 32 +++++ src/battlemap/attack.erl | 279 -------------------------------------- src/battlemap/movement.erl | 58 -------- src/battlemap/roll.erl | 32 ----- src/handler.erl | 2 +- src/query/character_turn.erl | 208 +++++++++------------------- src/query/load_state.erl | 24 ++-- src/shim/database_shim.erl | 6 +- src/struct/battle.erl | 227 +++++++++++++++++++++++++++++++ src/struct/battle_action.erl | 123 +++++++++-------- src/struct/battlemap_instance.erl | 196 -------------------------- 14 files changed, 880 insertions(+), 779 deletions(-) create mode 100644 src/battle/attack.erl create mode 100644 src/battle/battle_turn.erl create mode 100644 src/battle/movement.erl create mode 100644 src/battle/roll.erl delete mode 100644 src/battlemap/attack.erl delete mode 100644 src/battlemap/movement.erl delete mode 100644 src/battlemap/roll.erl create mode 100644 src/struct/battle.erl delete mode 100644 src/struct/battlemap_instance.erl diff --git a/src/battle/attack.erl b/src/battle/attack.erl new file mode 100644 index 0000000..7384f78 --- /dev/null +++ b/src/battle/attack.erl @@ -0,0 +1,279 @@ +-module(attack). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% TODO: find better names for those types. +-type hits() :: ('misses' | 'grazes' | 'hits'). +-type critical() :: ('critical' | 'basic'). +-type attack_order() :: 'first' | 'second' | 'counter'. +-type attack_order_with_parry() :: {attack_order(), boolean()}. +-type attack_category() :: + ( + attack_order() + | {attack_order(), 'parry'} + ). +-type attack_effect() :: {hits(), critical(), non_neg_integer()}. +-type attack_desc() :: + ( + {attack_category(), attack_effect()} + | 'nothing' + ). + +-export_type +( + [ + hits/0, + critical/0, + attack_category/0, + attack_effect/0, + attack_desc/0 + ] +). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + get_sequence/3, + get_description_of/3, + apply_to_healths/3 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec roll_hits + ( + statistics:struct(), + statistics:struct() + ) + -> hits(). +roll_hits (AttackerStatistics, DefenderStatistics) -> + DefenderDodges = statistics:get_dodges(DefenderStatistics), + AttackerAccuracy = statistics:get_accuracy(AttackerStatistics), + MissChance = max(0, (DefenderDodges - AttackerAccuracy)), + case roll:percentage() of + X when (X =< MissChance) -> misses; + X when (X =< (MissChance * 2)) -> grazes; + _ -> hits + end. + +-spec roll_damage + ( + statistics:struct(), + statistics:struct() + ) + -> {critical(), non_neg_integer()}. +roll_damage (AttackerStatistics, _DefenderStatistics) -> + {MinimumDamage, MaximumDamage} = statistics:get_damages(AttackerStatistics), + MaximumRoll = max(1, MaximumDamage - MinimumDamage), + BaseDamage = MinimumDamage + (rand:uniform(MaximumRoll) - 1), + CriticalHitChance = statistics:get_critical_hits(AttackerStatistics), + case roll:percentage() of + X when (X =< CriticalHitChance) -> {critical, (BaseDamage * 2)}; + _ -> {basic, BaseDamage} + end. + +-spec effect_of_attack + ( + statistics:struct(), + statistics:struct() + ) + -> attack_effect(). +effect_of_attack (AttackerStatistics, DefenderStatistics) -> + Hits = roll_hits(AttackerStatistics, DefenderStatistics), + {Critical, Damage} = roll_damage(AttackerStatistics, DefenderStatistics), + case Hits of + misses -> {Hits, Critical, 0}; + grazes -> {Hits, Critical, trunc(Damage / 2)}; + hits -> {Hits, Critical, Damage} + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec get_description_of + ( + attack_order_with_parry(), + statistics:struct(), + statistics:struct() + ) + -> attack_desc(). +get_description_of +( + {first, DefenderCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + DefenderParryChance = statistics:get_parries(DefenderStatistics), + ParryRoll = + case DefenderCanParry of + true -> roll:percentage(); + _ -> 101 + end, + if + (ParryRoll =< DefenderParryChance) -> + { + {first, parry}, + effect_of_attack(DefenderStatistics, AttackerStatistics) + }; + + true -> + {first, effect_of_attack(AttackerStatistics, DefenderStatistics)} + end; +get_description_of +( + {second, DefenderCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + DefenderParryChance = statistics:get_parries(DefenderStatistics), + ParryRoll = + case DefenderCanParry of + true -> roll:percentage(); + _ -> 101 + end, + AttackerDoubleAttackChange = statistics:get_double_hits(AttackerStatistics), + DoubleAttackRoll = roll:percentage(), + if + (DoubleAttackRoll > AttackerDoubleAttackChange) -> + nothing; + + (ParryRoll =< DefenderParryChance) -> + { + {second, parry}, + effect_of_attack(DefenderStatistics, AttackerStatistics) + }; + + true -> + {second, effect_of_attack(AttackerStatistics, DefenderStatistics)} + end; +get_description_of +( + {counter, AttackerCanParry}, + AttackerStatistics, + DefenderStatistics +) -> + AttackerParryChance = statistics:get_parries(AttackerStatistics), + ParryRoll = + case AttackerCanParry of + true -> roll:percentage(); + _ -> 101 + end, + if + (ParryRoll =< AttackerParryChance) -> + { + {counter, parry}, + effect_of_attack(AttackerStatistics, DefenderStatistics) + }; + + true -> + {counter, effect_of_attack(DefenderStatistics, AttackerStatistics)} + end. + +-spec apply_to_healths + ( + attack_desc(), + non_neg_integer(), + non_neg_integer() + ) + -> {attack_desc(), non_neg_integer(), non_neg_integer()}. +apply_to_healths +( + nothing, + AttackerHealth, + DefenderHealth +) -> + {nothing, AttackerHealth, DefenderHealth}; +apply_to_healths +( + {Attack, Effect}, + AttackerHealth, + DefenderHealth +) +when +( + (Attack == first) + or (Attack == second) + or (Attack == {counter, parry}) +) -> + {_Hits, _Critical, Damage} = Effect, + case AttackerHealth of + 0 -> + {nothing, AttackerHealth, DefenderHealth}; + + _ -> + { + {Attack, Effect}, + AttackerHealth, + max(0, (DefenderHealth - Damage)) + } + end; +apply_to_healths +( + {Attack, Effect}, + AttackerHealth, + DefenderHealth +) +when +( + (Attack == {first, parry}) + or (Attack == {second, parry}) + or (Attack == counter) +) -> + {_Hits, _Critical, Damage} = Effect, + case DefenderHealth of + 0 -> + {nothing, AttackerHealth, DefenderHealth}; + + _ -> + { + {Attack, Effect}, + max(0, (AttackerHealth - Damage)), + DefenderHealth + } + end. + +-spec get_sequence + ( + non_neg_integer(), + weapon:struct(), + weapon:struct() + ) + -> list(attack_order_with_parry()). +get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) -> + {AttackerDefenseRange, AttackerAttackRange} = + weapon:get_ranges(AttackerWeapon), + {DefenderDefenseRange, DefenderAttackRange} = + weapon:get_ranges(DefenderWeapon), + + AttackerCanAttack = (AttackRange =< AttackerAttackRange), + AttackerCanDefend = + (AttackerCanAttack and (AttackRange > AttackerDefenseRange)), + AttackerCanParry = + (AttackerCanDefend and weapon:can_parry(AttackerWeapon)), + + DefenderCanAttack = (AttackRange =< DefenderAttackRange), + DefenderCanDefend = + (DefenderCanAttack and (AttackRange > DefenderDefenseRange)), + DefenderCanParry = + (DefenderCanDefend and weapon:can_parry(DefenderWeapon)), + + First = {first, DefenderCanParry}, + Second = {second, DefenderCanParry}, + Counter = {counter, AttackerCanParry}, + + if + (not AttackerCanAttack) -> + []; + + (not DefenderCanDefend) -> + [First, Second]; + + true -> + [First, Counter, Second] + end. diff --git a/src/battle/battle_turn.erl b/src/battle/battle_turn.erl new file mode 100644 index 0000000..e295f09 --- /dev/null +++ b/src/battle/battle_turn.erl @@ -0,0 +1,135 @@ +-module(battle_turn). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + handle_post_play/1 + ] +). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec activate_relevant_character_instances + ( + list(non_neg_integer()), + array:array(character_instance:struct()), + player:id(), + (-1 | non_neg_integer()) + ) + -> {list(non_neg_integer()), array:array(character_instance:struct())}. +activate_relevant_character_instances (IXs, CharacterInstances, _Owner, -1) -> + {IXs, CharacterInstances}; +activate_relevant_character_instances (IXs, CharacterInstances, Owner, IX) -> + CharacterInstance = array:get(IX, CharacterInstances), + Character = character_instance:get_character(CharacterInstance), + case character:get_owner_id(Character) of + OwnerID when (OwnerID == Owner) -> + activate_relevant_character_instances + ( + [IX|IXs], + array:set + ( + IX, + character_instance:set_is_active(true, CharacterInstance), + CharacterInstances + ), + Owner, + (IX - 1) + ); + + _ -> + activate_relevant_character_instances + ( + IXs, + CharacterInstances, + Owner, + (IX - 1) + ) + end. + +-spec start_next_players_turn (battle:struct()) -> + {list(non_neg_integer()), battle:struct()}. +start_next_players_turn (Battle) -> + PlayerIDs = battle:get_player_ids(Battle), + PlayerTurn = battle:get_current_player_turn(Battle), + CurrentPlayerIX = player_turn:get_player_ix(PlayerTurn), + CurrentTurnNumber = player_turn:get_number(PlayerTurn), + CharacterInstances = battle:get_character_instances(Battle), + + NextPlayerIX = ((CurrentPlayerIX + 1) rem (array:size(PlayerIDs))), + NextPlayerTurn = + player_turn:new + ( + case NextPlayerIX of + 0 -> (CurrentTurnNumber + 1); + _ -> CurrentTurnNumber + end, + NextPlayerIX + ), + + {ActivatedCharacterInstanceIXs, UpdatedCharacterInstances} = + activate_relevant_character_instances + ( + [], + CharacterInstances, + array:get(NextPlayerIX, PlayerIDs), + (array:size(CharacterInstances) - 1) + ), + UpdatedBattle = + battle:set_character_instances + ( + UpdatedCharacterInstances, + battle:set_current_player_turn + ( + NextPlayerTurn, + Battle + ) + ), + {ActivatedCharacterInstanceIXs, UpdatedBattle}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec handle_post_play (battle:struct()) -> + {database_diff:struct(), battle:struct()}. +handle_post_play (Battle) -> + CharacterInstances = battle:get_character_instances(Battle), + + AnActiveCharacterInstanceRemains = + array:foldl + ( + fun (_IX, CharacterInstance, Prev) -> + (Prev or character_instance:get_is_active(CharacterInstance)) + end, + false, + CharacterInstances + ), + + case AnActiveCharacterInstanceRemains of + true -> + io:format("~nThere are still active characters.~n"), + {[], Battle}; + + false -> + io:format("~nThere are no more active characters.~n"), + {UpdatedCharacterInstanceIXs, UpdatedBattle} = + start_next_players_turn(Battle), + { + lists:map + ( + fun (IX) -> + {set, character_instance, IX, is_active, true} + end, + UpdatedCharacterInstanceIXs + ), + UpdatedBattle + } + end. diff --git a/src/battle/movement.erl b/src/battle/movement.erl new file mode 100644 index 0000000..720b60c --- /dev/null +++ b/src/battle/movement.erl @@ -0,0 +1,58 @@ +-module(movement). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([cross/4]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec cross + ( + battlemap:struct(), + list(location:type()), + list(direction:enum()), + non_neg_integer(), + location:type() + ) + -> {location:type(), non_neg_integer()}. +cross (_Battlemap, _ForbiddenLocations, [], Cost, Location) -> + {Location, Cost}; +cross (Battlemap, ForbiddenLocations, [Step|NextSteps], Cost, Location) -> + NextLocation = location:apply_direction(Step, Location), + NextTile = battlemap:get_tile_id(NextLocation, Battlemap), + NextCost = (Cost + tile:get_cost(NextTile)), + IsForbidden = + lists:foldl + ( + fun (ForbiddenLocation, Prev) -> + (Prev or (NextLocation == ForbiddenLocation)) + end, + false, + ForbiddenLocations + ), + + IsForbidden = false, + + cross(Battlemap, ForbiddenLocations, NextSteps, NextCost, NextLocation). + +-spec cross + ( + battlemap:struct(), + array:array(location:type()), + list(direction:enum()), + location:type() + ) + -> {location:type(), non_neg_integer()}. +cross (Battlemap, ForbiddenLocations, Path, Location) -> + cross(Battlemap, ForbiddenLocations, Path, 0, Location). diff --git a/src/battle/roll.erl b/src/battle/roll.erl new file mode 100644 index 0000000..074054b --- /dev/null +++ b/src/battle/roll.erl @@ -0,0 +1,32 @@ +-module(roll). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + percentage/0, + between/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec between (non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +between (Min, Max) -> + Diff = (Max - Min), + (Min + (rand:uniform(Diff + 1) - 1)). + +-spec percentage () -> 0..100. +percentage () -> + between(0, 100). diff --git a/src/battlemap/attack.erl b/src/battlemap/attack.erl deleted file mode 100644 index 7384f78..0000000 --- a/src/battlemap/attack.erl +++ /dev/null @@ -1,279 +0,0 @@ --module(attack). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% TODO: find better names for those types. --type hits() :: ('misses' | 'grazes' | 'hits'). --type critical() :: ('critical' | 'basic'). --type attack_order() :: 'first' | 'second' | 'counter'. --type attack_order_with_parry() :: {attack_order(), boolean()}. --type attack_category() :: - ( - attack_order() - | {attack_order(), 'parry'} - ). --type attack_effect() :: {hits(), critical(), non_neg_integer()}. --type attack_desc() :: - ( - {attack_category(), attack_effect()} - | 'nothing' - ). - --export_type -( - [ - hits/0, - critical/0, - attack_category/0, - attack_effect/0, - attack_desc/0 - ] -). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export -( - [ - get_sequence/3, - get_description_of/3, - apply_to_healths/3 - ] -). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec roll_hits - ( - statistics:struct(), - statistics:struct() - ) - -> hits(). -roll_hits (AttackerStatistics, DefenderStatistics) -> - DefenderDodges = statistics:get_dodges(DefenderStatistics), - AttackerAccuracy = statistics:get_accuracy(AttackerStatistics), - MissChance = max(0, (DefenderDodges - AttackerAccuracy)), - case roll:percentage() of - X when (X =< MissChance) -> misses; - X when (X =< (MissChance * 2)) -> grazes; - _ -> hits - end. - --spec roll_damage - ( - statistics:struct(), - statistics:struct() - ) - -> {critical(), non_neg_integer()}. -roll_damage (AttackerStatistics, _DefenderStatistics) -> - {MinimumDamage, MaximumDamage} = statistics:get_damages(AttackerStatistics), - MaximumRoll = max(1, MaximumDamage - MinimumDamage), - BaseDamage = MinimumDamage + (rand:uniform(MaximumRoll) - 1), - CriticalHitChance = statistics:get_critical_hits(AttackerStatistics), - case roll:percentage() of - X when (X =< CriticalHitChance) -> {critical, (BaseDamage * 2)}; - _ -> {basic, BaseDamage} - end. - --spec effect_of_attack - ( - statistics:struct(), - statistics:struct() - ) - -> attack_effect(). -effect_of_attack (AttackerStatistics, DefenderStatistics) -> - Hits = roll_hits(AttackerStatistics, DefenderStatistics), - {Critical, Damage} = roll_damage(AttackerStatistics, DefenderStatistics), - case Hits of - misses -> {Hits, Critical, 0}; - grazes -> {Hits, Critical, trunc(Damage / 2)}; - hits -> {Hits, Critical, Damage} - end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - --spec get_description_of - ( - attack_order_with_parry(), - statistics:struct(), - statistics:struct() - ) - -> attack_desc(). -get_description_of -( - {first, DefenderCanParry}, - AttackerStatistics, - DefenderStatistics -) -> - DefenderParryChance = statistics:get_parries(DefenderStatistics), - ParryRoll = - case DefenderCanParry of - true -> roll:percentage(); - _ -> 101 - end, - if - (ParryRoll =< DefenderParryChance) -> - { - {first, parry}, - effect_of_attack(DefenderStatistics, AttackerStatistics) - }; - - true -> - {first, effect_of_attack(AttackerStatistics, DefenderStatistics)} - end; -get_description_of -( - {second, DefenderCanParry}, - AttackerStatistics, - DefenderStatistics -) -> - DefenderParryChance = statistics:get_parries(DefenderStatistics), - ParryRoll = - case DefenderCanParry of - true -> roll:percentage(); - _ -> 101 - end, - AttackerDoubleAttackChange = statistics:get_double_hits(AttackerStatistics), - DoubleAttackRoll = roll:percentage(), - if - (DoubleAttackRoll > AttackerDoubleAttackChange) -> - nothing; - - (ParryRoll =< DefenderParryChance) -> - { - {second, parry}, - effect_of_attack(DefenderStatistics, AttackerStatistics) - }; - - true -> - {second, effect_of_attack(AttackerStatistics, DefenderStatistics)} - end; -get_description_of -( - {counter, AttackerCanParry}, - AttackerStatistics, - DefenderStatistics -) -> - AttackerParryChance = statistics:get_parries(AttackerStatistics), - ParryRoll = - case AttackerCanParry of - true -> roll:percentage(); - _ -> 101 - end, - if - (ParryRoll =< AttackerParryChance) -> - { - {counter, parry}, - effect_of_attack(AttackerStatistics, DefenderStatistics) - }; - - true -> - {counter, effect_of_attack(DefenderStatistics, AttackerStatistics)} - end. - --spec apply_to_healths - ( - attack_desc(), - non_neg_integer(), - non_neg_integer() - ) - -> {attack_desc(), non_neg_integer(), non_neg_integer()}. -apply_to_healths -( - nothing, - AttackerHealth, - DefenderHealth -) -> - {nothing, AttackerHealth, DefenderHealth}; -apply_to_healths -( - {Attack, Effect}, - AttackerHealth, - DefenderHealth -) -when -( - (Attack == first) - or (Attack == second) - or (Attack == {counter, parry}) -) -> - {_Hits, _Critical, Damage} = Effect, - case AttackerHealth of - 0 -> - {nothing, AttackerHealth, DefenderHealth}; - - _ -> - { - {Attack, Effect}, - AttackerHealth, - max(0, (DefenderHealth - Damage)) - } - end; -apply_to_healths -( - {Attack, Effect}, - AttackerHealth, - DefenderHealth -) -when -( - (Attack == {first, parry}) - or (Attack == {second, parry}) - or (Attack == counter) -) -> - {_Hits, _Critical, Damage} = Effect, - case DefenderHealth of - 0 -> - {nothing, AttackerHealth, DefenderHealth}; - - _ -> - { - {Attack, Effect}, - max(0, (AttackerHealth - Damage)), - DefenderHealth - } - end. - --spec get_sequence - ( - non_neg_integer(), - weapon:struct(), - weapon:struct() - ) - -> list(attack_order_with_parry()). -get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) -> - {AttackerDefenseRange, AttackerAttackRange} = - weapon:get_ranges(AttackerWeapon), - {DefenderDefenseRange, DefenderAttackRange} = - weapon:get_ranges(DefenderWeapon), - - AttackerCanAttack = (AttackRange =< AttackerAttackRange), - AttackerCanDefend = - (AttackerCanAttack and (AttackRange > AttackerDefenseRange)), - AttackerCanParry = - (AttackerCanDefend and weapon:can_parry(AttackerWeapon)), - - DefenderCanAttack = (AttackRange =< DefenderAttackRange), - DefenderCanDefend = - (DefenderCanAttack and (AttackRange > DefenderDefenseRange)), - DefenderCanParry = - (DefenderCanDefend and weapon:can_parry(DefenderWeapon)), - - First = {first, DefenderCanParry}, - Second = {second, DefenderCanParry}, - Counter = {counter, AttackerCanParry}, - - if - (not AttackerCanAttack) -> - []; - - (not DefenderCanDefend) -> - [First, Second]; - - true -> - [First, Counter, Second] - end. diff --git a/src/battlemap/movement.erl b/src/battlemap/movement.erl deleted file mode 100644 index 02f0ebc..0000000 --- a/src/battlemap/movement.erl +++ /dev/null @@ -1,58 +0,0 @@ --module(movement). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([cross/4]). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec cross - ( - battlemap:struct(), - array:array(location:type()), - list(direction:enum()), - non_neg_integer(), - location:type() - ) - -> {location:type(), non_neg_integer()}. -cross (_Battlemap, _ForbiddenLocations, [], Cost, Location) -> - {Location, Cost}; -cross (Battlemap, ForbiddenLocations, [Step|NextSteps], Cost, Location) -> - NextLocation = direction:apply_to(Step, Location), - NextTile = battlemap:get_tile_id(Battlemap, NextLocation), - NextCost = (Cost + tile:get_cost(NextTile)), - IsForbidden = - array:foldl - ( - fun (_IX, ForbiddenLocation, Prev) -> - (Prev or (NextLocation == ForbiddenLocation)) - end, - false, - ForbiddenLocations - ), - - IsForbidden = false, - - cross(Battlemap, ForbiddenLocations, NextSteps, NextCost, NextLocation). - --spec cross - ( - battlemap:struct(), - array:array(location:type()), - list(direction:enum()), - location:type() - ) - -> {location:type(), non_neg_integer()}. -cross (Battlemap, ForbiddenLocations, Path, Location) -> - cross(Battlemap, ForbiddenLocations, Path, 0, Location). diff --git a/src/battlemap/roll.erl b/src/battlemap/roll.erl deleted file mode 100644 index 074054b..0000000 --- a/src/battlemap/roll.erl +++ /dev/null @@ -1,32 +0,0 @@ --module(roll). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export -( - [ - percentage/0, - between/2 - ] -). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec between (non_neg_integer(), non_neg_integer()) -> non_neg_integer(). -between (Min, Max) -> - Diff = (Max - Min), - (Min + (rand:uniform(Diff + 1) - 1)). - --spec percentage () -> 0..100. -percentage () -> - between(0, 100). diff --git a/src/handler.erl b/src/handler.erl index f1cc1a0..0ecc8be 100644 --- a/src/handler.erl +++ b/src/handler.erl @@ -19,5 +19,5 @@ start (_YawsParams) -> {ok, Pid} = timed_caches_manager:start(), database_shim:generate_db(Pid), - timed_caches_manager:new_cache(Pid, battlemap_instance_db, none), + timed_caches_manager:new_cache(Pid, battle_db, none), ok. diff --git a/src/query/character_turn.erl b/src/query/character_turn.erl index adfc542..818ac8a 100644 --- a/src/query/character_turn.erl +++ b/src/query/character_turn.erl @@ -27,19 +27,7 @@ } ). --record -( - query_result, - { - is_new_turn :: boolean(), - updated_character_instance_ixs :: list(non_neg_integer()), - updated_battle :: battle:struct() - } -). - -type input() :: #input{}. --type query_result() :: #query_result{}. - -type relevant_data() :: #relevant_data{}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -117,6 +105,8 @@ finalize_and_fuse_relevant_data (RData, Input) -> Battle = RData#relevant_data.battle, CharacterInstance = RData#relevant_data.played_character_instance, + io:format("~nNot a character instance? ~p~n", [CharacterInstance]), + FinalizedCharacterInstance = character_instance:set_is_active(false, CharacterInstance), @@ -127,125 +117,6 @@ finalize_and_fuse_relevant_data (RData, Input) -> Battle ). --spec activate_relevant_character_instances - ( - list(non_neg_integer()), - array:array(character_instance:struct()), - player:id(), - (-1 | non_neg_integer()) - ) - -> {list(non_neg_integer()), array:array(character_instance:struct())}. -activate_relevant_character_instances (IXs, CharacterInstances, _Owner, -1) -> - {IXs, CharacterInstances}; -activate_relevant_character_instances (IXs, CharacterInstances, Owner, IX) -> - CharacterInstance = array:get(IX, CharacterInstances), - Character = character_instance:get_character(CharacterInstance), - case character:get_owner_id(Character) of - OwnerID when (OwnerID == Owner) -> - activate_relevant_character_instances - ( - [IX|IXs], - array:set - ( - IX, - character_instance:set_is_active(true, CharacterInstance), - CharacterInstances - ), - Owner, - (IX - 1) - ); - - _ -> - activate_relevant_character_instances - ( - IXs, - CharacterInstances, - Owner, - (IX - 1) - ) - end. - --spec start_next_players_turn - ( - query_state() - ) - -> {list(non_neg_integer()), battle:struct()}. -start_next_players_turn (QueryState) -> - Battle = QueryState#query_state.battle, - PlayerIDs = battle:get_player_ids(Battle), - PlayerTurn = battle:get_current_player_turn(Battle), - CurrentPlayerIX = player_turn:get_player_ix(PlayerTurn), - CurrentTurnNumber = player_turn:get_number(PlayerTurn), - CharacterInstances = battle:get_character_instances(Battle), - - NextPlayerIX = ((CurrentPlayerIX + 1) rem (array:size(PlayerIDs))), - NextPlayerTurn = - player_turn:new - ( - case NextPlayerIX of - 0 -> (CurrentTurnNumber + 1); - _ -> CurrentTurnNumber - end, - NextPlayerIX - ), - - {ActivatedCharacterInstanceIXs, UpdatedCharacterInstances} = - activate_relevant_character_instances - ( - [], - CharacterInstances, - array:get(NextPlayerIX, PlayerIDs), - (array:size(CharacterInstances) - 1) - ), - UpdatedBattle = - battle:set_character_instances - ( - UpdatedCharacterInstances, - battle:set_current_player_turn - ( - NextPlayerTurn, - Battle - ) - ), - {ActivatedCharacterInstanceIXs, UpdatedBattle}. - --spec finalize_character_turn (query_state()) -> query_result(). -finalize_character_turn (QueryState) -> - Battle = QueryState#query_state.battle, - CharacterInstances = - battle:get_character_instances(Battle), - - AnActiveCharacterInstanceRemains = - array:foldl - ( - fun (_IX, CharacterInstance, Prev) -> - (Prev or character_instance:get_is_active(CharacterInstance)) - end, - false, - CharacterInstances - ), - - case AnActiveCharacterInstanceRemains of - true -> - io:format("~nThere are still active characters.~n"), - #query_result - { - is_new_turn = false, - updated_character_instance_ixs = [], - updated_battle = Battle - }; - false -> - io:format("~nThere are no more active characters.~n"), - {UpdatedCharacterInstanceIXs, UpdatedBattle} = - start_next_players_turn(QueryState), - #query_result - { - is_new_turn = true, - updated_character_instance_ixs = UpdatedCharacterInstanceIXs, - updated_battle = UpdatedBattle - } - end. - %-spec send_to_database (list(database_diff:struct()), input()) -> 'ok'. -spec send_to_database (battle:struct(), input()) -> 'ok'. send_to_database (FinalizedBattle, Input) -> @@ -261,11 +132,10 @@ send_to_database (FinalizedBattle, Input) -> FinalizedBattle ). --spec update_cache (query_result(), input()) -> 'ok'. -update_cache (QueryResult, Input) -> +-spec update_cache (battle:struct(), input()) -> 'ok'. +update_cache (Battle, Input) -> PlayerID = Input#input.player_id, BattleID = Input#input.battle_id, - Battle = QueryResult#query_result.updated_battle, timed_cache:update ( @@ -292,12 +162,68 @@ generate_reply (ClientUpdate) -> ] ). +handle_actions (RData, Input) -> + Battle = RData#relevant_data.battle, + CharacterInstance= RData#relevant_data.played_character_instance, + CharacterInstanceIX = Input#input.character_instance_ix, + Actions = Input#input.actions, + + { + ActionsDiffUpdates, + ClientUpdates, + PostActionCharacterInstance, + PostActionBattle + } = + lists:foldl + ( + fun + ( + Action, + { + CurrActionsDiffUpdates, + CurrClientUpdates, + CurrCharacterInstance, + CurrBattle + } + ) -> + { + NewActionsDiffUpdates, + NewClientUpdates, + NewCharacterInstance, + NewBattle + } = + battle_action:handle + ( + CurrBattle, + CurrCharacterInstance, + CharacterInstanceIX, + Action + ), + { + (CurrActionsDiffUpdates ++ NewActionsDiffUpdates), + (CurrClientUpdates ++ NewClientUpdates), + NewCharacterInstance, + NewBattle + } + end, + {[], [], CharacterInstance, Battle}, + Actions + ), + { + ActionsDiffUpdates, + ClientUpdates, + RData#relevant_data + { + battle = PostActionBattle, + played_character_instance = PostActionCharacterInstance + } + }. + -spec handle (binary()) -> binary(). handle (Req) -> Input = parse_input(Req), PlayerID = Input#input.player_id, PlayerSessionToken = Input#input.session_token, - Actions = Input#input.actions, security:assert_identity(PlayerID, PlayerSessionToken), security:lock_queries(PlayerID), @@ -307,18 +233,12 @@ handle (Req) -> assert_character_instance_can_be_played(RData, Input), {ActionsDiffUpdate, ClientUpdate, UpdatedRData} = - lists:foldl - ( - fun (Action, Prev) -> - battle_action:handle(Action, Prev) - end, - {[], [], RData}, - Actions - ), + handle_actions(RData, Input), UpdatedBattle = finalize_and_fuse_relevant_data(UpdatedRData, Input), - {TurnDiffUpdate, FinalizedBattle} = end_of_turn:apply_to(UpdatedBattle), + {TurnDiffUpdate, FinalizedBattle} = + battle_turn:handle_post_play(UpdatedBattle), DiffUpdate = (TurnDiffUpdate ++ ActionsDiffUpdate), diff --git a/src/query/load_state.erl b/src/query/load_state.erl index 9999050..eb27b9e 100644 --- a/src/query/load_state.erl +++ b/src/query/load_state.erl @@ -11,7 +11,7 @@ { player_id :: player:id(), session_token :: binary(), - battlemap_instance_id :: binary() + battle_id :: binary() } ). @@ -19,7 +19,7 @@ ( query_state, { - battlemap_instance :: battlemap_instance:struct() + battle :: battle:struct() } ). @@ -39,42 +39,42 @@ parse_input (Req) -> JSONReqMap = jiffy:decode(Req, [return_maps]), PlayerID = maps:get(<<"pid">>, JSONReqMap), SessionToken = maps:get(<<"stk">>, JSONReqMap), - BattlemapInstanceID = maps:get(<<"bmi">>, JSONReqMap), + BattleID = maps:get(<<"bmi">>, JSONReqMap), #input { player_id = PlayerID, session_token = SessionToken, - battlemap_instance_id = BattlemapInstanceID + battle_id = BattleID }. -spec fetch_data (input()) -> query_state(). fetch_data (Input) -> PlayerID = Input#input.player_id, - BattlemapInstanceID = Input#input.battlemap_instance_id, + BattleID = Input#input.battle_id, - BattlemapInstance = + Battle = timed_cache:fetch ( - battlemap_instance_db, + battle_db, PlayerID, - BattlemapInstanceID + BattleID ), #query_state { - battlemap_instance = BattlemapInstance + battle = Battle }. -spec generate_reply(query_state(), input()) -> binary(). generate_reply (QueryState, Input) -> PlayerID = Input#input.player_id, - BattlemapInstance = QueryState#query_state.battlemap_instance, + Battle = QueryState#query_state.battle, jiffy:encode ( [ - set_map:generate(battlemap_instance:get_battlemap(BattlemapInstance)) + set_map:generate(battle:get_battlemap(Battle)) | array:sparse_to_list ( @@ -83,7 +83,7 @@ generate_reply (QueryState, Input) -> fun (IX, CharacterInstance) -> add_char:generate(IX, CharacterInstance, PlayerID) end, - battlemap_instance:get_character_instances(BattlemapInstance) + battle:get_character_instances(Battle) ) ) ] diff --git a/src/shim/database_shim.erl b/src/shim/database_shim.erl index 12d5bd5..e5afd2b 100644 --- a/src/shim/database_shim.erl +++ b/src/shim/database_shim.erl @@ -113,8 +113,8 @@ generate_db (Heir) -> Battlemap = battlemap:random(0, BattlemapWidth, BattlemapHeight), Characters = generate_random_characters(1, 8, 8, 0, []), PlayersAsList = [<<"0">>, <<"1">>], - BattlemapInstance = - battlemap_instance:random + Battle = + battle:random ( <<"0">>, PlayersAsList, @@ -122,7 +122,7 @@ generate_db (Heir) -> Characters ), - add_to_db({battlemap_instance_db, <<"0">>}, BattlemapInstance). + add_to_db({battle_db, <<"0">>}, Battle). -spec fetch (atom(), any()) -> ({'ok', any()} | 'nothing'). fetch (DB, ObjectID) -> diff --git a/src/struct/battle.erl b/src/struct/battle.erl new file mode 100644 index 0000000..bfa0e8d --- /dev/null +++ b/src/struct/battle.erl @@ -0,0 +1,227 @@ +-module(battle). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-opaque id() :: binary(). + +-record +( + battle, + { + id :: id(), + battlemap :: battlemap:struct(), + character_instances :: array:array(character_instance:struct()), + player_ids :: array:array(player:id()), + current_player_turn :: player_turn:struct(), + last_turns_effects :: list(any()) + } +). + +-opaque struct() :: #battle{}. + +-export_type([struct/0, id/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_id/1, + get_battlemap/1, + get_character_instances/1, + get_character_instance/2, + get_player_ids/1, + get_player_id/2, + get_current_player_turn/1, + get_last_turns_effects/1, + + set_battlemap/2, + set_character_instances/2, + set_character_instance/3, + set_player_ids/2, + set_current_player_turn/2, + set_last_turns_effects/2 + ] +). + +-export +( + [ + random/4 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_id (struct()) -> id(). +get_id (Battle) -> Battle#battle.id. + +-spec get_battlemap (struct()) -> battlemap:struct(). +get_battlemap (Battle) -> + Battle#battle.battlemap. + +-spec get_character_instances (struct()) -> + array:array(character_instance:struct()). +get_character_instances (Battle) -> + Battle#battle.character_instances. + +-spec get_character_instance (non_neg_integer(), struct()) -> + character_instance:struct(). +get_character_instance (IX, Battle) -> + array:get(IX, Battle#battle.character_instances). + +-spec get_player_ids (struct()) -> array:array(player:id()). +get_player_ids (Battle) -> + Battle#battle.player_ids. + +-spec get_player_id (non_neg_integer(), struct()) -> player:id(). +get_player_id (IX, Battle) -> + array:get(IX, Battle#battle.player_ids). + +-spec get_current_player_turn (struct()) -> player_turn:struct(). +get_current_player_turn (Battle) -> + Battle#battle.current_player_turn. + +-spec get_last_turns_effects (struct()) -> list(any()). +get_last_turns_effects (Battle) -> + Battle#battle.last_turns_effects. + +-spec set_battlemap (battlemap:struct(), struct()) -> struct(). +set_battlemap (Battlemap, Battle) -> + Battle#battle + { + battlemap = Battlemap + }. + +-spec set_character_instances + ( + array:array(character_instance:struct()), + struct() + ) + -> struct(). +set_character_instances (CharacterInstances, Battle) -> + Battle#battle + { + character_instances = CharacterInstances + }. + +-spec set_character_instance + ( + non_neg_integer(), + character_instance:struct(), + struct() + ) + -> struct(). +set_character_instance (IX, CharacterInstance, Battle) -> + Battle#battle + { + character_instances = + array:set + ( + IX, + CharacterInstance, + Battle#battle.character_instances + ) + }. + +-spec set_player_ids + ( + array:array(player:id()), + struct() + ) + -> struct(). +set_player_ids (Players, Battle) -> + Battle#battle + { + player_ids = Players + }. + +-spec set_current_player_turn + ( + player_turn:struct(), + struct() + ) + -> struct(). +set_current_player_turn (PlayerTurn, Battle) -> + Battle#battle + { + current_player_turn = PlayerTurn + }. + +-spec set_last_turns_effects + ( + list(any()), + struct() + ) + -> struct(). +set_last_turns_effects (Effects, Battle) -> + Battle#battle + { + last_turns_effects = Effects + }. + +-spec random + ( + id(), + list(player:id()), + battlemap:struct(), + list(character:struct()) + ) + -> struct(). +random (ID, PlayersAsList, Battlemap, Characters) -> + BattlemapWidth = battlemap:get_width(Battlemap), + BattlemapHeight = battlemap:get_height(Battlemap), + {CharacterInstancesAsList, _ForbiddenLocations} = + lists:mapfoldl + ( + fun (Character, ForbiddenLocations) -> + CharacterOwner = character:get_owner_id(Character), + NewCharacterInstance = + character_instance:random + ( + Character, + BattlemapWidth, + BattlemapHeight, + ForbiddenLocations + ), + NewCharacterInstanceActive = + case CharacterOwner of + <<"0">> -> + character_instance:set_is_active + ( + true, + NewCharacterInstance + ); + + _ -> + NewCharacterInstance + end, + NewCharacterInstanceLocation = + character_instance:get_location(NewCharacterInstanceActive), + { + NewCharacterInstanceActive, + [NewCharacterInstanceLocation|ForbiddenLocations] + } + end, + [], + Characters + ), + + #battle + { + id = ID, + battlemap = Battlemap, + character_instances = array:from_list(CharacterInstancesAsList), + player_ids = array:from_list(PlayersAsList), + current_player_turn = player_turn:new(0, 0), + last_turns_effects = [] + }. diff --git a/src/struct/battle_action.erl b/src/struct/battle_action.erl index 7287a95..c1bccc7 100644 --- a/src/struct/battle_action.erl +++ b/src/struct/battle_action.erl @@ -22,7 +22,7 @@ ( attack, { - target :: non_neg_integer() + target_ix :: non_neg_integer() } ). @@ -57,18 +57,76 @@ decode_mov_action (JSONMap) -> decode_atk_action (JSONMap) -> TargetIX = maps:get(<<"tix">>, JSONMap), - #attack { target = TargetIX }. + #attack { target_ix = TargetIX }. -spec decode_swp_action (map()) -> struct(). decode_swp_action (_JSONMap) -> #switch_weapon{}. + +handle_attack_sequence +( + CharacterInstance, + TargetCharacterInstance, + AttackSequence +) -> + Character = character_instance:get_character(CharacterInstance), + TargetCharacter = character_instance:get_character(TargetCharacterInstance), + CharacterStatistics = character:get_statistics(Character), + TargetCharacterStatistics = character:get_statistics(TargetCharacter), + + AttackPlannedEffects = + lists:map + ( + fun (AttackStep) -> + attack:get_description_of + ( + AttackStep, + CharacterStatistics, + TargetCharacterStatistics + ) + end, + AttackSequence + ), + + lists:foldl + ( + fun + ( + AttackEffectCandidate, + {AttackValidEffects, AttackerHealth, DefenderHealth} + ) -> + {AttackResult, NewAttackerHealth, NewDefenderHealth} = + attack:apply_to_healths + ( + AttackEffectCandidate, + AttackerHealth, + DefenderHealth + ), + case AttackResult of + nothing -> {AttackValidEffects, AttackerHealth, DefenderHealth}; + _ -> + { + [AttackResult|AttackValidEffects], + NewAttackerHealth, + NewDefenderHealth + } + end + end, + { + [], + character_instance:get_current_health(CharacterInstance), + character_instance:get_current_health(TargetCharacterInstance) + }, + AttackPlannedEffects + ). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec decode (binary()) -> struct(). decode (EncodedAction) -> - JSONActionMap = jiffy:decode(EncodedAction, [return_maps]), + JSONActionMap = EncodedAction, %jiffy:decode(EncodedAction, [return_maps]), ActionType = maps:get(<<"t">>, JSONActionMap), case ActionType of <<"mov">> -> decode_mov_action(JSONActionMap); @@ -175,16 +233,13 @@ when is_record(BattleAction, move) -> ], UpdatedCharacterInstance, Battle - }. -handle (Battle, CharacterInstance, CharacterInstanceIX, BattleAction) + }; +handle (Battle, CharacterInstance, _CharacterInstanceIX, BattleAction) when is_record(BattleAction, attack) -> Character = character_instance:get_character(CharacterInstance), - CharacterStatistics = character:get_statistics(Character), - Battlemap = battle:get_battlemap(Battle), TargetIX = BattleAction#attack.target_ix, TargetCharacterInstance = battle:get_character_instance(TargetIX, Battle), TargetCharacter = character_instance:get_character(TargetCharacterInstance), - TargetCharacterStatistics = character:get_statistics(TargetCharacter), Range = location:dist @@ -202,54 +257,14 @@ when is_record(BattleAction, attack) -> AttackSequence = attack:get_sequence(Range, AttackingWeapon, DefendingWeapon), - AttackPlannedEffects = - lists:map + {AttackEffects, RemainingAttackerHealth, RemainingDefenderHealth} = + handle_attack_sequence ( - fun (AttackStep) -> - attack:get_description_of - ( - AttackStep, - CharacterStatistics, - TargetCharacterStatistics - ) - end, + CharacterInstance, + TargetCharacterInstance, AttackSequence ), - % FIXME: may warrant a separate function - {AttackEffects, RemainingAttakerHealth, RemainingDefenderHealth} = - lists:foldl - ( - fun - ( - AttackEffectCandidate, - {AttackValidEffects, AttackerHealth, DefenderHealth } - ) -> - {AttackResult, NewAttackerHealth, NewDefenderHealth} = - attack:apply_to_healths - ( - AttackPlannedEffect, - AttackerHealth, - DefenderHealth - ), - case AttackResult of - nothing -> {AttackValidEffects, AttackerHealth, DefenderHealth}; - _ -> - { - [AttackResult|AttackValidEffects], - NewAttackerHealth, - NewDefenderHealth - } - end, - end, - { - [], - character_instance:get_current_health(CharacterInstance), - character_instance:get_current_health(TargetCharacterInstance) - }, - AttackPlannedEffects - ), - UpdatedCharacterInstance = character_instance:set_current_health ( @@ -267,12 +282,12 @@ when is_record(BattleAction, attack) -> TargetCharacterInstance ), Battle - ) + ), { % TODO: hide that into database_diff structs. [], % TODO % TODO: hide that into turn_result structs. - AttackEffets, + AttackEffects, UpdatedCharacterInstance, UpdatedBattle }. diff --git a/src/struct/battlemap_instance.erl b/src/struct/battlemap_instance.erl deleted file mode 100644 index c3b411b..0000000 --- a/src/struct/battlemap_instance.erl +++ /dev/null @@ -1,196 +0,0 @@ --module(battlemap_instance). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --opaque id() :: binary(). - --record -( - battlemap_instance, - { - id :: id(), - battlemap :: battlemap:struct(), - character_instances :: array:array(character_instance:struct()), - player_ids :: array:array(player:id()), - current_player_turn :: player_turn:struct(), - last_turns_effects :: list(any()) - } -). - --opaque struct() :: #battlemap_instance{}. - --export_type([struct/0, id/0]). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%% Accessors --export -( - [ - get_id/1, - get_battlemap/1, - get_character_instances/1, - get_player_ids/1, - get_current_player_turn/1, - get_last_turns_effects/1, - - set_battlemap/2, - set_character_instances/2, - set_player_ids/2, - set_current_player_turn/2, - set_last_turns_effects/2 - ] -). - --export -( - [ - random/4 - ] -). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%% Accessors --spec get_id (struct()) -> id(). -get_id (BattlemapInstance) -> BattlemapInstance#battlemap_instance.id. - --spec get_battlemap (struct()) -> battlemap:struct(). -get_battlemap (BattlemapInstance) -> - BattlemapInstance#battlemap_instance.battlemap. - --spec get_character_instances (struct()) -> - array:array(character_instance:struct()). -get_character_instances (BattlemapInstance) -> - BattlemapInstance#battlemap_instance.character_instances. - --spec get_player_ids (struct()) -> array:array(player:id()). -get_player_ids (BattlemapInstance) -> - BattlemapInstance#battlemap_instance.player_ids. - --spec get_current_player_turn (struct()) -> player_turn:struct(). -get_current_player_turn (BattlemapInstance) -> - BattlemapInstance#battlemap_instance.current_player_turn. - --spec get_last_turns_effects (struct()) -> list(any()). -get_last_turns_effects (BattlemapInstance) -> - BattlemapInstance#battlemap_instance.last_turns_effects. - --spec set_battlemap (battlemap:struct(), struct()) -> struct(). -set_battlemap (Battlemap, BattlemapInstance) -> - BattlemapInstance#battlemap_instance - { - battlemap = Battlemap - }. - --spec set_character_instances - ( - array:array(character_instance:struct()), - struct() - ) - -> struct(). -set_character_instances (CharacterInstances, BattlemapInstance) -> - BattlemapInstance#battlemap_instance - { - character_instances = CharacterInstances - }. - --spec set_player_ids - ( - array:array(player:id()), - struct() - ) - -> struct(). -set_player_ids (Players, BattlemapInstance) -> - BattlemapInstance#battlemap_instance - { - player_ids = Players - }. - --spec set_current_player_turn - ( - player_turn:struct(), - struct() - ) - -> struct(). -set_current_player_turn (PlayerTurn, BattlemapInstance) -> - BattlemapInstance#battlemap_instance - { - current_player_turn = PlayerTurn - }. - --spec set_last_turns_effects - ( - list(any()), - struct() - ) - -> struct(). -set_last_turns_effects (Effects, BattlemapInstance) -> - BattlemapInstance#battlemap_instance - { - last_turns_effects = Effects - }. - --spec random - ( - id(), - list(player:id()), - battlemap:struct(), - list(character:struct()) - ) - -> struct(). -random (ID, PlayersAsList, Battlemap, Characters) -> - BattlemapWidth = battlemap:get_width(Battlemap), - BattlemapHeight = battlemap:get_height(Battlemap), - {CharacterInstancesAsList, _ForbiddenLocations} = - lists:mapfoldl - ( - fun (Character, ForbiddenLocations) -> - CharacterOwner = character:get_owner_id(Character), - NewCharacterInstance = - character_instance:random - ( - Character, - BattlemapWidth, - BattlemapHeight, - ForbiddenLocations - ), - NewCharacterInstanceActive = - case CharacterOwner of - <<"0">> -> - character_instance:set_is_active - ( - true, - NewCharacterInstance - ); - - _ -> - NewCharacterInstance - end, - NewCharacterInstanceLocation = - character_instance:get_location(NewCharacterInstanceActive), - { - NewCharacterInstanceActive, - [NewCharacterInstanceLocation|ForbiddenLocations] - } - end, - [], - Characters - ), - - #battlemap_instance - { - id = ID, - battlemap = Battlemap, - character_instances = array:from_list(CharacterInstancesAsList), - player_ids = array:from_list(PlayersAsList), - current_player_turn = player_turn:new(0, 0), - last_turns_effects = [] - }. -- cgit v1.2.3-70-g09d2