From d558a9add0e183219a7a9ff482807bdcd677e21a Mon Sep 17 00:00:00 2001 From: Anson Bridges Date: Mon, 11 Aug 2025 22:42:00 -0700 Subject: Initialize repo from local files --- scripts/GameTable.gd | 207 +++++++++++++++++++++++++++++++++++++++++++ scripts/Globals.gd | 7 ++ scripts/HexSpace.gd | 50 +++++++++++ scripts/Plane.gd | 44 +++++++++ scripts/PlaneControlBoard.gd | 32 +++++++ scripts/ServerBrowser.gd | 71 +++++++++++++++ 6 files changed, 411 insertions(+) create mode 100644 scripts/GameTable.gd create mode 100644 scripts/Globals.gd create mode 100644 scripts/HexSpace.gd create mode 100644 scripts/Plane.gd create mode 100644 scripts/PlaneControlBoard.gd create mode 100644 scripts/ServerBrowser.gd (limited to 'scripts') diff --git a/scripts/GameTable.gd b/scripts/GameTable.gd new file mode 100644 index 0000000..cbee40f --- /dev/null +++ b/scripts/GameTable.gd @@ -0,0 +1,207 @@ +tool +extends Spatial + +const num_mountains = {"easy" : 0, "medium" : 3, "hard" : 6} +const num_hills = {"easy" : 4, "medium" : 6, "hard" : 8} + +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 + +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 + +onready var hex_space = preload("res://objects/HexSpace.tscn") + +# 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 generate_board_editor(_gbe): + generate_hex_board() + generate_board_cells() + populate_board() diff --git a/scripts/Globals.gd b/scripts/Globals.gd new file mode 100644 index 0000000..18f3d19 --- /dev/null +++ b/scripts/Globals.gd @@ -0,0 +1,7 @@ +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) ] + +func _ready(): + set_process(false) diff --git a/scripts/HexSpace.gd b/scripts/HexSpace.gd new file mode 100644 index 0000000..c8269fd --- /dev/null +++ b/scripts/HexSpace.gd @@ -0,0 +1,50 @@ +tool +extends StaticBody + +var cell_type : String = "normal" + +# airport variables +var airport_number : int +var airport_color : Color +var airport_id : int +# cell offsets that describe valid approaches based on runways and surroundings +# used to choose a takeoff position +const bearings = [ [0,1] , [-1, 0], [-1, -1], [0, -1], [1, 0], [1, 1] ] +var valid_approaches = [] +var valid_bearings = [] + + + +func _ready(): + pass + +func set_up(tile_type, settings={}): + valid_bearings = [] # reset + cell_type = tile_type + + if settings["rotation"]: # bearing according to E, NE, etc. + self.global_rotation.y = settings["rotation"] * deg2rad(60) + + if tile_type == "hills": + $Hills.visible = true + elif tile_type == "mountain": + $Mountain.visible = true + elif tile_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]) + airport_id = settings["airport_id"] + + $Airport/AirportIcon.texture = load("res://textures/airport_indicator_%d.png" % airport_number) + $Airport/AirportIcon.modulate = airport_color + + if settings["difficulty"] == "easy": return + $Airport/EasyRunway.visible = false + if settings["difficulty"] == "hard": + $Airport/MediumRunway.visible = false + diff --git a/scripts/Plane.gd b/scripts/Plane.gd new file mode 100644 index 0000000..62ffc43 --- /dev/null +++ b/scripts/Plane.gd @@ -0,0 +1,44 @@ +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 + +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 destination_num: int # for display purposes only +var destination_col: Color # for display purposes only +var destination_id: int # determines above ^ + +var rotation_tween: Tween = null + +var plane_material + +func _ready(): + plane_material = load("res://resources/blank_plane_material.material").duplicate() + for mesh in meshes: + 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) + +func _on_Plane_mouse_entered(): + $ActionIndicator.visible = true + + +func _on_Plane_mouse_exited(): + $ActionIndicator.visible = false + + +func _on_Plane_input_event(camera, event, position, normal, shape_idx): + pass # Replace with function body. diff --git a/scripts/PlaneControlBoard.gd b/scripts/PlaneControlBoard.gd new file mode 100644 index 0000000..cfe94b5 --- /dev/null +++ b/scripts/PlaneControlBoard.gd @@ -0,0 +1,32 @@ +extends ColorRect + +onready var actions = [$ActionHBox/ActionSquare1, $ActionHBox/ActionSquare2, $ActionHBox/ActionSquare3, $ActionHBox/ActionSquare4, $ActionHBox/ActionSquare5] +onready var altitude_icons = [$AltitudeHBox/Altitude1, $AltitudeHBox/Altitude2, $AltitudeHBox/Altitude3] +var airport_indicators = [] + + +const modulate_selected: Color = Color(1, 1, 1) +const modulate_deselected: Color = Color(0.3, 0.3, 0.3) + +func _ready(): + for i in range(1,7): + airport_indicators.push_back(load("res://textures/airport_indicator_%i.png" % i)) + + for action in actions: + action.reset() + action.disable() + + $DestinationHBOX/DestinationIcon + +func set_altitude_vis(alt): + var index: int = 0 + for icon in altitude_icons: + icon.set_modulate( modulate_selected if index == alt else modulate_deselected ) + index += 1 + +func set_destination(number, color): + $DestinationHBOX/DestinationIcon.texture = airport_indicators[number - 1] + $DestinationHBOX/DestinationIcon.modulate = color + +func update_display_from_plane(plane): + pass diff --git a/scripts/ServerBrowser.gd b/scripts/ServerBrowser.gd new file mode 100644 index 0000000..2a1492e --- /dev/null +++ b/scripts/ServerBrowser.gd @@ -0,0 +1,71 @@ +extends Control + +onready var ws_client = $GameCoordinator +onready var game_list = $GameList + +enum { ANY, GAME_LIST, HOST_RESPONSE, JOIN_RESPONSE, PASSWORD_RESPONSE } + +var game_ids = [] + +# change this!!! +var game_coordinator_url = "ws://127.0.0.1:8181" +var awaiting_connection = false +var expecting = [] +var queued_messages = [] + +func _ready(): + refresh_game_list() + $RefreshButton.connect("pressed", self, "refresh_game_list") + +func join_game(): + $HostPopup.visible = false + $HostButton.disabled = true + $RefreshButton.disabled = true + var message = { "type" : "join_game" } + if ws_client.state != 2: + ws_client.sock_connect_to_url(game_coordinator_url) + queued_messages.push_back( message ) + awaiting_connection = true + else: + ws_client.send_json( message ) + +func host_game(): + pass + +func refresh_game_list(): + $JoinButton.disabled = true + game_ids.clear() + game_list.clear() + + var message = {"type" : "list_open_games"} + if ws_client.state != 2: + ws_client.sock_connect_to_url(game_coordinator_url) + queued_messages.push_back( message ) + awaiting_connection = true + else: + ws_client.send_json( message ) + +func add_games_to_list(games): + for game in games: + var game_str = game["game_name"] + " (" + str(int(game["current_players"])) + "/" + str(int(game["max_players"])) + ") (" +game["state"]+ ")" + (" (PRIVATE)" if game["private"] else "") + game_list.add_item( game_str, null, true if game["state"] == "LOBBY" else false ) + game_ids.append( game["id"] ) + +func handle_gc_message(msg): + if msg == null or msg.error: return + msg = msg.result + if msg["type"] == "game_list": + add_games_to_list(msg["games"]) + ws_client.send_json({"type" : "ack"}) + if msg["type"] == "error": + print(msg["message"]) + +func _process(_delta): + $GameCoordinatorStatus.text = "Game Coordinator Connection: " + str(ws_client.state) + if ws_client.state == 2: + if awaiting_connection: + awaiting_connection = false + for queued_message in queued_messages: + ws_client.send_json( queued_message ) + + handle_gc_message(ws_client.receive(true)) -- cgit v1.2.3