update scarcity
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
extends Node
|
||||
class_name ModularMapGenerator
|
||||
|
||||
# ModularMapGenerator - Procedural generation for Floor 0 (Ground)
|
||||
|
||||
const GROUND_ITEM = 0
|
||||
const WALL_ITEM = 4 # Or Void if using empty cells
|
||||
|
||||
# Generation Parameters
|
||||
var grid_width: int = 14
|
||||
var grid_depth: int = 14
|
||||
var min_walkable_cells: int = 20 # Minimum for 12 players + space
|
||||
var max_walkable_cells: int = 100
|
||||
var connectivity_guaranteed: bool = true
|
||||
|
||||
# Shapes / Strategies
|
||||
enum MapShape {
|
||||
RECTANGLE,
|
||||
ISLANDS,
|
||||
MAZE_LIKE,
|
||||
ROOMS,
|
||||
RANDOM_NOISE
|
||||
}
|
||||
|
||||
func generate_map(gridmap: Node, width: int, depth: int, shape: int = MapShape.ROOMS) -> bool:
|
||||
grid_width = width
|
||||
grid_depth = depth
|
||||
|
||||
var grid_data = [] # 2D array [z][x]
|
||||
|
||||
# Initialize with walls/void
|
||||
for z in range(depth):
|
||||
var row = []
|
||||
for x in range(width):
|
||||
row.append(WALL_ITEM)
|
||||
grid_data.append(row)
|
||||
|
||||
# Apply generation strategy
|
||||
match shape:
|
||||
MapShape.RECTANGLE:
|
||||
_generate_rectangle(grid_data)
|
||||
MapShape.ISLANDS:
|
||||
_generate_islands(grid_data)
|
||||
MapShape.ROOMS:
|
||||
_generate_rooms(grid_data)
|
||||
MapShape.RANDOM_NOISE:
|
||||
_generate_noise(grid_data)
|
||||
_:
|
||||
_generate_rooms(grid_data) # Default
|
||||
|
||||
# Ensure connectivity
|
||||
if connectivity_guaranteed:
|
||||
_ensure_connectivity(grid_data)
|
||||
|
||||
# Ensure minimum walkable count (by growing if needed)
|
||||
_ensure_min_walkable(grid_data)
|
||||
|
||||
# Apply to GridMap
|
||||
_apply_to_gridmap(gridmap, grid_data)
|
||||
|
||||
return true
|
||||
|
||||
func _generate_rectangle(grid: Array):
|
||||
# Simple rectangle with some noise edges?
|
||||
for z in range(1, grid_depth - 1):
|
||||
for x in range(1, grid_width - 1):
|
||||
grid[z][x] = GROUND_ITEM
|
||||
|
||||
func _generate_noise(grid: Array):
|
||||
var rng = RandomNumberGenerator.new()
|
||||
rng.randomize()
|
||||
for z in range(1, grid_depth - 1):
|
||||
for x in range(1, grid_width - 1):
|
||||
if rng.randf() > 0.4:
|
||||
grid[z][x] = GROUND_ITEM
|
||||
|
||||
func _generate_rooms(grid: Array):
|
||||
# Random walk rooms
|
||||
var rng = RandomNumberGenerator.new()
|
||||
rng.randomize()
|
||||
|
||||
var room_count = rng.randi_range(3, 6)
|
||||
var rooms = [] # Rects
|
||||
|
||||
for i in range(room_count):
|
||||
var w = rng.randi_range(3, 6)
|
||||
var h = rng.randi_range(3, 6)
|
||||
var x = rng.randi_range(1, grid_width - w - 1)
|
||||
var z = rng.randi_range(1, grid_depth - h - 1)
|
||||
|
||||
# Carve room
|
||||
for rz in range(z, z + h):
|
||||
for rx in range(x, x + w):
|
||||
grid[rz][rx] = GROUND_ITEM
|
||||
|
||||
# Connect to previous room
|
||||
if i > 0:
|
||||
var prev = rooms[i-1]
|
||||
_connect_points(grid, Vector2i(prev[0], prev[1]), Vector2i(x, z))
|
||||
|
||||
rooms.append([x, z, w, h])
|
||||
|
||||
func _generate_islands(grid: Array):
|
||||
# Cellular automata smoothing
|
||||
_generate_noise(grid)
|
||||
|
||||
# Smoothing passes
|
||||
for i in range(3):
|
||||
var new_grid = grid.duplicate(true)
|
||||
for z in range(1, grid_depth - 1):
|
||||
for x in range(1, grid_width - 1):
|
||||
var neighbors = _count_neighbors(grid, x, z)
|
||||
if neighbors > 4:
|
||||
new_grid[z][x] = GROUND_ITEM
|
||||
elif neighbors < 3:
|
||||
new_grid[z][x] = WALL_ITEM
|
||||
grid = new_grid
|
||||
|
||||
func _count_neighbors(grid: Array, x: int, z: int) -> int:
|
||||
var count = 0
|
||||
for dz in range(-1, 2):
|
||||
for dx in range(-1, 2):
|
||||
if dz == 0 and dx == 0: continue
|
||||
if grid[z + dz][x + dx] == GROUND_ITEM:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
func _connect_points(grid: Array, p1: Vector2i, p2: Vector2i):
|
||||
var curr = p1
|
||||
while curr != p2:
|
||||
grid[curr.y][curr.x] = GROUND_ITEM
|
||||
if curr.x != p2.x:
|
||||
curr.x += 1 if p2.x > curr.x else -1
|
||||
elif curr.y != p2.y:
|
||||
curr.y += 1 if p2.y > curr.y else -1
|
||||
grid[p2.y][p2.x] = GROUND_ITEM
|
||||
|
||||
func _ensure_connectivity(grid: Array):
|
||||
# Flood fill from first walkable
|
||||
var start = Vector2i(-1, -1)
|
||||
var all_walkable = []
|
||||
|
||||
for z in range(grid_depth):
|
||||
for x in range(grid_width):
|
||||
if grid[z][x] == GROUND_ITEM:
|
||||
if start == Vector2i(-1, -1):
|
||||
start = Vector2i(x, z)
|
||||
all_walkable.append(Vector2i(x, z))
|
||||
|
||||
if start == Vector2i(-1, -1):
|
||||
return # Empty map
|
||||
|
||||
var visited = {}
|
||||
var queue = [start]
|
||||
visited[start] = true
|
||||
var connected_count = 0
|
||||
|
||||
while not queue.is_empty():
|
||||
var curr = queue.pop_front()
|
||||
connected_count += 1
|
||||
|
||||
# Check neighbors
|
||||
var dirs = [Vector2i(0, 1), Vector2i(0, -1), Vector2i(1, 0), Vector2i(-1, 0)]
|
||||
for d in dirs:
|
||||
var next = curr + d
|
||||
if next.x >= 0 and next.x < grid_width and next.y >= 0 and next.y < grid_depth:
|
||||
if grid[next.y][next.x] == GROUND_ITEM and not visited.has(next):
|
||||
visited[next] = true
|
||||
queue.append(next)
|
||||
|
||||
# If disconnected parts, keep largest island or removing others?
|
||||
# Better: grow bridges. But for simplicity, let's just use the main island
|
||||
# and turn others to walls? Or connect them?
|
||||
# For "Rooms" geneation, we explicitly connected them.
|
||||
# For "Islands", let's just keep the biggest island or assume automata made big blobs.
|
||||
|
||||
if connected_count < all_walkable.size():
|
||||
# Prune disconnected (simple approach)
|
||||
for pos in all_walkable:
|
||||
if not visited.has(pos):
|
||||
grid[pos.y][pos.x] = WALL_ITEM
|
||||
|
||||
func _ensure_min_walkable(grid: Array):
|
||||
var walkable = []
|
||||
for z in range(grid_depth):
|
||||
for x in range(grid_width):
|
||||
if grid[z][x] == GROUND_ITEM:
|
||||
walkable.append(Vector2i(x, z))
|
||||
|
||||
if walkable.size() < min_walkable_cells:
|
||||
# Force create a central room
|
||||
var center_x = grid_width / 2
|
||||
var center_z = grid_depth / 2
|
||||
var radius = int(sqrt(min_walkable_cells)) / 2 + 1
|
||||
for z in range(center_z - radius, center_z + radius):
|
||||
for x in range(center_x - radius, center_x + radius):
|
||||
if x >= 0 and x < grid_width and z >= 0 and z < grid_depth:
|
||||
grid[z][x] = GROUND_ITEM
|
||||
|
||||
func _apply_to_gridmap(gridmap: Node, grid: Array):
|
||||
for z in range(grid_depth):
|
||||
for x in range(grid_width):
|
||||
var item = grid[z][x]
|
||||
# Floor 0 logic
|
||||
gridmap.set_cell_item(Vector3i(x, 0, z), item)
|
||||
Reference in New Issue
Block a user