implement tilemap chunk generator

This commit is contained in:
Antti Hakkarainen 2023-02-12 13:54:14 +02:00
parent 83343c9be6
commit 7dc6a4c420
12 changed files with 244 additions and 1592 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,14 @@
[remap]
importer="image"
type="Image"
uid="uid://dprlyk4ednkp8"
path="res://.godot/imported/tampere_10x10km_1024px.png-569f3285557c876bd7277d49b8414424.image"
[deps]
source_file="res://maps/tampere_10x10km_1024px.png"
dest_files=["res://.godot/imported/tampere_10x10km_1024px.png-569f3285557c876bd7277d49b8414424.image"]
[params]

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -0,0 +1,14 @@
[remap]
importer="image"
type="Image"
uid="uid://bl4xbe4vb3nft"
path="res://.godot/imported/tampere_10x10km_4096px.png-a656fcf9b79fbe6e529e735982b88a37.image"
[deps]
source_file="res://maps/tampere_10x10km_4096px.png"
dest_files=["res://.godot/imported/tampere_10x10km_4096px.png-a656fcf9b79fbe6e529e735982b88a37.image"]
[params]

View file

@ -19,6 +19,10 @@ config/icon="res://icon.svg"
Globals="*res://scripts/Globals.gd" Globals="*res://scripts/Globals.gd"
[debug]
gdscript/warnings/integer_division=0
[display] [display]
window/size/viewport_height=1080 window/size/viewport_height=1080

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,11 @@ func _ready():
# Called every frame. 'delta' is the elapsed time since the previous frame. # Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta): func _process(_delta):
debug_info.set_text(str(get_viewport().get_mouse_position()) +"\n" + "FPS " + str(Engine.get_frames_per_second())) debug_info.set_text(
str(get_viewport().get_mouse_position()) +"\n" +
"FPS " + str(Engine.get_frames_per_second()) + "\n" +
"Zoom lvl: " + str(Globals.CAMERA_ZOOM_LEVEL)
)
# defines construction toolbar buttons # defines construction toolbar buttons
func create_buttons(): func create_buttons():

View file

@ -1,22 +1,11 @@
class_name World class_name EntityPlacer
extends TileMap extends Control
var has_placeable_building: bool = false var has_placeable_building: bool = false
var building var building
var building_type: String var building_type: String
var scene var scene
func _init():
Globals.world_map = self
# Called when the node enters the scene tree for the first time.
func _ready():
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
pass
func get_building_properties() -> Array: func get_building_properties() -> Array:
var tileset_id = 0 # default value var tileset_id = 0 # default value
var tilemap_tile_coords: Vector2i var tilemap_tile_coords: Vector2i
@ -62,40 +51,40 @@ func _on_control_button_pressed(type):
scene = load(Globals.SCENE_PATH + "Building.tscn") scene = load(Globals.SCENE_PATH + "Building.tscn")
building = scene.instantiate() building = scene.instantiate()
#building.set_cell(0, Vector2i(0,0), building_properties[0], building_properties[1], 0) #building.set_cell(0, Vector2i(0,0), building_properties[0], building_properties[1], 0)
add_child(building) #add_child(building)
has_placeable_building = true has_placeable_building = true
func _input(event): #func _input(event):
# place the building # # place the building
if event.is_action_pressed("place_building") and has_placeable_building: # if event.is_action_pressed("place_building") and has_placeable_building:
has_placeable_building = false # has_placeable_building = false
place_building_to_map() # place_building_to_map()
#
# cancel placement # # cancel placement
if event.is_action_pressed("cancel"): # if event.is_action_pressed("cancel"):
if has_placeable_building: # if has_placeable_building:
pass # pass
func calculate_grid_coordinates(map_position: Vector2) -> Vector2: func calculate_grid_coordinates(map_position: Vector2) -> Vector2:
return (map_position).floor() return (map_position).floor()
func place_building_to_map(): #func place_building_to_map():
var building_properties = get_building_properties() # var building_properties = get_building_properties()
var tile_on_mouse = local_to_map(get_global_mouse_position()) # #var tile_on_mouse = local_to_map(get_global_mouse_position())
#
if !Globals.are_coords_valid( # if !Globals.are_coords_valid(
tile_on_mouse.y, # tile_on_mouse.y,
Vector2i(0, Globals.map_image_size.y), # Vector2i(0, Globals.map_image_size.y),
Globals.ERROR_TILE_Y_COORDS_OUT_OF_BOUNDS # Globals.ERROR_TILE_Y_COORDS_OUT_OF_BOUNDS
): # ):
return false # return false
elif !Globals.are_coords_valid( # elif !Globals.are_coords_valid(
tile_on_mouse.x, # tile_on_mouse.x,
Vector2i(0, Globals.map_image_size.x), # Vector2i(0, Globals.map_image_size.x),
Globals.ERROR_TILE_X_COORDS_OUT_OF_BOUNDS # Globals.ERROR_TILE_X_COORDS_OUT_OF_BOUNDS
): # ):
return false # return false
set_cell(Globals.LAYER_BUILDINGS, tile_on_mouse, building_properties[0], building_properties[1], 0) # set_cell(Globals.LAYER_BUILDINGS, tile_on_mouse, building_properties[0], building_properties[1], 0)

