This repository has been archived on 2024-07-02. You can view files and clone it, but cannot push or open issues or pull requests.
HoppyEaster/Scripts/MapGenerator.gd

217 lines
6.1 KiB
GDScript3
Raw Normal View History

extends TileMap
@export var width := 128
@export var height := 75
@export var fill_percentage := 0.65
@export var solid_id := 0
@export var non_solid_id := 1
@export var solid_threshold := 7
@export var nonsolid_threshold := 4
@export var start_area_size := 5
@export var start_area_corner_size := 2
@export var cave_gen_iterations := 3
@export var cave_mine_size_threshold := 80
func _ready():
var start_time = Time.get_unix_time_from_system()
randomize()
random_fill()
make_cave_areas()
make_borders()
prepare_player_start_area()
connect_caves(get_caves())
make_cave_areas()
make_borders()
print(Time.get_unix_time_from_system() - start_time)
func random_fill():
for x in width:
for y in height:
if randf() < fill_percentage:
self.set_cell(0, Vector2i(x,y),solid_id, Vector2i(0, 0))
else:
self.set_cell(0, Vector2i(x,y),non_solid_id, Vector2i(0, 0))
pass
func make_borders():
for x in width:
self.set_cell(0, Vector2i(x,0),solid_id, Vector2i(0, 0))
self.set_cell(0, Vector2i(x,height-1),solid_id, Vector2i(0, 0))
for y in height:
self.set_cell(0, Vector2i(0,y),solid_id, Vector2i(0, 0))
self.set_cell(0, Vector2i(width-1,y),solid_id, Vector2i(0, 0))
func prepare_player_start_area():
var center = Vector2i(width/2, height/2)
for x in range(center.x - start_area_size, center.x + start_area_size):
# Getting a P factor for the corner "radius" Decission
var p := 0
if x <= center.x - start_area_size + start_area_corner_size:
p = center.x - start_area_size + start_area_corner_size - x
if x >= center.x + start_area_size - start_area_corner_size:
p = x - ( center.x + start_area_size) + start_area_corner_size + 1
var p2 = p
for y in range(center.y - start_area_size, center.y + start_area_size):
# Decide if the tile is part of the Corner or not
if !(p2 > 0 or p2 <= - (start_area_size*2 - p*2)):
self.set_cell(0, Vector2i(x,y),non_solid_id, Vector2i(0, 0))
p2 -= 1
func make_cave_areas():
for i in cave_gen_iterations:
for x in range(1, width):
for y in range(1, height):
# Count Solid Neighbor Cells
var count := 0
#y-1
if self.get_cell_source_id(0, Vector2i(x-1, y-1)) == solid_id: count +=1
if self.get_cell_source_id(0, Vector2i(x, y-1)) == solid_id: count +=1
if self.get_cell_source_id(0, Vector2i(x+1, y-1)) == solid_id: count +=1
#y
if self.get_cell_source_id(0, Vector2i(x-1, y)) == solid_id: count +=1
if self.get_cell_source_id(0, Vector2i(x+1, y)) == solid_id: count +=1
#y+1
if self.get_cell_source_id(0, Vector2i(x-1, y+1)) == solid_id: count +=1
if self.get_cell_source_id(0, Vector2i(x, y+1)) == solid_id: count +=1
if self.get_cell_source_id(0, Vector2i(x+1, y+1)) == solid_id: count +=1
# Check Wether to Change the Cell or Not
if count < nonsolid_threshold:
self.set_cell(0, Vector2i(x,y),non_solid_id, Vector2i(0, 0))
if count >= solid_threshold:
self.set_cell(0, Vector2i(x,y),solid_id, Vector2i(0, 0))
pass
func get_caves() -> Array:
# get caves
var caves := []
for x in range (2, width-2):
for y in range (2, height-2):
if self.get_cell_source_id(0, Vector2i(x, y)) == non_solid_id:
flood_fill(x,y, caves)
for cave in caves:
for tile in cave:
self.set_cell(0, Vector2i(tile.x,tile.y),non_solid_id, Vector2i(0, 0))
return caves
func flood_fill(tilex, tiley, caves) -> Array:
var cave := []
var to_fill := [Vector2i(tilex, tiley)]
while to_fill:
var tile = to_fill.pop_back()
if !cave.has(tile):
cave.append(tile)
self.set_cell(0, Vector2i(tile.x,tile.y),solid_id, Vector2i(0, 0))
#check adjacent cells
var north = Vector2i(tile.x, tile.y+1)
var south = Vector2i(tile.x, tile.y-1)
var east = Vector2i(tile.x+1, tile.y)
var west = Vector2i(tile.x-1, tile.y)
for dir in [north,south,east,west]:
if self.get_cell_source_id(0, dir) == non_solid_id:
if !to_fill.has(dir) and !cave.has(dir):
to_fill.append(dir)
if cave.size() >= cave_mine_size_threshold:
caves.append(cave)
return caves
func choose(choices):
randomize()
var rand_index = randi() % choices.size()
return choices[rand_index]
func connect_caves(caves):
var prev_cave = null
var tunnel_caves = caves.duplicate()
for cave in tunnel_caves:
if prev_cave:
var new_point = choose(cave)
var prev_point = choose(prev_cave)
# ensure not the same point
if new_point != prev_point:
create_tunnel(new_point, prev_point, cave)
prev_cave = cave
pass
func create_tunnel(point1, point2, cave):
randomize() # for randf
var max_steps = 500 # so editor won't hang if walk fails
var steps = 0
var drunk_x = point2[0]
var drunk_y = point2[1]
while steps < max_steps and !cave.has(Vector2i(drunk_x, drunk_y)):
steps += 1
# set initial dir weights
var n = 1.0
var s = 1.0
var e = 1.0
var w = 1.0
var weight = 1
# weight the random walk against edges
if drunk_x < point1.x: # drunkard is left of point1
e += weight
elif drunk_x > point1.x: # drunkard is right of point1
w += weight
if drunk_y < point1.y: # drunkard is above point1
s += weight
elif drunk_y > point1.y: # drunkard is below point1
n += weight
# normalize probabilities so they form a range from 0 to 1
var total = n + s + e + w
n /= total
s /= total
e /= total
w /= total
var dx
var dy
# choose the direction
var choice = randf()
if 0 <= choice and choice < n:
dx = 0
dy = -1
elif n <= choice and choice < (n+s):
dx = 0
dy = 1
elif (n+s) <= choice and choice < (n+s+e):
dx = 1
dy = 0
else:
dx = -1
dy = 0
# ensure not to walk past edge of map
if (2 < drunk_x + dx and drunk_x + dx < width-2) and \
(2 < drunk_y + dy and drunk_y + dy < height-2):
drunk_x += dx
drunk_y += dy
if self.get_cell_source_id(0, Vector2i(drunk_x, drunk_y)) == solid_id:
self.set_cell(0, Vector2i(drunk_x, drunk_y),non_solid_id, Vector2i(0, 0))
#make tunnel wider
self.set_cell(0, Vector2i(drunk_x+1, drunk_y),non_solid_id, Vector2i(0, 0))
self.set_cell(0, Vector2i(drunk_x+1, drunk_y+1),non_solid_id, Vector2i(0, 0))