Created
August 15, 2022 19:58
-
-
Save kzerot/d50ee6db055ff186d44e6f94b29211ca to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extends Spatial | |
# Helper classes | |
tool | |
class_name Terrain | |
class Hex: | |
#CONST FOR HEX | |
const hex_directions = [ | |
Vector3( 0, -1, 1), Vector3( 1, -1, 0), Vector3( 1, 0, -1), | |
Vector3( 0, 1, -1), Vector3(-1, 1, 0), Vector3(-1, 0, 1) | |
] | |
const hex_diagonals = [ | |
Vector3( 1, -2, 1), Vector3( 2, -1, -1), Vector3( 1, 1, -2), | |
Vector3(-1, 2, -1), Vector3(-2, 1, 1), Vector3(-1, -1, 2) | |
] | |
var chank = null | |
var height : int | |
var x = 0 | |
var y = 0 | |
var color = Color("ffffff") | |
var position = Vector3() | |
var building = null | |
var building_data = {} | |
var object = {} | |
var type = 0 | |
var index = 0 | |
var resource = null | |
var resource_object = null | |
var density = 0 | |
var meta = null | |
var weight = 1 | |
var decor = null | |
var decor_object = null | |
func _init(_x,_y, _color): | |
self.x = int(_x) | |
self.y = int(_y) | |
self.color = _color | |
func pack_short(): | |
return { | |
"x" : x, | |
"y" : y | |
} | |
func pack(): | |
return { | |
"object_type": "hex", | |
"height" : height, | |
"x" : x, | |
"y" : y, | |
"color" : color.to_html(), | |
"position" :[position.x, position.y,position.z], | |
"resource": resource, | |
"building" : building, | |
"type" : type, | |
"index" : index, | |
"meta" : meta, | |
"density": density, | |
"decor": decor | |
} | |
func unpack(data): | |
for i in data: | |
if i == "color": | |
set(i, Color(data[i])) | |
elif i == "position": | |
set(i, Vector3(data[i][0],data[i][1],data[i][2])) | |
elif i == "type": | |
set(i, int(data[i])) | |
else: | |
if typeof(get(i)) == TYPE_INT: | |
data[i] = int(data[i]) | |
set(i, data[i]) | |
func cube(): | |
return axial_to_cube(axial()) | |
func axial(): | |
return Vector2(self.x, self.y) | |
func get_neighbor(array, direction): | |
var n_coor = cube_to_axial(neighbor_hex(cube(), direction)) | |
var r = n_coor.x | |
var q = n_coor.y | |
if r + floor(q/2) >= 0 and r + floor(q/2) < array.size(): | |
if q >=0 and q < array[0].size(): | |
var n = array[r + floor(q/2)][q] | |
if n: | |
return n | |
return null | |
func is_free(): | |
var free = true | |
if building: | |
free = false | |
if weight < 1 or type in [MOUNTAIN, WATER]: | |
return false | |
if resource: | |
free = false | |
if weight < 1: | |
free = false | |
return free | |
func calc_res_count(array, radius, res): | |
var neighbors = get_all_neighbors(cube(), radius) | |
var count = 0 | |
if neighbors: | |
for n in neighbors: | |
var neig = get_hex(array, cube_to_axial(n)) | |
if neig and res == neig.resource: | |
count += 1 | |
return count | |
func to_str(): | |
return 'CUBE ' + str(cube()) + ' W ' + str(weight) + \ | |
" RES " + str(resource) + " BUILDING " + str(building) | |
# + str(type) + "\n BUILDING " + str(building) + \ | |
# something like static functions | |
# Return the coordinates of a direction from a given hex | |
static func get_hex(array, v): | |
var height = array.size() | |
var width = array[0].size() | |
if height > v.x + floor(v.y/2) and v.x + floor(v.y/2) >= 0 and \ | |
width > v.y and v.y >= 0: | |
return array[v.x + floor(v.y/2)][v.y] | |
static func neighbor_hex(hex, direction): | |
return hex + hex_directions[direction] | |
static func distance(hex1, hex2): | |
var a = hex1.cube() | |
var b = hex2.cube() | |
return max(max(abs(a.x - b.x), abs(a.y - b.y)), abs(a.z - b.z)) | |
static func get_all_neighbors(hex, N): | |
var results = [] | |
for x in range(-N, N+1): | |
for y in range(-N, N+1): | |
for z in range(-N, N+1): | |
if x + y + z == 0 and not (x == 0 and y == 0 and z == 0): | |
results.append(hex + Vector3(x, y, z)) | |
return results | |
static func diagonal_neighbor_hex(hex, direction): | |
return hex + hex_diagonals[direction] | |
static func cube_to_axial(cube): | |
var q = cube.x | |
var r = cube.z | |
return Vector2(q, r) | |
static func axial_to_cube(hex): | |
var x = hex.x | |
var z = hex.y | |
var y = -x-z | |
return Vector3(x, y, z) | |
static func cube_lerp(a, b, t): # for hexes | |
return Vector3(lerp(a.x, b.x, t), | |
lerp(a.y, b.y, t), | |
lerp(a.z, b.z, t)) | |
static func cube_to_real(hex_array, cube): | |
pass | |
static func linedraw(hex1, hex2, hex_array, full_los=true): | |
# var stri = "Draw LOS from "+ str( hex1.cube()) + str(hex2.cube()) + "\n" | |
var dist = Hex.distance(hex1, hex2) | |
# stri += "Dist = "+ str( dist)+ "\n" | |
var results = [] | |
for i in range(dist): | |
results.append( Hex.cube_round(Hex.cube_lerp(hex1.cube(), hex2.cube(), 1.0/(dist) * (i+1)), full_los)) | |
# stri+= "Result = " + str(results) | |
# print(stri) | |
# We need hexes not cube and not hexes | |
var fin_result = [] | |
for r in results: | |
var tmp = [] | |
if full_los: | |
for p in r.points: | |
tmp.append(get_hex(hex_array, cube_to_axial(p))) | |
fin_result.append({ | |
"real": cube_to_axial(r.real), | |
"points": tmp | |
}) | |
else: | |
# fin_result.append(get_hex(hex_array, cube_to_axial(r))) | |
fin_result.append(cube_to_axial(r)) | |
return fin_result | |
static func cube_round(cube, both=false): | |
var rx = round(cube.x-sign(cube.x)*0.01) | |
var ry = round(cube.y-sign(cube.y)*0.01) | |
var rz = round(cube.z-sign(cube.z)*0.01) | |
var x_diff = abs(rx - cube.x) | |
var y_diff = abs(ry - cube.y) | |
var z_diff = abs(rz - cube.z) | |
if x_diff > y_diff and x_diff > z_diff: | |
rx = -ry-rz | |
elif y_diff > z_diff: | |
ry = -rx-rz | |
else: | |
rz = -rx-ry | |
if both: | |
var rx2 = round(cube.x+sign(cube.x)*0.01) | |
var ry2 = round(cube.y+sign(cube.y)*0.01) | |
var rz2 = round(cube.z+sign(cube.z)*0.01) | |
var x_diff2 = abs(rx2 - cube.x) | |
var y_diff2 = abs(ry2 - cube.y) | |
var z_diff2 = abs(rz2 - cube.z) | |
if x_diff2 > y_diff2 and x_diff2 > z_diff2: | |
rx2 = -ry2-rz2 | |
elif y_diff2 > z_diff2: | |
ry2 = -rx2-rz2 | |
else: | |
rz2 = -rx2-ry2 | |
if Vector3(rx, ry, rz) != Vector3(rx2, ry2, rz2): | |
return {"real": cube, "points": [Vector3(rx, ry, rz), Vector3(rx2, ry2, rz2)]} | |
else: | |
return {"real": cube, "points": [Vector3(rx, ry, rz)]} | |
return Vector3(rx, ry, rz) | |
# END HEX CLASS | |
func pixel_to_hex(pos): | |
pos /= 2 | |
var q = 2*(sqrt(3)/3 * pos.x - 1.0/3 * pos.z) / (outerRadius) | |
var r = 2*( 2.0/3 * pos.z) / (outerRadius) | |
return Hex.cube_round(Hex.axial_to_cube(Vector2(q, r))) | |
func axis_pixel_to_hex(pos): | |
return Hex.cube_to_axial(pixel_to_hex(pos)) | |
func line(hex1, hex2, complex=false, return_hex=false): # Return only hexes | |
var line_raw = Hex.linedraw(hex1, hex2, hex_array, complex) | |
if not complex: | |
var _line = [] | |
for l in line_raw: | |
if not get_hex(l): | |
return false | |
if return_hex: | |
_line.append(get_hex(l)) | |
else: | |
_line.append(get_hex(l).position) | |
return _line | |
else: | |
for l in line_raw: | |
l.real = axial_coor_to_real(l.real) | |
return line_raw | |
func LOS(hex1, hex2): | |
var los = Hex.linedraw(hex1, hex2, hex_array) | |
for l in los: | |
l.real = axial_coor_to_real(l.real) | |
return los | |
func cube_round(cube): | |
return Hex.cube_round(cube) | |
func rotate_vertex_2d(pos, center, angle): | |
var newX = center.x + (pos.x-center.x)*cos(angle) - (pos.z-center.z)*sin(angle); | |
var newY = center.z + (pos.x-center.x)*sin(angle) + (pos.z-center.z)*cos(angle); | |
pos.x = newX | |
pos.z = newY | |
return pos | |
# Enums | |
enum { | |
GRASS=1, | |
MOUNTAIN=2, | |
WATER=3, | |
SAND=4 | |
} | |
const terrain_names = { | |
GRASS: "Grass", | |
MOUNTAIN: "Mountain", | |
WATER: "Water", | |
SAND: "Sand" | |
} | |
# Signals | |
signal clicked(hex, mouse_button, neighbor) | |
#signal mouse_move(hex) | |
# models | |
var crystall = preload("res://models/GlobalMap/Resources/Crystall.tscn") | |
var rocks = preload("res://models/GlobalMap/Resources/Rocks.tscn") | |
var iron = preload("res://models/GlobalMap/Resources/Iron.tscn") | |
#var forest = preload("res://models/GlobalMap/Trees/Forest.tscn") | |
var misc_array = { | |
GRASS: { | |
"g1": preload("res://models/GlobalMap/Misc/Grass1.scn"), | |
"g2": preload("res://models/GlobalMap/Misc/Grass2.scn"), | |
"g3": preload("res://models/GlobalMap/Misc/Grass3.scn"), | |
"g4": preload("res://models/GlobalMap/Misc/Grass4.scn"), | |
"g5": preload("res://models/GlobalMap/Misc/Grass5.scn") | |
} | |
} | |
var forest_array = { | |
"full": [preload("res://models/GlobalMap/Trees/Forest.tscn"), | |
preload("res://models/GlobalMap/Trees/Forest2.tscn")], | |
"medium":[preload("res://models/GlobalMap/Trees/ForestSmall.tscn")], | |
"small":[preload("res://models/GlobalMap/Trees/ForestVerySmall.tscn")], | |
"battle": [preload("res://models/GlobalMap/Trees/Pine1.tscn"), | |
preload("res://models/GlobalMap/Trees/Pine2.tscn"), | |
preload("res://models/GlobalMap/Trees/Pine3.tscn"), | |
preload("res://models/GlobalMap/Trees/Tree1.tscn"), | |
preload("res://models/GlobalMap/Trees/Tree2.tscn")] | |
} | |
enum {MODE_GAME, MODE_EDITOR} | |
var mode = MODE_GAME | |
#var tree = preload("res://models/GlobalMap/Trees/Tree2.tscn") | |
#towns | |
var town = preload("res://models/towns/Town.tscn") | |
var town_dummy = preload("res://models/towns/TownPlaceholder.tscn") | |
var real_towns = [] | |
var hexes_with_buildings = [] | |
# Map Objects | |
#var ruins = preload("res://models/GlobalMap/Ruins.tscn") | |
#var dwelling1 = preload("res://models/GlobalMap/Dwellings/MonsterCave.tscn") | |
var npc_village = preload("res://models/GlobalMap/Village.tscn") | |
var watch_tower = preload("res://models/GlobalMap/Buildings/WatchTower.tscn") | |
var waterfall = preload("res://models/GlobalMap/Misc/Waterfall.tscn") | |
var castle = preload("res://models/GlobalMap/Buildings/CastleUpgrade.tscn") | |
var tower_wisdom = preload("res://models/GlobalMap/Buildings/TowerWisdom.tscn") | |
#var dwellings = [dwelling1] | |
var global_buildings = [] | |
var towns = [] | |
#thread loading | |
var thread = null | |
var thread_free = true | |
var thread_callback = null | |
var building_thread = null | |
var need_build = true | |
var building_queue = [] | |
var progress_text = "" | |
var progress = 0 | |
var outerRadius = 5 | |
export var OuterRadius = 5 setget set_r, get_r | |
var width = 32 | |
var height = 32 | |
var is_editor = false | |
var solid = 0.9 | |
const min_height = -10 # TO CONST | |
export (int) var Width = 32 setget set_w, get_w | |
export (int) var Height = 32 setget set_h, get_h | |
export (bool) var update_in_editor = false | |
export (float) var step_height = 1 | |
export (int) var octaves = 4 | |
export (int) var period = 40 | |
export (int) var persistence = 4 | |
export (int) var water_level = 1 | |
export (int) var landscape_height = 5 | |
export (bool) var generate_height_maps = true | |
export (int) var min_mountain_height = 3 | |
export (int) var m_octaves = 4 | |
export (int) var m_period = 80 | |
export (int) var m_persistence = 5 | |
export (int) var m_height = 6 | |
export (int) var chank_dim = 16 | |
export (bool) var add_objects = false | |
export (bool) var force_bottom = false | |
#var blank = false | |
var misc_enabled = true | |
var params | |
var heightmap_texture | |
#var result_img | |
#var result_img_height | |
var chank_count | |
var chank_dims = {} | |
var mesh | |
var terrain_seed = 435234 | |
var innerRadius = outerRadius * 0.866025404 | |
var corners = [ | |
Vector3(0, 0, outerRadius), | |
Vector3(innerRadius, 0, 0.5 * outerRadius), | |
Vector3(innerRadius, 0, -0.5 * outerRadius), | |
Vector3(0, 0, -outerRadius), | |
Vector3(-innerRadius, 0, -0.5 * outerRadius), | |
Vector3(-innerRadius, 0, 0.5 * outerRadius), | |
Vector3(0, 0, outerRadius), | |
]; | |
#var materials = [] | |
var hex_array = [] | |
var chanks = [] | |
var UI | |
var mat = preload("res://shaders/terrain_fow_shader_mat.tres") | |
onready var mat_fow = mat.next_pass | |
var mat_misc = preload("res://shaders/terrain_shader_mat.tres") | |
var fow_mats = [] | |
var colors = { | |
"snow": Color("00ffffff"), | |
# "rock": Color("0000ff"), | |
"rock": Color("352e2b"), | |
"grass": Color("216818"), | |
# "grass": Color("00ff00"), | |
"grass1": Color("23571d"), | |
"grass2":Color("1c4d16"), | |
"sand": Color("c2bf57"), | |
# "sand": Color("ff0000"), | |
"water": Color("007fc7"), | |
"deep_water": Color("002ec7"), | |
"very_deep_water": Color("00126c"), | |
"forest": Color("0f5400"), | |
"bark": Color("541500"), | |
"crystall_green": Color("00ff00"), | |
"crystall_blue": Color("0000ff"), | |
"crystall_red": Color("ff0000"), | |
"strong_green": Color("00ff00"), | |
"strong_red": Color("ff0000"), | |
"strong_blue": Color("0000ff"), | |
"strong_alpha": Color("ff000000"), | |
} | |
# A Star | |
var astar = AStar.new() | |
var fly_astar = AStar.new() | |
# Debug print | |
export var print_in_console = false | |
export var keep_terrain = false | |
# Parametres | |
var config = { | |
} | |
var players = [{"capital":null}, {"capital":null}] | |
func dprint(arg, arg1="", arg2="", arg3="", arg4=""): | |
if print_in_console: | |
print(arg, arg1, arg2, arg3, arg4) | |
func col(color): | |
var c = colors[color] | |
if typeof(c) == TYPE_COLOR: | |
return c | |
elif typeof(c) == TYPE_ARRAY: | |
return c[randi()%c.size()] | |
var height_color = { | |
"medium":{ | |
15: "snow", | |
8: "rock", | |
3: ["grass", "grass1","grass2"], | |
0: ["sand","grass"], | |
-1: "water", | |
-2: "deep_water", | |
-3: "very_deep_water" | |
}, | |
"base":{ | |
15: "snow", | |
8: "rock", | |
3: ["grass", "grass1","grass2"], | |
0: "sand", | |
-1: "water", | |
-2: "deep_water", | |
-3: "very_deep_water" | |
} | |
} | |
var color_to_type = { | |
"snow": MOUNTAIN, | |
"rock": MOUNTAIN, | |
"grass": GRASS, | |
"grass1": GRASS, | |
"grass2":GRASS, | |
"sand": SAND, | |
"water": WATER, | |
"deep_water": WATER, | |
"very_deep_water": WATER, | |
} | |
var type_to_name = { | |
MOUNTAIN: "MOUNTAIN", | |
GRASS: "grass", | |
SAND: "sand", | |
WATER: "water" | |
} | |
var height_table | |
func get_min_max_height(type, territory="medium"): | |
if not height_table: | |
height_table = {} | |
if not type in height_table: | |
height_table[type] = {"min": -1000, "max": 1000} | |
var colors = [] | |
for c in color_to_type: | |
if color_to_type[c] == type: | |
colors.append(c) | |
for h in height_color[territory]: | |
if height_color[territory][h] in colors: | |
if h > height_table[type].max: | |
height_table[type].max = h | |
if h < height_table[type].min: | |
height_table[type].min = h | |
return height_table[type] | |
func height_to_solid(height): | |
if height > 11: | |
return 0.3 | |
elif height > 6: | |
return 0.5 | |
elif height > 3: | |
return 0.6 | |
else: | |
return solid | |
func get_col_from_height(height, climat="medium"): | |
if is_editor: | |
climat = "base" | |
var max_h = height_color[climat].keys().max() | |
var min_h = height_color[climat].keys().min() | |
if height > max_h: | |
height = max_h | |
elif height < min_h: | |
height = min_h | |
while not height in height_color[climat]: | |
height +=1 | |
if height > 15: height = 0 | |
var c = height_color[climat][height] | |
if typeof(c) == TYPE_ARRAY: | |
c = c[randi()%c.size()] | |
if c in color_to_type: | |
return [colors[c], color_to_type[c]] | |
return [colors[c], WATER] | |
func get_random_color(): | |
return colors[colors.keys()[randi()%len(colors)]] | |
func set_w(val, force=false): | |
if abs(val-width) == 1: | |
width += chank_dim * (val - width) | |
else: | |
width = round(val / chank_dim) * chank_dim | |
if not force: | |
check_update() | |
func get_w(): | |
return width | |
func set_h(val, force=false): | |
if abs(val-height) == 1: | |
height += chank_dim * (val - height) | |
else: | |
height = round(val / chank_dim) * chank_dim | |
if not force: | |
check_update() | |
func get_h(): | |
return height | |
func set_r(val, force=false): | |
# if not val or not typeof(val) in [TYPE_INT, TYPE_REAL]: | |
# return | |
outerRadius = val | |
innerRadius = outerRadius * 0.866025404 | |
corners = [ | |
Vector3(0, 0, outerRadius), | |
Vector3(innerRadius, 0, 0.5 * outerRadius), | |
Vector3(innerRadius, 0, -0.5 * outerRadius), | |
Vector3(0, 0, -outerRadius), | |
Vector3(-innerRadius, 0, -0.5 * outerRadius), | |
Vector3(-innerRadius, 0, 0.5 * outerRadius), | |
Vector3(0, 0, outerRadius), | |
]; | |
if not force: | |
check_update() | |
func get_r(): | |
return outerRadius | |
func is_hex_buildable(hex): | |
if hex.type in [GRASS, SAND] and (not hex.building or hex.building == "village_res"): | |
return true | |
return false | |
func get_hex(v, from_coordinates=false): | |
if from_coordinates: | |
v = axis_pixel_to_hex(v) | |
return Hex.get_hex(hex_array, v) | |
func get_hex_neighbor(hex, dir): | |
return hex.get_neighbor(hex_array, dir) | |
func get_hex_ring(center=Vector2(0,0), radius=5): | |
if typeof(center) == TYPE_VECTOR2: | |
center = Hex.axial_to_cube(center) | |
elif center is Hex: | |
center = Hex.cube() | |
var results = [] | |
# this code doesn't work for radius == 0; can you see why? | |
var cube = center | |
# cube.x -= radius | |
# cube.z += radius | |
for i in range(6): | |
for j in range(radius): | |
results.append(cube) | |
cube = Hex.neighbor_hex(cube, i) | |
return results | |
func get_hex_neighbors(hex): | |
var res = [] | |
for i in range(6): | |
var n = hex.get_neighbor(hex_array, i) | |
if n: | |
res.append(n) | |
return res | |
func get_hex_neighbors_at_range(hex, N, exlude_self=false): | |
var results = [] | |
for x in range(-N, N+1): | |
for y in range( max(-N, -x-N), min(+N, -x+N)+1): | |
var z = -x-y | |
var new_coor = Vector3(x,y,z) + hex.cube() | |
var h = get_hex(Hex.cube_to_axial(new_coor)) | |
if h and not (exlude_self and hex == h): | |
results.append(h) | |
return results | |
#Helper | |
func get_hex_pos(pos): | |
var hex = get_hex(axis_pixel_to_hex(pos)) | |
if hex: | |
return hex.position | |
func calc_res_count(hex, radius, res): | |
return hex.calc_res_count(hex_array, radius, res) | |
func check_update(): | |
# return | |
if (Engine.editor_hint and update_in_editor) or keep_terrain: | |
_update() | |
func _update(generate=true, blank = false): | |
dprint("Update, generate = ", generate, " blank = ", blank) | |
dprint ("Clear previous") | |
astar = AStar.new() | |
changed_hexes = [] | |
hexes_with_buildings = [] | |
for c in get_children(): | |
c.queue_free() | |
if generate or blank: | |
hex_array = [] | |
towns = [] | |
real_towns = [] | |
set_h(height, true) | |
set_w(width, true) | |
var chank_count_x = int(height / chank_dim) | |
var chank_count_y = int(width / chank_dim) | |
var t = OS.get_ticks_msec() | |
if generate: | |
dprint ("Generating map") | |
generate_map() | |
dprint("Map generated") | |
generate_hexes() | |
# adjust_mountains() | |
generate_map_buildings() | |
generate_aligned_reses() | |
if not is_editor: | |
generate_towns() | |
dprint("Hexes generated") | |
elif blank: | |
dprint("Generate blank") | |
generate_blank() | |
else: | |
# Update hexes with buildings | |
generate_only_heightmap() | |
for i in height: | |
for j in width: | |
var hex : Hex= hex_array[i][j] | |
if hex and hex.building: | |
hexes_with_buildings.append(hex) | |
if hex and hex.decor: | |
if hex.object or hex.building or hex.resource: | |
hex.decor = null | |
# Chanks | |
chanks = [] | |
for x in range(chank_count_x): | |
chanks.append([]) | |
for y in range(chank_count_y): | |
progress_text = "Chunk " + str(x) + "|" + str(y)+ ", overrall " \ | |
+ str(100 * (x* chank_count_x + y) / (chank_count_x*chank_count_y)) + "%" | |
progress = clamp(100 * (x* chank_count_x + y) / (chank_count_x*chank_count_y), 0, 100) | |
if get_parent().has_node("UI/Loading") and not Engine.editor_hint: | |
get_parent().UI.loading_screen.set_progress(progress) | |
yield(get_tree(), "idle_frame") | |
var x1 = x*chank_dim | |
var x2 = (x+1) * chank_dim | |
var y1 = y*chank_dim | |
var y2 = (y+1) * chank_dim | |
dprint("Terrain generate") | |
chanks[x].append(generate_terrain(x1,x2,y1,y2)) | |
chank_dims[chanks[x][y]] = [x1,x2,y1,y2] | |
dprint("Terrain ok") | |
dprint("generate_other_res") | |
chanks[x][y].call_deferred("add_child", generate_other_res(x1,x2,y1,y2, not is_editor)) | |
dprint("generate_other_res ok") | |
dprint("generate_buildings ok") | |
chanks[x][y].call_deferred("add_child", generate_buildings(chanks[x][y], not is_editor)) | |
dprint("generate_buildings ok") | |
generate_astar() | |
if generate and not is_editor: | |
for t in real_towns: | |
dprint("Init town") | |
t.init() | |
towns = real_towns | |
dprint("Map generated in ", OS.get_ticks_msec() - t) | |
dprint("Generated, call callback") | |
call_deferred("_bg_load_done") | |
func generate_map(): | |
# Instantiate | |
var noise = OpenSimplexNoise.new() | |
var tree_noise = OpenSimplexNoise.new() | |
var mountain_noise = OpenSimplexNoise.new() | |
# Configure | |
noise.seed = randi() | |
noise.octaves = octaves | |
noise.period = period | |
noise.persistence = persistence | |
tree_noise.seed = randi() | |
tree_noise.octaves = m_octaves | |
tree_noise.period = m_period | |
tree_noise.persistence = m_persistence | |
# mountain_noise.seed = randi() | |
# mountain_noise.octaves = octaves | |
# mountain_noise.period = period * 0.25 | |
# mountain_noise.persistence = persistence | |
# Sample | |
var temp_hex = [] | |
for x in range(height): | |
temp_hex.append([]) | |
for y in range(width): | |
temp_hex[x].append(noise.get_noise_2d(x,y)) | |
var rx = 1.5* abs(-height/2 + x) | |
var ry = 1.5*abs(y - width/2) | |
var r = rx*rx/ (0.25* height*height) + ry*ry/(0.25*width*width) | |
if r >= 1: | |
temp_hex[x][y] -= r-1 | |
# else: | |
# temp_hex[x][y] =1.0 | |
var hex_need_align = [] | |
hex_array = [] | |
for x in range(height): | |
hex_array.append([]) | |
for z in range(width): | |
var height_float = temp_hex[x][z] | |
var tmp_landscape_height = (landscape_height-water_level+1) | |
var h_height = clamp(int(round(height_float * tmp_landscape_height)) + water_level, min_height, tmp_landscape_height) | |
if h_height >=min_mountain_height: # Add some big mountains | |
h_height += 2*clamp(int(round(height_float * m_height))-2 + water_level, 0, m_height) | |
# height = clamp(int(round(((mountain_noise.get_noise_2d(x,z))/2 * m_height)))+ height, 4, m_height) | |
if x <= 1 or z <= 1 or x >= height-2 or z >= height - 2: | |
h_height = min_height | |
var color_type = get_col_from_height(h_height) | |
var hex = Hex.new(x,z,color_type[0]) | |
hex.index = x*width + z | |
hex.type = color_type[1] | |
var trees = tree_noise.get_noise_2d(x,z) | |
# var free = 6 | |
if not hex.building: | |
if hex.type == GRASS and trees > 0: | |
hex.resource = "wood" | |
hex.density = trees | |
elif hex.type in [GRASS, SAND] and randf() > 0.9: # variable must go out | |
hex.resource = "stone" | |
elif hex.type in [GRASS, SAND] and randf() > 0.9: # variable must go out | |
var c = ["crystall_green","crystall_red","crystall_blue"][randi()%3] | |
hex.resource = c | |
elif randf() > 0.3 and hex.type in misc_array: | |
hex.decor = misc_array[hex.type].keys()[randi()%misc_array[hex.type].size()] | |
hex_array[x].append(hex) | |
hex.height = h_height | |
func add_hex_decor(hex): | |
if hex.type in misc_array: | |
hex.decor = misc_array[hex.type].keys()[randi()%misc_array[hex.type].size()] | |
func adjust_mountains(): | |
return #Deprecated | |
for x in range(height): | |
for z in range(width): | |
var hex = hex_array[x][z] | |
if not hex or not hex.type == MOUNTAIN: | |
continue | |
var maxmin = get_min_max_height(MOUNTAIN) | |
var mountain_count = 0 | |
var medium_height = 0.0 | |
for neig in get_hex_neighbors(hex): | |
mountain_count += 1 | |
medium_height += neig.height | |
medium_height /= mountain_count | |
#All hexes around also mountains | |
if mountain_count == 6: | |
if hex.height < medium_height+4: | |
hex.height += 6 | |
elif mountain_count <=3 and randf() > 0.7: | |
hex.height += 4 | |
func generate_map_buildings(): | |
for x in range(height): | |
for z in range(width): | |
var hex = hex_array[x][z] | |
if not hex: | |
continue | |
# Add full_hex buildings | |
var available_building = { | |
"ruins": [1], | |
"dwelling": [2], | |
"npc_village": [3], | |
"watchtower":[4], | |
"waterfall":[5], | |
"dungeon":[6] | |
} | |
if hex.type in [GRASS, SAND]: | |
if randf() > 0.9: | |
hex.decor = null | |
var can_place = true | |
if not Engine.editor_hint: | |
var b_type = g.select_from_dice(available_building, "1d6", false) | |
for h in get_hex_neighbors_at_range(hex, 2, true): | |
if h.building: # and h.building == b_type: | |
can_place = false | |
break | |
if can_place: | |
hex.building = b_type | |
dprint("Building ", hex.building) | |
hexes_with_buildings.append(hex) | |
if hex.resource: | |
hex.resource = null | |
else: | |
hex.building = ["ruins","dwelling", "npc_village"][randi()%3] | |
func generate_aligned_reses(): | |
for x in range(height): | |
for z in range(width): | |
var hex = hex_array[x][z] | |
if not hex: | |
continue | |
if hex.type in [GRASS, SAND] and not hex.building: | |
# var neis = get_hex_neighbors(hex) | |
for i in range(6): | |
var n_right = get_hex_neighbor(hex, i) | |
var n_left = get_hex_neighbor(hex, i-1) | |
if n_right.type == MOUNTAIN and n_left.type == MOUNTAIN and randf() >0.7: | |
hex.resource = "iron" | |
break | |
func get_align_res_angle(hex, align_to, rad=false): | |
var angle = 0 | |
for i in range(6): | |
var n_right = get_hex_neighbor(hex, i) | |
var n_left = get_hex_neighbor(hex, i-1) | |
if n_right.type == align_to and n_left.type == align_to: | |
angle = (i+3) * 60 | |
if angle > 360: | |
angle -= 360 | |
if rad: | |
return deg2rad(angle) | |
else: | |
return angle | |
return angle | |
func generate_towns(): | |
if Engine.editor_hint: | |
return | |
dprint("Generate towns") | |
towns = [] | |
var p = 0 | |
var iters = 0 | |
while p < players.size() and iters < 3000: | |
iters += 1 | |
# dprint("Player ", p) | |
var x = int(randf() * height) | |
var y = int(randf() * width) | |
var hex = hex_array[x][y] | |
if hex and not hex.building and hex.type in [GRASS, SAND]: | |
var free_neighbors = 0 | |
# Check distance to other capitals | |
if get_dist_to_nearest_capital(hex) < (height / players.size() / 2): | |
# dprint("Too near") | |
continue | |
for i in range(6): | |
var n = hex.get_neighbor(hex_array, i) | |
if n and n.type in [GRASS, SAND]: | |
free_neighbors += 1 | |
if free_neighbors >= 2: # and hex.calc_res_count(hex_array, 3, "wood") > 0 and hex.calc_res_count(hex_array, 3, "stone") > 0: | |
hex.building = "town" | |
players[p].capital = hex | |
hex.resource = null | |
towns.append(g.town(hex,g.get_random_town_name(),players[p])) | |
hex.building_data = towns[towns.size()-1] | |
p += 1 | |
if not hex in hexes_with_buildings: | |
hexes_with_buildings.append(hex) | |
dprint("Iters for towns used: ",iters) | |
# elif hex: | |
# dprint("Bad hex ", not hex.building, " ", hex.type in [GRASS, SAND]) | |
func generate_other_res(x1,x2,y1,y2, start_invisible=true): | |
# dprint("Res. Start invisible? ", start_invisible) | |
var node = Spatial.new() | |
# var mod = outerRadius / 5.0 | |
node.name = "other_res" | |
if add_objects or is_editor: | |
for x in range(x1,x2): | |
for y in range(y1,y2): | |
var hex = hex_array[x][y] | |
if hex: | |
update_res_at_hex(node, hex, false, start_invisible) | |
update_decor_at_hex(node, hex, false, start_invisible) | |
return node | |
func update_building_at_hex(node, hex, clear=false, start_invisible=true): | |
if clear: | |
if hex.object and hex.object is Spatial: | |
hex.object.queue_free() | |
hex.object = null | |
var b = null | |
var mod = outerRadius / 5.0 | |
match hex.building: | |
"npc_village": | |
b = npc_village | |
"town": | |
b = town | |
"town_dummy": | |
b = town_dummy | |
"watchtower": | |
b = watch_tower | |
"dwelling": | |
#Fix for previous version | |
if typeof(hex.meta) == TYPE_STRING: | |
hex.meta = {'building_type': hex.meta} | |
if hex.meta and "building_type" in hex.meta: | |
b = g.get_dwelling(hex.meta.building_type, true) | |
else: | |
var b_name = g.get_random_dwelling_name() | |
b = g.get_dwelling(b_name, true) | |
if not hex.meta: | |
hex.meta = {} | |
hex.meta.building_type = b_name | |
"ruins", "dungeon": | |
if hex.meta and "building_type" in hex.meta: | |
b = g.get_dungeon(hex.meta.building_type, true) | |
else: | |
var b_name = g.get_random_dungeon_name() | |
b = g.get_dungeon(b_name, true) | |
if not hex.meta: | |
hex.meta = {} | |
hex.meta.building_type = b_name | |
"waterfall": | |
b = waterfall | |
"castle": | |
b = castle | |
"tower_wisdom": | |
b = tower_wisdom | |
_: | |
pass | |
if b: | |
# dprint("Building is in progress ", hex.building) | |
var building = b.instance() | |
building.translation = hex.position | |
building.rotate_y(PI* randf()) | |
# building.scale = building.scale * mod | |
if hex.building == "town": | |
building.rotation.y = 0 | |
building.town = hex.building_data | |
building.hex = hex | |
real_towns.append(building) | |
elif hex.building == "town_dummy": | |
building.hex = hex | |
if not hex.building_data: | |
hex.building_data = g.town(hex,g.get_random_town_name()) | |
building.town = hex.building_data | |
if building.town and building.town.player: | |
building.init() | |
elif building is GlobalBuilding: | |
building.hex = hex | |
global_buildings.append(building) | |
if building is Dwelling: | |
building.init(g.get_dwelling(hex.meta.building_type, false)) | |
if building is Dungeon: | |
building.init(g.get_dungeon(hex.meta.building_type, false)) | |
hex.object = building | |
node.add_child(building) | |
if start_invisible: | |
if building.has_method("set_grayscale"): | |
building.set_grayscale(true) | |
building.hide() | |
else: | |
if building.has_method("set_grayscale"): | |
building.set_grayscale(false) | |
building.show() | |
# dprint("Not invisible") | |
func update_fow_mats_for(meshes): | |
for m in meshes + [mat_misc]: | |
if m is MeshInstance: | |
for i in range(m.get_surface_material_count()): | |
if m.get_surface_material(i) is ShaderMaterial: | |
var mat = m.get_surface_material(i).next_pass if m.get_surface_material(i).next_pass else m.get_surface_material(i) | |
if mat in fow_mats: | |
continue | |
fow_mats.append(mat) | |
func update_decor_at_hex(node, hex, clear=false, start_invisible=true): | |
if not hex or not hex.type in misc_array: | |
return | |
if not hex.decor or clear: | |
if hex.decor_object and hex.decor_object is Spatial: | |
hex.decor_object.queue_free() | |
hex.decor_object = null | |
if not hex.decor: | |
return | |
# seed(hex.x + hex.y) | |
var mod = outerRadius / 5.0 | |
var mesh = misc_array[hex.type][hex.decor].instance() | |
var meshes = mesh.get_children() | |
if mesh: | |
meshes.append(mesh) | |
update_fow_mats_for(meshes) | |
mesh.translation = hex.position # + get_coordinates_for_item(n, random) | |
mesh.scale = mesh.scale * mod | |
mesh.name = "decor"+str(hex.x)+str(hex.y) | |
#common | |
hex.decor_object = mesh | |
node.add_child(mesh) | |
var angle = (randi() % 6) * 60 | |
mesh.rotation.y = deg2rad(angle) | |
if start_invisible: | |
if mesh.has_method("set_grayscale"): | |
mesh.set_grayscale(true) | |
mesh.hide() | |
else: | |
if mesh.has_method("set_grayscale"): | |
mesh.set_grayscale(false) | |
mesh.show() | |
func update_res_at_hex(node, hex, clear=false, start_invisible=true): | |
if not hex: | |
return | |
if clear: | |
if hex.resource_object and hex.resource_object is Spatial: | |
hex.resource_object.queue_free() | |
hex.resource_object = null | |
# seed(hex.x + hex.y) | |
var mod = outerRadius / 5.0 | |
var c = hex.resource | |
var mesh | |
if c == "stone": | |
mesh = rocks.instance() | |
elif c == "wood": | |
var forest_pack | |
if hex.density > 0.7: | |
forest_pack = forest_array.full | |
elif hex.density > 0.3: | |
forest_pack = forest_array.medium | |
else: | |
forest_pack = forest_array.small | |
mesh = forest_pack[randi() % forest_pack.size()].instance() | |
update_fow_mats_for(mesh.get_children()) | |
elif c == "tree": | |
mesh = forest_array.battle[randi() % forest_array.battle.size()].instance() | |
elif c == "iron": | |
mesh = iron.instance() | |
elif c in ["crystall_green", "crystall_red", "crystall_blue"]: | |
mesh = crystall.instance() | |
mesh.get_node("Crystall").material_override.set_shader_param("color",colors[c]) | |
mesh.get_node("Crystall").material_override.set_shader_param("use_vertex_color",false) | |
if mesh: | |
mesh.translation = hex.position # + get_coordinates_for_item(n, random) | |
mesh.scale = mesh.scale * mod | |
mesh.name = c+str(hex.x)+str(hex.y) | |
#common | |
hex.resource_object = mesh | |
node.add_child(mesh) | |
if not c == "iron": | |
var angle = (randi() % 6) * 60 | |
mesh.rotation.y = deg2rad(angle) | |
else: | |
mesh.rotation_degrees.y = get_align_res_angle(hex, MOUNTAIN) | |
if start_invisible: | |
if mesh.has_method("set_grayscale"): | |
mesh.set_grayscale(true) | |
mesh.hide() | |
else: | |
if mesh.has_method("set_grayscale"): | |
mesh.set_grayscale(false) | |
mesh.show() | |
func generate_buildings(chank, start_invisible=true): | |
dprint("Buildings. Start invisible? ", start_invisible) | |
var node = Spatial.new() | |
node.name = "buildings" | |
# var mod = outerRadius / 5.0 | |
for hex in hexes_with_buildings: | |
if not hex.building or hex.chank != chank: | |
continue | |
update_building_at_hex(node, hex, false, start_invisible) | |
return node | |
func build_process(data): | |
while need_build: | |
if building_queue and building_queue.size() > 0: | |
var hex = building_queue.pop_front() | |
build_hex(hex) | |
building_thread.wait_to_finish() | |
func stop(): | |
need_build = false | |
if thread and thread.is_active(): | |
thread.wait_to_finish() | |
func add_to_queue(hex): | |
if not building_thread or not building_thread.is_active(): | |
need_build = true | |
building_thread = Thread.new() | |
building_thread.start(self, "build_process") | |
if not Engine.editor_hint: | |
g.workers.append(self) | |
if not building_queue: | |
building_queue = [] | |
building_queue.append(hex) | |
func generate_blank(): | |
var hex_need_align = [] | |
hex_array = [] | |
for x in range(height): | |
hex_array.append([]) | |
for z in range(width): | |
var height = 1 | |
var color_type = get_col_from_height(height) | |
var hex = Hex.new(x,z,color_type[0]) | |
hex.index = x*width + z | |
hex.type = GRASS | |
hex_array[x].append(hex) | |
hex.height = height | |
generate_hexes() | |
func generate_hexes(): | |
var tex | |
# if generate_height_maps: | |
tex = Image.new() | |
tex.create(height, width, false, Image.FORMAT_RGBAF) | |
tex.lock() | |
for x in range(height): | |
for z in range(width): | |
var hex = hex_array[x][z] | |
# if generate_height_maps: | |
if not Engine.editor_hint: | |
tex.set_pixel(x,z, Color((hex.height)/g.MAX_TERRAIN_HEIGHT, | |
(hex.height)/g.MAX_TERRAIN_HEIGHT, | |
(hex.height)/g.MAX_TERRAIN_HEIGHT)) | |
var position = Vector3() | |
position.x = (x + z/2.0) * (innerRadius * 2) | |
position.y = hex.height * step_height | |
position.z = z * (outerRadius * 1.5) | |
if z/2 > 0: | |
position.x -= z/2 * (innerRadius * 2) | |
hex.x -= (z/2) | |
hex.position = position | |
# if generate_height_maps: | |
tex.unlock() | |
tex.save_png("res://height.png") | |
heightmap_texture = ImageTexture.new() | |
heightmap_texture.create_from_image(tex, 0) | |
func generate_only_heightmap(): | |
var tex | |
# if generate_height_maps: | |
tex = Image.new() | |
tex.create(height, width, false, Image.FORMAT_RGBAF) | |
tex.lock() | |
for x in range(height): | |
for z in range(width): | |
var hex = hex_array[x][z] | |
# if generate_height_maps: | |
if hex: | |
tex.set_pixel(x,z, Color((hex.height)/g.MAX_TERRAIN_HEIGHT, | |
(hex.height)/g.MAX_TERRAIN_HEIGHT, | |
(hex.height)/g.MAX_TERRAIN_HEIGHT)) | |
tex.unlock() | |
tex.save_png("res://height.png") | |
heightmap_texture = ImageTexture.new() | |
heightmap_texture.create_from_image(tex, 0) | |
func build_hex(hex, force=false, update_resource=true, update_building=true, update_decor=true, after_build=null): | |
if update_resource and ((hex and hex.resource and not hex.resource_object) or force): | |
update_res_at_hex(hex.chank.get_node("other_res"), hex, true,false) | |
if update_building and ((hex and hex.building and not hex.object) or force): | |
update_building_at_hex(hex.chank.get_node("buildings"), hex, true,false) | |
if update_decor and ((hex and hex.decor and not hex.decor_object) or force): | |
update_decor_at_hex(hex.chank.get_node("other_res"), hex, true,false) | |
if after_build: | |
if "target" in after_build and "func" in after_build: | |
if "arg" in after_build: | |
after_build.target.call(after_build.func, after_build.arg) | |
else: | |
after_build.target.call(after_build.func) | |
func build_hex_deffered(hex, force=false, update_resource=true, update_building=true, update_decor=true, after_build=null): | |
call_deferred("build_hex", hex, force, update_resource, update_building, update_decor, after_build) | |
func axial_coor_to_real(axial, h=2): | |
var x = outerRadius * (sqrt(3) * axial.x + sqrt(3)/2 * axial.y) | |
var y = outerRadius * ( 3.0/2 * axial.y) | |
return Vector3(x, h, y) | |
func generate_fly_astar(_height=null, _width=null): | |
fly_astar = AStar.new() | |
if not _height: | |
_height = height | |
if not _width: | |
_width = width | |
for x in range(_height): | |
for z in range(_width): | |
var hex = hex_array[x][z] | |
if hex: # Can move on these tiles | |
if not fly_astar.has_point(hex.index): | |
var p = hex.position | |
fly_astar.add_point(hex.index, p, 1) | |
# Astar create connections | |
for x in range(_height): | |
for z in range(_width): | |
var hex = hex_array[x][z] | |
if hex and fly_astar.has_point(hex.index): | |
for i in range(6): | |
var neighbor = hex.get_neighbor(hex_array, i) | |
if neighbor: | |
if fly_astar.has_point(neighbor.index): | |
if not fly_astar.are_points_connected(hex.index, neighbor.index): | |
fly_astar.connect_points(hex.index, neighbor.index) | |
func generate_astar(_height=null, _width=null): | |
astar = AStar.new() | |
# astar.clear() | |
if not _height: | |
_height = height | |
if not _width: | |
_width = width | |
for x in range(_height): | |
for z in range(_width): | |
var hex = hex_array[x][z] | |
if hex and hex.type in [GRASS, SAND] and hex.weight >= 1: # Can move on these tiles | |
if not astar.has_point(hex.index): | |
var p = hex.position | |
# p.y = 0 | |
# if hex.resource: | |
# print("Astar add point ", hex.resource, hex.weight) | |
astar.add_point(hex.index, p, hex.weight) | |
# Astar create connections | |
for x in range(_height): | |
for z in range(_width): | |
var hex = hex_array[x][z] | |
if hex and astar.has_point(hex.index): | |
for i in range(6): | |
var neighbor = hex.get_neighbor(hex_array, i) | |
if neighbor: | |
if astar.has_point(neighbor.index): | |
if not astar.are_points_connected(hex.index, neighbor.index): | |
astar.connect_points(hex.index, neighbor.index) | |
var changed_hexes = [] | |
func clean_astar(): | |
for hex in changed_hexes: | |
if astar.has_point(hex.index) and hex.weight >= 1: | |
astar.set_point_weight_scale(hex.index, hex.weight) | |
else: | |
add_point(hex) | |
changed_hexes = [] | |
func get_astar_weight(hex): | |
if astar.has_point(hex.index): | |
return astar.get_point_weight_scale(hex.index) | |
func update_astar_weight(hex, new_weight=-1): | |
if not hex: | |
return | |
if new_weight <=0: | |
new_weight = hex.weight | |
if astar.has_point(hex.index): | |
astar.set_point_weight_scale(hex.index, new_weight) | |
changed_hexes.append(hex) | |
func remove_point(hex): | |
astar.remove_point(hex.index) | |
changed_hexes.append(hex) | |
func add_point(hex, weight=-1): | |
if weight <=0: | |
if hex.weight >= 1: | |
weight = hex.weight | |
else: | |
return | |
if not astar.has_point(hex.index): | |
var p = hex.position | |
astar.add_point(hex.index, p, weight) | |
for i in range(6): | |
var neighbor = hex.get_neighbor(hex_array, i) | |
if neighbor: | |
if astar.has_point(neighbor.index): | |
if not astar.are_points_connected(hex.index, neighbor.index): | |
astar.connect_points(hex.index, neighbor.index) | |
func generate_terrain(x1,x2,y1,y2, base_chank=null): | |
var geom; | |
dprint("Start generate chank ", str(x1)+":"+str(x2)," - ", str(y1)+":"+str(y2)) | |
if not base_chank: | |
geom = MeshInstance.new() | |
add_child(geom) | |
else: | |
geom = base_chank | |
# var t = OS.get_ticks_msec() | |
var surf_tool = SurfaceTool.new() | |
mesh = Mesh.new() | |
surf_tool.begin(Mesh.PRIMITIVE_TRIANGLES) | |
surf_tool.set_material(mat) | |
dprint("Create hexes") | |
var is_hexes_generated = false | |
for x in range(x1,x2): | |
for y in range(y1,y2): | |
var hex = hex_array[x][y] | |
if hex: | |
if hex.height > min_height: | |
is_hexes_generated = true | |
hex.chank = geom | |
create_cell(surf_tool, hex.position, hex) | |
dprint("Normales") | |
surf_tool.generate_normals() | |
surf_tool.index() | |
surf_tool.commit(mesh) | |
dprint("Additional generation") | |
addition_generation(surf_tool) | |
#Generate misc | |
if misc_enabled: | |
surf_tool.begin(Mesh.PRIMITIVE_TRIANGLES) | |
surf_tool.set_material(mat_misc) | |
for x in range(x1,x2): | |
for y in range(y1,y2): | |
var hex = hex_array[x][y] | |
if hex and hex.type in [GRASS, SAND]: | |
hex.chank = geom | |
create_misc_cell(surf_tool, hex.position, hex) | |
surf_tool.generate_normals() | |
surf_tool.index() | |
surf_tool.commit(mesh) | |
geom.set_mesh(mesh) | |
for c in geom.get_children(): | |
if c is StaticBody: | |
c.queue_free() | |
if is_hexes_generated: | |
geom.create_trimesh_collision() | |
# geom.material_override = mat | |
if not base_chank: | |
if geom.get_child_count() > 0: | |
geom.get_child(0).connect("input_event", self, "_on_input_event") | |
geom.get_child(0).set_collision_layer_bit(3,true) | |
geom.get_child(0).set_collision_mask_bit(3,true) | |
else: | |
for c in geom.get_children(): | |
if c is StaticBody and is_instance_valid(c): | |
if not c.is_connected("input_event", self, "_on_input_event"): | |
c.connect("input_event", self, "_on_input_event") | |
c.set_collision_layer_bit(3,true) | |
c.set_collision_mask_bit(3,true) | |
geom.name = "chank"+str(x1)+","+str(x2)+"-" +str(y1)+"_"+str(y2) | |
# geom.cast_shadow = false | |
dprint("Chank generated") | |
return geom | |
func addition_generation(surf_tool): | |
pass | |
func create_cell(surf_tool, pos=Vector3(0,0,0), hex=null): | |
var color = Color("ffffff") | |
if hex: | |
color = hex.color | |
var add_vector = Vector3(0,0,0) | |
if hex.type == MOUNTAIN: | |
var count = 0 | |
for nei in get_hex_neighbors(hex): | |
if nei.type == MOUNTAIN: | |
count += 1 | |
if count == 0 or randf() >= 0.8: | |
add_vector = Vector3(0,2,0) | |
for i in range(6): | |
var left = i - 1 | |
if left < 0: left = 5 | |
var right = i + 1 | |
if right > 5: right = 0 | |
# pos.y = hex.height * step_height | |
var tmp_solid = height_to_solid(hex.height) | |
var n = hex.get_neighbor(hex_array, i) | |
var n_r = hex.get_neighbor(hex_array, right) | |
if hex.height > min_height: | |
if hex.type != MOUNTAIN: | |
add_triangle(surf_tool, pos, | |
pos+corners[i+1]*tmp_solid, | |
pos+corners[i]*tmp_solid, | |
color) | |
else: | |
add_triangle(surf_tool, pos+add_vector, | |
pos+corners[i+1]*tmp_solid, | |
pos+corners[i]*tmp_solid, | |
color) | |
# else: | |
# if n and n.height <= min_height: | |
# n = null | |
# if n_r and n_r.height <= min_height: | |
# n_r = null | |
var col_out = color | |
var col_right = color | |
var col_mix_r = color | |
var col_mix = color | |
var col_in = color | |
# Mountains goes from bottom | |
if n: | |
col_out = n.color | |
col_mix = (n.color + color)/2 | |
if n_r: | |
col_right = (color + n_r.color) / 2 | |
if n_r and n: | |
col_mix_r = (color+n_r.color + n.color) / 3 | |
elif n_r: | |
col_mix_r = col_right | |
elif n: | |
col_mix_r = col_mix | |
var center = ((corners[i] + corners[i+1]) / 2) * (1 - solid) | |
var v1 = pos+(corners[i]* solid + center) | |
var v2 = pos+(corners[i+1]* solid + center) | |
# Fix mountains | |
if color == colors.rock or (n and n.color == colors.rock): | |
if color != colors.snow and (n and n.color != colors.snow): | |
col_in = colors.rock | |
col_out = colors.rock | |
if n and i >2 and (n.height > min_height or hex.height > min_height): | |
var oi = i - 3 | |
if oi < 0: | |
oi += 6 | |
var n_solid = height_to_solid(n.height) | |
add_quad(surf_tool, pos+corners[i]*tmp_solid, pos+corners[i+1]*tmp_solid, | |
n.position+corners[oi+1]*n_solid, n.position+corners[oi]*n_solid, col_in, col_out) | |
if n_r and n and i >2 and (n_r.height > min_height or n.height > min_height or hex.height > min_height): | |
var oi = i - 3 | |
var loi = i -1 | |
if oi < 0: | |
oi += 6 | |
if loi < 0: | |
loi += 6 | |
col_in = color | |
col_out = n.color | |
col_right = n_r.color | |
if color == colors.rock or n.color == colors.rock or n_r.color == colors.rock: | |
if color != colors.snow and n.color != colors.snow and n_r.color != colors.snow: | |
col_out = colors.rock | |
col_right = colors.rock | |
col_in = colors.rock | |
add_triangle(surf_tool, pos+corners[i+1]*tmp_solid, | |
n_r.position + corners[loi]*height_to_solid(n_r.height), | |
n.position+corners[oi]*height_to_solid(n.height), | |
col_in, col_right, col_out) | |
func strong_color_from_type(hex): | |
match hex.type: | |
GRASS: | |
return colors.strong_green | |
SAND: | |
return colors.strong_red | |
MOUNTAIN: | |
return colors.strong_blue | |
_: | |
return Color("ffffff") | |
func create_misc_cell(surf_tool, pos=Vector3(0,0,0), hex=null): | |
var color = Color("ffffff") | |
color = strong_color_from_type(hex) | |
var color_alpha = color | |
color_alpha.a = 0 | |
var add = Vector3(0,0.01,0) | |
for i in range(6): | |
var left = i - 1 | |
if left < 0: left = 5 | |
var right = i + 1 | |
if right > 5: right = 0 | |
# pos.y = hex.height * step_height | |
var tmp_solid = height_to_solid(hex.height) | |
var n = hex.get_neighbor(hex_array, i) | |
var n_r = hex.get_neighbor(hex_array, right) | |
var n_l = hex.get_neighbor(hex_array, left) | |
# var center = ((corners[i] + corners[i+1]) / 2) * (1 - solid) | |
# var v1 = pos+(corners[i]* solid + center) | |
# var v2 = pos+(corners[i+1]* solid + center) | |
var col_l = color | |
var col_r = color | |
col_l.a = 0.0 | |
col_r.a = 0.0 | |
if n_l and n_l.type == hex.type and hex.height == n_l.height: | |
col_l.a = 1 | |
if n_r and n_r.type == hex.type and hex.height == n_r.height: | |
col_r.a = 1 | |
if n and i >2 and n.type == hex.type and hex.height == n.height: | |
var oi = i - 3 | |
if oi < 0: | |
oi += 6 | |
var n_solid = height_to_solid(n.height) | |
add_quad_prec(surf_tool, pos+corners[i]*tmp_solid, pos+corners[i+1]*tmp_solid, | |
n.position+corners[oi+1]*n_solid, n.position+corners[oi]*n_solid, | |
col_l, col_r, col_l, col_r) | |
if n_r and n and i >2 and n_r.type == hex.type and n.type == hex.type and \ | |
hex.height == n_r.height and hex.height == n.height: | |
var oi = i - 3 | |
var loi = i -1 | |
if oi < 0: | |
oi += 6 | |
if loi < 0: | |
loi += 6 | |
add_triangle(surf_tool, pos+corners[i+1]*tmp_solid, | |
n.position+corners[oi]*height_to_solid(n.height), | |
n_r.position + corners[loi]*height_to_solid(n_r.height), | |
color, color, color) | |
if n and n.type == hex.type and hex.height == n.height: | |
add_triangle(surf_tool, | |
pos+add, | |
pos+corners[i]*tmp_solid, | |
pos+corners[i+1]*tmp_solid,color, | |
col_l, col_r) | |
else: | |
add_triangle(surf_tool, pos+add, | |
pos+corners[i]*tmp_solid, | |
pos+corners[i+1]*tmp_solid, | |
color, color_alpha, color_alpha) | |
func uv(vertex): | |
return Vector2((vertex.x+outerRadius/2)/outerRadius, (vertex.z+outerRadius/2)/outerRadius) | |
func add_triangle(surf_tool, v1,v2,v3, color, color2=null, color3=null): | |
if not color2: | |
color2 = color | |
if not color3: | |
color3 = color | |
surf_tool.add_color(color) | |
surf_tool.add_uv(uv(v1)) | |
surf_tool.add_vertex(v1) | |
surf_tool.add_color(color2) | |
surf_tool.add_uv(uv(v2)) | |
surf_tool.add_vertex(v2) | |
surf_tool.add_color(color3) | |
surf_tool.add_uv(uv(v3)) | |
surf_tool.add_vertex(v3) | |
func add_quad(surf_tool, v1,v2,v3,v4, color_inner, color_outer): | |
add_quad_prec(surf_tool, v1,v2,v3,v4, color_inner, color_inner,color_outer,color_outer) | |
func add_quad_prec(surf_tool, v1,v2,v3,v4, color_inner_l, color_inner_r, color_outer_l, color_outer_r): | |
surf_tool.add_color(color_inner_l) | |
surf_tool.add_uv(uv(v1)) | |
surf_tool.add_vertex(v1) | |
surf_tool.add_color(color_inner_r) | |
surf_tool.add_uv(uv(v2)) | |
surf_tool.add_vertex(v2) | |
surf_tool.add_color(color_outer_l) | |
surf_tool.add_uv(uv(v3)) | |
surf_tool.add_vertex(v3) | |
surf_tool.add_color(color_outer_l) | |
surf_tool.add_uv(uv(v3)) | |
surf_tool.add_vertex(v3) | |
surf_tool.add_color(color_inner_r) | |
surf_tool.add_uv(uv(v2)) | |
surf_tool.add_vertex(v2) | |
surf_tool.add_color(color_outer_r) | |
surf_tool.add_uv(uv(v4)) | |
surf_tool.add_vertex(v4) | |
func update(param=null): | |
dprint("Try to start update thread") | |
if is_inside_tree(): | |
_bg_load(param) | |
return | |
if thread_free and not thread: | |
thread = Thread.new() | |
thread_free = false | |
thread.start(self, "_bg_load", param) | |
else: | |
dprint("Thread is running or corrupted") | |
else: | |
dprint("Not ready") | |
# Called every frame. 'delta' is the elapsed time since the previous frame. | |
#var time_from_update = 0 | |
#func _process(delta): | |
# if not Engine.editor_hint and thread and thread.is_active(): | |
# if time_from_update > 0.5: | |
# time_from_update = 0 | |
# time_from_update += delta | |
func _bg_load(data): | |
dprint("THREAD LOADING!") | |
# Load the resource | |
var generate = true | |
var blank = false | |
dprint("Thread, params = ", data) | |
if data: | |
thread_callback = data.callback | |
params = data.params | |
if "generate" in data: | |
generate = data.generate | |
if "clear" in data: | |
generate = generate and not data.clear | |
blank = data.clear | |
_update(generate, blank) | |
# Call _bg_load_done on main thread | |
func _ready() -> void: | |
if keep_terrain: | |
check_update() | |
func _bg_load_done(): | |
# Wait for the thread to complete, get the returned value | |
if thread: | |
var error = thread.wait_to_finish() | |
if error == "OK": | |
thread_free = true | |
if thread_callback: | |
dprint("Callback ", thread_callback) | |
if thread_callback.target: | |
if "arg" in thread_callback and thread_callback.arg: | |
thread_callback.target.call(thread_callback.function, thread_callback.arg) | |
else: | |
thread_callback.target.call(thread_callback.function) | |
thread_callback = null | |
# Set to the sprite | |
thread = null | |
var start = true | |
var first_pos = Vector2() | |
func _on_input_event(camera, event, click_position, click_normal, shape_idx): | |
if event is InputEventMouseButton and event.pressed: | |
if not event.button_index in [BUTTON_LEFT, BUTTON_RIGHT]: | |
return | |
var hex = get_hex(axis_pixel_to_hex(click_position)) | |
if not hex: | |
return | |
var nei = null | |
nei = get_hex_neigh(click_position, hex) | |
if hex: | |
emit_signal("clicked", hex, event.button_index, nei) | |
# if event is InputEventMouseMotion: | |
# var hex = get_hex(axis_pixel_to_hex(click_position)) | |
# if hex: | |
# emit_signal("mouse_move", hex) | |
func get_hex_neigh(click_position, hex=null): | |
if not hex: | |
hex = get_hex(click_position, true) | |
if not hex: | |
return null | |
var nei = null | |
# 3d Angle_to working extremly bad | |
# var vec_diff = Vector2(hex.position.x-click_position.x, hex.position.z-click_position.z) | |
# var angle = atan2(vec_diff.x, vec_diff.y) - atan2(0,1) | |
var angle = PI/6 + Vector2(hex.position.x, hex.position.z).angle_to_point(Vector2(click_position.x, click_position.z)) | |
if angle > PI: | |
angle-=2*PI | |
var dir = 0 | |
if angle <=-2*PI/3: dir = 1 | |
elif angle <= -PI/3: dir = 0 | |
elif angle <= 0: dir = 5 | |
elif angle <= PI/3: dir = 4 | |
elif angle <= 2*PI/3:dir = 3 | |
elif angle <= PI: dir = 2 | |
nei = hex.get_neighbor(hex_array, dir) | |
return nei | |
func get_a_fly_path(hex_start, hex_end, exclude_first=true, exclude_last=false, cut=0, _astar=null): | |
return get_a_path(hex_start, hex_end, exclude_first, exclude_last, cut, fly_astar) | |
func get_a_path(hex_start, hex_end, exclude_first=true, exclude_last=false, cut=0, _astar=null): | |
if not _astar: | |
_astar = astar | |
if hex_start and hex_end and astar.has_point(hex_start.index) and astar.has_point(hex_end.index): | |
var res = astar.get_point_path(hex_start.index, hex_end.index) | |
if exclude_first and res.size() > 0: | |
res.remove(0) | |
if exclude_last and res.size() > 0: | |
res.remove(res.size()-1) | |
if cut == 0 or cut > g.MAX_MOVEMENT: | |
cut = g.MAX_MOVEMENT | |
if cut > 0: | |
var i = 0 | |
while res.size() >= cut: | |
res.remove(res.size()-1) | |
i+= 1 | |
assert i < 100 | |
if i >= 100: | |
return [] | |
return res | |
return [] | |
func get_dist_to_nearest_capital(hex): | |
var dist = 10000 | |
for p in players: | |
if "capital" in p and p.capital: | |
dist = min( Hex.distance(hex, p.capital), dist) | |
return dist | |
# Change landscape | |
func get_distance(hex1, hex2): | |
return Hex.distance(hex1, hex2) | |
func change_hex(hex, resource, building): | |
var chank = hex.chank | |
if not chank: | |
return | |
if resource != hex.resource: | |
# to_regenerate.append("other_res") | |
hex.resource = resource | |
update_res_at_hex(chank.get_node("other_res"), hex, true) | |
# regenerate(x1,x2,y1,y2, to_regenerate, hex, chank) | |
func alter_hex_terrain(hexes, alter_height=true): | |
var chanks = [] | |
for h in hexes: | |
if not h.chank in chanks: | |
chanks.append(h.chank) | |
for n in get_hex_neighbors(h): | |
if n.chank != h.chank and not n.chank in chanks: | |
chanks.append(n.chank) | |
call_deferred("bg_generate", {"function": "after_bg_generate",\ | |
"chanks":chanks, "hexes":hexes}, alter_height) | |
func clear_hex(hex,clear_res=true,clear_building=true): | |
if clear_building: | |
if is_instance_valid(hex.object): | |
hex.object.queue_free() | |
hex.building = null | |
if clear_res: | |
if is_instance_valid(hex.resource_object): | |
hex.resource_object.queue_free() | |
hex.resource = null | |
func bg_generate(dict, alter_height): | |
for hex in dict.hexes: | |
if alter_height: | |
var color_type = get_col_from_height(hex.height) | |
if hex.type != color_type[1]: | |
#If was mountain, check neighbors for mines | |
if hex.type == MOUNTAIN: | |
for n in get_hex_neighbors(hex): | |
if n.resource == "iron": | |
clear_hex(n, true, false) | |
hex.type = color_type[1] | |
hex.color = color_type[0] | |
hex.position.y = hex.height * step_height | |
if hex.type in [MOUNTAIN, WATER]: | |
clear_hex(hex) | |
else: | |
if hex.object and is_instance_valid(hex.object): | |
hex.object.transform.origin.y = hex.position.y | |
if hex.resource_object and is_instance_valid(hex.resource_object): | |
hex.resource_object.transform.origin.y = hex.position.y | |
for chank in dict.chanks: | |
var d = chank_dims[chank] | |
var x1 = d[0] | |
var x2 = d[1] | |
var y1 = d[2] | |
var y2 = d[3] | |
generate_terrain(x1,x2,y1,y2, chank) | |
# call(dict.function) | |
func after_bg_generate(): | |
thread.wait_to_finish() | |
while thread.is_active(): | |
pass | |
thread_free = true | |
thread = null | |
func pack(): | |
var new_array = [] | |
for x in range(height): | |
new_array.append([]) | |
var i =0 | |
for y in range(width): | |
var hex = hex_array[x][y] | |
if hex: | |
new_array[x].append(hex.pack()) | |
else: | |
new_array[x].append(null) | |
i+=1 | |
# print(new_array) | |
return { | |
"seed": terrain_seed, | |
"width": width, | |
"height": height, | |
"map": new_array | |
} | |
func unpack(data): | |
terrain_seed = data.seed | |
width = data.width | |
height = data.height | |
hex_array = [] | |
for x in range(height): | |
hex_array.append([]) | |
for y in range(width): | |
var h = data.map[x][y] | |
if mode == MODE_GAME: | |
if h.building == "town_dummy": | |
h.building = "town" | |
if h: | |
hex_array[x].append(g.unpack(h)) | |
else: | |
hex_array[x].append(null) | |
# _update(false) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment