diff options
-rw-r--r-- | Character.tscn | 12 | ||||
-rw-r--r-- | Global.gd | 56 | ||||
-rw-r--r-- | Levels/Level_0001.tscn | 7 | ||||
-rw-r--r-- | Network/Lobby.gd | 82 | ||||
-rw-r--r-- | Network/Lobby.tscn | 101 | ||||
-rw-r--r-- | Network/Network.gd | 114 | ||||
-rw-r--r-- | Objects/Flag.tscn | 1 | ||||
-rw-r--r-- | UI/LevelSelect.gd | 6 | ||||
-rw-r--r-- | UI/LevelSelectCell.gd | 7 | ||||
-rw-r--r-- | UI/LevelSelectCell.tscn | 12 | ||||
-rw-r--r-- | project.godot | 11 |
11 files changed, 393 insertions, 16 deletions
diff --git a/Character.tscn b/Character.tscn index 5902649..eb0b6b6 100644 --- a/Character.tscn +++ b/Character.tscn @@ -14,6 +14,11 @@ [sub_resource type="SpriteFrames" id=1] animations = [ { +"frames": [ ExtResource( 8 ) ], +"loop": true, +"name": "slide", +"speed": 5.0 +}, { "frames": [ ExtResource( 4 ) ], "loop": true, "name": "back", @@ -24,11 +29,6 @@ animations = [ { "name": "idle", "speed": 5.0 }, { -"frames": [ ExtResource( 8 ) ], -"loop": true, -"name": "slide", -"speed": 5.0 -}, { "frames": [ ExtResource( 5 ), ExtResource( 6 ) ], "loop": true, "name": "walk", @@ -64,6 +64,7 @@ animations = [ { extents = Vector2( 9.21901, 11.2317 ) [node name="Character" type="KinematicBody2D"] +collision_mask = 15 script = ExtResource( 1 ) [node name="Sprite" type="AnimatedSprite" parent="."] @@ -81,4 +82,3 @@ cast_to = Vector2( 0, 150 ) [node name="CheckFallLanding" type="RayCast2D" parent="."] enabled = true -cast_to = Vector2( 0, 50 ) @@ -1,6 +1,10 @@ extends Node +signal level_map_updated() +signal game_won() + + const Levels = [ "Level_0001", "Level_0001", @@ -20,7 +24,10 @@ func _ready(): for level in self.Levels: self.Level_Map.push_back({ time = 0, - cleared_by = -1, + cleared_by = { # Network.player + idx = -1, + name = "" + }, meta = { path = "res://Levels/%s.tscn" % level, name = level, @@ -55,6 +62,51 @@ func end_level(instance_level): var time = stepify(instance_level.timer, 0.01) if (global_level.time == 0 or time < global_level.time): global_level.time = time - global_level.cleared_by = randi() & 1 + global_level.cleared_by = Network.player + for id in Network.players: + rpc_id(id, "_update_level_map", instance_level.idx, global_level) get_tree().change_scene("res://UI/LevelSelect.tscn") + + self.check_win() + + +remote func _update_level_map(idx, global_level): + self.Level_Map[idx] = global_level + emit_signal("level_map_updated") + + +func check_win(): + var has_won = false + + var cleared_levels_idx = [] + for idx in range(self.Level_Map.size()): + if self.Level_Map[idx].cleared_by.idx == Network.player.idx: + cleared_levels_idx.push_back(idx) + + var possible_winning_conditions = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + + [0, 4, 8], + [2, 4, 6], + ] + + # because there's no intersection method on arrays.. + for cond in possible_winning_conditions: + var has_cleared = 0 + for digit in cond: + if digit in cleared_levels_idx: + has_cleared += 1 + + if has_cleared == 3: + has_won = true + break + + if has_won: + emit_signal("game_won", Network.player) diff --git a/Levels/Level_0001.tscn b/Levels/Level_0001.tscn index bbae830..89dba41 100644 --- a/Levels/Level_0001.tscn +++ b/Levels/Level_0001.tscn @@ -7,21 +7,22 @@ [node name="Level_0001" type="Node2D"] script = ExtResource( 4 ) -startingPosition = Vector2( 144.471, 115.947 ) -cameraLimitRight = 700 -cameraLimitBottom = 500 [node name="TileMap" type="TileMap" parent="."] tile_set = ExtResource( 1 ) cell_size = Vector2( 16, 16 ) +collision_layer = 2 +collision_mask = 15 format = 1 tile_data = PoolIntArray( 327692, 0, 6, 327693, 0, 8, 393228, 0, 65542, 393229, 0, 65545, 458764, 0, 65542, 458765, 0, 65545, 524295, 0, 0, 524296, 0, 1, 524297, 0, 1, 524298, 0, 1, 524299, 0, 1, 524300, 0, 65542, 524301, 0, 65545, 524302, 0, 1, 524303, 0, 1, 524304, 0, 1, 524305, 0, 1, 524306, 0, 1, 524307, 0, 2, 589836, 0, 131078, 589837, 0, 131080, 720918, 0, 0, 720919, 0, 1, 720920, 0, 1, 720921, 0, 1, 720922, 0, 1, 720923, 0, 1, 720924, 0, 1, 720925, 0, 1, 720926, 0, 1, 720927, 0, 1, 720928, 0, 1, 720929, 0, 1, 720930, 0, 1, 720931, 0, 1, 720932, 0, 1, 720933, 0, 1, 720934, 0, 1, 720935, 0, 1, 720936, 0, 1, 720937, 0, 2, 917518, 0, 6, 917519, 0, 8, 917523, 0, 6, 917524, 0, 8, 983054, 0, 65542, 983055, 0, 65545, 983059, 0, 65542, 983060, 0, 65545, 1048590, 0, 65542, 1048591, 0, 65545, 1048595, 0, 65542, 1048596, 0, 65545, 1114126, 0, 65542, 1114127, 0, 65545, 1114131, 0, 65542, 1114132, 0, 65545, 1179662, 0, 65542, 1179663, 0, 65545, 1179667, 0, 65542, 1179668, 0, 65545, 1245198, 0, 65542, 1245199, 0, 65545, 1245203, 0, 65542, 1245204, 0, 65545, 1310734, 0, 65542, 1310735, 0, 65545, 1310739, 0, 65542, 1310740, 0, 65545, 1376270, 0, 65542, 1376271, 0, 65545, 1376275, 0, 65542, 1376276, 0, 65545, 1441806, 0, 65542, 1441807, 0, 65545, 1441811, 0, 65542, 1441812, 0, 65545, 1507342, 0, 65542, 1507343, 0, 65545, 1507347, 0, 65542, 1507348, 0, 65545, 1572878, 0, 65542, 1572879, 0, 65545, 1572883, 0, 65542, 1572884, 0, 65545, 1638414, 0, 65542, 1638415, 0, 65545, 1638419, 0, 65542, 1638420, 0, 65545, 1703950, 0, 131078, 1703951, 0, 131080, 1703955, 0, 131078, 1703956, 0, 131080, 1900544, 0, 0, 1900545, 0, 1, 1900546, 0, 1, 1900547, 0, 1, 1900548, 0, 1, 1900549, 0, 1, 1900550, 0, 1, 1900551, 0, 1, 1900552, 0, 1, 1900553, 0, 1, 1900554, 0, 1, 1900555, 0, 1, 1900556, 0, 1, 1900557, 0, 1, 1900558, 0, 1, 1900559, 0, 1, 1900560, 0, 1, 1900561, 0, 1, 1900562, 0, 1, 1900563, 0, 1, 1900564, 0, 1, 1900565, 0, 2 ) [node name="Flag" parent="." instance=ExtResource( 3 )] position = Vector2( 602.469, 111.673 ) +collision_layer = 0 [node name="Character" parent="." instance=ExtResource( 2 )] position = Vector2( 144.471, 115.947 ) +collision_mask = 15 [node name="Camera2D" type="Camera2D" parent="Character"] current = true diff --git a/Network/Lobby.gd b/Network/Lobby.gd new file mode 100644 index 0000000..d962081 --- /dev/null +++ b/Network/Lobby.gd @@ -0,0 +1,82 @@ +extends Control + + +func _ready(): + Network.connect("connection_succeeded", self, "_on_connection_succeeded") + Network.connect("connection_failed", self, "_on_connection_failed") + Network.connect("player_list_changed", self, "refresh_lobby") + Network.connect("game_error", self, "_on_game_error") + Network.connect("game_ended", self, "_on_game_ended") + + if OS.has_environment("USERNAME"): + $Connect/Name.text = OS.get_environment("USERNAME") + else: + var desktop_path = OS.get_system_dir(0).replace("\\", "/").split("/") + $Connect/Name.text = desktop_path[desktop_path.size() - 2] + + +func disable_connect_buttons(is_disabled = true): + $Connect/Host.disabled = is_disabled + $Connect/Join.disabled = is_disabled + + +func refresh_lobby(): + var players = Network.players.values() + var player = Network.player + + $Players/List.clear() + $Players/List.add_item(player.name + " (You)") + for p in players: + $Players/List.add_item(p.name) + + $Players/Start.disabled = not get_tree().is_network_server() + + +func _on_connection_succeeded(): + $Connect.hide() + $Players.show() + + +func _on_connection_failed(): + self.disable_connect_buttons() + $Connect/Error.set_text("Connection failed!") + + +func _on_game_error(error): + $ErrorDialog.dialog_text = error + $ErrorDialog.popup_centered_minsize() + self.disable_connect_buttons() + + +func _on_game_ended(): + self.show() + $Connect.show() + $Players.hide() + self.disable_connect_buttons() + + +func _on_Host_pressed(): + $Connect.hide() + $Players.show() + $Connect/Error.text = "" + + var player_name = $Connect/Name.text + Network.host_game(player_name) + self.refresh_lobby() + + +func _on_Join_pressed(): + var ip = $Connect/IP.text + if not ip.is_valid_ip_address(): + $Connect/Error.text = "Invalid IP address!" + return + + $Connect/Error.text = "" + self.disable_connect_buttons(false) + + var player_name = $Connect/Name.text + Network.join_game(ip, player_name) + + +func _on_Start_pressed(): + Network.start_game() diff --git a/Network/Lobby.tscn b/Network/Lobby.tscn new file mode 100644 index 0000000..0caf892 --- /dev/null +++ b/Network/Lobby.tscn @@ -0,0 +1,101 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Network/Lobby.gd" type="Script" id=1] + +[node name="Lobby" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Connect" type="Panel" parent="."] +margin_left = 424.0 +margin_top = 232.0 +margin_right = 600.0 +margin_bottom = 376.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Name" type="LineEdit" parent="Connect"] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 168.0 +margin_bottom = 32.0 +placeholder_text = "Name" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="IP" type="LineEdit" parent="Connect"] +margin_left = 8.0 +margin_top = 40.0 +margin_right = 168.0 +margin_bottom = 64.0 +placeholder_text = "IP" + +[node name="Host" type="Button" parent="Connect"] +margin_left = 8.0 +margin_top = 112.0 +margin_right = 64.0 +margin_bottom = 136.0 +text = "Host" + +[node name="Join" type="Button" parent="Connect"] +margin_left = 112.0 +margin_top = 112.0 +margin_right = 168.0 +margin_bottom = 136.0 +text = "Join" + +[node name="Error" type="Label" parent="Connect"] +margin_left = 8.0 +margin_top = 72.0 +margin_right = 168.0 +margin_bottom = 104.0 +custom_colors/font_color = Color( 1, 0.137255, 0.137255, 1 ) + +[node name="Players" type="Panel" parent="."] +visible = false +margin_left = 384.0 +margin_top = 48.0 +margin_right = 640.0 +margin_bottom = 512.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="Players"] +margin_left = 16.0 +margin_top = 16.0 +margin_right = 240.0 +margin_bottom = 32.0 +text = "Lobby" + +[node name="List" type="ItemList" parent="Players"] +margin_left = 16.0 +margin_top = 48.0 +margin_right = 240.0 +margin_bottom = 400.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Start" type="Button" parent="Players"] +margin_left = 64.0 +margin_top = 416.0 +margin_right = 192.0 +margin_bottom = 448.0 +text = "START!" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ErrorDialog" type="AcceptDialog" parent="."] +margin_right = 83.0 +margin_bottom = 58.0 +[connection signal="pressed" from="Connect/Host" to="." method="_on_Host_pressed"] +[connection signal="pressed" from="Connect/Join" to="." method="_on_Join_pressed"] +[connection signal="pressed" from="Players/Start" to="." method="_on_Start_pressed"] diff --git a/Network/Network.gd b/Network/Network.gd new file mode 100644 index 0000000..0f007b4 --- /dev/null +++ b/Network/Network.gd @@ -0,0 +1,114 @@ +extends Node + + +signal connection_succeeded() +signal connection_failed() +signal player_list_changed() +signal game_error() +signal game_ended() + + +const PORT = 10567 + +const MAX_PEERS = 2 + + +var peer = null + +var player = { + idx = 0, + name = "", +} + +# Dictionary { id = { idx: 1, name: "" } } +var players = {} +var players_ready = [] + + +func _ready(): + get_tree().connect("network_peer_connected", self, '_player_connected') + get_tree().connect("network_peer_disconnected", self,"_player_disconnected") + get_tree().connect("connected_to_server", self, "_connection_succeeded") + get_tree().connect("connection_failed", self, "_connection_failed") + get_tree().connect("server_disconnected", self, "_server_disconnected") + Global.connect("game_won", self, "end_game") + + +remote func register_player(name): + var id = get_tree().get_rpc_sender_id() + self.players[id] = { + name = name + } + emit_signal("player_list_changed") + + +func unregister_player(id): + self.player.erase(id) + emit_signal("player_list_changed") + + +func host_game(player_name): + self.player.name = player_name + self.peer = NetworkedMultiplayerENet.new() + self.peer.create_server(self.PORT, self.MAX_PEERS) + get_tree().set_network_peer(self.peer) + + +func join_game(ip, player_name): + self.player.name = player_name + self.peer = NetworkedMultiplayerENet.new() + self.peer.create_client(ip, self.PORT) + get_tree().set_network_peer(self.peer) + + +func start_game(): + # preconfigure game and set idx to each player + var idx = 1 + for id in self.players: + self.players[id].idx = idx + rpc_id(id, "_preconfigure_game", idx) + idx += 1 + + # start game for everyone + rpc("_start_game") + + +remote func _preconfigure_game(idx): + self.player.idx = idx + + +sync func _start_game(): + get_tree().change_scene("res://UI/LevelSelect.tscn") + + +func end_game(winning_player): + # TODO: change scene for all to win scene and set winning player id, + # so it can be displayed with self.players + print(winning_player) + get_tree().change_scene("res://Network/Lobby.tscn") + pass + + +func _player_connected(id): + rpc_id(id, "register_player", self.player.name) + + +func _player_disconnected(id): + # if game is in progress: + # self.end_game() ? + # else + self.unregister_player(id) + + +func _connection_succeeded(): + emit_signal("connection_succeeded") + + +func _connection_failed(): + get_tree().set_network_peer(null) # remove peer + emit_signal("connection_failed") + + +func _server_disconnected(): + emit_signal("game_error", "Server disconnected!") + self.end_game("") diff --git a/Objects/Flag.tscn b/Objects/Flag.tscn index 0cc1c35..e2c44eb 100644 --- a/Objects/Flag.tscn +++ b/Objects/Flag.tscn @@ -7,6 +7,7 @@ extents = Vector2( 64.2993, 64.1957 ) [node name="Flag" type="Area2D"] +collision_layer = 0 script = ExtResource( 2 ) [node name="Sprite" type="Sprite" parent="."] diff --git a/UI/LevelSelect.gd b/UI/LevelSelect.gd index c173a7b..7abc5f2 100644 --- a/UI/LevelSelect.gd +++ b/UI/LevelSelect.gd @@ -2,10 +2,14 @@ extends Control func _ready(): + Global.connect("level_map_updated", self, "draw") + draw() + + +func draw(): var Cell = load("res://UI/LevelSelectCell.tscn") for idx in range(9): - var level = Global.get_level(idx) var cell = Cell.instance() cell.level_idx = idx cell.set_rect_size(1024/3, 600/3) diff --git a/UI/LevelSelectCell.gd b/UI/LevelSelectCell.gd index 1bd1366..16ff961 100644 --- a/UI/LevelSelectCell.gd +++ b/UI/LevelSelectCell.gd @@ -8,12 +8,13 @@ func _ready(): var level = Global.get_level(self.level_idx) $ClearMark.text = "" - if level.cleared_by == Enum.PLAYER.FIRST: + if level.cleared_by.idx == Enum.PLAYER.FIRST: $ClearMark.text = "X" - elif level.cleared_by == Enum.PLAYER.SECOND: + elif level.cleared_by.idx == Enum.PLAYER.SECOND: $ClearMark.text = "O" - $Time.text = String(level.time) + "s" + $Time.text = str(level.time) + "s" + $Name.text = level.cleared_by.name func set_rect_size(x, y): diff --git a/UI/LevelSelectCell.tscn b/UI/LevelSelectCell.tscn index 968e913..3660cef 100644 --- a/UI/LevelSelectCell.tscn +++ b/UI/LevelSelectCell.tscn @@ -56,3 +56,15 @@ valign = 1 __meta__ = { "_edit_use_anchors_": false } + +[node name="Name" type="Label" parent="."] +margin_left = 8.0 +margin_top = 176.0 +margin_right = 66.0 +margin_bottom = 197.0 +custom_fonts/font = SubResource( 3 ) +text = "Player" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/project.godot b/project.godot index 19ec07a..c47face 100644 --- a/project.godot +++ b/project.godot @@ -16,12 +16,14 @@ _global_script_class_icons={ [application] config/name="TicTacTux" +run/main_scene="res://Network/Lobby.tscn" config/icon="res://icon.png" [autoload] -Global="*res://Global.gd" Enum="*res://Enum.gd" +Global="*res://Global.gd" +Network="*res://Network/Network.gd" [input] @@ -41,6 +43,13 @@ ui_up={ ] } +[layer_names] + +2d_physics/layer_1="Player" +2d_physics/layer_2="Platform" +2d_physics/layer_3="Object" +2d_physics/layer_4="Enemy" + [rendering] environment/default_environment="res://default_env.tres" |