TowerGame/TerrainGen.gd

146 lines
4.8 KiB
GDScript

@tool
extends GridMap
class_name TerrainGen
signal map_changed()
## TODO:
## - [x] Finish inner corner tiles
## - [x] Place inner corner tiles
## - [x] Fix nav meshes
## - [x] Generate nav regions
## - [x] Bake navmesh
## - [x] Add spawners / functionality to test level
## - [ ] Hook up everything up
## - [ ] Finish outer corner tiles
## - [ ] Add tile variants
## - [x] Use tile variants in code
## - [ ] Variants have a percentage?
@export_tool_button("Generate", "Reload") var action: Callable = generate
@export_group("Map", "map_")
## Dimension for the map
@export var map_region: Rect2i = Rect2i(0,0,200,200)
## Maximum height of the map
@export var map_max_height: int = 16
## Chance of a ramp appearing at any spot they can
@export_range(0.0, 1.0) var map_ramp_chance: float = 0.1
## Texture used to place tiles in map
@export var map_texture: Texture2D
## Seed used for random placement within generation
@export var map_seed: String = "69420"
var map_image: Image
## Dictionary of tiles, add multiple to have one randomly picked.
## Multiple entries of the same id can help to increase chances
@export var tile_dictionary: Dictionary[String, Array] = {
"block": [0],
"floor": [1, 1, 1, 10],
"ramp_n": [2],
"ramp_e": [3],
"ramp_s": [4],
"ramp_w": [5],
"inner_ne": [6],
"inner_se": [7],
"inner_sw": [8],
"inner_nw": [9],
#"outer_ne": [10],
#"outer_se": [11],
#"outer_sw": [12],
#"outer_nw": [13],
}
class Heights: ## object holding all surrounding heights
var c: int ## center height of point (the one you pass into get_heights())
var l: int ## lowest height of all the points
var n: int ## north height
var ne: int ## north-east height
var e: int ## east height
var se: int ## south-east height
var s: int ## south height
var sw: int ## south-west height
var w: int ## west height
var nw: int ## north-west height
func generate() -> void:
if map_texture == null: return
map_image = map_texture.get_image()
clear()
seed(map_seed.hash())
for x: int in range(map_region.position.x, map_region.size.x):
for y: int in range(map_region.position.y, map_region.size.y):
var points: Heights = get_heights(x, y)
var point_3d: Vector3i = Vector3i(x, points.c, y)
# floor
set_cell_item(point_3d, get_tile("floor"))
# block in cliffs
for i: int in range(points.l - 1, points.c):
set_cell_item(Vector3i(x, i, y), get_tile("block"))
var ramp_roll: float = randf()
if ramp_roll > map_ramp_chance: continue
# cardinal direction ramps
if points.n - 1 == points.c:
set_cell_item(point_3d, get_tile("block"))
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("ramp_n"))
elif points.e - 1 == points.c:
set_cell_item(point_3d, get_tile("block"))
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("ramp__e"))
elif points.s - 1 == points.c:
set_cell_item(point_3d, get_tile("block"))
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("ramp_s"))
elif points.w - 1 == points.c:
set_cell_item(point_3d, get_tile("block"))
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("ramp_w"))
# replace cardinal ramps w/ inner ramps
if points.n - 1 == points.c && points.e - 1 == points.c:
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("inner_ne"))
if points.s - 1 == points.c && points.e - 1 == points.c:
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("inner_se"))
if points.s - 1 == points.c && points.w - 1 == points.c:
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("inner_sw"))
if points.n - 1 == points.c && points.w - 1 == points.c:
set_cell_item(point_3d + Vector3i(0, 1, 0), get_tile("inner_nw"))
#endfor
map_changed.emit()
func get_height(x: int, y: int) -> int:
var fx: float = clamp((x - map_region.position.x) as float / map_region.size.x as float, 0.0, 1.0)
var fy: float = clamp((y - map_region.position.y) as float / map_region.size.y as float, 0.0, 1.0)
var px: int = floori(fx * (map_image.get_width() - 1))
var py: int = floori(fy * (map_image.get_height() - 1))
#print("x: %s, fx: %s, px: %s" % [x, fx, px])
var col: Color = map_image.get_pixel(px, py)
var val: float = col.get_luminance()
var height: int = floori(((val + 1)/2.0) * map_max_height)
return height
func get_heights(x: int, y: int) -> Heights:
var h: Heights = Heights.new()
h.n = get_height(x, y + 1)
h.ne = get_height(x - 1, y + 1)
h.e = get_height(x - 1, y)
h.se = get_height(x - 1, y - 1)
h.s = get_height(x, y - 1)
h.sw = get_height(x + 1, y - 1)
h.w = get_height(x + 1, y)
h.nw = get_height(x + 1, y + 1)
h.c = get_height(x, y)
h.l = min(h.n, h.ne, h.e, h.se, h.s, h.sw, h.w, h.nw, h.c - 1)
return h
func get_tile(tile_name: String) -> int:
var tile_list: Array = tile_dictionary.get(tile_name, [0])
var tile: int = tile_list[randi_range(0, tile_list.size() - 1)]
return tile