From 64f37f4209d80bfad976dd4a139c98002caef15f Mon Sep 17 00:00:00 2001 From: Anson Bridges Date: Fri, 22 Aug 2025 12:46:04 -0700 Subject: the harmonious transition from menu to game --- scripts/GameTable.gd | 161 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 12 deletions(-) (limited to 'scripts/GameTable.gd') diff --git a/scripts/GameTable.gd b/scripts/GameTable.gd index b0bedf2..f21d027 100644 --- a/scripts/GameTable.gd +++ b/scripts/GameTable.gd @@ -2,7 +2,7 @@ extends Spatial # MULTIPLAYER DATA -var gc_client # to be assigned by MainScene upon game creation/joining +var GC : GCClient # Game Coordinator connection/client, to be assigned by MainScene upon game creation/joining @@ -33,7 +33,7 @@ var RULES = { "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, + "starting_planes_per_player": 3, "round_timer" : 60, # seconds } @@ -43,26 +43,163 @@ var PLANES = [] var desired_player_count: int var is_board_generated: bool = false # determine whether host can begin the game +var PLAYER_INFO = {} # + # 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] ] +func _ready(): + pass +func _process(_delta): + var msg = GC.receive() + if msg: handle_gc_message(msg) -func _ready(): +func set_up(gc_client): + GC = gc_client + GC.connect("join_request_received", self, "on_join_request_received") + GC.connect("disconnected_from_lobby", self, "on_disconnect") + GC.connect("connection_closed_dirty", self, "on_connection_closed_dirty") + + var titletext = $PregameUI/Header/TitleText.text.replace("$JCODE", GC.lobby_id) + titletext = titletext.replace("$LOBBY_NAME", GC.lobby_name) + if OS.get_name() == "HTML5": # running on web + titletext += " (click to copy)" + $PregameUI/Header/TitleText.text = titletext + + # set up pregame controls + # TODO : function where editability/enabled is changed on game host change + var i = 0 + for control_group in ["Board Params", "Game Rules"]: + var path_root : String = "PregameUI/PregameControls/ControlsTabContainer/"+control_group+"/" + var dict = BOARD_GEN_PARAMS if i == 0 else RULES + for key in dict.keys(): + var setting_node = get_node(path_root + key) + if setting_node is OptionButton: + setting_node.connect("item_selected", self, "on_setting_update", [control_group, key]) + if not GC.is_host: setting_node.disabled = true + elif setting_node is SpinBox: + setting_node.connect("value_changed", self, "on_setting_update", [control_group, key]) + if not GC.is_host: setting_node.editable = false + i += 1 + + $PregameUI/PregameControls/PlayerTab/StartButton.disabled = not GC.is_host + var gen_btn : Button = get_node("PregameUI/PregameControls/ControlsTabContainer/Board Params/GenerateBoardButton") + gen_btn.disabled = not GC.is_host + gen_btn.connect("pressed", self, "generate_board_ui") + +func set_up_args(args): + if !(GC.player_id in PLAYER_INFO): PLAYER_INFO[GC.player_id] = {} + PLAYER_INFO[GC.player_id]["color_id"] = args["color_id"] + +func on_join_request_received(player_id, args): + if !GC.is_host: return # just in case + var response = { "type" : "lobby_control", "command": "accept_player", "player_id" : player_id } + + GC.send_json(response) + +# new_value filled by UI element signal, control_group and setting_name from custom binds +func on_setting_update(new_value, control_group, setting_name): + if !GC.is_host: return # prevent non-hosts from broadcasting updates when setting the value because of https://github.com/godotengine/godot/issues/70821 + var dict = BOARD_GEN_PARAMS if control_group == "Board Params" else RULES + dict[setting_name] = new_value + + var command = "set_board_gen_params" if control_group == "Board Params" else "set_game_rules" + var message = {"type" : "broadcast", "command" : command, "payload" : { setting_name : new_value } } + GC.send_json(message) + +# fields being dictionary of form { field : value } +func set_setting_remote(control_group, fields): + var dict = BOARD_GEN_PARAMS if control_group == "Board Params" else RULES + var path_root : String = "PregameUI/PregameControls/ControlsTabContainer/"+control_group+"/" + for field in fields: + var new_value = fields[field] + dict[field] = new_value + var setting_node = get_node(path_root + field) + if setting_node is SpinBox: + setting_node.set_value(new_value) + elif setting_node is OptionButton: + setting_node.select(new_value) # does not emit signal + +func generate_board_ui(): + is_board_generated = BOARD.generate_board(BOARD_GEN_PARAMS["board_side_length"], + BOARD_GEN_PARAMS["num_mountains"], + BOARD_GEN_PARAMS["num_hills"], + BOARD_GEN_PARAMS["num_airports"], + BOARD_GEN_PARAMS["runways_per_airport"], + BOARD_GEN_PARAMS["airport_style"] + ) + var message = {"type" : "broadcast", "command" : "set_board", "payload" : BOARD.board } + GC.send_json(message) + +func on_disconnect(reason): 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 +func on_connection_closed_dirty(): + get_parent() + +# messages have been pre-processed by game coordinator client to ensure +# valid JSON with "type" field +func handle_gc_message(message): + if "current_players" in message: update_player_info() # add new players to PLAYER_INFO, remove disconnected players + if message["type"] == "request": + #var data = get_data() + #var response = { "type" : "request_response", "requested_data" : data, "destination" : event["source"] } + #GC.send_json + pass + elif message["type"] == "request_response": + #set_data() + pass + elif message["type"] == "broadcast" and "command" in message: + var cmd : String = message["command"] + if cmd == "set_game_rules": + set_setting_remote("Game Rules", message["payload"]) + elif cmd == "set_board_gen_params": + set_setting_remote("Board Params", message["payload"]) + elif cmd == "set_board": + BOARD.set_board(message["payload"]) + +# info being a dictionary of format { player_id : {field : value} } +func update_player_info(info = null): + # ADD NEW PLAYERS + var connected_player_ids : Array = [] + for player in GC.players: + connected_player_ids.push_back(player["player_id"]) + if !(player["player_id"] in PLAYER_INFO): + PLAYER_INFO[player["player_id"]] = {"username" : player["username"]} + + # EDIT PLAYER ATTRIBUTES + if info is Dictionary: + for player_id in info.keys(): + if !(info[player_id] is Dictionary) or !(player_id in PLAYER_INFO): return + for field in info[player_id].keys(): + PLAYER_INFO[player_id][field] = info[player_id][field] + + # DELETE DISCONNECTED PLAYERS + var player_ids_info = PLAYER_INFO.keys() + for player_id in player_ids_info: + if !(player_id in connected_player_ids) and (player_id in PLAYER_INFO): + PLAYER_INFO.erase(player_id) + continue # 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" } - + if GC.is_host: return + var request = { "type" : "request", "source" : GC.host_id, "requested_data" : "ALL" } + GC.send_json(request) + + +func _on_TitleText_gui_input(event): + if (event is InputEventMouseButton && event.pressed && event.button_index == 1): + if OS.get_name() == "HTML5": # running on web + var direct_join_URL : String = JavaScript.eval("window.location.href") + direct_join_URL += "?lobby_id="+GC.lobby_id + if Globals.GC_URL != Globals.DEFAULT_GC_URL: + direct_join_URL += "&gc_url="+Globals.GC_URL + if GC.is_private: + direct_join_URL += "&pw="+GC.lobby_password + OS.set_clipboard(direct_join_URL) + -- cgit v1.2.3