Skip to content

Instantly share code, notes, and snippets.

@kzerot
Created August 15, 2022 19:58
Show Gist options
  • Save kzerot/d50ee6db055ff186d44e6f94b29211ca to your computer and use it in GitHub Desktop.
Save kzerot/d50ee6db055ff186d44e6f94b29211ca to your computer and use it in GitHub Desktop.
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