opencyber/scripts/map_generator.gd

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))