extends Control @export var world_width = 1024 @export var world_height = 512 @export var min_continent = 5 @export var max_continent = 5 @export var min_area = 0.5 @export var max_area = 2.0 @export var min_ratio = -1.0 @export var max_ratio = 1.0 @export var ocean_ratio = 0 @export var nbr_rect_position = 10000 @export var max_intersection_area = 1000.0 @export var mountain_frequency = 1.5 # higher number means less mountains @export var world_elevation = 10 @export var details = 2 @export var coasts_thickness = 0.055 var heightmap: Array var oceanmap: Array var coastmap: Array var slopemap: Array var rivers: Array var continents: Array var nbr_continent var list_continents = [] var list_rect_continents = [] var blocs: Array var mountain_blocs: Array var noise = FastNoiseLite.new() func _ready(): Global.world.width = world_width Global.world.height = world_height noise.seed = randi() noise.fractal_lacunarity = details noise.frequency = 0.005 init_maps() init_blocs() create_continents() # pangea() for continent in continents: fill_continent(continent) set_oceans() set_coasts() # set_slope() # for x in 50000: # erode(Vector2i(randi_range(0, world_width), randi_range(0, world_height))) fix_heightmap() # set_rivers() set_blocs() Global.world.heightMap = heightmap Global.world.blocs = blocs get_tree().change_scene_to_file("scenes/Game.tscn") func init_maps(): for x in world_width: heightmap.append([]) oceanmap.append([]) coastmap.append([]) slopemap.append([]) for y in world_height: heightmap[x].append([]) heightmap[x][y] = 0 oceanmap[x].append([]) oceanmap[x][y] = 0 coastmap[x].append([]) coastmap[x][y] = 0 slopemap[x].append([]) slopemap[x][y] = 0 func create_continents(): var america = Rect2i(Vector2i(0, 0), Vector2i(world_width / 4, world_height)) continents.append(america) var antarctica = Rect2i(Vector2i(world_width / 4, world_height / 2), Vector2i(3 * world_width / 4, world_height / 2)) continents.append(antarctica) var africa = Rect2i(Vector2i(world_width / 4, 3 * world_height / 8), Vector2i(3 * world_width / 8, 3 * world_height / 8)) continents.append(africa) var europa = Rect2i(Vector2i(3 * world_width / 8, 10), Vector2i(world_width / 4, world_height / 2)) continents.append(europa) var asia = Rect2i(Vector2i(4 * world_width / 9, world_height / 8), Vector2i(world_width / 2, 3 * world_height / 8)) continents.append(asia) func pangea(): var pangea = Rect2i(Vector2i(0, 0), Vector2i(world_width, world_height)) continents.append(pangea) # var rng = RandomNumberGenerator.new() # # var continents_area = world_width * world_height * (1.0 - ocean_ratio) # var total_area = 0.0 # var list_other_continents = [] # # rng.randomize() # # nbr_continent = rng.randi_range(min_continent,max_continent) # # for i in nbr_continent: # var continent = {} # continent["area"] = rng.randf_range(min_area,max_area) # continent["ratio"] = exp(rng.randf_range(min_ratio,max_ratio)) # continent["width"] = 0.0 # continent["height"] = 0.0 # continent["x"] = 0.0 # continent["y"] = 0.0 # total_area += continent["area"] # list_continents.append(continent) # # # print("Total area : %f\n" % [total_area]) # # for continent in list_continents: # continent["area"] = (continent["area"] * continents_area / total_area) # continent["height"] = sqrt(continent["area"] / continent["ratio"]) # continent["width"] = continent["area"] / continent["height"] # var rect_continent # var selected_rect_continent # var min_intersection_area = 99999999.99 # # for i in nbr_rect_position: # var intersection_area = 0.0 # # var x = rng.randf_range(0.0,world_width - continent["width"]) # var y = rng.randf_range(0.0,world_height - continent["height"]) # # rect_continent = Rect2(x, y, continent["width"], continent["height"]) # # for other_continent in list_other_continents: # var rect_other_continent = Rect2(other_continent["x"], other_continent["y"], other_continent["width"], other_continent["height"]) # if rect_continent.intersects(rect_other_continent): # # print("Ok intersection\n") # intersection_area += rect_continent.intersection(rect_other_continent).get_area() # # if intersection_area < min_intersection_area: # min_intersection_area = intersection_area # continent["x"] = x # continent["y"] = y # # list_other_continents.append(continent) # continents.append(Rect2(continent["x"], continent["y"], continent["width"], continent["height"])) func fill_continent(continent: Rect2i): for x in continent.size.x: for y in continent.size.y: # set rectange radius var center = Vector2(float(continent.size.x) / 2.0, float(continent.size.y) / 2.0) var distance_x = abs(float(x) - center.x) / center.x var distance_y = abs(float(y) - center.y) / center.y var distance = sqrt(pow(distance_x, 2) + pow(distance_y, 2)) distance = remap(distance, 0, sqrt(2), 0, 1) * 1.2 # set height var height = noise.get_noise_2d(x, y) height = remap(height, -1, 1, 0, 1) height -= distance if height > 0: var current_height = heightmap[continent.position.x + x][continent.position.y + y] height = pow(height * 1.2, mountain_frequency) height = remap(height, 0, 1, 1, world_elevation) heightmap[continent.position.x + x][continent.position.y + y] = max(current_height, height) if height > heightmap[continent.position.x + x][continent.position.y + y]: heightmap[continent.position.x + x][continent.position.y + y] = height elif height > 0 - coasts_thickness: coastmap[x + continent.position.x][y + continent.position.y] = 1 #func sampleNormal(position: Vector2i): # var left = heightmap[position.x - 1][position.y] # var right = heightmap[position.x + 1][position.y] # var top = heightmap[position.x][position.y - 1] # var bottom = heightmap[position.x][position.y + 1] # ## Vec3 normal = Vec3(2*(R-L), 2*(B-T), -4).Normalize(); ## return Vector3i(2 * heightmap) # pass func erode(position: Vector2i): const dropsPerCell = .4 const erosionRate = 1 const depositionRate = 0.7 const speed = .15 const friction = .7 const radius = .8 const maxIterations = 80 const iterationScale = .04 var ox = (randf() * 2 - 1) * radius # The X offset var oy = (randf() * 2 - 1) * radius # The Y offset var sediment = 0 # The amount of carried sediment var xp = position.x # The previous X position var yp = position.y # The previous Y position var vx = 0 # The horizontal velocity var vy = 0 # The vertical velocity for i in maxIterations: # Get the surface normal of the terrain at the current location # var surfaceNormal = heightMap.sampleNormal(x + ox, y + oy) var surfaceNormal = normal(Vector2i(position.x + ox, position.y + oy)) # If the terrain is flat, stop simulating, the snowball cannot roll any further if (surfaceNormal.y == 1): break # Calculate the deposition and erosion rate var deposit = sediment * depositionRate * surfaceNormal.y var erosion = erosionRate * (1 - surfaceNormal.y) * min(1, i * iterationScale) # print(deposit) # print(erosion) # Change the sediment on the place this snowball came from # print(heightmap[xp][yp]) # heightmap[xp][yp] += deposit - erosion heightmap[xp][yp] = 0 # print(heightmap[xp][yp]) sediment += erosion - deposit; vx = friction * vx + surfaceNormal.x * speed vy = friction * vy + surfaceNormal.z * speed xp = position.x yp = position.y position.x += vx position.y += vy pass func set_oceans(): var stack = [] var first_coord = Vector2i(0, 0) stack.append(first_coord) while stack.size(): var coord = stack.pop_back() oceanmap[coord.x][coord.y] = 1 for neighbour in neighbours(coord).values(): if heightmap[neighbour.x][neighbour.y] == 0 and oceanmap[neighbour.x][neighbour.y] == 0: stack.append(neighbour) for x in world_width: for y in world_height: if heightmap[x][y] == 0 and not oceanmap[x][y]: heightmap[x][y] = 2 func set_coasts(): for x in world_width: for y in world_height: if coastmap[x][y] and heightmap[x][y] == 0: heightmap[x][y] = 1 pass func neighbours(coord: Vector2i): var neighbours: Dictionary if coord.x + 1 < world_width: neighbours.right = Vector2i(coord.x + 1, coord.y) if coord.y + 1 < world_height : neighbours.bottom = Vector2i(coord.x, coord.y + 1) if coord.x - 1 >= 0: neighbours.left = Vector2i(coord.x - 1, coord.y) if coord.y - 1 >= 0: neighbours.top = Vector2i(coord.x, coord.y - 1) return neighbours func normal(coord: Vector2i): var neighbours = neighbours(coord) if neighbours.size() < 4: return Vector3(0, 1, 0) # var normal = Vector3(2 * (neighbours.right - neighbours.left), 2 * (neighbours.bottom - neighbours.top), -4) var normal = Vector3( heightmap[neighbours.left.x][neighbours.left.y] - heightmap[neighbours.right.x][neighbours.right.y], 2, heightmap[neighbours.top.x][neighbours.top.y] - heightmap[neighbours.bottom.x][neighbours.bottom.y]).normalized() return normal func set_slope(): for x in world_width: for y in world_height: var direction = null var min_height = heightmap[x][y] if x + 1 < world_width: if heightmap[x + 1][y] < min_height: min_height = heightmap[x + 1][y] direction = "right" if y + 1 < world_height: if heightmap[x][y + 1] < min_height: min_height = heightmap[x][y + 1] direction = "bottom" if x - 1 < world_width: if heightmap[x - 1][y] < min_height: min_height = heightmap[x - 1][y] direction = "left" if y - 1 < world_height: if heightmap[x][y - 1] < min_height: min_height = heightmap[x][y - 1] direction = "top" slopemap[x][y] = direction func set_rivers(): for i in 20: var good_river = false while not good_river: var river_path = [] var reached = false var coord = mountain_blocs[randi_range(0, mountain_blocs.size()-1)] var old_direction while not reached: var direction = slopemap[coord.x][coord.y] if heightmap[coord.x][coord.y] == 1 or direction == opposite_direction(old_direction): direction = old_direction old_direction = direction river_path.append({"coord": coord, "thickness": 5, "direction": direction}) if direction == "right": coord = Vector2(coord.x + 1, coord.y) elif direction == "bottom": coord = Vector2(coord.x, coord.y + 1) elif direction == "left": coord = Vector2(coord.x - 1, coord.y) elif direction == "top": coord = Vector2(coord.x, coord.y - 1) else: reached = true if ( heightmap[river_path.back().coord.x][river_path.back().coord.y] == 0 and heightmap[river_path.back().coord.x] != heightmap[river_path.front().coord.x] and heightmap[river_path.back().coord.y] != heightmap[river_path.front().coord.y] ): good_river = true rivers.append(river_path) for river in rivers: var coord: Vector2 for bloc in river: for i in bloc.thickness: if perpendicular_direction(bloc.direction) == "right": coord = Vector2(bloc.coord.x + i, bloc.coord.y) elif perpendicular_direction(bloc.direction) == "bottom": coord = Vector2(bloc.coord.x, bloc.coord.y + i) elif perpendicular_direction(bloc.direction) == "left": coord = Vector2(bloc.coord.x - i, bloc.coord.y) elif perpendicular_direction(bloc.direction) == "top": coord = Vector2(bloc.coord.x, bloc.coord.y - i) if blocs[coord.x][coord.y].type != Global.blocs.WATER: # heightmap[coord.x][coord.y] -= 1 blocs[coord.x][coord.y].type = Global.blocs.WATER func opposite_direction(direction): if direction == "top": return("bottom") if direction == "bottom": return "top" if direction == "left": return "right" if direction == "right": return "left" return null func perpendicular_direction(direction): if direction == "top": return("left") if direction == "bottom": return "right" if direction == "left": return "top" if direction == "right": return "bottom" return null func fix_heightmap(): for x in world_width: for y in world_height: heightmap[x][y] = ceil(heightmap[x][y]) # if heightmap[x][y] > 4: # mountain_blocs.append(Vector2i(x, y)) func init_blocs(): for x in world_width: blocs.append([]) for y in world_height: var bloc = { "type": 0, "entity": -1 } blocs[x].append(bloc) func set_blocs(): for x in world_width: for y in world_height: if not blocs[x][y].type: if heightmap[x][y] > 0: blocs[x][y].type = Global.blocs.SAND if heightmap[x][y] > 1: blocs[x][y].type = Global.blocs.GRASS if heightmap[x][y] > 10: blocs[x][y].type = Global.blocs.STONE if heightmap[x][y] > 15: blocs[x][y].type = Global.blocs.SNOW return blocs