Now About Social Code
summaryrefslogtreecommitdiff
path: root/scripts/bsp_level_generator.gd
blob: b6cb3162ba65cf64569be5fa4a735711ce48fc27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
extends Node3D

@export_category("Level Generator")
@export var width: int = 40
@export var height: int = 40
@export var min_dim: int = 5

@export_category("Geometry Generator")
## Geometry generation config
@export var wall_thickness: float = 1

var min_room_size: int = min_dim * min_dim

enum Direction {LEFT, RIGHT}

class BSPNode:
	var axis: int
	var min_dims: Vector2i
	var max_dims: Vector2i
	var left: BSPNode
	var right: BSPNode
	
	func _init(p_axis: int, p_min_dims: Vector2i, p_max_dims: Vector2i, p_left: BSPNode, p_right: BSPNode):
		axis = p_axis
		min_dims = p_min_dims
		max_dims = p_max_dims
		left = p_left
		right = p_right

func generate_level(axis: int, min_space: Vector2i, max_space: Vector2i, depth: int = 0) -> BSPNode:
	var dims = max_space - min_space
	var new_axis = (axis + 1) % 2
	# 10% we stop here and just create a big room
	if (depth > 2 and randi_range(0, 9) == 0) \
	or dims.x * dims.y <= min_dim * min_dim \
	or dims[new_axis] / 2 < min_dim:
		return BSPNode.new(new_axis, min_space, max_space, null, null)
		
	# Calculate min and max ranges so that a split 
	# doesn't create a room that violates min dimensions
	var min_value = min_space[axis] + min_dim
	var max_value = max_space[axis] - min_dim
	var split = randi_range(min_value, max_value)
	#print("Spliting axis ", axis, " at ", split)
	
	var left_min_space = min_space
	var left_max_space = max_space
	left_max_space[axis] = split
	var left = generate_level(new_axis, left_min_space, left_max_space, depth + 1)
	
	var right_min_space = min_space
	right_min_space[axis] = split
	var right_max_space = max_space
	var right = generate_level(new_axis, right_min_space, right_max_space, depth + 1)
	
	return BSPNode.new(axis, min_space, max_space, left, right)
	
func is_leaf(node: BSPNode) -> bool:
	return node.left == null and node.right == null
	
func get_children_on_axis(node: BSPNode, axis: int, value: int) -> Array[BSPNode]:
	if is_leaf(node):
		if node.min_dims[axis] == value or node.max_dims[axis] == value:
			return [node]
		else:
			return []
	else:
		return get_children_on_axis(node.left, axis, value) + get_children_on_axis(node.right, axis, value)

func generate_doors(node: BSPNode):
	var left_children = get_children_on_axis(node.left, node.axis, node.left.max_dims[node.axis])
	var right_children = get_children_on_axis(node.right, node.axis, node.right.min_dims[node.axis])
	
	print(left_children)
	print(right_children)
	
func generate_geo(map: BSPNode):
	# TODO figure out where to place walls
	if map.left == null and map.right == null:
		var box = CSGBox3D.new()
		var p1 = Vector3(map.min_dims.x + wall_thickness/2, 0, map.min_dims.y + wall_thickness/2)
		var p2 = Vector3(map.max_dims.x - wall_thickness/2, 0.1, map.max_dims.y - wall_thickness/2)
		box.size = p2 - p1
		var pos_p1 = Vector3(map.min_dims.x, 0, map.min_dims.y)
		var pos_p2 = Vector3(map.max_dims.x, 0, map.max_dims.y)
		var offset = (pos_p2 - pos_p1) / 2
		box.position = p1 + offset - Vector3(0, 1, 0)
		add_child(box)
	else:
		generate_geo(map.left)
		generate_geo(map.right)
		
		# Generate the wall between the two
		var wall = CSGBox3D.new()
		var axis = 0 if map.axis == 0 else 2
		var other_axis = 2 if axis == 0 else 0
		var max_dim = Vector3(map.max_dims.x, 0, map.max_dims.y)
		var min_dim = Vector3(map.min_dims.x, 0, map.min_dims.y)
		wall.size[map.axis] = wall_thickness
		wall.size[other_axis] = max_dim[other_axis] - min_dim[other_axis]
		wall.size[1] = 2
		
		var pos2d = map.left.max_dims
		var pos3d = Vector3(pos2d.x, 0, pos2d.y)
		wall.position = pos3d - (wall.size/2) + Vector3(1, 1, 1)
		add_child(wall)
		

func _ready() -> void:
	var starting_axis = randi_range(0, 1)
	var min_space = Vector2i(0, 0)
	var max_space = Vector2i(width, height)
	var map = generate_level(starting_axis, min_space, max_space)
	generate_doors(map)
	generate_geo(map)