1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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()
|