mirror of
https://github.com/Relintai/broken_seals.git
synced 2024-12-28 13:27:10 +01:00
311 lines
8.1 KiB
GDScript
311 lines
8.1 KiB
GDScript
tool
|
|
extends Spatial
|
|
|
|
export(bool) var generate_on_ready : bool = true
|
|
export(PropData) var start_room : PropData
|
|
export(Array, PropData) var rooms : Array
|
|
export(PropData) var plug : PropData
|
|
export(bool) var generate : bool setget set_generate, get_generate
|
|
export(bool) var spawn_mobs : bool = true
|
|
|
|
export(int) var min_level : int = 1
|
|
export(int) var max_level : int = 2
|
|
|
|
export(int) var dungeon_seed : int = 0
|
|
|
|
#todo calc aabbs and store in PropData during prop conversion
|
|
var room_hulls : Dictionary
|
|
var portal_map : Dictionary
|
|
var portals : Array
|
|
|
|
var current_aabbs : Array
|
|
|
|
var debug : bool = true
|
|
|
|
var _rng : RandomNumberGenerator = RandomNumberGenerator.new()
|
|
|
|
func _enter_tree() -> void:
|
|
if not Engine.editor_hint && generate_on_ready:
|
|
call_deferred("generate")
|
|
|
|
func set_up_room_data() -> void:
|
|
clear_room_data()
|
|
|
|
process_prop(start_room)
|
|
|
|
for r in rooms:
|
|
process_prop(r)
|
|
|
|
process_portals()
|
|
|
|
func process_prop(room : PropData) -> void:
|
|
if !room:
|
|
return
|
|
|
|
if !room.is_room:
|
|
return
|
|
|
|
if room in room_hulls:
|
|
return
|
|
|
|
var ps : PoolVector3Array = room.room_bounds
|
|
|
|
#Convert the bounds to 2d
|
|
#This should probably be done in 3d later
|
|
#I found no simple way to do it for now
|
|
#Will look later
|
|
|
|
var points : PoolVector2Array = PoolVector2Array()
|
|
|
|
for p in ps:
|
|
points.push_back(Vector2(p.x, p.z))
|
|
|
|
points = Geometry.convex_hull_2d(points)
|
|
|
|
room_hulls[room] = points
|
|
|
|
for i in range(room.props.size()):
|
|
var pe : PropDataEntry = room.props[i]
|
|
|
|
if pe is PropDataPortal:
|
|
portals.append([pe, room, i])
|
|
|
|
|
|
func process_portals() -> void:
|
|
for i in range(portals.size()):
|
|
var pe = portals[i]
|
|
|
|
var portal : PropDataPortal = pe[0]
|
|
var room : PropData = pe[1]
|
|
|
|
var portal_points : PoolVector2Array = portal.points
|
|
|
|
if portal in portal_map:
|
|
continue
|
|
|
|
var map_data : Array = Array()
|
|
|
|
for j in range(portals.size()):
|
|
if i == j:
|
|
continue
|
|
|
|
var pp : Array = portals[j]
|
|
var cportal : PropDataPortal = pp[0]
|
|
|
|
var cportal_points : PoolVector2Array = cportal.points
|
|
|
|
if (cportal_points.size() != portal_points.size()):
|
|
continue
|
|
|
|
var eq : bool = true
|
|
|
|
for k in range(portal_points.size()):
|
|
var p1 : Vector2 = portal_points[k]
|
|
var p2 : Vector2 = cportal_points[k]
|
|
|
|
if !p1.is_equal_approx(p2):
|
|
eq = false
|
|
break
|
|
|
|
if !eq:
|
|
continue
|
|
|
|
var croom : PropData = pp[1]
|
|
var cj : int = pp[2]
|
|
|
|
map_data.append([ croom, cportal, cj ])
|
|
|
|
portal_map[portal] = map_data
|
|
|
|
|
|
func clear_room_data() -> void:
|
|
portal_map.clear()
|
|
portals.clear()
|
|
room_hulls.clear()
|
|
current_aabbs.clear()
|
|
|
|
func generate() -> void:
|
|
_rng.seed = dungeon_seed
|
|
|
|
clear()
|
|
set_up_room_data()
|
|
|
|
spawn_room(Transform(), start_room)
|
|
|
|
func spawn_room(room_lworld_transform : Transform, room : PropData, level : int = 0, current_portal : PropDataPortal = null) -> void:
|
|
if level > 4:
|
|
var plugi : PropInstanceMerger = PropInstanceMerger.new()
|
|
plugi.prop_data = plug
|
|
plugi.first_lod_distance_squared = 4000
|
|
add_child(plugi)
|
|
plugi.transform = room_lworld_transform
|
|
|
|
#test_spawn_pos(room_lworld_transform)
|
|
|
|
return
|
|
|
|
var orig_room_lworld_transform : Transform = room_lworld_transform
|
|
|
|
if current_portal:
|
|
var lworld_curr_portal : Transform = current_portal.transform
|
|
#portal center should be precalculated
|
|
#this will only work with the current portals
|
|
lworld_curr_portal = lworld_curr_portal.translated(Vector3(-0.5, 0, 0))
|
|
lworld_curr_portal.basis = lworld_curr_portal.basis.rotated(Vector3(0, 1, 0), PI)
|
|
room_lworld_transform = room_lworld_transform * lworld_curr_portal.inverse()
|
|
|
|
var sr : PropInstanceMerger = PropInstanceMerger.new()
|
|
sr.prop_data = room
|
|
sr.first_lod_distance_squared = 4000
|
|
add_child(sr)
|
|
sr.transform = room_lworld_transform
|
|
|
|
var cab : PoolVector2Array = room_hulls[room]
|
|
var ctfab : PoolVector2Array = PoolVector2Array()
|
|
|
|
for a in cab:
|
|
var v : Vector3 = Vector3(a.x, 0, a.y)
|
|
v = room_lworld_transform.xform(v)
|
|
ctfab.push_back(Vector2(v.x, v.z))
|
|
#v.y = 0
|
|
#test_spawn_pos(Transform(Basis(), v))
|
|
|
|
current_aabbs.push_back(ctfab)
|
|
|
|
if spawn_mobs && level > 0 && ctfab.size() > 0:
|
|
if _rng.randi() % 3 == 0:
|
|
var v2 : Vector2 = ctfab[0]
|
|
|
|
for i in range(1, ctfab.size()):
|
|
v2 = v2.linear_interpolate(ctfab[i], 0.5)
|
|
|
|
var gt : Transform = global_transform
|
|
var scale : Vector3 = gt.basis.get_scale()
|
|
v2 *= Vector2(scale.x, scale.z)
|
|
|
|
ESS.entity_spawner.spawn_mob(0, min_level + (_rng.randi() % (max_level - min_level)), Vector3(v2.x, gt.origin.y, v2.y))
|
|
|
|
#if Engine.editor_hint and debug:
|
|
# sr.owner = get_tree().edited_scene_root
|
|
|
|
for pe in room.props:
|
|
if pe is PropDataPortal:
|
|
if pe == current_portal:
|
|
continue
|
|
|
|
var current_portal_lworld_position : Transform = room_lworld_transform * pe.transform
|
|
|
|
var d : Array = portal_map[pe]
|
|
|
|
if d.size() == 0:
|
|
continue
|
|
|
|
var new_room_data = d[_rng.randi() % d.size()]
|
|
|
|
#[ croom, cportal, cj ]
|
|
var new_room : PropData = new_room_data[0]
|
|
var new_room_portal : PropDataPortal = new_room_data[1]
|
|
|
|
#todo figure out the transforms
|
|
var poffset : Vector3 = new_room_portal.transform.xform(Vector3())
|
|
|
|
#middle of the current portal
|
|
var offset_current_portal_lworld_position : Transform = current_portal_lworld_position
|
|
#portal center should be precalculated
|
|
#this will only work with the current portals
|
|
offset_current_portal_lworld_position = offset_current_portal_lworld_position.translated(Vector3(-0.5, 0, 0))
|
|
|
|
var ab : PoolVector2Array = room_hulls[new_room]
|
|
var tfab : PoolVector2Array = PoolVector2Array()
|
|
|
|
for a in ab:
|
|
var v : Vector3 = Vector3(a.x, 0, a.y)
|
|
v = offset_current_portal_lworld_position.xform(v)
|
|
tfab.push_back(Vector2(v.x, v.z))
|
|
# v.y = 0
|
|
# test_spawn_pos(Transform(Basis(), v))
|
|
|
|
#test_spawn_pos(offset_current_portal_lworld_position)
|
|
|
|
var can_spawn : bool = true
|
|
for saab in current_aabbs:
|
|
var ohull : PoolVector2Array = saab
|
|
|
|
var poly_int_res : Array = Geometry.intersect_polygons_2d(ohull, tfab)
|
|
|
|
if poly_int_res.size() > 0:
|
|
for poly in poly_int_res:
|
|
var indices : PoolIntArray = Geometry.triangulate_polygon(poly)
|
|
|
|
for i in range(0, indices.size(), 3):
|
|
var p1 : Vector2 = poly[indices[i]]
|
|
var p2 : Vector2 = poly[indices[i + 1]]
|
|
var p3 : Vector2 = poly[indices[i + 2]]
|
|
|
|
var pp1 : float = (p1.x * p2.y + p2.x * p3.y + p3.x * p1.y)
|
|
var pp2 : float = (p2.x * p1.y + p3.x * p2.y + p1.x * p3.y)
|
|
var area : float = 0.5 * (pp1 - pp2)
|
|
|
|
if area > 0.2:
|
|
#print(area)
|
|
#print(poly)
|
|
#print(indices)
|
|
|
|
#for p in poly:
|
|
# test_spawn_pos(Transform(Basis(), Vector3(p.x, 0, p.y)))
|
|
|
|
can_spawn = false
|
|
break
|
|
|
|
if !can_spawn:
|
|
break
|
|
|
|
if !can_spawn:
|
|
break
|
|
|
|
if can_spawn:
|
|
spawn_room(offset_current_portal_lworld_position, new_room, level + 1, new_room_portal)
|
|
else:
|
|
var plugi : PropInstanceMerger = PropInstanceMerger.new()
|
|
plugi.prop_data = plug
|
|
add_child(plugi)
|
|
plugi.transform = offset_current_portal_lworld_position
|
|
#test_spawn_pos(offset_current_portal_lworld_position)
|
|
|
|
func clear() -> void:
|
|
if Engine.editor_hint and debug:
|
|
#don't destroy the user's nodes
|
|
for c in get_children():
|
|
if c.owner == get_tree().edited_scene_root:
|
|
c.queue_free()
|
|
else:
|
|
for c in get_children():
|
|
c.queue_free()
|
|
|
|
func test_spawn_pos(lworld_position : Transform, color : Color = Color(1, 1, 1)):
|
|
var testspat : ImmediateGeometry = ImmediateGeometry.new()
|
|
add_child(testspat)
|
|
testspat.transform = lworld_position
|
|
testspat.begin(Mesh.PRIMITIVE_LINES)
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(0, -0.5, 0))
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(0, 0.5, 0))
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(-0.5, 0, 0))
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(0.5, 0, 0))
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(0, 0, -0.5))
|
|
testspat.set_color(color)
|
|
testspat.add_vertex(Vector3(0, 0, 0.5))
|
|
testspat.end()
|
|
|
|
|
|
func set_generate(on) -> void:
|
|
if on:
|
|
generate()
|
|
|
|
func get_generate() -> bool:
|
|
return false
|