summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Character.tscn12
-rw-r--r--Global.gd56
-rw-r--r--Levels/Level_0001.tscn7
-rw-r--r--Network/Lobby.gd82
-rw-r--r--Network/Lobby.tscn101
-rw-r--r--Network/Network.gd114
-rw-r--r--Objects/Flag.tscn1
-rw-r--r--UI/LevelSelect.gd6
-rw-r--r--UI/LevelSelectCell.gd7
-rw-r--r--UI/LevelSelectCell.tscn12
-rw-r--r--project.godot11
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 )
diff --git a/Global.gd b/Global.gd
index d9414c8..7371ff5 100644
--- a/Global.gd
+++ b/Global.gd
@@ -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"