View file

@ -20,8 +20,17 @@ func are_coords_valid(value:int, bounds:Vector2i, errmsg:String) -> bool:
func choose_randomly(list_of_entries): func choose_randomly(list_of_entries):
return list_of_entries[randi() % list_of_entries.size()] return list_of_entries[randi() % list_of_entries.size()]
var world_map: TileMap # map size is based on input image x*y pixel size
var map_image_size:Vector2i var map_size:Vector2i
# store terrain type (water, land, forest etc. for every map cell)
var map_terrain_data:Array[Array] = [[]]
# preprocess and store exact tile for every map cell to speed up setting tiles
var map_tile_data:Array[Array] = [[]]
# current camera zoom level
var CAMERA_ZOOM_LEVEL: float = 1.0
# FILE PATHS # FILE PATHS
const SCENE_PATH:String = "res://scenes/" const SCENE_PATH:String = "res://scenes/"
@ -52,8 +61,6 @@ const TYPE_ROADS:String = "roads"
const TYPE_DEMOLISH:String = "demolish" const TYPE_DEMOLISH:String = "demolish"
# camera movement settings # camera movement settings
var CAMERA_ZOOM_LEVEL: float = 1.0
const CAMERA_MIN_ZOOM_LEVEL: float = 0.1 const CAMERA_MIN_ZOOM_LEVEL: float = 0.1
const CAMERA_MAX_ZOOM_LEVEL: float = 2.0 const CAMERA_MAX_ZOOM_LEVEL: float = 2.0
const CAMERA_ZOOM_FACTOR: float = 0.1 const CAMERA_ZOOM_FACTOR: float = 0.1
@ -65,10 +72,10 @@ const GROUND_TILE_COLOR_IN_MAP_FILE: Color = Color(0,0,0,1)
const WATER_TILE_COLOR_IN_MAP_FILE: Color = Color(1,1,1,1) const WATER_TILE_COLOR_IN_MAP_FILE: Color = Color(1,1,1,1)
# min and max sizes for a map so the map won't be unreasonably small or large # min and max sizes for a map so the map won't be unreasonably small or large
const MAP_MIN_HEIGHT:int = 100 const MAP_MIN_HEIGHT:int = 128
const MAP_MAX_HEIGHT:int = 1000 const MAP_MAX_HEIGHT:int = 4096
const MAP_MIN_WIDTH:int = 100 const MAP_MIN_WIDTH:int = 128
const MAP_MAX_WIDTH:int = 1000 const MAP_MAX_WIDTH:int = 4096
# tile size # tile size
const TILE_SIZE_X:int = 16 const TILE_SIZE_X:int = 16

View file

