summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/battle/struct')
-rw-r--r-- | src/battle/struct/btl_attack.erl | 306 | ||||
-rw-r--r-- | src/battle/struct/btl_battle.erl | 216 | ||||
-rw-r--r-- | src/battle/struct/btl_battle_action.erl | 114 | ||||
-rw-r--r-- | src/battle/struct/btl_battlemap.erl | 100 | ||||
-rw-r--r-- | src/battle/struct/btl_character.erl | 290 | ||||
-rw-r--r-- | src/battle/struct/btl_character_turn_data.erl | 115 | ||||
-rw-r--r-- | src/battle/struct/btl_character_turn_request.erl | 84 | ||||
-rw-r--r-- | src/battle/struct/btl_character_turn_update.erl | 85 | ||||
-rw-r--r-- | src/battle/struct/btl_direction.erl | 38 | ||||
-rw-r--r-- | src/battle/struct/btl_location.erl | 90 | ||||
-rw-r--r-- | src/battle/struct/btl_player.erl | 104 | ||||
-rw-r--r-- | src/battle/struct/btl_player_turn.erl | 106 | ||||
-rw-r--r-- | src/battle/struct/btl_tile.erl | 124 | ||||
-rw-r--r-- | src/battle/struct/btl_turn_result.erl | 215 |
14 files changed, 1987 insertions, 0 deletions
diff --git a/src/battle/struct/btl_attack.erl b/src/battle/struct/btl_attack.erl new file mode 100644 index 0000000..aa7659f --- /dev/null +++ b/src/battle/struct/btl_attack.erl @@ -0,0 +1,306 @@ +-module(btl_attack). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type order() :: ('first' | 'second' | 'counter'). +-type precision() :: ('misses' | 'grazes' | 'hits'). + +-record +( + attack, + { + order :: order(), + precision :: precision(), + is_critical :: boolean(), + is_parry :: boolean(), + damage :: non_neg_integer() + } +). + +-opaque type() :: #attack{}. +-type maybe_type() :: ('nothing' | type()). +-opaque step() :: {order(), boolean()}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export_type([type/0, maybe_type/0, step/0]). + +-export +( + [ + get_sequence/3, + get_description_of/3, + apply_to_healths/3 + ] +). + +-export +( + [ + encode/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec roll_precision + ( + sh_statistics:type(), + sh_statistics:type() + ) + -> precision(). +roll_precision (AttackerStatistics, DefenderStatistics) -> + DefenderDodges = sh_statistics:get_dodges(DefenderStatistics), + AttackerAccuracy = sh_statistics:get_accuracy(AttackerStatistics), + MissChance = max(0, (DefenderDodges - AttackerAccuracy)), + case sh_roll:percentage() of + X when (X =< MissChance) -> misses; + X when (X =< (MissChance * 2)) -> grazes; + _ -> hits + end. + +-spec roll_damage + ( + sh_statistics:type(), + sh_statistics:type() + ) + -> {non_neg_integer(), boolean()}. +roll_damage (AttackerStatistics, _DefenderStatistics) -> + {MinimumDamage, MaximumDamage} = + sh_statistics:get_damages(AttackerStatistics), + MaximumRoll = max(1, MaximumDamage - MinimumDamage), + BaseDamage = MinimumDamage + (rand:uniform(MaximumRoll) - 1), + CriticalHitChance = sh_statistics:get_critical_hits(AttackerStatistics), + case sh_roll:percentage() of + X when (X =< CriticalHitChance) -> {(BaseDamage * 2), true}; + _ -> {BaseDamage, false} + end. + +-spec roll_parry (sh_statistics:type()) -> boolean(). +roll_parry (DefenderStatistics) -> + DefenderParryChance = sh_statistics:get_parries(DefenderStatistics), + (sh_roll:percentage() =< DefenderParryChance). + +-spec effect_of_attack + ( + order(), + btl_character:type(), + btl_character:type(), + boolean() + ) + -> type(). +effect_of_attack (Order, Attacker, Defender, CanParry) -> + AttackerStatistics = btl_character:get_statistics(Attacker), + DefenderStatistics = btl_character:get_statistics(Defender), + + ParryIsSuccessful = (CanParry and roll_parry(DefenderStatistics)), + + {ActualAtkStatistics, ActualDefStatistics} = + case ParryIsSuccessful of + true -> {DefenderStatistics, AttackerStatistics}; + false -> {AttackerStatistics, DefenderStatistics} + end, + {ActualAttacker, ActualDefender} = + case ParryIsSuccessful of + true -> {Defender, Attacker}; + false -> {Attacker, Defender} + end, + + ActualDefArmor = sh_armor:from_id(btl_character:get_armor_id(ActualDefender)), + {ActualAtkWeaponID, _} = btl_character:get_weapon_ids(ActualAttacker), + ActualAtkWeaponDmgType = + sh_weapon:get_damage_type(sh_weapon:from_id(ActualAtkWeaponID)), + + Precision = roll_precision(ActualAtkStatistics, ActualDefStatistics), + {Damage, IsCritical} = roll_damage(ActualAtkStatistics, ActualDefStatistics), + S0Damage = + case Precision of + misses -> 0; + grazes -> trunc(Damage / 2); + hits -> Damage + end, + ArmorResistance = + sh_armor:get_resistance_to(ActualAtkWeaponDmgType, ActualDefArmor), + ActualDamage = max(0, (S0Damage - ArmorResistance)), + + #attack + { + order = Order, + precision = Precision, + is_critical = IsCritical, + is_parry = ParryIsSuccessful, + damage = ActualDamage + }. + +-spec encode_order (order()) -> binary(). +encode_order (first) -> <<"f">>; +encode_order (counter) -> <<"c">>; +encode_order (second) -> <<"s">>. + +-spec encode_precision (precision()) -> binary(). +encode_precision (hits) -> <<"h">>; +encode_precision (grazes) -> <<"g">>; +encode_precision (misses) -> <<"m">>. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec get_description_of + ( + step(), + btl_character:type(), + btl_character:type() + ) + -> maybe_type(). +get_description_of ({first, CanParry}, Attacker, Defender) -> + effect_of_attack(first, Attacker, Defender, CanParry); +get_description_of ({second, CanParry}, Attacker, Defender) -> + AttackerStatistics = btl_character:get_statistics(Attacker), + AttackerDoubleAttackChange = + sh_statistics:get_double_hits(AttackerStatistics), + + case sh_roll:percentage() of + X when (X =< AttackerDoubleAttackChange) -> + effect_of_attack (second, Attacker, Defender, CanParry); + + _ -> + nothing + end; +get_description_of ({counter, CanParry}, Attacker, Defender) -> + effect_of_attack(counter, Defender, Attacker, CanParry). + +-spec apply_to_healths + ( + maybe_type(), + non_neg_integer(), + non_neg_integer() + ) + -> {maybe_type(), non_neg_integer(), non_neg_integer()}. +apply_to_healths +( + nothing, + AttackerHealth, + DefenderHealth +) -> + {nothing, AttackerHealth, DefenderHealth}; +apply_to_healths +( + _Attack, + AttackerHealth, + DefenderHealth +) +when +( + (AttackerHealth =< 0) + or (DefenderHealth =< 0) +) -> + {nothing, AttackerHealth, DefenderHealth}; +apply_to_healths +( + Attack, + AttackerHealth, + DefenderHealth +) +when +( + ( + (not Attack#attack.is_parry) + and ((Attack#attack.order == first) or (Attack#attack.order == second)) + ) + or + ( + Attack#attack.is_parry + and (Attack#attack.order == counter) + ) +) -> + Damage = Attack#attack.damage, + + { + Attack, + AttackerHealth, + (DefenderHealth - Damage) + }; +apply_to_healths +( + Attack, + AttackerHealth, + DefenderHealth +) +when +( + ( + (not Attack#attack.is_parry) + and (Attack#attack.order == counter) + ) + or + ( + Attack#attack.is_parry + and ((Attack#attack.order == first) or (Attack#attack.order == second)) + ) +) -> + Damage = Attack#attack.damage, + + { + Attack, + (AttackerHealth - Damage), + DefenderHealth + }. + +-spec get_sequence + ( + non_neg_integer(), + sh_weapon:type(), + sh_weapon:type() + ) + -> list(step()). +get_sequence (AttackRange, AttackerWeapon, DefenderWeapon) -> + {AttackerDefenseRange, AttackerAttackRange} = + sh_weapon:get_ranges(AttackerWeapon), + {DefenderDefenseRange, DefenderAttackRange} = + sh_weapon:get_ranges(DefenderWeapon), + + AttackerCanAttack = (AttackRange =< AttackerAttackRange), + AttackerCanAttack = true, + AttackerCanDefend = + (AttackerCanAttack and (AttackRange > AttackerDefenseRange)), + AttackerCanParry = + (AttackerCanDefend and sh_weapon:can_parry(AttackerWeapon)), + + DefenderCanAttack = (AttackRange =< DefenderAttackRange), + DefenderCanDefend = + (DefenderCanAttack and (AttackRange > DefenderDefenseRange)), + DefenderCanParry = + (DefenderCanDefend and sh_weapon:can_parry(DefenderWeapon)), + + First = {first, DefenderCanParry}, + Second = {second, DefenderCanParry}, + Counter = {counter, AttackerCanParry}, + + if + (not DefenderCanDefend) -> + [First, Second]; + + true -> + [First, Counter, Second] + end. + +-spec encode (type()) -> {list(any())}. +encode (Attack) -> + Order = Attack#attack.order, + Precision = Attack#attack.precision, + IsCritical = Attack#attack.is_critical, + IsParry = Attack#attack.is_parry, + Damage = Attack#attack.damage, + + { + [ + {<<"ord">>, encode_order(Order)}, + {<<"pre">>, encode_precision(Precision)}, + {<<"cri">>, IsCritical}, + {<<"par">>, IsParry}, + {<<"dmg">>, Damage} + ] + }. diff --git a/src/battle/struct/btl_battle.erl b/src/battle/struct/btl_battle.erl new file mode 100644 index 0000000..8befc4e --- /dev/null +++ b/src/battle/struct/btl_battle.erl @@ -0,0 +1,216 @@ +-module(btl_battle). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type id() :: binary(). + +-record +( + battle, + { + id :: id(), + used_armor_ids :: list(sh_armor:id()), + used_weapon_ids :: list(sh_weapon:id()), + used_tile_ids :: list(btl_tile:id()), + battlemap :: btl_battlemap:type(), + characters :: array:array(btl_character:type()), + players :: array:array(btl_player:type()), + current_player_turn :: btl_player_turn:type() + } +). + +-opaque type() :: #battle{}. + +-export_type([type/0, id/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_id/1, + get_used_weapon_ids/1, + get_used_armor_ids/1, + get_used_tile_ids/1, + get_battlemap/1, + get_characters/1, + get_character/2, + get_players/1, + get_player/2, + get_current_player_turn/1, + get_encoded_last_turns_effects/1, + + set_battlemap/2, + set_characters/2, + set_character/3, + set_players/2, + set_player/3, + set_current_player_turn/2, + + get_characters_field/0, + get_players_field/0, + get_current_player_turn_field/0 + ] +). + +-export +( + [ + new/7 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_all_timelines (Result, CurrentIndex, EndPoint, ArraySize, Players) -> + Player = array:get(CurrentIndex, Players), + Timeline = btl_player:get_timeline(Player), + NextIndex = ((CurrentIndex + 1) rem ArraySize), + NextResult = (Timeline ++ Result), + case CurrentIndex of + EndPoint -> + NextResult; + + _ -> + get_all_timelines(NextResult, NextIndex, EndPoint, ArraySize, Players) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_id (type()) -> id(). +get_id (Battle) -> Battle#battle.id. + +-spec get_used_weapon_ids (type()) -> list(sh_weapon:id()). +get_used_weapon_ids (Battle) -> Battle#battle.used_weapon_ids. + +-spec get_used_armor_ids (type()) -> list(sh_armor:id()). +get_used_armor_ids (Battle) -> Battle#battle.used_armor_ids. + +-spec get_used_tile_ids (type()) -> list(btl_tile:id()). +get_used_tile_ids (Battle) -> Battle#battle.used_tile_ids. + +-spec get_battlemap (type()) -> btl_battlemap:type(). +get_battlemap (Battle) -> Battle#battle.battlemap. + +-spec get_characters (type()) -> array:array(btl_character:type()). +get_characters (Battle) -> Battle#battle.characters. + +-spec get_character (non_neg_integer(), type()) -> btl_character:type(). +get_character (IX, Battle) -> + array:get(IX, Battle#battle.characters). + +-spec get_players (type()) -> array:array(btl_player:type()). +get_players (Battle) -> + Battle#battle.players. + +-spec get_player (non_neg_integer(), type()) -> btl_player:type(). +get_player (IX, Battle) -> + array:get(IX, Battle#battle.players). + +-spec get_current_player_turn (type()) -> btl_player_turn:type(). +get_current_player_turn (Battle) -> + Battle#battle.current_player_turn. + +-spec get_encoded_last_turns_effects (type()) -> list(any()). +get_encoded_last_turns_effects (Battle) -> + CurrentPlayerTurn = Battle#battle.current_player_turn, + Players = Battle#battle.players, + CurrentPlayerIX = btl_player_turn:get_player_ix(CurrentPlayerTurn), + + PlayersCount = array:size(Players), + StartingPoint = ((CurrentPlayerIX + 1) rem PlayersCount), + get_all_timelines([], StartingPoint, CurrentPlayerIX, PlayersCount, Players). + +-spec set_battlemap (btl_battlemap:type(), type()) -> type(). +set_battlemap (Battlemap, Battle) -> + Battle#battle + { + battlemap = Battlemap + }. + +-spec set_characters (array:array(btl_character:type()), type()) -> type(). +set_characters (Characters, Battle) -> + Battle#battle + { + characters = Characters + }. + +-spec set_character (non_neg_integer(), btl_character:type(), type()) -> type(). +set_character (IX, Character, Battle) -> + Battle#battle + { + characters = + array:set + ( + IX, + Character, + Battle#battle.characters + ) + }. + +-spec set_players (array:array(btl_player:type()), type()) -> type(). +set_players (Players, Battle) -> + Battle#battle + { + players = Players + }. + +-spec set_player (non_neg_integer(), btl_player:type(), type()) -> type(). +set_player (IX, Player, Battle) -> + Battle#battle + { + players = + array:set + ( + IX, + Player, + Battle#battle.players + ) + }. + +-spec set_current_player_turn (btl_player_turn:type(), type()) -> type(). +set_current_player_turn (PlayerTurn, Battle) -> + Battle#battle + { + current_player_turn = PlayerTurn + }. + +-spec new + ( + id(), + list(btl_player:type()), + btl_battlemap:type(), + list(btl_character:type()), + list(sh_weapon:id()), + list(sh_armor:id()), + list(btl_tile:id()) + ) + -> type(). +new (ID, PlayersAsList, Battlemap, CharactersAsList, UWIDs, UAIDs, UTIDs) -> + #battle + { + id = ID, + used_weapon_ids = UWIDs, + used_armor_ids = UAIDs, + used_tile_ids = UTIDs, + battlemap = Battlemap, + characters = array:from_list(CharactersAsList), + players = array:from_list(PlayersAsList), + current_player_turn = btl_player_turn:new(0, 0) + }. + + +-spec get_characters_field () -> non_neg_integer(). +get_characters_field () -> #battle.characters. + +-spec get_players_field () -> non_neg_integer(). +get_players_field () -> #battle.players. + +-spec get_current_player_turn_field () -> non_neg_integer(). +get_current_player_turn_field () -> #battle.current_player_turn. diff --git a/src/battle/struct/btl_battle_action.erl b/src/battle/struct/btl_battle_action.erl new file mode 100644 index 0000000..307043a --- /dev/null +++ b/src/battle/struct/btl_battle_action.erl @@ -0,0 +1,114 @@ +-module(btl_battle_action). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + move, + { + path :: list(btl_direction:enum()) + } +). + +-record +( + switch_weapon, + { + } +). + +-record +( + attack, + { + target_ix :: non_neg_integer() + } +). + +-type category() :: ('move' | 'switch_weapon' | 'attack' | 'nothing'). +-opaque type() :: (#move{} | #switch_weapon{} | #attack{}). + +-export_type([category/0, type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + decode/1, + can_follow/2 + ] +). + +-export +( + [ + get_path/1, + get_target_ix/1, + get_category/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec decode_mov_action (map()) -> type(). +decode_mov_action (JSONMap) -> + PathInBinary = maps:get(<<"p">>, JSONMap), + Path = lists:map(fun btl_direction:decode/1, PathInBinary), + + #move { path = Path }. + +-spec decode_atk_action (map()) -> type(). +decode_atk_action (JSONMap) -> + TargetIX = maps:get(<<"tix">>, JSONMap), + + #attack { target_ix = TargetIX }. + +-spec decode_swp_action (map()) -> type(). +decode_swp_action (_JSONMap) -> + #switch_weapon{}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec decode (map()) -> type(). +decode (EncodedAction) -> + JSONActionMap = EncodedAction, %jiffy:decode(EncodedAction, [return_maps]), + ActionType = maps:get(<<"t">>, JSONActionMap), + case ActionType of + <<"mov">> -> decode_mov_action(JSONActionMap); + <<"atk">> -> decode_atk_action(JSONActionMap); + <<"swp">> -> decode_swp_action(JSONActionMap) + end. + +-spec can_follow (category(), category()) -> boolean(). +can_follow (nothing, attack) -> true; +can_follow (nothing, switch_weapon) -> true; +can_follow (nothing, move) -> true; +can_follow (switch_weapon, attack) -> true; +can_follow (move, attack) -> true; +can_follow (_, _) -> false. + +-spec get_path (type()) -> list(btl_direction:type()). +get_path (Action) when is_record(Action, move) -> + Action#move.path; +get_path (_) -> + []. + +-spec get_target_ix (type()) -> non_neg_integer(). +get_target_ix (Action) when is_record(Action, attack) -> + Action#attack.target_ix; +get_target_ix (_) -> + []. + +-spec get_category (type()) -> category(). +get_category (Action) when is_record(Action, attack) -> attack; +get_category (Action) when is_record(Action, move) -> move; +get_category (Action) when is_record(Action, switch_weapon) -> switch_weapon; +get_category (Action) -> + io:format("How'd you get there?~p~n", [Action]), + true = Action. + diff --git a/src/battle/struct/btl_battlemap.erl b/src/battle/struct/btl_battlemap.erl new file mode 100644 index 0000000..886e2a9 --- /dev/null +++ b/src/battle/struct/btl_battlemap.erl @@ -0,0 +1,100 @@ +-module(btl_battlemap). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type id() :: binary(). + +-record +( + battlemap, + { + id :: id(), + width :: integer(), + height :: integer(), + tile_class_ids :: array:array(btl_tile:class_id()) + } +). + +-opaque type() :: #battlemap{}. + +-export_type([type/0, id/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_id/1, + get_width/1, + get_height/1, + get_tile_class_ids/1, + get_tile_class_id/2 + ] +). + +-export +( + [ + from_list/4 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec location_to_array_index + ( + non_neg_integer(), + btl_location:type() + ) + -> ('error' | non_neg_integer()). +location_to_array_index (ArrayWidth, {X, Y}) -> + if + (X < 0) -> error; + (Y < 0) -> error; + (X >= ArrayWidth) -> error; + true -> ((Y * ArrayWidth) + X) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_id (type()) -> id(). +get_id (Battlemap) -> Battlemap#battlemap.id. + +-spec get_width (type()) -> integer(). +get_width (Battlemap) -> Battlemap#battlemap.width. + +-spec get_height (type()) -> integer(). +get_height (Battlemap) -> Battlemap#battlemap.height. + +-spec get_tile_class_ids (type()) -> array:array(btl_tile:class_id()). +get_tile_class_ids (Battlemap) -> Battlemap#battlemap.tile_class_ids. + +-spec get_tile_class_id (btl_location:type(), type()) -> btl_tile:class_id(). +get_tile_class_id (Location, Battlemap) -> + TileIX = location_to_array_index(Battlemap#battlemap.width, Location), + array:get(TileIX, Battlemap#battlemap.tile_class_ids). + +-spec from_list + ( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + list(non_neg_integer()) + ) + -> type(). +from_list (ID, Width, Height, List) -> + TileClassIDs = lists:map(fun btl_tile:class_id_from_int/1, List), + + #battlemap + { + id = list_to_binary(integer_to_list(ID)), + width = Width, + height = Height, + tile_class_ids = array:from_list(TileClassIDs) + }. diff --git a/src/battle/struct/btl_character.erl b/src/battle/struct/btl_character.erl new file mode 100644 index 0000000..841f4ea --- /dev/null +++ b/src/battle/struct/btl_character.erl @@ -0,0 +1,290 @@ +-module(btl_character). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type id() :: non_neg_integer(). +-type rank() :: ('optional' | 'target' | 'commander'). + +-record +( + character, + { + id :: id(), + player_ix :: non_neg_integer(), + name :: binary(), + rank :: rank(), + icon :: binary(), + portrait :: binary(), + attributes :: sh_attributes:type(), + statistics :: sh_statistics:type(), + weapon_ids :: {sh_weapon:id(), sh_weapon:id()}, + armor_id :: sh_armor:id(), + location :: {non_neg_integer(), non_neg_integer()}, + current_health :: integer(), %% Negative integers let us reverse attacks. + is_active :: boolean(), + is_defeated :: boolean() + } +). + +-opaque type() :: #character{}. + +-export_type([type/0, rank/0, id/0]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_id/1, + get_player_index/1, + get_name/1, + get_rank/1, + get_icon/1, + get_portrait/1, + get_attributes/1, + get_statistics/1, + get_weapon_ids/1, + get_armor_id/1, + get_location/1, + get_current_health/1, + get_is_alive/1, + get_is_active/1, + get_is_defeated/1, + + set_rank/2, + set_weapon_ids/2, + set_armor_id/2, + set_statistics/2, + set_location/2, + set_current_health/2, + set_is_active/2, + set_is_defeated/2, + + get_rank_field/0, + get_statistics_field/0, + get_weapons_field/0, + get_location_field/0, + get_current_health_field/0, + get_is_active_field/0, + get_is_defeated_field/0 + ] +). + +-export +( + [ + random/5 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec find_random_location + ( + non_neg_integer(), + non_neg_integer(), + list({non_neg_integer(), non_neg_integer()}) + ) + -> {non_neg_integer(), non_neg_integer()}. +find_random_location (BattlemapWidth, BattlemapHeight, ForbiddenLocations) -> + X = sh_roll:between(0, (BattlemapWidth - 1)), + Y = sh_roll:between(0, (BattlemapHeight - 1)), + + IsForbidden = lists:member({X, Y}, ForbiddenLocations), + + case IsForbidden of + true -> + find_random_location + ( + BattlemapWidth, + BattlemapHeight, + ForbiddenLocations + ); + + _ -> {X, Y} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_id (type()) -> id(). +get_id (Char) -> Char#character.id. + +-spec get_player_index (type()) -> non_neg_integer(). +get_player_index (Char) -> Char#character.player_ix. + +-spec get_name (type()) -> binary(). +get_name (Char) -> Char#character.name. + +-spec get_rank (type()) -> rank(). +get_rank (Char) -> Char#character.rank. + +-spec get_icon (type()) -> binary(). +get_icon (Char) -> Char#character.icon. + +-spec get_portrait (type()) -> binary(). +get_portrait (Char) -> Char#character.portrait. + +-spec get_attributes (type()) -> sh_attributes:type(). +get_attributes (Char) -> Char#character.attributes. + +-spec get_armor_id (type()) -> sh_armor:id(). +get_armor_id (Char) -> Char#character.armor_id. + +-spec get_weapon_ids (type()) -> {sh_weapon:id(), sh_weapon:id()}. +get_weapon_ids (Char) -> Char#character.weapon_ids. + +-spec get_statistics (type()) -> sh_statistics:type(). +get_statistics (Char) -> Char#character.statistics. + +-spec get_location (type()) -> {non_neg_integer(), non_neg_integer()}. +get_location (Char) -> Char#character.location. + +-spec get_current_health (type()) -> integer(). +get_current_health (Char) -> Char#character.current_health. + +-spec get_is_alive (type()) -> boolean(). +get_is_alive (Char) -> + ( + (not Char#character.is_defeated) + and (Char#character.current_health > 0) + ). + +-spec get_is_active (type()) -> boolean(). +get_is_active (Char) -> + ( + (not Char#character.is_defeated) + and Char#character.is_active + and get_is_alive(Char) + ). + +-spec get_is_defeated (type()) -> boolean(). +get_is_defeated (Char) -> Char#character.is_defeated. + +-spec set_rank (rank(), type()) -> type(). +set_rank (Rank, Char) -> + Char#character + { + rank = Rank + }. + +-spec set_location + ( + {non_neg_integer(), non_neg_integer()}, + type() + ) + -> type(). +set_location (Location, Char) -> + Char#character + { + location = Location + }. + +-spec set_current_health (integer(), type()) -> type(). +set_current_health (Health, Char) -> + Char#character + { + current_health = Health + }. + +-spec set_is_active (boolean(), type()) -> type(). +set_is_active (Active, Char) -> + Char#character + { + is_active = Active + }. + +-spec set_is_defeated (boolean(), type()) -> type(). +set_is_defeated (Defeated, Char) -> + Char#character + { + is_defeated = Defeated + }. + +-spec set_armor_id (sh_armor:id(), type()) -> type(). +set_armor_id (ArmorID, Char) -> + Char#character + { + armor_id = ArmorID + }. + +-spec set_weapon_ids ({sh_weapon:id(), sh_weapon:id()}, type()) -> type(). +set_weapon_ids (WeaponIDs, Char) -> + Char#character + { + weapon_ids = WeaponIDs + }. + +-spec set_statistics + ( + sh_statistics:type(), + type() + ) + -> type(). +set_statistics (Stats, Char) -> + Char#character + { + statistics = Stats + }. + +%%%% Utils +-spec random + ( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + list({non_neg_integer(), non_neg_integer()}) + ) + -> type(). +random (ID, PlayerIX, BattlemapWidth, BattlemapHeight, ForbiddenLocations) -> + Location = + find_random_location(BattlemapWidth, BattlemapHeight, ForbiddenLocations), + WeaponIDs = {sh_weapon:random_id(), sh_weapon:random_id()}, + ArmorID = sh_armor:random_id(), + Attributes = sh_attributes:random(), + Statistics = sh_statistics:new(Attributes, WeaponIDs, ArmorID), + IDAsListString = integer_to_list(ID), + IDAsBinaryString = list_to_binary(IDAsListString), + + #character + { + id = ID, + player_ix = PlayerIX, + name = list_to_binary("Char" ++ IDAsListString), + rank = + if + ((ID rem 8) == 0) -> commander; + ((ID rem 3) == 0) -> target; + true -> optional + end, + icon = IDAsBinaryString, + portrait = IDAsBinaryString, + attributes = Attributes, + weapon_ids = WeaponIDs, + armor_id = ArmorID, + statistics = Statistics, + location = Location, + current_health = sh_statistics:get_health(Statistics), + is_active = false, + is_defeated = false + }. + +-spec get_rank_field() -> non_neg_integer(). +get_rank_field () -> #character.rank. +-spec get_statistics_field() -> non_neg_integer(). +get_statistics_field () -> #character.statistics. +-spec get_weapons_field() -> non_neg_integer(). +get_weapons_field () -> #character.weapon_ids. +-spec get_location_field() -> non_neg_integer(). +get_location_field () -> #character.location. +-spec get_current_health_field() -> non_neg_integer(). +get_current_health_field () -> #character.current_health. +-spec get_is_active_field() -> non_neg_integer(). +get_is_active_field () -> #character.is_active. +-spec get_is_defeated_field() -> non_neg_integer(). +get_is_defeated_field () -> #character.is_defeated. diff --git a/src/battle/struct/btl_character_turn_data.erl b/src/battle/struct/btl_character_turn_data.erl new file mode 100644 index 0000000..31a4b7d --- /dev/null +++ b/src/battle/struct/btl_character_turn_data.erl @@ -0,0 +1,115 @@ +-module(btl_character_turn_data). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + type, + { + dirty :: boolean(), + battle :: btl_battle:type(), + character :: btl_character:type(), + character_ix :: non_neg_integer() + } +). + +-opaque type() :: #type{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + new/2, + + get_battle_is_dirty/1, + get_battle/1, + get_character/1, + get_character_ix/1, + + set_battle/2, + set_character/2 + ] +). + +-export +( + [ + clean_battle/1, + refresh_character/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec new (btl_battle:type(), non_neg_integer()) -> type(). +new (Battle, CharacterIX) -> + Character = btl_battle:get_character(CharacterIX, Battle), + + #type + { + dirty = false, + battle = Battle, + character = Character, + character_ix = CharacterIX + }. + +-spec get_battle_is_dirty (type()) -> boolean(). +get_battle_is_dirty (Data) -> Data#type.dirty. + +-spec get_battle (type()) -> btl_battle:type(). +get_battle (Data) -> Data#type.battle. + +-spec get_character (type()) -> btl_character:type(). +get_character (Data) -> Data#type.character. + +-spec get_character_ix (type()) -> non_neg_integer(). +get_character_ix (Data) -> Data#type.character_ix. + +-spec set_battle (btl_battle:type(), type()) -> type(). +set_battle (Battle, Data) -> + Data#type{ battle = Battle }. + +-spec set_character (btl_character:type(), type()) -> type(). +set_character (Character, Data) -> + Data#type + { + dirty = true, + character = Character + }. + +-spec clean_battle (type()) -> type(). +clean_battle (Data) -> + Data#type + { + dirty = false, + battle = + btl_battle:set_character + ( + Data#type.character_ix, + Data#type.character, + Data#type.battle + ) + }. + +-spec refresh_character (type()) -> type(). +refresh_character (Data) -> + Data#type + { + dirty = false, + character = + btl_battle:get_character + ( + Data#type.character_ix, + Data#type.battle + ) + }. diff --git a/src/battle/struct/btl_character_turn_request.erl b/src/battle/struct/btl_character_turn_request.erl new file mode 100644 index 0000000..a4f310d --- /dev/null +++ b/src/battle/struct/btl_character_turn_request.erl @@ -0,0 +1,84 @@ +-module(btl_character_turn_request). + +-define(PLAYER_ID_FIELD, <<"pid">>). +-define(SESSION_TOKEN_FIELD, <<"stk">>). +-define(BATTLE_ID_FIELD, <<"bid">>). +-define(CHAR_IX_FIELD, <<"cix">>). +-define(ACTIONS_FIELD, <<"act">>). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + type, + { + player_id :: btl_player:id(), + session_token :: binary(), + battle_id :: binary(), + character_ix :: non_neg_integer(), + actions :: list(btl_battle_action:type()) + } +). + +-opaque type() :: #type{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + decode/1 + ] +). + +-export +( + [ + get_player_id/1, + get_session_token/1, + get_battle_id/1, + get_character_ix/1, + get_actions/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec decode (map()) -> type(). +decode (Map) -> + CharacterIX = maps:get(?CHAR_IX_FIELD, Map), + EncodedActions = maps:get(?ACTIONS_FIELD, Map), + Actions = lists:map(fun btl_battle_action:decode/1, EncodedActions), + + #type + { + player_id = maps:get(?PLAYER_ID_FIELD, Map), + session_token = maps:get(?SESSION_TOKEN_FIELD, Map), + battle_id = maps:get(?BATTLE_ID_FIELD, Map), + character_ix = CharacterIX, + actions = Actions + }. + +-spec get_player_id (type()) -> btl_player:id(). +get_player_id (Request) -> Request#type.player_id. + +-spec get_session_token (type()) -> binary(). +get_session_token (Request) -> Request#type.session_token. + +-spec get_battle_id (type()) -> binary(). +get_battle_id (Request) -> Request#type.battle_id. + +-spec get_character_ix (type()) -> non_neg_integer(). +get_character_ix (Request) -> Request#type.character_ix. + +-spec get_actions (type()) -> list(btl_battle_action:type()). +get_actions (Request) -> Request#type.actions. diff --git a/src/battle/struct/btl_character_turn_update.erl b/src/battle/struct/btl_character_turn_update.erl new file mode 100644 index 0000000..a6b29d9 --- /dev/null +++ b/src/battle/struct/btl_character_turn_update.erl @@ -0,0 +1,85 @@ +-module(btl_character_turn_update). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + type, + { + data :: btl_character_turn_data:type(), + timeline :: list(any()), + db :: list(sh_db_query:op()) + } +). + +-opaque type() :: #type{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + new/1, + + get_data/1, + get_timeline/1, + get_db/1, + + set_data/2, + add_to_timeline/3, + add_to_db/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec new (btl_character_turn_data:type()) -> type(). +new (Data) -> + #type + { + data = Data, + timeline = [], + db = [] + }. + +-spec get_data (type()) -> btl_character_turn_data:type(). +get_data (Update) -> Update#type.data. + +-spec get_timeline (type()) -> list(any()). +get_timeline (Update) -> Update#type.timeline. + +-spec get_db (type()) -> list(sh_db_query:op()). +get_db (Update) -> Update#type.db. + +-spec set_data (btl_character_turn_data:type(), type()) -> type(). +set_data (Data, Update) -> + Update#type{ data = Data}. + +-spec add_to_timeline + ( + btl_turn_result:type(), + sh_db_query:op(), + type() + ) -> type(). +add_to_timeline (Item, DBUpdate, Update) -> + add_to_db + ( + DBUpdate, + Update#type + { + timeline = [btl_turn_result:encode(Item)|Update#type.timeline] + } + ). + +-spec add_to_db (sh_db_query:op(), type()) -> type(). +add_to_db (Item, Update) -> + Update#type{ db = [Item|Update#type.db] }. diff --git a/src/battle/struct/btl_direction.erl b/src/battle/struct/btl_direction.erl new file mode 100644 index 0000000..9fb5a01 --- /dev/null +++ b/src/battle/struct/btl_direction.erl @@ -0,0 +1,38 @@ +-module(btl_direction). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type enum() :: ('up' | 'down' | 'left' | 'right'). +-type type() :: enum(). + +-export_type([enum/0, type/0]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + decode/1, + encode/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec decode (binary()) -> enum(). +decode (<<"U">>) -> up; +decode (<<"D">>) -> down; +decode (<<"L">>) -> left; +decode (<<"R">>) -> right. + +-spec encode (enum()) -> binary(). +encode (up) -> <<"U">>; +encode (down) -> <<"D">>; +encode (left) -> <<"L">>; +encode (right) -> <<"R">>. diff --git a/src/battle/struct/btl_location.erl b/src/battle/struct/btl_location.erl new file mode 100644 index 0000000..9670cb0 --- /dev/null +++ b/src/battle/struct/btl_location.erl @@ -0,0 +1,90 @@ +-module(btl_location). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type type() :: ({non_neg_integer(), non_neg_integer()} | 'nowhere'). + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + decode/1, + encode/1, + get_nowhere/0 + ] +). + +-export +( + [ + apply_direction/2, + dist/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec validate ({integer(), integer()}) -> type(). +validate ({X, Y}) -> + if + (X < 0) -> nowhere; + (Y < 0) -> nowhere; + true -> {X, Y} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec get_nowhere () -> type(). +get_nowhere () -> nowhere. + +-spec apply_direction (btl_direction:enum(), type()) -> type(). +apply_direction (left, {X, Y}) -> + validate({(X - 1), Y}); +apply_direction (right, {X, Y}) -> + validate({(X + 1), Y}); +apply_direction (up, {X, Y}) -> + validate({X, (Y - 1)}); +apply_direction (down, {X, Y}) -> + validate({X, (Y + 1)}); +apply_direction (_, nowhere) -> + error("Trying to move from 'nowhere'."), + nowhere. + +-spec dist(type(), type()) -> non_neg_integer(). +dist ({OX, OY}, {DX, DY}) -> + (abs(DY - OY) + abs(DX - OX)); +dist (_, _) -> + error("Trying to measure distance to 'nowhere'"), + 999. + +-spec encode (type()) -> {list(any())}. +encode ({X, Y}) -> + { + [ + {<<"x">>, X}, + {<<"y">>, Y} + ] + }; +encode (nowhere) -> + { + [ + {<<"x">>, -1}, + {<<"y">>, -1} + ] + }. + +-spec decode (map()) -> type(). +decode (Map) -> + X = maps:get(<<"x">>, Map), + Y = maps:get(<<"y">>, Map), + + true = (is_integer(X) and is_integer(Y)), + + validate({X, Y}). diff --git a/src/battle/struct/btl_player.erl b/src/battle/struct/btl_player.erl new file mode 100644 index 0000000..1cb1d93 --- /dev/null +++ b/src/battle/struct/btl_player.erl @@ -0,0 +1,104 @@ +-module(btl_player). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type id() :: binary(). + +-record +( + player, + { + ix :: non_neg_integer(), + id :: id(), + character_ix :: non_neg_integer(), + timeline :: list(any()), + is_active :: boolean() + } +). + +-opaque type() :: #player{}. + +-export_type([type/0, id/0]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + get_id/1, + get_index/1, + get_character_index/1, + get_timeline/1, + + get_is_active/1, + set_is_active/2, + + add_to_timeline/2, + reset_timeline/1, + + get_timeline_field/0, + get_is_active_field/0 + ] +). + +-export +( + [ + new/3 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec get_id (type()) -> id(). +get_id (Player) -> Player#player.id. + +-spec get_index (type()) -> non_neg_integer(). +get_index (Player) -> Player#player.ix. + +-spec get_character_index (type()) -> non_neg_integer(). +get_character_index (Player) -> Player#player.character_ix. + +-spec get_timeline (type()) -> list(any()). +get_timeline (Player) -> Player#player.timeline. + +-spec get_is_active (type()) -> boolean(). +get_is_active (Player) -> Player#player.is_active. + +-spec set_is_active (boolean(), type()) -> type(). +set_is_active (Val, Player) -> Player#player{ is_active = Val }. + +-spec add_to_timeline (list(any()), type()) -> type(). +add_to_timeline (NewEvents, Player) -> + OldTimeline = Player#player.timeline, + + Player#player + { + timeline = (NewEvents ++ OldTimeline) + }. + +-spec reset_timeline (type()) -> type(). +reset_timeline (Player) -> Player#player{ timeline = [] }. + +-spec new (non_neg_integer(), non_neg_integer(), id()) -> type(). +new (IX, CharacterIX, ID) -> + #player + { + ix = IX, + character_ix = CharacterIX, + id = ID, + is_active = true, + timeline = [] + }. + +-spec get_timeline_field () -> non_neg_integer(). +get_timeline_field () -> #player.timeline. + +-spec get_is_active_field () -> non_neg_integer(). +get_is_active_field () -> #player.is_active. diff --git a/src/battle/struct/btl_player_turn.erl b/src/battle/struct/btl_player_turn.erl new file mode 100644 index 0000000..e0665f4 --- /dev/null +++ b/src/battle/struct/btl_player_turn.erl @@ -0,0 +1,106 @@ +-module(btl_player_turn). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + player_turn, + { + number :: non_neg_integer(), + player_ix :: non_neg_integer() + } +). + +-opaque type() :: #player_turn{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + new/2, + next/2 + ] +). + +%%%% Accessors +-export +( + [ + get_number/1, + get_player_ix/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec next_valid_player + ( + non_neg_integer(), + array:array(btl_player:type()), + non_neg_integer(), + non_neg_integer() + ) -> non_neg_integer(). +next_valid_player (StartingPoint, _Players, _PlayersCount, StartingPoint) -> + StartingPoint; +next_valid_player (CandidateIX, Players, PlayersCount, StartingPoint) -> + Candidate = array:get(CandidateIX, Players), + + case btl_player:get_is_active(Candidate) of + true -> CandidateIX; + _ -> + next_valid_player + ( + ((CandidateIX + 1) rem PlayersCount), + Players, + PlayersCount, + StartingPoint + ) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec new (non_neg_integer(), non_neg_integer()) -> type(). +new (Number, PlayerIX) -> + #player_turn + { + number = Number, + player_ix = PlayerIX + }. + +-spec get_number (type()) -> non_neg_integer(). +get_number (PlayerTurn) -> PlayerTurn#player_turn.number. + +-spec get_player_ix (type()) -> non_neg_integer(). +get_player_ix (PlayerTurn) -> PlayerTurn#player_turn.player_ix. + +-spec next (array:array(btl_player:type()), type()) -> type(). +next (Players, CurrentPlayerTurn) -> + CurrentPlayerIX = CurrentPlayerTurn#player_turn.player_ix, + CurrentTurnNumber = CurrentPlayerTurn#player_turn.number, + PlayersCount = array:size(Players), + + NextPlayerIX = + next_valid_player + ( + ((CurrentPlayerIX + 1) rem PlayersCount), + Players, + PlayersCount, + CurrentPlayerIX + ), + + NextTurnNumber = + case (NextPlayerIX < CurrentPlayerIX) of + true -> (CurrentTurnNumber + 1); + _ -> CurrentTurnNumber + end, + + new(NextTurnNumber, NextPlayerIX). diff --git a/src/battle/struct/btl_tile.erl b/src/battle/struct/btl_tile.erl new file mode 100644 index 0000000..16e671b --- /dev/null +++ b/src/battle/struct/btl_tile.erl @@ -0,0 +1,124 @@ +-module(btl_tile). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + tile, + { + id :: id(), + name :: binary(), + cost :: non_neg_integer(), + class_range_min :: non_neg_integer(), + class_range_max :: non_neg_integer() + } +). + +-opaque id() :: non_neg_integer(). +-opaque class_id() :: non_neg_integer(). +-opaque type() :: #tile{}. + +-export_type([type/0, class_id/0, id/0]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + get_id/1, + get_name/1, + get_cost/1, + get_range_minimum/1, + get_range_maximum/1, + from_id/1, + cost_when_oob/0 + ] +). + +-export +( + [ + class_id_to_type_id/1, + class_id_from_int/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec class_id_to_type_id (class_id()) -> id(). +class_id_to_type_id (ClassID) -> + case ClassID of + 0 -> 0; + 1 -> 1; + 2 -> 2; + N when ((N >= 3) and (N =< 17)) -> 3 + end. + +-spec from_id (id()) -> type(). +from_id (0) -> + #tile + { + id = 0, + name = <<"[Grassland] Grass">>, + cost = 6, + class_range_min = 0, + class_range_max = 0 + }; +from_id (1) -> + #tile + { + id = 1, + name = <<"[Grassland] Mushroom Infestation">>, + cost = 12, + class_range_min = 1, + class_range_max = 1 + }; +from_id (2) -> + #tile + { + id = 2, + name = <<"[Grassland] Tree Remains">>, + cost = 24, + class_range_min = 2, + class_range_max = 2 + }; +from_id (3) -> + #tile + { + id = 3, + name = <<"[Grassland] Clear Water">>, + cost = cost_when_occupied(), + class_range_min = 3, + class_range_max = 17 + }. + +-spec cost_when_oob () -> non_neg_integer(). +cost_when_oob () -> 255. + +-spec cost_when_occupied () -> non_neg_integer(). +cost_when_occupied () -> 201. + +-spec get_id (type()) -> non_neg_integer(). +get_id (Tile) -> Tile#tile.id. + +-spec get_cost (type()) -> non_neg_integer(). +get_cost (Tile) -> Tile#tile.cost. + +-spec get_name (type()) -> binary(). +get_name (Tile) -> Tile#tile.name. + +-spec get_range_minimum (type()) -> non_neg_integer(). +get_range_minimum (Tile) -> Tile#tile.class_range_min. + +-spec get_range_maximum (type()) -> non_neg_integer(). +get_range_maximum (Tile) -> Tile#tile.class_range_max. + +-spec class_id_from_int (non_neg_integer()) -> id(). +class_id_from_int (I) -> I. diff --git a/src/battle/struct/btl_turn_result.erl b/src/battle/struct/btl_turn_result.erl new file mode 100644 index 0000000..97169e3 --- /dev/null +++ b/src/battle/struct/btl_turn_result.erl @@ -0,0 +1,215 @@ +-module(btl_turn_result). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +-record +( + switched_weapon, + { + character_ix :: btl_character:id() + } +). + +-record +( + moved, + { + character_ix :: btl_character:id(), + path :: list(btl_direction:enum()), + new_location :: btl_location:type() + } +). + +-record +( + attacked, + { + attacker_ix :: btl_character:id(), + defender_ix :: btl_character:id(), + sequence :: list(btl_attack:type()) + } +). + +-record +( + player_won, + { + player_ix :: non_neg_integer() + } +). + +-record +( + player_lost, + { + player_ix :: non_neg_integer() + } +). + +-record +( + player_turn_started, + { + player_ix :: non_neg_integer() + } +). + +-opaque type() :: ( + #switched_weapon{} + | #moved{} + | #attacked{} + | #player_won{} + | #player_lost{} + | #player_turn_started{} +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export_type([type/0]). + +-export +( + [ + new_player_won/1, + new_player_lost/1, + new_player_turn_started/1, + new_character_switched_weapons/1, + new_character_moved/3, + new_character_attacked/3 + ] +). + +-export +( + [ + encode/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec new_player_won (non_neg_integer()) -> type(). +new_player_won (PlayerIX) -> + #player_won { player_ix = PlayerIX }. + +-spec new_player_lost (non_neg_integer()) -> type(). +new_player_lost (PlayerIX) -> + #player_lost { player_ix = PlayerIX }. + +-spec new_player_turn_started (non_neg_integer()) -> type(). +new_player_turn_started (PlayerIX) -> + #player_turn_started { player_ix = PlayerIX }. + +-spec new_character_switched_weapons (btl_character:id()) -> type(). +new_character_switched_weapons (CharacterIX) -> + #switched_weapon { character_ix = CharacterIX }. + +-spec new_character_moved + ( + btl_character:id(), + list(btl_direction:enum()), + btl_location:type() + ) + -> type(). +new_character_moved (CharacterIX, Path, NewLocation) -> + #moved + { + character_ix = CharacterIX, + path = Path, + new_location = NewLocation + }. + +-spec new_character_attacked + ( + btl_character:id(), + btl_character:id(), + list(btl_attack:type()) + ) + -> type(). +new_character_attacked (AttackerIX, DefenderIX, AttackSequence) -> + #attacked + { + attacker_ix = AttackerIX, + defender_ix = DefenderIX, + sequence = AttackSequence + }. + +-spec encode (type()) -> {list(any())}. +encode (TurnResult) when is_record(TurnResult, switched_weapon) -> + CharacterIX = TurnResult#switched_weapon.character_ix, + + { + [ + {<<"t">>, <<"swp">>}, + {<<"ix">>, CharacterIX} + ] + }; +encode (TurnResult) when is_record(TurnResult, moved) -> + CharacterIX = TurnResult#moved.character_ix, + Path = TurnResult#moved.path, + NewLocation = TurnResult#moved.new_location, + + EncodedPath = lists:map(fun btl_direction:encode/1, Path), + EncodedNewLocation = btl_location:encode(NewLocation), + + { + [ + {<<"t">>, <<"mv">>}, + {<<"ix">>, CharacterIX}, + {<<"p">>, EncodedPath}, + {<<"nlc">>, EncodedNewLocation} + ] + }; +encode (TurnResult) when is_record(TurnResult, attacked) -> + AttackerIX = TurnResult#attacked.attacker_ix, + DefenderIX = TurnResult#attacked.defender_ix, + Sequence = TurnResult#attacked.sequence, + + EncodedSequence = lists:map(fun btl_attack:encode/1, Sequence), + + { + [ + {<<"t">>, <<"atk">>}, + {<<"aix">>, AttackerIX}, + {<<"dix">>, DefenderIX}, + {<<"seq">>, EncodedSequence} + ] + }; +encode (TurnResult) when is_record(TurnResult, player_won) -> + PlayerIX = TurnResult#player_won.player_ix, + + { + [ + {<<"t">>, <<"pwo">>}, + {<<"ix">>, PlayerIX} + ] + }; +encode (TurnResult) when is_record(TurnResult, player_lost) -> + PlayerIX = TurnResult#player_lost.player_ix, + + { + [ + {<<"t">>, <<"plo">>}, + {<<"ix">>, PlayerIX} + ] + }; +encode (TurnResult) when is_record(TurnResult, player_turn_started) -> + PlayerIX = TurnResult#player_turn_started.player_ix, + + { + [ + {<<"t">>, <<"pts">>}, + {<<"ix">>, PlayerIX} + ] + }; +encode (Other) -> + io:format("~n invalid encode param\"~p\"~n", [Other]), + true = Other. |