diff options
47 files changed, 971 insertions, 156 deletions
diff --git a/Assets/UI/tilemap_white.png b/Assets/UI/tilemap_white.png Binary files differnew file mode 100644 index 0000000..207208e --- /dev/null +++ b/Assets/UI/tilemap_white.png diff --git a/Assets/UI/tilemap_white.png.import b/Assets/UI/tilemap_white.png.import new file mode 100644 index 0000000..bd87880 --- /dev/null +++ b/Assets/UI/tilemap_white.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlg78heamuf5g" +path="res://.godot/imported/tilemap_white.png-24d00e6fec9bf913023169274ca175c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/UI/tilemap_white.png" +dest_files=["res://.godot/imported/tilemap_white.png-24d00e6fec9bf913023169274ca175c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Game/Client.gd b/Game/Client.gd index 77ce500..3d9cf34 100644 --- a/Game/Client.gd +++ b/Game/Client.gd @@ -1,37 +1,50 @@ extends Node -var state: State +signal stage_state_changed(state: State) + +var state: State : + set(value): + state = value + stage_state_changed.emit(value) + var stage: Stage -var player: Player = Player.new() +var player: Player -func update_player(local_player: Player): - player = local_player - Network.players[multiplayer.get_unique_id()] = local_player +func _ready(): + player = Player.new() + player.id = multiplayer.get_unique_id() + + +func update_player(): + Network.players[multiplayer.get_unique_id()] = player + Network.update_player.rpc(Network.to_rpc_object(player)) func initialize_stage(current_stage: Stage): stage = current_stage - player = Player.new() func place_tower(tower: Tower, position: Vector2): - tower.owner_id = multiplayer.get_unique_id() + var network_id = multiplayer.get_unique_id() + tower.owner_id = network_id + tower.set_multiplayer_authority(network_id) + tower.name = "Tower@" + str(network_id) + "@" + str(Time.get_ticks_usec()) stage.place_tower(tower, position) Network.place_tower.rpc(Network.to_rpc_object(tower), position) player.towers[position] = tower player.score += 1 - update_player(player) + update_player() func remove_tower(tower: Tower): if tower.owner_id == multiplayer.get_unique_id(): destroy_tower(tower) player.score -= 1 - update_player(player) + update_player() func destroy_tower(tower: Tower): @@ -39,7 +52,7 @@ func destroy_tower(tower: Tower): Network.destroy_tower.rpc(Network.to_rpc_object(tower)) player.towers.erase(tower.global_position) - update_player(player) + update_player() func select_tower(tower: Tower): @@ -54,10 +67,15 @@ func deselect_tower(): func spawn_unit(unit: Unit, spawn: Spawn): + var network_id = multiplayer.get_unique_id() + unit.owner_id = network_id + unit.set_multiplayer_authority(network_id) + unit.name = "Unit@" + str(network_id) + "@" + str(Time.get_ticks_usec()) + unit.global_position = spawn.spawn_position unit.target = spawn.next_node - unit.hp = 20000 - unit.speed = randi_range(50, 150) + unit.hp = randi_range(50, 150) #20000b + unit.speed = randi_range(100, 150) stage.spawn_unit(unit, spawn) Network.spawn_unit.rpc(Network.to_rpc_object(unit), Network.to_rpc_object(spawn)) diff --git a/Game/Network.gd b/Game/Network.gd index 021de69..9349245 100644 --- a/Game/Network.gd +++ b/Game/Network.gd @@ -1,26 +1,29 @@ extends Node +signal players_changed + var players = {} func _ready(): multiplayer.connected_to_server.connect(_on_connected_to_server) multiplayer.peer_connected.connect(_on_peer_connected) + multiplayer.peer_disconnected.connect(_on_peer_disconnected) multiplayer.allow_object_decoding = true -func host_game(): +func host_game(port): var peer = ENetMultiplayerPeer.new() - peer.create_server(1234, 2) + peer.create_server(int(port)) multiplayer.multiplayer_peer = peer players[1] = Client.player -func join_game(): +func join_game(ip, port): var peer = ENetMultiplayerPeer.new() - peer.create_client("127.0.0.1", 1234) + peer.create_client(ip, int(port)) multiplayer.multiplayer_peer = peer @@ -31,6 +34,23 @@ func _on_connected_to_server(): func _on_peer_connected(id): print("peer connected: ", id) add_to_players.rpc(to_rpc_object(Client.player)) + # TODO: add existing towers to new peers + +func _on_peer_disconnected(id): + print("peer disconnected: ", id) + # TODO: move towers owned by peer to host + + if id == 1: + get_tree().change_scene_to_file("res://UI/Lobby.tscn") + + +func get_ordered_player_ids(): + var keys = players.keys() + keys.sort_custom(func(a, b): + return int(str(a).substr(0, 8)) < int(str(b).substr(0, 8)) + ) + + return keys func to_rpc_object(object: Variant): @@ -42,6 +62,8 @@ func to_rpc_object(object: Variant): remote_object[property] = {} for key in object[property]: remote_object[property][key] = to_rpc_object(object[property][key]) + elif property_class is String and property_class.begins_with("node://"): + remote_object[property] = object[property] elif property_class: remote_object[property] = to_rpc_object(object[property]) else: @@ -66,6 +88,12 @@ func from_rpc_object(remote_object: Dictionary, remote_class_path: String): if object[property] is Dictionary: for key in object[property]: object[property][key] = from_rpc_object(remote_object[property], property_class) + elif property_class is String and property_class.begins_with("node://"): + var node_path = property_class.substr(7) # after node:// + object.remove_child(object.get_node(node_path)) + remote_object[property].name = node_path.get_basename() + object[property] = remote_object[property] + object.add_child(remote_object[property]) elif property_class: object[property] = from_rpc_object(remote_object[property], property_class) else: @@ -94,14 +122,37 @@ func merge_with_rpc_object(object: Variant, remote_object: Dictionary): func add_to_players(remote_player: Dictionary): var id = multiplayer.get_remote_sender_id() var player = from_rpc_object(remote_player, "res://Game/Player.gd") + player.id = id players[id] = player + players_changed.emit() @rpc("call_local", "any_peer") func update_player(remote_player: Dictionary): var id = multiplayer.get_remote_sender_id() var player = merge_with_rpc_object(players[id], remote_player) + player.id = id players[id] = player + players_changed.emit() + + +@rpc("any_peer", "reliable") +func update_node(node_path: NodePath, properties: Dictionary): + var root_node = get_tree().root.get_node(node_path) + + for property in properties: + if property.contains(":"): + var child_node = get_nested_node_and_property(root_node, property) + child_node.node[child_node.property] = properties[property] + else: + root_node[property] = properties[property] + +func get_nested_node_and_property(node: Node, property: String) -> Dictionary: + if property.contains(":"): + var nested_node_path = property.substr(0, property.find(":")) + var nested_property = property.substr(property.find(":") + 1) + return get_nested_node_and_property(node.get_node(nested_node_path), nested_property) + return { "node": node, "property": property } @rpc("any_peer") @@ -129,4 +180,25 @@ func spawn_unit(remote_unit: Dictionary, remote_spawn: Dictionary): var unit = from_rpc_object(remote_unit, "res://Units/Unit.tscn") var spawn = from_rpc_object(remote_spawn, "res://Stages/Paths/Spawn.tscn") + var remote_id = multiplayer.get_remote_sender_id() + unit.owner_id = remote_id + unit.set_multiplayer_authority(remote_id) + Client.stage.spawn_unit(unit, spawn) + +@rpc("any_peer") +func remove_unit(remote_unit_node_path): + var unit = get_tree().current_scene.get_node_or_null(remote_unit_node_path) + if unit: + unit.queue_free() + +@rpc("any_peer") +func update_unit(remote_unit_node_path, data): + var unit: Unit = get_tree().current_scene.get_node_or_null(remote_unit_node_path) + if unit: + if "position" in data: + unit.position = data.position + unit.hp = data.hp + if "sprite" in data: + unit.get_node("Sprite2D").self_modulate = data.sprite.self_modulate + diff --git a/Game/Player.gd b/Game/Player.gd index 2726929..43856d5 100644 --- a/Game/Player.gd +++ b/Game/Player.gd @@ -2,6 +2,10 @@ class_name Player extends Resource +signal score_changed + +var id := 1 + var towers: Dictionary : set(value): towers = value @@ -9,12 +13,24 @@ var towers: Dictionary : var score: int : set(value): score = value - Client.stage.hud.score.text = str(score) + score_changed.emit() var units: Array[Unit] +func get_color(): + if id == 1: + return Color("#fff") + + var rng = RandomNumberGenerator.new() + rng.seed = id + + @warning_ignore("integer_division") + return Color(rng.randf(), rng.randf(), rng.randf()) + + func get_rpc_properties() -> Dictionary: return { + "id": null, "score": null, } diff --git a/Game/Selection/MultiSelectArea.tscn b/Game/Selection/MultiSelectArea.tscn new file mode 100644 index 0000000..9e6bd43 --- /dev/null +++ b/Game/Selection/MultiSelectArea.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://bmi8eb80wghjs"] + +[ext_resource type="Script" path="res://Game/Selection/multi_select_area.gd" id="1_g76x3"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_8io84"] + +[node name="MultiSelectArea" type="Area2D"] +collision_layer = 0 +collision_mask = 0 +script = ExtResource("1_g76x3") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_8io84") diff --git a/Game/Selection/SelectableArea.tscn b/Game/Selection/SelectableArea.tscn new file mode 100644 index 0000000..3f64dec --- /dev/null +++ b/Game/Selection/SelectableArea.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=2 format=3 uid="uid://cqktpc8c7ecn3"] + +[ext_resource type="Script" path="res://Game/Selection/selectable_area.gd" id="1_8w2y0"] + +[node name="SelectableArea" type="Area2D"] +collision_layer = 32 +collision_mask = 16 +script = ExtResource("1_8w2y0") + +[connection signal="area_entered" from="." to="." method="_on_area_entered"] +[connection signal="area_exited" from="." to="." method="_on_area_exited"] +[connection signal="input_event" from="." to="." method="_on_input_event"] +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/Game/Selection/SelectionRectangle.tscn b/Game/Selection/SelectionRectangle.tscn new file mode 100644 index 0000000..61517d0 --- /dev/null +++ b/Game/Selection/SelectionRectangle.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=3 format=3 uid="uid://ic2hc7gr27p3"] + +[ext_resource type="Script" path="res://Game/Selection/selection_rectangle.gd" id="1_on0pa"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_nq6xv"] +size = Vector2(1, 1) + +[node name="SelectionRectangle" type="Area2D" groups=["selection_rectangle"]] +z_index = 1 +collision_layer = 16 +collision_mask = 32 +script = ExtResource("1_on0pa") +color_background = Color(0.0823529, 0.392157, 0.203922, 0.392157) +color_border = Color(0.0823529, 1, 0.203922, 0.501961) + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_nq6xv") + +[connection signal="area_entered" from="." to="." method="_on_area_entered"] +[connection signal="area_exited" from="." to="." method="_on_area_exited"] diff --git a/Game/Selection/multi_select_area.gd b/Game/Selection/multi_select_area.gd new file mode 100644 index 0000000..9b2e8ce --- /dev/null +++ b/Game/Selection/multi_select_area.gd @@ -0,0 +1,19 @@ +extends Area2D + + +signal select(nodes: Array) + + +func _ready() -> void: + var camera_rect = Client.stage.get_node("Camera").get_rect() + + global_position = camera_rect.position + camera_rect.size / 2 + + $CollisionShape2D.shape.size = camera_rect.size + + +func _process(_delta: float) -> void: + var nodes = get_overlapping_areas() + get_overlapping_bodies() + if nodes.size() > 0: + select.emit(nodes) + queue_free() diff --git a/Game/Selection/selectable_area.gd b/Game/Selection/selectable_area.gd new file mode 100644 index 0000000..59d4be7 --- /dev/null +++ b/Game/Selection/selectable_area.gd @@ -0,0 +1,29 @@ +extends Area2D + + +signal hover_enter +signal hover_exit +signal select(event: InputEvent) + + +func _on_area_entered(_area: Area2D) -> void: + hover_enter.emit() + + +func _on_area_exited(_area: Area2D) -> void: + hover_exit.emit() + + +func _on_mouse_entered() -> void: + if not get_tree().get_first_node_in_group("selection_rectangle").is_active: + hover_enter.emit() + + +func _on_mouse_exited() -> void: + if not get_tree().get_first_node_in_group("selection_rectangle").is_active: + hover_exit.emit() + + +func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void: + if event.is_action_pressed("select"): + select.emit(event) diff --git a/Game/Selection/selection_rectangle.gd b/Game/Selection/selection_rectangle.gd new file mode 100644 index 0000000..a57f25b --- /dev/null +++ b/Game/Selection/selection_rectangle.gd @@ -0,0 +1,51 @@ +extends Area2D + + +@export_group("Color", "color") +@export var color_background: Color +@export var color_border: Color + +var anchor: Vector2 = Vector2.ZERO +var size: Vector2 = Vector2.ZERO : + set(value): + size = value + $CollisionShape2D.position = size / 2 + $CollisionShape2D.shape.size = abs(size) + +var is_active: bool : + get(): + return abs(size) > Vector2(1, 1) + + +func _process(_delta: float) -> void: + if Input.is_action_just_pressed("select"): + anchor = get_global_mouse_position() + global_position = anchor + + if Input.is_action_pressed("select"): + size = get_global_mouse_position() - anchor + if is_active: + queue_redraw() + + if is_active and Input.is_action_just_released("select"): + for area in get_overlapping_areas(): + if "is_selected" in area.get_parent(): + area.get_parent().is_selected = true + + size = Vector2.ZERO + queue_redraw() + + +func _draw(): + if is_active: + var rect = Rect2(Vector2.ZERO, size) + draw_rect(rect, color_background) + draw_rect(rect, color_border, false, 2.0) + + +func _on_area_entered(area: Area2D) -> void: + area.get_parent().is_hovered = true + + +func _on_area_exited(area: Area2D) -> void: + area.get_parent().is_hovered = false diff --git a/Game/States/Build/BuilderElement.gd b/Game/States/Build/BuilderElement.gd index 8315792..bb10b06 100644 --- a/Game/States/Build/BuilderElement.gd +++ b/Game/States/Build/BuilderElement.gd @@ -67,6 +67,9 @@ func _process(_delta): if global_position != previous_position: queue_redraw() + + # TODO: rpc to other peers + # TODO: only as preview not as blocking to build func can_build(): diff --git a/Game/States/Build/StateBuild.gd b/Game/States/Build/StateBuild.gd index b92020b..d2fb39d 100644 --- a/Game/States/Build/StateBuild.gd +++ b/Game/States/Build/StateBuild.gd @@ -7,19 +7,6 @@ static var current_builder_element: BuilderElement func _state_enter(): %BuildGrid.visible = true - var builder_element_scene = preload("res://Game/States/Build/BuilderElement.tscn") - var builder_element = builder_element_scene.instantiate() - - var tower = preload("res://Towers/Tower.tscn").instantiate() as Tower - tower.attack_range = [ - Client.stage.map.tile_set.tile_size.x * 2, - Client.stage.map.tile_set.tile_size.x * 3, - Client.stage.map.tile_set.tile_size.x * 4, - ].pick_random() - - builder_element.element = tower - get_tree().current_scene.add_child(builder_element) - current_builder_element = builder_element func _state_exit(): @@ -28,9 +15,8 @@ func _state_exit(): func _state_input(event: InputEvent): if event.is_action_pressed("builder_tower_select"): - get_viewport().set_input_as_handled() - if current_builder_element.can_build(): + if current_builder_element and current_builder_element.can_build(): var placed_tower = current_builder_element.element.duplicate() as Tower Client.place_tower(placed_tower, current_builder_element.global_position) @@ -42,9 +28,13 @@ func _state_input(event: InputEvent): if not Input.is_action_pressed("builder_tower_place_keep"): current_builder_element.queue_free() - set_state("StateDefault") + current_builder_element = null - if event.is_action_pressed("builder_cancel") or event.is_action_pressed("build_mode_start"): + if event.is_action_pressed("build_mode_start"): get_viewport().set_input_as_handled() - current_builder_element.queue_free() + + if current_builder_element: + current_builder_element.queue_free() + current_builder_element = null + set_state("StateDefault") diff --git a/Game/States/Default/StateDefault.gd b/Game/States/Default/StateDefault.gd index 26384c5..1d2dd9f 100644 --- a/Game/States/Default/StateDefault.gd +++ b/Game/States/Default/StateDefault.gd @@ -4,9 +4,11 @@ extends State func _state_input(event: InputEvent) -> void: if event.is_action_pressed("build_mode_start"): - set_state("StateBuild") + set_state("StateBuild") - if event.is_action_pressed("builder_tower_select"): + # deselect + if event.is_action_pressed("select"): + # if not multi selecting if not event.is_double_click() and not Input.is_action_pressed("select_multiple"): if Tower.selected_towers: for tower in Tower.selected_towers.duplicate(): @@ -2,4 +2,7 @@ Make Tower yourself Power / Range / Speed - per cost and upgradelevel rpg-like points to distribute Give them abilities/skills Splash yes/no + splash range - could only be available at tower upgrade level 2+ -exp system for towers?? +exp system for towers?? - transfer xp to other selected tower that is over max_xp usable + +Add Components to Towers +Pre-Build Tower Configurations diff --git a/Stages/Stage.gd b/Stages/Stage.gd index fec5b76..963ee46 100644 --- a/Stages/Stage.gd +++ b/Stages/Stage.gd @@ -37,24 +37,17 @@ func _ready() -> void: func place_tower(tower: Tower, position: Vector2): - if tower.owner_id != 1: - var id = str(tower.owner_id) - tower.get_node("Sprite2D").modulate = Color( - int(id.substr(0, 3)) / 255, - int(id.substr(3, 3)) / 255, - int(id.substr(6, 3)) / 255 - ) + var player: Player = Network.players[tower.owner_id] + tower.get_node("Sprite2D").modulate = player.get_color() tower.global_position = position fill_tower_region(tower, true) towers.add_child(tower) - path_grid_changed.emit() func destroy_tower(tower: Tower): fill_tower_region(tower, false) tower.queue_free() - path_grid_changed.emit() func fill_tower_region(tower: Tower, solid = true): @@ -76,8 +69,12 @@ func fill_tower_region(tower: Tower, solid = true): ), solid ) + path_grid_changed.emit() @warning_ignore("shadowed_variable") func spawn_unit(unit: Unit, _spawn: Spawn): + var player: Player = Network.players[unit.owner_id] + unit.get_node("Sprite2D").modulate = player.get_color() + units.add_child(unit) diff --git a/Stages/world.gd b/Stages/world.gd index b5a4daa..0549a1c 100644 --- a/Stages/world.gd +++ b/Stages/world.gd @@ -19,16 +19,3 @@ func _input(event: InputEvent): var scene = preload("res://Units/Unit.tscn") var unit = scene.instantiate() Client.spawn_unit(unit, %Spawn) - - if event.is_action_pressed("spawn_box_toggle"): - hud.spawn_box.visible = not hud.spawn_box.visible - - -func _on_build_mode_button_gui_input(event: InputEvent) -> void: - if event.is_action_pressed("builder_tower_select"): - $StateManager.set_state("StateBuild") - - -func _on_spawner_box_button_gui_input(event: InputEvent) -> void: - if event.is_action_pressed("select"): - hud.spawn_box.visible = not hud.spawn_box.visible diff --git a/Stages/world.tscn b/Stages/world.tscn index d1072e1..e53d874 100644 --- a/Stages/world.tscn +++ b/Stages/world.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=15 format=4 uid="uid://bl65jllb3e2py"] +[gd_scene load_steps=16 format=4 uid="uid://bl65jllb3e2py"] [ext_resource type="Texture2D" uid="uid://b1b18rd0tqbar" path="res://core_outdoor.png" id="1_luil3"] [ext_resource type="Script" path="res://Stages/world.gd" id="1_o88ua"] [ext_resource type="PackedScene" uid="uid://bylx30cweulmk" path="res://UI/HUD.tscn" id="2_v3f6l"] [ext_resource type="PackedScene" uid="uid://of5ggu6lifwy" path="res://Stages/Paths/Spawn.tscn" id="3_f2sda"] [ext_resource type="PackedScene" uid="uid://2lt8m7df0e2u" path="res://Stages/Paths/Goal.tscn" id="5_dp16q"] +[ext_resource type="PackedScene" uid="uid://ic2hc7gr27p3" path="res://Game/Selection/SelectionRectangle.tscn" id="6_7dk4w"] [ext_resource type="PackedScene" uid="uid://d0ukgoppkh1fn" path="res://Stages/Paths/PathNode.tscn" id="6_lh0f6"] [ext_resource type="PackedScene" uid="uid://t8feyd2giabm" path="res://UI/Camera.tscn" id="6_yijl8"] [ext_resource type="PackedScene" uid="uid://by1x56w21o165" path="res://Towers/Tower.tscn" id="7_5o3d3"] @@ -1958,6 +1959,8 @@ zoom = Vector2(1.5, 1.5) drag_horizontal_enabled = true drag_vertical_enabled = true +[node name="SelectionRectangle" parent="." instance=ExtResource("6_7dk4w")] + [node name="Paths" type="Node" parent="."] [node name="Spawn" parent="Paths" node_paths=PackedStringArray("next_node") instance=ExtResource("3_f2sda")] diff --git a/Towers/Assets/attack-component.png b/Towers/Assets/attack-component.png Binary files differnew file mode 100644 index 0000000..bb9250c --- /dev/null +++ b/Towers/Assets/attack-component.png diff --git a/Towers/Assets/attack-component.png.import b/Towers/Assets/attack-component.png.import new file mode 100644 index 0000000..ef9a73b --- /dev/null +++ b/Towers/Assets/attack-component.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gbknvb38euuq" +path="res://.godot/imported/attack-component.png-648f34932bc6754ec1b1094b76b170bc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Towers/Assets/attack-component.png" +dest_files=["res://.godot/imported/attack-component.png-648f34932bc6754ec1b1094b76b170bc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Towers/Assets/attack-component.png~ b/Towers/Assets/attack-component.png~ Binary files differnew file mode 100644 index 0000000..e2f80bc --- /dev/null +++ b/Towers/Assets/attack-component.png~ diff --git a/Towers/Assets/burn-component.png b/Towers/Assets/burn-component.png Binary files differnew file mode 100644 index 0000000..33f9f7e --- /dev/null +++ b/Towers/Assets/burn-component.png diff --git a/Towers/Assets/burn-component.png.import b/Towers/Assets/burn-component.png.import new file mode 100644 index 0000000..0783a55 --- /dev/null +++ b/Towers/Assets/burn-component.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2djpswd6sgng" +path="res://.godot/imported/burn-component.png-c625b666f5c33be88ffb5514acf984cb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Towers/Assets/burn-component.png" +dest_files=["res://.godot/imported/burn-component.png-c625b666f5c33be88ffb5514acf984cb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Towers/Assets/burn-component.png~ b/Towers/Assets/burn-component.png~ Binary files differnew file mode 100644 index 0000000..c9c1619 --- /dev/null +++ b/Towers/Assets/burn-component.png~ diff --git a/Towers/Assets/frost-component.png b/Towers/Assets/frost-component.png Binary files differnew file mode 100644 index 0000000..e1c12f7 --- /dev/null +++ b/Towers/Assets/frost-component.png diff --git a/Towers/Assets/frost-component.png.import b/Towers/Assets/frost-component.png.import new file mode 100644 index 0000000..d22126b --- /dev/null +++ b/Towers/Assets/frost-component.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ba3dmlce1wv2p" +path="res://.godot/imported/frost-component.png-e1bd3aee287cb92be940679bf3553e5f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Towers/Assets/frost-component.png" +dest_files=["res://.godot/imported/frost-component.png-e1bd3aee287cb92be940679bf3553e5f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Towers/Assets/range-component.png b/Towers/Assets/range-component.png Binary files differnew file mode 100644 index 0000000..e5ef51e --- /dev/null +++ b/Towers/Assets/range-component.png diff --git a/Towers/Assets/range-component.png.import b/Towers/Assets/range-component.png.import new file mode 100644 index 0000000..c626726 --- /dev/null +++ b/Towers/Assets/range-component.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dx07y4scyi5a1" +path="res://.godot/imported/range-component.png-5b23d157d3796cb80d7b2edb7addabe5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Towers/Assets/range-component.png" +dest_files=["res://.godot/imported/range-component.png-5b23d157d3796cb80d7b2edb7addabe5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Towers/Assets/range-component.png~ b/Towers/Assets/range-component.png~ Binary files differnew file mode 100644 index 0000000..e1c12f7 --- /dev/null +++ b/Towers/Assets/range-component.png~ diff --git a/Towers/Tower.gd b/Towers/Tower.gd index f3b6667..54687fd 100644 --- a/Towers/Tower.gd +++ b/Towers/Tower.gd @@ -27,14 +27,11 @@ var is_hovered = false : var mobs_in_range: Array = [] -var selection_area: Area2D +#var selection_area: Area2D # rpc owner id var owner_id = 1 -# unique shared id on the network on all clients -var network_id - @export var attack_range: int = 32 @export var attack_power: int = 1 @export var attack_speed: int = 1 @@ -74,12 +71,12 @@ func _process(_delta: float) -> void: shoot() $ShootCooldown.start() - if selection_area and is_instance_valid(selection_area): - var bodies = selection_area.get_overlapping_bodies() - if bodies.size() > 0: - selection_area.queue_free() - for body in bodies: - Client.select_tower(body) + #if selection_area and is_instance_valid(selection_area): + #var bodies = selection_area.get_overlapping_bodies() + #if bodies.size() > 0: + #selection_area.queue_free() + #for body in bodies: + #Client.select_tower(body) func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int): @@ -87,25 +84,7 @@ func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int): if owner_id != multiplayer.get_unique_id(): return - if Client.state is StateDefault: - if event.is_action_pressed("builder_tower_select"): - Client.select_tower(self) - - if event is InputEventMouseButton: - if event.is_double_click(): - selection_area = Area2D.new() - selection_area.position = ( - Client.stage.get_node("Camera").get_rect().position + - Client.stage.get_node("Camera").get_rect().size / 2 - ) - selection_area.set_collision_mask_value(3, true) - var collision_shape = CollisionShape2D.new() - var shape = RectangleShape2D.new() - shape.size = Client.stage.get_node("Camera").get_rect().size - collision_shape.shape = shape - selection_area.add_child(collision_shape) - get_tree().current_scene.add_child(selection_area) - + if Client.state is StateBuild: if event.is_action_pressed("builder_cancel"): Client.remove_tower(self) @@ -114,22 +93,45 @@ func _on_range_body_entered(body: Node2D) -> void: mobs_in_range.append(body) func _on_range_body_exited(body: Node2D) -> void: - mobs_in_range.remove_at(mobs_in_range.find(body)) + mobs_in_range.erase(body) -func _on_mouse_entered() -> void: +func _on_selectable_area_hover_enter() -> void: is_hovered = true -func _on_mouse_exited() -> void: +func _on_selectable_area_hover_exit() -> void: is_hovered = false +func _on_selectable_area_select(event: InputEvent) -> void: + # disable remote select for now + if owner_id != multiplayer.get_unique_id(): + return + + if Client.state is StateDefault: + Client.select_tower(self) + + if event.is_double_click(): + var selection_area = preload("res://Game/Selection/MultiSelectArea.tscn").instantiate() + selection_area.set_collision_mask_value(3, true) + selection_area.select.connect(func(nodes): + for node in nodes: + Client.select_tower(node) + ) + get_tree().current_scene.add_child(selection_area) + + + func is_melee_range(): return attack_range <= (Client.stage.map.tile_set.tile_size.x * 2) func shoot(): - var target = mobs_in_range.pick_random() as Unit + if get_multiplayer_authority() != multiplayer.get_unique_id(): + # TODO: do shoot animation, but don't subtract hp + return + + var target = mobs_in_range[0] as Unit if is_melee_range(): target.set_hp(target.hp - 1) @@ -158,7 +160,7 @@ func _on_tree_exiting() -> void: func get_rpc_properties() -> Dictionary: return { + "name": null, "global_position": null, "owner_id": null, - "network_id": null, } diff --git a/Towers/Tower.tscn b/Towers/Tower.tscn index dfb2364..4177a3a 100644 --- a/Towers/Tower.tscn +++ b/Towers/Tower.tscn @@ -1,13 +1,17 @@ -[gd_scene load_steps=5 format=3 uid="uid://by1x56w21o165"] +[gd_scene load_steps=7 format=3 uid="uid://by1x56w21o165"] [ext_resource type="Script" path="res://Towers/Tower.gd" id="1_axo1d"] [ext_resource type="Texture2D" uid="uid://b1b18rd0tqbar" path="res://core_outdoor.png" id="1_mrep8"] +[ext_resource type="PackedScene" uid="uid://cqktpc8c7ecn3" path="res://Game/Selection/SelectableArea.tscn" id="3_57d5u"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_atm5x"] size = Vector2(31, 31) [sub_resource type="CircleShape2D" id="CircleShape2D_qa8kt"] +[sub_resource type="RectangleShape2D" id="RectangleShape2D_312i7"] +size = Vector2(32, 32) + [node name="Tower" type="StaticBody2D"] y_sort_enabled = true collision_layer = 4 @@ -38,9 +42,18 @@ shape = SubResource("CircleShape2D_qa8kt") [node name="ShootCooldown" type="Timer" parent="."] one_shot = true +[node name="SelectableArea" parent="." instance=ExtResource("3_57d5u")] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="SelectableArea"] +position = Vector2(16, 16) +shape = SubResource("RectangleShape2D_312i7") + [connection signal="input_event" from="." to="." method="_on_input_event"] [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] [connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] [connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"] [connection signal="body_entered" from="Range" to="." method="_on_range_body_entered"] [connection signal="body_exited" from="Range" to="." method="_on_range_body_exited"] +[connection signal="hover_enter" from="SelectableArea" to="." method="_on_selectable_area_hover_enter"] +[connection signal="hover_exit" from="SelectableArea" to="." method="_on_selectable_area_hover_exit"] +[connection signal="select" from="SelectableArea" to="." method="_on_selectable_area_select"] diff --git a/UI/GameMenu.tscn b/UI/GameMenu.tscn new file mode 100644 index 0000000..cf6646e --- /dev/null +++ b/UI/GameMenu.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://c13v4wmjm4sev"] + +[ext_resource type="Script" path="res://UI/game_menu.gd" id="1_frq7d"] + +[node name="GameMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_frq7d") @@ -5,3 +5,42 @@ extends CanvasLayer @onready var score: Label = %Score @onready var tower: Label = %Tower @onready var spawn_box: Control = %SpawnBox +@onready var players_list: PanelContainer = %PlayersList + + +func _ready(): + Client.player.score_changed.connect(func(): + score.text = str(Client.player.score) + ) + + Client.stage_state_changed.connect(func(state: State): + if state is StateBuild: + $TowerConfigurationsContainer.visible = true + else: + $TowerConfigurationsContainer.visible = false + ) + + +func _input(event: InputEvent): + if event.is_action_pressed("spawn_box_toggle"): + spawn_box.visible = not spawn_box.visible + if event.is_action_pressed("players_list_toggle"): + players_list.visible = not players_list.visible + + +func _on_build_mode_button_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + if Client.state is StateDefault: + get_tree().current_scene.get_node("StateManager").set_state("StateBuild") + elif Client.state is StateBuild: + get_tree().current_scene.get_node("StateManager").set_state("StateDefault") + + +func _on_spawner_box_button_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + spawn_box.visible = not spawn_box.visible + + +func _on_player_list_button_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + players_list.visible = not players_list.visible diff --git a/UI/HUD.tscn b/UI/HUD.tscn index b73fc34..b4756cd 100644 --- a/UI/HUD.tscn +++ b/UI/HUD.tscn @@ -1,10 +1,10 @@ -[gd_scene load_steps=17 format=3 uid="uid://bylx30cweulmk"] +[gd_scene load_steps=24 format=3 uid="uid://bylx30cweulmk"] [ext_resource type="Script" path="res://UI/HUD.gd" id="1_2bu0v"] -[ext_resource type="Texture2D" uid="uid://c7ntdvxvv16io" path="res://Assets/UI/key_e.png" id="1_d2guw"] -[ext_resource type="Texture2D" uid="uid://hljlcokgys6y" path="res://Assets/UI/key_r.png" id="2_b00ni"] +[ext_resource type="Texture2D" uid="uid://dlg78heamuf5g" path="res://Assets/UI/tilemap_white.png" id="2_dyehp"] [ext_resource type="PackedScene" uid="uid://x6kohecnw7f5" path="res://UI/SpawnButton.tscn" id="3_7eaea"] [ext_resource type="Texture2D" uid="uid://up1rtweit3ut" path="res://Assets/Mobs/angesnow-menu01.png" id="4_w7sef"] +[ext_resource type="Theme" uid="uid://c7f1ftrx53ag1" path="res://theme.tres" id="5_121ry"] [ext_resource type="Texture2D" uid="uid://dq2i36oe1wj0m" path="res://Assets/Mobs/mob-pressed.png" id="5_xcxr8"] [ext_resource type="Texture2D" uid="uid://dnkr5y0cfxu68" path="res://Assets/Mobs/mob-hovered.png" id="6_4go2d"] [ext_resource type="Texture2D" uid="uid://dsy7k2v5fhh6v" path="res://Assets/Mobs/angesnow-front.png" id="7_ba5tw"] @@ -15,10 +15,30 @@ [ext_resource type="Texture2D" uid="uid://byrx3c087exvb" path="res://Assets/Mobs/windeye-menu01.png" id="12_20egp"] [ext_resource type="Texture2D" uid="uid://coiiq1yaonxeg" path="res://Assets/Mobs/windeye-front.png" id="13_iq5a7"] [ext_resource type="Texture2D" uid="uid://dr02nqmrnciy0" path="res://Assets/Mobs/mob.png" id="14_t3qlu"] +[ext_resource type="PackedScene" uid="uid://cxd6c4kbnk04c" path="res://UI/PlayersList.tscn" id="16_mq4um"] +[ext_resource type="PackedScene" uid="uid://c05aq7xd4kx1p" path="res://UI/TowerConfiguration.tscn" id="17_1c5dq"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3rjde"] bg_color = Color(0, 0, 0, 0.54902) +[sub_resource type="AtlasTexture" id="AtlasTexture_byi0r"] +atlas = ExtResource("2_dyehp") +region = Rect2(392, 69, 13, 13) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dixt2"] +atlas = ExtResource("2_dyehp") +region = Rect2(341, 35, 13, 13) + +[sub_resource type="AtlasTexture" id="AtlasTexture_tuk1j"] +atlas = ExtResource("2_dyehp") +region = Rect2(443, 35, 13, 13) + +[sub_resource type="InputEventAction" id="InputEventAction_t6x4q"] +action = &"spawn_unit" + +[sub_resource type="Shortcut" id="Shortcut_i6rmj"] +events = [SubResource("InputEventAction_t6x4q")] + [node name="HUD" type="CanvasLayer"] script = ExtResource("1_2bu0v") @@ -37,7 +57,7 @@ layout_mode = 2 [node name="GridContainer" type="GridContainer" parent="Panel/VBoxContainer/Container"] layout_mode = 2 -columns = 2 +columns = 3 [node name="MarginContainer" type="MarginContainer" parent="Panel/VBoxContainer/Container/GridContainer"] layout_mode = 2 @@ -50,9 +70,10 @@ theme_override_constants/margin_bottom = 4 layout_mode = 2 [node name="BuildModeButton" type="TextureRect" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer/HBoxContainer"] +texture_filter = 1 custom_minimum_size = Vector2(24, 24) layout_mode = 2 -texture = ExtResource("1_d2guw") +texture = SubResource("AtlasTexture_byi0r") expand_mode = 1 [node name="Label" type="Label" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer/HBoxContainer"] @@ -70,15 +91,37 @@ theme_override_constants/margin_bottom = 4 layout_mode = 2 [node name="SpawnerBoxButton" type="TextureRect" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer2/HBoxContainer2"] +texture_filter = 1 custom_minimum_size = Vector2(24, 24) layout_mode = 2 -texture = ExtResource("2_b00ni") +texture = SubResource("AtlasTexture_dixt2") expand_mode = 1 [node name="Label" type="Label" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer2/HBoxContainer2"] layout_mode = 2 text = "Spawn Box" +[node name="MarginContainer3" type="MarginContainer" parent="Panel/VBoxContainer/Container/GridContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer3"] +layout_mode = 2 + +[node name="PlayerListButton" type="TextureRect" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer3/HBoxContainer2"] +texture_filter = 1 +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +texture = SubResource("AtlasTexture_tuk1j") +expand_mode = 1 + +[node name="Label" type="Label" parent="Panel/VBoxContainer/Container/GridContainer/MarginContainer3/HBoxContainer2"] +layout_mode = 2 +text = "Players List" + [node name="Control" type="Control" parent="Panel/VBoxContainer/Container"] layout_mode = 2 size_flags_horizontal = 3 @@ -115,8 +158,10 @@ anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = -40.0 -offset_top = -40.0 +offset_left = -148.0 +offset_top = -116.0 +offset_right = -4.0 +offset_bottom = -4.0 grow_horizontal = 0 grow_vertical = 0 @@ -129,12 +174,13 @@ theme_override_constants/margin_bottom = 8 [node name="GridContainer" type="GridContainer" parent="SpawnBox/MarginContainer"] layout_mode = 2 -theme_override_constants/h_separation = 12 -theme_override_constants/v_separation = 12 +theme_override_constants/h_separation = 0 +theme_override_constants/v_separation = 0 columns = 4 [node name="SpawnButton" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] layout_mode = 2 +theme = ExtResource("5_121ry") texture_normal = ExtResource("4_w7sef") texture_pressed = ExtResource("5_xcxr8") texture_hover = ExtResource("6_4go2d") @@ -142,6 +188,7 @@ texture = ExtResource("7_ba5tw") [node name="SpawnButton2" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] layout_mode = 2 +shortcut = SubResource("Shortcut_i6rmj") texture_normal = ExtResource("8_wmbg8") texture_hover = ExtResource("6_4go2d") texture = ExtResource("9_nmd8t") @@ -208,26 +255,54 @@ texture_normal = ExtResource("14_t3qlu") texture_pressed = ExtResource("5_xcxr8") texture_hover = ExtResource("6_4go2d") -[node name="SpawnButton13" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] +[node name="PlayersList" parent="." instance=ExtResource("16_mq4um")] +unique_name_in_owner = true +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -144.0 +offset_top = 42.0 +offset_right = -5.0 +offset_bottom = 85.0 +grow_horizontal = 0 + +[node name="TowerConfigurationsContainer" type="MarginContainer" parent="."] +visible = false +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -120.0 +offset_right = -152.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="PanelContainer" type="PanelContainer" parent="TowerConfigurationsContainer"] layout_mode = 2 -texture_normal = ExtResource("14_t3qlu") -texture_pressed = ExtResource("5_xcxr8") -texture_hover = ExtResource("6_4go2d") -[node name="SpawnButton14" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] +[node name="MarginContainer" type="MarginContainer" parent="TowerConfigurationsContainer/PanelContainer"] layout_mode = 2 -texture_normal = ExtResource("14_t3qlu") -texture_pressed = ExtResource("5_xcxr8") -texture_hover = ExtResource("6_4go2d") +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 -[node name="SpawnButton15" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] +[node name="ScrollContainer" type="ScrollContainer" parent="TowerConfigurationsContainer/PanelContainer/MarginContainer"] layout_mode = 2 -texture_normal = ExtResource("14_t3qlu") -texture_pressed = ExtResource("5_xcxr8") -texture_hover = ExtResource("6_4go2d") +vertical_scroll_mode = 0 -[node name="SpawnButton16" parent="SpawnBox/MarginContainer/GridContainer" instance=ExtResource("3_7eaea")] +[node name="TowerConfigurations" type="HBoxContainer" parent="TowerConfigurationsContainer/PanelContainer/MarginContainer/ScrollContainer"] layout_mode = 2 -texture_normal = ExtResource("14_t3qlu") -texture_pressed = ExtResource("5_xcxr8") -texture_hover = ExtResource("6_4go2d") +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="TextureRect" parent="TowerConfigurationsContainer/PanelContainer/MarginContainer/ScrollContainer/TowerConfigurations" instance=ExtResource("17_1c5dq")] +layout_mode = 2 + +[connection signal="gui_input" from="Panel/VBoxContainer/Container/GridContainer/MarginContainer/HBoxContainer/BuildModeButton" to="." method="_on_build_mode_button_gui_input"] +[connection signal="gui_input" from="Panel/VBoxContainer/Container/GridContainer/MarginContainer2/HBoxContainer2/SpawnerBoxButton" to="." method="_on_spawner_box_button_gui_input"] +[connection signal="gui_input" from="Panel/VBoxContainer/Container/GridContainer/MarginContainer3/HBoxContainer2/PlayerListButton" to="." method="_on_player_list_button_gui_input"] diff --git a/UI/Lobby.gd b/UI/Lobby.gd index d4eb7c2..4fea0bc 100644 --- a/UI/Lobby.gd +++ b/UI/Lobby.gd @@ -1,11 +1,26 @@ extends Control -func _on_button_pressed() -> void: - Network.host_game() +func get_ip(): + var ip := "127.0.0.1" + if %IP.text: + ip = %IP.text + + return ip + + +func get_port(): + var port := 1234 + if %Port.text: + port = %Port.text + + return port + +func _on_host_pressed() -> void: + Network.host_game(get_port()) get_tree().change_scene_to_file("res://Stages/world.tscn") -func _on_button_2_pressed() -> void: - Network.join_game() +func _on_join_pressed() -> void: + Network.join_game(get_ip(), get_port()) get_tree().change_scene_to_file("res://Stages/world.tscn") diff --git a/UI/Lobby.tscn b/UI/Lobby.tscn index 64bfc97..f3775d8 100644 --- a/UI/Lobby.tscn +++ b/UI/Lobby.tscn @@ -11,18 +11,44 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_f10dr") -[node name="Button" type="Button" parent="."] -layout_mode = 0 -offset_right = 8.0 -offset_bottom = 8.0 +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 + +[node name="Host" type="Button" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 text = "Host" -[node name="Button2" type="Button" parent="."] -layout_mode = 0 -offset_top = 32.0 -offset_right = 38.0 -offset_bottom = 63.0 +[node name="IP" type="TextEdit" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(150, 50) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 18 +placeholder_text = "Join IP" + +[node name="Port" type="TextEdit" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(150, 50) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 18 +placeholder_text = "Host&Join Port" + +[node name="Join" type="Button" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 text = "Join" -[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] -[connection signal="pressed" from="Button2" to="." method="_on_button_2_pressed"] +[connection signal="pressed" from="CenterContainer/VBoxContainer/Host" to="." method="_on_host_pressed"] +[connection signal="pressed" from="CenterContainer/VBoxContainer/Join" to="." method="_on_join_pressed"] diff --git a/UI/PlayersList.tscn b/UI/PlayersList.tscn new file mode 100644 index 0000000..b1106b5 --- /dev/null +++ b/UI/PlayersList.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=3 format=3 uid="uid://cxd6c4kbnk04c"] + +[ext_resource type="Script" path="res://UI/players_list.gd" id="1_67rpy"] +[ext_resource type="PackedScene" uid="uid://wxe1hpn013y8" path="res://UI/PlayersListItem.tscn" id="2_ug8m7"] + +[node name="PlayersList" type="PanelContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_67rpy") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="List" type="VBoxContainer" parent="MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="PlayersListItem" parent="MarginContainer/List" instance=ExtResource("2_ug8m7")] +layout_mode = 2 + +[node name="ID" parent="MarginContainer/List/PlayersListItem/HBoxContainer" index="0"] +text = "ID" + +[node name="Score" parent="MarginContainer/List/PlayersListItem/HBoxContainer" index="2"] +text = "Score" + +[node name="Indicator" parent="MarginContainer/List/PlayersListItem" index="1"] +visible = false + +[editable path="MarginContainer/List/PlayersListItem"] diff --git a/UI/PlayersListItem.tscn b/UI/PlayersListItem.tscn new file mode 100644 index 0000000..8349b05 --- /dev/null +++ b/UI/PlayersListItem.tscn @@ -0,0 +1,48 @@ +[gd_scene format=3 uid="uid://wxe1hpn013y8"] + +[node name="PlayersListItem" type="MarginContainer"] +offset_right = 6.0 +offset_bottom = 23.0 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="ID" type="Label" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(10, 0) +layout_mode = 2 +size_flags_horizontal = 3 +text = "1" +horizontal_alignment = 1 +clip_text = true +text_overrun_behavior = 1 + +[node name="VSeparator" type="VSeparator" parent="HBoxContainer"] +visible = false +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Score" type="Label" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "0" +horizontal_alignment = 1 + +[node name="Indicator" type="Control" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Label" type="Label" parent="Indicator"] +layout_mode = 2 +offset_left = -8.0 +offset_top = 5.0 +offset_right = -2.0 +offset_bottom = 19.0 +size_flags_horizontal = 0 +theme_override_font_sizes/font_size = 10 +text = ">" diff --git a/UI/SpawnButton.tscn b/UI/SpawnButton.tscn index ec2423b..7b3450b 100644 --- a/UI/SpawnButton.tscn +++ b/UI/SpawnButton.tscn @@ -3,10 +3,12 @@ [ext_resource type="Script" path="res://UI/spawn_button.gd" id="1_ayei4"] [node name="SpawnButton" type="TextureButton"] +custom_minimum_size = Vector2(32, 32) offset_left = 2.0 offset_top = 2.0 offset_right = 38.0 offset_bottom = 38.0 +stretch_mode = 3 script = ExtResource("1_ayei4") [connection signal="pressed" from="." to="." method="_on_pressed"] diff --git a/UI/TowerConfiguration.tscn b/UI/TowerConfiguration.tscn new file mode 100644 index 0000000..0f97073 --- /dev/null +++ b/UI/TowerConfiguration.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=5 format=3 uid="uid://c05aq7xd4kx1p"] + +[ext_resource type="Texture2D" uid="uid://b1b18rd0tqbar" path="res://core_outdoor.png" id="1_3ypmu"] +[ext_resource type="Script" path="res://UI/tower_configuration.gd" id="2_vvfd0"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_k0400"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_7gm7m"] +atlas = ExtResource("1_3ypmu") +region = Rect2(400, 439, 32, 41) + +[node name="PanelContainer" type="PanelContainer"] +custom_minimum_size = Vector2(74, 88) +offset_right = 80.0 +offset_bottom = 104.0 +theme_override_styles/panel = SubResource("StyleBoxEmpty_k0400") +script = ExtResource("2_vvfd0") + +[node name="TextureRect" type="TextureRect" parent="."] +texture_filter = 1 +layout_mode = 2 +mouse_default_cursor_shape = 2 +texture = SubResource("AtlasTexture_7gm7m") +stretch_mode = 4 + +[connection signal="gui_input" from="TextureRect" to="." method="_on_texture_rect_gui_input"] +[connection signal="mouse_entered" from="TextureRect" to="TextureRect" method="_on_mouse_entered"] +[connection signal="mouse_exited" from="TextureRect" to="TextureRect" method="_on_mouse_exited"] diff --git a/UI/game_menu.gd b/UI/game_menu.gd new file mode 100644 index 0000000..357f369 --- /dev/null +++ b/UI/game_menu.gd @@ -0,0 +1,4 @@ +extends Control + + +# TODO: show controls diff --git a/UI/players_list.gd b/UI/players_list.gd new file mode 100644 index 0000000..c1ca00f --- /dev/null +++ b/UI/players_list.gd @@ -0,0 +1,33 @@ +extends PanelContainer + + +@onready var list: Control = %List + + +func _ready() -> void: + #players_list_container.visible = false + Network.players_changed.connect(update_players) + update_players() + + multiplayer.peer_disconnected.connect(remove_player) + + +func update_players(): + for id in Network.players.keys(): + var player: Player = Network.players[id] + + var control: Control = list.get_node_or_null(str(id)) + if not control: + control = preload("res://UI/PlayersListItem.tscn").instantiate() + control.name = str(id) + control.modulate = player.get_color() + control.get_node("%Indicator").visible = id == multiplayer.get_unique_id() + list.add_child(control) + + control.get_node("%ID").text = str(id) + control.get_node("%Score").text = str(player.score) + list.move_child(control, Network.get_ordered_player_ids().find(id) + 1) + + +func remove_player(id): + list.remove_child(list.get_node(str(id))) diff --git a/UI/tower_configuration.gd b/UI/tower_configuration.gd new file mode 100644 index 0000000..9eb12cd --- /dev/null +++ b/UI/tower_configuration.gd @@ -0,0 +1,35 @@ +extends PanelContainer + + +var is_hovered = false + + +func _on_mouse_entered() -> void: + is_hovered = true + + var stylebox := StyleBoxFlat.new() + stylebox.bg_color = Color(1.0, 1.0, 1.0, 0.25) + add_theme_stylebox_override("panel", stylebox) + +func _on_mouse_exited() -> void: + is_hovered = false + + remove_theme_stylebox_override("panel") + + +func _on_texture_rect_gui_input(event: InputEvent) -> void: + var state: StateBuild = get_tree().current_scene.get_node("StateManager/StateBuild") + if event.is_action_pressed("select") and not state.current_builder_element: + var builder_element_scene = preload("res://Game/States/Build/BuilderElement.tscn") + var builder_element = builder_element_scene.instantiate() + + var tower = preload("res://Towers/Tower.tscn").instantiate() as Tower + tower.attack_range = [ + Client.stage.map.tile_set.tile_size.x * 2, + Client.stage.map.tile_set.tile_size.x * 3, + Client.stage.map.tile_set.tile_size.x * 4, + ].pick_random() + + builder_element.element = tower + get_tree().current_scene.add_child(builder_element) + state.current_builder_element = builder_element diff --git a/Units/Unit.gd b/Units/Unit.gd index 6c3b254..8373a59 100644 --- a/Units/Unit.gd +++ b/Units/Unit.gd @@ -23,6 +23,11 @@ var is_selected = false : var is_hovered = false : set(value): + if value: + $Label.visible = true + else: + if not is_selected: + $Label.visible = false is_hovered = value queue_redraw() @@ -39,9 +44,6 @@ var current_path_idx = 0 : if line: line.points = PackedVector2Array(current_path.slice(value)) -var previous_path: PackedVector2Array -var previous_path_idx = 0 - var previous_position: Vector2 var recent_closest_paths: Array[PackedVector2Array] @@ -52,15 +54,15 @@ var roaming_mode = false # rpc owner id var owner_id = 1 -# unique shared id on the network on all clients -var network_id - @export var base_speed: float = 100 @export var speed: float = base_speed -@export var hp = 50 +@export var hp = 50 : + set = set_hp @onready var line: Line2D = $UnitPathLine.duplicate() +@onready var sprite = $Sprite2D + func _ready(): if not target: @@ -89,6 +91,12 @@ func _ready(): func _physics_process(delta): + if get_multiplayer_authority() != multiplayer.get_unique_id(): + Network.update_unit.rpc(get_path(), { + "hp": hp, + }) + return + previous_position = global_position if not current_path.is_empty(): @@ -113,6 +121,12 @@ func _physics_process(delta): reset_path() else: stuck_position_accumulator = 0 + + Network.update_unit.rpc(get_path(), { + "position": global_position, + "hp": hp, + "sprite": {"self_modulate": $Sprite2D.self_modulate} + }) func _draw(): @@ -139,8 +153,12 @@ func _draw(): func _on_navigation_base_area_entered(area: Area2D): + if get_multiplayer_authority() != multiplayer.get_unique_id(): + return + if area.is_in_group("goal"): Client.player.score += 1 + Client.update_player() queue_free() if area.is_in_group("path"): var path_node = area.get_parent() @@ -150,7 +168,7 @@ func _on_navigation_base_area_entered(area: Area2D): func walk_along_path(path: PackedVector2Array, index: int, delta: float): immediate_target = path[index] - var displacement := (path[index]) - global_position + var displacement := immediate_target - global_position var direction := displacement.normalized() var distance := displacement.length() @@ -161,7 +179,7 @@ func walk_along_path(path: PackedVector2Array, index: int, delta: float): if effect.has_method("apply_physics"): effect.apply_physics(delta) # todo: changing velocity here doesn't work nicely - # todo: because the velocity expects to each the point, so it stutters + # todo: because the velocity expects ??to each the point??, so it stutters move_and_slide() @@ -170,9 +188,11 @@ func set_hp(value): # TODO: rpc on damage hp = value - %HPBar.set_value(value) - $Label.text = str(hp) + if get_node("%HPBar"): + %HPBar.set_value(value) + if get_node("Label"): + $Label.text = str(hp) if hp <= 0: queue_free() @@ -192,6 +212,9 @@ func reset_path(): current_path = get_grid_path() if current_path.is_empty(): + # TODO: nimm letzte route davor die noch ging, falls verfügbar + # TODO: schneide ab bis eins vor neue tower position. (=partial path) + # TODO: laufe bis dahin und checke dann end of partial path current_path = get_grid_path(true) recent_closest_paths.append(current_path) @@ -224,6 +247,10 @@ func get_grid_path(partial = false): func _on_selection_area_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void: + # disable remote select for now + if owner_id != multiplayer.get_unique_id(): + return + if Client.state is StateDefault: if event.is_action_pressed("select"): if selected_unit: @@ -243,14 +270,20 @@ func _on_selection_area_mouse_exited() -> void: func _on_tree_exiting() -> void: is_selected = false line.queue_free() + + #if get_multiplayer_authority() == multiplayer.get_unique_id(): + Network.remove_unit.rpc(get_path()) func get_rpc_properties(): return { + "name": null, "global_position": null, "target": "res://Stages/Paths/PathNode.tscn", "hp": null, "speed": null, "current_path": null, "current_path_idx": null, + "sprite": "node://Sprite2D", + "owner_id": null, } diff --git a/Units/Unit.tscn b/Units/Unit.tscn index 312389e..879df08 100644 --- a/Units/Unit.tscn +++ b/Units/Unit.tscn @@ -53,7 +53,7 @@ text = "1000" horizontal_alignment = 1 [node name="SelectionArea" type="Area2D" parent="."] -collision_layer = 16 +collision_layer = 32 collision_mask = 0 [node name="CollisionShape2D" type="CollisionShape2D" parent="SelectionArea"] @@ -65,9 +65,6 @@ width = 1.0 default_color = Color(1, 1, 1, 0.392157) target_circle_radius = 4.0 -[connection signal="input_event" from="." to="." method="_on_input_event"] -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] [connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"] [connection signal="area_entered" from="NavigationBase" to="." method="_on_navigation_base_area_entered"] [connection signal="input_event" from="SelectionArea" to="." method="_on_selection_area_input_event"] diff --git a/project.godot b/project.godot index 64ca3b8..7bad496 100644 --- a/project.godot +++ b/project.godot @@ -24,8 +24,6 @@ Client="*res://Game/Client.gd" window/size/viewport_width=1280 window/size/viewport_height=720 -window/stretch/mode="viewport" -window/stretch/aspect="expand" [global_group] @@ -70,7 +68,7 @@ select={ } build_mode_start={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":66,"key_label":0,"unicode":98,"location":0,"echo":false,"script":null) ] } spawn_box_toggle={ @@ -83,13 +81,24 @@ select_multiple={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +spawn_unit={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +players_list_toggle={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null) +] +} [layer_names] 2d_physics/layer_1="Mob" 2d_physics/layer_2="Path" 2d_physics/layer_3="Tower" -2d_physics/layer_5="Selection" +2d_physics/layer_5="SelectionRectangle" +2d_physics/layer_6="SelectableArea" [rendering] diff --git a/theme.tres b/theme.tres new file mode 100644 index 0000000..b351305 --- /dev/null +++ b/theme.tres @@ -0,0 +1,3 @@ +[gd_resource type="Theme" format=3 uid="uid://c7f1ftrx53ag1"] + +[resource] |