@ -12,10 +12,15 @@ extends Node
signal set_camera_position(pos:Vector2) signal set_camera_position(pos:Vector2)
# The idea is for the user to be able to choose the map from GUI later # The idea is for the user to be able to choose the map from GUI later
#var map_filename: String = "res://maps/tampere_10x10km_1000px.png" var map_filenames:Array = [
var map_filename:String = "res://maps/tampere_200px_crop.png" "res://maps/tampere_10x10km_1000px.png",
var _world := World.new() "res://maps/tampere_10x10km_1024px.png",
var _world_generator := WorldGeneration.new() "res://maps/varkaus_256x256px_test.png"
]
var map_filename:String = map_filenames[2]
var _world_generator:WorldGenerator
var _tilemap_generator:TileMapGenerator
func _init(): func _init():
# DisplayServer.window_set_size( # DisplayServer.window_set_size(
@ -26,20 +31,27 @@ func _init():
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
if !_world: # create a new world and worldgenerator
push_error(Globals.ERROR_MAKING_WORLD_INSTANCE) _world_generator = WorldGenerator.new()
quit_game() _tilemap_generator = TileMapGenerator.new()
# generate terrain. quit game if generation fails. # generate terrain. quit game if generation fails.
if !_world_generator.generate_world(map_filename): if !_world_generator.generate_world(map_filename):
push_error(Globals.ERROR_WHILE_GENERATING_MAP) push_error(Globals.ERROR_WHILE_GENERATING_MAP)
quit_game() quit_game()
if !_tilemap_generator:
push_error(Globals.ERROR_MAKING_WORLD_INSTANCE)
quit_game()
_tilemap_generator.start()
_tilemap_generator.test_func()
# center camera to world map # center camera to world map
emit_signal( emit_signal(
"set_camera_position", "set_camera_position",
Vector2(Globals.map_image_size.x / 2.0 * Globals.TILE_SIZE_X, Vector2(Globals.map_size.x / 2.0 * Globals.TILE_SIZE_X,
Globals.map_image_size.y / 2.0 * Globals.TILE_SIZE_Y) Globals.map_size.y / 2.0 * Globals.TILE_SIZE_Y)
) )
# Called every frame. 'delta' is the elapsed time since the previous frame. # Called every frame. 'delta' is the elapsed time since the previous frame.

View file

@ -0,0 +1,57 @@
class_name TileMapGenerator
extends CanvasLayer
# one tilemap is one chunk
# map consists of many chunks
# chunks are loaded to view when needed
# chunks are deleted after they are no longer needed (in view)
# This is done to speed up game loading and avoiding setting one large tilemap in one go
# which is extremely slow in godot 4.0, 4096x4096 takes minutes to fill with set_cell() commands
const CHUNK_SIZE = Vector2i(128,128)
var map_tiles:Array[Array] = [[]]
func start() -> void:
# Initialize the map tile array with enough chunks to cover the whole map
var ms:int = Globals.map_size.y/CHUNK_SIZE.y
map_tiles.resize(2)
print("map tiles size y: ", map_tiles.size())
for y in ms:
map_tiles[y].resize(2)
print("map tiles size x: ", map_tiles[y].size())
for x in ms:
map_tiles[y][x] = TileMap.new()
func test_func():
self.set_chunk_tiles(Vector2i(1,0))
#self.map_tiles[0][1].visible()
print(map_tiles[0][1])
func clear_chunk_tiles(chunk_pos:Vector2i) -> void:
map_tiles[chunk_pos.y][chunk_pos.x].clear()
func set_chunk_tiles(chunk_pos:Vector2i) -> void:
# Set an invidiual chunk's tiles based on map terrain data
# Try to load the world tilemap where we place the tiles
if (map_tiles[chunk_pos.y][chunk_pos.x] == null):
var errmsg = Globals.ERROR_TILEMAP_NODE_MISSING
push_error(errmsg % str(chunk_pos))
#return false
for y in CHUNK_SIZE.y:
for x in CHUNK_SIZE.x:
var tile_data: Array = Globals.map_tile_data[chunk_pos.y*CHUNK_SIZE.y][chunk_pos.x*CHUNK_SIZE.x]
# layer | tile coords at tilemap | tilemap id | coords of the tile at tileset | alternative tile
map_tiles[chunk_pos.y][chunk_pos.x].set_cell(
Globals.LAYER_TERRAIN,
Vector2i(x, y),
2,
tile_data[0],
tile_data[1]
)

View file

