diff --git a/PCs/FSM/carding_state.gd b/PCs/FSM/carding_state.gd index 3680180..17a0c7c 100644 --- a/PCs/FSM/carding_state.gd +++ b/PCs/FSM/carding_state.gd @@ -5,10 +5,11 @@ extends HeroState func enter_state() -> void: + if hero.game_manager: + hero.game_manager.level.disable_all_tower_frames() hero.left_hand_model.visible = true hero.gauntlet_model.visible = true hero.set_card_elements_visibility(true) - #hero.left_hand.visible = true hero.carding_tool.enabled = true var tween: Tween = create_tween() tween.set_ease(Tween.EASE_OUT) diff --git a/Scenes/FlowField/flow_field.gd b/Scenes/FlowField/flow_field.gd index f32d19c..255cbf5 100644 --- a/Scenes/FlowField/flow_field.gd +++ b/Scenes/FlowField/flow_field.gd @@ -12,6 +12,7 @@ var flow_node_scene: PackedScene = preload("res://Scenes/FlowField/flow_node.tsc var nodes: Array[FlowNode] = [] var start_nodes: Array[FlowNode] = [] var goal_nodes: Array[FlowNode] = [] +var created_nodes: int = 0 func _ready() -> void: @@ -21,20 +22,22 @@ func _ready() -> void: 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 - nodes.append(new_flow_node) if node_data.type == FlowNodeData.NodeType.START: start_nodes.append(new_flow_node) elif node_data.type == FlowNodeData.NodeType.GOAL: @@ -191,6 +194,7 @@ func toggle_buildable(node: FlowNode) -> void: 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 @@ -199,6 +203,7 @@ func create_node(pos: Vector3 = Vector3.ZERO, grid_id: int = -1, grid_x: int = 0 nodes.append(node) add_child(node) node.owner = self + created_nodes += 1 return node diff --git a/Scenes/FlowField/flow_field_data.gd b/Scenes/FlowField/flow_field_data.gd index f816ed8..8eb9279 100644 --- a/Scenes/FlowField/flow_field_data.gd +++ b/Scenes/FlowField/flow_field_data.gd @@ -3,3 +3,25 @@ extends Resource @export var nodes: Array[FlowNodeData] @export var grids: int = 0 + + +func to_dict() -> Dictionary: + var dict: Dictionary = {} + for node: FlowNodeData in nodes: + dict[node.node_id] = node.to_dict() + dict["grids"] = grids + return dict + + +static func from_dict(dict: Dictionary) -> FlowFieldData: + var flow: FlowFieldData = FlowFieldData.new() + var unpopulated: Dictionary[FlowNodeData, Dictionary] = {} + for key: String in dict.keys(): + if key.is_valid_int(): + var data: FlowNodeData = FlowNodeData.from_dict(dict[key]) + flow.nodes.append(data) + unpopulated[data] = dict[key] + for key: FlowNodeData in unpopulated.keys(): + key.populate_connections(unpopulated[key], flow.nodes) + flow.grids = dict["grids"] + return flow diff --git a/Scenes/FlowField/flow_field_tool.gd b/Scenes/FlowField/flow_field_tool.gd index 3695c74..08fcbd3 100644 --- a/Scenes/FlowField/flow_field_tool.gd +++ b/Scenes/FlowField/flow_field_tool.gd @@ -29,6 +29,7 @@ var level: Level var radius: float = 0 var up_angle: float = 0 var rotate_held: bool = false +var flow_field_data: FlowFieldData func _ready() -> void: @@ -50,10 +51,10 @@ func load_zone() -> void: if level: level.queue_free() level = zone_list[selected_zone].instantiate() as Level + level.flow_field = flow_field zone_holder.add_child(level) camera.make_current() editing = true - print("set editing true") func _process(delta: float) -> void: @@ -63,12 +64,16 @@ func _process(delta: float) -> void: if hover and !raycast.is_colliding(): hover = null if selected.size() == 1 and vector_dirty: + $Position/Button.visible = true position_field.visible = true x_field.text = str(selected[0].global_position.x) y_field.text = str(selected[0].global_position.y) z_field.text = str(selected[0].global_position.z) vector_dirty = false - elif selected.size() != 1: + elif selected.size() > 1: + $Position/Button.visible = false + position_field.visible = true + elif selected.size() < 1: position_field.visible = false set_node_colors() @@ -152,6 +157,16 @@ func _on_z_field_changed(text: String) -> void: selected[0].global_position.z = float(text) +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)) + + +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)) + + func _on_create_button_pressed() -> void: flow_field.create_node() @@ -159,6 +174,14 @@ func _on_create_button_pressed() -> void: 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) + + +func create_grid_select_button(grid: int) -> void: + var button: Button = Button.new() + button.text = "Grid " + str(grid) + button.pressed.connect(select_in_grid.bind(grid)) + $VBoxContainer3.add_child(button) func _on_calculate_button_pressed() -> void: @@ -222,9 +245,13 @@ func _on_project_downwards_button_pressed() -> void: 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 @@ -241,18 +268,77 @@ func _on_save_button_pressed() -> void: for neighbor: FlowNode in node.connections: flow_node_data.connected_nodes.append(dict[neighbor]) new_flow_field_data.nodes.append(flow_node_data) - ResourceSaver.save(new_flow_field_data, save_path.text) + new_flow_field_data.grids = grid_num + + var string: String = JSON.stringify(new_flow_field_data.to_dict()) + var path: String = save_path.text + ".json" + var dir: DirAccess = DirAccess.open("user://") + if !dir.dir_exists("pathing_graphs"): + dir.make_dir("pathing_graphs") + dir.change_dir("pathing_graphs") + var save_file: FileAccess = FileAccess.open("user://pathing_graphs/" + path, FileAccess.WRITE) + save_file.store_line(string) + + +static func load_flow_field_from_disc(path: String) -> FlowFieldData: + if FileAccess.file_exists(path): + var save_file: FileAccess = FileAccess.open(path, FileAccess.READ) + var json_string: String = save_file.get_line() + var json: JSON = JSON.new() + var parse_result: Error = json.parse(json_string) + if parse_result == OK: + var dict: Dictionary = json.data + var flow_field_data: FlowFieldData = FlowFieldData.from_dict(dict) + return flow_field_data + return FlowFieldData.new() func _on_load_button_pressed() -> void: - if ResourceLoader.exists(save_path.text): - var resource: Resource = ResourceLoader.load(save_path.text) - if resource is FlowFieldData: - flow_field.load_from_data(resource) + if FileAccess.file_exists("user://pathing_graphs/" + save_path.text + ".json"): + var save_file: FileAccess = FileAccess.open("user://pathing_graphs/" + save_path.text + ".json", FileAccess.READ) + var json_string: String = save_file.get_line() + var json: JSON = JSON.new() + var parse_result: Error = json.parse(json_string) + 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) + 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!") func _on_trash_button_pressed() -> void: if flow_field: flow_field.queue_free() + 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 add_child(flow_field) + if level: + level.flow_field = flow_field + + +func _on_select_all_pressed() -> void: + selected = [] + for node: FlowNode 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: + selected.append(node) diff --git a/Scenes/FlowField/flow_field_tool.tscn b/Scenes/FlowField/flow_field_tool.tscn index 8b996c3..a44e9ec 100644 --- a/Scenes/FlowField/flow_field_tool.tscn +++ b/Scenes/FlowField/flow_field_tool.tscn @@ -68,6 +68,10 @@ layout_mode = 2 text = "2.5" placeholder_text = "gap" +[node name="SelectAll" type="Button" parent="VBoxContainer" unique_id=1460776735] +layout_mode = 2 +text = "Select All Nodes" + [node name="Connect" type="Button" parent="VBoxContainer" unique_id=330867073] layout_mode = 2 text = "Connect Nodes" @@ -123,13 +127,10 @@ size_flags_horizontal = 3 text = "Load" [node name="Position" type="HBoxContainer" parent="." unique_id=1194117729] -visible = false -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -323.0 -offset_bottom = 41.0 -grow_horizontal = 0 +anchors_preset = -1 +anchor_left = 0.4 +anchor_right = 0.6 +grow_horizontal = 2 [node name="x" type="LineEdit" parent="Position" unique_id=1634710518] layout_mode = 2 @@ -143,6 +144,14 @@ size_flags_horizontal = 3 layout_mode = 2 size_flags_horizontal = 3 +[node name="Button" type="Button" parent="Position" unique_id=362195148] +layout_mode = 2 +text = "Set Position" + +[node name="Button2" type="Button" parent="Position" unique_id=1670413853] +layout_mode = 2 +text = "Add Offset" + [node name="CameraFocus" type="Node3D" parent="." unique_id=1567712529] [node name="Camera3D" type="Camera3D" parent="CameraFocus" unique_id=1970273041] @@ -169,10 +178,19 @@ layout_mode = 2 layout_mode = 2 text = "Load zone" +[node name="VBoxContainer3" type="VBoxContainer" parent="." unique_id=179301019] +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -40.0 +offset_right = 40.0 +grow_vertical = 0 + [connection signal="pressed" from="VBoxContainer/Button" to="." method="_on_trash_button_pressed"] [connection signal="pressed" from="VBoxContainer/Create" to="." method="_on_create_button_pressed"] [connection signal="pressed" from="VBoxContainer/Delete" to="." method="_on_create_button_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/GenerateGrid" to="." method="_on_generate_grid_button_pressed"] +[connection signal="pressed" from="VBoxContainer/SelectAll" to="." method="_on_select_all_pressed"] [connection signal="pressed" from="VBoxContainer/Connect" to="." method="_on_connect_button_pressed"] [connection signal="pressed" from="VBoxContainer/ProjectDownwards" to="." method="_on_project_downwards_button_pressed"] [connection signal="pressed" from="VBoxContainer/MarkGoal" to="." method="_on_mark_goal_button_pressed"] @@ -183,7 +201,6 @@ text = "Load zone" [connection signal="pressed" from="VBoxContainer/Calculate" to="." method="_on_calculate_button_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer2/Save" to="." method="_on_save_button_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer2/Load" to="." method="_on_load_button_pressed"] -[connection signal="text_changed" from="Position/x" to="." method="_on_x_field_changed"] -[connection signal="text_changed" from="Position/y" to="." method="_on_y_field_changed"] -[connection signal="text_changed" from="Position/z" to="." method="_on_z_field_changed"] +[connection signal="pressed" from="Position/Button" to="." method="set_position"] +[connection signal="pressed" from="Position/Button2" to="." method="offset_position"] [connection signal="pressed" from="VBoxContainer2/Button" to="." method="load_zone"] diff --git a/Scenes/FlowField/flow_node.gd b/Scenes/FlowField/flow_node.gd index a5ba5cf..8dfaf29 100644 --- a/Scenes/FlowField/flow_node.gd +++ b/Scenes/FlowField/flow_node.gd @@ -7,6 +7,7 @@ extends StaticBody3D @export var buildable: bool = true 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 diff --git a/Scenes/FlowField/flow_node_data.gd b/Scenes/FlowField/flow_node_data.gd index 10b426e..ba406ba 100644 --- a/Scenes/FlowField/flow_node_data.gd +++ b/Scenes/FlowField/flow_node_data.gd @@ -7,6 +7,7 @@ enum NodeType { GOAL = 2, } +@export var node_id: int = -1 @export var position: Vector3 = Vector3.ZERO @export var type: NodeType = NodeType.STANDARD @export var buildable: bool = true @@ -15,3 +16,47 @@ enum NodeType { @export var grid_id: int = -1 @export var grid_x: int = 0 @export var grid_y: int = 0 + + +#This function cannot fill in the connection information until a complete set +#of nodes have been loaded and can be looped over +static func from_dict(dict: Dictionary) -> FlowNodeData: + var data: FlowNodeData = FlowNodeData.new() + data.node_id = dict["node_id"] + data.position.x = dict["position_x"] + data.position.y = dict["position_y"] + data.position.z = dict["position_z"] + data.type = dict["type"] + data.buildable = dict["buildable"] + data.in_grid = dict["in_grid"] + data.grid_id = dict["grid_id"] + data.grid_x = dict["grid_x"] + data.grid_y = dict["grid_y"] + return data + + +func populate_connections(dict: Dictionary, node_array: Array[FlowNodeData]) -> void: + var connections_array: Array = dict["connected_nodes"] + for x: float in connections_array: + for y: FlowNodeData in node_array: + if y.node_id == int(x): + connected_nodes.append(y) + + +func to_dict() -> Dictionary: + var dict: Dictionary = {} + var connections: Array[int] = [] + for node: FlowNodeData in connected_nodes: + connections.append(node.node_id) + dict["connected_nodes"] = connections + dict["node_id"] = node_id + dict["position_x"] = position.x + dict["position_y"] = position.y + dict["position_z"] = position.z + dict["type"] = type + dict["buildable"] = buildable + dict["in_grid"] = in_grid + dict["grid_id"] = grid_id + dict["grid_x"] = grid_x + dict["grid_y"] = grid_y + return dict diff --git a/Scripts/EnemyAI/pathing_controller.gd b/Scripts/EnemyAI/pathing_controller.gd index 1ebafaf..4cc98c1 100644 --- a/Scripts/EnemyAI/pathing_controller.gd +++ b/Scripts/EnemyAI/pathing_controller.gd @@ -26,6 +26,10 @@ func _ready() -> void: super._ready() if flow_field: next_node = flow_field.get_closest_traversable_point(character.global_position) + #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 + next_node = next_node.best_path distance_remaining += calculate_distance_to_goal(next_node) diff --git a/Scripts/cinema_cam.gd b/Scripts/cinema_cam.gd index 3d4b30b..35eec50 100644 --- a/Scripts/cinema_cam.gd +++ b/Scripts/cinema_cam.gd @@ -5,7 +5,7 @@ extends Node3D @export var cameras: Array[Camera3D] @export var pan_speed: float = 1.0 var current_cam: int = 0 -@export var does_its_thing: bool = true +@export var does_its_thing: bool = false func _ready() -> void: diff --git a/Scripts/enemy_spawner.gd b/Scripts/enemy_spawner.gd index 3c562dd..ae2b5a1 100644 --- a/Scripts/enemy_spawner.gd +++ b/Scripts/enemy_spawner.gd @@ -26,10 +26,6 @@ var path_polygon: PackedScene = preload("res://path_polygon.tscn") var game_manager: GameManager -func _ready() -> void: - create_path() - - func _process(delta: float) -> void: if enemies_to_spawn == 0: done_spawning = true diff --git a/Scripts/game.gd b/Scripts/game.gd index b4641db..2ac8c81 100644 --- a/Scripts/game.gd +++ b/Scripts/game.gd @@ -15,6 +15,7 @@ signal switch_to_main_menu var root_scene: Node var level_scene: PackedScene = load("res://Worlds/GreenPlanet/Levels/Bridge/bridge.tscn") +var level_2_scene: PackedScene = load("res://Worlds/GreenPlanet/Levels/Cave/cave.tscn") var player_scene: PackedScene = load("res://PCs/hero.tscn") var game_end_scene: PackedScene = load("res://UI/Menus/GameEndScreen/game_end_screen.tscn") var connected_players_nodes: Dictionary = {} @@ -31,6 +32,7 @@ var wave_limit: int = 20 var shop_chance: float = 0.0 var stats: RoundStats var card_gameplay: bool = false +var level_layout: FlowFieldData #TODO: Create a reference to some generic Lobby object that wraps the multiplayer players list stuff @@ -89,14 +91,23 @@ func networked_set_wave(wave_number: int) -> void: func spawn_level() -> void: - level = level_scene.instantiate() as Level + level = level_2_scene.instantiate() as Level + 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("user://pathing_graphs/level3.json")) + flow_field.calculate() + level.load_flow_field() level.game_manager = self for x: EnemySpawner in level.enemy_spawns: + x.flow_field = flow_field x.game_manager = self x.enemy_died_callback = enemy_died x.enemy_reached_goal_callback = damage_goal x.enemy_spawned.connect(increase_enemy_count) root_scene.add_child(level) + for spawner: EnemySpawner in level.enemy_spawns: + spawner.create_path() func spawn_players() -> void: @@ -174,11 +185,13 @@ func set_wave_to_spawners(wave_thing: Wave, wave_number: int) -> void: ground_spawners.append(spawner) else: air_spawners.append(spawner) + var assignment_salt: int = 0 for card: EnemyCard in wave_thing.enemy_groups: + assignment_salt += 1 if card.enemy.target_type == Data.EnemyType.LAND: - ground_spawners[NoiseRandom.randi_in_range(wave_number, 0, ground_spawners.size() - 1)].add_card(card) + ground_spawners[NoiseRandom.randi_in_range((wave_number * assignment_salt) - assignment_salt, 0, ground_spawners.size() - 1)].add_card(card) else: - air_spawners[NoiseRandom.randi_in_range(wave_number, 0, air_spawners.size() - 1)].add_card(card) + air_spawners[NoiseRandom.randi_in_range((wave_number * assignment_salt) + assignment_salt, 0, air_spawners.size() - 1)].add_card(card) func set_upcoming_wave() -> void: @@ -186,6 +199,7 @@ func set_upcoming_wave() -> void: var spawn_power: int = WaveManager.calculate_spawn_power(wave, connected_players_nodes.size()) #var new_wave: Dictionary = WaveManager.generate_wave(spawn_power, level.enemy_pool) var new_wave: Wave = WaveManager.generate_wave(spawn_power, level.enemy_pool) + #var new_wave: Wave = WaveManager.get_test_wave(level.enemy_pool) set_wave_to_spawners(new_wave, wave) temp_set_upcoming_wave(new_wave, WaveManager.calculate_pot(wave, connected_players_nodes.size())) #networked_set_upcoming_wave.rpc(new_wave, 6 + floori(spawn_power / 70.0)) diff --git a/Scripts/level.gd b/Scripts/level.gd index 791d7d9..99a2edb 100644 --- a/Scripts/level.gd +++ b/Scripts/level.gd @@ -22,13 +22,16 @@ var flow_field: FlowField func load_flow_field() -> void: - flow_field.path_updated.connect(enemy_spawns[0].update_path) + 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: if node.buildable: + x += 1 var frame: Node3D = tower_frame_scene.instantiate() tower_frames[node] = frame add_child(frame) - frame.global_position = node.global_position + frame.position = node.position func disable_all_tower_frames() -> void: diff --git a/Scripts/wave_manager.gd b/Scripts/wave_manager.gd index 9542762..d0c4ecf 100644 --- a/Scripts/wave_manager.gd +++ b/Scripts/wave_manager.gd @@ -22,6 +22,16 @@ static func calculate_pot(wave_number: int, number_of_players: int) -> int: return ceili((3.0 * number_of_players) + (2.5 * wave_number)) +static func get_test_wave(spawn_pool: Array[Enemy]) -> Wave: + var wave: Wave = Wave.new() + for x: int in 3: + var new_card: EnemyCard = EnemyCard.new() + new_card.enemy = spawn_pool[0] + new_card.rarity = Data.Rarity.COMMON + wave.enemy_groups.append(new_card) + return wave + + ## Uses a spawn power budget to "buy" cards of enemies at random selection from ## the given spawn pool, returns the resulting wave but also assigns the cards ## among the given set of enemy spawners diff --git a/Worlds/GreenPlanet/Levels/Bridge/bridge.tscn b/Worlds/GreenPlanet/Levels/Bridge/bridge.tscn index a50448f..0afc4b0 100644 --- a/Worlds/GreenPlanet/Levels/Bridge/bridge.tscn +++ b/Worlds/GreenPlanet/Levels/Bridge/bridge.tscn @@ -16,7 +16,6 @@ [ext_resource type="PackedScene" uid="uid://cmneu5o0m02l8" path="res://Worlds/GreenPlanet/Enemies/leaping_enemy.tscn" id="20_ttr7n"] [ext_resource type="PackedScene" uid="uid://c8ecof4oeng1f" path="res://BigTree1/big_tree.tscn" id="21_6tcu8"] [ext_resource type="PackedScene" uid="uid://c6isprnkaliqr" path="res://Scenes/MixingTable/remix_table.tscn" id="21_ks6qx"] -[ext_resource type="PackedScene" uid="uid://2vv6mrxiwibf" path="res://Scenes/FlowField/flow_field_tool_output.tscn" id="24_sbix1"] [ext_resource type="Script" uid="uid://cy6oj2hr8q8br" path="res://Scripts/killbox.gd" id="26_tuncf"] [ext_resource type="PackedScene" uid="uid://c2avjln5vmr0y" path="res://Scenes/Shredder/shredder.tscn" id="27_gppb4"] [ext_resource type="Texture2D" uid="uid://dqrfgw65d0sq8" path="res://Assets/Textures/bridge_map.png" id="28_6od8s"] @@ -323,54 +322,48 @@ shape = SubResource("BoxShape3D_awjk1") [node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="EnemyGoal" unique_id=1838624313] stream = ExtResource("11_l537x") -[node name="GroundSpawn" parent="." unique_id=185953135 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="GroundSpawn" parent="." unique_id=185953135 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -43.4011, 0.5, 0) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") type = 1 dest = NodePath("../EnemyGoal") enemy_path = NodePath("../Enemies") -[node name="GroundSpawn2" parent="." unique_id=1956318080 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="GroundSpawn2" parent="." unique_id=1956318080 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -40.9011, 0.5, -5) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") own_id = 1 type = 1 dest = NodePath("../EnemyGoal") enemy_path = NodePath("../Enemies") -[node name="GroundSpawn3" parent="." unique_id=1447527413 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="GroundSpawn3" parent="." unique_id=1447527413 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -40.9011, 0.5, 5) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") own_id = 2 type = 1 dest = NodePath("../EnemyGoal") enemy_path = NodePath("../Enemies") -[node name="GroundSpawn4" parent="." unique_id=623589346 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="GroundSpawn4" parent="." unique_id=623589346 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -38.4011, 0.5, 10) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") own_id = 3 type = 1 dest = NodePath("../EnemyGoal") enemy_path = NodePath("../Enemies") -[node name="GroundSpawn5" parent="." unique_id=29660902 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="GroundSpawn5" parent="." unique_id=29660902 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -38.4011, 0.5, -10) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") own_id = 4 type = 1 dest = NodePath("../EnemyGoal") enemy_path = NodePath("../Enemies") -[node name="AirSpawn" parent="." unique_id=727755035 node_paths=PackedStringArray("flow_field", "dest", "enemy_path") instance=ExtResource("3_5imwp")] +[node name="AirSpawn" parent="." unique_id=727755035 node_paths=PackedStringArray("dest", "enemy_path") instance=ExtResource("3_5imwp")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -55.3737, 19.0055, 0) leap_enemy_scene = ExtResource("20_ttr7n") -flow_field = NodePath("../FlowField2") own_id = 5 type = 2 dest = NodePath("../EnemyGoal") @@ -423,9 +416,6 @@ transform = Transform3D(-3.62805e-07, -8.3, -3.62805e-07, 0, -3.62805e-07, 8.3, layers = 4 texture = ExtResource("28_6od8s") -[node name="FlowField2" parent="." unique_id=1588891010 instance=ExtResource("24_sbix1")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.215, 1.56, 0) - [node name="Node3D" parent="." unique_id=126006136 instance=ExtResource("21_ks6qx")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30.507853, 1.00142, 15.890633) diff --git a/Worlds/GreenPlanet/Levels/Cave/cave.tscn b/Worlds/GreenPlanet/Levels/Cave/cave.tscn index 9e99ce3..2ad5200 100644 --- a/Worlds/GreenPlanet/Levels/Cave/cave.tscn +++ b/Worlds/GreenPlanet/Levels/Cave/cave.tscn @@ -6,17 +6,212 @@ [ext_resource type="PackedScene" uid="uid://bhroqr4s1qso5" path="res://Worlds/GreenPlanet/Levels/Cave/cave_level.glb" id="4_x53u6"] [ext_resource type="Script" uid="uid://dkuxg6ek5us4f" path="res://Scripts/enemy_spawner.gd" id="5_e8b6i"] [ext_resource type="Script" uid="uid://cxwtuxytavfu5" path="res://Scripts/enemy_goal.gd" id="6_n26ay"] +[ext_resource type="AudioStream" uid="uid://dknygn5eyuhxt" path="res://Audio/shot1.wav" id="7_e8b6i"] [ext_resource type="PackedScene" uid="uid://1b2ikdanl66b" path="res://Scenes/CardPrinter/card_printer.tscn" id="7_y5pxn"] [ext_resource type="PackedScene" uid="uid://7g3jev3v6d3l" path="res://Scenes/ShopStand/shop_stand.tscn" id="8_x53u6"] +[ext_resource type="Script" uid="uid://yk54owkf7pgj" path="res://Scripts/cinema_cam.gd" id="9_x53u6"] -[node name="Node3D" type="Node3D" unique_id=1915460305 node_paths=PackedStringArray("tower_path", "player_spawns", "enemy_spawns", "enemy_goals", "corpses", "printer", "shop")] +[sub_resource type="SphereShape3D" id="SphereShape3D_x53u6"] +radius = 4.5510416 + +[sub_resource type="Shader" id="Shader_6od8s"] +code = "shader_type sky; +render_mode use_debanding; + +uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); +uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); +uniform float sky_curve : hint_range(0, 1) = 0.15; +uniform float sky_energy = 1.0; // In Lux. +uniform sampler2D sky_cover : filter_linear, source_color, hint_default_black; +uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); +uniform vec4 ground_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); +uniform float ground_curve : hint_range(0, 1) = 0.02; +uniform float ground_energy = 1.0; +uniform float sun_angle_max = 30.0; +uniform float sun_curve : hint_range(0, 1) = 0.15; +uniform float exposure : hint_range(0, 128) = 1.0; + +// Wind offset direction (x and y only) +uniform vec2 wind_offset_direction = vec2(0.5, 0.1); // Control direction of offset (x, y) +uniform float wind_speed : hint_range(0.0, 25.0) = 1.0; // Speed of the noise movement over time + +// Cloud change settings +uniform bool clouds_change = true; // Whether to change the cloud layer or not +uniform float cloud_change_rate : hint_range(0.0, 5.0) = .40; // Rate at which the cloud effect changes + +// Pole blending parameters +uniform float pole_blend_shape : hint_range(0.0, 2.0) = 0.05; // How much the noise affects the blend shape +uniform float pole_blend_strength : hint_range(0, 1) = 0.05; // Control blending intensity at poles +uniform float pole_blend_brightness : hint_range(0.0, 1.0) = .3; + +// Horizon blending parameters - similar to pole blending +uniform float horizon_blend_shape : hint_range(0.0, 2.0) = 0.05; // How much the noise affects the horizon blend shape +uniform float horizon_blend_strength : hint_range(0, 1) = 0.05; // Control blending intensity at horizon +uniform float horizon_blend_brightness : hint_range(0.0, 1.0) = .3; +uniform float horizon_blend_width : hint_range(0.0, 0.5) = 0.1; // Width of the horizon blend region + +void sky() { + float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); + float c = (1.0 - v_angle / (PI * 0.5)); + vec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0)); + sky *= sky_energy; + + if (LIGHT0_ENABLED) { + float sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT0_SIZE) { + sky = LIGHT0_COLOR * LIGHT0_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE); + sky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT1_ENABLED) { + float sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT1_SIZE) { + sky = LIGHT1_COLOR * LIGHT1_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE); + sky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT2_ENABLED) { + float sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT2_SIZE) { + sky = LIGHT2_COLOR * LIGHT2_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE); + sky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT3_ENABLED) { + float sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT3_SIZE) { + sky = LIGHT3_COLOR * LIGHT3_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE); + sky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + // Sample the sky cover texture with dynamic offset (only x and y direction) + vec2 noise_coords = SKY_COORDS.xy + wind_offset_direction * wind_speed * TIME * 0.01; + + // Wrap UVs to keep tiling seamless + noise_coords = mod(noise_coords, 1.0); + + // Sample the original noise texture + vec4 sky_cover_texture = texture(sky_cover, noise_coords); + + // Sample flipped noise for Z-offset effect + vec4 flipped_noise = texture(sky_cover, vec2(noise_coords.x, 1.0 - noise_coords.y)); + + // Z blending (cloud change) logic + float cloud_blend_factor = 0.0; + if (clouds_change) { + // Use a sine wave to blend clouds smoothly over time based on the cloud_change_rate + cloud_blend_factor = 0.5 + 0.5 * sin(TIME * cloud_change_rate); + } + + // Blend between the original and flipped noise using cloud_blend_factor + vec4 blended_noise_texture = mix(sky_cover_texture, flipped_noise, cloud_blend_factor); + + // Pole blending + float base_pole_blend_factor = abs(EYEDIR.y); // Original blend factor (circular) + float noise_pole_blend = blended_noise_texture.r * pole_blend_shape; + float pole_blend_factor = smoothstep(1.0 - pole_blend_strength, 1.0, base_pole_blend_factor + noise_pole_blend); + + // Horizon blending - detect when we're near the horizon + float horizon_distance = abs(EYEDIR.y); // This will be close to 0 near the horizon + float horizon_factor = 1.0 - smoothstep(0.0, horizon_blend_width, horizon_distance); + float noise_horizon_blend = blended_noise_texture.g * horizon_blend_shape; + float horizon_blend_factor = smoothstep(1.0 - horizon_blend_strength, 1.0, horizon_factor + noise_horizon_blend); + + // Combine both blend factors (poles and horizon) + float combined_blend_factor = max(pole_blend_factor, horizon_blend_factor); + + // Blend noise with brightness value based on the combined factor + vec3 blended_noise = mix(blended_noise_texture.rgb, vec3(pole_blend_brightness), pole_blend_factor); + blended_noise = mix(blended_noise, vec3(horizon_blend_brightness), horizon_blend_factor); + + sky += (blended_noise * sky_cover_modulate.rgb) * blended_noise_texture.a * sky_cover_modulate.a * sky_energy; + + // Ground blending + c = (v_angle - (PI * 0.5)) / (PI * 0.5); + vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0)); + ground *= ground_energy; + + COLOR = mix(ground, sky, step(0.0, EYEDIR.y)) * exposure; +}" + +[sub_resource type="Gradient" id="Gradient_e8b6i"] +offsets = PackedFloat32Array(0.151786, 0.5625, 1) +colors = PackedColorArray(0, 0, 0, 1, 0.565217, 0.565217, 0.565217, 1, 1, 1, 1, 1) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_n26ay"] +noise_type = 3 +domain_warp_enabled = true + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_r4es0"] +width = 1024 +height = 1024 +noise = SubResource("FastNoiseLite_n26ay") +color_ramp = SubResource("Gradient_e8b6i") +seamless = true + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_gow2y"] +shader = SubResource("Shader_6od8s") +shader_parameter/sky_top_color = Color(0.25262, 0.408375, 0.692798, 1) +shader_parameter/sky_horizon_color = Color(0.48476, 0.638261, 0.884351, 1) +shader_parameter/sky_curve = 0.0349887 +shader_parameter/sky_energy = 1.0 +shader_parameter/sky_cover = SubResource("NoiseTexture2D_r4es0") +shader_parameter/sky_cover_modulate = Color(1, 1, 1, 1) +shader_parameter/ground_bottom_color = Color(0.121409, 0.203944, 0.437026, 1) +shader_parameter/ground_horizon_color = Color(0.486275, 0.639216, 0.882353, 1) +shader_parameter/ground_curve = 0.02 +shader_parameter/ground_energy = 1.0 +shader_parameter/sun_angle_max = 0.523599 +shader_parameter/sun_curve = 0.15 +shader_parameter/exposure = 1.0 +shader_parameter/wind_offset_direction = Vector2(0.5, 0.1) +shader_parameter/wind_speed = 0.0 +shader_parameter/clouds_change = true +shader_parameter/cloud_change_rate = 0.4 +shader_parameter/pole_blend_shape = 0.05 +shader_parameter/pole_blend_strength = 0.05 +shader_parameter/pole_blend_brightness = 0.3 +shader_parameter/horizon_blend_shape = 0.05 +shader_parameter/horizon_blend_strength = 0.05 +shader_parameter/horizon_blend_brightness = 0.3 +shader_parameter/horizon_blend_width = 0.1 + +[sub_resource type="Sky" id="Sky_t42h5"] +sky_material = SubResource("ShaderMaterial_gow2y") + +[sub_resource type="Environment" id="Environment_y23i6"] +background_mode = 2 +sky = SubResource("Sky_t42h5") +tonemap_mode = 4 +ssao_detail = 0.0 +fog_enabled = true +fog_mode = 1 +fog_density = 1.0 +fog_depth_begin = 50.0 +fog_depth_end = 200.0 + +[node name="Node3D" type="Node3D" unique_id=1915460305 node_paths=PackedStringArray("tower_path", "player_spawns", "enemy_spawns", "enemy_goals", "corpses", "cinematic_cam", "printer", "shop")] script = ExtResource("1_li03l") enemy_pool = Array[ExtResource("2_1sih4")]([ExtResource("3_y5pxn")]) tower_path = NodePath("Parents/Towers") player_spawns = [NodePath("PlayerSpawn")] -enemy_spawns = [NodePath("EnemySpawner")] +enemy_spawns = [NodePath("EnemySpawner"), NodePath("EnemySpawner2"), NodePath("EnemySpawner3")] enemy_goals = [NodePath("EnemyGoal")] corpses = NodePath("Parents/Corpses") +cinematic_cam = NodePath("CinematicCamManager") printer = NodePath("CardPrinter") shop = NodePath("ShopStand") metadata/_custom_type_script = "uid://cvejbo3srx8py" @@ -32,18 +227,61 @@ metadata/_custom_type_script = "uid://cvejbo3srx8py" [node name="PlayerSpawn" type="Node3D" parent="." unique_id=2082895124] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.7781315, 0) -[node name="EnemySpawner" type="Node3D" parent="." unique_id=1276116196] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.93684864, -30.9888) +[node name="EnemySpawner" type="Node3D" parent="." unique_id=1276116196 node_paths=PackedStringArray("enemy_path")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.40332747, -70.750275) script = ExtResource("5_e8b6i") +type = 1 +enemy_path = NodePath("../Node") metadata/_custom_type_script = "uid://dkuxg6ek5us4f" -[node name="EnemyGoal" type="Node3D" parent="." unique_id=1902271599] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.93684864, -11.24234) +[node name="EnemySpawner2" type="Node3D" parent="." unique_id=422958724 node_paths=PackedStringArray("enemy_path")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -43.008965, -0.40332747, -48.143326) +script = ExtResource("5_e8b6i") +type = 1 +enemy_path = NodePath("../Node") +metadata/_custom_type_script = "uid://dkuxg6ek5us4f" + +[node name="EnemySpawner3" type="Node3D" parent="." unique_id=2111262624 node_paths=PackedStringArray("enemy_path")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 33.049927, 3.8600483, -66.462) +script = ExtResource("5_e8b6i") +type = 1 +enemy_path = NodePath("../Node") +metadata/_custom_type_script = "uid://dkuxg6ek5us4f" + +[node name="EnemyGoal" type="Node3D" parent="." unique_id=1902271599 node_paths=PackedStringArray("audio_player")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.7545819, 0.21650457, 3.3273573) script = ExtResource("6_n26ay") +audio_player = NodePath("AudioStreamPlayer3D") metadata/_custom_type_script = "uid://cxwtuxytavfu5" +[node name="Area3D" type="Area3D" parent="EnemyGoal" unique_id=598009303] +collision_mask = 4 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="EnemyGoal/Area3D" unique_id=1884514185] +shape = SubResource("SphereShape3D_x53u6") + +[node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="EnemyGoal" unique_id=1013833370] +stream = ExtResource("7_e8b6i") + [node name="CardPrinter" parent="." unique_id=459800869 instance=ExtResource("7_y5pxn")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.507948, 0.03549701, 5.868264) [node name="ShopStand" parent="." unique_id=1287436541 instance=ExtResource("8_x53u6")] transform = Transform3D(-1, 0, 6.976922e-05, 0, 1, 0, -6.976922e-05, 0, -1, 4.381688, 0.5, 7.0162544) + +[node name="CinematicCamManager" type="Node3D" parent="." unique_id=1005738879 node_paths=PackedStringArray("path_follows", "cameras")] +script = ExtResource("9_x53u6") +path_follows = [NodePath("PathFollow3D")] +cameras = [NodePath("Camera3D")] +metadata/_custom_type_script = "uid://yk54owkf7pgj" + +[node name="Camera3D" type="Camera3D" parent="CinematicCamManager" unique_id=63929698] + +[node name="PathFollow3D" type="PathFollow3D" parent="CinematicCamManager" unique_id=1152081903] + +[node name="Node" type="Node" parent="." unique_id=1123170520] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=624876315] +environment = SubResource("Environment_y23i6") + +[connection signal="body_entered" from="EnemyGoal/Area3D" to="EnemyGoal" method="_on_area_3d_body_entered"]