summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2019-01-02 01:46:34 +0100
committerNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2019-01-02 01:46:34 +0100
commit71c2f729208cecb039e2bd753a50b55c2788f2d0 (patch)
treea0905159ccb3cce53419dbe48457951245af9559 /src/bounty/bnt_join_battle.erl
parenta81ff643a823dde57ebd0ed5da8a91fb75d32267 (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/bnt_join_battle.erl')
-rw-r--r--src/bounty/bnt_join_battle.erl483
1 files changed, 483 insertions, 0 deletions
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.