diff options
Diffstat (limited to 'Game')
-rw-r--r-- | Game/Client.gd | 75 | ||||
-rw-r--r-- | Game/Network.gd | 132 | ||||
-rw-r--r-- | Game/Player.gd | 20 | ||||
-rw-r--r-- | Game/States/Build/BuildGrid.tscn | 6 | ||||
-rw-r--r-- | Game/States/Build/BuilderElement.gd | 87 | ||||
-rw-r--r-- | Game/States/Build/BuilderElement.tscn | 19 | ||||
-rw-r--r-- | Game/States/Build/StateBuild.gd | 50 | ||||
-rw-r--r-- | Game/States/Build/StateBuild.tscn | 11 | ||||
-rw-r--r-- | Game/States/Build/build_grid.gd | 21 | ||||
-rw-r--r-- | Game/States/Default/StateDefault.gd | 17 | ||||
-rw-r--r-- | Game/States/Default/StateDefault.tscn | 6 | ||||
-rw-r--r-- | Game/States/State.gd | 13 | ||||
-rw-r--r-- | Game/States/StateManager.gd | 19 | ||||
-rw-r--r-- | Game/States/StateManager.tscn | 6 |
14 files changed, 482 insertions, 0 deletions
diff --git a/Game/Client.gd b/Game/Client.gd new file mode 100644 index 0000000..77ce500 --- /dev/null +++ b/Game/Client.gd @@ -0,0 +1,75 @@ +extends Node + + +var state: State +var stage: Stage +var player: Player = Player.new() + + +func update_player(local_player: Player): + player = local_player + Network.players[multiplayer.get_unique_id()] = local_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() + + 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) + + +func remove_tower(tower: Tower): + if tower.owner_id == multiplayer.get_unique_id(): + destroy_tower(tower) + player.score -= 1 + update_player(player) + + +func destroy_tower(tower: Tower): + stage.destroy_tower(tower) + Network.destroy_tower.rpc(Network.to_rpc_object(tower)) + + player.towers.erase(tower.global_position) + update_player(player) + + +func select_tower(tower: Tower): + if tower.owner_id == multiplayer.get_unique_id(): + tower.is_selected = true + + +func deselect_tower(): + if not Tower.selected_towers.is_empty(): + for tower in Tower.selected_towers: + tower.is_selected = false + + +func spawn_unit(unit: Unit, spawn: Spawn): + unit.global_position = spawn.spawn_position + unit.target = spawn.next_node + unit.hp = 20000 + unit.speed = randi_range(50, 150) + + stage.spawn_unit(unit, spawn) + Network.spawn_unit.rpc(Network.to_rpc_object(unit), Network.to_rpc_object(spawn)) + + +func array_intersect(first, second): + var compare = {} + for value in first: + compare[value] = true + + for value in second: + if compare.get(value, false): + return true + + return false diff --git a/Game/Network.gd b/Game/Network.gd new file mode 100644 index 0000000..021de69 --- /dev/null +++ b/Game/Network.gd @@ -0,0 +1,132 @@ +extends Node + + +var players = {} + + +func _ready(): + multiplayer.connected_to_server.connect(_on_connected_to_server) + multiplayer.peer_connected.connect(_on_peer_connected) + + multiplayer.allow_object_decoding = true + + +func host_game(): + var peer = ENetMultiplayerPeer.new() + peer.create_server(1234, 2) + + multiplayer.multiplayer_peer = peer + players[1] = Client.player + +func join_game(): + var peer = ENetMultiplayerPeer.new() + peer.create_client("127.0.0.1", 1234) + + multiplayer.multiplayer_peer = peer + + +func _on_connected_to_server(): + print("connected to server") + +func _on_peer_connected(id): + print("peer connected: ", id) + add_to_players.rpc(to_rpc_object(Client.player)) + + +func to_rpc_object(object: Variant): + var remote_object = {} + var properties = object.get_rpc_properties() + for property in properties: + var property_class = properties[property] + if object[property] is Dictionary: + remote_object[property] = {} + for key in object[property]: + remote_object[property][key] = to_rpc_object(object[property][key]) + elif property_class: + remote_object[property] = to_rpc_object(object[property]) + else: + remote_object[property] = object[property] + + return remote_object + + +func from_rpc_object(remote_object: Dictionary, remote_class_path: String): + var object + var remote_class = load(remote_class_path) + if remote_class is PackedScene: + object = remote_class.instantiate() + elif remote_class is GDScript: + object = remote_class.new() + else: + assert(false, "unexpected remote class type") + + var properties = object.get_rpc_properties() + for property in properties: + var property_class = properties[property] + if object[property] is Dictionary: + for key in object[property]: + object[property][key] = from_rpc_object(remote_object[property], property_class) + elif property_class: + object[property] = from_rpc_object(remote_object[property], property_class) + else: + object[property] = remote_object[property] + + return object + + +func merge_with_rpc_object(object: Variant, remote_object: Dictionary): + var properties = object.get_rpc_properties() + for property in properties: + var property_class = properties[property] + if object[property] is Dictionary: + object[property] = {} + for key in remote_object[property]: + object[property][key] = from_rpc_object(remote_object[property][key], property_class) + elif property_class: + object[property] = merge_with_rpc_object(object[property], remote_object[property]) + else: + object[property] = remote_object[property] + + return object + + +@rpc("call_local", "any_peer") +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") + players[id] = player + + +@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) + players[id] = player + + +@rpc("any_peer") +func place_tower(remote_tower: Dictionary, position: Vector2): + var remote_player_id = multiplayer.get_remote_sender_id() + + var tower = from_rpc_object(remote_tower, "res://Towers/Tower.tscn") + tower.owner_id = remote_player_id + + players[remote_player_id].towers[tower.global_position] = tower + + Client.stage.place_tower(tower, position) + + +@rpc("any_peer") +func destroy_tower(remote_tower: Dictionary): + var player = players[remote_tower.owner_id] as Player + var tower = player.towers.get(remote_tower.global_position) + + Client.stage.destroy_tower(tower) + + +@rpc("any_peer") +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") + + Client.stage.spawn_unit(unit, spawn) diff --git a/Game/Player.gd b/Game/Player.gd new file mode 100644 index 0000000..2726929 --- /dev/null +++ b/Game/Player.gd @@ -0,0 +1,20 @@ +class_name Player +extends Resource + + +var towers: Dictionary : + set(value): + towers = value + +var score: int : + set(value): + score = value + Client.stage.hud.score.text = str(score) + +var units: Array[Unit] + + +func get_rpc_properties() -> Dictionary: + return { + "score": null, + } diff --git a/Game/States/Build/BuildGrid.tscn b/Game/States/Build/BuildGrid.tscn new file mode 100644 index 0000000..ffb41d7 --- /dev/null +++ b/Game/States/Build/BuildGrid.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://8oq8aa1q4c1h"] + +[ext_resource type="Script" path="res://Game/States/Build/build_grid.gd" id="1_jige0"] + +[node name="BuildGrid" type="Node2D"] +script = ExtResource("1_jige0") diff --git a/Game/States/Build/BuilderElement.gd b/Game/States/Build/BuilderElement.gd new file mode 100644 index 0000000..8315792 --- /dev/null +++ b/Game/States/Build/BuilderElement.gd @@ -0,0 +1,87 @@ +class_name BuilderElement +extends Node2D + + +@export var element: Node2D + +var collision_shape: Shape2D + +var previous_position: Vector2 + +var collision_areas: Array[Area2D] = [] + + +func _ready(): + var element_sprite: Sprite2D = element.get_node("Sprite2D") + $Sprite2D.texture = element_sprite.texture + $Sprite2D.region_enabled = element_sprite.region_enabled + $Sprite2D.region_rect = element_sprite.region_rect + $Sprite2D.scale = element_sprite.scale + $Sprite2D.position = element_sprite.position + + var element_collision_shape = element.get_node("CollisionShape2D").duplicate() as CollisionShape2D + collision_shape = element_collision_shape.shape + + var tile_size = Client.stage.map.tile_set.tile_size + var snapped_size = collision_shape.size.snapped(tile_size) + for x in (snapped_size.x / tile_size.x): + for y in (snapped_size.y / tile_size.y): + var collision_visibility_area_scene = preload("res://UI/CollisionVisibilityArea.tscn") + var collision_visibility_area = collision_visibility_area_scene.instantiate() + collision_visibility_area.position = Vector2( + x * tile_size.x, + y * tile_size.y + ) + collision_visibility_area.set_collision_layer_value(3, true) + collision_visibility_area.set_collision_mask_value(1, true) + collision_visibility_area.set_collision_mask_value(2, true) + collision_visibility_area.set_collision_mask_value(3, true) + collision_visibility_area.colliding_color = Color(1, 0, 0, 0.5) + collision_visibility_area.not_colliding_color = Color(0, 1, 0, 0.25) + + var shape = RectangleShape2D.new() + shape.size = Vector2(tile_size - Vector2i(1, 1)) + var cshape = CollisionShape2D.new() + cshape.shape = shape + cshape.position = tile_size / 2 + + collision_visibility_area.add_child(cshape) + add_child(collision_visibility_area) + + collision_areas.append(collision_visibility_area) + + +func _draw(): + draw_circle( + Client.stage.map.tile_set.tile_size, + 8 + element.attack_range, + Color(1, 1, 1, 0.75), + false, + 1.0 + ) + + +func _process(_delta): + previous_position = global_position + global_position = get_global_mouse_position().snapped(Client.stage.map.tile_set.tile_size) - Vector2(16,16) + + if global_position != previous_position: + queue_redraw() + + +func can_build(): + for area in collision_areas: + if area.get_overlapping_areas().size() > 0: + return false + + if area.get_overlapping_bodies().size() > 0: + return false + + return true + + +func _on_area_entered(_node) -> void: + queue_redraw() + +func _on_area_exited(_node) -> void: + queue_redraw() diff --git a/Game/States/Build/BuilderElement.tscn b/Game/States/Build/BuilderElement.tscn new file mode 100644 index 0000000..ea0a857 --- /dev/null +++ b/Game/States/Build/BuilderElement.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=2 format=3 uid="uid://cleoiemwasbm5"] + +[ext_resource type="Script" path="res://Game/States/Build/BuilderElement.gd" id="1_xxacj"] + +[node name="BuilderElement" type="Node2D"] +script = ExtResource("1_xxacj") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture_filter = 1 +centered = false + +[node name="Area2D" type="Area2D" parent="."] +collision_layer = 4 +collision_mask = 7 + +[connection signal="area_entered" from="Area2D" to="." method="_on_area_entered"] +[connection signal="area_exited" from="Area2D" to="." method="_on_area_exited"] +[connection signal="body_entered" from="Area2D" to="." method="_on_area_entered"] +[connection signal="body_exited" from="Area2D" to="." method="_on_area_exited"] diff --git a/Game/States/Build/StateBuild.gd b/Game/States/Build/StateBuild.gd new file mode 100644 index 0000000..b92020b --- /dev/null +++ b/Game/States/Build/StateBuild.gd @@ -0,0 +1,50 @@ +class_name StateBuild +extends State + + +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(): + %BuildGrid.visible = false + + +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(): + var placed_tower = current_builder_element.element.duplicate() as Tower + Client.place_tower(placed_tower, current_builder_element.global_position) + + placed_tower.selected.connect(func(): + Client.stage.hud.tower.text = "Range: %s - Power: %s - Speed: %s" % [ + placed_tower.attack_range, placed_tower.attack_power, placed_tower.attack_speed + ] + ) + + if not Input.is_action_pressed("builder_tower_place_keep"): + current_builder_element.queue_free() + set_state("StateDefault") + + if event.is_action_pressed("builder_cancel") or event.is_action_pressed("build_mode_start"): + get_viewport().set_input_as_handled() + current_builder_element.queue_free() + set_state("StateDefault") diff --git a/Game/States/Build/StateBuild.tscn b/Game/States/Build/StateBuild.tscn new file mode 100644 index 0000000..84583c2 --- /dev/null +++ b/Game/States/Build/StateBuild.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=3 format=3 uid="uid://bo5dp02vlui3l"] + +[ext_resource type="Script" path="res://Game/States/Build/StateBuild.gd" id="1_s0n2d"] +[ext_resource type="PackedScene" uid="uid://8oq8aa1q4c1h" path="res://Game/States/Build/BuildGrid.tscn" id="2_iheti"] + +[node name="StateBuild" type="Node"] +script = ExtResource("1_s0n2d") + +[node name="BuildGrid" parent="." instance=ExtResource("2_iheti")] +unique_name_in_owner = true +visible = false diff --git a/Game/States/Build/build_grid.gd b/Game/States/Build/build_grid.gd new file mode 100644 index 0000000..6fd93aa --- /dev/null +++ b/Game/States/Build/build_grid.gd @@ -0,0 +1,21 @@ +extends Node2D + + +func _draw(): + var area = Client.stage.map.get_used_rect() + var cell_size = Client.stage.map.tile_set.tile_size + var offset = Client.stage.map.position + for row in range(0, area.size.y + 1): + draw_line( + Vector2(offset.x, offset.y + row * cell_size.y), + Vector2(offset.x + area.size.x * cell_size.x, offset.y + row * cell_size.y), + Color(1, 1, 1, 0.25), + 1.0 + ) + for column in range(0, area.size.x + 1): + draw_line( + Vector2(offset.x + column * cell_size.x, offset.y), + Vector2(offset.x + column * cell_size.x, offset.y + area.size.y * cell_size.y), + Color(1, 1, 1, 0.25), + 1.0 + ) diff --git a/Game/States/Default/StateDefault.gd b/Game/States/Default/StateDefault.gd new file mode 100644 index 0000000..26384c5 --- /dev/null +++ b/Game/States/Default/StateDefault.gd @@ -0,0 +1,17 @@ +class_name StateDefault +extends State + + +func _state_input(event: InputEvent) -> void: + if event.is_action_pressed("build_mode_start"): + set_state("StateBuild") + + if event.is_action_pressed("builder_tower_select"): + 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(): + tower.is_selected = false + + if event.is_action_pressed("select"): + if Unit.selected_unit: + Unit.selected_unit.is_selected = false diff --git a/Game/States/Default/StateDefault.tscn b/Game/States/Default/StateDefault.tscn new file mode 100644 index 0000000..1f73d30 --- /dev/null +++ b/Game/States/Default/StateDefault.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cg16o7eqqha70"] + +[ext_resource type="Script" path="res://Game/States/Default/StateDefault.gd" id="1_e8s2t"] + +[node name="StateDefault" type="Node"] +script = ExtResource("1_e8s2t") diff --git a/Game/States/State.gd b/Game/States/State.gd new file mode 100644 index 0000000..c1a2bde --- /dev/null +++ b/Game/States/State.gd @@ -0,0 +1,13 @@ +class_name State +extends Node + + +func _state_enter(): + pass + +func _state_exit(): + pass + + +func set_state(state: NodePath): + get_parent().set_state(state) diff --git a/Game/States/StateManager.gd b/Game/States/StateManager.gd new file mode 100644 index 0000000..e62cdb4 --- /dev/null +++ b/Game/States/StateManager.gd @@ -0,0 +1,19 @@ +class_name StateManager +extends Node + + +func _ready(): + Client.state = get_child(0) + + +func _input(event: InputEvent) -> void: + Client.state._state_input(event) + + +func set_state(state: Variant): + if state is not State: + state = get_node(state) + + Client.state._state_exit() + Client.state = state + Client.state._state_enter() diff --git a/Game/States/StateManager.tscn b/Game/States/StateManager.tscn new file mode 100644 index 0000000..5584445 --- /dev/null +++ b/Game/States/StateManager.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://d4hgrh7danbbx"] + +[ext_resource type="Script" path="res://Game/States/StateManager.gd" id="1_1q4x6"] + +[node name="StateManager" type="Node"] +script = ExtResource("1_1q4x6") |