diff options
| author | Anson Bridges <bridges.anson@gmail.com> | 2025-08-19 12:38:02 -0700 |
|---|---|---|
| committer | Anson Bridges <bridges.anson@gmail.com> | 2025-08-19 12:38:02 -0700 |
| commit | 255fbf19cc9499ef384d41f68515da5e49e8a3ce (patch) | |
| tree | 13c838229198383b24644f613787e34842ea7ab2 | |
| parent | f087c6a98b1da55525a6e3c1d7c82477f82eb5cd (diff) | |
added menus, reworking GC client architecture
| -rw-r--r-- | network/GCClient.tscn | 6 | ||||
| -rw-r--r-- | network/GameCoordinatorClient.gd | 101 | ||||
| -rw-r--r-- | network/WSClient.tscn | 6 | ||||
| -rw-r--r-- | network/websocket_client_basic.gd (renamed from network/websocket_client.gd) | 1 | ||||
| -rw-r--r-- | objects/HexSpace.tscn | 1 | ||||
| -rw-r--r-- | pages/GameTable.tscn | 60 | ||||
| -rw-r--r-- | pages/MainMenu.tscn | 299 | ||||
| -rw-r--r-- | pages/MainScene.tscn | 15 | ||||
| -rw-r--r-- | pages/ServerBrowser.tscn | 8 | ||||
| -rw-r--r-- | project.godot | 2 | ||||
| -rw-r--r-- | resources/MenuOptions.theme | bin | 0 -> 337 bytes | |||
| -rw-r--r-- | resources/external/game_coordinator.py | 21 | ||||
| -rw-r--r-- | scripts/Board.gd | 213 | ||||
| -rw-r--r-- | scripts/GameTable.gd | 245 | ||||
| -rw-r--r-- | scripts/Globals.gd | 28 | ||||
| -rw-r--r-- | scripts/HexSpace.gd | 86 | ||||
| -rw-r--r-- | scripts/MainMenu.gd | 84 | ||||
| -rw-r--r-- | scripts/MainScene.gd | 13 | ||||
| -rw-r--r-- | scripts/Plane.gd | 15 | ||||
| -rw-r--r-- | scripts/ServerBrowser.gd | 5 |
20 files changed, 963 insertions, 246 deletions
diff --git a/network/GCClient.tscn b/network/GCClient.tscn new file mode 100644 index 0000000..2b04171 --- /dev/null +++ b/network/GCClient.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://network/GameCoordinatorClient.gd" type="Script" id=1] + +[node name="GCClient" type="Node"] +script = ExtResource( 1 ) diff --git a/network/GameCoordinatorClient.gd b/network/GameCoordinatorClient.gd new file mode 100644 index 0000000..3d87b7b --- /dev/null +++ b/network/GameCoordinatorClient.gd @@ -0,0 +1,101 @@ +extends Node + +# CONNECTION DATA +enum { NONE, SERVER_BROWSER, LOBBY, VOICE } +var socket_client: WebSocketClient = null +var socket: WebSocketPeer = null +var state: int = 0 # -1 = CONNECTION_FAILED, 0 = CONNECTION_DISCONNECTED, 1 = CONNECTION_CONNECTING, 2 = CONNECTION_CONNECTED, 3 = CONNECTION_DISCONNECTING +var message_queue : Array = [] # messages to be sent upon connection +var connection_type : int = NONE + +# GC DATA +var is_host : bool +var host_id : String +var lobby_id : String +var player_id : String +var rejoin_key : String +var players : Array = [] + +func _ready(): + socket_client = WebSocketClient.new() + socket_client.connect("connection_established", self, "on_connection_success") + socket_client.connect("connection_closed", self, "on_connection_close_success") + socket_client.connect("connection_error", self, "on_connection_error") + #socket_client.connect("data_received", self, "receive") + +func connect_to_gc(c_type: int): + if !(c_type in [SERVER_BROWSER, LOBBY, VOICE]): + print("Invalid connection type!") + return + connection_type = c_type + var url : String = Globals.GC_URL + print("Connecting to game coordinator: %s..." % url) + message_queue.clear() + var error = socket_client.connect_to_url(url) + if error != OK: + return error + + state = 1 # CONNECTING + return OK + +func sock_close(code = 1000, reason = ""): + print("Closing websocket...") + socket_client.disconnect_from_host(code, reason) + state = 3 # DISCONNECTING + +func on_connection_success(protocol): + print("WebSocket connection success with protocol %s." % protocol) + socket = socket_client.get_peer(1) + socket.set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) # defaults to text mode + state = 2 # CONNECTED + + while len(message_queue) > 0: + var msg = message_queue.pop_at(0) + send(msg) + +func on_connection_close_success(clean): + print("WebSocket closed successfully.") + socket = null + connection_type = NONE + if clean: + state = 0 # DISCONNECTED + else: + state = -1 # DISCONNECT DIRTY + +func on_connection_error(): # connection failed + print("WebSocket connection failed!") + socket = null + state = -1 # DISCONNECT DIRTY + connection_type = NONE + +func send(message, as_bytes=false) -> int: + if state != 2: + message_queue.push_back(message) + return -1 + return socket.put_packet(message) + + +func send_json(message) -> int: + if state != 2: + message_queue.push_back(JSON.print(message).to_utf8()) + return -1 + var message_json = JSON.print(message).to_utf8() + return socket.put_packet(message_json) + +func receive(string_to_json=false): + if state != 2: return null + if socket.get_available_packet_count() < 1: return null + var packet : PoolByteArray = socket.get_packet() + if socket.was_string_packet(): + var message = packet.get_string_from_utf8() + if string_to_json: + var json = JSON.parse(message) + if json.error: + return null + message = json.result + return message + return bytes2var(packet) + + +func _process(_delta): + socket_client.poll() diff --git a/network/WSClient.tscn b/network/WSClient.tscn deleted file mode 100644 index 2224ed5..0000000 --- a/network/WSClient.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://network/websocket_client.gd" type="Script" id=1] - -[node name="WSClient" type="Node"] -script = ExtResource( 1 ) diff --git a/network/websocket_client.gd b/network/websocket_client_basic.gd index d10f518..353dd81 100644 --- a/network/websocket_client.gd +++ b/network/websocket_client_basic.gd @@ -3,7 +3,6 @@ extends Node var socket_client: WebSocketClient = null var socket: WebSocketPeer = null var state: int = 0 # -1 = CONNECTION_FAILED, 0 = CONNECTION_DISCONNECTED, 1 = CONNECTION_CONNECTING, 2 = CONNECTION_CONNECTED, 3 = CONNECTION_DISCONNECTING -var id var message_queue : Array = [] # messages to be sent upon connection diff --git a/objects/HexSpace.tscn b/objects/HexSpace.tscn index 121a5a9..2fa1d71 100644 --- a/objects/HexSpace.tscn +++ b/objects/HexSpace.tscn @@ -157,6 +157,7 @@ font = ExtResource( 2 ) [node name="AirportIcon" type="Sprite3D" parent="Airport"] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.366498, 0.366924, 0.20708 ) +visible = false pixel_size = 0.0035 billboard = 1 transparent = false diff --git a/pages/GameTable.tscn b/pages/GameTable.tscn index 25a4c16..efd6540 100644 --- a/pages/GameTable.tscn +++ b/pages/GameTable.tscn @@ -1,8 +1,8 @@ -[gd_scene load_steps=7 format=2] +[gd_scene load_steps=8 format=2] [ext_resource path="res://scripts/GameTable.gd" type="Script" id=1] [ext_resource path="res://textures/wood_board_knotty.png" type="Texture" id=2] -[ext_resource path="res://objects/Plane.tscn" type="PackedScene" id=3] +[ext_resource path="res://scripts/Board.gd" type="Script" id=3] [sub_resource type="Environment" id=1] background_mode = 1 @@ -22,10 +22,52 @@ albedo_texture = ExtResource( 2 ) material = SubResource( 2 ) size = Vector2( 30, 30 ) +[sub_resource type="GDScript" id=4] +resource_name = "testing_camera" +script/source = "extends Camera + +const max_scroll_distance : float = 10.0 +const min_scroll_distance : float = 2.0 +onready var scroll_distance : float = transform.origin.z +const scroll_step : float = 0.2 + +var mouse_clicked : bool = false +const max_pitch : float = -15.0 # deg +const min_pitch : float = -90.0 +const pan_factor : float = 0.5 + +onready var yaw = get_node(\"../..\") +onready var pitch = get_node(\"..\") + +func _ready(): + pass + +func _input(event): + if event is InputEventMouseButton: + if event.button_index == BUTTON_LEFT: + mouse_clicked = event.pressed + if event.button_index == BUTTON_WHEEL_UP and event.pressed: + if scroll_distance > min_scroll_distance: + scroll_distance -= scroll_step + if event.button_index == BUTTON_WHEEL_DOWN and event.pressed: + if scroll_distance < max_scroll_distance: + scroll_distance += scroll_step + transform.origin.z = scroll_distance + if event is InputEventMouseMotion and mouse_clicked: + var mouse_dir : Vector2 = event.get_relative() + yaw.rotation_degrees.y -= mouse_dir.x * pan_factor + + var new_pitch = pitch.rotation_degrees.x - mouse_dir.y * pan_factor*2 + if new_pitch < max_pitch and new_pitch > min_pitch: + transform.origin.y = -2 * (1 - (new_pitch - max_pitch) / (min_pitch - max_pitch)) + pitch.rotation_degrees.x = new_pitch +" + [node name="GameTable" type="Spatial"] script = ExtResource( 1 ) [node name="Board" type="Spatial" parent="."] +script = ExtResource( 3 ) [node name="ActivePieces" type="Spatial" parent="."] @@ -40,15 +82,15 @@ spot_range = 45.4139 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource( 1 ) -[node name="Camera" type="Camera" parent="."] -transform = Transform( 1, 0, 0, 0, 0.965939, 0.258768, 0, -0.258768, 0.965939, 0, 3.30213, 10.1497 ) - [node name="Tabletop" type="MeshInstance" parent="."] transform = Transform( 1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0 ) mesh = SubResource( 3 ) -[node name="Plane" parent="." instance=ExtResource( 3 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.625405, 1.50965, -2.27158 ) +[node name="CameraHingeYaw" type="Spatial" parent="."] + +[node name="CameraHingePitch" type="Spatial" parent="CameraHingeYaw"] +transform = Transform( 1, 0, 0, 0, 0.965926, 0.258819, 0, -0.258819, 0.965926, 0, 0, 0 ) -[node name="Plane2" parent="." instance=ExtResource( 3 )] -transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -0.92594, 0.711815, -1.28117 ) +[node name="Camera" type="Camera" parent="CameraHingeYaw/CameraHingePitch"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7 ) +script = SubResource( 4 ) diff --git a/pages/MainMenu.tscn b/pages/MainMenu.tscn new file mode 100644 index 0000000..d216836 --- /dev/null +++ b/pages/MainMenu.tscn @@ -0,0 +1,299 @@ +[gd_scene load_steps=17 format=2] + +[ext_resource path="res://scripts/MainMenu.gd" type="Script" id=1] +[ext_resource path="res://resources/fonts/Cochineal-Bold.otf" type="DynamicFontData" id=2] +[ext_resource path="res://resources/fonts/Cochineal-Roman.otf" type="DynamicFontData" id=3] +[ext_resource path="res://resources/MenuOptions.theme" type="Theme" id=4] + +[sub_resource type="DynamicFont" id=29] +size = 72 +font_data = ExtResource( 2 ) + +[sub_resource type="DynamicFont" id=1] +size = 72 +font_data = ExtResource( 2 ) + +[sub_resource type="Theme" id=2] +default_font = SubResource( 1 ) +Button/fonts/font = SubResource( 29 ) + +[sub_resource type="DynamicFont" id=30] +size = 45 +font_data = ExtResource( 2 ) + +[sub_resource type="Theme" id=31] +default_font = SubResource( 1 ) +Button/fonts/font = SubResource( 30 ) + +[sub_resource type="Gradient" id=15] +offsets = PoolRealArray( 0 ) +colors = PoolColorArray( 0, 0, 0, 1 ) + +[sub_resource type="GradientTexture2D" id=22] +gradient = SubResource( 15 ) +width = 32 +height = 32 + +[sub_resource type="Gradient" id=23] +offsets = PoolRealArray( 0 ) +colors = PoolColorArray( 0, 0, 0, 1 ) + +[sub_resource type="GradientTexture2D" id=21] +gradient = SubResource( 23 ) +width = 32 +height = 32 + +[sub_resource type="GDScript" id=24] +resource_name = "set_colors_builtin" +script/source = "extends OptionButton + + +func _ready(): + for i in range(get_item_count()): + set_item_icon(i, get_item_icon(i).duplicate()) + var icon = get_item_icon(i) + icon.gradient = icon.gradient.duplicate() + icon.gradient.set_color(0, Globals.colors[i]) + icon.gradient.colors.remove(0) + + +" + +[sub_resource type="DynamicFont" id=27] +size = 95 +font_data = ExtResource( 3 ) + +[sub_resource type="Theme" id=28] +default_font = SubResource( 27 ) + +[node name="MainMenu" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_pivot_offset = Vector2( 560, -158 ) +script = ExtResource( 1 ) + +[node name="HostMenuButton" type="Button" parent="."] +anchor_left = 0.1 +anchor_top = 0.25 +anchor_right = 0.25 +anchor_bottom = 0.35 +theme = SubResource( 2 ) +text = "HOST" + +[node name="JoinMenuButton" type="Button" parent="."] +anchor_left = 0.1 +anchor_top = 0.4 +anchor_right = 0.25 +anchor_bottom = 0.5 +theme = SubResource( 2 ) +text = "JOIN" + +[node name="SettingsButton" type="Button" parent="."] +anchor_left = 0.1 +anchor_top = 0.55 +anchor_right = 0.35 +anchor_bottom = 0.65 +theme = SubResource( 2 ) +text = "SETTINGS" + +[node name="BackButton" type="Button" parent="."] +visible = false +anchor_left = 0.1 +anchor_top = 0.88 +anchor_right = 0.212 +anchor_bottom = 0.963 +theme = SubResource( 31 ) +text = "BACK" + +[node name="PlayerInfo" type="GridContainer" parent="."] +visible = false +anchor_left = 0.1 +anchor_top = 0.257 +anchor_right = 0.358 +anchor_bottom = 0.9 +columns = 2 + +[node name="UsernameLabel" type="Label" parent="PlayerInfo"] +margin_top = 5.0 +margin_right = 138.0 +margin_bottom = 38.0 +theme = ExtResource( 4 ) +text = "Username:" +align = 2 + +[node name="Username" type="LineEdit" parent="PlayerInfo"] +margin_left = 142.0 +margin_right = 342.0 +margin_bottom = 43.0 +rect_min_size = Vector2( 200, 0 ) +theme = ExtResource( 4 ) +text = "Player" +max_length = 32 + +[node name="ColorLabel" type="Label" parent="PlayerInfo"] +margin_top = 50.0 +margin_right = 138.0 +margin_bottom = 83.0 +theme = ExtResource( 4 ) +text = "Color:" +align = 2 + +[node name="PlayerColor" type="OptionButton" parent="PlayerInfo"] +margin_left = 142.0 +margin_top = 47.0 +margin_right = 202.0 +margin_bottom = 87.0 +rect_min_size = Vector2( 60, 40 ) +size_flags_horizontal = 0 +icon = SubResource( 22 ) +expand_icon = true +items = [ "", SubResource( 21 ), false, 0, null, "", SubResource( 21 ), false, 1, null, "", SubResource( 21 ), false, 2, null, "", SubResource( 21 ), false, 3, null, "", SubResource( 21 ), false, 4, null, "", SubResource( 21 ), false, 5, null, "", SubResource( 21 ), false, 6, null, "", SubResource( 21 ), false, 7, null, "", SubResource( 21 ), false, 8, null, "", SubResource( 21 ), false, 9, null ] +selected = 0 +script = SubResource( 24 ) + +[node name="AltColorLabel" type="Label" parent="PlayerInfo"] +margin_top = 94.0 +margin_right = 138.0 +margin_bottom = 127.0 +theme = ExtResource( 4 ) +text = "Alt color:" +align = 2 + +[node name="AltPlayerColor" type="OptionButton" parent="PlayerInfo"] +margin_left = 142.0 +margin_top = 91.0 +margin_right = 202.0 +margin_bottom = 131.0 +rect_min_size = Vector2( 60, 40 ) +size_flags_horizontal = 0 +icon = SubResource( 21 ) +expand_icon = true +items = [ "", SubResource( 21 ), false, 0, null, "", SubResource( 21 ), false, 1, null, "", SubResource( 21 ), false, 2, null, "", SubResource( 21 ), false, 3, null, "", SubResource( 21 ), false, 4, null, "", SubResource( 21 ), false, 5, null, "", SubResource( 21 ), false, 6, null, "", SubResource( 21 ), false, 7, null, "", SubResource( 21 ), false, 8, null, "", SubResource( 21 ), false, 9, null ] +selected = 1 +script = SubResource( 24 ) + +[node name="Title" type="Label" parent="."] +anchor_left = 0.075 +anchor_top = 0.067 +anchor_right = 0.75 +anchor_bottom = 0.069 +margin_right = 40.0 +margin_bottom = 14.0 +theme = SubResource( 28 ) +text = "ATC: AIR TRAFFIC CHAOS" + +[node name="HostMenu" type="Control" parent="."] +visible = false +anchor_left = 0.1 +anchor_top = 0.45 +anchor_right = 1.0 +anchor_bottom = 0.8 +margin_right = 40.0 +margin_bottom = 40.0 + +[node name="GameName" type="LineEdit" parent="HostMenu"] +anchor_right = 0.229 +theme = ExtResource( 4 ) +text = "Player's Game" +max_length = 39 + +[node name="HostButton" type="Button" parent="HostMenu"] +anchor_top = 0.597 +anchor_right = 0.229 +anchor_bottom = 0.698 +theme = SubResource( 2 ) +text = "Host" + +[node name="PlayerCountLabel" type="Label" parent="HostMenu"] +anchor_top = 0.17 +anchor_bottom = 0.17 +theme = ExtResource( 4 ) +text = "Players:" + +[node name="PlayerCount" type="OptionButton" parent="HostMenu"] +anchor_left = 0.083 +anchor_top = 0.165 +anchor_right = 0.229 +anchor_bottom = 0.196 +theme = ExtResource( 4 ) +text = "2" +items = [ "2", null, false, 2, null, "3", null, false, 3, null, "4", null, false, 4, null, "5", null, false, 5, null, "6", null, false, 6, null ] +selected = 0 + +[node name="PrivateToggle" type="CheckButton" parent="HostMenu"] +anchor_top = 0.3 +anchor_bottom = 0.3 +size_flags_horizontal = 0 +size_flags_vertical = 0 +theme = ExtResource( 4 ) +text = "Password Protected" + +[node name="Password" type="LineEdit" parent="HostMenu"] +visible = false +anchor_top = 0.428 +anchor_right = 0.23 +anchor_bottom = 0.465 +theme = ExtResource( 4 ) +max_length = 16 +placeholder_text = "Password" +placeholder_alpha = 0.587 + +[node name="JoinMenu" type="Control" parent="."] +visible = false +anchor_left = 0.1 +anchor_top = 0.45 +anchor_right = 1.0 +anchor_bottom = 0.8 +margin_right = 40.0 +margin_bottom = 40.0 + +[node name="LobbyID" type="LineEdit" parent="JoinMenu"] +anchor_right = 0.229 +theme = ExtResource( 4 ) +max_length = 39 +placeholder_text = "Lobby ID" +placeholder_alpha = 0.389 + +[node name="Password" type="LineEdit" parent="JoinMenu"] +anchor_top = 0.165 +anchor_right = 0.229 +anchor_bottom = 0.165 +theme = ExtResource( 4 ) +max_length = 39 +placeholder_text = "Password (if private)" +placeholder_alpha = 0.389 + +[node name="JoinButton" type="Button" parent="JoinMenu"] +anchor_top = 0.321 +anchor_right = 0.229 +anchor_bottom = 0.465 +theme = SubResource( 2 ) +text = "JOIN " + +[node name="SettingsMenu" type="GridContainer" parent="."] +visible = false +anchor_left = 0.1 +anchor_top = 0.267 +anchor_right = 1.0 +anchor_bottom = 0.8 +margin_right = 40.0 +margin_bottom = 40.0 +columns = 2 + +[node name="GCURLLabel" type="Label" parent="SettingsMenu"] +margin_top = 5.0 +margin_right = 322.0 +margin_bottom = 38.0 +theme = ExtResource( 4 ) +text = "Game Coordinator URL: " +align = 2 + +[node name="GameCoordinatorURL" type="LineEdit" parent="SettingsMenu"] +margin_left = 326.0 +margin_right = 676.0 +margin_bottom = 43.0 +rect_min_size = Vector2( 350, 0 ) +theme = ExtResource( 4 ) +max_length = 39 +placeholder_text = "Game Coordinator URL" +placeholder_alpha = 0.389 diff --git a/pages/MainScene.tscn b/pages/MainScene.tscn new file mode 100644 index 0000000..da9bdcd --- /dev/null +++ b/pages/MainScene.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://network/websocket_client_basic.gd" type="Script" id=1] +[ext_resource path="res://pages/MainMenu.tscn" type="PackedScene" id=2] +[ext_resource path="res://scripts/MainScene.gd" type="Script" id=3] + +[node name="MainScene" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 3 ) + +[node name="GameCoordinator" type="Node" parent="."] +script = ExtResource( 1 ) + +[node name="MainMenu" parent="." instance=ExtResource( 2 )] diff --git a/pages/ServerBrowser.tscn b/pages/ServerBrowser.tscn index c4fcc61..c5d2ef4 100644 --- a/pages/ServerBrowser.tscn +++ b/pages/ServerBrowser.tscn @@ -1,14 +1,14 @@ [gd_scene load_steps=6 format=2] -[ext_resource path="res://network/websocket_client.gd" type="Script" id=1] +[ext_resource path="res://network/websocket_client_basic.gd" type="Script" id=1] [ext_resource path="res://scripts/ServerBrowser.gd" type="Script" id=2] -[sub_resource type="Gradient" id=15] +[sub_resource type="Gradient" id=23] offsets = PoolRealArray( 0 ) colors = PoolColorArray( 0, 0, 0, 1 ) [sub_resource type="GradientTexture2D" id=21] -gradient = SubResource( 15 ) +gradient = SubResource( 23 ) width = 32 height = 32 @@ -23,7 +23,6 @@ func _ready(): var icon = get_item_icon(i) icon.gradient = icon.gradient.duplicate() icon.gradient.set_color(0, Globals.colors[i]) - print(icon.gradient.colors) icon.gradient.colors.remove(0) @@ -92,6 +91,7 @@ text = "Player" max_length = 32 [node name="HostPopup" type="PopupPanel" parent="."] +visible = true margin_left = 383.0 margin_top = 129.0 margin_right = 633.0 diff --git a/project.godot b/project.godot index 68af7ba..e90fb8a 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=4 [application] config/name="ATC" -run/main_scene="res://network/GameCoordinatorTester.tscn" +run/main_scene="res://pages/MainScene.tscn" boot_splash/show_image=false config/icon="res://icon.png" diff --git a/resources/MenuOptions.theme b/resources/MenuOptions.theme Binary files differnew file mode 100644 index 0000000..899c3df --- /dev/null +++ b/resources/MenuOptions.theme diff --git a/resources/external/game_coordinator.py b/resources/external/game_coordinator.py index bd2eb9c..0f04e18 100644 --- a/resources/external/game_coordinator.py +++ b/resources/external/game_coordinator.py @@ -15,13 +15,15 @@ import secrets import sys import random import time +from string import ascii_uppercase # optional, for 4-letter id format import websockets from websockets.asyncio.server import serve DEFAULT_PORT = 8181 -DEFAULT_IP = "192.168.7.112" +DEFAULT_IP = "" +MAX_LOBBY_ID_GEN_ATTEMPTS = 20 LOBBY_CONTROL_COMMANDS = ["disconnect", "kick_player", "get_lobby_info", "deny_player", "accept_player", "end_lobby", "set_lobby_locked", "set_lobby_state", "set_paused"] HOST_LOBBY_REQD_FIELDS = ["lobby_name", "game_type", "username", "private", "password", "max_players"] HOST_LOBBY_REQD_FIELD_TYPES = [str, str, str, bool, str, int] @@ -46,6 +48,19 @@ async def send_lobby_list(websocket): """ LOBBY FUNCTIONS """ +# generate lobby id +async def generate_lobby_id(): + new_id = None + attempts = 0 + while (attempts < MAX_LOBBY_ID_GEN_ATTEMPTS) and ( (not new_id) or (new_id in LOBBIES) ): + new_id = ''.join(random.choices(ascii_uppercase, k=4)) # DEFINE FID FORMAT HERE + attempts += 1 + + if attempts >= MAX_LOBBY_ID_GEN_ATTEMPTS: + return secrets.token_urlsafe(12) # fall back to non-repeating lobby ID + return new_id + + # get information of all connected players to lobby for async def get_player_info(lobby): players = [] @@ -268,7 +283,7 @@ async def lobby_loop(websocket, lobby, player): # message forwarding elif event["type"] == "request": - response = {"type" : "request", "destination" : player_id, "data" : event["request_fields"] } + response = {"type" : "request", "destination" : player_id, "request_fields" : event["request_fields"] } await game["players"][ event["source"] ]["socket"].send( json.dumps(response) ) elif event["type"] == "request_response": response = {"type" : "request_response", "data" : event["requested_data"] } @@ -397,7 +412,7 @@ async def host_lobby(websocket, event): host_player = await create_player(websocket, event["username"], is_host=True) - lobby_id = secrets.token_urlsafe(12) + lobby_id = await generate_lobby_id() new_lobby = { "lobby_id" : lobby_id, "lobby_name" : event["lobby_name"], "game_type" : event["game_type"], diff --git a/scripts/Board.gd b/scripts/Board.gd new file mode 100644 index 0000000..f18ca74 --- /dev/null +++ b/scripts/Board.gd @@ -0,0 +1,213 @@ +extends Spatial + +enum { Y, X } + +# hex board represented in square-grid form like so (e.g., 3-length-side hex grid): +# x x x +# x x x x +# x x x x x +# x x x x +# x x x +# going up and to the right is done by decreasing the row by 1 +# going up and to the left is done by decreasing the row by 1 and the column by 1 +var board: Array = [] # 2D Array of JSON objects describing the board, which can be turned into objects +var board_display: Array = [] +var available_board_coords: Array = [] # for population purposes +var airports = {} # id : HexSpace of cell_type airport + +var side_len: int + + +# Y R B G +var airport_colors = [ Color(1, 1, 0), Color(1, 0, 0), Color(0.3, 0.3, 1), Color(0, 0.8, 0) ] +enum { NUMBER, COLOR } + +onready var hex_space = preload("res://objects/HexSpace.tscn") + +# cell types +enum { PLAIN, HILLS, MOUNTAINS, AIRPORT } + +# directions: E, NE, NW, W, SW, SE +const adjacent_offsets = [ [0,1] , [-1, 0], [-1, -1], [0, -1], [1, 0], [1, 1] ] + +# indices of the offsets that are valid cells to approach from +const approaches_i: Array= [ [0, 1, 2, 3, 4, 5], [0,1,3,4], [0,3] ] + + +func _ready(): + pass + +func reset_board(): + for node in get_children(): + node.queue_free() + board.clear() + board_display.clear() + available_board_coords.clear() + +func create_board_base(hex_side_length : int): + side_len = hex_side_length + var number_of_cells = 3*( pow(hex_side_length, 2) - hex_side_length) + 1 + + reset_board() + + var board_diameter = hex_side_length * 2 - 1 + for r in range(board_diameter): + var row_length: int = board_diameter - abs(r-(hex_side_length-1)) + + var row = [] + row.resize(board_diameter) + row.fill(null) # not in hex grid + + var offset : int = 0 + if r > (hex_side_length - 1): offset = r - (hex_side_length - 1) + for i in range(row_length): + row[offset+i] = { "cell_type" : PLAIN, "pos" : [r, offset+i] } # ground cell + available_board_coords.push_back( [r, offset+i] ) + + board.append(row) + + +func display_board(): + var cell_size_x = 1 # distance between center of two adjacent hex cells + var row_offset_y:float = cos(deg2rad(30)) * cell_size_x + var board_diam:int = len(board) + var side_len:int = ( board_diam + 1 ) / 2 + + for r in range(board_diam): + var row_display = [] + row_display.resize(board_diam) + row_display.fill(null) + var row = board[r] + var z = row_offset_y * (r - board_diam/2) + var offset_x = abs(side_len - (r+1)) * (cell_size_x / 2.0) if (r+1) <= side_len else -1*abs(side_len - (r+1)) * (cell_size_x/2.0) + offset_x -= board_diam/2 * cell_size_x + for c in range(board_diam): + if row[c] == null: continue + var x = offset_x + c * cell_size_x + + var new_cell = hex_space.instance() + new_cell.call_deferred("set", "global_position", Vector3(x, randf()/15, z)) + new_cell.set_up(row[c]) + add_child(new_cell) + row_display[c] = new_cell + board_display.push_back(row_display) + + +# populate board with airports, hills, and mountains +# depending on game settings +func populate_board(num_mountains : int, num_hills : int, num_airports : int, runway_count : int, use_names : bool = false) -> bool: + var board_diam:int = len(board) + + for _m in range(num_mountains): + if len(available_board_coords) < 1: return false + var spot_i:int = randi() % len(available_board_coords) + var spot = available_board_coords[ spot_i ] + var args = {"cell_type" : MOUNTAINS, "orientation" : randi() % 6, "pos" : [spot[Y], spot[X]]} + board[spot[Y]][spot[X]] = args + available_board_coords.pop_at(spot_i) + + for _h in range(num_hills): + if len(available_board_coords) < 1: return false + var spot_i:int = randi() % len(available_board_coords) + var spot = available_board_coords[ spot_i ] + var args = { "cell_type" : HILLS, "orientation" : randi() % 6, "pos" : [spot[Y], spot[X]] } + board[spot[Y]][spot[X]] = args + available_board_coords.pop_at(spot_i) + + # airport identification + var used_airports : Array = [] + + var airport_id:int = 0 + for a in range(num_airports): + var airport_display + if use_names: + airport_display = Globals.get_random_airport_name(used_airports) + else: + airport_display = [ randi() % 9 + 1, randi() % 4 ] # number, color + while airport_display in used_airports: + airport_display = [ randi() % 9 + 1, randi() % 4 ] + # find valid spot + var spot_okay:bool = false + var rot:int + var spot_r:int + var spot_c:int + var spot_i:int + var valid_approaches = [] + var runways = (randi() % 3 + 1) if (runway_count == 0) else runway_count + while (not spot_okay) and (len(available_board_coords) > 0): + spot_i = randi() % len(available_board_coords) + var spot = available_board_coords[ spot_i ] + spot_r = spot[Y] + spot_c = spot[X] + + var has_adjacent_airport = false + for offset in adjacent_offsets: # away from other airports + var new_r: int = spot_r + offset[Y] + var new_c: int = spot_c + offset[X] + if new_r < 0 or new_c < 0 or new_r >= board_diam or new_c >= board_diam: # offset out of square grid + continue + var adjacent_cell = board[new_r][new_c] + if adjacent_cell != null and adjacent_cell["cell_type"] == AIRPORT: + has_adjacent_airport = true + break + if has_adjacent_airport: + available_board_coords.pop_at(spot_i) + continue + + spot_okay = true + + # find rotation that leaves at least 1 runway open + rot = randi() % 3 + var rot_okay = false + for _i in range(3): + var rot_approaches = adjacent_offsets.slice(rot, 5) + if rot != 0: rot_approaches += adjacent_offsets.slice(0, rot - 1) + + var possible_approaches = [] + for approach_index in approaches_i[runways]: + possible_approaches.push_back(rot_approaches[approach_index]) + + var has_runway = false + for approach in possible_approaches: + var app_r: int = spot_r + approach[0] + var app_c: int = spot_c + approach[1] + if app_r < 0 or app_r >= board_diam or app_c < 0 or app_c >= board_diam: continue # out of square map + if board[app_r][app_c] == null: continue # out of hex map + if board[app_r][app_c]["cell_type"] in [HILLS, MOUNTAINS]: continue # invalid approach square + has_runway = true + valid_approaches.push_back(approach) + + if has_runway: + rot_okay = true + break + else: + rot += 1 # rotate 60 deg (effectively) + if not rot_okay: + available_board_coords.pop_at(spot_i) + continue + + if not spot_okay: + return false # could not form valid map + + var args = {"cell_type" : AIRPORT , "pos" : [spot_r, spot_c], "orientation" : rot, "airport_id" : airport_id, "runways" : runways, 'valid_approach_offsets' : valid_approaches, "use_names" : use_names} + if use_names: + args["airport_name"] = airport_display + else: + args["airport_color"] = airport_colors[airport_display[COLOR]] + args["airport_number"] = airport_display[NUMBER] + board[spot_r][spot_c] = args + available_board_coords.pop_at(spot_i) + airport_id += 1 + return true + +func get_json(): + return board + +func generate_board(hex_side_len: int, num_mountains : int, num_hills : int, num_airports : int, runway_count : int, use_names : bool = false) -> bool: + create_board_base(hex_side_len) + if not populate_board(num_mountains, num_hills, num_airports, runway_count, use_names): + reset_board() + print("Invalid board creation parameters") + return false + display_board() + return true diff --git a/scripts/GameTable.gd b/scripts/GameTable.gd index cbee40f..b0bedf2 100644 --- a/scripts/GameTable.gd +++ b/scripts/GameTable.gd @@ -1,207 +1,68 @@ -tool extends Spatial -const num_mountains = {"easy" : 0, "medium" : 3, "hard" : 6} -const num_hills = {"easy" : 4, "medium" : 6, "hard" : 8} +# MULTIPLAYER DATA -export var hex_side_length = 6 setget set_hex_side_len -export var airports_per_color = 6 setget set_airports_per_color -export var num_airport_colors = 3 setget set_num_airport_colors -export var _generate_board_editor: bool = false setget generate_board_editor +var gc_client # to be assigned by MainScene upon game creation/joining -export (String, "easy", "medium", "hard") var game_difficulty = "easy" -# hex board represented in square-grid form like so (e.g., 3-length-side hex grid): -# x x x -# x x x x -# x x x x x -# x x x x -# x x x -# going up and to the right is done by decreasing the row by 1 -# going up and to the left is done by decreasing the row by 1 and the column by 1 -var board = [] -var available_board_coords = [] -enum { GROUND_LAYER, WEATHER_LAYER, PLANES_LAYER } -# Y R B G -var airport_colors = [ Color(1, 1, 0), Color(1, 0, 0), Color(0.3, 0.3, 1), Color(0, 0.8, 0) ] -var airports = {} # id : HexSpace of cell_type airport +# END MULTIPLAYER DATA -onready var hex_space = preload("res://objects/HexSpace.tscn") +# GAME DATA + +enum { PREGAME, PLACING, DETERMINE_ACTIONS, MOVEMENT } +var game_state: int +var BOARD_GEN_PARAMS = { + "airport_style" : 0, # 0 = colors + numbers, 1 = names + "num_airports" : 18, + "board_side_length" : 6, + "num_hills" : 8, + "num_mountains" : 4, + "runways_per_airport" : 0 # 0 random, 1-3 +} +var RULES = { + "fly_over_airports" : 0, # 1 = only at altitude 2, 2 = any altitude + "must_land_on_final_action" : 0, + "takeoff_action" : 2, # 0 - 4 + "move_forward_order" : 0, # 0 = either before or after, 1 = after, 2 = before + "gamemode" : 0, # 0 = cooperative, 1 = versus + "plane_assignment" : 0, # 0 = random, 1 = draft + "altitude_and_turn" : 0, # whether a plane can change altitude and turn in the same action + "event_frequency" : 1, # turns per event + "weather_enabled" : 1, + "misc_enabled" : 1, + "new_planes_per_turn" : 4, # 1 - 8 + "ramp_up_enabled" : 0, # whether to ramp up to max planes per turn + "starting_planes_per_player": 4, + "round_timer" : 60, # seconds +} + +onready var BOARD = $Board +var PLANES = [] + +var desired_player_count: int +var is_board_generated: bool = false # determine whether host can begin the game + +# END GAME DATA # directions: E, NE, NW, W, SW, SE const adjacent_offsets = [ [0,1] , [-1, 0], [-1, -1], [0, -1], [1, 0], [1, 1] ] -# indices of the offsets that are valid cells to approach from -const approaches_i = {"easy": [0, 1, 2, 3, 4, 5], "medium" : [0,1,3,4], "hard" : [0,3]} -func _ready(): - if not Engine.editor_hint: - generate_hex_board() - generate_board_cells() - populate_board() - -func set_hex_side_len(side_length): - hex_side_length = side_length - -func set_airports_per_color(num_airports): - airports_per_color = num_airports - -func set_num_airport_colors(num_colors): - num_airport_colors = num_colors - -func generate_hex_board(): - var number_of_cells = 3*( pow(hex_side_length, 2) - hex_side_length) + 1 - var player_spaces = number_of_cells - 1 # center should always be a mountain - for node in $Board.get_children(): - $Board.remove_child(node) - board = [] # reset board + contents - available_board_coords = [] - var board_diameter = hex_side_length * 2 - 1 - for r in range(board_diameter): - var row_length: int = board_diameter - abs(r-(hex_side_length-1)) - - var row = [] - row.resize(board_diameter) - row.fill(null) # not in hex grid - - if r <= (hex_side_length - 1): - for i in range(row_length): - row[i] = [ 1, [], [] ] # ground cell, weather effects, planes - else: - for i in range(row_length): - row[board_diameter-1-i] = [ 1, [], [] ] # ground cell, weather effects, planes - - board.append(row) - - -func generate_board_cells(): - var cell_size_x = 1 # distance between center of two adjacent hex cells - var row_offset_y:float = cos(deg2rad(30)) * cell_size_x - var board_diam:int = len(board) - var side_len:int = ( board_diam + 1 ) / 2 - - for r in range(board_diam): - var row = board[r] - var z = row_offset_y * (r - board_diam/2) - var offset_x = abs(side_len - (r+1)) * (cell_size_x / 2.0) if (r+1) <= side_len else -1*abs(side_len - (r+1)) * (cell_size_x/2.0) - offset_x -= board_diam/2 * cell_size_x - for c in range(board_diam): - if row[c] == null: continue - var x = offset_x + c * cell_size_x - - var new_cell = hex_space.instance() - new_cell.call_deferred("set", "global_position", Vector3(x, randf()/15, z)) - $Board.add_child(new_cell) - - board[r][c][GROUND_LAYER] = new_cell - if (r == c) and (r == (board_diam/2)): # central cell always a mountain - var cell_type = "mountain" - var args = {} - args["rotation"] = randi() % 6 - new_cell.set_up(cell_type, args) - else: - available_board_coords.push_back( [r, c] ) - -# populate board with airports, hills, and mountains -# depending on game settings -func populate_board(): - var board_diam:int = len(board) - - for _m in range(num_mountains[game_difficulty]): - if len(available_board_coords) < 1: return null - var spot_i:int = randi() % len(available_board_coords) - var spot = available_board_coords[ spot_i ] - var args = {"rotation" : randi() % 6} - board[spot[0]][spot[1]][GROUND_LAYER].set_up("mountain", args) - available_board_coords.pop_at(spot_i) - - for _h in range(num_hills[game_difficulty]): - if len(available_board_coords) < 1: return null - var spot_i:int = randi() % len(available_board_coords) - var spot = available_board_coords[ spot_i ] - var args = {"rotation" : randi() % 6} - board[spot[0]][spot[1]][GROUND_LAYER].set_up("hills", args) - available_board_coords.pop_at(spot_i) - - var airport_id:int = 0 - for c in range(num_airport_colors): - for a in range(airports_per_color): - # find valid spot - var spot_okay:bool = false - var rot:int - var spot_r:int - var spot_c:int - var spot_i:int - var valid_approaches = [] - while (not spot_okay) and (len(available_board_coords) > 0): - spot_i = randi() % len(available_board_coords) - var spot = available_board_coords[ spot_i ] - spot_r = spot[0] - spot_c = spot[1] - - # should no longer be necessary - #if board[spot_r][spot_c] == null: continue - - var has_adjacent_airport = false - for offset in adjacent_offsets: # away from other airports - var new_r: int = spot_r + offset[0] - var new_c: int = spot_c + offset[1] - if new_r < 0 or new_c < 0 or new_r >= board_diam or new_c >= board_diam: # offset out of square grid - continue - var adjacent_cell = board[new_r][new_c] - if adjacent_cell != null and adjacent_cell[GROUND_LAYER].cell_type == "airport": - has_adjacent_airport = true - break - if has_adjacent_airport: - available_board_coords.pop_at(spot_i) - continue - - spot_okay = true - - # find rotation that leaves at least 1 runway open - rot = randi() % 3 - var rot_okay = false - for _i in range(3): - var rot_approaches = adjacent_offsets.slice(rot, 5) - if rot != 0: rot_approaches += adjacent_offsets.slice(0, rot - 1) - - var possible_approaches = [] - for approach_index in approaches_i[game_difficulty]: - possible_approaches.push_back(rot_approaches[approach_index]) - - var has_runway = false - for approach in possible_approaches: - var app_r: int = spot_r + approach[0] - var app_c: int = spot_c + approach[1] - if app_r < 0 or app_r >= board_diam or app_c < 0 or app_c >= board_diam: continue # out of square map - if board[app_r][app_c] == null: continue # out of hex map - if board[app_r][app_c][GROUND_LAYER].cell_type in ["hills", "mountain"]: continue # invalid approach square - has_runway = true - valid_approaches.push_back(approach) - - if has_runway: - rot_okay = true - break - else: - rot += 1 # rotate 60 deg (effectively) - if not rot_okay: - available_board_coords.pop_at(spot_i) - continue - - if not spot_okay: - print('couldnt find spot') - return null # could not form valid map - #print(c, " ", a, "(", spot_r, ", ", spot_c, ")") - var args = {"rotation" : rot, "airport_color" : airport_colors[c], "airport_number" : a+1, "airport_id" : airport_id, "difficulty" : game_difficulty, 'valid_approaches' : valid_approaches} - board[spot_r][spot_c][GROUND_LAYER].set_up("airport", args) - available_board_coords.pop_at(spot_i) - airport_id += 1 +func _ready(): + pass + +func set_up(ws_client, is_host: bool, lobby_id: String, player_id: String, rejoin_key: String): + self.ws_client = ws_client + self.is_host = is_host + self.lobby_id = lobby_id + self.player_id = player_id + self.rejoin_key = rejoin_key + +# ask host for complete game state. returned fields will depend on game state +func request_complete_game_state(): + if is_host: return + var request = { "type" : "request", "source" : host_id, "requested_data" : "ALL" } - -func generate_board_editor(_gbe): - generate_hex_board() - generate_board_cells() - populate_board() diff --git a/scripts/Globals.gd b/scripts/Globals.gd index 18f3d19..3603dbd 100644 --- a/scripts/Globals.gd +++ b/scripts/Globals.gd @@ -1,7 +1,33 @@ extends Node # Y R B G W Cy Pk O P dG -const colors = [ Color(1, 1, 0), Color(1, 0, 0), Color(0.3, 0.3, 1), Color(0, 0.8, 0), Color(1, 1, 1), Color(0, 1, 1), Color(1, .35, 1), Color(1, 0.4, 0), Color(0.38, 0, 0.38), Color(0, 0.4, 0) ] +const colors : Array = [ Color(1, 1, 0), Color(1, 0, 0), Color(0.3, 0.3, 1), Color(0, 0.8, 0), Color(1, 1, 1), Color(0, 1, 1), Color(1, .35, 1), Color(1, 0.4, 0), Color(0.38, 0, 0.38), Color(0, 0.4, 0) ] + +const airport_names_file : String = 'res://resources/airports.txt' +var airport_names : Array = [] + +const DEFAULT_GC_URL : String = "ws://192.168.7.112:8181" +var GC_URL : String = "ws://192.168.7.112:8181" func _ready(): + load_airport_names() set_process(false) + +func update_gc_url(new_url : String): + GC_URL = new_url + + + +func load_airport_names(): + var f = File.new() + f.open(airport_names_file, File.READ) + var index = 1 + while not f.eof_reached(): # iterate through all lines until the end of file is reached + airport_names.push_back(f.get_line()) + f.close() + +func get_random_airport_name(exceptions=[]): + var name_index:int = randi() % len(airport_names) + while airport_names[name_index] in exceptions: + name_index = randi() % len(airport_names) + return airport_names[name_index] diff --git a/scripts/HexSpace.gd b/scripts/HexSpace.gd index c8269fd..bcf64f1 100644 --- a/scripts/HexSpace.gd +++ b/scripts/HexSpace.gd @@ -1,50 +1,92 @@ tool extends StaticBody -var cell_type : String = "normal" +enum { PLAIN, HILLS, MOUNTAINS, AIRPORT } +enum { Y, X } + +# general cell variables +var x : int = -1 +var y : int = -1 +var cell_type : int = PLAIN +var orientation : int = 0 # 0 - 5 # airport variables var airport_number : int var airport_color : Color var airport_id : int +var airport_name : String +var runway_count : int +var airport_closed : bool = false + # cell offsets that describe valid approaches based on runways and surroundings # used to choose a takeoff position +enum rotations { EAST, NORTHEAST, NORTHWEST, WEST, SOUTHWEST, SOUTHEAST } const bearings = [ [0,1] , [-1, 0], [-1, -1], [0, -1], [1, 0], [1, 1] ] -var valid_approaches = [] -var valid_bearings = [] + +var valid_departure_bearings = [] +var valid_arrival_bearings = [] func _ready(): pass -func set_up(tile_type, settings={}): - valid_bearings = [] # reset - cell_type = tile_type +func reset(): + $Hills.visible = false + $Mountain.visible = false + $Airport.visible = false + $Airport/EasyRunway.visible = true # reset runways + $Airport/MediumRunway.visible = true + $Airport/AirportName.visible = false + $Airport/AirportIcon.visible = false + orientation = 0 + self.rotation.y = 0 + cell_type = PLAIN + x = -1 + y = -1 + airport_closed = false + - if settings["rotation"]: # bearing according to E, NE, etc. - self.global_rotation.y = settings["rotation"] * deg2rad(60) +func set_up(settings): + x = settings["pos"][X] ; y = settings["pos"][Y] + cell_type = settings["cell_type"] - if tile_type == "hills": + valid_departure_bearings.clear() + valid_arrival_bearings.clear() + + + if settings["orientation"]: # bearing according to E, NE, etc. + orientation = settings["orientation"] + self.global_rotation.y = orientation * deg2rad(60) + + if cell_type == HILLS: $Hills.visible = true - elif tile_type == "mountain": + elif cell_type == MOUNTAINS: $Mountain.visible = true - elif tile_type == "airport": + elif cell_type == AIRPORT: $Airport.visible = true - airport_number = settings["airport_number"] - airport_color = settings["airport_color"] - valid_approaches = settings["valid_approaches"] - for approach in valid_approaches: - var bearing_i = bearings.find(approach) - valid_bearings.push_back(bearings[bearing_i]) + if settings["use_names"]: + airport_name = settings["airport_name"] + $Airport/AirportName.visible = true + else: + airport_number = settings["airport_number"] + airport_color = settings["airport_color"] + $Airport/AirportIcon.visible = true + $Airport/AirportIcon.texture = load("res://textures/airport_indicator_%d.png" % airport_number) + $Airport/AirportIcon.modulate = airport_color + + valid_departure_bearings = settings["valid_approach_offsets"] airport_id = settings["airport_id"] - $Airport/AirportIcon.texture = load("res://textures/airport_indicator_%d.png" % airport_number) - $Airport/AirportIcon.modulate = airport_color + for departure_bearing in valid_departure_bearings: + var bearing_i = bearings.find(departure_bearing) + bearing_i = (bearing_i + 3) % 6 # opposite bearing + valid_arrival_bearings.push_back(bearings[bearing_i]) - if settings["difficulty"] == "easy": return - $Airport/EasyRunway.visible = false - if settings["difficulty"] == "hard": + runway_count = int(clamp(settings["runway_count"], 1, 3)) + if runway_count < 3: + $Airport/EasyRunway.visible = false + if runway_count == 1: $Airport/MediumRunway.visible = false diff --git a/scripts/MainMenu.gd b/scripts/MainMenu.gd new file mode 100644 index 0000000..fca6658 --- /dev/null +++ b/scripts/MainMenu.gd @@ -0,0 +1,84 @@ +extends Control + +signal game_host_request(args) +signal game_join_request(args) + +var lobby_name_changed : bool = false # automatically update lobby name to be based on player's name + +func _ready(): + $BackButton.connect("pressed", self, "back_button") + $HostMenuButton.connect("pressed", self, "host_menu_button_pressed") + $JoinMenuButton.connect("pressed", self, "join_menu_button_pressed") + $SettingsButton.connect("pressed", self, "settings_menu_button_pressed") + $HostMenu/PrivateToggle.connect("toggled", $HostMenu/Password, "set_visible") + $SettingsMenu/GameCoordinatorURL.connect("text_changed", Globals, "update_gc_url") + $PlayerInfo/Username.connect("text_changed", self, "automatically_update_lobby_name") + $HostMenu/GameName.connect("text_changed", self, "set_lobby_name_changed") + #main_menu() # in case things are incorrectly visible from editing + + +func set_lobby_name_changed(_disregard_new_text): + lobby_name_changed = true + +func automatically_update_lobby_name(username : String): + if lobby_name_changed: return + var suffix : String = "' Game" if username.ends_with("s") else "'s Game" + $HostMenu/GameName.set_text(username + suffix) + +func main_menu(): + set_menu_buttons_visible(true) + set_player_info_visible(false) + set_join_menu_visible(false) + set_host_menu_visible(false) + set_settings_menu_visible(false) + set_back_button_visible(false) + +func set_menu_buttons_visible(visible : bool): + $HostMenuButton.visible = visible + $JoinMenuButton.visible = visible + $SettingsButton.visible = visible + +func set_player_info_visible(visible : bool): + $PlayerInfo.visible = visible + +func set_join_menu_visible(visible : bool): + $JoinMenu.visible = visible + +func set_host_menu_visible(visible : bool): + $HostMenu.visible = visible + +func set_settings_menu_visible(visible : bool): + $SettingsMenu.visible = visible + +func set_back_button_visible(visible : bool): + $BackButton.visible = visible + $BackButton.disabled = false # reset in case left disabled by other function + +# go to join game menu +func join_menu_button_pressed(lobby_id_from_url : String = "", password_from_url : String = ""): + set_menu_buttons_visible(false) + set_player_info_visible(true) + set_join_menu_visible(true) + set_back_button_visible(true) + if lobby_id_from_url: + $JoinMenu/LobbyID.text = lobby_id_from_url + if password_from_url: + $JoinMenu/Password.text = password_from_url + +# go to host game menu +func host_menu_button_pressed(): + set_menu_buttons_visible(false) + set_player_info_visible(true) + set_host_menu_visible(true) + set_back_button_visible(true) + +# go to settings menu +func settings_menu_button_pressed(): + $SettingsMenu/GameCoordinatorURL.text = Globals.GC_URL + set_menu_buttons_visible(false) + set_settings_menu_visible(true) + set_back_button_visible(true) + +# return to main menu +func back_button(): + main_menu() diff --git a/scripts/MainScene.gd b/scripts/MainScene.gd new file mode 100644 index 0000000..fc15463 --- /dev/null +++ b/scripts/MainScene.gd @@ -0,0 +1,13 @@ +extends Control + + +func _ready(): + if OS.get_name() == "HTML5": # running on web + var lobby_id = JavaScript.eval("new URLSearchParams(document.location.search).get('lobby_id')") + var gc_url = JavaScript.eval("new URLSearchParams(document.location.search).get('gc_url')") + var password = JavaScript.eval("new URLSearchParams(document.location.search).get('pw')") + if lobby_id: + var pw : String = password if password else "" + $MainMenu.join_menu_button_pressed(lobby_id, pw) + if gc_url: + Globals.update_gc_url(gc_url) diff --git a/scripts/Plane.gd b/scripts/Plane.gd index 62ffc43..6889e31 100644 --- a/scripts/Plane.gd +++ b/scripts/Plane.gd @@ -1,22 +1,25 @@ extends Area -var starting_altitude:int = 0 # initial altitude for the turn, determines number of actions -var altitude:int = 0 # 0, 1, or 2 -var pos_x:int -var pos_y:int +var starting_altitude: int = 0 # initial altitude for the turn, determines number of actions +var altitude: int = 0 # 0, 1, or 2 +var pos_x: int +var pos_y: int onready var meshes = [$Fuselage, $Cone, $Wings, $Tail] # bearings: E, NE, NW, W, SW, SE const bearings = [ [0,1] , [-1, 0], [-1, -1], [0, -1], [1, 0], [1, 1] ] -var bearing:int = 0 # index of above list of potential bearings +var bearing: int = 0 # index of above list of potential bearings var destination_num: int # for display purposes only var destination_col: Color # for display purposes only +var destination_name: String # for display purposes var destination_id: int # determines above ^ var rotation_tween: Tween = null +var actions: Array = [] + var plane_material func _ready(): @@ -25,9 +28,7 @@ func _ready(): mesh.set_surface_material(0, plane_material) var new_col = Color(randf(), randf(), randf() ) - print(new_col) set_color(new_col) - func set_color(color: Color): plane_material.set_albedo(color) diff --git a/scripts/ServerBrowser.gd b/scripts/ServerBrowser.gd index 3c59b9b..410eec2 100644 --- a/scripts/ServerBrowser.gd +++ b/scripts/ServerBrowser.gd @@ -16,6 +16,8 @@ var queued_messages = [] func _ready(): refresh_game_list() $RefreshButton.connect("pressed", self, "refresh_game_list") + $HostPopup/Control/PrivateToggle.connect("toggled", self, "toggle_password_vis") + $Username.connect("text_changed", $HostPopup/Control/GameName, "set_text") func join_game(): $HostPopup.visible = false @@ -50,6 +52,9 @@ func add_games_to_list(games): game_list.add_item( game_str, null, true if game["state"] == "LOBBY" else false ) game_ids.append( game["id"] ) +func toggle_password_vis(pressed): + $HostPopup/Control/Password.visible = pressed + func _process(_delta): $GameCoordinatorStatus.text = "Game Coordinator Connection: " + str(ws_client.state) |