@ -1,8 +1,8 @@
class_name WorldGeneration class_name WorldGenerator
extends RefCounted extends RefCounted
var image:Image = Image.new() var image:Image = Image.new()
var map_tile_data:Array[Array] = [[]] # store map tile info to a 2d array
var directions:Array = [ var directions:Array = [
Vector2i(0,1), # south Vector2i(0,1), # south
Vector2i(1,0), # east Vector2i(1,0), # east
@ -10,15 +10,15 @@ var directions:Array = [
Vector2i(-1,0) # west Vector2i(-1,0) # west
] ]
func choose_tile(tile:Vector2i, selected, surrounding) -> void: func choose_tile(tile:Vector2i, selected, surrounding) -> Array:
var surrounding_tiles:Array = [] var surrounding_tiles:Array = []
# determine which directions have land around the tile # determine which directions have land around the tile
for dir in directions: for dir in directions:
# avoid index out of bounds # avoid index out of bounds
if (tile.y+dir.y >= Globals.map_image_size.y) or (tile.x+dir.x >= Globals.map_image_size.x): if (tile.y+dir.y >= Globals.map_size.y) or (tile.x+dir.x >= Globals.map_size.x):
surrounding_tiles.append(surrounding) surrounding_tiles.append(surrounding)
elif map_tile_data[tile.y+dir.y][tile.x+dir.x] == surrounding: elif Globals.map_terrain_data[tile.y+dir.y][tile.x+dir.x] == surrounding:
surrounding_tiles.append(surrounding) surrounding_tiles.append(surrounding)
else: else:
surrounding_tiles.append(selected) surrounding_tiles.append(selected)
@ -33,14 +33,10 @@ func choose_tile(tile:Vector2i, selected, surrounding) -> void:
else: else:
tile_coords = selected_tile[0] tile_coords = selected_tile[0]
# layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile return [
Globals.world_map.set_cell(
Globals.LAYER_TERRAIN,
Vector2i(tile.x, tile.y),
2,
tile_coords, tile_coords,
0 if selected_tile else Globals.choose_randomly([0,1,2,3]) 0 if selected_tile else Globals.choose_randomly([0,1,2,3])
) ]
# Generates biomes, like forest and bog # Generates biomes, like forest and bog
func generate_biomes() -> void: func generate_biomes() -> void:
@ -56,25 +52,25 @@ func generate_biomes() -> void:
var water_next_to_tile:bool = false var water_next_to_tile:bool = false
for y in map_tile_data.size(): for y in Globals.map_terrain_data.size():
for x in map_tile_data[y].size(): for x in Globals.map_terrain_data[y].size():
# replace non-water with biomes # replace non-water with biomes
if map_tile_data[y][x] > 0: if Globals.map_terrain_data[y][x] > 0:
water_next_to_tile = false water_next_to_tile = false
# don't put forest next to water # don't put forest next to water
for dir in directions: for dir in directions:
if (y+dir.y >= Globals.map_image_size.y) or (x+dir.x >= Globals.map_image_size.x): if (y+dir.y >= Globals.map_size.y) or (x+dir.x >= Globals.map_size.x):
continue continue
if map_tile_data[y+dir.y][x+dir.x] == Globals.TILE_WATER: if Globals.map_terrain_data[y+dir.y][x+dir.x] == Globals.TILE_WATER:
water_next_to_tile = true water_next_to_tile = true
# if there's no water next to a land tile, it can be replaced with forest # if there's no water next to a land tile, it can be replaced with forest
if !water_next_to_tile: if !water_next_to_tile:
var noise_sample = fnl.get_noise_2d(x,y) var noise_sample = fnl.get_noise_2d(x,y)
if noise_sample < 0.1: if noise_sample < 0.1:
map_tile_data[y][x] = Globals.TILE_FOREST Globals.map_terrain_data[y][x] = Globals.TILE_FOREST
# can add other tresholds here for other biomes # can add other tresholds here for other biomes
func generate_world(filename) -> bool: func generate_world(filename) -> bool:
@ -86,58 +82,67 @@ func generate_world(filename) -> bool:
return false return false
# Check if image is too small or too large # Check if image is too small or too large
Globals.map_image_size = image.get_size() Globals.map_size = image.get_size()
if !validate_mapgen_params(): if !validate_mapgen_params():
return false return false
var start = Time.get_ticks_usec()
read_image_pixel_data() read_image_pixel_data()
var end = Time.get_ticks_usec()
print("read image data ", (end-start)/1000.0, "ms")
start = Time.get_ticks_usec()
smooth_land_features(Globals.TILE_WATER) # smooth water smooth_land_features(Globals.TILE_WATER) # smooth water
end = Time.get_ticks_usec()
print("smooth water ", (end-start)/1000.0, "ms")
start = Time.get_ticks_usec()
generate_biomes() generate_biomes()
end = Time.get_ticks_usec()
print("generate biomes ", (end-start)/1000.0, "ms")
start = Time.get_ticks_usec()
smooth_land_features(Globals.TILE_FOREST) # smooth out forest smooth_land_features(Globals.TILE_FOREST) # smooth out forest
set_tilemap_tiles() end = Time.get_ticks_usec()
print("smooth forest ", (end-start)/1000.0, "ms")
start = Time.get_ticks_usec()
set_tilemap_tiles()
end = Time.get_ticks_usec()
print("set tiles ", (end-start)/1000.0, "ms")
return true return true
func read_image_pixel_data(): func read_image_pixel_data():
# initialize the array to have enough rows # initialize the array to have enough rows
map_tile_data.resize(Globals.map_image_size.y) Globals.map_terrain_data.resize(Globals.map_size.y)
Globals.map_tile_data.resize(Globals.map_size.y)
for y in Globals.map_image_size.y: for y in Globals.map_size.y:
#initialize the row to have enough columns #initialize the row to have enough columns
map_tile_data[y].resize(Globals.map_image_size.y) Globals.map_terrain_data[y].resize(Globals.map_size.y)
Globals.map_tile_data[y].resize(Globals.map_size.y)
for x in Globals.map_image_size.x: for x in Globals.map_size.x:
if image.get_pixel(x, y) == Globals.WATER_TILE_COLOR_IN_MAP_FILE: if image.get_pixel(x, y) == Globals.WATER_TILE_COLOR_IN_MAP_FILE:
map_tile_data[y][x] = Globals.TILE_WATER Globals.map_terrain_data[y][x] = Globals.TILE_WATER
else: else:
map_tile_data[y][x] = Globals.TILE_TERRAIN Globals.map_terrain_data[y][x] = Globals.TILE_TERRAIN
func set_tilemap_tiles() -> void: func set_tilemap_tiles() -> void:
for y in map_tile_data.size(): for y in Globals.map_terrain_data.size():
for x in map_tile_data[y].size(): for x in Globals.map_terrain_data[y].size():
# layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile # layer | position coords | tilemap id | coords of the tile at tilemap | alternative tile
match map_tile_data[y][x]: match Globals.map_terrain_data[y][x]:
Globals.TILE_WATER: # water or shoreline Globals.TILE_WATER: # water or shoreline
choose_tile(Vector2i(x, y), Globals.TILE_WATER, Globals.TILE_TERRAIN) Globals.map_tile_data[y][x] = choose_tile(Vector2i(x, y), Globals.TILE_WATER, Globals.TILE_TERRAIN)
Globals.TILE_TERRAIN: #terrain or forest edge Globals.TILE_TERRAIN: #terrain or forest edge
# Globals.world_map.set_cell( Globals.map_tile_data[y][x] = choose_tile(Vector2i(x,y), Globals.TILE_TERRAIN, Globals.TILE_FOREST)
# Globals.LAYER_TERRAIN,
# Vector2i(x, y),
# 2,
# Vector2i(0,0),
# Globals.choose_randomly([0,1,2,3])
# )
choose_tile(Vector2i(x,y), Globals.TILE_TERRAIN, Globals.TILE_FOREST)
Globals.TILE_FOREST: Globals.TILE_FOREST:
Globals.world_map.set_cell( Globals.map_tile_data[y][x] = [Vector2i(5,1), Globals.choose_randomly([0,1,2,3])]
Globals.LAYER_TERRAIN,
Vector2i(x, y),
2,
Vector2i(5,1),
Globals.choose_randomly([0,1,2,3])
)
_: #default _: #default
pass pass
@ -147,9 +152,9 @@ func set_tilemap_tiles() -> void:
# Do it recursively with limit of n recursions! # Do it recursively with limit of n recursions!
func smooth_land_features(tile_type:int) -> void: func smooth_land_features(tile_type:int) -> void:
# TODO for testing avoid map borders to make it simpler to implement # TODO for testing avoid map borders to make it simpler to implement
for y in range(1, Globals.map_image_size.y-1): for y in range(1, Globals.map_size.y-1):
for x in range(1, Globals.map_image_size.x-1): for x in range(1, Globals.map_size.x-1):
if map_tile_data[y][x] != tile_type: if Globals.map_terrain_data[y][x] != tile_type:
continue continue
match tile_type: match tile_type:
@ -173,26 +178,26 @@ func smooth_forest_recursively(pos:Vector2i, selected:int, comp:int) -> void:
# determine which directions have land around the tile # determine which directions have land around the tile
for dir in directions: for dir in directions:
if map_tile_data[pos.y+dir.y][pos.x+dir.x] == comp: if Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == comp:
surrounding_tiles.append(comp) surrounding_tiles.append(comp)
elif map_tile_data[pos.y+dir.y][pos.x+dir.x] == selected: elif Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == selected:
surrounding_tiles.append(selected) surrounding_tiles.append(selected)
match surrounding_tiles: match surrounding_tiles:
[1,1,1,2]: #west [1,1,1,2]: #west
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.x -= 1 pos.x -= 1
[1,1,2,1]: #north [1,1,2,1]: #north
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.y -= 1 pos.y -= 1
[1,2,1,1]: #east [1,2,1,1]: #east
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.x += 1 pos.x += 1
[2,1,1,1]: #south [2,1,1,1]: #south
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.y += 1 pos.y += 1
[1,1,1,1]: # remove solo forests [1,1,1,1]: # remove solo forests
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
return return
_: _:
return return
@ -205,46 +210,40 @@ func smooth_recursively(pos:Vector2i, selected:int, comp:int) -> void:
# determine which directions have land around the tile # determine which directions have land around the tile
for dir in directions: for dir in directions:
if map_tile_data[pos.y+dir.y][pos.x+dir.x] == comp: if Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == comp:
surrounding_tiles.append(comp) surrounding_tiles.append(comp)
elif map_tile_data[pos.y+dir.y][pos.x+dir.x] == selected: elif Globals.map_terrain_data[pos.y+dir.y][pos.x+dir.x] == selected:
surrounding_tiles.append(selected) surrounding_tiles.append(selected)
match surrounding_tiles: match surrounding_tiles:
[1,1,1,0]: #west [1,1,1,0]: #west
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.x -= 1 pos.x -= 1
[1,1,0,1]: #north [1,1,0,1]: #north
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.y -= 1 pos.y -= 1
[1,0,1,1]: #east [1,0,1,1]: #east
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.x += 1 pos.x += 1
[0,1,1,1]: #south [0,1,1,1]: #south
map_tile_data[pos.y][pos.x] = comp Globals.map_terrain_data[pos.y][pos.x] = comp
pos.y += 1 pos.y += 1
_: _:
return return
smooth_recursively(pos, selected, comp) smooth_recursively(pos, selected, comp)
func validate_mapgen_params() -> bool: func validate_mapgen_params() -> bool:
if !Globals.are_coords_valid( if !Globals.are_coords_valid(
Globals.map_image_size.y, Globals.map_size.y,
Vector2i(Globals.MAP_MIN_HEIGHT, Globals.MAP_MAX_HEIGHT), Vector2i(Globals.MAP_MIN_HEIGHT, Globals.MAP_MAX_HEIGHT),
Globals.ERROR_IMAGE_HEIGHT_INCORRECT): Globals.ERROR_IMAGE_HEIGHT_INCORRECT):
return false return false
elif !Globals.are_coords_valid( elif !Globals.are_coords_valid(
Globals.map_image_size.x, Globals.map_size.x,
Vector2i(Globals.MAP_MIN_WIDTH, Globals.MAP_MAX_WIDTH), Vector2i(Globals.MAP_MIN_WIDTH, Globals.MAP_MAX_WIDTH),
Globals.ERROR_IMAGE_WIDTH_INCORRECT): Globals.ERROR_IMAGE_WIDTH_INCORRECT):
return false return false
# Try to load the world tilemap where we place the tiles
if (Globals.world_map == null):
var errmsg = Globals.ERROR_WORLD_TILEMAP_NODE_MISSING % Globals.WORLD_NODE
push_error(errmsg)
return false
return true return true