225 lines
5.6 KiB
GDScript
225 lines
5.6 KiB
GDScript
extends Node2D
|
|
|
|
@export var map_width: int = 80
|
|
@export var map_height: int = 80
|
|
@export var tile_size: int = 64
|
|
|
|
var rng = RandomNumberGenerator.new()
|
|
var zone_map = []
|
|
var region_map = []
|
|
var tile_map: TileMap
|
|
|
|
const ZONE_ASPHALT = 0
|
|
const ZONE_CONCRETE = 1
|
|
const ZONE_GROUND = 2
|
|
const ZONE_GRASS = 3
|
|
const ZONE_STEEL = 4
|
|
const ZONE_WATER = 5
|
|
|
|
var zone_textures = {}
|
|
|
|
func _ready() -> void:
|
|
rng.randomize()
|
|
tile_map = get_node("/root/Main/TileMap")
|
|
|
|
if not tile_map:
|
|
push_error("TileMap not found!")
|
|
return
|
|
|
|
load_textures()
|
|
generate_map()
|
|
|
|
func load_textures() -> void:
|
|
var base = "res://assets/seamless/"
|
|
var ts = TileSet.new()
|
|
ts.tile_size = Vector2i(tile_size, tile_size)
|
|
|
|
zone_textures = {
|
|
ZONE_ASPHALT: ["asfalt1.jpg", "asfalt2.jpg", "asfalt3.jpg", "asfalt4.jpg", "asfalt5.jpg"],
|
|
ZONE_CONCRETE: ["beton1.jpg", "beton2.jpg", "beton3.jpg", "beton4.jpg", "beton5.jpg"],
|
|
ZONE_GROUND: ["ground1.jpg", "ground2.jpg", "ground3.jpg", "ground4.jpg", "ground5.jpg",
|
|
"ground6.jpg", "ground7.jpg", "ground8.jpg", "ground9.jpg", "ground10.jpg"],
|
|
ZONE_GRASS: ["grass1.jpg", "grass2.jpg", "grass3.jpg", "grass4.jpg", "grass5.jpg"],
|
|
ZONE_STEEL: ["steel1.jpg", "steel2.jpg", "steel3.jpg", "steel4.jpg", "steel5.jpg"],
|
|
}
|
|
|
|
var source_id = 0
|
|
for zone in zone_textures:
|
|
var textures = zone_textures[zone]
|
|
var zone_sources = []
|
|
|
|
for tex_name in textures:
|
|
var full_path = base + tex_name
|
|
var tex = load(full_path)
|
|
if tex:
|
|
var source = TileSetAtlasSource.new()
|
|
source.texture = tex
|
|
source.texture_region_size = Vector2i(tile_size, tile_size)
|
|
source.create_tile(Vector2i(0, 0))
|
|
ts.add_source(source, source_id)
|
|
zone_sources.append(source_id)
|
|
source_id += 1
|
|
|
|
zone_textures[zone] = zone_sources
|
|
|
|
tile_map.tile_set = ts
|
|
|
|
func generate_map() -> void:
|
|
zone_map.clear()
|
|
region_map.clear()
|
|
|
|
for x in range(map_width):
|
|
zone_map.append([])
|
|
region_map.append([])
|
|
for y in range(map_height):
|
|
zone_map[x].append(-1)
|
|
region_map[x].append(-1)
|
|
|
|
generate_zones()
|
|
add_water_features()
|
|
smooth_zones()
|
|
identify_regions()
|
|
draw_map()
|
|
|
|
func generate_zones() -> void:
|
|
for x in range(map_width):
|
|
for y in range(map_height):
|
|
var h = get_noise(x, y)
|
|
var zone = get_zone(h)
|
|
zone_map[x][y] = zone
|
|
|
|
func get_noise(x: int, y: int) -> float:
|
|
var s = 0.05
|
|
var v = sin(x * s * 2) * cos(y * s * 1.5)
|
|
v += sin(x * s * 4 + y * s * 3) * 0.3
|
|
v += sin(x * s * 8 + y * s * 7) * 0.15
|
|
return clamp(v * 0.5 + 0.5, 0.0, 1.0)
|
|
|
|
func get_zone(val: float) -> int:
|
|
if val < 0.15:
|
|
return ZONE_ASPHALT
|
|
elif val < 0.3:
|
|
return ZONE_CONCRETE
|
|
elif val < 0.45:
|
|
return ZONE_STEEL
|
|
elif val < 0.65:
|
|
return ZONE_GROUND
|
|
else:
|
|
return ZONE_GRASS
|
|
|
|
func add_water_features() -> void:
|
|
for _i in range(8):
|
|
var rx = rng.randi() % map_width
|
|
var ry = rng.randi() % map_height
|
|
create_water_body(rx, ry, rng.randi() % 3 + 1)
|
|
|
|
func create_water_body(start_x: int, start_y: int, size: int) -> void:
|
|
var queue = [{"x": start_x, "y": start_y}]
|
|
var visited = {}
|
|
visited[str(start_x) + "," + str(start_y)] = true
|
|
|
|
var count = 0
|
|
while not queue.is_empty() and count < size * 15:
|
|
var current = queue.pop_front()
|
|
var x = current.x
|
|
var y = current.y
|
|
|
|
if x >= 0 and x < map_width and y >= 0 and y < map_height:
|
|
if zone_map[x][y] != ZONE_WATER:
|
|
zone_map[x][y] = ZONE_WATER
|
|
count += 1
|
|
|
|
var dirs = [[0,1], [0,-1], [1,0], [-1,0]]
|
|
for d in dirs:
|
|
var nx = x + d[0]
|
|
var ny = y + d[1]
|
|
var key = str(nx) + "," + str(ny)
|
|
if not visited.has(key):
|
|
visited[key] = true
|
|
if rng.randf() > 0.3:
|
|
queue.append({"x": nx, "y": ny})
|
|
|
|
func smooth_zones() -> void:
|
|
for iter in range(2):
|
|
var new_zone = []
|
|
for x in range(map_width):
|
|
new_zone.append([])
|
|
for y in range(map_height):
|
|
new_zone[x].append(zone_map[x][y])
|
|
|
|
for x in range(1, map_width - 1):
|
|
for y in range(1, map_height - 1):
|
|
if zone_map[x][y] == ZONE_WATER:
|
|
continue
|
|
|
|
var counts = {}
|
|
for dx in [-1, 0, 1]:
|
|
for dy in [-1, 0, 1]:
|
|
var n = zone_map[x + dx][y + dy]
|
|
if n != ZONE_WATER:
|
|
counts[n] = counts.get(n, 0) + 1
|
|
|
|
var max_count = 0
|
|
var dominant = zone_map[x][y]
|
|
for z in counts:
|
|
if counts[z] > max_count:
|
|
max_count = counts[z]
|
|
dominant = z
|
|
|
|
if max_count >= 6:
|
|
new_zone[x][y] = dominant
|
|
|
|
zone_map = new_zone
|
|
|
|
func identify_regions() -> void:
|
|
var visited = {}
|
|
var region_id = 0
|
|
|
|
for x in range(map_width):
|
|
for y in range(map_height):
|
|
var key = str(x) + "," + str(y)
|
|
if visited.has(key):
|
|
continue
|
|
|
|
var zone = zone_map[x][y]
|
|
var queue = [{"x": x, "y": y}]
|
|
visited[key] = true
|
|
|
|
while not queue.is_empty():
|
|
var current = queue.pop_front()
|
|
var cx = current.x
|
|
var cy = current.y
|
|
|
|
region_map[cx][cy] = region_id
|
|
|
|
var dirs = [[0,1], [0,-1], [1,0], [-1,0]]
|
|
for d in dirs:
|
|
var nx = cx + d[0]
|
|
var ny = cy + d[1]
|
|
var nkey = str(nx) + "," + str(ny)
|
|
|
|
if nx >= 0 and nx < map_width and ny >= 0 and ny < map_height:
|
|
if not visited.has(nkey) and zone_map[nx][ny] == zone:
|
|
visited[nkey] = true
|
|
queue.append({"x": nx, "y": ny})
|
|
|
|
region_id += 1
|
|
|
|
func draw_map() -> void:
|
|
for x in range(map_width):
|
|
for y in range(map_height):
|
|
place_tile(x, y, zone_map[x][y], region_map[x][y])
|
|
|
|
func place_tile(x: int, y: int, zone: int, region: int) -> void:
|
|
if not zone_textures.has(zone):
|
|
zone = ZONE_GROUND
|
|
|
|
var sources = zone_textures[zone]
|
|
if sources.is_empty():
|
|
return
|
|
|
|
var variant_idx = region % sources.size()
|
|
var source_id = sources[variant_idx]
|
|
|
|
tile_map.set_cell(0, Vector2i(x, y), source_id, Vector2i(0, 0))
|