summaryrefslogtreecommitdiff
path: root/scripts/GameTable.gd
blob: cbee40fd7a8e786ad83d9984c9204373ae6f2fb5 (plain)
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()