| summaryrefslogtreecommitdiff | 
diff options
| author | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2019-01-02 01:46:34 +0100 | 
|---|---|---|
| committer | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2019-01-02 01:46:34 +0100 | 
| commit | 71c2f729208cecb039e2bd753a50b55c2788f2d0 (patch) | |
| tree | a0905159ccb3cce53419dbe48457951245af9559 /src/bounty | |
| parent | a81ff643a823dde57ebd0ed5da8a91fb75d32267 (diff) | |
Bounties?
Somehow, Dialyzer does not see the type issues with
src/bounty/bnt_join_battle.erl, but there are btl_character and
rst_character mix-ups.
Diffstat (limited to 'src/bounty')
| -rw-r--r-- | src/bounty/bnt_generate_player.erl | 191 | ||||
| -rw-r--r-- | src/bounty/bnt_grant_land.erl | 58 | ||||
| -rw-r--r-- | src/bounty/bnt_join_battle.erl | 483 | 
3 files changed, 732 insertions, 0 deletions
| diff --git a/src/bounty/bnt_generate_player.erl b/src/bounty/bnt_generate_player.erl new file mode 100644 index 0000000..e02a94b --- /dev/null +++ b/src/bounty/bnt_generate_player.erl @@ -0,0 +1,191 @@ +-module(bnt_generate_player). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([attempt/3]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec reserve_login (binary(), binary()) -> 'ok'. +reserve_login (UsernameLC, EmailLC) -> +   ok = ataxia_client:reserve(login_db, UsernameLC), +   ok = ataxia_client:reserve(login_db, EmailLC), + +   ok. + +-spec finalize_login (binary(), binary(), binary()) -> 'ok'. +finalize_login (UsernameLC, EmailLC, PlayerID) -> +   LoginUpdateQueryOps = +      ataxic:sequence_meta +      ( +         [ +            ataxic:update_value(ataxic:constant(PlayerID)), +            ataxic:update_read_permission +            ( +               ataxic:constant +               ( +                  ataxia_security:allow_only +                  ( +                     ataxia_security:any() +                  ) +               ) +            ), +            ataxic:update_write_permission +            ( +               ataxic:constant +               ( +                  ataxia_security:allow_only +                  ( +                     ataxia_security:user_from_id(PlayerID) +                  ) +               ) +            ) +         ] +      ), + +   ok = +      ataxia_client:update +      ( +         login_db, +         ataxia_security:janitor(), +         LoginUpdateQueryOps, +         UsernameLC +      ), + +   ok = +      ataxia_client:update +      ( +         login_db, +         ataxia_security:janitor(), +         LoginUpdateQueryOps, +         EmailLC +      ), + +   'ok'. + +-spec generate_inventory (ataxia_id:type()) -> ataxia_id:type(). +generate_inventory (PlayerID) -> +   Inventory = shr_inventory:new(PlayerID), + +   {ok, InventoryID} = +      ataxia_client:add +      ( +         inventory_db, +         ataxia_security:allow_only(ataxia_security:any()), +         ataxia_security:allow_only(ataxia_security:user_from_id(PlayerID)), +         Inventory +      ), + +   InventoryID. + +-spec generate_roster (ataxia_id:type()) -> ataxia_id:type(). +generate_roster (PlayerID) -> +   Roster = rst_roster:new(PlayerID), +   {ok, RosterID} = +      ataxia_client:add +      ( +         roster_db, +         ataxia_security:allow_only(ataxia_security:any()), +         ataxia_security:allow_only(ataxia_security:user_from_id(PlayerID)), +         Roster +      ), + +   RosterID. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec attempt (binary(), binary(), binary()) -> shr_player:type(). +attempt (Username, Password, Email) -> +   UsernameLC = string:lowercase(Username), +   EmailLC = string:lowercase(Email), + +   ok = reserve_login(UsernameLC, EmailLC), + +   Player = shr_player:new(<<"">>, Username, Password, Email), + +   JanitorOnlyPermission = +      ataxia_security:allow_only(ataxia_security:janitor()), + +   {ok, PlayerID} = +      ataxia_client:add +      ( +         player_db, +         JanitorOnlyPermission, +         JanitorOnlyPermission, +         Player +      ), + +   shr_janitor:new(player_db, PlayerID), + +   InvID = generate_inventory(PlayerID), +   RosterID = generate_roster(PlayerID), + +   PlayerUpdateQueryOps = +      ataxic:sequence_meta +      ( +         [ +            ataxic:update_value +            ( +               ataxic:sequence +               ( +                  [ +                     ataxic:update_field +                     ( +                        shr_player:get_id_field(), +                        ataxic:constant(PlayerID) +                     ), +                     ataxic:update_field +                     ( +                        shr_player:get_inventory_id_field(), +                        ataxic:constant(InvID) +                     ), +                     ataxic:update_field +                     ( +                        shr_player:get_roster_id_field(), +                        ataxic:constant(RosterID) +                     ) +                  ] +               ) +            ), +            ataxic:update_read_permission +            ( +               ataxic:constant +               ( +                  ataxia_security:allow_only(ataxia_security:any()) +               ) +            ), +            ataxic:update_write_permission +            ( +               ataxic:constant +               ( +                  ataxia_security:allow_only +                  ( +                     ataxia_security:user_from_id(PlayerID) +                  ) +               ) +            ) +         ] +      ), + +   ok = finalize_login(UsernameLC, EmailLC, PlayerID), + +   ok = +      ataxia_client:update +      ( +         player_db, +         ataxia_security:janitor(), +         PlayerUpdateQueryOps, +         PlayerID +      ), + + +   Result = shr_player:set_id(PlayerID, Player), + +   Result. diff --git a/src/bounty/bnt_grant_land.erl b/src/bounty/bnt_grant_land.erl new file mode 100644 index 0000000..1a8a62e --- /dev/null +++ b/src/bounty/bnt_grant_land.erl @@ -0,0 +1,58 @@ +-module(bnt_grant_land). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([attempt/1]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec attempt (ataxia_id:type()) -> map_map:type(). +attempt (OwnerID) -> +   Map = map_map:default(OwnerID), + +   {ok, MapID} = +      ataxia_client:add +      ( +         map_db, +         ataxia_security:allow_only(ataxia_security:any()), +         ataxia_security:allow_only(ataxia_security:user_from_id(OwnerID)), +         Map +      ), + +   MapSummary = shr_map_summary:new(MapID, <<"Untitled Map">>), + +   PlayerUpdateQueryOp = +      ataxic:update_value +      ( +         ataxic:update_field +         ( +            shr_player:get_map_summaries_field(), +            ataxic:apply_function +            ( +               lists, +               append, +               [ataxic:constant([MapSummary]), ataxic:current_value()] +            ) +         ) +      ), + +   ok = +      ataxia_client:update +      ( +         player_db, +         ataxia_security:admin(), +         PlayerUpdateQueryOp, +         OwnerID +      ), + +   Map. diff --git a/src/bounty/bnt_join_battle.erl b/src/bounty/bnt_join_battle.erl new file mode 100644 index 0000000..1028166 --- /dev/null +++ b/src/bounty/bnt_join_battle.erl @@ -0,0 +1,483 @@ +-module(bnt_join_battle). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([generate/3, attempt/3]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% USED IDS COLLECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec update_ordset +   ( +      ordsets:ordset(any()), +      ordsets:ordset(any()) +   ) +   -> ataxic:basic(). +update_ordset (New, Old) -> +   AddedElements = ordsets:subtract(New, Old), + +   ataxic:sequence +   ( +      lists:map +      ( +         fun (V) -> +            ataxic:apply_function +            ( +               ordsets, +               add_element, +               [ +                  ataxic:constant(V), +                  ataxic:current_value() +               ] +            ) +         end, +         ordsets:to_list(AddedElements) +      ) +   ). + +-spec get_equipment_ids +   ( +      orddict:orddict(non_neg_integer(), btl_character:type()) +   ) +   -> +   { +      ordsets:ordset(shr_portrait:id()), +      ordsets:ordset(shr_weapon:id()), +      ordsets:ordset(shr_armor:id()) +   }. +get_equipment_ids (Characters) -> +   { +      UsedPortraitIDs, +      UsedWeaponIDs, +      UsedArmorIDs +   } = +      orddict:fold +      ( +         fun (_IX, Character, {UPIDs, UWIDs, UAIDs}) -> +            {MWpID, SWpID} = btl_character:get_weapon_ids(Character), +            AID = btl_character:get_armor_id(Character), +            PID = btl_character:get_portrait_id(Character), +            { +               ordsets:add_element(PID, UPIDs), +               ordsets:add_element(MWpID, ordsets:add_element(SWpID, UWIDs)), +               ordsets:add_element(AID, UAIDs) +            } +         end, +         {ordsets:new(), ordsets:new(), ordsets:new()}, +         Characters +      ), + +   {UsedPortraitIDs, UsedWeaponIDs, UsedArmorIDs}. + + +%%%% ROSTERS HANDLING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec get_forbidden_locations +   ( +      btl_battle:type() +   ) +   -> ordsets:ordset(btl_location:type()). +get_forbidden_locations (Battle) -> +   orddict:fold +   ( +      fun (_IX, Char, Set) -> +         ordsets:add_element(btl_character:get_location(Char), Set) +      end, +      ordsets:new(), +      btl_battle:get_characters(Battle) +   ). + +-spec find_random_location +   ( +      btl_map:type(), +      ordsets:ordset(btl_location:type()) +   ) +   -> {btl_location:type(), shr_tile:type()}. +find_random_location (Map, ForbiddenLocations) -> +   MapWidth = btl_map:get_width(Map), +   MapHeight = btl_map:get_height(Map), + +   Candidate = +      { +         shr_roll:between(0, (MapWidth - 1)), +         shr_roll:between(0, (MapHeight - 1)) +      }, + +   IsForbidden = ordsets:is_element(Candidate, ForbiddenLocations), + +   case IsForbidden of +      true -> find_random_location(Map, ForbiddenLocations); + +      _ -> +         Tile = +            shr_tile:from_class_id +            ( +               shr_tile:extract_main_class_id +               ( +                  btl_map:get_tile_instance(Candidate, Map) +               ) +            ), + +         case (shr_tile:get_cost(Tile) > 200) of +            true -> find_random_location(Map, ForbiddenLocations); + +            false -> {Candidate, Tile} +         end +   end. + +-spec get_glyphs_omnimods (rst_character:type()) -> shr_omnimods:type(). +get_glyphs_omnimods (RosterChar) -> +   GlyphBoardID = rst_character:get_glyph_board_id(RosterChar), +   GlyphIDs = rst_character:get_glyph_ids(RosterChar), +   GlyphBoard = shr_glyph_board:from_id(GlyphBoardID), +   Glyphs = lists:map(fun shr_glyph:from_id/1, GlyphIDs), +   case shr_glyph_board:get_omnimods_with_glyphs(Glyphs, GlyphBoard) of +      {ok, Result} -> Result; +      error -> shr_omnimods:new([], [], [], []) +   end. + +-spec create_character +   ( +      non_neg_integer(), +      rst_character:type(), +      btl_map:type(), +      ordsets:ordset(btl_location:type()) +   ) +   -> btl_character:type(). +create_character (PlayerIX, RosterChar, Map, ForbiddenLocations) -> +   {Location, Tile} = find_random_location(Map, ForbiddenLocations), +   TileOmnimods = shr_tile:get_omnimods(Tile), +   GlyphsOmnimods = get_glyphs_omnimods(RosterChar), + +   Result = +      btl_character:new +      ( +         PlayerIX, +         rst_character:get_name(RosterChar), +         optional, % TODO: link this to roster. +         GlyphsOmnimods, +         rst_character:get_portrait_id(RosterChar), +         rst_character:get_weapon_ids(RosterChar), +         rst_character:get_armor_id(RosterChar), +         Location, +         TileOmnimods +      ), + +   Result. + +-spec handle_characters +   ( +      list(rst_character:type()), +      non_neg_integer(), +      btl_map:type(), +      ordsets:ordset(btl_location:type()), +      non_neg_integer(), +      orddict:orddict(non_neg_integer(), btl_character:type()), +      list(ataxic:basic()) +   ) +   -> +   { +      orddict:orddict(non_neg_integer(), btl_character:type()), +      list(ataxic:basic()) +   }. +handle_characters +( +   [], +   _PlayerIX, +   _Map, +   _UsedLocations, +   _NextCharIX, +   Characters, +   AtaxicUpdates +) -> +   {Characters, AtaxicUpdates}; +handle_characters +( +   [RosterCharacter|NextRosterCharacters], +   PlayerIX, +   Map, +   UsedLocations, +   NextCharIX, +   Characters, +   AtaxicUpdates +) -> +   NewCharacter = +      create_character(PlayerIX, RosterCharacter, Map, UsedLocations), + +   NewCharacters = orddict:store(NextCharIX, NewCharacter, Characters), + +   NewUpdate = +      ataxic:apply_function +      ( +         orddict, +         store, +         [ +            ataxic:constant(NextCharIX), +            ataxic:constant(NewCharacter), +            ataxic:current_value() +         ] +      ), + +   handle_characters +   ( +      NextRosterCharacters, +      PlayerIX, +      Map, +      [btl_character:get_location(NewCharacter)|UsedLocations], +      (NextCharIX + 1), +      NewCharacters, +      [NewUpdate|AtaxicUpdates] +   ). + +-spec add_player +   ( +      shr_player:id(), +      btl_battle:type() +   ) +   -> {btl_battle:type(), non_neg_integer(), ataxic:basic()}. +add_player (PlayerID, Battle) -> +   Players = btl_battle:get_players(Battle), + +   PlayerIX = orddict:size(Players), +   NewPlayer = btl_player:new(PlayerIX, 0, PlayerID), + +   NewPlayers = orddict:store(PlayerIX, NewPlayer, Players), +   S0Battle = btl_battle:set_players(NewPlayers, Battle), + +   Update = +      ataxic:update_field +      ( +         btl_battle:get_players_field(), +         ataxic:apply_function +         ( +            orddict, +            store, +            [ +               ataxic:constant(PlayerIX), +               ataxic:constant(NewPlayer), +               ataxic:current_value() +            ] +         ) +      ), + +   {S0Battle, PlayerIX, Update}. + +-spec add_characters +   ( +      list(rst_character:type()), +      non_neg_integer(), +      btl_battle:type() +   ) +   -> {btl_battle:type(), ataxic:basic()}. +add_characters (RosterCharacters, PlayerIX, Battle) -> +   CurrentCharacters = btl_battle:get_characters(Battle), +   NextCharacterIX = orddict:size(CurrentCharacters), +   Map = btl_battle:get_map(Battle), + +   ForbiddenLocations = get_forbidden_locations(Battle), + +   {NewCharacters, CharactersUpdates} = +      handle_characters +      ( +         RosterCharacters, +         PlayerIX, +         Map, +         ForbiddenLocations, +         NextCharacterIX, +         CurrentCharacters, +         [] +      ), + +   {UsedPortraitIDs, UsedWeaponIDs, UsedArmorIDs} = +      get_equipment_ids(RosterCharacters), + +   OldPortraitIDs = btl_battle:get_used_portrait_ids(Battle), +   PortraitIDsUpdate = +      ataxic:update_field +      ( +         btl_battle:get_used_portrait_ids_field(), +         update_ordset(UsedPortraitIDs, OldPortraitIDs) +      ), + +   OldWeaponIDs = btl_battle:get_used_portrait_ids(Battle), +   WeaponIDsUpdate = +      ataxic:update_field +      ( +         btl_battle:get_used_weapon_ids_field(), +         update_ordset(UsedWeaponIDs, OldWeaponIDs) +      ), + +   OldArmorIDs = btl_battle:get_used_armor_ids(Battle), +   ArmorIDsUpdate = +      ataxic:update_field +      ( +         btl_battle:get_used_armor_ids_field(), +         update_ordset(UsedArmorIDs, OldArmorIDs) +      ), + +   S0Battle = btl_battle:set_characters(NewCharacters, Battle), +   S1Battle = +      btl_battle:set_used_armor_ids +      ( +         ordsets:union(UsedArmorIDs, OldArmorIDs), +         btl_battle:set_used_weapon_ids +         ( +            ordsets:union(UsedWeaponIDs, OldWeaponIDs), +            btl_battle:set_used_portrait_ids +            ( +               ordsets:union(UsedPortraitIDs, OldPortraitIDs), +               S0Battle +            ) +         ) +      ), + +   Update = +      ataxic:sequence +      ( +         [ +            ataxic:update_field +            ( +               btl_battle:get_characters_field(), +               ataxic:sequence(CharactersUpdates) +            ), +            PortraitIDsUpdate, +            WeaponIDsUpdate, +            ArmorIDsUpdate +         ] +      ), + +   {S1Battle, Update}. + +-spec get_roster_characters +   ( +      shr_player:id(), +      list(non_neg_integer()) +   ) +   -> list(rst_character:type()). +get_roster_characters (PlayerID, SelectedRosterCharacterIXs) -> +   Player = shr_timed_cache:fetch(player_db, ataxia_security:any(), PlayerID), +   RosterID = shr_player:get_roster_id(Player), +   Roster = +      shr_timed_cache:fetch +      ( +         roster_db, +         ataxia_security:user_from_id(PlayerID), +         RosterID +      ), + +   RosterCharacters = rst_roster:get_characters(Roster), + +   lists:map +   ( +      fun (CharIX) -> +         orddict:fetch(CharIX, RosterCharacters) +      end, +      SelectedRosterCharacterIXs +   ). + +%%%% BATTLE CREATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec generate_battle (shr_player:id(), map_map:id()) -> btl_battle:type(). +generate_battle (PlayerID, MapID) -> +   Map = +      shr_timed_cache:fetch +      ( +         map_db, +         ataxia_security:user_from_id(PlayerID), +         MapID +      ), +   TileInstances = map_map:get_tile_instances(Map), +   BattleMap = +      btl_map:from_instances_tuple +      ( +         map_map:get_width(Map), +         map_map:get_height(Map), +         TileInstances +      ), + +   Battle = btl_battle:new(BattleMap), + +   Battle. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec generate +   ( +      shr_player:id(), +      map_map:id(), +      list(non_neg_integer()) +   ) +   -> btl_pending_battle:type(). +generate (PlayerID, MapID, SelectedRosterCharacterIXs) -> +   Battle = generate_battle(PlayerID, MapID), +   PendingBattle = +      btl_pending_battle:new +      ( +         <<"">>, +         % TODO: More options than 1 vs N. +         (length(SelectedRosterCharacterIXs) * 2), +         Battle +      ), + +   attempt(PlayerID, SelectedRosterCharacterIXs, PendingBattle). + +-spec attempt +   ( +      shr_player:id(), +      list(non_neg_integer()), +      btl_pending_battle:type() +   ) +   -> btl_pending_battle:type(). +attempt (PlayerID, SelectedRosterCharacterIXs, PendingBattle) -> +   Battle = btl_pending_battle:get_battle(PendingBattle), +   RemainingSlots = +      ( +         btl_pending_battle:get_free_slots(PendingBattle) +         - length(SelectedRosterCharacterIXs) +      ), + +   NewCharacters = get_roster_characters(PlayerID, SelectedRosterCharacterIXs), +   {S0Battle, PlayerIX, BattleUpdate0} = add_player(PlayerID, Battle), +   {S1Battle, BattleUpdate1} = +      add_characters(NewCharacters, PlayerIX, S0Battle), + +   S0PendingBattle = btl_pending_battle:set_battle(S1Battle, PendingBattle), +   S1PendingBattle = +      btl_pending_battle:set_free_slots(RemainingSlots, S0PendingBattle), + +   Update = +      ataxic:sequence +      ( +         [ +            ataxic:update_field +            ( +               btl_pending_battle:get_battle_field(), +               ataxic:sequence +               ( +                  [ +                     BattleUpdate0, +                     BattleUpdate1 +                  ] +               ) +            ), +            ataxic:update_field +            ( +               btl_pending_battle:get_free_slots_field(), +               ataxic:constant(RemainingSlots) +            ) +         ] +      ), + +   {S1PendingBattle, Update}, + +   S1PendingBattle. + +   % TODO: +   % if RemainingSlots = 0 -> del this, new Battle. +   % Link either new Battle or current Pending Battle to player account. | 


