From e441a121ff47a7b0f918abc2d5ccc38063ec52c8 Mon Sep 17 00:00:00 2001 From: Lexi Quinn Date: Sun, 8 Feb 2026 02:10:23 +1100 Subject: [PATCH] fixed the path rebuilding lag --- PCs/PathEditTool/path_edit_tool.gd | 10 +- PCs/hero.tscn | 2 - Scenes/FlowField/flow_field.gd | 213 +++++--------------------- Scenes/FlowField/flow_field_data.gd | 6 +- Scenes/FlowField/flow_field_editor.gd | 62 +++++++- Scenes/FlowField/flow_field_tool.gd | 184 ++++++++++++---------- Scenes/FlowField/flow_field_tool.tscn | 7 +- Scenes/FlowField/flow_node.gd | 26 ++-- Scenes/FlowField/flow_node_data.gd | 28 ++-- Scenes/TowerBase/tower_base.gd | 2 +- Scripts/EnemyAI/leaping_controller.gd | 6 +- Scripts/EnemyAI/pathing_controller.gd | 18 +-- Scripts/enemy_spawner.gd | 29 ++-- Scripts/game.gd | 9 +- Scripts/level.gd | 76 ++------- Scripts/wave_manager.gd | 3 +- Sprite-0001.png | Bin 0 -> 549 bytes Sprite-0001.png.import | 40 +++++ Sprite-0002.png | Bin 0 -> 272 bytes Sprite-0002.png.import | 41 +++++ path_material.tres | 12 ++ path_vfx.gd | 163 ++++++++++++++++++++ path_vfx.gd.uid | 1 + path_vfx.tscn | 35 +++++ red.png | Bin 0 -> 224 bytes red.png.import | 41 +++++ 26 files changed, 629 insertions(+), 385 deletions(-) create mode 100644 Sprite-0001.png create mode 100644 Sprite-0001.png.import create mode 100644 Sprite-0002.png create mode 100644 Sprite-0002.png.import create mode 100644 path_material.tres create mode 100644 path_vfx.gd create mode 100644 path_vfx.gd.uid create mode 100644 path_vfx.tscn create mode 100644 red.png create mode 100644 red.png.import diff --git a/PCs/PathEditTool/path_edit_tool.gd b/PCs/PathEditTool/path_edit_tool.gd index ff0eb5c..c8e1a8f 100644 --- a/PCs/PathEditTool/path_edit_tool.gd +++ b/PCs/PathEditTool/path_edit_tool.gd @@ -8,12 +8,12 @@ extends Node3D var enabled: bool = false var level: Level -var point: FlowNode +var point: FlowNodeData var obstacle_last_point: int = -1 var valid_point: bool = false # a point is valid if the path would still be traversable overall if this point was made untraversable var ray_collider: Object var ray_point: Vector3 -var last_point: FlowNode +var last_point: FlowNodeData var last_tower_base: TowerBase var interact_key_held: bool = false var interacted_once: bool = false @@ -78,7 +78,7 @@ func _process(delta: float) -> void: if !valid_point: wall_preview.set_visible(false) if point: - wall_preview.global_position = point.global_position + wall_preview.global_position = point.position wall_preview.global_rotation = Vector3.ZERO @@ -92,8 +92,8 @@ func reset() -> void: func process_looking_at_level() -> void: - point = level.flow_field.get_closest_buildable_point(ray_point) - if level.walls.has(point) or !point.buildable or hero.currency < Data.wall_cost: + point = level.flow_field.get_closest_point(ray_point, false, true) + if level.walls.has(point) or hero.currency < Data.wall_cost: wall_preview.set_visible(false) valid_point = false clear_previous_point() diff --git a/PCs/hero.tscn b/PCs/hero.tscn index bebdca8..da9a76c 100644 --- a/PCs/hero.tscn +++ b/PCs/hero.tscn @@ -23,7 +23,6 @@ [ext_resource type="Texture2D" uid="uid://chhmkmlfrobhu" path="res://Assets/Textures/bubble.png" id="15_q3yot"] [ext_resource type="Texture2D" uid="uid://cqnapc8cscl7i" path="res://Assets/Textures/border.png" id="16_x1xjr"] [ext_resource type="PackedScene" uid="uid://chnj376d3lcjd" path="res://UI/pickup_notification.tscn" id="17_oyeww"] -[ext_resource type="PackedScene" uid="uid://d17c77pqsi8oy" path="res://UI/EnemyCard/enemy_card_ui.tscn" id="18_dfkac"] [ext_resource type="Script" uid="uid://b5wle8f6rv3e7" path="res://PCs/player_movement.gd" id="20_cfhw8"] [ext_resource type="Texture2D" uid="uid://deelc254ct7ae" path="res://Assets/Textures/place_icon.png" id="22_o55s8"] [ext_resource type="AudioStream" uid="uid://csu2hce4bfoki" path="res://Audio/cardPlace1.ogg" id="24_8ch4w"] @@ -433,7 +432,6 @@ pickup_notif_scene = ExtResource("17_oyeww") wave_start_label = NodePath("StartWaveLabel") place_text = NodePath("VBoxContainer2/HBoxContainer/RichTextLabel") swap_text = NodePath("VBoxContainer2/HBoxContainer2/RichTextLabel") -enemy_card_scene = ExtResource("18_dfkac") energy_label = NodePath("Currencies/HBoxContainer/EnergyLabel") blank_cassette_label = NodePath("Currencies/BlankCassetteLabel") feature_preview = NodePath("FeaturePreview") diff --git a/Scenes/FlowField/flow_field.gd b/Scenes/FlowField/flow_field.gd index 255cbf5..4f121e1 100644 --- a/Scenes/FlowField/flow_field.gd +++ b/Scenes/FlowField/flow_field.gd @@ -3,101 +3,38 @@ extends Node3D signal path_updated() -@export var data_file: FlowFieldData @export var start_points: Array[Node3D] @export var goal_points: Array[Node3D] -@export var nodes_visible: bool = false -var flow_node_scene: PackedScene = preload("res://Scenes/FlowField/flow_node.tscn") -var nodes: Array[FlowNode] = [] -var start_nodes: Array[FlowNode] = [] -var goal_nodes: Array[FlowNode] = [] -var created_nodes: int = 0 +var start_nodes: Array[FlowNodeData] +var goal_nodes: Array[FlowNodeData] +var magic_node: FlowNodeData +var data: FlowFieldData : + get(): + return data + set(value): + data = value + for node: FlowNodeData in data.nodes: + if node.type == FlowNodeData.NodeType.START: + start_nodes.append(node) + if node.type == FlowNodeData.NodeType.GOAL: + goal_nodes.append(node) -func _ready() -> void: - if !nodes_visible: - for node: FlowNode in nodes: - node.visible = false - - -func load_from_data(data: FlowFieldData = data_file) -> void: - data_file = data - for node: FlowNode in nodes: - delete_node(node) - nodes = [] - start_nodes = [] - goal_nodes = [] - var dict: Dictionary[FlowNodeData, FlowNode] = {} - created_nodes = 0 - for node_data: FlowNodeData in data_file.nodes: - var new_flow_node: FlowNode = create_node(node_data.position) - new_flow_node.node_id = node_data.node_id - new_flow_node.grid_id = node_data.grid_id - new_flow_node.grid_x = node_data.grid_x - new_flow_node.grid_y = node_data.grid_y - new_flow_node.buildable = node_data.buildable - dict[node_data] = new_flow_node - if node_data.type == FlowNodeData.NodeType.START: - start_nodes.append(new_flow_node) - elif node_data.type == FlowNodeData.NodeType.GOAL: - goal_nodes.append(new_flow_node) - for node_data: FlowNodeData in dict.keys(): - for neighbor: FlowNodeData in node_data.connected_nodes: - dict[node_data].add_connection(dict[neighbor]) - - -@warning_ignore("unused_parameter") -func _process(delta: float) -> void: - if !nodes_visible: - return - for node: FlowNode in nodes: - if node.traversable and node.buildable: - node.set_color(Color.WEB_GRAY) - elif node.traversable and !node.buildable: - node.set_color(Color.CORAL) - else: - node.set_color(Color.BLACK) - if goal_nodes.has(node): - node.set_color(Color.BLUE) - if start_nodes.has(node): - node.set_color(Color.PINK) - if magic_node: - magic_node.set_color(Color.DEEP_PINK) - - -func get_closest_traversable_point(pos: Vector3) -> FlowNode: - var closest_point: FlowNode = null +func get_closest_point(pos: Vector3, traversable_required: bool = false, buildable_required: bool = false) -> FlowNodeData: + var closest_point: FlowNodeData = null var closest_dist: float = 100000.0 - for node: FlowNode in nodes: - if node.traversable and node.global_position.distance_to(pos) < closest_dist: - closest_dist = node.global_position.distance_to(pos) - closest_point = node - return closest_point - - -func get_closest_point_point(pos: Vector3) -> FlowNode: - var closest_point: FlowNode = null - var closest_dist: float = 100000.0 - for node: FlowNode in nodes: - if node.global_position.distance_to(pos) < closest_dist: - closest_dist = node.global_position.distance_to(pos) - closest_point = node - return closest_point - - -func get_closest_buildable_point(pos: Vector3) -> FlowNode: - var closest_point: FlowNode = null - var closest_dist: float = 100000.0 - for node: FlowNode in nodes: - if node.buildable and node.global_position.distance_to(pos) < closest_dist: - closest_dist = node.global_position.distance_to(pos) - closest_point = node + for node: FlowNodeData in data.nodes: + if node.position.distance_to(pos) < closest_dist: + if !traversable_required or node.traversable: + if !buildable_required or node.buildable: + closest_dist = node.position.distance_to(pos) + closest_point = node return closest_point func test_traversability() -> bool: - for node: FlowNode in start_nodes: + for node: FlowNodeData in start_nodes: while node.best_path != null: if node.best_path.traversable: node = node.best_path @@ -106,9 +43,9 @@ func test_traversability() -> bool: return true -func iterate_search(search_frontier: Array[FlowNode], reached: Array[FlowNode]) -> void: - var current: FlowNode = search_frontier.pop_front() - for node: FlowNode in current.connections: +func iterate_search(search_frontier: Array[FlowNodeData], reached: Array[FlowNodeData]) -> void: + var current: FlowNodeData = search_frontier.pop_front() + for node: FlowNodeData in current.connected_nodes: if !reached.has(node): reached.append(node) if node.traversable: @@ -117,9 +54,9 @@ func iterate_search(search_frontier: Array[FlowNode], reached: Array[FlowNode]) func calculate() -> void: - var reached: Array[FlowNode] = [] - var search_frontier: Array[FlowNode] = [] - for node: FlowNode in goal_nodes: + var reached: Array[FlowNodeData] = [] + var search_frontier: Array[FlowNodeData] = [] + for node: FlowNodeData in goal_nodes: node.best_path = null reached.append(node) search_frontier.append(node) @@ -127,20 +64,19 @@ func calculate() -> void: iterate_search(search_frontier, reached) -var magic_node: FlowNode = null -func traversable_after_blocking_point(point: FlowNode) -> bool: +func traversable_after_blocking_point(point: FlowNodeData) -> bool: magic_node = null - var reached: Array[FlowNode] = [point] - var search_frontier: Array[FlowNode] = [] - for node: FlowNode in point.connections: + var reached: Array[FlowNodeData] = [point] + var search_frontier: Array[FlowNodeData] = [] + for node: FlowNodeData in point.connected_nodes: if node.best_path == point and node.traversable: reached.append(node) search_frontier.append(node) if search_frontier.size() == 0: # if no neighbors rely on this node, then we're all good return true while search_frontier.size() > 0: - var current: FlowNode = search_frontier.pop_front() - for node: FlowNode in current.connections: + var current: FlowNodeData = search_frontier.pop_front() + for node: FlowNodeData in current.connected_nodes: if !reached.has(node): if node.traversable and node.best_path != node and !reached.has(node.best_path): #if we havent already seen the node this neighbor goes to, @@ -154,33 +90,23 @@ func traversable_after_blocking_point(point: FlowNode) -> bool: return false -## Connects many nodes to a single single node, if any connections already -## exist, this function disconnects them instead -func connect_many_nodes(common_node: FlowNode, child_nodes: Array[FlowNode]) -> void: - for node: FlowNode in child_nodes: - if common_node.connections.has(node): - disconnect_nodes(common_node, node) - else: - connect_nodes(common_node, node) - - -func toggle_goal(nodes_to_toggle: Array[FlowNode]) -> void: - for node: FlowNode in nodes_to_toggle: +func toggle_goal(nodes_to_toggle: Array[FlowNodeData]) -> void: + for node: FlowNodeData in nodes_to_toggle: if goal_nodes.has(node): goal_nodes.erase(node) else: goal_nodes.append(node) -func toggle_start(nodes_to_toggle: Array[FlowNode]) -> void: - for node: FlowNode in nodes_to_toggle: +func toggle_start(nodes_to_toggle: Array[FlowNodeData]) -> void: + for node: FlowNodeData in nodes_to_toggle: if start_nodes.has(node): start_nodes.erase(node) else: start_nodes.append(node) -func toggle_traversable(node: FlowNode) -> bool: +func toggle_traversable(node: FlowNodeData) -> bool: node.traversable = !node.traversable calculate() #TODO: technically the path only changed if the new path IS traversable @@ -188,64 +114,5 @@ func toggle_traversable(node: FlowNode) -> bool: return test_traversability() -func toggle_buildable(node: FlowNode) -> void: +func toggle_buildable(node: FlowNodeData) -> void: node.buildable = !node.buildable - - -func create_node(pos: Vector3 = Vector3.ZERO, grid_id: int = -1, grid_x: int = 0, grid_y: int = 0) -> FlowNode: - var node: FlowNode = flow_node_scene.instantiate() - node.node_id = created_nodes - node.grid_id = grid_id - node.grid_x = grid_x - node.grid_y = grid_y - node.position = pos - node.set_color(Color.WEB_GRAY) - nodes.append(node) - add_child(node) - node.owner = self - created_nodes += 1 - return node - - -func delete_node(node: FlowNode) -> void: - for neighbor: FlowNode in node.connections: - node.remove_connection(neighbor) - nodes.erase(node) - node.queue_free() - - -func connect_nodes(node1: FlowNode, node2: FlowNode) -> void: - if node1 != node2: - node1.add_connection(node2) - node2.add_connection(node1) - - -func disconnect_nodes(node1: FlowNode, node2: FlowNode) -> void: - if node1 != node2: - node1.remove_connection(node2) - node2.remove_connection(node1) - - -func create_grid(x_size: int, y_size: int, gap: float) -> void: - data_file.grids += 1 - var grid_id: int = data_file.grids - var grid: Array[Array] = [] - for x: int in x_size: - var row: Array[FlowNode] = [] - for y: int in y_size: - #var start_pos: Vector3 = Vector3.ZERO - (Vector3(gap * x_size, 0, gap * y_size) / 2.0) - var point_position: Vector3 = Vector3((x - floori(x_size / 2.0)) * gap, 0, (y - floori(y_size / 2.0)) * gap) - #point_position += global_position - #row.append(create_node(start_pos + Vector3(gap * x, 0, gap * y))) - row.append(create_node(point_position, grid_id, x, y)) - grid.append(row) - for x: int in grid.size(): - for y: int in grid[x].size(): - if y > 0: - connect_nodes(grid[x][y], grid[x][y - 1]) - if x > 0: - connect_nodes(grid[x][y], grid[x - 1][y]) - if y < grid[x].size() - 1: - connect_nodes(grid[x][y], grid[x][y + 1]) - if x < grid.size() - 1: - connect_nodes(grid[x][y], grid[x + 1][y]) diff --git a/Scenes/FlowField/flow_field_data.gd b/Scenes/FlowField/flow_field_data.gd index 8eb9279..000d428 100644 --- a/Scenes/FlowField/flow_field_data.gd +++ b/Scenes/FlowField/flow_field_data.gd @@ -1,8 +1,8 @@ class_name FlowFieldData -extends Resource +extends RefCounted -@export var nodes: Array[FlowNodeData] -@export var grids: int = 0 +var nodes: Array[FlowNodeData] +var grids: int = 0 func to_dict() -> Dictionary: diff --git a/Scenes/FlowField/flow_field_editor.gd b/Scenes/FlowField/flow_field_editor.gd index c720f49..0574da8 100644 --- a/Scenes/FlowField/flow_field_editor.gd +++ b/Scenes/FlowField/flow_field_editor.gd @@ -4,10 +4,62 @@ extends Node @export var flow_field: FlowField -func create_grid(x: int, y: int, gap: int) -> Array[FlowNode]: - #return flow_field.create_grid(x, y, gap) - return [] +func create_grid(x_size: int, y_size: int, gap: float) -> Array[FlowNodeData]: + flow_field.data.grids += 1 + var grid_id: int = flow_field.data.grids + var grid: Array[Array] = [] + var created_nodes: Array[FlowNodeData] = [] + + for x: int in x_size: + var row: Array[FlowNodeData] = [] + for y: int in y_size: + var point_position: Vector3 = Vector3((x - floori(x_size / 2.0)) * gap, 0, (y - floori(y_size / 2.0)) * gap) + var created_node: FlowNodeData = create_node(point_position, grid_id, x, y) + created_nodes.append(created_node) + row.append(created_node) + grid.append(row) + + for x: int in grid.size(): + for y: int in grid[x].size(): + if y > 0: + connect_nodes(grid[x][y], grid[x][y - 1]) + if x > 0: + connect_nodes(grid[x][y], grid[x - 1][y]) + if y < grid[x].size() - 1: + connect_nodes(grid[x][y], grid[x][y + 1]) + if x < grid.size() - 1: + connect_nodes(grid[x][y], grid[x + 1][y]) + return created_nodes -func create_node(pos: Vector3 = Vector3.ZERO, grid_id: int = -1, grid_x: int = 0, grid_y: int = 0) -> FlowNode: - return flow_field.create_node(pos, grid_id, grid_x, grid_y) +func create_node(pos: Vector3 = Vector3.ZERO, grid_id: int = -1, grid_x: int = 0, grid_y: int = 0) -> FlowNodeData: + var node: FlowNodeData = FlowNodeData.new() + node.node_id = flow_field.data.nodes.size() + node.grid_id = grid_id + node.grid_x = grid_x + node.grid_y = grid_y + node.position = pos + flow_field.data.nodes.append(node) + return node + + +func delete_node(node: FlowNodeData) -> void: + for neighbor: FlowNodeData in node.connections: + disconnect_nodes(node, neighbor) + flow_field.data.nodes.erase(node) + + +func connect_nodes(a: FlowNodeData, b: FlowNodeData) -> void: + if a != b: + if a.connected_nodes.has(b): + a.connected_nodes.append(b) + if b.connected_nodes.has(a): + b.add_connection(a) + + +func disconnect_nodes(a: FlowNodeData, b: FlowNodeData) -> void: + if a != b: + if a.connected_nodes.has(b): + a.connected_nodes.erase(b) + if b.connected_nodes.has(a): + b.connected_nodes.erase(a) diff --git a/Scenes/FlowField/flow_field_tool.gd b/Scenes/FlowField/flow_field_tool.gd index 1fe61f2..1317115 100644 --- a/Scenes/FlowField/flow_field_tool.gd +++ b/Scenes/FlowField/flow_field_tool.gd @@ -4,14 +4,17 @@ extends Node @export_group("Basic Function") @export var zone_list: Array[PackedScene] @export var zone_holder: Node3D +@export var visualiser_scene: PackedScene @export_group("Flow Field Editor") -@export var flow_field: FlowField @export var raycast: RayCast3D @export var project_raycast: RayCast3D @export var camera: Camera3D @export var camera_pivot: Node3D @export var position_field: HBoxContainer +@export var position_x: LineEdit +@export var position_y: LineEdit +@export var position_z: LineEdit @export var x_field: LineEdit @export var y_field: LineEdit @export var z_field: LineEdit @@ -20,8 +23,10 @@ extends Node @export var gap_field: LineEdit @export var save_path: LineEdit -var hover: FlowNode = null -var selected: Array[FlowNode] = [] +var flow_field: FlowField +var visualisers: Dictionary[FlowNodeData, FlowNodeVisualiser] +var hover: FlowNodeVisualiser = null +var selected: Array[FlowNodeVisualiser] = [] var vector_dirty: bool = false var editing: bool = false var selected_zone: int = -1 @@ -29,10 +34,13 @@ var level: Level var radius: float = 0 var up_angle: float = 0 var rotate_held: bool = false -var flow_field_data: FlowFieldData +var flow_field_editor: FlowFieldEditor +var path_vfx: PathVFX func _ready() -> void: + flow_field_editor = FlowFieldEditor.new() + add_child(flow_field_editor) var i: int = 0 for zone: PackedScene in zone_list: i += 1 @@ -40,6 +48,36 @@ func _ready() -> void: $VBoxContainer2/OptionButton.select(0) $VBoxContainer2/OptionButton.item_selected.connect(select_zone) _on_trash_button_pressed() + path_vfx = PathVFX.new() + path_vfx.line_width = 0.4 + path_vfx.material = load("res://path_material.tres") + add_child(path_vfx) + + +func setup_visualisers_from_flow_field_data(data: FlowFieldData) -> void: + for visualiser: FlowNodeVisualiser in visualisers.keys(): + visualiser.queue_free() + visualisers = {} + for node: FlowNodeData in data.nodes: + add_visual(node) + for node: FlowNodeData in visualisers.keys(): + add_visual_connections(node) + + +func add_visual(data: FlowNodeData) -> void: + var visual: FlowNodeVisualiser = visualiser_scene.instantiate() as FlowNodeVisualiser + visual.data = data + visual.position = data.position + add_child(visual) + visualisers[data] = visual + + +func add_visual_connections(data: FlowNodeData) -> void: + var connections: Array[FlowNodeVisualiser] = [] + for node: FlowNodeData in data.connected_nodes: + connections.append(visualisers[node]) + visualisers[data].connections = connections + visualisers[data].setup_connection_visualisers() func select_zone(zone_index: int) -> void: @@ -86,24 +124,23 @@ func _process(delta: float) -> void: var y: float = Input.get_axis("Move Forward", "Move Backward") var x: float = Input.get_axis("Move Left", "Move Right") var input_vector: Vector2 = Input.get_vector("Move Left", "Move Right", "Move Forward", "Move Backward") - #camera_pivot.position += Vector3(x, 0, y) * delta * 30 - #set_cam_position() + var movement: Vector3 = ((camera_pivot.transform.basis.z * input_vector.y) + (camera_pivot.transform.basis.x * input_vector.x)) var vec2: Vector2 = Vector2(movement.x, movement.z).normalized() camera_pivot.position += Vector3(vec2.x, 0.0, vec2.y) * delta * 30.0 func set_node_colors() -> void: - for node: FlowNode in flow_field.nodes: - if node.traversable and node.buildable: + for node: FlowNodeVisualiser in visualisers.values(): + if node.data.traversable and node.data.buildable: node.set_color(Color.WEB_GRAY) - elif node.traversable and !node.buildable: + elif node.data.traversable and !node.data.buildable: node.set_color(Color.CORAL) else: node.set_color(Color.BLACK) - if flow_field.goal_nodes.has(node): + if flow_field.goal_nodes.has(node.data): node.set_color(Color.BLUE) - if flow_field.start_nodes.has(node): + if flow_field.start_nodes.has(node.data): node.set_color(Color.PINK) if selected.has(node): node.set_color(Color.GREEN) @@ -157,24 +194,39 @@ func _on_z_field_changed(text: String) -> void: selected[0].global_position.z = float(text) +## Connects many nodes to a single single node, if any connections already +## exist, this function disconnects them instead +func connect_many_nodes(common_node: FlowNodeData, child_nodes: Array[FlowNodeData]) -> void: + for node: FlowNodeData in child_nodes: + if common_node.connections.has(node): + flow_field_editor.disconnect_nodes(common_node, node) + else: + flow_field_editor.connect_nodes(common_node, node) + + func set_position() -> void: - for node: FlowNode in selected: - node.global_position = Vector3(float($Position/x.text), float($Position/y.text), float($Position/z.text)) + for node: FlowNodeVisualiser in selected: + var vector: Vector3 = Vector3(float(position_x.text), float(position_y.text), float(position_z.text)) + node.data.position = vector + node.global_position = vector func offset_position() -> void: - for node: FlowNode in selected: - node.global_position += Vector3(float($Position/x.text), float($Position/y.text), float($Position/z.text)) + for node: FlowNodeVisualiser in selected: + var vector: Vector3 = Vector3(float(position_x.text), float(position_y.text), float(position_z.text)) + node.data.position += vector + node.global_position += vector func _on_create_button_pressed() -> void: - flow_field.create_node() + add_visual(flow_field_editor.create_node()) func _on_generate_grid_button_pressed() -> void: - flow_field.create_grid(int(x_size_field.text), int(y_size_field.text), float(gap_field.text)) - selected.append_array(flow_field.nodes) - create_grid_select_button(flow_field.data_file.grids) + for node: FlowNodeData in flow_field_editor.create_grid(int(x_size_field.text), int(y_size_field.text), float(gap_field.text)): + add_visual(node) + selected.append(node) + create_grid_select_button(flow_field.data.grids) func create_grid_select_button(grid: int) -> void: @@ -186,6 +238,13 @@ func create_grid_select_button(grid: int) -> void: func _on_calculate_button_pressed() -> void: flow_field.calculate() + var points: Array[Vector3] = [] + var node: FlowNodeData = flow_field.get_closest_point(flow_field.start_nodes[0].position, true, false) + points.append(node.position + Vector3(0, 0.1, 0)) + while node.best_path: + node = node.best_path + points.append(node.position + Vector3(0, 0.1, 0)) + path_vfx.path(points) func _on_connect_button_pressed() -> void: @@ -193,20 +252,24 @@ func _on_connect_button_pressed() -> void: func _on_mark_goal_button_pressed() -> void: - flow_field.toggle_goal(selected) + for node: FlowNodeVisualiser in selected: + flow_field.toggle_goal([node.data]) selected = [] vector_dirty = true func _on_mark_start_button_pressed() -> void: - flow_field.toggle_start(selected) + for node: FlowNodeVisualiser in selected: + flow_field.toggle_start([node.data]) selected = [] vector_dirty = true func _on_extrude_button_pressed() -> void: if selected.size() == 1: - var node: FlowNode = flow_field.create_node(selected[0].position) + var node: FlowNodeVisualiser = visualiser_scene.instantiate() as FlowNodeVisualiser + add_child(node) + node.data = flow_field.create_node(selected[0].position) node.add_connection(selected[0]) selected[0].add_connection(node) selected[0].set_color(Color.WEB_GRAY) @@ -216,61 +279,35 @@ func _on_extrude_button_pressed() -> void: func _on_toggle_traversable_button_pressed() -> void: - for node: FlowNode in selected: - if !flow_field.toggle_traversable(node): - flow_field.toggle_traversable(node) + for node: FlowNodeVisualiser in selected: + if !flow_field.toggle_traversable(node.data): + flow_field.toggle_traversable(node.data) selected = [] return selected = [] func _on_toggle_buildable_button_pressed() -> void: - for node: FlowNode in selected: - flow_field.toggle_buildable(node) + for node: FlowNodeVisualiser in selected: + flow_field.toggle_buildable(node.data) #TODO: This doesnt work as you'd expect because of physics frames func _on_project_downwards_button_pressed() -> void: - for node: FlowNode in selected: - project_raycast.global_position = node.global_position + Vector3.UP + for node: FlowNodeVisualiser in selected: + project_raycast.position = node.position + Vector3.UP project_raycast.target_position = Vector3.DOWN * 100.0 await get_tree().physics_frame await get_tree().physics_frame await get_tree().physics_frame await get_tree().physics_frame if project_raycast.is_colliding(): - node.global_position = project_raycast.get_collision_point() + node.position = project_raycast.get_collision_point() + node.data.position = node.position func _on_save_button_pressed() -> void: - var new_flow_field_data: FlowFieldData = FlowFieldData.new() - var dict: Dictionary[FlowNode, FlowNodeData] = {} - var grid_num: int = -1 - for node: FlowNode in flow_field.nodes: - var new_flow_node_data: FlowNodeData = FlowNodeData.new() - new_flow_node_data.node_id = node.node_id - new_flow_node_data.grid_id = node.grid_id - if node.grid_id > grid_num: - grid_num += 1 - new_flow_node_data.grid_x = node.grid_x - new_flow_node_data.grid_y = node.grid_y - new_flow_node_data.position = node.global_position - new_flow_node_data.buildable = node.buildable - if flow_field.start_nodes.has(node): - new_flow_node_data.type = FlowNodeData.NodeType.START - elif flow_field.goal_nodes.has(node): - new_flow_node_data.type = FlowNodeData.NodeType.GOAL - else: - new_flow_node_data.type = FlowNodeData.NodeType.STANDARD - dict[node] = new_flow_node_data - for node: FlowNode in flow_field.nodes: - var flow_node_data: FlowNodeData = dict[node] - for neighbor: FlowNode in node.connections: - flow_node_data.connected_nodes.append(dict[neighbor]) - new_flow_field_data.nodes.append(flow_node_data) - new_flow_field_data.grids = grid_num - - var string: String = JSON.stringify(new_flow_field_data.to_dict()) + var string: String = JSON.stringify(flow_field.data.to_dict()) var path: String = save_path.text + ".json" var dir: DirAccess = DirAccess.open("user://") if !dir.dir_exists("pathing_graphs"): @@ -302,48 +339,41 @@ func _on_load_button_pressed() -> void: if parse_result == OK: var dict: Dictionary = json.data var flow_field_data: FlowFieldData = FlowFieldData.from_dict(dict) - flow_field.load_from_data(flow_field_data) + flow_field.data = flow_field_data for grid: int in flow_field_data.grids: create_grid_select_button(grid + 1) - - -#func _on_load_button_pressed() -> void: - #if ResourceLoader.exists(save_path.text + ".res"): - #print("file exists") - #var resource: Resource = ResourceLoader.load(save_path.text + ".res", "", ResourceLoader.CACHE_MODE_IGNORE) - #if resource is FlowFieldData: - #flow_field.load_from_data(resource) - #print("loaded some data") - #else: - #print("some error loading!") + setup_visualisers_from_flow_field_data(flow_field_data) func _on_trash_button_pressed() -> void: if flow_field: flow_field.queue_free() + for visualiser: FlowNodeVisualiser in visualisers.values(): + visualiser.queue_free() + visualisers = {} for child: Node in $VBoxContainer3.get_children(): child.queue_free() flow_field = FlowField.new() - flow_field_data = FlowFieldData.new() - flow_field.data_file = flow_field_data + flow_field.data = FlowFieldData.new() add_child(flow_field) + flow_field_editor.flow_field = flow_field if level: level.flow_field = flow_field func _on_select_all_pressed() -> void: selected = [] - for node: FlowNode in flow_field.nodes: + for node: FlowNodeVisualiser in flow_field.nodes: selected.append(node) func select_in_grid(grid: int) -> void: selected = [] - for node: FlowNode in flow_field.nodes: - if node.grid_id == grid: + for node: FlowNodeVisualiser in flow_field.nodes: + if node.data.grid_id == grid: selected.append(node) func _on_print_ids_pressed() -> void: - for node: FlowNode in selected: - print(node.node_id) + for node: FlowNodeVisualiser in selected: + print(node.data.node_id) diff --git a/Scenes/FlowField/flow_field_tool.tscn b/Scenes/FlowField/flow_field_tool.tscn index 4f0be36..7f78acf 100644 --- a/Scenes/FlowField/flow_field_tool.tscn +++ b/Scenes/FlowField/flow_field_tool.tscn @@ -3,20 +3,25 @@ [ext_resource type="Script" uid="uid://05c5q1v2nv8p" path="res://Scenes/FlowField/flow_field_tool.gd" id="1_e7pmn"] [ext_resource type="PackedScene" uid="uid://y1qa1g3ic8sp" path="res://Zones/Moat/scn_moat.tscn" id="2_030xf"] [ext_resource type="PackedScene" uid="uid://csq7if8wojp4g" path="res://Zones/Cave/scn_cave.tscn" id="3_xar7e"] +[ext_resource type="PackedScene" uid="uid://bssfvyxv5uo1f" path="res://Scenes/FlowField/flow_node.tscn" id="4_50p2d"] [sub_resource type="Environment" id="Environment_e7pmn"] ambient_light_source = 2 ambient_light_color = Color(0.728822, 0.728822, 0.728822, 1) -[node name="FlowFieldTool" type="Node" unique_id=897052359 node_paths=PackedStringArray("zone_holder", "raycast", "project_raycast", "camera", "camera_pivot", "position_field", "x_field", "y_field", "z_field", "x_size_field", "y_size_field", "gap_field", "save_path")] +[node name="FlowFieldTool" type="Node" unique_id=897052359 node_paths=PackedStringArray("zone_holder", "raycast", "project_raycast", "camera", "camera_pivot", "position_field", "position_x", "position_y", "position_z", "x_field", "y_field", "z_field", "x_size_field", "y_size_field", "gap_field", "save_path")] script = ExtResource("1_e7pmn") zone_list = Array[PackedScene]([ExtResource("2_030xf"), ExtResource("3_xar7e")]) zone_holder = NodePath("ZoneHolder") +visualiser_scene = ExtResource("4_50p2d") raycast = NodePath("CameraFocus/Camera3D/RayCast3D") project_raycast = NodePath("RayCast3D") camera = NodePath("CameraFocus/Camera3D") camera_pivot = NodePath("CameraFocus") position_field = NodePath("Position") +position_x = NodePath("Position/x") +position_y = NodePath("Position/y") +position_z = NodePath("Position/z") x_field = NodePath("Position/x") y_field = NodePath("Position/y") z_field = NodePath("Position/z") diff --git a/Scenes/FlowField/flow_node.gd b/Scenes/FlowField/flow_node.gd index 8dfaf29..1a9a1bf 100644 --- a/Scenes/FlowField/flow_node.gd +++ b/Scenes/FlowField/flow_node.gd @@ -1,17 +1,11 @@ -class_name FlowNode +class_name FlowNodeVisualiser extends StaticBody3D -@export var connections: Array[FlowNode] -@export var visualisers: Array[Node3D] -@export var traversable: bool = true -@export var buildable: bool = true - +var data: FlowNodeData +var connections: Array[FlowNodeVisualiser] +var visualisers: Array[Node3D] var visual_scene: PackedScene = preload("res://Scenes/FlowField/cube2.tscn") -var node_id: int = 0 -var grid_id: int = -1 -var grid_x: int = 0 -var grid_y: int = 0 -var best_path: FlowNode : +var best_path: FlowNodeVisualiser : get(): return best_path set(value): @@ -20,9 +14,9 @@ var best_path: FlowNode : set_connector_color(best_path, Color.DARK_GREEN) -func _ready() -> void: +func setup_connection_visualisers() -> void: visualisers = [] - for node: FlowNode in connections: + for node: FlowNodeVisualiser in connections: var visual: Node3D = visual_scene.instantiate() add_child(visual) visual.owner = self @@ -45,13 +39,13 @@ func set_color(new_color: Color) -> void: $flow_node/Sphere.material_override.albedo_color = new_color -func set_connector_color(node: FlowNode, new_color: Color) -> void: +func set_connector_color(node: FlowNodeVisualiser, new_color: Color) -> void: if visible: var i: int = connections.find(node) visualisers[i].get_child(0).material_override.albedo_color = new_color -func add_connection(node: FlowNode) -> void: +func add_connection(node: FlowNodeVisualiser) -> void: if !connections.has(node): var visual: Node3D = visual_scene.instantiate() add_child(visual) @@ -61,7 +55,7 @@ func add_connection(node: FlowNode) -> void: set_connector_color(node, Color.WEB_GRAY) -func remove_connection(node: FlowNode) -> void: +func remove_connection(node: FlowNodeVisualiser) -> void: if connections.has(node): var i: int = connections.find(node) visualisers.pop_at(i).queue_free() diff --git a/Scenes/FlowField/flow_node_data.gd b/Scenes/FlowField/flow_node_data.gd index ba406ba..b2bc917 100644 --- a/Scenes/FlowField/flow_node_data.gd +++ b/Scenes/FlowField/flow_node_data.gd @@ -1,21 +1,25 @@ class_name FlowNodeData -extends Resource +extends RefCounted enum NodeType { STANDARD = 0, START = 1, GOAL = 2, + OBSTACLE = 3, } -@export var node_id: int = -1 -@export var position: Vector3 = Vector3.ZERO -@export var type: NodeType = NodeType.STANDARD -@export var buildable: bool = true -@export var connected_nodes: Array[FlowNodeData] -@export var in_grid: bool = false -@export var grid_id: int = -1 -@export var grid_x: int = 0 -@export var grid_y: int = 0 + +var node_id: int = -1 +var position: Vector3 = Vector3.ZERO +var type: NodeType = NodeType.STANDARD +var buildable: bool = true +var traversable: bool = true +var connected_nodes: Array[FlowNodeData] +var best_path: FlowNodeData +var in_grid: bool = false +var grid_id: int = -1 +var grid_x: int = 0 +var grid_y: int = 0 #This function cannot fill in the connection information until a complete set @@ -32,6 +36,10 @@ static func from_dict(dict: Dictionary) -> FlowNodeData: data.grid_id = dict["grid_id"] data.grid_x = dict["grid_x"] data.grid_y = dict["grid_y"] + + if data.type == NodeType.OBSTACLE: + data.buildable = false + data.traversable = false return data diff --git a/Scenes/TowerBase/tower_base.gd b/Scenes/TowerBase/tower_base.gd index f56b26c..7bc7772 100644 --- a/Scenes/TowerBase/tower_base.gd +++ b/Scenes/TowerBase/tower_base.gd @@ -9,7 +9,7 @@ class_name TowerBase extends StaticBody3D var game_manager: GameManager var owner_id: int -var point: FlowNode +var point: FlowNodeData var tower: Tower = null var has_card: bool : set(_value): diff --git a/Scripts/EnemyAI/leaping_controller.gd b/Scripts/EnemyAI/leaping_controller.gd index 458b346..1cd04e2 100644 --- a/Scripts/EnemyAI/leaping_controller.gd +++ b/Scripts/EnemyAI/leaping_controller.gd @@ -138,7 +138,7 @@ func _physics_process(delta: float) -> void: func consider_leap(direction: Vector3) -> void: - var node: FlowNode = check_jump(character.global_position + (direction * jump_distance)) + var node: FlowNodeData = check_jump(character.global_position + (direction * jump_distance)) if node: var expected_distance_remaining: float = calculate_distance_to_goal(node) expected_distance_remaining += (character.global_position + (direction * jump_distance)).distance_to(node.global_position) @@ -153,8 +153,8 @@ func finish_jump() -> void: jumping = false -func check_jump(destination: Vector3) -> FlowNode: - var closest_point: FlowNode = flow_field.get_closest_traversable_point(destination) +func check_jump(destination: Vector3) -> FlowNodeData: + var closest_point: FlowNodeData = flow_field.get_closest_point(destination, true, false) if !closest_point.best_path or closest_point.global_position.distance_to(destination) > 1.2: return null return closest_point.best_path diff --git a/Scripts/EnemyAI/pathing_controller.gd b/Scripts/EnemyAI/pathing_controller.gd index 4cc98c1..501c254 100644 --- a/Scripts/EnemyAI/pathing_controller.gd +++ b/Scripts/EnemyAI/pathing_controller.gd @@ -4,7 +4,7 @@ extends EnemyMovement #var path: Curve3D #var path_progress: float = 0.0 var flow_field: FlowField -var next_node: FlowNode : +var next_node: FlowNodeData : get(): return next_node set(value): @@ -16,16 +16,16 @@ var next_node: FlowNode : #TODO: make deterministic random var x: float = randf_range(-1, 1) var y: float = randf_range(-1, 1) - if Vector3(next_node.global_position.x + x, next_node.global_position.y, next_node.global_position.z + y).distance_to(next_node.global_position) <= 1.0: + if Vector3(next_node.position.x + x, next_node.position.y, next_node.position.z + y).distance_to(next_node.position) <= 1.0: found_point = true - next_pos = Vector3(next_node.global_position.x + x, next_node.global_position.y, next_node.global_position.z + y) + next_pos = Vector3(next_node.position.x + x, next_node.position.y, next_node.position.z + y) var next_pos: Vector3 func _ready() -> void: super._ready() if flow_field: - next_node = flow_field.get_closest_traversable_point(character.global_position) + next_node = flow_field.get_closest_point(character.global_position, true, false) #We skip one node so the "start" nodes placed near #spawners are just usefull for "catching" enemies that are looking #for a way into the pathfinding graph @@ -33,14 +33,14 @@ func _ready() -> void: distance_remaining += calculate_distance_to_goal(next_node) -func calculate_distance_to_goal(node: FlowNode) -> float: +func calculate_distance_to_goal(node: FlowNodeData) -> float: var distance: float = 0.0 - distance += character.global_position.distance_to(node.global_position) + distance += character.global_position.distance_to(node.position) if node.best_path: - var then_next_node: FlowNode = node.best_path - distance += node.global_position.distance_to(then_next_node.global_position) + var then_next_node: FlowNodeData = node.best_path + distance += node.position.distance_to(then_next_node.position) while then_next_node.best_path: - distance += then_next_node.global_position.distance_to(then_next_node.best_path.global_position) + distance += then_next_node.position.distance_to(then_next_node.best_path.position) then_next_node = then_next_node.best_path return distance diff --git a/Scripts/enemy_spawner.gd b/Scripts/enemy_spawner.gd index bdb562e..d168724 100644 --- a/Scripts/enemy_spawner.gd +++ b/Scripts/enemy_spawner.gd @@ -21,9 +21,8 @@ var enemies_spawned: Dictionary = {} var enemies_to_spawn: int = 0 var done_spawning: bool = true var enemy_id: int = 0 -var path: Path3D -var path_polygon: PackedScene = preload("res://path_polygon.tscn") var game_manager: GameManager +var path_vfx: PathVFX func _process(delta: float) -> void: @@ -84,27 +83,27 @@ func networked_spawn_land_enemy(enemy_num: int, id1: int, id2: int) -> void: func create_path() -> void: if type != Data.EnemyType.LAND: return - path = Path3D.new() - path.curve = Curve3D.new() - add_child(path) - var polygon: CSGPolygon3D = path_polygon.instantiate() - path.add_child(polygon) - polygon.mode = CSGPolygon3D.MODE_PATH - polygon.path_node = path.get_path() - path.global_position = Vector3.ZERO + if path_vfx: + path_vfx.queue_free() + path_vfx = PathVFX.new() + path_vfx.line_width = 0.2 + path_vfx.material = load("res://path_material.tres") + add_child(path_vfx) + path_vfx.global_position = Vector3.ZERO update_path() func update_path() -> void: - if type != Data.EnemyType.LAND or !flow_field.nodes: + if type != Data.EnemyType.LAND or !flow_field.data.nodes: return - path.curve = Curve3D.new() - var node: FlowNode = flow_field.get_closest_traversable_point(global_position) - path.curve.add_point(node.global_position + Vector3(0, 0.5, 0)) + var points: Array[Vector3] = [] + var node: FlowNodeData = flow_field.get_closest_point(flow_field.start_nodes[0].position, true, false) + points.append(node.position + Vector3(0, 0.15, 0)) while node.best_path: node = node.best_path - path.curve.add_point(node.global_position + Vector3(0, 0.5, 0)) + points.append(node.position + Vector3(0, 0.15, 0)) + path_vfx.path(points) diff --git a/Scripts/game.gd b/Scripts/game.gd index 5b15e3e..e571dc7 100644 --- a/Scripts/game.gd +++ b/Scripts/game.gd @@ -109,7 +109,7 @@ func spawn_level(scene: PackedScene) -> void: var flow_field: FlowField = FlowField.new() level.flow_field = flow_field level.add_child(flow_field) - flow_field.load_from_data(FlowFieldTool.load_flow_field_from_disc(level_config.zone.flow_field_data_path)) + flow_field.data = FlowFieldTool.load_flow_field_from_disc(level_config.zone.flow_field_data_path) level.load_flow_field() level.game_manager = self for x: EnemySpawner in level.enemy_spawns: @@ -121,7 +121,7 @@ func spawn_level(scene: PackedScene) -> void: root_scene.add_child(level) for spawner: EnemySpawner in level.enemy_spawns: spawner.create_path() - level.generate_obstacle(level_config.points_blocked) + level.generate_obstacles(level_config.points_blocked) func spawn_players() -> void: @@ -214,7 +214,9 @@ func temp_set_upcoming_wave(new_wave: WaveConfig, coins: int) -> void: pot = coins var dict: Dictionary[String, int] = {} for enemy_group: EnemyGroup in new_wave.enemy_groups.keys(): - dict[enemy_group.enemy.title] = enemy_group.count + if !dict.has(enemy_group.enemy.title): + dict[enemy_group.enemy.title] = 0 + dict[enemy_group.enemy.title] += enemy_group.count connected_players_nodes[multiplayer.get_unique_id()].hud.set_upcoming_wave(dict) @@ -351,7 +353,6 @@ func start() -> void: set_upcoming_wave() level.flow_field.calculate() level.enemy_spawns[0].update_path() - level.generate_obstacles() #Start game game_active = true diff --git a/Scripts/level.gd b/Scripts/level.gd index ff41e62..5688ecc 100644 --- a/Scripts/level.gd +++ b/Scripts/level.gd @@ -11,22 +11,22 @@ extends Node3D @export var printer: CardPrinter @export var shop: ShopStand @export var obstacles: Array[PackedScene] -var walls: Dictionary[FlowNode, TowerBase] = {} + +var walls: Dictionary[FlowNodeData, TowerBase] = {} var wall_id: int = 0 var tower_base_scene: PackedScene = load("res://Scenes/TowerBase/tower_base.tscn") var tower_frame_scene: PackedScene = load("res://Scenes/tower_frame.tscn") -var tower_frames: Dictionary[FlowNode, Node3D] = {} +var tower_frames: Dictionary[FlowNodeData, Node3D] = {} var game_manager: GameManager var flow_field: FlowField func load_flow_field() -> void: - var x: int = 0 for spawn: EnemySpawner in enemy_spawns: flow_field.path_updated.connect(spawn.update_path) - for node: FlowNode in flow_field.nodes: + + for node: FlowNodeData in flow_field.data.nodes: if node.buildable: - x += 1 var frame: Node3D = tower_frame_scene.instantiate() tower_frames[node] = frame add_child(frame) @@ -34,23 +34,23 @@ func load_flow_field() -> void: func disable_all_tower_frames() -> void: - for node: FlowNode in tower_frames: + for node: FlowNodeData in tower_frames: tower_frames[node].visible = false func enable_non_path_tower_frames() -> void: - for node: FlowNode in tower_frames: + for node: FlowNodeData in tower_frames: tower_frames[node].visible = true disable_path_tower_frames() func disable_path_tower_frames() -> void: - for node: FlowNode in tower_frames: + for node: FlowNodeData in tower_frames: if node.traversable and !flow_field.traversable_after_blocking_point(node): tower_frames[node].visible = false -func set_wall(point: FlowNode, caller_id: int) -> void: +func set_wall(point: FlowNodeData, caller_id: int) -> void: point.traversable = false flow_field.calculate() flow_field.path_updated.emit() @@ -59,7 +59,7 @@ func set_wall(point: FlowNode, caller_id: int) -> void: wall_id += 1 -func remove_wall(point: FlowNode) -> void: +func remove_wall(point: FlowNodeData) -> void: var wall: TowerBase = walls[point] #game_manager.connected_players_nodes[wall.owner_id].currency += Data.wall_cost game_manager.connected_players_nodes[wall.owner_id].unready_self() @@ -71,10 +71,10 @@ func remove_wall(point: FlowNode) -> void: enable_non_path_tower_frames() -func spawn_wall(point: FlowNode, name_id: int, caller_id: int) -> void: +func spawn_wall(point: FlowNodeData, name_id: int, caller_id: int) -> void: var base: TowerBase = tower_base_scene.instantiate() as TowerBase base.game_manager = game_manager - base.position = point.global_position + base.position = point.position base.name = "Wall" + str(name_id) base.owner_id = caller_id base.point = point @@ -83,59 +83,15 @@ func spawn_wall(point: FlowNode, name_id: int, caller_id: int) -> void: disable_path_tower_frames() -func generate_obstacle(ids: Array[int]) -> void: - var points: Array[FlowNode] = [] - for node: FlowNode in flow_field.nodes: +func generate_obstacles(ids: Array[int]) -> void: + var points: Array[FlowNodeData] = [] + for node: FlowNodeData in flow_field.data.nodes: if ids.has(node.node_id): points.append(node) - for node: FlowNode in points: + for node: FlowNodeData in points: var obstacle: Node3D = obstacles[0].instantiate() obstacle.position = node.position flow_field.toggle_buildable(node) if node.traversable: flow_field.toggle_traversable(node) add_child(obstacle) - - -func generate_obstacles() -> void: - pass - #print(str(multiplayer.get_unique_id()) + " spawning obstacles with seed: " + str(Game.rng.seed)) - #var obstacle_count: int = NoiseRandom.randi_in_range(1, 0, 5) - #obstacle_count = 0 -# for index: int in obstacle_count: -# #var x: int = Game.randi_in_range(10 * index, 1 - a_star_graph_3d.grid_size.x, a_star_graph_3d.grid_size.x - 1) - #var y: int = Game.randi_in_range(32 * index, 1 - a_star_graph_3d.grid_size.y, a_star_graph_3d.grid_size.y - 1) -# var chosen_obstacle: int = Game.randi_in_range(4 * index, 0, obstacle_scenes.size() - 1) -# var obstacle: GridMap = obstacle_scenes[chosen_obstacle].instantiate() as GridMap -# var orientations: Array[int] = [0, 90, 180, 270] -# var chosen_orientation: int = Game.randi_in_range(15 * index, 0, orientations.size() - 1) -# #obstacle.position = Vector3(x, 0, y) -# obstacle.set_rotation_degrees(Vector3(0, chosen_orientation, 0)) -# add_child(obstacle) -# for cell: Vector3i in obstacle.get_used_cells(): -# var cell_coord: Vector3 = obstacle.to_global(obstacle.map_to_local(cell)) -# remove_world_tile(round(cell_coord.x), round(cell_coord.z)) -# obstacle.queue_free() - - -#func cell_coord_to_astar_point(x: int, y: int) -> int: -# var center_point_x: int = floori(a_star_graph_3d.grid_size.x / 2.0) * a_star_graph_3d.grid_size.y -# var center_point_y: int = floori(a_star_graph_3d.grid_size.y / 2.0) -# return (center_point_x + (int(x / 2.0) * a_star_graph_3d.grid_size.y)) + (center_point_y + int(y / 2.0)) - - -#func remove_world_tile(x: int, y: int) -> void: -# if get_cell_item(Vector3i(x, 0, y)) != 1 or abs(x) >= a_star_graph_3d.grid_size.x or abs(y) >= a_star_graph_3d.grid_size.y: -# return -# set_cell_item(Vector3i(x, 0, y), INVALID_CELL_ITEM) -# var point: int = cell_coord_to_astar_point(x, y) -# var north_point: int = cell_coord_to_astar_point(x - 1, y) -# var south_point: int = cell_coord_to_astar_point(x + 1, y) -# var east_point: int = cell_coord_to_astar_point(x, y + 1) -# var west_point: int = cell_coord_to_astar_point(x, y - 1) -# if x % 2 == 0 and y % 2 == 0: #If the tile is on a point on the pathfinding grid -# a_star_graph_3d.astar.set_point_disabled(point) -# if x % 2 == 1 and y % 2 == 0: #If the cell breaks a north-south link -# a_star_graph_3d.astar.disconnect_points(north_point, south_point) -# if x % 2 == 0 and y % 2 == 1: #If the cell breaks a east-west link -# a_star_graph_3d.astar.disconnect_points(east_point, west_point) diff --git a/Scripts/wave_manager.gd b/Scripts/wave_manager.gd index d038055..867a050 100644 --- a/Scripts/wave_manager.gd +++ b/Scripts/wave_manager.gd @@ -52,7 +52,8 @@ static func generate_wave(spawn_power: int, spawn_pool: Array[Enemy], spawners: var first_enemy_id: int = -1 while !enemy_chosen: #Next, determine what is the most groups we can afford - most_enemies_afforded = int(spawn_power / enemy.spawn_power) + @warning_ignore("integer_division") + most_enemies_afforded = spawn_power / enemy.spawn_power if most_enemies_afforded > 0: enemy_chosen = true else: diff --git a/Sprite-0001.png b/Sprite-0001.png new file mode 100644 index 0000000000000000000000000000000000000000..892413da22443cc0919bdd72c31ca09efd49f270 GIT binary patch literal 549 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hEk44ofy`glX=O&z7Cvd!58N5P+xe+^@EDb;@{%)86J6_PzvNY zS1jxFZeY`eqe?<2FP5Ww_jdqBhdQh`?X*$O&mIWDK zC0I?vxH=d#`U{Wi+@6)?s{8ShamDiM9dgrheQhsZ5@l4jX=4*&l^r_=y10pw`+=ZoPG~5@ozsQk-TKy!t1%LSswc?K3deq(h%3K zo4ZD=k44ofy`glX=O&z;Mdb z#WAE}&fA+C1)CfsSQF20lr(M^;#hM)E92k|u{DRdcyt;S7t81{q!zH{@&EtJb)j7P z;KX0Ai*K-QTh4xg$AN!=WP_Z8F@tSG8bcL>ZcT%8pS|V1bD3-J_x;n*_nf5SIjL^C zThzy2;?pS+AbKP{nYT;S0ko#utnx%omt5R@ZKxy|`t0fQdt%D$8^g z&q-V#KgKSooV(A?`2Op6>+i|Cwa>Jw;oHfq{_DV&58_H}3s7zU# void: + var test_points: Array[Vector3] = [] + test_points.append(Vector3(5, 1, 1)) + test_points.append(Vector3(1, 1, 1)) + test_points.append(Vector3(1, 1, 5)) + path(test_points) + + +func path(path_points: Array[Vector3]) -> void: + if path_points.size() < 2: + return + #Add vertices + var vertices: PackedVector3Array = PackedVector3Array() + + for x: int in path_points.size() - 1: + if x > 0: + if path_points.size() > x + 2: + vertices.append_array(get_quad(path_points[x], path_points[x + 1], path_points[x - 1], path_points[x + 2])) + else: + vertices.append_array(get_quad(path_points[x], path_points[x + 1], path_points[x - 1], Vector3.ZERO)) + if x == 0: + if path_points.size() > 2: + vertices.append_array(get_quad(path_points[x], path_points[x + 1], Vector3.ZERO, path_points[x + 2])) + else: + print("go1") + vertices.append_array(get_quad(path_points[x], path_points[x + 1], Vector3.ZERO, Vector3.ZERO)) + + #Add UVs + var uvs: PackedVector2Array = PackedVector2Array() + + for i: int in path_points.size() - 1: + uvs.append(Vector2(1, float(i) / (path_points.size() - 1))) + uvs.append(Vector2(0, float(i + 1) / (path_points.size() - 1))) + uvs.append(Vector2(1, float(i + 1) / (path_points.size() - 1))) + + uvs.append(Vector2(0, float(i + 1) / path_points.size() - 1)) + uvs.append(Vector2(1, float(i) / path_points.size() - 1)) + uvs.append(Vector2(0, float(i) / path_points.size() - 1)) + #print(float(i) / path_points.size() - 1) + + #var prev_distance: float = 0 + #var scaler: float = 0.4 + # + #for i: int in path_points.size() - 1: + #var distance: float = path_points[i].distance_to(path_points[i + 1]) + #var next_distance: float = prev_distance + distance + #uvs.append(Vector2(1, prev_distance * scaler)) + #uvs.append(Vector2(0, next_distance * scaler)) + #uvs.append(Vector2(1, next_distance * scaler)) + # + #uvs.append(Vector2(0, next_distance * scaler)) + #uvs.append(Vector2(1, prev_distance * scaler)) + #uvs.append(Vector2(0, prev_distance * scaler)) + ##print(float(i) / path_points.size() - 1) + #prev_distance = distance + + #Initialize the ArrayMesh + var arr_mesh: ArrayMesh = ArrayMesh.new() + var arrays: Array = [] + arrays.resize(Mesh.ARRAY_MAX) + arrays[Mesh.ARRAY_VERTEX] = vertices + arrays[Mesh.ARRAY_TEX_UV] = uvs + + #Create the Mesh + arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) + arr_mesh.surface_set_material(arr_mesh.get_surface_count() - 1, material) + mesh = arr_mesh + + +func get_edge(pos: Vector3, direction1: Vector2, direction2: Vector2) -> Array[Vector3]: + var verts: Array[Vector3] = [] + + var ortho1: Vector2 = Vector2(-direction1.y, direction1.x).normalized() + var ortho2: Vector2 = Vector2(direction2.y, -direction2.x).normalized() + + var top_sample1: Vector3 = pos - (Vector3(ortho1.x, 0.0, ortho1.y) * line_width) + var top_sample2: Vector3 = pos - (Vector3(ortho2.x, 0.0, ortho2.y) * line_width) + + var bottom_sample1: Vector3 = pos + (Vector3(ortho1.x, 0.0, ortho1.y) * line_width) + var bottom_sample2: Vector3 = pos + (Vector3(ortho2.x, 0.0, ortho2.y) * line_width) + + var top: Vector3 = top_sample1 + ((top_sample2 - top_sample1) * 0.5) + var bottom: Vector3 = bottom_sample1 + ((bottom_sample2 - bottom_sample1) * 0.5) + + var top_dir: Vector3 = (pos - top).normalized() + var bottom_dir: Vector3 = (pos - bottom).normalized() + + verts.append(pos + (top_dir * line_width)) + verts.append(pos + (bottom_dir * line_width)) + + #verts.append(top) + #verts.append(bottom) + + return verts + + +func get_quad(start_point: Vector3, end_point: Vector3, head_point: Vector3, tail_point: Vector3) -> Array[Vector3]: + var verts: Array[Vector3] = [] + + var head_to_start: Vector2 + if head_point != Vector3.ZERO: + head_to_start = Vector2(start_point.x, start_point.z) - Vector2(head_point.x, head_point.z) + var start_to_end: Vector2 = Vector2(end_point.x, end_point.z) - Vector2(start_point.x, start_point.z) + var end_to_start: Vector2 = Vector2(start_point.x, start_point.z) - Vector2(end_point.x, end_point.z) + var end_to_tail: Vector2 + if tail_point != Vector3.ZERO: + end_to_tail = Vector2(tail_point.x, tail_point.z) - Vector2(end_point.x, end_point.z) + + var first_edge: Array[Vector3] = [] + if head_point != Vector3.ZERO: + first_edge.append_array(get_edge(start_point, -head_to_start, start_to_end)) + else: + first_edge.append_array(get_edge(start_point, Vector2.ZERO, start_to_end)) + + var second_edge: Array[Vector3] = [] + if tail_point != Vector3.ZERO: + second_edge.append_array(get_edge(end_point, start_to_end, -end_to_tail)) + else: + second_edge.append_array(get_edge(end_point, start_to_end, Vector2.ZERO)) + + + verts.append(first_edge[1]) + verts.append(second_edge[1]) + verts.append(second_edge[0]) + + verts.append(second_edge[1]) + verts.append(first_edge[1]) + verts.append(first_edge[0]) + + #verts.append(first_edge[0]) + #verts.append(first_edge[1]) + #verts.append(second_edge[0]) + + #verts.append(first_edge[0]) + #verts.append(second_edge[1]) + #verts.append(first_edge[1]) + + #verts.append(first_edge[0]) + #verts.append(second_edge[0]) + #verts.append(second_edge[1]) + + #var top_left: Vector3 = start_point - (orthovec3 * line_width) + #var bottom_left: Vector3 = start_point + (orthovec3 * line_width) + #var top_right: Vector3 = end_point - (orthovec3 * line_width) + #var bottom_right: Vector3 = end_point + (orthovec3 * line_width) + + + #verts.append(top_left) + #verts.append(bottom_right) + #verts.append(bottom_left) + #verts.append(top_left) + #verts.append(top_right) + #verts.append(bottom_right) + + #print(verts) + return verts diff --git a/path_vfx.gd.uid b/path_vfx.gd.uid new file mode 100644 index 0000000..a1c732c --- /dev/null +++ b/path_vfx.gd.uid @@ -0,0 +1 @@ +uid://ca8u5ji004qkp diff --git a/path_vfx.tscn b/path_vfx.tscn new file mode 100644 index 0000000..9037a61 --- /dev/null +++ b/path_vfx.tscn @@ -0,0 +1,35 @@ +[gd_scene format=3 uid="uid://dywi5lu2c7y0f"] + +[ext_resource type="Script" uid="uid://ca8u5ji004qkp" path="res://path_vfx.gd" id="1_npwh2"] +[ext_resource type="Material" uid="uid://bqn48u5unlnmj" path="res://path_material.tres" id="2_avv77"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_avv77"] +sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) +ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) + +[sub_resource type="Sky" id="Sky_a4eki"] +sky_material = SubResource("ProceduralSkyMaterial_avv77") + +[sub_resource type="Environment" id="Environment_2hybw"] +background_mode = 2 +sky = SubResource("Sky_a4eki") +tonemap_mode = 2 +glow_enabled = true + +[node name="Node3D" type="MeshInstance3D" unique_id=401062502] +script = ExtResource("1_npwh2") +line_width = 0.4 +material = ExtResource("2_avv77") + +[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=336386562] +environment = SubResource("Environment_2hybw") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=365896800] +transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0) +shadow_enabled = true +directional_shadow_max_distance = 1.0 + +[node name="Camera3D" type="Camera3D" parent="." unique_id=2114620631] +transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4980783, 1.4475169, 1.9058113) +projection = 1 +size = 8.752 diff --git a/red.png b/red.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d201b84cc5ed299cdad670f30588c14f6fe5ea GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hEk44ofy`glX=O&z%a|x z#WAE}&f5!)f(H~B4j3HZvitC!Nz>U)E?eY_)%%