summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorAnson Bridges <bridges.anson@gmail.com>2025-08-11 22:42:00 -0700
committerAnson Bridges <bridges.anson@gmail.com>2025-08-11 22:42:00 -0700
commitd558a9add0e183219a7a9ff482807bdcd677e21a (patch)
tree49e454649a4b45ce02c419894109de55f7f2e465 /scripts
Initialize repo from local files
Diffstat (limited to 'scripts')
-rw-r--r--scripts/GameTable.gd207
-rw-r--r--scripts/Globals.gd7
-rw-r--r--scripts/HexSpace.gd50
-rw-r--r--scripts/Plane.gd44
-rw-r--r--scripts/PlaneControlBoard.gd32
-rw-r--r--scripts/ServerBrowser.gd71
6 files changed, 411 insertions, 0 deletions
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))