Mario Party
parent
5ab6f82f4b
commit
2a22d3865c
|
@ -1,61 +1,61 @@
|
||||||
class_name MPNavigationGraph
|
class_name MPNavigationGraph
|
||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
# godot does not support types on dictionaries, actual type is Dictionary[NavigationNode, Array[NavigationNode]]
|
# godot does not support types on dictionaries, actual type is Dictionary[MPNavigationNode, Array[MPNavigationNode]]
|
||||||
var navigation_nodes: Dictionary = {}
|
var navigation_nodes: Dictionary = {}
|
||||||
var latest_navigation_result: PathfindingResult = null
|
var latest_navigation_result: MPPathfindingResult = null
|
||||||
var draw_nodes: bool = true
|
var draw_nodes: bool = false
|
||||||
var draw_edges: bool = true
|
var draw_edges: bool = false
|
||||||
|
|
||||||
|
|
||||||
func all_nodes() -> Array[NavigationNode]:
|
func all_nodes() -> Array[MPNavigationNode]:
|
||||||
# i've had a problem where godot would not allow me to directly return navigation_nodes.keys()
|
# i've had a problem where godot would not allow me to directly return navigation_nodes.keys()
|
||||||
# because it wasn't able to cast the keys to Array[NavigationNode] directly because the type is not explicit
|
# because it wasn't able to cast the keys to Array[MPNavigationNode] directly because the type is not explicit
|
||||||
# on the dictionary, so i had to do this workaround.
|
# on the dictionary, so i had to do this workaround.
|
||||||
var keys: Array = navigation_nodes.keys()
|
var keys: Array = navigation_nodes.keys()
|
||||||
var nodes: Array[NavigationNode] = []
|
var nodes: Array[MPNavigationNode] = []
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key is NavigationNode:
|
if key is MPNavigationNode:
|
||||||
nodes.append(key)
|
nodes.append(key)
|
||||||
else:
|
else:
|
||||||
push_error("Key is not a NavigationNode: %s" % key)
|
push_error("Key is not a MPNavigationNode: %s" % key)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
func get_connections(from: NavigationNode) -> Array[NavigationNode]:
|
func get_connections(from: MPNavigationNode) -> Array[MPNavigationNode]:
|
||||||
# the same problem as the all_nodes() function
|
# the same problem as the all_nodes() function
|
||||||
var connections: Array = navigation_nodes[from]
|
var connections: Array = navigation_nodes[from]
|
||||||
var nodes: Array[NavigationNode] = []
|
var nodes: Array[MPNavigationNode] = []
|
||||||
for connection in connections:
|
for connection in connections:
|
||||||
if connection is NavigationNode:
|
if connection is MPNavigationNode:
|
||||||
nodes.append(connection)
|
nodes.append(connection)
|
||||||
else:
|
else:
|
||||||
push_error("Connection is not a NavigationNode: %s" % connection)
|
push_error("Connection is not a MPNavigationNode: %s" % connection)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
func add_connection(from: NavigationNode, to: NavigationNode) -> void:
|
func add_connection(from: MPNavigationNode, to: MPNavigationNode) -> void:
|
||||||
if all_nodes().has(from):
|
if all_nodes().has(from):
|
||||||
navigation_nodes[from].append(to)
|
navigation_nodes[from].append(to)
|
||||||
else:
|
else:
|
||||||
navigation_nodes[from] = [to]
|
navigation_nodes[from] = [to]
|
||||||
|
|
||||||
|
|
||||||
func add_node(x: float, y: float, merge_threshold: float = -1.0) -> NavigationNode:
|
func add_node(x: float, y: float, merge_threshold: float = -1.0) -> MPNavigationNode:
|
||||||
if merge_threshold > 0:
|
if merge_threshold > 0:
|
||||||
var closest_node: NavigationNode = find_closest_node_with_threshold(Vector2(x, y), merge_threshold)
|
var closest_node: MPNavigationNode = find_closest_node_with_threshold(Vector2(x, y), merge_threshold)
|
||||||
if closest_node:
|
if closest_node:
|
||||||
closest_node.was_merged = true
|
closest_node.was_merged = true
|
||||||
return closest_node
|
return closest_node
|
||||||
var node: NavigationNode = NavigationNode.new()
|
var node: MPNavigationNode = MPNavigationNode.new()
|
||||||
node.set_position(Vector2(x, y))
|
node.set_position(Vector2(x, y))
|
||||||
navigation_nodes[node] = []
|
navigation_nodes[node] = []
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
func find_closest_node_with_threshold(position: Vector2, threshold: float) -> NavigationNode:
|
func find_closest_node_with_threshold(position: Vector2, threshold: float) -> MPNavigationNode:
|
||||||
var closest_node: NavigationNode = null
|
var closest_node: MPNavigationNode = null
|
||||||
var closest_distance: float = threshold
|
var closest_distance: float = threshold
|
||||||
for node in all_nodes():
|
for node in all_nodes():
|
||||||
var distance: float = position.distance_to(node.position)
|
var distance: float = position.distance_to(node.position)
|
||||||
if distance < closest_distance:
|
if distance < closest_distance:
|
||||||
|
@ -64,12 +64,12 @@ func find_closest_node_with_threshold(position: Vector2, threshold: float) -> Na
|
||||||
return closest_node
|
return closest_node
|
||||||
|
|
||||||
|
|
||||||
func remove_connection(from: NavigationNode, to: NavigationNode) -> void:
|
func remove_connection(from: MPNavigationNode, to: MPNavigationNode) -> void:
|
||||||
if all_nodes().has(from):
|
if all_nodes().has(from):
|
||||||
navigation_nodes[from].erase(to)
|
navigation_nodes[from].erase(to)
|
||||||
|
|
||||||
|
|
||||||
func remove_node(node: NavigationNode) -> void:
|
func remove_node(node: MPNavigationNode) -> void:
|
||||||
navigation_nodes.erase(node)
|
navigation_nodes.erase(node)
|
||||||
for other_node in all_nodes():
|
for other_node in all_nodes():
|
||||||
if other_node != node:
|
if other_node != node:
|
||||||
|
@ -77,7 +77,6 @@ func remove_node(node: NavigationNode) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
print("Drawing navigation graph")
|
|
||||||
if draw_nodes or draw_edges:
|
if draw_nodes or draw_edges:
|
||||||
for from in all_nodes():
|
for from in all_nodes():
|
||||||
if draw_edges:
|
if draw_edges:
|
||||||
|
@ -90,24 +89,31 @@ func _draw() -> void:
|
||||||
if latest_navigation_result.path.size() > 1:
|
if latest_navigation_result.path.size() > 1:
|
||||||
for i in range(latest_navigation_result.path.size() - 1):
|
for i in range(latest_navigation_result.path.size() - 1):
|
||||||
draw_line(local_to_world(latest_navigation_result.path[i].position), local_to_world(latest_navigation_result.path[i + 1].position), Color.GREEN, 1, false)
|
draw_line(local_to_world(latest_navigation_result.path[i].position), local_to_world(latest_navigation_result.path[i + 1].position), Color.GREEN, 1, false)
|
||||||
draw_circle(local_to_world(latest_navigation_result.next_position), 5, Color.GREEN)
|
for node in latest_navigation_result.path:
|
||||||
|
draw_circle(local_to_world(node.position), 5, Color.GREEN)
|
||||||
|
|
||||||
|
|
||||||
func local_to_world(location: Vector2) -> Vector2:
|
func local_to_world(location: Vector2) -> Vector2:
|
||||||
return location * 32 + Vector2(16, 16)
|
return location * 32 + Vector2(16, 16)
|
||||||
|
|
||||||
func draw_pathfinding_result(result: PathfindingResult) -> void:
|
|
||||||
|
func draw_pathfinding_result(result: MPPathfindingResult) -> void:
|
||||||
if latest_navigation_result and latest_navigation_result.is_identical_to(result):
|
if latest_navigation_result and latest_navigation_result.is_identical_to(result):
|
||||||
return
|
return
|
||||||
latest_navigation_result = result
|
latest_navigation_result = result
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
func determine_next_position(current_position: Vector2, target_position: Vector2) -> PathfindingResult:
|
func clear_pathfinding_result() -> void:
|
||||||
var result: PathfindingResult = PathfindingResult.new()
|
latest_navigation_result = null
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func determine_next_position(current_position: Vector2, target_position: Vector2) -> MPPathfindingResult:
|
||||||
|
var result: MPPathfindingResult = MPPathfindingResult.new()
|
||||||
|
|
||||||
# Find the closest node to the current position
|
# Find the closest node to the current position
|
||||||
var start_node: NavigationNode = find_closest_node_with_threshold(current_position, INF)
|
var start_node: MPNavigationNode = find_closest_node_with_threshold(current_position, INF)
|
||||||
if start_node == null:
|
if start_node == null:
|
||||||
# No nodes exist; cannot navigate
|
# No nodes exist; cannot navigate
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
|
@ -115,7 +121,7 @@ func determine_next_position(current_position: Vector2, target_position: Vector2
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Find the closest node to the target position
|
# Find the closest node to the target position
|
||||||
var end_node: NavigationNode = find_closest_node_with_threshold(target_position, INF)
|
var end_node: MPNavigationNode = find_closest_node_with_threshold(target_position, INF)
|
||||||
if end_node == null:
|
if end_node == null:
|
||||||
# No nodes exist; cannot navigate
|
# No nodes exist; cannot navigate
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
|
@ -129,7 +135,7 @@ func determine_next_position(current_position: Vector2, target_position: Vector2
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Run Dijkstra's algorithm to find the path
|
# Run Dijkstra's algorithm to find the path
|
||||||
var path: Array[NavigationNode] = dijkstra(start_node, end_node)
|
var path: Array[MPNavigationNode] = dijkstra(start_node, end_node)
|
||||||
result.path = path
|
result.path = path
|
||||||
|
|
||||||
# If a path is found, return the position of the next node in the path
|
# If a path is found, return the position of the next node in the path
|
||||||
|
@ -149,26 +155,26 @@ func determine_next_position(current_position: Vector2, target_position: Vector2
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
func array_contains_node(arr: Array, node: NavigationNode) -> bool:
|
func array_contains_node(arr: Array, node: MPNavigationNode) -> bool:
|
||||||
for item in arr:
|
for item in arr:
|
||||||
if item == node:
|
if item == node:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
func dijkstra(start_node: NavigationNode, end_node: NavigationNode) -> Array[NavigationNode]:
|
func dijkstra(start_node: MPNavigationNode, end_node: MPNavigationNode) -> Array[MPNavigationNode]:
|
||||||
var open_set: Array[NavigationNode] = []
|
var open_set: Array[MPNavigationNode] = []
|
||||||
var closed_set: Array[NavigationNode] = []
|
var closed_set: Array[MPNavigationNode] = []
|
||||||
var distances: Dictionary = {}
|
var distances: Dictionary = {}
|
||||||
var previous_nodes: Dictionary = {}
|
var previous_nodes: Dictionary = {}
|
||||||
|
|
||||||
distances[start_node] = 0
|
distances[start_node] = 0
|
||||||
open_set.append(start_node)
|
open_set.append(start_node)
|
||||||
|
|
||||||
while open_set.size() > 0:
|
while open_set.size() > 0:
|
||||||
# Find the node with the smallest tentative distance
|
# Find the node with the smallest tentative distance
|
||||||
var current_node: NavigationNode = open_set[0]
|
var current_node: MPNavigationNode = open_set[0]
|
||||||
var current_distance: float = distances[current_node]
|
var current_distance: float = distances[current_node]
|
||||||
for node in open_set:
|
for node in open_set:
|
||||||
if distances[node] < current_distance:
|
if distances[node] < current_distance:
|
||||||
current_node = node
|
current_node = node
|
||||||
|
@ -176,8 +182,8 @@ func dijkstra(start_node: NavigationNode, end_node: NavigationNode) -> Array[Nav
|
||||||
|
|
||||||
# If the end node is reached, reconstruct the path
|
# If the end node is reached, reconstruct the path
|
||||||
if current_node == end_node:
|
if current_node == end_node:
|
||||||
var path: Array[NavigationNode] = []
|
var path: Array[MPNavigationNode] = []
|
||||||
var node: NavigationNode = end_node
|
var node: MPNavigationNode = end_node
|
||||||
while node != null:
|
while node != null:
|
||||||
path.insert(0, node)
|
path.insert(0, node)
|
||||||
node = previous_nodes.get(node, null)
|
node = previous_nodes.get(node, null)
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
class_name MPNavigationNode
|
class_name MPNavigationNode
|
||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
|
var was_merged: bool = false
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
class_name MPPathfindingResult
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var path: Array[MPNavigationNode] = []
|
||||||
|
var next_position: Vector2 = Vector2.ZERO
|
||||||
|
var is_next_target: bool = false
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
path = []
|
||||||
|
next_position = Vector2.ZERO
|
||||||
|
is_next_target = false
|
||||||
|
|
||||||
|
func is_identical_to(other: MPPathfindingResult) -> bool:
|
||||||
|
if path.size() != other.path.size():
|
||||||
|
return false
|
||||||
|
for i in range(path.size()):
|
||||||
|
if path[i] != other.path[i]:
|
||||||
|
return false
|
||||||
|
if next_position != other.next_position:
|
||||||
|
return false
|
||||||
|
return true
|
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,14 @@
|
||||||
[gd_scene load_steps=4 format=4 uid="uid://ddlksorhs8rls"]
|
[gd_scene load_steps=7 format=4 uid="uid://ddlksorhs8rls"]
|
||||||
|
|
||||||
[ext_resource type="TileSet" uid="uid://cm6wi7fjgfk56" path="res://scenes/mario-party/MarioPartyTiles.tres" id="1_3jghe"]
|
[ext_resource type="TileSet" uid="uid://cm6wi7fjgfk56" path="res://scenes/mario-party/MarioPartyTiles.tres" id="1_3jghe"]
|
||||||
[ext_resource type="Script" path="res://scenes/mario-party/MarioPartyManagement.gd" id="1_r0jft"]
|
[ext_resource type="Script" path="res://scenes/mario-party/MarioPartyManagement.gd" id="1_r0jft"]
|
||||||
[ext_resource type="Script" path="res://scenes/mario-party/MPNavigationGraph.gd" id="3_ww84c"]
|
[ext_resource type="Script" path="res://scenes/mario-party/MPNavigationGraph.gd" id="3_ww84c"]
|
||||||
|
[ext_resource type="Script" path="res://scenes/mario-party/PlayerController.gd" id="4_djnau"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c53ftc05so8rx" path="res://icon.svg" id="5_3opuj"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_cgpao"]
|
||||||
|
radius = 24.9999
|
||||||
|
height = 49.9999
|
||||||
|
|
||||||
[node name="MarioParty" type="Node2D"]
|
[node name="MarioParty" type="Node2D"]
|
||||||
script = ExtResource("1_r0jft")
|
script = ExtResource("1_r0jft")
|
||||||
|
@ -16,19 +22,43 @@ navigation_enabled = false
|
||||||
|
|
||||||
[node name="PathLayer" type="TileMapLayer" parent="."]
|
[node name="PathLayer" type="TileMapLayer" parent="."]
|
||||||
scale = Vector2(2, 2)
|
scale = Vector2(2, 2)
|
||||||
tile_map_data = PackedByteArray("AAAGAAYAAwAAAAEAAAAGAAcAAwAAAAEAAAAGAAgAAwAAAAEAAAAGAAkAAwAAAAEAAAAGAAoAAwAAAAEAAAAGAAsAAwAAAAEAAAAGAAwAAwAAAAEAAAAGAA0AAwAAAAEAAAAGAA4AAwAAAAEAAAAGAA8AAwAAAAEAAAAGABAAAwABAAIAAAAHAAgAAwACAAMAAAAHABAAAwACAAMAAAAIAAgAAwACAAMAAAAIABAAAwACAAMAAAAJAAkAAwAAAAEAAAAJAAoAAwAAAAEAAAAJAAsAAwAAAAEAAAAJAAwAAwAAAAEAAAAJAA0AAwAAAAEAAAAJAA4AAwAAAAEAAAAJAA8AAwAAAAEAAAAKAAgAAwACAAMAAAAKABAAAwACAAMAAAALAAgAAwACAAMAAAALABAAAwACAAMAAAAMAAgAAwACAAMAAAAMABAAAwACAAMAAAANAAgAAwACAAMAAAANABAAAwACAAMAAAAOAAgAAwACAAMAAAAOABAAAwACAAMAAAAPAAgAAwACAAMAAAAPABAAAwACAAMAAAAQAAgAAwACAAMAAAAQABAAAwACAAMAAAARAAgAAwACAAMAAAARAA4AAwABAAEAAAARAA8AAwAAAAEAAAARABAAAwACAAIAAAASAA4AAwACAAMAAAATAAgAAwACAAMAAAATAA4AAwACAAMAAAAUAAgAAwACAAMAAAAUAA4AAwACAAMAAAAVAAgAAwACAAMAAAAVAA4AAwACAAMAAAAWAAgAAwACAAMAAAAWAA4AAwACAAMAAAAXAAgAAwACAAMAAAAXAA4AAwACAAMAAAAYAA4AAwACAAMAAAAZAAgAAwACAAMAAAAZAA4AAwACAAMAAAAaAAgAAwACAAMAAAAaAA4AAwACAAMAAAAbAAgAAwACAAMAAAAbAA4AAwACAAMAAAAcAAgAAwACAAMAAAAcAA4AAwACAAMAAAAeAAYAAwAAAAEAAAAeAAcAAwAAAAEAAAAeAAkAAwAAAAEAAAAeAAoAAwAAAAEAAAAeAAsAAwAAAAEAAAAeAAwAAwAAAAEAAAAeAA0AAwAAAAEAAAAeAA4AAwACAAIAAAAdAAgAAwACAAMAAAAdAA4AAwACAAMAAAASAAgAAwACAAMAAAAGAAMAAwABAAEAAAAHAAMAAwACAAMAAAAIAAMAAwACAAMAAAAJAAMAAwACAAMAAAAKAAMAAwACAAMAAAALAAMAAwACAAMAAAAMAAMAAwACAAMAAAANAAMAAwACAAMAAAAOAAMAAwACAAMAAAAQAAMAAwACAAMAAAARAAMAAwACAAMAAAASAAMAAwACAAMAAAATAAMAAwACAAMAAAAUAAMAAwACAAMAAAAVAAMAAwACAAMAAAAWAAMAAwACAAMAAAAXAAMAAwACAAMAAAAYAAMAAwACAAMAAAAZAAMAAwACAAMAAAAaAAMAAwACAAMAAAAbAAMAAwACAAMAAAAcAAMAAwACAAMAAAAdAAMAAwACAAMAAAAeAAMAAwACAAEAAAAQAAUAAwACAAMAAAARAAUAAwACAAMAAAASAAUAAwACAAMAAAATAAUAAwACAAMAAAAUAAUAAwACAAMAAAAVAAUAAwACAAMAAAAWAAUAAwACAAMAAAAXAAUAAwACAAMAAAAPAAQAAwAAAAEAAAAYAAYAAwAAAAEAAAAYAAcAAwAAAAEAAAAYAAUAAwACAAEAAAAPAAUAAwABAAIAAAAGAAQAAwAAAAEAAAAGAAUAAwAAAAEAAAAeAAQAAwAAAAEAAAAeAAUAAwAAAAEAAAA=")
|
tile_map_data = PackedByteArray("AAAGAAYAAwAAAAEAAAAGAAcAAwAAAAEAAAAGAAgAAwAAAAEAAAAGAAkAAwAAAAEAAAAGAAoAAwAAAAEAAAAGAAsAAwAAAAEAAAAGAAwAAwAAAAEAAAAGAA0AAwAAAAEAAAAGAA4AAwAAAAEAAAAGAA8AAwAAAAEAAAAGABAAAwABAAIAAAAHAAgAAwACAAMAAAAHABAAAwACAAMAAAAIAAgAAwACAAMAAAAIABAAAwACAAMAAAAJAAkAAwAAAAEAAAAJAAoAAwAAAAEAAAAJAAsAAwAAAAEAAAAJAAwAAwAAAAEAAAAJAA0AAwAAAAEAAAAJAA4AAwAAAAEAAAAJAA8AAwAAAAEAAAAKAAgAAwACAAMAAAAKABAAAwACAAMAAAALAAgAAwACAAMAAAALABAAAwACAAMAAAAMAAgAAwACAAMAAAAMABAAAwACAAMAAAANAAgAAwACAAMAAAANABAAAwACAAMAAAAOAAgAAwACAAMAAAAOABAAAwACAAMAAAAPAAgAAwACAAMAAAAPABAAAwACAAMAAAAQAAgAAwACAAMAAAAQABAAAwACAAMAAAARAAgAAwACAAMAAAARAA4AAwAAAAEAAAARAA8AAwAAAAEAAAARABAAAwACAAIAAAASAA4AAwACAAMAAAATAAgAAwACAAMAAAATAA4AAwACAAMAAAAUAAgAAwACAAMAAAAUAA4AAwACAAMAAAAVAAgAAwACAAMAAAAVAA4AAwACAAMAAAAWAAgAAwACAAMAAAAWAA4AAwACAAMAAAAXAAgAAwACAAMAAAAXAA4AAwACAAMAAAAYAA4AAwACAAMAAAAZAAgAAwACAAMAAAAZAA4AAwACAAMAAAAaAAgAAwACAAMAAAAaAA4AAwACAAMAAAAbAAgAAwACAAMAAAAbAA4AAwACAAMAAAAcAAgAAwACAAMAAAAcAA4AAwACAAMAAAAeAAYAAwAAAAEAAAAeAAcAAwAAAAEAAAAeAAkAAwAAAAEAAAAeAAoAAwAAAAEAAAAeAAsAAwAAAAEAAAAeAAwAAwAAAAEAAAAeAA0AAwAAAAEAAAAeAA4AAwACAAIAAAAdAAgAAwACAAMAAAAdAA4AAwACAAMAAAASAAgAAwACAAMAAAAGAAMAAwABAAEAAAAHAAMAAwACAAMAAAAIAAMAAwACAAMAAAAJAAMAAwACAAMAAAAKAAMAAwACAAMAAAALAAMAAwACAAMAAAAMAAMAAwACAAMAAAANAAMAAwACAAMAAAAOAAMAAwACAAMAAAAQAAMAAwACAAMAAAARAAMAAwACAAMAAAASAAMAAwACAAMAAAATAAMAAwACAAMAAAAUAAMAAwACAAMAAAAVAAMAAwACAAMAAAAWAAMAAwACAAMAAAAXAAMAAwACAAMAAAAYAAMAAwACAAMAAAAZAAMAAwACAAMAAAAaAAMAAwACAAMAAAAbAAMAAwACAAMAAAAcAAMAAwACAAMAAAAdAAMAAwACAAMAAAAeAAMAAwACAAEAAAAQAAUAAwACAAMAAAARAAUAAwACAAMAAAASAAUAAwACAAMAAAATAAUAAwACAAMAAAAUAAUAAwACAAMAAAAVAAUAAwACAAMAAAAWAAUAAwACAAMAAAAXAAUAAwACAAMAAAAPAAQAAwAAAAEAAAAYAAYAAwAAAAEAAAAYAAcAAwAAAAEAAAAYAAUAAwACAAEAAAAPAAUAAwABAAIAAAAGAAQAAwAAAAEAAAAGAAUAAwAAAAEAAAAeAAQAAwAAAAEAAAAeAAUAAwAAAAEAAAAPAAkAAwAAAAEAAAAPAAoAAwAAAAEAAAANAAwAAwAAAAEAAAANAA0AAwAAAAEAAAANAA4AAwAAAAEAAAANAA8AAwAAAAEAAAAOAAsAAwACAAMAAAANAAsAAwABAAEAAAAPAAsAAwACAAIAAAAJAAgAAwACAAMAAAAYAAgAAwACAAMAAAAPAAMAAwACAAMAAAAJABAAAwACAAMAAAAeAAgAAwAAAAEAAAAVAAkAAwAAAAEAAAAVAAoAAwAAAAEAAAASAAsAAwACAAMAAAATAAsAAwACAAMAAAAUAAsAAwACAAMAAAAVAAsAAwACAAMAAAAWAAsAAwACAAMAAAAXAAsAAwACAAMAAAAYAAsAAwACAAMAAAAZAAsAAwACAAMAAAARAAwAAwAAAAEAAAARAA0AAwAAAAEAAAAaAAwAAwAAAAEAAAAaAA0AAwAAAAEAAAAaAAsAAwACAAEAAAARAAsAAwABAAEAAAA=")
|
||||||
tile_set = ExtResource("1_3jghe")
|
tile_set = ExtResource("1_3jghe")
|
||||||
|
|
||||||
[node name="DataLayer" type="TileMapLayer" parent="."]
|
[node name="DataLayer" type="TileMapLayer" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
scale = Vector2(2, 2)
|
scale = Vector2(2, 2)
|
||||||
tile_map_data = PackedByteArray("AAAGAAYAAQAAAAAAAAAGAAcAAQABAAAAAAAGAAgAAQAAAAEAAAAGAAkAAQABAAAAAAAGAAoAAQAAAAEAAAAGAAsAAQAAAAAAAAAGAAwAAQAAAAAAAAAGAA0AAQABAAAAAAAGAA4AAQAAAAEAAAAGAA8AAQABAAAAAAAGABAAAQAAAAEAAAARAA4AAQAAAAEAAAASAA4AAQAAAAAAAAATAA4AAQAEAAAAAAAUAA4AAQAAAAEAAAAWAA4AAQAEAAAAAAAXAA4AAQAAAAEAAAAZAA4AAQAEAAAAAAAcAA4AAQAAAAAAAAARAA8AAQADAAAAAAARABAAAQAAAAEAAAAHABAAAQAAAAAAAAAIABAAAQAEAAAAAAAJABAAAQAAAAEAAAAKABAAAQAAAAAAAAAMABAAAQAEAAAAAAANABAAAQAAAAEAAAAPABAAAQAAAAAAAAAQABAAAQAEAAAAAAAJAAgAAQAAAAEAAAAJAAkAAQAAAAAAAAAJAA8AAQABAAAAAAAHAAgAAQACAAAAAAAIAAgAAQAAAAAAAAAKAAgAAQACAAAAAAALAAgAAQAAAAAAAAAMAAgAAQAAAAEAAAANAAgAAQACAAAAAAAOAAgAAQAAAAAAAAAPAAgAAQAAAAEAAAAQAAgAAQACAAAAAAARAAgAAQAAAAAAAAASAAgAAQAAAAEAAAATAAgAAQACAAAAAAAUAAgAAQAAAAAAAAAVAAgAAQAAAAEAAAAWAAgAAQACAAAAAAAXAAgAAQAAAAAAAAAYAAgAAQAAAAEAAAAZAAgAAQACAAAAAAAaAAgAAQAAAAAAAAAbAAgAAQAAAAEAAAAcAAgAAQACAAAAAAAeAAcAAQAAAAAAAAAeAAgAAQAAAAEAAAAeAAkAAQADAAAAAAAeAAoAAQAAAAAAAAAeAAsAAQAAAAEAAAAeAAwAAQADAAAAAAAeAA0AAQAAAAAAAAAeAA4AAQAAAAEAAAAdAA4AAQAEAAAAAAAdAAgAAQAAAAAAAAAVAA4AAQAAAAAAAAAYAA4AAQAAAAAAAAAaAA4AAQAAAAEAAAAbAA4AAQAAAAAAAAALABAAAQAAAAAAAAAOABAAAQAAAAAAAAAJAAsAAQAAAAEAAAAJAAwAAQABAAAAAAAJAA0AAQAAAAEAAAAJAA4AAQAAAAAAAAAJAAoAAQABAAAAAAAGAAMAAQAAAAEAAAAHAAMAAQACAAAAAAAIAAMAAQAAAAAAAAAJAAMAAQAAAAEAAAAKAAMAAQACAAAAAAALAAMAAQAAAAAAAAAMAAMAAQAAAAEAAAANAAMAAQACAAAAAAAOAAMAAQAAAAAAAAAPAAMAAQAAAAEAAAAQAAMAAQACAAAAAAARAAMAAQAAAAAAAAASAAMAAQAAAAEAAAATAAMAAQACAAAAAAAUAAMAAQAAAAAAAAAVAAMAAQAAAAEAAAAWAAMAAQACAAAAAAAXAAMAAQAAAAAAAAAYAAMAAQAAAAEAAAAZAAMAAQACAAAAAAAaAAMAAQAAAAAAAAAbAAMAAQAAAAEAAAAcAAMAAQACAAAAAAAdAAMAAQAAAAAAAAAeAAMAAQAAAAEAAAAGAAQAAQABAAAAAAAGAAUAAQAAAAEAAAAeAAQAAQADAAAAAAAeAAUAAQAAAAEAAAAeAAYAAQADAAAAAAAPAAQAAQADAAAAAAAPAAUAAQAAAAEAAAAQAAUAAQACAAAAAAARAAUAAQAAAAAAAAASAAUAAQAAAAAAAAATAAUAAQAAAAAAAAAUAAUAAQAAAAAAAAAVAAUAAQAAAAAAAAAWAAUAAQAAAAAAAAAXAAUAAQAAAAAAAAAYAAUAAQAAAAEAAAAYAAYAAQADAAAAAAAYAAcAAQAAAAAAAAA=")
|
tile_map_data = PackedByteArray("AAAGAAYAAQAAAAAAAAAGAAcAAQABAAAAAAAGAAgAAQAAAAEAAAAGAAkAAQABAAAAAAAGAAoAAQAAAAEAAAAGAAsAAQAAAAAAAAAGAAwAAQAAAAAAAAAGAA0AAQABAAAAAAAGAA4AAQAAAAEAAAAGAA8AAQABAAAAAAAGABAAAQAAAAEAAAARAA4AAQAAAAEAAAASAA4AAQAAAAAAAAATAA4AAQAEAAAAAAAUAA4AAQAAAAEAAAAWAA4AAQAEAAAAAAAXAA4AAQAAAAEAAAAZAA4AAQAEAAAAAAAcAA4AAQAAAAAAAAARAA8AAQADAAAAAAARABAAAQAAAAEAAAAHABAAAQAAAAAAAAAIABAAAQAEAAAAAAAJABAAAQAAAAEAAAAKABAAAQAAAAAAAAAMABAAAQAEAAAAAAANABAAAQAAAAEAAAAPABAAAQAAAAAAAAAQABAAAQAEAAAAAAAJAAgAAQAAAAEAAAAJAAkAAQAAAAAAAAAJAA8AAQABAAAAAAAHAAgAAQACAAAAAAAIAAgAAQAAAAAAAAAKAAgAAQACAAAAAAALAAgAAQAAAAAAAAAMAAgAAQAAAAEAAAANAAgAAQACAAAAAAAOAAgAAQAAAAAAAAAPAAgAAQAAAAEAAAAQAAgAAQACAAAAAAARAAgAAQAAAAAAAAASAAgAAQAAAAEAAAATAAgAAQACAAAAAAAUAAgAAQAAAAAAAAAVAAgAAQAAAAEAAAAWAAgAAQACAAAAAAAXAAgAAQAAAAAAAAAYAAgAAQAAAAEAAAAZAAgAAQACAAAAAAAaAAgAAQAAAAAAAAAbAAgAAQAAAAEAAAAcAAgAAQACAAAAAAAeAAcAAQAAAAAAAAAeAAgAAQAAAAEAAAAeAAkAAQADAAAAAAAeAAoAAQAAAAAAAAAeAAsAAQAAAAEAAAAeAAwAAQADAAAAAAAeAA0AAQAAAAAAAAAeAA4AAQAAAAEAAAAdAA4AAQAEAAAAAAAdAAgAAQAAAAAAAAAVAA4AAQAAAAAAAAAYAA4AAQAAAAAAAAAaAA4AAQAAAAEAAAAbAA4AAQAAAAAAAAALABAAAQAAAAAAAAAOABAAAQAAAAAAAAAJAAsAAQAAAAEAAAAJAAwAAQABAAAAAAAJAA0AAQAAAAEAAAAJAA4AAQAAAAAAAAAJAAoAAQABAAAAAAAGAAMAAQAAAAEAAAAHAAMAAQACAAAAAAAIAAMAAQAAAAAAAAAJAAMAAQAAAAEAAAAKAAMAAQACAAAAAAALAAMAAQAAAAAAAAAMAAMAAQAAAAEAAAANAAMAAQACAAAAAAAOAAMAAQAAAAAAAAAPAAMAAQAAAAEAAAAQAAMAAQACAAAAAAARAAMAAQAAAAAAAAASAAMAAQAAAAEAAAATAAMAAQACAAAAAAAUAAMAAQAAAAAAAAAVAAMAAQAAAAEAAAAWAAMAAQACAAAAAAAXAAMAAQAAAAAAAAAYAAMAAQAAAAEAAAAZAAMAAQACAAAAAAAaAAMAAQAAAAAAAAAbAAMAAQAAAAEAAAAcAAMAAQACAAAAAAAdAAMAAQAAAAAAAAAeAAMAAQAAAAEAAAAGAAQAAQABAAAAAAAGAAUAAQAAAAEAAAAeAAQAAQADAAAAAAAeAAUAAQAAAAEAAAAeAAYAAQADAAAAAAAPAAQAAQADAAAAAAAPAAUAAQAAAAEAAAAQAAUAAQACAAAAAAARAAUAAQAAAAAAAAASAAUAAQAAAAAAAAATAAUAAQAAAAAAAAAUAAUAAQAAAAAAAAAVAAUAAQAAAAAAAAAWAAUAAQAAAAAAAAAXAAUAAQAAAAAAAAAYAAUAAQAAAAEAAAAYAAYAAQADAAAAAAAYAAcAAQAAAAAAAAAaAAsAAQAAAAEAAAAaAAwAAQAAAAAAAAAaAA0AAQABAAAAAAARAAsAAQAAAAEAAAASAAsAAQAAAAAAAAATAAsAAQAAAAAAAAAUAAsAAQAEAAAAAAAVAAsAAQAAAAEAAAAWAAsAAQAAAAAAAAAXAAsAAQAAAAAAAAAYAAsAAQAAAAAAAAAZAAsAAQAEAAAAAAARAAwAAQADAAAAAAARAA0AAQAAAAAAAAAVAAkAAQABAAAAAAAVAAoAAQAAAAAAAAAPAAkAAQADAAAAAAAPAAoAAQAAAAAAAAAPAAsAAQAAAAEAAAANAAsAAQAAAAEAAAAOAAsAAQAEAAAAAAANAAwAAQADAAAAAAANAA0AAQAAAAAAAAANAA4AAQAAAAAAAAANAA8AAQAAAAAAAAA=")
|
||||||
tile_set = ExtResource("1_3jghe")
|
tile_set = ExtResource("1_3jghe")
|
||||||
|
|
||||||
[node name="TestDataLayer" type="TileMapLayer" parent="."]
|
[node name="TestDataLayer" type="TileMapLayer" parent="."]
|
||||||
|
visible = false
|
||||||
scale = Vector2(2, 2)
|
scale = Vector2(2, 2)
|
||||||
tile_map_data = PackedByteArray("AAAKABAAAQAEAAAAAAALABAAAQAAAAAAAAAMABAAAQAAAAAAAAANABAAAQAAAAAAAAAOABAAAQAAAAAAAAAPABAAAQAAAAAAAAAQABAAAQAEAAAAAAARABAAAQAAAAEAAAAJABAAAQAAAAEAAAANAA4AAQABAAAAAAANAA8AAQAAAAAAAAANAA0AAQAAAAEAAAAOAA0AAQACAAAAAAAPAA0AAQAAAAAAAAAQAA0AAQAAAAEAAAA=")
|
tile_map_data = PackedByteArray("AAAKABAAAQAEAAAAAAALABAAAQAAAAAAAAAMABAAAQAAAAAAAAANABAAAQAAAAAAAAAOABAAAQAAAAAAAAAPABAAAQAAAAAAAAAQABAAAQAEAAAAAAARABAAAQAAAAEAAAAJABAAAQAAAAEAAAANAA4AAQABAAAAAAANAA8AAQAAAAAAAAANAA0AAQAAAAEAAAAOAA0AAQACAAAAAAAPAA0AAQAAAAAAAAAQAA0AAQAAAAEAAAA=")
|
||||||
tile_set = ExtResource("1_3jghe")
|
tile_set = ExtResource("1_3jghe")
|
||||||
|
|
||||||
[node name="NavigationGraph" type="Node2D" parent="."]
|
[node name="NavigationGraph" type="Node2D" parent="."]
|
||||||
script = ExtResource("3_ww84c")
|
script = ExtResource("3_ww84c")
|
||||||
|
|
||||||
|
[node name="Player" type="CharacterBody2D" parent="."]
|
||||||
|
position = Vector2(-75, 73)
|
||||||
|
scale = Vector2(0.640001, 0.640001)
|
||||||
|
collision_mask = 3
|
||||||
|
script = ExtResource("4_djnau")
|
||||||
|
metadata/_edit_group_ = true
|
||||||
|
|
||||||
|
[node name="Icon" type="Sprite2D" parent="Player"]
|
||||||
|
scale = Vector2(0.4, 0.4)
|
||||||
|
texture = ExtResource("5_3opuj")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"]
|
||||||
|
shape = SubResource("CapsuleShape2D_cgpao")
|
||||||
|
|
||||||
|
[node name="ResultLabel" type="Label" parent="."]
|
||||||
|
offset_left = 442.0
|
||||||
|
offset_top = 574.0
|
||||||
|
offset_right = 701.0
|
||||||
|
offset_bottom = 646.0
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 51
|
||||||
|
text = "Rolled a 2"
|
||||||
|
|
|
@ -2,20 +2,90 @@ extends Node2D
|
||||||
|
|
||||||
@onready var ground_layer: TileMapLayer = $GroundLayer
|
@onready var ground_layer: TileMapLayer = $GroundLayer
|
||||||
@onready var path_layer: TileMapLayer = $PathLayer
|
@onready var path_layer: TileMapLayer = $PathLayer
|
||||||
@onready var data_layer: TileMapLayer = $TestDataLayer
|
@onready var data_layer: TileMapLayer = $DataLayer
|
||||||
@onready var navigation_graph: MPNavigationGraph = $NavigationGraph
|
@onready var navigation_graph: MPNavigationGraph = $NavigationGraph
|
||||||
const DIRECTIONS: Dictionary = {
|
#
|
||||||
"up": Vector2(0, -1),
|
@onready var player: CharacterBody2D = $Player
|
||||||
"down": Vector2(0, 1),
|
|
||||||
"left": Vector2(-1, 0),
|
var last_dice_result: int = 0
|
||||||
"right": Vector2(1, 0)
|
#
|
||||||
}
|
var global_transform_size: Vector2 = Vector2(0, 0)
|
||||||
var node_positions: Dictionary = {}
|
#
|
||||||
|
const DIRECTIONS: Dictionary = {
|
||||||
|
"up": Vector2(0, -1),
|
||||||
|
"down": Vector2(0, 1),
|
||||||
|
"left": Vector2(-1, 0),
|
||||||
|
"right": Vector2(1, 0)
|
||||||
|
}
|
||||||
|
var node_positions: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
global_transform_size = Vector2(16, 16) * data_layer.transform.get_scale()
|
||||||
|
|
||||||
build_graph()
|
build_graph()
|
||||||
|
|
||||||
|
# place the player at the first node
|
||||||
|
var current_player_position: Vector2 = node_positions.keys()[0]
|
||||||
|
player.global_position = local_to_world(current_player_position)
|
||||||
|
|
||||||
|
# bring player to top
|
||||||
|
player.z_index = 1
|
||||||
|
|
||||||
|
roll_dice()
|
||||||
|
|
||||||
|
player.finished_movement.connect(roll_dice)
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
if Input.is_action_just_pressed("draw_toggle_nodes") or Input.is_action_just_pressed("draw_toggle_edges"):
|
||||||
|
navigation_graph.draw_nodes = !navigation_graph.draw_nodes
|
||||||
|
navigation_graph.draw_edges = !navigation_graph.draw_edges
|
||||||
|
navigation_graph.queue_redraw()
|
||||||
|
|
||||||
|
var dice_controls_min: int = 2
|
||||||
|
var dice_controls_max: int = 8
|
||||||
|
|
||||||
|
|
||||||
|
func roll_dice() -> void:
|
||||||
|
last_dice_result = randi() % (dice_controls_max - dice_controls_min + 1) + dice_controls_min
|
||||||
|
print("Dice result: ", last_dice_result)
|
||||||
|
$ResultLabel.text = "Rolled a " + str(last_dice_result)
|
||||||
|
|
||||||
|
# Find possible movement options
|
||||||
|
var movement_options: Array[MPNavigationNode] = find_nodes_with_distance(world_to_local(player.global_position), last_dice_result)
|
||||||
|
# visualize by spawning an indicator at each possible node, with a click event to move the player
|
||||||
|
for node in movement_options:
|
||||||
|
# res://scenes/mario-party/TileIndicator.tscn
|
||||||
|
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
||||||
|
indicator_scene.scale = Vector2(2, 2)
|
||||||
|
var area_node: Area2D = indicator_scene.get_node("area")
|
||||||
|
area_node.set_data(node.position, "player_movement")
|
||||||
|
area_node.indicator_clicked.connect(_on_indicator_clicked)
|
||||||
|
indicator_scene.global_position = local_to_world(node.get_position())
|
||||||
|
add_child(indicator_scene)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_indicator_clicked(pos: Vector2, type: String) -> void:
|
||||||
|
print("Indicator clicked: ", type, " ", pos)
|
||||||
|
if type == "player_movement":
|
||||||
|
player.move_to(pos)
|
||||||
|
# delete all indicators
|
||||||
|
for child in get_children():
|
||||||
|
if child is Node2D:
|
||||||
|
var area_node: Area2D = child.get_node("area")
|
||||||
|
if area_node != null:
|
||||||
|
if area_node.get_type() == "player_movement":
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
func local_to_world(location: Vector2) -> Vector2:
|
||||||
|
return location * global_transform_size + global_transform_size / 2
|
||||||
|
|
||||||
|
|
||||||
|
func world_to_local(location: Vector2) -> Vector2:
|
||||||
|
return (location - global_transform_size / 2) / global_transform_size
|
||||||
|
|
||||||
|
|
||||||
func build_graph() -> void:
|
func build_graph() -> void:
|
||||||
print("Identifying nodes")
|
print("Identifying nodes")
|
||||||
|
@ -24,9 +94,13 @@ func build_graph() -> void:
|
||||||
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||||||
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
||||||
if is_tile:
|
if is_tile:
|
||||||
var node: NavigationNode = navigation_graph.add_node(position.x, position.y)
|
var node: MPNavigationNode = navigation_graph.add_node(position.x, position.y)
|
||||||
node_positions[position] = node
|
node_positions[position] = node
|
||||||
print(Vector2(position))
|
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
||||||
|
var area_node: Area2D = indicator_scene.get_node("area")
|
||||||
|
area_node.set_display_type("node")
|
||||||
|
indicator_scene.global_position = local_to_world(position)
|
||||||
|
add_child(indicator_scene)
|
||||||
|
|
||||||
# Step 2: Connect nodes using flood-fill based on walkable tiles
|
# Step 2: Connect nodes using flood-fill based on walkable tiles
|
||||||
print("Connecting nodes")
|
print("Connecting nodes")
|
||||||
|
@ -38,19 +112,19 @@ func connect_node(start_position: Vector2) -> void:
|
||||||
var start_node = node_positions.get(Vector2i(start_position))
|
var start_node = node_positions.get(Vector2i(start_position))
|
||||||
var visited: Dictionary = {}
|
var visited: Dictionary = {}
|
||||||
visited[start_position] = true
|
visited[start_position] = true
|
||||||
print("Connecting node at ", start_position)
|
# print("Connecting node at ", start_position)
|
||||||
|
|
||||||
# For each direction, perform flood-fill
|
# For each direction, perform flood-fill
|
||||||
for dir_name in DIRECTIONS.keys():
|
for dir_name in DIRECTIONS.keys():
|
||||||
var direction = DIRECTIONS[dir_name]
|
var direction = DIRECTIONS[dir_name]
|
||||||
var next_position = start_position + direction
|
var next_position = start_position + direction
|
||||||
|
|
||||||
print("Checking direction ", dir_name, " from ", start_position, " to ", next_position)
|
# print("Checking direction ", dir_name, " from ", start_position, " to ", next_position)
|
||||||
|
|
||||||
# Ensure the first tile respects the direction
|
# Ensure the first tile respects the direction
|
||||||
if not is_valid_direction(next_position, dir_name):
|
if not is_valid_direction(next_position, dir_name):
|
||||||
continue
|
continue
|
||||||
print("Flood-fill in direction ", dir_name, " from ", next_position)
|
# print("Flood-fill in direction ", dir_name, " from ", next_position)
|
||||||
|
|
||||||
# Perform flood-fill from the valid tile
|
# Perform flood-fill from the valid tile
|
||||||
var connected_nodes: Array = flood_fill(next_position, visited)
|
var connected_nodes: Array = flood_fill(next_position, visited)
|
||||||
|
@ -70,7 +144,7 @@ func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
||||||
|
|
||||||
while stack.size() > 0:
|
while stack.size() > 0:
|
||||||
var current_position = stack.pop_back()
|
var current_position = stack.pop_back()
|
||||||
print(" - Visiting ", current_position)
|
# print(" - Visiting ", current_position)
|
||||||
|
|
||||||
# Skip if already visited
|
# Skip if already visited
|
||||||
if visited.has(current_position):
|
if visited.has(current_position):
|
||||||
|
@ -90,10 +164,8 @@ func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
||||||
|
|
||||||
# If this position is a node, add it to the result
|
# If this position is a node, add it to the result
|
||||||
if is_tile:
|
if is_tile:
|
||||||
print(" - Found node tile at ", current_position)
|
# print(" - Found node tile at ", current_position)
|
||||||
connected_nodes.append(current_position)
|
connected_nodes.append(current_position)
|
||||||
else:
|
|
||||||
print(" - Found walkable tile at ", current_position)
|
|
||||||
|
|
||||||
# Add neighboring tiles to the stack if they respect the direction
|
# Add neighboring tiles to the stack if they respect the direction
|
||||||
for dir_name in DIRECTIONS.keys():
|
for dir_name in DIRECTIONS.keys():
|
||||||
|
@ -110,16 +182,54 @@ func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
||||||
func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
||||||
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||||||
if tile_data == null:
|
if tile_data == null:
|
||||||
print(" L Cancelled due to null tile data")
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
||||||
var walk_dir: String = tile_data.get_custom_data("walk_dir")
|
var walk_dir: String = tile_data.get_custom_data("walk_dir")
|
||||||
# If walk_dir is empty, treat it as "any" direction
|
|
||||||
if walk_dir == "":
|
if walk_dir == "":
|
||||||
walk_dir = "any"
|
walk_dir = "any"
|
||||||
|
|
||||||
print(" L ", position, " ", is_walkable, " ", walk_dir, " ", required_dir)
|
# print(" L ", position, " ", is_walkable, " ", walk_dir, " ", required_dir)
|
||||||
|
|
||||||
# Check if the tile is walkable and allows movement in the required direction
|
# Check if the tile is walkable and allows movement in the required direction
|
||||||
return is_walkable and (walk_dir == required_dir or walk_dir == "any")
|
return is_walkable and (walk_dir == required_dir or walk_dir == "any")
|
||||||
|
|
||||||
|
|
||||||
|
# the first function that evaluates the finished graph
|
||||||
|
# it is given a starting position and a distance in integers, which represent the amount of nodes to travel to reach the target node(s)
|
||||||
|
# it will find any nodes that are the given distance away from the starting node and return them in an array
|
||||||
|
# distance here is the amount of nodes to travel, not the actual distance in pixels
|
||||||
|
# must use the MPNavigationNode class and the navigation_graph.get_connections(node: MPNavigationNode) function
|
||||||
|
func find_nodes_with_distance(start_position: Vector2, distance: int) -> Array[MPNavigationNode]:
|
||||||
|
var start_node = node_positions.get(Vector2i(start_position))
|
||||||
|
if start_node == null:
|
||||||
|
print("Error: No node found at starting position ", start_position)
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Initialize BFS
|
||||||
|
var queue: Array = [[start_node, 0]] # Each element is [node, current_distance]
|
||||||
|
var visited: Dictionary = {start_node: true}
|
||||||
|
var result_nodes: Array[MPNavigationNode] = []
|
||||||
|
|
||||||
|
while queue.size() > 0:
|
||||||
|
var current = queue.pop_front()
|
||||||
|
var current_node: MPNavigationNode = current[0]
|
||||||
|
var current_distance: int = current[1]
|
||||||
|
|
||||||
|
# If the target distance is reached, add the node to the result
|
||||||
|
if current_distance == distance:
|
||||||
|
result_nodes.append(current_node)
|
||||||
|
continue # Do not explore further from this node
|
||||||
|
|
||||||
|
# If the current distance exceeds the target, stop exploring
|
||||||
|
if current_distance > distance:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Explore neighbors of the current node
|
||||||
|
var neighbors: Array[MPNavigationNode] = navigation_graph.get_connections(current_node)
|
||||||
|
for neighbor in neighbors:
|
||||||
|
if not visited.has(neighbor):
|
||||||
|
visited[neighbor] = true
|
||||||
|
queue.append([neighbor, current_distance + 1])
|
||||||
|
|
||||||
|
return result_nodes
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
extends CharacterBody2D
|
||||||
|
|
||||||
|
signal finished_movement()
|
||||||
|
|
||||||
|
const MAX_SPEED: float = 300.0
|
||||||
|
const ACCELERATION: int = 4800
|
||||||
|
const SNAP_DISTANCE: float = 2.0
|
||||||
|
var frozen: bool = false
|
||||||
|
var target: Vector2 = Vector2(-1, -1)
|
||||||
|
var path: Array[MPNavigationNode] = []
|
||||||
|
var current_path_index: int = 0
|
||||||
|
|
||||||
|
@onready var navigation_graph = $"../NavigationGraph"
|
||||||
|
|
||||||
|
|
||||||
|
func move_to(target: Vector2) -> void:
|
||||||
|
var using_position: Vector2 = (global_position - Vector2(16, 16)) / 32
|
||||||
|
var pathfinding_result: MPPathfindingResult = navigation_graph.determine_next_position(using_position, target)
|
||||||
|
navigation_graph.draw_pathfinding_result(pathfinding_result)
|
||||||
|
path = pathfinding_result.path
|
||||||
|
current_path_index = 0
|
||||||
|
print("Moving to ", target)
|
||||||
|
for node in path:
|
||||||
|
print(" - ", node.position)
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
if Input.is_action_just_pressed("ui_select"):
|
||||||
|
frozen = !frozen
|
||||||
|
|
||||||
|
if frozen or path.size() == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
var movement: Vector2 = Vector2()
|
||||||
|
|
||||||
|
if current_path_index < path.size():
|
||||||
|
var next_position: Vector2 = path[current_path_index].global_position * 32 + Vector2(16, 16)
|
||||||
|
|
||||||
|
var distance_to_next: float = global_position.distance_to(next_position)
|
||||||
|
if distance_to_next > SNAP_DISTANCE:
|
||||||
|
movement = (next_position - global_position).normalized() * ACCELERATION
|
||||||
|
else:
|
||||||
|
# snap to the next node if close enough
|
||||||
|
global_position = next_position
|
||||||
|
current_path_index += 1
|
||||||
|
|
||||||
|
# if the end of the path is reached, stop movement
|
||||||
|
if current_path_index >= path.size():
|
||||||
|
path.clear()
|
||||||
|
target = Vector2(-1, -1)
|
||||||
|
finished_movement.emit()
|
||||||
|
navigation_graph.clear_pathfinding_result()
|
||||||
|
|
||||||
|
# Immediate deceleration for tight control
|
||||||
|
velocity = movement
|
||||||
|
if velocity.length() > MAX_SPEED:
|
||||||
|
velocity = velocity.normalized() * MAX_SPEED
|
||||||
|
|
||||||
|
move_and_slide()
|
|
@ -0,0 +1,19 @@
|
||||||
|
[gd_scene load_steps=4 format=3 uid="uid://b4btlpsdkstnw"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dpg3am0sik4s3" path="res://scenes/mario-party/img/tile_highlight.png" id="1_c8wfh"]
|
||||||
|
[ext_resource type="Script" path="res://scenes/mario-party/tile_indicator.gd" id="1_eusgv"]
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_ywle7"]
|
||||||
|
size = Vector2(16, 16)
|
||||||
|
|
||||||
|
[node name="TileIndicator" type="Node2D"]
|
||||||
|
|
||||||
|
[node name="area" type="Area2D" parent="."]
|
||||||
|
script = ExtResource("1_eusgv")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="area"]
|
||||||
|
texture_filter = 1
|
||||||
|
texture = ExtResource("1_c8wfh")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="area"]
|
||||||
|
shape = SubResource("RectangleShape2D_ywle7")
|
Binary file not shown.
After Width: | Height: | Size: 642 B |
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dpg3am0sik4s3"
|
||||||
|
path="res://.godot/imported/tile_highlight.png-ccc02681cbe2e24aaade05280cf6e075.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://scenes/mario-party/img/tile_highlight.png"
|
||||||
|
dest_files=["res://.godot/imported/tile_highlight.png-ccc02681cbe2e24aaade05280cf6e075.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
Binary file not shown.
After Width: | Height: | Size: 551 B |
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bn6ccsbh6nga0"
|
||||||
|
path="res://.godot/imported/tile_node.png-d5169d56110746a53897400abc7e745e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://scenes/mario-party/img/tile_node.png"
|
||||||
|
dest_files=["res://.godot/imported/tile_node.png-d5169d56110746a53897400abc7e745e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
|
@ -0,0 +1,26 @@
|
||||||
|
extends Area2D
|
||||||
|
|
||||||
|
signal indicator_clicked()
|
||||||
|
var pos: Vector2 = Vector2()
|
||||||
|
var type: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void:
|
||||||
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
|
indicator_clicked.emit(pos, type)
|
||||||
|
|
||||||
|
|
||||||
|
func set_data(p: Vector2, t: String) -> void:
|
||||||
|
pos = p
|
||||||
|
type = t
|
||||||
|
|
||||||
|
func get_type() -> String:
|
||||||
|
return type
|
||||||
|
|
||||||
|
func set_display_type(t: String) -> void:
|
||||||
|
if t == "node":
|
||||||
|
$Sprite2D.texture = preload("res://scenes/mario-party/img/tile_node.png") as Texture
|
||||||
|
elif t == "player_movement":
|
||||||
|
$Sprite2D.texture = preload("res://scenes/mario-party/img/tile_highlight.png") as Texture
|
||||||
|
else:
|
||||||
|
push_error("Invalid type: %s" % t)
|
Loading…
Reference in New Issue