summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'elm/battlemap/src/Battlemap/Navigator')
-rw-r--r-- | elm/battlemap/src/Battlemap/Navigator/Path.elm | 170 | ||||
-rw-r--r-- | elm/battlemap/src/Battlemap/Navigator/RangeIndicator.elm | 284 |
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 |