summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'elm/battlemap/src/Battlemap/Navigator')
-rw-r--r--elm/battlemap/src/Battlemap/Navigator/Path.elm170
-rw-r--r--elm/battlemap/src/Battlemap/Navigator/RangeIndicator.elm284
2 files changed, 454 insertions, 0 deletions
diff --git a/elm/battlemap/src/Battlemap/Navigator/Path.elm b/elm/battlemap/src/Battlemap/Navigator/Path.elm
new file mode 100644
index 0000000..5ce2d4c
--- /dev/null
+++ b/elm/battlemap/src/Battlemap/Navigator/Path.elm
@@ -0,0 +1,170 @@
+module Battlemap.Navigator.Path exposing
+ (
+ Type,
+ new,
+ get_current_location,
+ get_remaining_points,
+ follow_directions
+ )
+
+import Set
+
+import Battlemap.Direction
+import Battlemap.Location
+import Battlemap.Tile
+
+--------------------------------------------------------------------------------
+-- TYPES -----------------------------------------------------------------------
+--------------------------------------------------------------------------------
+type alias Type =
+ {
+ current_location : Battlemap.Location.Type,
+ visited_locations : (Set.Set Battlemap.Location.Ref),
+ previous_directions : (List Battlemap.Direction.Type),
+ remaining_points : Int
+ }
+
+--------------------------------------------------------------------------------
+-- LOCAL -----------------------------------------------------------------------
+--------------------------------------------------------------------------------
+has_not_been_to : (
+ Type ->
+ Battlemap.Location.Type ->
+ Bool
+ )
+has_not_been_to path location =
+ (
+ (path.current_location /= location)
+ &&
+ (not
+ (Set.member
+ (Battlemap.Location.get_ref location)
+ path.visited_locations
+ )
+ )
+ )
+
+move_to : (
+ Type ->
+ Battlemap.Direction.Type ->
+ Battlemap.Location.Type ->
+ Int ->
+ Type
+ )
+move_to path dir next_loc cost =
+ {path |
+ current_location = next_loc,
+ visited_locations =
+ (Set.insert
+ (Battlemap.Location.get_ref path.current_location)
+ path.visited_locations
+ ),
+ previous_directions = (dir :: path.previous_directions),
+ remaining_points = (path.remaining_points - cost)
+ }
+
+battlemap_backtrack : (
+ Battlemap.Type ->
+ Battlemap.Location.Type ->
+ Battlemap.Type
+ )
+battlemap_backtrack battlemap current_loc =
+ (Battlemap.apply_to_tile_unsafe
+ battlemap
+ current_loc
+ (Battlemap.Tile.set_direction
+ Battlemap.Direction.None
+ )
+ )
+
+navigator_backtrack : (
+ Battlemap.Navigator.Type ->
+ Battlemap.Location.Type ->
+ (List Battlemap.Direction.Type) ->
+ Battlemap.Navigator.Type
+ )
+try_backtracking_to path location dir =
+ case (Util.List.pop nav.previous_directions) of
+ (Just (head, tail)) ->
+ if (head == (Battlemap.Direction.opposite_of dir))
+ then
+ (backtrack_to
+ nav
+ next_location
+ tail
+ )
+ )
+ else
+ (battlemap, nav)
+ Nothing -> (battlemap, nav)
+ move_to path next_location
+ if (can_move_to_new_tile path next_location)
+ then
+ else
+ {nav |
+ current_location = next_loc,
+ visited_locations =
+ (Set.remove
+ (Battlemap.Location.get_ref next_loc)
+ nav.visited_locations
+ ),
+ previous_directions = prev_dir_tail,
+ remaining_points = (nav.remaining_points + 1)
+ }
+
+
+to : (
+ Type ->
+ Battlemap.Direction.Type ->
+ (Battlemap.Type, Battlemap.Navigator.Type)
+ )
+to battlemap nav dir char_list =
+
+--------------------------------------------------------------------------------
+-- EXPORTED --------------------------------------------------------------------
+--------------------------------------------------------------------------------
+
+new : Battlemap.Location.Type -> Int -> Type
+new start points =
+ {
+ current_location = start,
+ visited_locations = Set.empty,
+ previous_directions = [],
+ remaining_points = points
+ }
+
+get_current_location : Type -> Battlemap.Location.Type
+get_current_location path = path.current_location
+
+get_remaining_points : Type -> Int
+get_remaining_points path = path.remaining_points
+
+follow_direction : (
+ (Battlemap.Location.Type -> Bool) ->
+ (Maybe Type) ->
+ Battlemap.Direction.Type ->
+ (Maybe Type)
+ )
+follow_direction can_cross cost_fun maybe_path dir =
+ case maybe_path of
+ (Just path) ->
+ let
+ next_location =
+ (Battlemap.Location.neighbor
+ nav.current_location
+ dir
+ )
+ in
+ if (can_cross path next_location)
+ then
+ if (has_not_been_to path next_location)
+ then
+ (Just (move_to path next_location dir))
+ else
+ (try_backtracking_to path next_location dir)
+ else
+ Nothing
+ else
+ (battlemap, nav)
+
+ Nothing -> Nothing
diff --git a/elm/battlemap/src/Battlemap/Navigator/RangeIndicator.elm b/elm/battlemap/src/Battlemap/Navigator/RangeIndicator.elm
new file mode 100644
index 0000000..c370d03
--- /dev/null
+++ b/elm/battlemap/src/Battlemap/Navigator/RangeIndicator.elm
@@ -0,0 +1,284 @@
+module Battlemap.Navigator.RangeIndicator exposing
+ (
+ Type,
+ generate,
+ get_marker
+ )
+
+import Dict
+import List
+import Debug
+
+import Battlemap.Direction
+import Battlemap.Location
+import Battlemap.Marker
+
+import Util.List
+
+type alias Type =
+ {
+ distance: Int,
+ path: (List Battlemap.Direction.Type),
+ node_cost: Int,
+ marker: Battlemap.Marker.Type
+ }
+
+generate_row : (
+ Battlemap.Location.Type ->
+ Int ->
+ Int ->
+ Int ->
+ (List Battlemap.Location.Type) ->
+ (List Battlemap.Location.Type)
+ )
+generate_row src max_x_mod curr_y curr_x_mod curr_row =
+ if (curr_x_mod > max_x_mod)
+ then
+ curr_row
+ else
+ (generate_row
+ src
+ max_x_mod
+ curr_y
+ (curr_x_mod + 1)
+ ({x = (src.x + curr_x_mod), y = curr_y} :: curr_row)
+ )
+
+generate_grid : (
+ Battlemap.Location.Type ->
+ Int ->
+ Int ->
+ (List Battlemap.Location.Type) ->
+ (List Battlemap.Location.Type)
+ )
+generate_grid src dist curr_y_mod curr_list =
+ if (curr_y_mod > dist)
+ then
+ curr_list
+ else
+ let
+ new_limit = (dist - (abs curr_y_mod))
+ in
+ (generate_grid
+ src
+ dist
+ (curr_y_mod + 1)
+ (
+ (generate_row
+ src
+ new_limit
+ (src.y + curr_y_mod)
+ (-new_limit)
+ []
+ )
+ ++ curr_list
+ )
+ )
+
+get_closest : (
+ Battlemap.Location.Ref ->
+ Type ->
+ (Battlemap.Location.Ref, Type) ->
+ (Battlemap.Location.Ref, Type)
+ )
+get_closest ref indicator (prev_ref, prev_indicator) =
+ if (indicator.distance < prev_indicator.distance)
+ then
+ (ref, indicator)
+ else
+ (prev_ref, prev_indicator)
+
+handle_neighbors : (
+ Battlemap.Location.Type ->
+ Int ->
+ Int ->
+ Type ->
+ (Dict.Dict Battlemap.Location.Ref Type) ->
+ (List Battlemap.Direction.Type) ->
+ (Dict.Dict Battlemap.Location.Ref Type)
+ )
+handle_neighbors loc dist atk_dist indicator remaining directions =
+ case (Util.List.pop directions) of
+ Nothing -> remaining
+ (Just (head, tail)) ->
+ let
+ neighbor_loc = (Battlemap.Location.neighbor loc head)
+ neighbor_indicator =
+ (Dict.get
+ (Battlemap.Location.get_ref neighbor_loc)
+ remaining
+ )
+ in
+ case neighbor_indicator of
+ Nothing ->
+ (handle_neighbors
+ loc
+ dist
+ atk_dist
+ indicator
+ remaining
+ tail
+ )
+ (Just neighbor) ->
+ let
+ is_attack_range = (indicator.distance >= dist)
+ new_dist =
+ (
+ if (is_attack_range)
+ then
+ (indicator.distance + 1)
+ else
+ (indicator.distance + neighbor.node_cost)
+ )
+ in
+ (handle_neighbors
+ loc
+ dist
+ atk_dist
+ indicator
+ (
+ if
+ (
+ (new_dist < neighbor.distance)
+ && (new_dist <= atk_dist)
+ )
+ then
+ (Dict.insert
+ (Battlemap.Location.get_ref neighbor_loc)
+ {neighbor |
+ distance = new_dist,
+ path = (head :: indicator.path)
+ }
+ remaining
+ )
+ else
+ remaining
+ )
+ tail
+ )
+
+search : (
+ (Dict.Dict Battlemap.Location.Ref Type) ->
+ (Dict.Dict Battlemap.Location.Ref Type) ->
+ Int ->
+ Int ->
+ (Dict.Dict Battlemap.Location.Ref Type)
+ )
+search result remaining dist atk_dist =
+ if (Dict.isEmpty remaining)
+ then
+ result
+ else
+ let
+ (min_loc_ref, min) =
+ (Dict.foldl
+ (get_closest)
+ (
+ (-1,-1),
+ {
+ distance = (atk_dist + 1),
+ path = [],
+ node_cost = 99,
+ marker = Battlemap.Marker.CanAttack
+ }
+ )
+ remaining
+ )
+ in
+ (search
+ (Dict.insert
+ min_loc_ref
+ {min |
+ marker =
+ (
+ if (min.distance > dist)
+ then
+ Battlemap.Marker.CanAttack
+ else
+ Battlemap.Marker.CanGoTo
+ )
+ }
+ result
+ )
+ (handle_neighbors
+ (Battlemap.Location.from_ref min_loc_ref)
+ dist
+ atk_dist
+ min
+ (Dict.remove min_loc_ref remaining)
+ [
+ Battlemap.Direction.Left,
+ Battlemap.Direction.Right,
+ Battlemap.Direction.Up,
+ Battlemap.Direction.Down
+ ]
+ )
+ dist
+ atk_dist
+ )
+
+grid_to_range_indicators : (
+ (Battlemap.Location.Type -> Bool) ->
+ Battlemap.Location.Type ->
+ Int ->
+ (List Battlemap.Location.Type) ->
+ (Dict.Dict Battlemap.Location.Ref Type) ->
+ (Dict.Dict Battlemap.Location.Ref Type)
+ )
+grid_to_range_indicators can_cross_fun location dist grid result =
+ case (Util.List.pop grid) of
+ Nothing -> result
+ (Just (head, tail)) ->
+ if (can_cross_fun head)
+ then
+ -- TODO: test if the current char can cross that tile.
+ -- TODO: get tile cost.
+ (grid_to_range_indicators
+ (can_cross_fun)
+ location
+ dist
+ tail
+ (Dict.insert
+ (Battlemap.Location.get_ref head)
+ {
+ distance =
+ (
+ if ((location.x == head.x) && (location.y == head.y))
+ then
+ 0
+ else
+ (dist + 1)
+ ),
+ path = [],
+ node_cost = 1,
+ marker = Battlemap.Marker.CanGoTo
+ }
+ result
+ )
+ )
+ else
+ (grid_to_range_indicators (can_cross_fun) location dist tail result)
+
+generate : (
+ Battlemap.Location.Type ->
+ Int ->
+ Int ->
+ (Battlemap.Location.Type -> Bool) ->
+ (Dict.Dict Battlemap.Location.Ref Type)
+ )
+generate location dist atk_dist can_cross_fun =
+ (search
+ Dict.empty
+ (grid_to_range_indicators
+ (can_cross_fun)
+ location
+ atk_dist
+ (generate_grid location atk_dist (-atk_dist) [])
+ Dict.empty
+ )
+ dist
+ atk_dist
+ )
+
+get_marker : Type -> Battlemap.Marker.Type
+get_marker indicator = indicator.marker