Remove SimpleDungeons plugin

This commit is contained in:
2025-09-15 20:50:19 -07:00
parent b501d22782
commit 4808b1dd63
169 changed files with 1583 additions and 17955 deletions

View File

@@ -2,8 +2,4 @@ namespace Zennysoft.Ma.Adapter;
public enum BackgroundMusic
{
CrossingTheGate,
Droney,
TarWinds,
UselessImmuneSystem
}

View File

@@ -1,935 +0,0 @@
@tool
class_name DungeonGenerator3D
extends Node3D
signal done_generating()
signal generating_failed()
# This can be set to a callable to customize room spawning in a fine grained way.
# Function signature should be Callable(room_instances : Array[DungeonRoom3D], rng : RandomNumberGenerator) -> Array[DungeonRoom3D]
var custom_get_rooms_function = null
# For any vars which may be accessed from multiple threads
var t_mutex := Mutex.new()
enum BuildStage { NOT_STARTED = -2, PREPARING = -1, PLACE_ROOMS = 0, PLACE_STAIRS = 1, SEPARATE_ROOMS = 2, CONNECT_ROOMS = 3, FINALIZING = 4, DONE = 5 }
var stage : BuildStage = BuildStage.NOT_STARTED :
set(v): t_mutex.lock(); stage = v; t_mutex.unlock();
get: t_mutex.lock(); var v = stage; t_mutex.unlock(); return v;
## Add all the rooms for the dungeon generator to use in this array.
## Each one must inherit DungeonRoom.
## Not necessary to place the corridor room here. Place that in the corridor_room_scene property.
@export var room_scenes : Array[PackedScene] = []
## The corridor room is a special room scene which must be a 1x1x1 (in voxels) scene inheriting DungeonRoom which is used to connect all the placed rooms.
@export var corridor_room_scene : PackedScene
## Dungeon grid size measured in voxel units, voxel size is chosen in the voxel_scale property.
@export var dungeon_size := Vector3i(10,10,10) :
set(v):
dungeon_size = v.clamp(Vector3i(1,1,1),Vector3i(9999,9999,9999))
## Voxel scale/size in world units. Controls the standardized 1x1x1 room size of the dungeon.
## This should match the voxel scale on each of your rooms.voxel_scale
## If the voxel scale is different, the rooms will be scaled (perhaps non uniformly) to match the DungeonGenerator's voxel scale.
@export var voxel_scale := Vector3(10,10,10) :
set(v):
voxel_scale = v.clamp(Vector3(0.0001,0.0001,0.0001),Vector3(9999,9999,9999))
## Seed to use when generating the dungeon. Blank for random. You can call .generate(seed : int) to override.
## I'm setting this as a string because it didn't seem like you could set very large ints from editor if set to int.
@export var generate_seed : String = "" :
set(v):
var stripped_value = v.strip_edges().replace(r"[^0-9-]", "")
if stripped_value.begins_with("-"):
stripped_value = "-" + stripped_value.replace("-", "")
else:
stripped_value = stripped_value.replace("-", "")
generate_seed = stripped_value
@export var generate_on_ready : bool = true
## Depending on the dungeon kit used and the separation/connecting algorithm, it's possible that
## a dungeon may not be able to correctly generate and have all rooms reachable/not overlapping.
## In that case, the algorithm can simply restart from the beginning and try again.
@export var max_retries : int = 1
## Max total iterations in generate loop running one pass for the current stage.
## Generation stages: PLACE_ROOMS, PLACE_STAIRS, SEPARATE_ROOMS, CONNECT_ROOMS
@export var max_safe_iterations : int = 250
## Run generation of dungeon on a separate thread.
## It is safe to access vars .stage, .is_currently_generating, and .failed_to_generate but otherwise you should use .call_deferred_thread_group() to call any functions.
## Does not work in editor - was causing crashes so I disabled it.
@export var generate_threaded := false
## Generate the dungeon in editor. If you added any custom rooms which extend DungeonRoom,
## they also need the @tool directive for this to work.
@export var editor_button_generate_dungeon : bool = false :
set(value):
generate()
## Abort the generation started in editor
@export var abort_editor_button : bool = false :
set(value):
abort_generation()
@export_group("AStar room connection options")
enum AStarHeuristics { NONE_DIJKSTRAS = 0, MANHATTAN = 1, EUCLIDEAN = 2 }
## What heuristic to use for the AStar room connection algorithm.
## Euclidan - Standard, tends towards straight corridors connecting rooms.
## Manhattan - May lead to zigzagging corridors between rooms.
## Dijkstra's - No heuristic, this turns AStar into Dijkstra's algorithm. Guaranteed to find the shortest possible path but may lead to zigzagging corridors.
@export var astar_heuristic : AStarHeuristics = AStarHeuristics.EUCLIDEAN
## Increasing the heuristic scale may make the path less optimal but can help reduce zigzagging corridors.
## A heuristic of 3.0 (with either manhattan or euclidean, Dijkstra's already means 0 heuristic scale) may help reduce zigzagging corridors.
@export var heuristic_scale : float = 1.0
## By making the corridors cost less to walk through, the algorithm will tend towards merging into single corridors,
## thus making hallways more compact.
@export var corridor_cost_multiplier : float = 0.25
## Similar to corridor cost, setting this lower makes it so the algorithm will walk through a rooms to connect 2 rooms/doors, thus saving corridors placements.
## You could also set it greater than 1 to make the algorithm less likely to walk through existing (non-corridor) rooms.
@export var room_cost_multiplier : float = 0.25
## After connecting all rooms, some doors may not have been connected.
## By setting the cost for astar to walk through rooms higher at this stage, it encourages more interesting room connections,
## rather than just putting 1x1x1 corridor caps at doors.
@export var room_cost_at_end_for_required_doors : float = 2.0
@export_group("Debug options")
@export var show_debug_in_editor : bool = true
@export var show_debug_in_game : bool = false
## Whether to place the dungeon rooms so far even if the generation failed.
@export var place_even_if_fail : bool = false
@export var visualize_generation_progress : bool = false
# By default hide debug after gen. Othewise will lag.
@export var hide_debug_visuals_for_all_generated_rooms : bool = true
@export var cycle_debug_draw_when_press_n : bool = false
## A timer will be called with this wait time for the next iteration of the generation loop
@export_range(0, 1000, 1, "or_greater", "suffix:ms") var visualize_generation_wait_between_iterations : int = 100
###########################
## INIT/PROCESS/BUILTINS ##
###########################
func _init():
RenderingServer.set_debug_generate_wireframes(true)
func _input(event):
if cycle_debug_draw_when_press_n and event is InputEventKey and Input.is_key_pressed(KEY_N):
var vp = get_viewport()
vp.debug_draw = (vp.debug_draw + 1 ) % 4
var _debug_view = null
func add_debug_view_if_not_exist():
if not _debug_view:
_debug_view = preload("res://addons/SimpleDungeons/debug_visuals/DungeonGenerator3DDebugView.gd").new()
add_child(_debug_view)
func _ready():
add_debug_view_if_not_exist()
if Engine.is_editor_hint():
return
if generate_on_ready:
generate()
func _process(delta):
if Engine.is_editor_hint():
# Debug view doesn't get added in Editor sometimes, like if you manually drag script on.
for c in get_children():
if c is DungeonRoom3D and not c.virtualized_from:
c.add_debug_view_if_not_exist()
return
if _visualization_in_progress and Time.get_ticks_msec() - _last_iteration_end_time > visualize_generation_wait_between_iterations:
_run_generate_loop(false)
###################################
## GENERATION ENTRY POINT & LOOP ##
###################################
# Mostly setting up proper error handling, threads, visualization here.
# Most of the interesting stuff is in the build logic portion of the code below.
var room_instances : Array[DungeonRoom3D]
var corridor_room_instance : DungeonRoom3D
var iterations := 0
var retry_attempts := 0
var rooms_container : Node3D
var rng := RandomNumberGenerator.new()
var running_thread : Thread
var failed_to_generate := false :
set(v): t_mutex.lock(); failed_to_generate = v; t_mutex.unlock();
get: t_mutex.lock(); var v = failed_to_generate; t_mutex.unlock(); return v;
var full_abort_triggered := false :
set(v): t_mutex.lock(); full_abort_triggered = v; t_mutex.unlock();
get: t_mutex.lock(); var v = full_abort_triggered; t_mutex.unlock(); return v;
var is_currently_generating : bool :
get: return not (stage == BuildStage.NOT_STARTED or stage == BuildStage.DONE) and not failed_to_generate
var _is_generating_threaded = false
var _visualization_in_progress = false
var _last_iteration_end_time : int = Time.get_ticks_msec()
func generate(seed : int = int(generate_seed) if generate_seed.is_valid_int() else randi()) -> void:
if is_currently_generating:
_printerr("SimpleDungeons Error: Dungeon currently generating, cannot generate.")
return
stage = BuildStage.NOT_STARTED
if not validate_dungeon():
_printerr("SimpleDungeons Error: Cannot generate.")
return
rng = RandomNumberGenerator.new()
rng.seed = seed
print("DungeonGenerator3D generate(): Using seed ", seed)
cleanup_and_reset_dungeon_generator()
create_or_recreate_rooms_container()
get_preplaced_rooms() # Ensure cached because can't call get_children on threads
for room in get_preplaced_rooms():
room.snap_room_to_dungeon_grid()
if not room.validate_room():
_fail_generation("Could not validate preplaced rooms.")
return
room.ensure_doors_and_or_transform_cached_for_threads_and_virtualized_rooms()
stage = BuildStage.PREPARING
if not setup_room_instances_and_validate_before_generate():
_fail_generation("DungeonGenerator3D generation failed while setting up rooms.")
return
_is_generating_threaded = generate_threaded and not visualize_generation_progress
_visualization_in_progress = visualize_generation_progress
if _is_generating_threaded and Engine.is_editor_hint():
_is_generating_threaded = false
_printwarning("Disabling threaded generation because in editor. Kept running into crashes with editor threads, looked like Godot bugs, so disabling for now. You can still use visualize generation in editor. Threaded generation in game seems to work fine.")
if _is_generating_threaded:
running_thread = Thread.new()
running_thread.start(_run_generate_loop)
else:
_run_generate_loop()
func _run_generate_loop(first_call : bool = true) -> void:
if first_call:
retry_attempts = 0
iterations = 0
failed_to_generate = false
stage = BuildStage.PLACE_ROOMS
while retry_attempts <= max_retries and not full_abort_triggered:
while iterations < max_safe_iterations and not failed_to_generate and stage != BuildStage.FINALIZING:
var start_stage := stage
_run_one_loop_iteration_and_increment_iterations()
_last_iteration_end_time = Time.get_ticks_msec()
if _visualization_in_progress and stage != BuildStage.FINALIZING and not failed_to_generate:
return # Will be called again from _process
if stage == BuildStage.FINALIZING:
break
# If the dungeon failed to generate after looping through all stages until max_safe_iterations, retry.
retry_attempts += 1
if retry_attempts > max_retries:
_fail_generation("Reached max generation retries. Failed to generate. Failed at stage "+BuildStage.find_key(stage))
break
else:
clear_rooms_container_and_setup_for_next_iteration()
iterations = 0
failed_to_generate = false
stage = BuildStage.PLACE_ROOMS
if _visualization_in_progress:
return # Will be called again from _process
_printwarning("Generation failed on attempt "+str(retry_attempts)+" at stage "+BuildStage.find_key(stage)+". Retrying generation.")
_visualization_in_progress = false
if _is_generating_threaded:
if not failed_to_generate: _dungeon_finished_generating.call_deferred()
else: _dungeon_failed_generating.call_deferred()
else:
if not failed_to_generate: _dungeon_finished_generating()
else: _dungeon_failed_generating()
var _stage_just_changed = false
func _run_one_loop_iteration_and_increment_iterations() -> void:
if iterations == 0:
_stage_just_changed = true
var cur_stage = stage
# Each stage will increment stage once it is done.
# Also, each stage can call _abort_generation_and_fail if it encounters any errors.
if stage == BuildStage.PLACE_ROOMS:
place_room_iteration(_stage_just_changed)
elif stage == BuildStage.PLACE_STAIRS:
place_stairs_iteration(_stage_just_changed)
elif stage == BuildStage.SEPARATE_ROOMS:
# This looked to be so extremely slow to run all the checks after each separate iteration, so just going to iterate here:
separate_rooms_iteration(_stage_just_changed)
elif stage == BuildStage.CONNECT_ROOMS:
connect_rooms_iteration(_stage_just_changed)
_stage_just_changed = stage != cur_stage
iterations += 1
func _fail_generation(error : String = "Aborted generation") -> void:
_printerr("SimpleDungeons Error: ", error)
_printerr("SimpleDungeons Error: Failed to generate dungeon")
failed_to_generate = true
func abort_generation():
if not is_currently_generating:
_printwarning("DungeonGenerator3D not currently generating.")
return
failed_to_generate = true
full_abort_triggered = true
_printerr("abort_generation() called")
if running_thread and running_thread.is_alive() and OS.get_main_thread_id() == OS.get_thread_caller_id():
running_thread.wait_to_finish()
for room in room_instances:
room.queue_free()
for room in _rooms_placed:
room.queue_free()
rooms_container.queue_free()
if rooms_container.is_inside_tree():
rooms_container.get_parent().remove_child(rooms_container)
func _finalize_rooms(ready_callback = null) -> void:
if not rooms_container.is_inside_tree():
add_child(rooms_container)
rooms_container.owner = self.owner
for room in _rooms_placed.slice(0):
_rooms_placed.erase(room)
var unvirtualized = room.unvirtualize_and_free_clone_if_needed(rooms_container)
unvirtualized.owner = self.owner
_rooms_placed.push_back(unvirtualized)
for room in room_instances:
if room and is_instance_valid(room):
room.queue_free()
room_instances = []
if corridor_room_instance and is_instance_valid(corridor_room_instance):
corridor_room_instance.queue_free()
corridor_room_instance = null
if ready_callback is Callable:
if rooms_container.is_node_ready():
ready_callback.call_deferred()
else:
rooms_container.ready.connect(ready_callback)
func _dungeon_finished_generating() -> void:
_finalize_rooms(_emit_done_signals)
func _dungeon_failed_generating() -> void:
if place_even_if_fail:
_finalize_rooms()
elif not visualize_generation_progress:
rooms_container.queue_free()
for room in _rooms_placed:
room.queue_free()
for room in room_instances:
room.queue_free()
_emit_failed_signal.call_deferred() # Ensure rooms container placed/might be on thread.
# Emit done signals for dungeon & place_room for all DungeonRooms.
func _emit_done_signals():
stage = BuildStage.DONE
# Also need to call emit signal for each of place_rooms
for room in _rooms_placed:
if not room.original_ready_func_called:
_printwarning("_ready not called on "+room.name+". Room placement, finalization, and doors will be broken. Make sure to call super._ready() at the top of your _ready func when inheriting DungeonRoom3D.")
room.dungeon_done_generating.emit()
for preplaced_room in find_children("*", "DungeonRoom3D", false):
preplaced_room.dungeon_done_generating.emit()
print("DungeonGenerator3D finished generating.")
for room in room_instances:
room.queue_free()
done_generating.emit()
func _emit_failed_signal(): # So I can call_deferred
if running_thread:
running_thread.wait_to_finish()
generating_failed.emit()
#########################
## DUNGEON BUILD LOGIC ##
#########################
# Placing rooms: A random room is selected from room_scenes until the min_count of all is matched
var _rooms_placed : Array[DungeonRoom3D]
var _custom_rand_rooms : Array[DungeonRoom3D]
var _use_custom_rand_rooms := false
func place_room_iteration(first_call_in_loop : bool) -> void:
if first_call_in_loop:
_rooms_placed = []
_custom_rand_rooms = []
_use_custom_rand_rooms = false
if custom_get_rooms_function is Callable:
_use_custom_rand_rooms = true
_custom_rand_rooms = custom_get_rooms_function.call(room_instances, rng)
if not _custom_rand_rooms is Array or len(_custom_rand_rooms) == 0:
_fail_generation("custom_get_rooms_function takes should return a non-empty Array of DungeonRoom3Ds.")
_printwarning("custom_get_rooms_function takes (room_instances : Array[DungeonRoom3D], rng_seeded : RandomNumberGenerator) as the arguments and should use .create_clone_and_make_virtual_unless_visualizing() to clone and then position with .set_position_by_grid_pos(Vector3i) or .rotation = 0 through 3 for number of 90 degree y rotations for the room.")
return
for room in _custom_rand_rooms:
if not room is DungeonRoom3D:
_fail_generation("custom_get_rooms_function supplied an object that is not a DungeonRoom3D. Ensure all rooms supplied inherit DungeonRoom3D, and use the @tool annotation if generating in editor.")
return
if room_instances.find(room) != -1:
_fail_generation("custom_get_rooms_function supplied a room instance without cloning it. Always use DungeonRoom3D.create_clone_and_make_virtual_unless_visualizing() to create room instances.")
return
var rand_room : DungeonRoom3D
if _use_custom_rand_rooms:
rand_room = _custom_rand_rooms.pop_front()
else:
if get_rooms_less_than_max_count(false).size() > 0:
rand_room = get_randomly_positioned_room()
if rand_room:
place_room(rand_room)
if _use_custom_rand_rooms:
if _custom_rand_rooms.size() == 0:
stage += 1
else:
if get_rooms_less_than_min_count(false).size() == 0:
if _rooms_placed.size() == 0:
_fail_generation("Unable to place any rooms. Ensure min_count and max_count are set correctly on rooms.")
else:
stage += 1
# Placing stairs:
# Array [DungeonRoom3D, grid_pos_y] for where to place rooms
var _stair_rooms_and_placements = []
var _stair_rooms_placed_count = {}
func place_stairs_iteration(first_call_in_loop : bool) -> void:
if first_call_in_loop:
_stair_rooms_placed_count = {}
for s in get_stair_rooms_from_instances():
_stair_rooms_placed_count[s] = 0
_stair_rooms_and_placements = _make_and_solve_floors_graph()
if failed_to_generate:
return # solve failed and called abort
if _stair_rooms_and_placements.size() > 0:
var stair_and_y_pos = _stair_rooms_and_placements.pop_back()
_stair_rooms_placed_count[stair_and_y_pos[0]] += 1
var room = stair_and_y_pos[0].create_clone_and_make_virtual_unless_visualizing()
var y_pos = stair_and_y_pos[1]
room.room_rotations = rng.randi_range(0,4)
room.set_position_by_grid_pos(Vector3i(
rng.randi_range(0, dungeon_size.x - room.get_grid_aabbi(true).size.x),
y_pos,
rng.randi_range(0, dungeon_size.z - room.get_grid_aabbi(true).size.z)))
place_room(room)
elif get_stair_rooms_from_instances().filter(func(s): return s.min_count > _stair_rooms_placed_count[s]).size() > 0:
var stairs_less_than_min = get_stair_rooms_from_instances().filter(func(s): return s.min_count > _stair_rooms_placed_count[s])
var room_original = stairs_less_than_min[rng.randi() % stairs_less_than_min.size()]
_stair_rooms_placed_count[room_original] += 1
var room = room_original.create_clone_and_make_virtual_unless_visualizing()
room.room_rotations = rng.randi_range(0,4)
room.set_position_by_grid_pos(Vector3i(
rng.randi_range(0, dungeon_size.x - room.get_grid_aabbi(true).size.x),
rng.randi_range(0, dungeon_size.y - room.get_grid_aabbi(true).size.y),
rng.randi_range(0, dungeon_size.z - room.get_grid_aabbi(true).size.z)))
place_room(room)
else:
stage += 1
# Encapsulate some checks for stair room instances & how they can connect two or more floors.
class StairRoomInfo:
var inst : DungeonRoom3D
var stair_gaps = [] # Has door(s) leading 1 floors up, 2 floors up, etc.
var stair_gaps_dict = {} # Gap:local door y position. To check y offset to place room at when connecting floors.
var lowest_door_local_y_pos : int
var available_to_use : int # Make sure not to go above stair's max_count value
# Helper funcs for the stair chains where it might fail
var _saved_available_to_use : int = 0
func save_available_to_use():
_saved_available_to_use = available_to_use
func restore_available_to_use():
available_to_use =_saved_available_to_use
func _init(room_instance : DungeonRoom3D):
self.inst = room_instance
available_to_use = room_instance.max_count
var door_y_positions = []
for door in room_instance.get_doors_cached():
if not door.local_pos.y in door_y_positions:
door_y_positions.push_back(door.local_pos.y)
door_y_positions.sort()
stair_gaps_dict = {}
for i in range(0, len(door_y_positions)):
for j in range(i+1, len(door_y_positions)):
var gap = door_y_positions[j] - door_y_positions[i]
if not stair_gaps_dict.has(gap): stair_gaps_dict[gap] = []
stair_gaps_dict[gap].append(door_y_positions[i])
stair_gaps = stair_gaps_dict.keys()
# format: [[DungeonRoom3D, grid_pos_y], ...]. returns [] if no valid way to connect those floors
func get_valid_connect_positions(floor_1 : int, floor_2 : int) -> Array:
if available_to_use == 0: return []
var bottom_floor : int = min(floor_1, floor_2)
var top_floor : int = max(floor_1, floor_2)
var gap : int = top_floor - bottom_floor
if stair_gaps_dict.has(gap):
# Find actual valid pos from door local y positions, where doors spanning gap will land on floor_1 and floor_2
var valid_offsets = stair_gaps_dict[gap].filter(func(y_offset : int): return floor_1 - y_offset >= 0 and floor_1 - y_offset + inst.size_in_voxels.y <= inst.dungeon_generator.dungeon_size.y)
return valid_offsets.map(func(y_offset : int): return [inst, floor_1 - y_offset])
return []
# Returns empty array if couldn't find a chain of stair rooms to connect the two floors.
# The main case I want to solve here is just a simple chain of multiple 2 floor stairs to connect a more than 2 floor gap.
# There are a ton of edge cases, which I won't even check for.
# This is some heuristic algorithm I thought of which just tries to find a set of rooms which when chained together,
# continually closes the gap between floors. Should work for basic cases and also some more advanced ones.
func _find_stair_chain_to_connect_floors(floor_1 : int, floor_2 : int, floor_graph : TreeGraph, stair_info_dict : Dictionary) -> Array:
var stair_chain := []
var bottom_floor : int = min(floor_1, floor_2)
var top_floor : int = max(floor_1, floor_2)
var cur_floor := bottom_floor
for s in stair_info_dict.values():
s.save_available_to_use()
while cur_floor != top_floor:
# Valid floor = any floor which is closer to top_floor than cur_floor.
var valid_to_floors := range(dungeon_size.y).filter(func(floor : int): return abs(floor - top_floor) < abs(top_floor - cur_floor))
var valid_stair_placements := {}
for to_floor in valid_to_floors:
for stair_info in stair_info_dict.values():
var valid_connect = stair_info.get_valid_connect_positions(cur_floor, to_floor)
if valid_connect.size() > 0:
if not valid_stair_placements.has(to_floor): valid_stair_placements[to_floor] = []
valid_stair_placements[to_floor].append_array(valid_connect)
if valid_stair_placements.keys().size() == 0:
for s in stair_info_dict.values():
s.restore_available_to_use()
return [] # None found
var to_floor : int = valid_stair_placements.keys()[rng.randi() % valid_stair_placements.keys().size()]
var choose = valid_stair_placements[to_floor][rng.randi() % valid_stair_placements[to_floor].size()]
if not floor_graph.has_node(cur_floor) or not floor_graph.has_node(to_floor) or not floor_graph.are_nodes_connected(cur_floor, to_floor):
stair_chain.push_back(choose) # No need unless not connected already
stair_info_dict[choose[0]].available_to_use -= 1
cur_floor = to_floor
return stair_chain
# returns Array with elements in format [room : DungeonRoom3D, room_grid_pos_y : int]
func _make_and_solve_floors_graph() -> Array:
# Set up a tree (union find) graph of all the floors with doors on them.
# This keeps track of what floors are connected by rooms spanning multiple floors.
var floors_tree_graph := TreeGraph.new()
var add_room_to_floors_graph := (func(room : DungeonRoom3D, room_y_pos : int):
for door in room.get_doors_cached():
var door_exit_y = door.local_pos.y + room_y_pos
if not door_exit_y in floors_tree_graph.get_all_nodes():
floors_tree_graph.add_node(door_exit_y)
floors_tree_graph.connect_nodes(door_exit_y, room.get_doors_cached()[0].local_pos.y + room_y_pos))
var rooms = []
rooms.append_array(_rooms_placed)
rooms.append_array(get_preplaced_rooms())
for room in rooms:
add_room_to_floors_graph.call(room, room.get_grid_pos().y)
if not floors_tree_graph.is_fully_connected() and get_stair_rooms_from_instances().size() == 0:
_fail_generation("No stair rooms defined. Add a room with with is_stair_room set to true.")
return []
# Solve the stair graph. We'll use a heuristic algorithm.
# Tried to think of something elegant to do this but it may be harder than I thought.
# This should be fine for 99% of cases. For truly odd stair patterns in dungeons you can preplace stairs or mod yourself.
var stair_info_dict : Dictionary = {}
for room in get_stair_rooms_from_instances():
stair_info_dict[room] = StairRoomInfo.new(room)
var stairs_to_add : Array = [] # element format: [DungeonRoom3D, grid_pos_y]
while not floors_tree_graph.is_fully_connected():
# Simple case - look for when we can directly connect 2 floors with a stair
var stair_able_to_connect_floors_directly = []
for f1 in floors_tree_graph.get_all_nodes():
for f2 in floors_tree_graph.get_all_nodes().filter(func(_f2): return _f2 > f1):
if floors_tree_graph.are_nodes_connected(f1, f2): continue
for s in stair_info_dict.values():
# Just looking for 1 valid element [DungeonRoom3D, grid_pos_y]
stair_able_to_connect_floors_directly.append_array(s.get_valid_connect_positions(f1, f2))
if stair_able_to_connect_floors_directly.size() > 0:
break
if stair_able_to_connect_floors_directly.size() > 0: break
if stair_able_to_connect_floors_directly.size() > 0: break
if stair_able_to_connect_floors_directly.size() > 0:
var stair_and_pos = stair_able_to_connect_floors_directly[rng.randi() % stair_able_to_connect_floors_directly.size()]
stairs_to_add.push_back(stair_and_pos)
add_room_to_floors_graph.call(stair_and_pos[0], stair_and_pos[1])
stair_info_dict[stair_and_pos[0]].available_to_use -= 1
continue
# There are many cases where it's not possible to directly connect floors,
# but you could chain more than 1 stair room together to connect them.
# So here we'll do a non exhaustive search which attempts to cover most of these cases.
var stair_room_chain_to_connect_floors = []
for f1 in floors_tree_graph.get_all_nodes():
var nearest_floors_up = floors_tree_graph.get_all_nodes().filter(func(_f2): return _f2 > f1)
nearest_floors_up.sort_custom(func(fa,fb): return abs(fb - f1) > abs(fa - f1))
for f2 in nearest_floors_up:
if floors_tree_graph.are_nodes_connected(f1, f2): continue
stair_room_chain_to_connect_floors = _find_stair_chain_to_connect_floors(f1, f2, floors_tree_graph, stair_info_dict)
if stair_room_chain_to_connect_floors.size() > 0:
break
if stair_room_chain_to_connect_floors.size() > 0: break
if stair_room_chain_to_connect_floors.size() > 0:
for room_and_pos in stair_room_chain_to_connect_floors:
add_room_to_floors_graph.call(room_and_pos[0], room_and_pos[1])
stairs_to_add.push_back(room_and_pos)
continue
_fail_generation("Failed to connect all floors together with stairs. Ensure you have at least 1 DungeonRoom3D with 'is_stair_room' set to true with 2 or more doors leading different floors. Simplest is a 2 floor room with 1 door on each floor.")
_printerr("Stair algorithm failed. If your stairs are shaped very oddly it can fail, it's not an exhaustive search but should work for most cases. Also ensure stairs max_count is enough to connect all the floors in your dungeon.")
for s in stair_info_dict.values():
_printwarning("Room "+str(s.inst.name)+" max_count is "+str(s.inst.max_count)+". One potential reason this could fail is you need to increase the max_count on your stair room(s) so all floors can be connected.")
return []
return stairs_to_add
# Separating rooms:
var _aabbis_with_doors = {}
func separate_rooms_iteration(first_call_in_loop : bool) -> void:
var rooms = get_all_placed_and_preplaced_rooms()
if first_call_in_loop:
_aabbis_with_doors = {}
for room in rooms:
_aabbis_with_doors[room] = room.get_grid_aabbi(true)
var any_overlap := false
for i in range(0, len(rooms)):
for j in range(i + 1, len(rooms)):
var fast_check = _aabbis_with_doors[rooms[i]].intersects(_aabbis_with_doors[rooms[j]])
if fast_check and rooms[i].overlaps_room(rooms[j]):
if not rooms[i] in get_preplaced_rooms():
rooms[i].push_away_from_and_stay_within_bounds(rooms[j])
_aabbis_with_doors[rooms[i]] = rooms[i].get_grid_aabbi(true)
if not rooms[j] in get_preplaced_rooms():
rooms[j].push_away_from_and_stay_within_bounds(rooms[i])
_aabbis_with_doors[rooms[j]] = rooms[j].get_grid_aabbi(true)
any_overlap = true
if not any_overlap:
stage += 1
# Connecting rooms:
var _astar3d : DungeonAStar3D
var _quick_room_check_dict : Dictionary
var _quick_corridors_check_dict : Dictionary
var _non_corridor_rooms : Array
var _rooms_to_connect : Array
var _all_doors_dict : Dictionary
var _required_doors_dict : Dictionary
var _last_rooms_to_connect_counts := []
func connect_rooms_iteration(first_call_in_loop : bool) -> void:
if first_call_in_loop:
_rooms_to_connect = []
_last_rooms_to_connect_counts = []
_non_corridor_rooms = []
_quick_room_check_dict = {}
_quick_corridors_check_dict = {}
_all_doors_dict = {}
_required_doors_dict = {}
for room in get_all_placed_and_preplaced_rooms():
var doors = room.get_doors_cached()
if room.get_doors_cached().size() > 0:
_rooms_to_connect.push_back(room)
_non_corridor_rooms.push_back(room)
for door in doors:
if get_grid_aabbi().contains_point(door.exit_pos_grid):
_all_doors_dict[door.exit_pos_grid] = door
if not door.optional:
_required_doors_dict[door.exit_pos_grid] = door
var aabbi = room.get_grid_aabbi(false)
for x in aabbi.size.x: for y in aabbi.size.y: for z in aabbi.size.z:
_quick_room_check_dict[aabbi.position + Vector3i(x,y,z)] = room
# Init after corridors/room dict setup
_astar3d = DungeonAStar3D.new(self, _quick_room_check_dict, _quick_corridors_check_dict)
# Sort rooms by door y positions. Likely to make astar connections more stable
_rooms_to_connect.sort_custom(func(a,b): return b.get_doors_cached()[0].exit_pos_grid.y > a.get_doors_cached()[0].exit_pos_grid.y)
# Can exit early if _rooms_to_connect stops going down, aka we tried shuffling and couldn't find a connection
# Don't think this is necessary actually, I just exit below instead if it fails ever.
#_last_rooms_to_connect_counts.push_front(len(_rooms_to_connect))
#_last_rooms_to_connect_counts = _last_rooms_to_connect_counts.filter(func(c): return c == _last_rooms_to_connect_counts[0])
#if len(_last_rooms_to_connect_counts) > (len(_rooms_to_connect) * 4) and _last_rooms_to_connect_counts[0] == len(_rooms_to_connect):
#_fail_generation("Unable to connect all rooms")
#return
# First, just pathfind through all the rooms, one to the next, until all rooms are connected
if len(_rooms_to_connect) >= 2:
var room_0_pos = _rooms_to_connect[0].get_grid_aabbi(false).position
var room_1_pos = _rooms_to_connect[1].get_grid_aabbi(false).position
var connect_path := _astar3d.get_vec3i_path(room_0_pos, room_1_pos)
var room_a := _rooms_to_connect.pop_front()
if len(connect_path) == 0:
#print("Failed somehow")
#_rooms_to_connect.insert(rng.randi_range(1, len(_rooms_to_connect)), room_a)
_fail_generation("Failed to fully connect dungeon rooms with corridors.")
return
#print("Connecting ", room_a.name, " to ", _rooms_to_connect[0].name, ". Result: ", connect_path)
for corridor_pos in connect_path:
if not _quick_room_check_dict.has(corridor_pos) and not _quick_corridors_check_dict.has(corridor_pos):
var room := corridor_room_instance.create_clone_and_make_virtual_unless_visualizing()
room.set_position_by_grid_pos(corridor_pos)
place_room(room)
_quick_corridors_check_dict[corridor_pos] = room
return
# Next, not all the doors may be connected, so we have to do some strategy to nicely connect the required doors remaining.
for required_door in _required_doors_dict.values().slice(0): # Slice necessary? Not sure.
# No need to connect doors which already have a corridor
if _quick_room_check_dict.has(required_door.exit_pos_grid) or _quick_corridors_check_dict.has(required_door.exit_pos_grid):
_required_doors_dict.erase(required_door)
continue
# Get other room doors which are closest to the required door
var closest_other_room_doors = _all_doors_dict.values().slice(0)#filter(func(d): return d.room != required_door.room)
closest_other_room_doors.sort_custom(func(a,b):
# Make sure doors not on same floor sorted far after
# Also make sure doors of same room sorted far after
var b_dist = Vector3(b.exit_pos_grid - required_door.exit_pos_grid).length()
var a_dist = Vector3(a.exit_pos_grid - required_door.exit_pos_grid).length()
if a.exit_pos_grid.y != required_door.exit_pos_grid.y or a.room == required_door.room:
a_dist += dungeon_size.x + dungeon_size.y + dungeon_size.z
if b.exit_pos_grid.y != required_door.exit_pos_grid.y or b.room == required_door.room:
b_dist += dungeon_size.x + dungeon_size.y + dungeon_size.z
return b_dist > a_dist)
_astar3d.cap_required_doors_phase = true
var connect_path := _astar3d.get_vec3i_path(required_door.exit_pos_grid, closest_other_room_doors[0].exit_pos_grid)
for corridor_pos in connect_path:
if not _quick_room_check_dict.has(corridor_pos) and not _quick_corridors_check_dict.has(corridor_pos):
var room := corridor_room_instance.create_clone_and_make_virtual_unless_visualizing()
room.set_position_by_grid_pos(corridor_pos)
place_room(room)
_quick_corridors_check_dict[corridor_pos] = room
_required_doors_dict.erase(required_door)
return
stage = BuildStage.FINALIZING
####################################
## DUNGEON BUILD HELPER FUNCTIONS ##
####################################
func get_rooms_less_than_min_count(include_stairs : bool):
return room_instances.filter(func(room : DungeonRoom3D):
if not include_stairs and room.is_stair_room:
return false
var already_in_tree = _rooms_placed.filter(func(placed_room : DungeonRoom3D):
return room.get_original_packed_scene() == placed_room.get_original_packed_scene())
return len(already_in_tree) < room.min_count)
func get_rooms_less_than_max_count(include_stairs : bool):
return room_instances.filter(func(room : DungeonRoom3D):
if not include_stairs and room.is_stair_room:
return false
var already_in_tree = _rooms_placed.filter(func(placed_room : DungeonRoom3D):
return room.get_original_packed_scene() == placed_room.get_original_packed_scene())
return len(already_in_tree) < room.min_count)
func get_randomly_positioned_room() -> DungeonRoom3D:
var room : DungeonRoom3D = get_rooms_less_than_max_count(false)[rng.randi() % get_rooms_less_than_max_count(false).size()]
room = room.create_clone_and_make_virtual_unless_visualizing()
var buf := dungeon_size - room.get_grid_aabbi(false).size
var rand_pos : Vector3i = Vector3i(rng.randi_range(0, buf.x), rng.randi_range(0, buf.y), rng.randi_range(0, buf.z))
room.room_rotations = rng.randi_range(0,3)
room.set_position_by_grid_pos(rand_pos)
return room
func place_room(room : DungeonRoom3D) -> void:
room.dungeon_generator = self # Incase wasn't set yet
_rooms_placed.push_back(room)
if visualize_generation_progress:
rooms_container.add_child(room)
room.snap_room_to_dungeon_grid()
###################################
## INITIALIZE/CLEANUP GENERATION ##
###################################
func create_or_recreate_rooms_container() -> void:
if rooms_container and is_instance_valid(rooms_container):
if rooms_container.is_inside_tree():
rooms_container.get_parent().remove_child(rooms_container)
rooms_container.queue_free()
if get_node_or_null("RoomsContainer"): # Incase it's still a child if rooms_container was null somehow
var rc = get_node_or_null("RoomsContainer")
remove_child(rc)
rc.queue_free()
rooms_container = Node3D.new()
rooms_container.name = "RoomsContainer"
if visualize_generation_progress:
add_child(rooms_container)
func clear_rooms_container_and_setup_for_next_iteration():
if visualize_generation_progress:
for c in rooms_container.get_children():
c.queue_free()
rooms_container.remove_child(c)
else:
for room in _rooms_placed:
room.queue_free()
_rooms_placed = []
# Clears the room & corridor instances pool, unless they are in tree.
# For flexibility checking if in tree
# Room instances are used as a library to run checks on,
# but could allow placing them as last room of their kind to save some memory/an instantiate call.
func _clear_room_instances() -> void:
for room in room_instances:
if room and is_instance_valid(room):
room.queue_free()
if corridor_room_instance and is_instance_valid(corridor_room_instance):
corridor_room_instance.queue_free()
room_instances = []
corridor_room_instance = null
func cleanup_and_reset_dungeon_generator() -> void:
if is_currently_generating:
_fail_generation("Dungeon reset while generating.")
if running_thread:
if running_thread.is_alive() or running_thread.is_started():
running_thread.wait_to_finish()
running_thread = null
if get_node_or_null("RoomsContainer"):
var rc = get_node_or_null("RoomsContainer")
remove_child(rc)
rc.queue_free()
_clear_room_instances()
failed_to_generate = false
full_abort_triggered = false
iterations = 0
retry_attempts = 0
stage = BuildStage.NOT_STARTED
func setup_room_instances_and_validate_before_generate() -> bool:
if not room_scenes:
_printerr("SimpleDungeons Error: No DungeonRoom3D room scenes set.")
return false
var inst_arr : Array[DungeonRoom3D] = []
for s in room_scenes:
if not s: continue
var inst = s.instantiate()
if not inst is DungeonRoom3D:
_printerr("SimpleDungeons Error: "+s.resource_path+" room scene does not inherit DungeonRoom3D. Also may need @tool annotation if generating in editor.")
return false
else:
inst.dungeon_generator = self
# Need to save door info before cloning/making virtual copies w/o actual nodes/meshes inside for performance
inst.ensure_doors_and_or_transform_cached_for_threads_and_virtualized_rooms()
inst_arr.append(inst as DungeonRoom3D)
room_instances = inst_arr
corridor_room_instance = corridor_room_scene.instantiate() if corridor_room_scene else null
if corridor_room_instance:
#corridor_room_instance.dungeon_generator = self
corridor_room_instance.set("dungeon_generator", self)
#corridor_room_instance.ensure_doors_and_or_transform_cached_for_threads_and_virtualized_rooms()
return validate_dungeon()
####################
## UTIL FUNCTIONS ##
####################
# printerr() and push_warning() eat my outputs a lot. Regular prints are more reliable.
func _printerr(str : String, str2 : String = "", str3 : String = "", str4 : String = ""):
print_rich("[color=#FF3531]"+(str+str2+str3+str4)+"[/color]")
func _printwarning(str : String, str2 : String = "", str3 : String = "", str4 : String = ""):
print_rich("[color=#FFF831]"+(str+str2+str3+str4)+"[/color]")
func get_grid_aabbi() -> AABBi:
return AABBi.new(Vector3i(0,0,0), dungeon_size)
func get_room_at_pos(grid_pos : Vector3i) -> DungeonRoom3D:
if stage > BuildStage.CONNECT_ROOMS:
# Can use these vars for speedup if past the connect rooms stage where we set them
var quick_check = _quick_room_check_dict.get(grid_pos)
return quick_check if quick_check else _quick_corridors_check_dict.get(grid_pos)
for room in get_all_placed_and_preplaced_rooms():
if room.get_grid_aabbi(false).contains_point(grid_pos):
return room
return null
var _preplaced_rooms_cached : Array = []
func get_preplaced_rooms() -> Array:
var rooms := []
if OS.get_thread_caller_id() != OS.get_main_thread_id():
return _preplaced_rooms_cached.slice(0)
else:
rooms.assign(get_children().filter(func(c): return c is DungeonRoom3D))
_preplaced_rooms_cached = rooms.slice(0)
return rooms
func get_all_placed_and_preplaced_rooms() -> Array:
var rooms := get_preplaced_rooms()
rooms.append_array(_rooms_placed)
return rooms
# Any rooms with doors leading to more than 1 floor are considered stairs
func get_stair_rooms_from_instances() -> Array:
var rooms := []
rooms.assign(room_instances.filter(func(c):
if not c is DungeonRoom3D: return false
if not c.is_stair_room: return false
var floors_dict = {}
for door in c.get_doors_cached():
floors_dict[door.local_pos.y] = true
return floors_dict.keys().size() >= 2))
return rooms
################
## VALIDATION ##
################
# Returns true if no errors found before generating.
# Calls callbacks with warning/error string if any.
# Upon generate, also calls validation checks on each of the rooms.
func validate_dungeon(error_callback = null, warning_callback = null) -> bool:
# printerr and push_warning do not always work. Only print() seems to reliably output.
if not warning_callback is Callable: warning_callback = (func(str): _printwarning("SimpleDungeons Warning: ", str))
if not error_callback is Callable: error_callback = (func(str): _printerr("SimpleDungeons Error: ", str))
var any_errors : = {"err": false} # So lambda closure captures
error_callback = (func(str): any_errors["err"] = true; error_callback.call(str))
if not corridor_room_scene:
error_callback.call("No corridor room scene set. Add a 1x1x1 (in voxels) corridor room scene.")
if room_scenes.size() == 0:
error_callback.call("No rooms added. Add DungeonRoom scenes to the room_scenes property.")
if not is_currently_generating:
return not any_errors["err"]
# Build stage checks:
if not room_instances is Array or len(room_instances) == 0:
error_callback.call("No rooms added. Cannot generate dungeon.")
if room_instances is Array:
for room in room_instances:
if not room is DungeonRoom3D:
error_callback.call("Room "+room.name+" does not inherit DungeonRoom3D! Also add @tool to dungeon room script if generating in editor.")
else:
if not room.has_method("validate_room"):
error_callback.call("validate_room() method not found on room "+room.name+". Ensure it inherits DungeonRoom3D and has the @tool annotation if you're trying to generate in editor.")
room.validate_room(error_callback, warning_callback)
if not corridor_room_instance is DungeonRoom3D:
var corridor_name = corridor_room_instance.name if corridor_room_instance else "null"
error_callback.call("Corridor Room "+corridor_name+" does not inherit DungeonRoom3D!")
if corridor_room_instance is DungeonRoom3D and corridor_room_instance.size_in_voxels != Vector3i(1,1,1):
error_callback.call("Corridor Room must be 1x1x1 in voxels.")
if corridor_room_instance and not corridor_room_instance.has_method("get_doors"):
error_callback.call("get_doors() method not found on the corridor room. Ensure it inherits DungeonRoom3D and has the @tool annotation if you're trying to generate in editor.")
if corridor_room_instance is DungeonRoom3D and (corridor_room_instance.get_doors().size() != 4 or corridor_room_instance.get_doors().any(func(d): return not d.optional)):
error_callback.call("Corridor Room must have 4 optional doors")
if corridor_room_instance is DungeonRoom3D:
corridor_room_instance.validate_room(error_callback, warning_callback)
if corridor_room_instance.size_in_voxels != Vector3i(1,1,1):
error_callback.call("Corridor room scene must be 1x1x1 in voxels. It is used to connect the rooms with hallways.")
return not any_errors["err"]

View File

@@ -1 +0,0 @@
uid://b1x125h0tya2w

View File

@@ -1,423 +0,0 @@
@tool
class_name DungeonRoom3D
extends Node3D
signal dungeon_done_generating()
var dungeon_generator : DungeonGenerator3D :
get:
if is_inside_tree() and get_parent() is DungeonGenerator3D:
return get_parent()
else: return dungeon_generator
@export var size_in_voxels := Vector3i(1,1,1) :
set(v):
size_in_voxels = v.clamp(Vector3i(1,1,1),Vector3i(9999,9999,9999))
@export var voxel_scale := Vector3(10,10,10) :
set(v):
voxel_scale = v.clamp(Vector3(0.0001,0.0001,0.0001),Vector3(9999,9999,9999))
@export var min_count : int = 2
@export var max_count : int = 5
## Stair rooms are used in the floor connection stage and will have their min/max counts ignored.
## For the dungeon to generate, you must mark at least 1 room as a stair and have its doors span multiple floors.
@export var is_stair_room := false
## Preplaced rooms are immovable rooms you can place at a preset position in the dungeon
@export_group("Pre-placed room options")
## Editor button to align the room's position & scale with the dungeon's voxel grid.
## Will be called before generation to ensure room is aligned with grid.
@export var force_align_with_grid_button := false :
set(_v):
virtual_transform = self.transform
snap_room_to_dungeon_grid()
func _validate_property(property: Dictionary):
if property.name in ["force_align_with_grid_button"] and not get_parent() is DungeonGenerator3D:
property.usage = PROPERTY_USAGE_NO_EDITOR
@export_group("Debug view")
@export var show_debug_in_editor : bool = true
@export var show_debug_in_game : bool = false
## For internal debug use only
@export var show_grid_aabb_with_doors : bool = false
var was_preplaced = false :
get:
if Engine.is_editor_hint():
return is_inside_tree() and get_parent() is DungeonGenerator3D
else: return was_preplaced
## Number of 90 degree rotations around the y axis
var room_rotations : int :
set(v): virtual_transform.basis = Basis.from_euler(Vector3(0,wrapi(v, 0, 4) * deg_to_rad(90.0),0)).scaled(virtual_transform.basis.get_scale())
get: return round(wrapi(round(virtual_transform.basis.get_euler().y / deg_to_rad(90.0)), 0, 4))
# For performance, we should not spawn/instantiate many dungeon rooms.
# This is because the dungeon generator may have to restart multiple times.
# We don't want to have to delete and restart all nodes/models/etc we instantiated
# Also, we cannot access the scene tree from other threads.
# So instead, we will create virtual DungeonRoom3Ds which reference the single original we create.
# Then, we can still encapsulate all the logic into this one class & improve performance.
var virtualized_from : DungeonRoom3D = null
# To be called instead of self on anything that requires transform or node access
# I think just in getting door nodes is the only part we need this.
var virtual_self : DungeonRoom3D = self :
get: return virtualized_from if virtualized_from else self
# Cannot access the transform in threads at all so just saving it here and copying on unvirtualize.
var virtual_transform : Transform3D = Transform3D() :
set(v):
virtual_transform = v
if is_inside_tree() and OS.get_main_thread_id() == OS.get_thread_caller_id():
self.transform = v
var original_ready_func_called := false # For error checking. Ensure noone inherits this class w/o calling _ready.
func _ready():
original_ready_func_called = true
if not virtualized_from:
add_debug_view_if_not_exist()
# When spawning a room, make sure to set its transform to whatever its virtual/out of tree transform was set to.
if virtual_transform != Transform3D():
self.transform = virtual_transform
elif self.transform != Transform3D():
# For preplaced rooms:
virtual_transform = self.transform
# Mostly for debug and pre placed rooms:
if get_parent() is Node3D:
if get_parent() is DungeonGenerator3D:
dungeon_generator = get_parent()
if get_parent().get_parent() is DungeonGenerator3D:
dungeon_generator = get_parent().get_parent()
if Engine.is_editor_hint():
return
func _process(delta):
if Engine.is_editor_hint():
return
const _dungeon_room_export_props_names = ["size_in_voxels", "voxel_scale", "min_count", "max_count", "is_stair_room", "show_debug_in_editor", "show_debug_in_game", "show_grid_aabb_with_doors"]
func copy_all_props(from : DungeonRoom3D, to : DungeonRoom3D) -> void:
for prop in _dungeon_room_export_props_names:
if from.get(prop) != to.get(prop):
to.set(prop, from.get(prop))
to.name = from.name
to.dungeon_generator = from.dungeon_generator
func get_original_packed_scene() -> PackedScene:
# Try to find the room packed scene in the DungeonGenerator3D so we don't have to load()
if dungeon_generator:
if dungeon_generator.corridor_room_scene.resource_path == virtual_self.scene_file_path:
return dungeon_generator.corridor_room_scene
for scene in dungeon_generator.room_scenes:
if scene.resource_path == virtual_self.scene_file_path:
return scene
# Fall back to just getting it from the scene path just incase
if virtual_self.scene_file_path:
return load(virtual_self.scene_file_path)
printerr(self.name+" Could not find DungeonRoom3D's original packed scene. This shouldn't happen. Are you manually spawning rooms?")
return null
func create_clone_and_make_virtual_unless_visualizing() -> DungeonRoom3D:
var make_clone_virtual : bool = true
if dungeon_generator and dungeon_generator.visualize_generation_progress:
make_clone_virtual = false
var _clone
if make_clone_virtual:
#if not self.virtual_self._doors_cache:
# printerr("Cloning dungeon room without doors cached!!! Make sure to call .get_doors() at least once for all rooms.")
_clone = DungeonRoom3D.new()
_clone.virtualized_from = self.virtual_self
# Can't access door nodes on threads, also if it's a clone, won't have any door nodes to check door pos/dir.
#_clone._doors_cache = []
for d in _doors_cache:
_clone._doors_cache.push_back(Door.new(d.local_pos, d.dir, d.optional, _clone, d.door_node))
else: _clone = get_original_packed_scene().instantiate()
copy_all_props(virtual_self, _clone)
_clone.dungeon_generator = self.dungeon_generator
var name = virtual_self.name
if dungeon_generator:
name = name + "_" + str(len(dungeon_generator._rooms_placed))
_clone.name = name
return _clone
# Spawn the real room into the DungeonGenerator
func unvirtualize_and_free_clone_if_needed(into_parent : Node3D) -> DungeonRoom3D:
if not virtualized_from:
#if self.get_parent() != into_parent:
#if self.get_parent() != null:
#self.get_parent().remove_child(self)
#into_parent.add_child(self)
return self
# Can't do this threaded anyway for virtualized so it's implied there will be no parent
#var parent = self.get_parent()
#parent.remove_child(self)
self.queue_free()
var inst : DungeonRoom3D = get_original_packed_scene().instantiate()
copy_all_props(self, inst)
inst.transform = self.virtual_transform
into_parent.add_child(inst)
inst.owner = into_parent.owner
return inst
var _debug_view = null
func add_debug_view_if_not_exist():
if not _debug_view:
_debug_view = preload("res://addons/SimpleDungeons/debug_visuals/DungeonRoom3DDebugView.gd").new()
add_child(_debug_view)
###########
## DOORS ##
###########
## A class that represent a door on a dungeon room
## TODO probably remove this 'dir' variable as it is just confusing now that we have rotations
class Door:
var local_pos : Vector3i
var grid_pos : Vector3i :
get: return room.local_grid_pos_to_dungeon_grid_pos(local_pos)
var exit_pos_local : Vector3i :
get: return local_pos + Vector3i(DungeonUtils.DIRECTION_VECTORS[dir])
var exit_pos_grid : Vector3i :
get: return room.local_grid_pos_to_dungeon_grid_pos(exit_pos_local)
var dir : DungeonUtils.Direction
var optional : bool
var room : DungeonRoom3D
var door_node : Node3D
func _init(local_pos : Vector3, dir : DungeonUtils.Direction, optional : bool, room : DungeonRoom3D, door_node : Node3D):
self.local_pos = Vector3i(local_pos.round())
self.dir = dir
self.optional = optional
self.room = room
self.door_node = door_node
func fits_other_door(other_room_door : Door) -> bool:
return other_room_door.exit_pos_grid == grid_pos and other_room_door.grid_pos == exit_pos_grid
func find_duplicates() -> Array:
return room.get_doors().filter(func (d : Door): return d.exit_pos_local == exit_pos_local and d.local_pos == local_pos)
func validate_door() -> bool:
if not AABBi.new(Vector3i(), room.size_in_voxels).contains_point(local_pos):
return false
if AABBi.new(Vector3i(), room.size_in_voxels).contains_point(exit_pos_local):
return false
if find_duplicates().size() > 1:
return false
return true
func get_room_leads_to() -> DungeonRoom3D:
var other_room = room.dungeon_generator.get_room_at_pos(exit_pos_grid)
if other_room == null: return null
for door in other_room.get_doors():
if fits_other_door(door):
return other_room
return null
func get_door_nodes() -> Array[Node3D]:
var doors : Array[Node3D] = [] # .assign typecast workaround https://github.com/godotengine/godot/issues/72566
doors.assign(virtual_self.find_children("DOOR*", "Node3D"))
return doors
func get_door_by_node(node : Node) -> Door:
for door in get_doors():
if door.door_node == node:
return door
return null
# For calling on other threads/for virtualized rooms
func get_doors_cached() -> Array:
return self._doors_cache
func ensure_doors_and_or_transform_cached_for_threads_and_virtualized_rooms() -> void:
if is_inside_tree(): # transform cache only applies to preplaced rooms
virtual_transform = self.transform
get_doors()
# For some reason this mutex is required or I get crashes all over the place on threads.
# I never access _doors_cache from main thread so maybe it's overly sensitive thread guards.
var _thread_fix_mutex := Mutex.new()
var _doors_cache : Array = [] :
set(v):
_thread_fix_mutex.lock()
_doors_cache = v
_thread_fix_mutex.unlock()
get:
_thread_fix_mutex.lock()
var tmp = _doors_cache
_thread_fix_mutex.unlock()
return tmp
func get_doors() -> Array:
if OS.get_thread_caller_id() != OS.get_main_thread_id() or virtualized_from != null:
# Ensure using get_doors_cached() when dealing with virtual rooms/threads.
return _doors_cache
var real_aabb_local = get_local_aabb()
var room_doors = []
for door in get_door_nodes():
# Get door pos from min corner of aabb, then divide by the full aabb size.
var door_pos_pct_across = (get_transform_rel_to(door, self).origin - real_aabb_local.position) / real_aabb_local.size
# Snap door pos to the grid square it's in
var door_pos_grid = (door_pos_pct_across * Vector3(size_in_voxels)).floor()
door_pos_grid = door_pos_grid.clamp(Vector3(0,0,0), Vector3(size_in_voxels) - Vector3(1,1,1))
var grid_center_pct_across = (door_pos_grid + Vector3(0.5,0.5,0.5)) / Vector3(size_in_voxels)
# Find the door direction by the its vector from the grid square's center point
var door_dir := DungeonUtils.vec3_to_direction(door_pos_pct_across - grid_center_pct_across)
var door_obj := Door.new(door_pos_grid, door_dir, door.name.begins_with("DOOR?"), self, door)
room_doors.push_back(door_obj)
_doors_cache = room_doors
return room_doors
#######################
## UTILITY FUNCTIONS ##
#######################
func push_away_from_and_stay_within_bounds(other_room : DungeonRoom3D) -> void:
var diff := other_room.virtual_transform.origin - self.virtual_transform.origin
var move := Vector3i(
-1 if diff.x > 0 else 1,
0,
-1 if diff.z > 0 else 1)
var dpos = get_grid_aabbi(true)
var able_to_move = dpos.translated(move).push_within(dungeon_generator.get_grid_aabbi(), true).position - dpos.position
if able_to_move.x != 0 or able_to_move.z != 0:
set_position_by_grid_pos(get_grid_pos() + able_to_move)
func overlaps_room(other_room : DungeonRoom3D) -> bool:
var aabbis = { self: self.get_grid_aabbi(false), other_room: other_room.get_grid_aabbi(false) }
if aabbis[self].intersects(aabbis[other_room]): return true
# Separate with a margin for doors, but allow if 2 opposing doors fit together
var door_intersects = (func(door : Door, room : DungeonRoom3D):
# Optional doors can intersect but not if stair room
if door.optional and not door.room.is_stair_room: return false
if not aabbis[room].contains_point(door.exit_pos_grid): return false
return not room.get_doors().any(func(_d): return _d.fits_other_door(door)))
if other_room.get_doors().any(door_intersects.bind(self)): return true
if get_doors().any(door_intersects.bind(other_room)): return true
return false
func snap_room_to_dungeon_grid() -> void:
if not dungeon_generator:
return
snap_rotation_and_scale_to_dungeon_grid()
set_position_by_grid_pos()
constrain_room_to_bounds_with_doors()
func constrain_room_to_bounds_with_doors():
# For stair rooms, also ensure optional doors can be reached. So stairs don't get their path blocked against wall
var aabbi_with_doors := get_grid_aabbi(true)
var aabbi_with_doors_constrained := aabbi_with_doors.push_within(dungeon_generator.get_grid_aabbi(), false)
set_position_by_grid_pos(get_grid_pos() + (aabbi_with_doors_constrained.position - aabbi_with_doors.position))
## Room must be scaled so voxel scale matches the DungeonGenerator3D's voxel scale.
func snap_rotation_and_scale_to_dungeon_grid() -> void:
virtual_transform = Transform3D(Basis().rotated(Vector3(0,1,0), self.room_rotations * deg_to_rad(90.0)).scaled(dungeon_generator.voxel_scale / voxel_scale), virtual_transform.origin)
## Returns room pos from corner (min) of AABB on dungeon grid
func get_grid_pos() -> Vector3i:
return get_grid_aabbi(false).position
## Set position of room from corner (min) of AABB on dungeon grid
func set_position_by_grid_pos(new_grid_pos : Vector3i = get_grid_pos()) -> void:
if not dungeon_generator: printerr("set_position_by_grid_pos: No dungeon_generator set on DungeonRoom3D")
var cur_aabb := xform_aabb(get_local_aabb(), get_xform_to(SPACE.LOCAL_SPACE, SPACE.DUNGEON_GRID))
cur_aabb.position = Vector3(new_grid_pos)
cur_aabb = xform_aabb(cur_aabb, get_xform_to(SPACE.DUNGEON_GRID, SPACE.DUNGEON_SPACE))
virtual_transform.origin = cur_aabb.get_center()
# Probably a good idea to have this in. Hopping between local/dungeon space, grid, and editor positions
enum SPACE { LOCAL_GRID = 0, LOCAL_SPACE = 1, DUNGEON_SPACE = 2, DUNGEON_GRID = 3 }
func get_xform_to(from : SPACE, to : SPACE) -> Transform3D:
var t = Transform3D()
var inv := to < from
if inv:
var tmp = to; to = from; from = tmp
if from <= SPACE.LOCAL_GRID and to >= SPACE.LOCAL_SPACE:
t = Transform3D(Basis().scaled(voxel_scale), self.get_local_aabb().position) * t
if from <= SPACE.LOCAL_SPACE and to >= SPACE.DUNGEON_SPACE:
t = virtual_transform * t
if from <= SPACE.DUNGEON_SPACE and to >= SPACE.DUNGEON_GRID and dungeon_generator:
t = Transform3D(Basis().scaled(dungeon_generator.voxel_scale.inverse()), Vector3(dungeon_generator.dungeon_size)/2.0) * t
return t.affine_inverse() if inv else t
## Different behavior than Godot's transform * AABB. This properly scales too. Regular just does position & rotation.
func xform_aabb(aabb : AABB, xform : Transform3D) -> AABB:
var pos := xform * aabb.position
var end := xform * aabb.end
return AABB(pos, end - pos).abs()
func xform_aabbi(aabbi : AABBi, xform : Transform3D) -> AABBi:
return AABBi.from_AABB_rounded(xform_aabb(aabbi.to_AABB(), xform))
func get_local_aabb() -> AABB:
var size := Vector3(size_in_voxels) * voxel_scale
return AABB(-size/2.0, size)
func get_grid_aabbi(include_doors : bool) -> AABBi:
# For stair rooms, all doors are counted for AABB. Generally even if they are optional, want to guarantee able to walk out of them to connect floors
var grid_aabbi = AABBi.from_AABB_rounded(xform_aabb(get_local_aabb(), get_xform_to(SPACE.LOCAL_SPACE, SPACE.DUNGEON_GRID)))
if include_doors: # Include doors after to keep position the same
for door in get_doors().filter(func(d : Door): return !d.optional or self.is_stair_room):
grid_aabbi = grid_aabbi.expand_to_include(door.exit_pos_grid)
return grid_aabbi
func local_grid_pos_to_dungeon_grid_pos(local_pos : Vector3i) -> Vector3i:
# It will be rounded in wrong direction when rotated 180 going to dungeon space.
# So use middle of local grid and .floor() instead of .round() at end.
var transformed_middle := get_xform_to(SPACE.LOCAL_GRID, SPACE.DUNGEON_GRID) * (Vector3(local_pos) + Vector3(0.5,0.5,0.5))
return Vector3i(transformed_middle.floor())
# Can't use global_transform when it's not actually in the scene tree.
# Just using this for doors.
func get_transform_rel_to(child_node : Node3D, parent_node : Node3D) -> Transform3D:
var transform = Transform3D()
while child_node != parent_node:
transform = child_node.transform * transform
if child_node.get_parent() is Node3D: child_node = child_node.get_parent()
else: break
return transform
################
## VALIDATION ##
################
# printerr() and push_warning() eat my outputs a lot. Regular prints are more reliable.
func _printerr(str : String, str2 : String = "", str3 : String = "", str4 : String = ""):
print_rich("[color=#FF3531]"+(str+str2+str3+str4)+"[/color]")
func _printwarning(str : String, str2 : String = "", str3 : String = "", str4 : String = ""):
print_rich("[color=#FFF831]"+(str+str2+str3+str4)+"[/color]")
# Returns true if no errors found before generating.
# Calls callbacks with warning/error string if any.
func validate_room(error_callback = null, warning_callback = null) -> bool:
if not warning_callback is Callable: warning_callback = (func(str): _printwarning(str))
if not error_callback is Callable: error_callback = (func(str): _printerr(str))
var any_errors := {"err": false} # So lambda closure captures
error_callback = func(str): any_errors["err"] = true; error_callback.call(str)
var doors = get_doors()
if doors.size() == 0:
warning_callback.call("Room "+self.name+" has no doors defined. Room will be unreachable. Add doors by creating Node3Ds with names prefixed with DOOR or DOOR? for optional doors.")
if not doors.all(func(d : Door): return d.validate_door()):
error_callback.call("Room "+self.name+" has one or more invalid doors.")
if doors.any(func(d : Door): return d.find_duplicates().size() > 1):
error_callback.call("Room "+self.name+" has one or more overlapping/duplicate doors.")
var unique_door_y = doors.reduce(func(acc : Dictionary, d : Door):
acc[d.local_pos.y] = true
return acc, {})
if is_stair_room and unique_door_y.keys().size() < 2:
error_callback.call("Room "+self.name+" is set as is_stair_room but does not have doors leading to 2 or more floors.")
# Post instantiate/place checks:
if not dungeon_generator:
return not any_errors["err"]
if not self.scale.is_equal_approx(Vector3(1,1,1)):
warning_callback.call("Room "+self.name+"'s root node scale should be set to Vector3(1,1,1). Will be scaled during generation.")
if voxel_scale != dungeon_generator.voxel_scale:
warning_callback.call("Room "+self.name+"'s voxel scale does not match DungeonGenerator3D voxel size. Room will be scaled to match.")
if size_in_voxels != size_in_voxels.clamp(Vector3i(0,0,0), dungeon_generator.dungeon_size):
error_callback.call("Room "+self.name+" is too big for the DungeonGenerator3D!")
return not any_errors["err"]

View File

@@ -1 +0,0 @@
uid://ce73fuh74l81l

View File

@@ -1,69 +0,0 @@
class_name DungeonUtils
extends Node
enum Direction { LEFT = 0, RIGHT = 1, FRONT = 2, BACK = 3 }
const NEGATE_DIRECTION = {
Direction.LEFT: Direction.RIGHT, Direction.RIGHT: Direction.LEFT,
Direction.FRONT: Direction.BACK, Direction.BACK: Direction.FRONT
}
const DIRECTION_VECTORS = {
Direction.LEFT: Vector3(-1,0,0), Direction.RIGHT: Vector3(1,0,0),
Direction.FRONT: Vector3(0,0,1), Direction.BACK: Vector3(0,0,-1),
}
static func vec3_to_direction(vec : Vector3) -> Direction:
var closest = Direction.LEFT
var closest_dot = -INF
for dir in DIRECTION_VECTORS.keys():
var dir_vec = DIRECTION_VECTORS[dir]
if dir_vec.dot(vec.normalized()) > closest_dot:
closest_dot = dir_vec.dot(vec.normalized())
closest = dir
return closest
#static func rotate_vec3i(vec : Vector3i, angle : float, axis : Vector3 = Vector3.UP) -> Vector3i:
#for i in wrapi(cc_90_turns, 0, 4): vec = Vector3i(-vec.z, vec.y, vec.x)
# return vec
static func _make_set(arr: Array) -> Array:
var unique_elements = []
for element in arr:
if element not in unique_elements:
unique_elements.append(element)
return unique_elements
static func _flatten(arr: Array, depth: int = 1) -> Array:
var result = Array()
for element in arr:
if element is Array and depth > 0:
result += _flatten(element, depth - 1)
else:
result.append(element)
return result
static func _vec3i_min(a : Vector3i, b : Vector3i) -> Vector3i:
return Vector3i(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z))
static func _vec3i_max(a : Vector3i, b : Vector3i) -> Vector3i:
return Vector3i(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z))
class DungeonFloorAStarGrid2D extends AStarGrid2D:
var corridors = [] as Array[Vector2i]
func _init(dungeon_size : Vector3i, all_dungeon_rooms : Array, floor : int):
self.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
self.region = Rect2i(0, 0, dungeon_size.x, dungeon_size.z)
self.update()
for x in dungeon_size.x:
for z in dungeon_size.z:
if all_dungeon_rooms.any(func(_r): return _r.get_aabb_in_grid().has_point(Vector3(x + 0.5,floor + 0.5,z + 0.5))):
set_point_solid(Vector2i(x,z))
#
func _compute_cost(from : Vector2i, to : Vector2i):
return 0 if corridors.has(to) else self.cell_size.x
func _estimate_cost(from : Vector2i, to : Vector2i):
return 0 # Make it Dijkstra's algorithm
func add_corridor(xz : Vector2i):
corridors.push_back(xz)

View File

@@ -1 +0,0 @@
uid://8wiue37ta0cs

View File

@@ -1,17 +0,0 @@
@tool
extends Node3D
@export var text = "" :
set(v):
text = v
if is_inside_tree(): update_visual()
func update_visual():
self.visible = len(text) > 0
self.global_transform.basis = self.global_transform.basis.orthonormalized() * self.global_transform.basis.y.length()
$Label3D.text = text
$Sprite3D.texture = preload("res://addons/SimpleDungeons/res/error-sign.svg") if text.begins_with("Error") else preload("res://addons/SimpleDungeons/res/warning-sign.svg")
func _ready():
update_visual()
set_process_input(false)

View File

@@ -1 +0,0 @@
uid://ba0ibij5hm0y0

View File

@@ -1,20 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://djjvvvx7svoux"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/debug_visuals/DebugAlert.gd" id="1_ngt8o"]
[ext_resource type="Texture2D" uid="uid://byywkosue8x0q" path="res://addons/SimpleDungeons/res/warning-sign.svg" id="2_q7g1p"]
[node name="DebugAlert" type="Node3D"]
visible = false
script = ExtResource("1_ngt8o")
metadata/_edit_lock_ = true
[node name="Label3D" type="Label3D" parent="."]
pixel_size = 0.02
billboard = 1
no_depth_test = true
[node name="Sprite3D" type="Sprite3D" parent="."]
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 4.17956, 0)
billboard = 1
no_depth_test = true
texture = ExtResource("2_q7g1p")

View File

@@ -1,54 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://cjxyp5lrix0pu"]
[ext_resource type="Material" uid="uid://pq2fqq4ophsy" path="res://addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres" id="1_3h4gq"]
[sub_resource type="GDScript" id="GDScript_18siu"]
script/source = "@tool
extends Node3D
@export var text = \"DOOR\"
@export var color = Color(0, 1, 0)
func _ready():
set_process_input(false)
func _process(_delta):
$CSGCylinder3D2.material.set_shader_parameter(\"color\", Vector3(color.r, color.g, color.b))
$CSGBox3D2.material.set_shader_parameter(\"color\", Vector3(color.r, color.g, color.b))
$MeshInstance3D.mesh.material.set_shader_parameter(\"color\", Vector3(color.r, color.g, color.b))
$Label3D.modulate = color
$Label3D.text = text
"
[sub_resource type="QuadMesh" id="QuadMesh_35eqg"]
resource_local_to_scene = true
material = ExtResource("1_3h4gq")
size = Vector2(10, 10)
[node name="DoorDebugVisual" type="Node3D"]
script = SubResource("GDScript_18siu")
metadata/_edit_lock_ = true
[node name="Label3D" type="Label3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.657576, 0)
pixel_size = 0.01
no_depth_test = true
modulate = Color(0, 1, 0, 1)
text = "DOOR"
[node name="CSGCylinder3D2" type="CSGCylinder3D" parent="."]
transform = Transform3D(0.48016, 1.27422e-07, 0.48016, 0.48016, -2.09885e-08, -0.48016, -7.52601e-08, 0.679049, -1.04942e-07, 0, 0, 0.997)
radius = 0.423
height = 0.716463
sides = 4
cone = true
smooth_faces = false
material = ExtResource("1_3h4gq")
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.464)
size = Vector3(0.166, 0.166, 0.682)
material = ExtResource("1_3h4gq")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("QuadMesh_35eqg")

View File

@@ -1,50 +0,0 @@
@tool
extends Node3D
var dungeon_generator : DungeonGenerator3D = null
var wireframe_cube : Node3D = null
var debug_alert : Node3D = null
func update_visual():
var show_editor = Engine.is_editor_hint() and dungeon_generator.show_debug_in_editor
var show_game = not Engine.is_editor_hint() and dungeon_generator.show_debug_in_game
if not show_editor and not show_game:
if wireframe_cube and is_instance_valid(wireframe_cube):
remove_child(wireframe_cube)
wireframe_cube.queue_free()
wireframe_cube = null
if debug_alert and is_instance_valid(debug_alert):
remove_child(debug_alert)
debug_alert.queue_free()
debug_alert = null
return
if not wireframe_cube or not is_instance_valid(wireframe_cube):
wireframe_cube = preload("res://addons/SimpleDungeons/debug_visuals/WireframeCube.tscn").instantiate()
wireframe_cube.enable_depth_test = true
add_child(wireframe_cube)
wireframe_cube.scale = Vector3(dungeon_generator.dungeon_size) * dungeon_generator.voxel_scale
wireframe_cube.grid_size = dungeon_generator.dungeon_size
if not debug_alert or not is_instance_valid(debug_alert):
debug_alert = preload("res://addons/SimpleDungeons/debug_visuals/DebugAlert.tscn").instantiate()
add_child(debug_alert)
debug_alert.scale = Vector3(dungeon_generator.voxel_scale.y/5.0, dungeon_generator.voxel_scale.y/5.0, dungeon_generator.voxel_scale.y/5.0)
debug_alert.position = ((Vector3(dungeon_generator.dungeon_size) / 2) + Vector3(0,0.35,0)) * Vector3(0, dungeon_generator.voxel_scale.y, 0)
var err_warning = {"error": "", "warning": ""} # Closure won't capture normal vars
dungeon_generator.validate_dungeon(
(func(str):
err_warning["error"] = "Error: " + str),
(func(str): err_warning["warning"] = "Warning: " + str))
debug_alert.text = err_warning["error"] if err_warning["error"] else err_warning["warning"]
if not debug_alert.text: debug_alert.position = Vector3() # Fix AABB visual in editor
func _ready():
dungeon_generator = get_parent()
update_visual.call_deferred() # Call after parent _ready()
set_process_input(false)
func _process(_delta):
update_visual()

View File

@@ -1,92 +0,0 @@
@tool
extends Node3D
var dungeon_room : DungeonRoom3D = null
var wireframe_cube : Node3D = null
var debug_alert : Node3D = null
var aabb_with_doors : Node3D = null
# Dict mapping DungeonRoom3D Door:DoorDebugVisual
var door_visuals = []
func update_door_visuals(show_visuals : bool):
var room_doors := []
if show_visuals:
room_doors = dungeon_room.get_doors()
# Add/remove debug visuals to match
while len(door_visuals) < len(room_doors):
var door_visual = preload("res://addons/SimpleDungeons/debug_visuals/DoorDebugVisual.tscn").instantiate()
add_child(door_visual)
door_visuals.push_back(door_visual)
while len(door_visuals) > len(room_doors):
var door_visual = door_visuals.pop_back()
remove_child(door_visual)
door_visual.queue_free()
for i in len(room_doors):
door_visuals[i].position = (Vector3(room_doors[i].local_pos) + Vector3(.5,.5,.5)) * dungeon_room.voxel_scale
door_visuals[i].position -= Vector3(dungeon_room.size_in_voxels) * dungeon_room.voxel_scale * 0.5
door_visuals[i].position += DungeonUtils.DIRECTION_VECTORS[room_doors[i].dir] * 0.5 * dungeon_room.voxel_scale
door_visuals[i].rotation.y = -DungeonUtils.DIRECTION_VECTORS[room_doors[i].dir].signed_angle_to(DungeonUtils.DIRECTION_VECTORS[DungeonUtils.Direction.FRONT], Vector3.UP)
door_visuals[i].scale = dungeon_room.voxel_scale / 10.0
var col = Color.YELLOW if room_doors[i].optional else Color.GREEN
door_visuals[i].text = "OPTIONAL DOOR" if room_doors[i].optional else "DOOR"
door_visuals[i].color = col if room_doors[i].validate_door() else Color.RED
func update_visual():
var show_editor = Engine.is_editor_hint() and dungeon_room.show_debug_in_editor
var show_game = not Engine.is_editor_hint() and dungeon_room.show_debug_in_game
#if not get_parent() is DungeonGenerator3D:
#print(dungeon_room.name)
if dungeon_room.dungeon_generator and dungeon_room.dungeon_generator.hide_debug_visuals_for_all_generated_rooms and not (dungeon_room.get_parent() is DungeonGenerator3D):
show_editor = false
show_game = false
if not show_editor and not show_game:
update_door_visuals(false)
for dbg_visual in [wireframe_cube, debug_alert, aabb_with_doors]:
if dbg_visual and is_instance_valid(dbg_visual):
remove_child(dbg_visual)
dbg_visual.queue_free()
wireframe_cube = null; debug_alert = null; aabb_with_doors = null;
return
update_door_visuals(true)
if not wireframe_cube or not is_instance_valid(wireframe_cube):
wireframe_cube = preload("res://addons/SimpleDungeons/debug_visuals/WireframeCube.tscn").instantiate()
add_child(wireframe_cube)
wireframe_cube.scale = Vector3(dungeon_room.size_in_voxels) * dungeon_room.voxel_scale
wireframe_cube.grid_size = dungeon_room.size_in_voxels
wireframe_cube.show_coordinates = false
wireframe_cube.color = Color.WHITE if dungeon_room.was_preplaced else Color.BLACK
# Show grid AABB with doors
if not aabb_with_doors:
aabb_with_doors = CSGBox3D.new()
aabb_with_doors.material = preload("res://addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres")
add_child(aabb_with_doors)
var rel_room_aabb = dungeon_room.xform_aabb(dungeon_room.get_grid_aabbi(true).to_AABB(), dungeon_room.get_xform_to(DungeonRoom3D.SPACE.DUNGEON_GRID, DungeonRoom3D.SPACE.LOCAL_SPACE)).abs()
aabb_with_doors.size = rel_room_aabb.size if dungeon_room.show_grid_aabb_with_doors else Vector3()
aabb_with_doors.position = rel_room_aabb.get_center()
aabb_with_doors.visible = dungeon_room.show_grid_aabb_with_doors
if not debug_alert or not is_instance_valid(debug_alert):
debug_alert = preload("res://addons/SimpleDungeons/debug_visuals/DebugAlert.tscn").instantiate()
add_child(debug_alert)
debug_alert.scale = Vector3(dungeon_room.voxel_scale.y/10.0, dungeon_room.voxel_scale.y/10.0, dungeon_room.voxel_scale.y/10.0)
debug_alert.position = ((Vector3(dungeon_room.size_in_voxels) / 2) + Vector3(0,0.35,0)) * Vector3(0, dungeon_room.voxel_scale.y, 0)
var err_warning = {"error": "", "warning": ""} # Closure won't capture normal vars
dungeon_room.validate_room(
(func(str):
err_warning["error"] = "Error: " + str),
(func(str): err_warning["warning"] = "Warning: " + str))
debug_alert.text = err_warning["error"] if err_warning["error"] else err_warning["warning"]
if not debug_alert.text: debug_alert.position = Vector3() # Fix AABB visual in editor
func _ready():
dungeon_room = get_parent()
update_visual.call_deferred() # Call after parent _ready()
set_process_input(false)
func _process(_delta):
update_visual()

View File

@@ -1,44 +0,0 @@
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://pq2fqq4ophsy"]
[sub_resource type="Shader" id="Shader_xtf8b"]
resource_local_to_scene = true
code = "shader_type spatial;
render_mode unshaded, cull_disabled, depth_draw_always, depth_test_disabled;
uniform vec3 color = vec3(0.0, 1.0, 0.0);
uniform float line_width : hint_range(0.0, 10.0) = 0.85;
varying vec3 barycentric;
void vertex() {
vec3 b_coords[3] = vec3[](vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1));
int vertex_id = int(VERTEX_ID) % 3;
barycentric = b_coords[vertex_id];
}
void fragment() {
// Calculate the screen-space derivatives of the barycentric coordinates
vec3 dFdx_barycentric = dFdx(barycentric);
vec3 dFdy_barycentric = dFdy(barycentric);
// Calculate the minimum barycentric coordinate value
float min_bary = min(min(barycentric.x, barycentric.y), barycentric.z);
// Calculate the screen-space line width
float screen_line_width = line_width * length(vec2(dFdx_barycentric.x, dFdy_barycentric.x));
// Draw the line based on the calculated screen-space line width
if (min_bary < screen_line_width) {
ALBEDO = color;
} else {
discard;
}
}
"
[resource]
resource_local_to_scene = true
render_priority = 0
shader = SubResource("Shader_xtf8b")
shader_parameter/color = Vector3(0, 1, 0)
shader_parameter/line_width = 0.85

View File

@@ -1,69 +0,0 @@
@tool
extends Node3D
@export var grid_size := Vector3i(1,1,1)
@export var show_coordinates := true
@export var color := Color.BLACK
@export var enable_depth_test := false
var last_grid_size = null
var last_voxel_scale = null
var last_show_coordinates = null
var last_enable_depth = null
func update_grid():
for child in %Levels.get_children():
%Levels.remove_child(child)
child.queue_free()
%Front.mesh.material.shader = preload("res://addons/SimpleDungeons/debug_visuals/WireframeCubeDepthEnabled.gdshader") if enable_depth_test else preload("res://addons/SimpleDungeons/debug_visuals/WireframeCube.gdshader")
%Right.mesh.material.shader = preload("res://addons/SimpleDungeons/debug_visuals/WireframeCubeDepthEnabled.gdshader") if enable_depth_test else preload("res://addons/SimpleDungeons/debug_visuals/WireframeCube.gdshader")
%Top.mesh.material.shader = preload("res://addons/SimpleDungeons/debug_visuals/WireframeCubeDepthEnabled.gdshader") if enable_depth_test else preload("res://addons/SimpleDungeons/debug_visuals/WireframeCube.gdshader")
%Front.mesh.material.set_shader_parameter("grid_size", Vector2(grid_size.x, grid_size.y))
%Right.mesh.material.set_shader_parameter("grid_size", Vector2(grid_size.z, grid_size.y))
%Top.mesh.material.set_shader_parameter("grid_size", Vector2(grid_size.x, grid_size.z))
%Front.mesh.material.set_shader_parameter("color", Vector3(color.r, color.g, color.b))
%Right.mesh.material.set_shader_parameter("color", Vector3(color.r, color.g, color.b))
%Top.mesh.material.set_shader_parameter("color", Vector3(color.r, color.g, color.b))
if grid_size.x < 1 or grid_size.y < 1 or grid_size.z < 1:
return
if grid_size.x <= 1 and grid_size.y <= 1 and grid_size.z <= 1:
return
for y in range(1, grid_size.y):
var level = %Top.duplicate()
%Levels.add_child(level)
level.position.y -= float(y) / grid_size.y - 0.00001
var b = grid_size - Vector3i(1,1,1)
var positions = [Vector3i(0,0,0), Vector3i(grid_size.x-1,0,0), Vector3i(0,grid_size.y-1,0), Vector3i(0,0,grid_size.z-1),
b, b - Vector3i(grid_size.x-1,0,0), b - Vector3i(0,grid_size.y-1,0), b - Vector3i(0,0,grid_size.z-1)]
if not show_coordinates:
return
var make_scale_uniform_node = Node3D.new()
%Levels.add_child(make_scale_uniform_node)
make_scale_uniform_node.scale = self.transform.basis.inverse().get_scale()
for grid_pos in positions:
var label = Label3D.new()
label.text = str(grid_pos)
make_scale_uniform_node.add_child(label)
label.position = ((Vector3(grid_pos) + Vector3(0.5, 0.5, 0.5)) / Vector3(grid_size)) * self.scale - self.scale/2
label.scale *= (Vector3(1,1,1) / Vector3(grid_size)) * self.scale
label.scale.x = min(label.scale.x, label.scale.y, label.scale.z)
label.scale.y = min(label.scale.x, label.scale.y, label.scale.z)
label.scale.z = min(label.scale.x, label.scale.y, label.scale.z)
func _ready():
update_grid()
set_process_input(false)
func _process(delta):
if last_grid_size != grid_size or last_show_coordinates != show_coordinates or last_enable_depth != enable_depth_test:
update_grid()
last_grid_size = grid_size
last_show_coordinates = show_coordinates
last_enable_depth = enable_depth_test

View File

@@ -1,14 +0,0 @@
shader_type spatial;
render_mode unshaded, cull_disabled, depth_test_disabled;
uniform vec2 grid_size;
uniform vec3 color = vec3(0.);
void fragment() {
vec2 uv = fract(UV * grid_size);
vec2 eps = 0.0015 * grid_size;
vec2 inv_eps = 1.0 - eps;
if(uv.x > eps.x && uv.x < inv_eps.x && uv.y > eps.y && uv.y < inv_eps.y) {
discard;
}
ALBEDO = color;
}

View File

@@ -1,79 +0,0 @@
[gd_scene load_steps=11 format=3 uid="uid://cfch5o0wphv4r"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/debug_visuals/WireframeCube.gd" id="1_unksl"]
[ext_resource type="Shader" path="res://addons/SimpleDungeons/debug_visuals/WireframeCube.gdshader" id="2_hwi17"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_jymxj"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("2_hwi17")
shader_parameter/grid_size = Vector2(1, 1)
shader_parameter/color = Vector3(0, 0, 0)
[sub_resource type="QuadMesh" id="QuadMesh_pwl2e"]
resource_local_to_scene = true
material = SubResource("ShaderMaterial_jymxj")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_lnw1d"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("2_hwi17")
shader_parameter/grid_size = Vector2(1, 1)
shader_parameter/color = Vector3(0, 0, 0)
[sub_resource type="QuadMesh" id="QuadMesh_llwc2"]
resource_local_to_scene = true
material = SubResource("ShaderMaterial_lnw1d")
[sub_resource type="QuadMesh" id="QuadMesh_hdmjf"]
resource_local_to_scene = true
material = SubResource("ShaderMaterial_lnw1d")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_3xn3f"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("2_hwi17")
shader_parameter/grid_size = Vector2(1, 1)
shader_parameter/color = Vector3(0, 0, 0)
[sub_resource type="QuadMesh" id="QuadMesh_d7b6y"]
resource_local_to_scene = true
material = SubResource("ShaderMaterial_3xn3f")
[sub_resource type="QuadMesh" id="QuadMesh_kjtax"]
resource_local_to_scene = true
material = SubResource("ShaderMaterial_3xn3f")
[node name="WireframeCube" type="Node3D"]
script = ExtResource("1_unksl")
metadata/_edit_lock_ = true
[node name="Back" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.5)
mesh = SubResource("QuadMesh_pwl2e")
[node name="Front" type="MeshInstance3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.5)
mesh = SubResource("QuadMesh_pwl2e")
[node name="Right" type="MeshInstance3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0.5, 0, 0)
mesh = SubResource("QuadMesh_llwc2")
[node name="Left" type="MeshInstance3D" parent="."]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -0.5, 0, 0)
mesh = SubResource("QuadMesh_hdmjf")
[node name="Top" type="MeshInstance3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.5, 0)
mesh = SubResource("QuadMesh_d7b6y")
[node name="Bottom" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, -0.5, 0)
mesh = SubResource("QuadMesh_kjtax")
[node name="Levels" type="Node3D" parent="."]
unique_name_in_owner = true

View File

@@ -1,14 +0,0 @@
shader_type spatial;
render_mode unshaded, cull_disabled;
uniform vec2 grid_size;
uniform vec3 color = vec3(0.);
void fragment() {
vec2 uv = fract(UV * grid_size);
vec2 eps = 0.0015 * grid_size;
vec2 inv_eps = 1.0 - eps;
if(uv.x > eps.x && uv.x < inv_eps.x && uv.y > eps.y && uv.y < inv_eps.y) {
discard;
}
ALBEDO = color;
}

View File

@@ -1,7 +0,0 @@
[plugin]
name="SimpleDungeons"
description="3D Procedurally Generated Dungeons Addon"
author="MajikayoGames"
version=""
script="plugin.gd"

View File

@@ -1,11 +0,0 @@
@tool
extends EditorPlugin
func _enter_tree():
add_custom_type("DungeonGenerator3D", "Node3D", preload("DungeonGenerator3D.gd"), preload("res://addons/SimpleDungeons/res/dungeongenerator3dicon.svg"))
add_custom_type("DungeonRoom3D", "Node3D", preload("DungeonRoom3D.gd"), preload("res://addons/SimpleDungeons/res/dungeonroom3dicon.svg"))
func _exit_tree():
# Clean-up of the plugin goes here.
remove_custom_type("DungeonGenerator3D")
remove_custom_type("DungeonRoom3D")

View File

@@ -1 +0,0 @@
uid://be04ajwabsns6

View File

@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="dungeongenerator3d.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
inkscape:zoom="1.2065631"
inkscape:cx="244.08173"
inkscape:cy="293.80975"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g9852"
showguides="false" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g9852"
transform="translate(0.65974083,1.9323538)">
<g
id="g12191"
style="stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke:#000000;stroke-opacity:1">
<g
id="g12175"
style="stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke:#000000;stroke-opacity:1">
<g
id="g13029"
style="stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke:#000000;stroke-opacity:1">
<path
d="M 35.675158,13.999619 8.915225,32.403803 V 117.60234 H 98.472027 L 125.23196,99.198168 V 13.999619 Z"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path10584"
sodipodi:nodetypes="ccccccc" />
</g>
</g>
</g>
<path
d="M 8.915225,32.403803 H 98.472029 L 125.23196,13.999619 H 35.675158 Z"
style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5522" />
<path
d="M 98.472029,32.403803 V 117.60234 L 125.23196,99.198168 V 13.999619 Z"
style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5524" />
<path
d="m 8.915225,32.403803 h 89.5568 v 85.198537 h -89.5568 z"
style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5526" />
<path
d="M 52.658313,32.403803 79.418244,13.999619 Z"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path6110"
sodipodi:nodetypes="ccc" />
<path
d="M 52.658313,32.403803 V 117.60234 Z"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path6114"
sodipodi:nodetypes="ccc" />
<path
d="M 9.573803,76.61366 H 99.130609 L 125.20453,58.617937"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path8145"
sodipodi:nodetypes="ccc" />
<path
d="M 23.972342,22.473583 H 113.52915"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path8145-5"
sodipodi:nodetypes="cc" />
<path
d="M 113.52915,22.473583 V 106.87305"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path8709"
sodipodi:nodetypes="cc" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -1,37 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://jesvvp3ney5x"
path="res://.godot/imported/dungeongenerator3dicon.svg-8ea7452a326a8749c574f5171cab795a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/SimpleDungeons/res/dungeongenerator3dicon.svg"
dest_files=["res://.godot/imported/dungeongenerator3dicon.svg-8ea7452a326a8749c574f5171cab795a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg13273"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="dungeonroom3dicon.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview13275"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
inkscape:zoom="1.0482005"
inkscape:cx="293.35991"
inkscape:cy="254.24525"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g13444" />
<defs
id="defs13270" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g13444"
style="fill:none;stroke:#000000;stroke-width:45.3543;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-0.32914132,-0.10880017)">
<path
d="M 87.137443,16.227017 127.90612,29.987555 V 92.820072 L 87.137443,79.059533 Z"
style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:42.2873;stroke-linejoin:round"
id="path14240" />
<path
d="M 8.2188293,42.864195 V 105.69671 L 87.137443,79.059533 V 16.227017 Z"
style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-linejoin:round"
id="path14242" />
<path
d="M 8.2188293,105.69671 48.987508,119.45725 127.90612,92.820072 87.137443,79.059533 Z"
style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-linejoin:round"
id="path14244" />
<path
d="M 8.2188293,42.864195 48.987508,56.624733 127.90612,29.987555 87.137443,16.227017 Z"
style="fill:#4d4d9f;fill-rule:evenodd;stroke:none;stroke-linejoin:round"
id="path14246" />
<path
d="M 48.987508,56.624733 V 119.45725 L 127.90612,92.820072 V 29.987555 Z"
style="fill:#d7d7ff;fill-rule:evenodd;stroke:none;stroke-linejoin:round"
id="path14248" />
<path
d="M 8.2188293,42.864195 48.987508,56.624733 V 119.45725 L 8.2188293,105.69671 Z"
style="fill:#8686bf;fill-rule:evenodd;stroke:none;stroke-linejoin:round"
id="path14250" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,37 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bbkx850bx8fsi"
path="res://.godot/imported/dungeonroom3dicon.svg-7eed1d33227e64437c65643acbd1d7c7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/SimpleDungeons/res/dungeonroom3dicon.svg"
dest_files=["res://.godot/imported/dungeonroom3dicon.svg-7eed1d33227e64437c65643acbd1d7c7.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46666"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="error-sign.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
inkscape:zoom="0.37059484"
inkscape:cx="338.64476"
inkscape:cy="360.23168"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient18996">
<stop
style="stop-color:#ffdcc5;stop-opacity:1"
offset="0"
id="stop18992" />
<stop
style="stop-color:#ff6f48;stop-opacity:1"
offset="1"
id="stop18994" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient18996"
id="linearGradient18998"
x1="105.91866"
y1="58.452225"
x2="25.6213"
y2="91.293571"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="rect846"
style="fill:url(#linearGradient18998);stroke:#ff2d2d;stroke-width:14.9057;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1;stroke-opacity:1"
d="M 67.733336,15.69611 C 75.03685,26.110351 124.90333,119.77028 124.90333,119.77028 H 10.56334 c 0,0 42.752814,-83.513868 57.169996,-104.07417 z"
sodipodi:nodetypes="cccc" />
<g
aria-label="!"
id="text4934"
style="font-size:66.2082px;line-height:1.25;stroke-width:0.344834">
<path
d="m 61.914243,54.291506 h 11.63816 v 18.524071 l -1.648739,13.513196 h -8.340681 l -1.64874,-13.513196 z m 0,36.757188 h 11.63816 v 11.508846 h -11.63816 z"
style="font-weight:bold;-inkscape-font-specification:'sans-serif Bold'"
id="path915" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,38 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dj3t48rpleaqy"
path.s3tc="res://.godot/imported/error-sign.svg-7d93dee82584cb708ab0dfad824e912d.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/res/error-sign.svg"
dest_files=["res://.godot/imported/error-sign.svg-7d93dee82584cb708ab0dfad824e912d.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 135.46666 135.46666"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="warning-sign.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
inkscape:zoom="0.37059484"
inkscape:cx="311.66111"
inkscape:cy="640.86159"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient18996">
<stop
style="stop-color:#ffcc00;stop-opacity:1"
offset="0"
id="stop18992" />
<stop
style="stop-color:#ff9000;stop-opacity:1"
offset="1"
id="stop18994" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient18996"
id="linearGradient18998"
x1="105.91866"
y1="58.452225"
x2="25.6213"
y2="91.293571"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="rect846"
style="fill:url(#linearGradient18998);stroke:#ffec2d;stroke-width:14.9057;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1;stroke-opacity:1"
d="M 67.733336,15.69611 C 75.03685,26.110351 124.90333,119.77028 124.90333,119.77028 H 10.56334 c 0,0 42.752814,-83.513868 57.169996,-104.07417 z"
sodipodi:nodetypes="cccc" />
<g
aria-label="!"
id="text4934"
style="font-size:66.2082px;line-height:1.25;stroke-width:0.344834">
<path
d="m 61.914243,54.291506 h 11.63816 v 18.524071 l -1.648739,13.513196 h -8.340681 l -1.64874,-13.513196 z m 0,36.757188 h 11.63816 v 11.508846 h -11.63816 z"
style="font-weight:bold;-inkscape-font-specification:'sans-serif Bold'"
id="path1152" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,38 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://byywkosue8x0q"
path.s3tc="res://.godot/imported/warning-sign.svg-8e061f920e0c1c369bbe5e6c9df382f2.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/res/warning-sign.svg"
dest_files=["res://.godot/imported/warning-sign.svg-8e061f920e0c1c369bbe5e6c9df382f2.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i7rtr0ewxm5j"
path.s3tc="res://.godot/imported/blue-checkerboard-cc0.png-1c2a1348fd1d3b6ea7498ed312065a17.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/blue-checkerboard-cc0.png"
dest_files=["res://.godot/imported/blue-checkerboard-cc0.png-1c2a1348fd1d3b6ea7498ed312065a17.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@@ -1,37 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://d2vy5uyl5mwba"
path="res://.godot/imported/free-modular-lowpoly-dungeon-cc0-by-rgsdev.glb-3f9c0eedad3e0ef421e24580ad8e0e11.scn"
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/free-modular-lowpoly-dungeon-cc0-by-rgsdev.glb"
dest_files=["res://.godot/imported/free-modular-lowpoly-dungeon-cc0-by-rgsdev.glb-3f9c0eedad3e0ef421e24580ad8e0e11.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://djhy80eyqjqpv"
path.s3tc="res://.godot/imported/kenney-dark-grey-checkboard-cc0.png-a474cdcb7f780d0c1f0fbe07f09dca20.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-checkboard-cc0.png"
dest_files=["res://.godot/imported/kenney-dark-grey-checkboard-cc0.png-a474cdcb7f780d0c1f0fbe07f09dca20.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://wvh1xmoax4ok"
path.s3tc="res://.godot/imported/kenney-dark-grey-grid-cc0.png-641e868f49742dac557c00f298646a06.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-grid-cc0.png"
dest_files=["res://.godot/imported/kenney-dark-grey-grid-cc0.png-641e868f49742dac557c00f298646a06.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bwe02yta75ooa"
path.s3tc="res://.godot/imported/kenney-green-checkerboar-cc0.png-5b7bcfea56d2c3f2ba54eb8bc0be04ac.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-green-checkerboar-cc0.png"
dest_files=["res://.godot/imported/kenney-green-checkerboar-cc0.png-5b7bcfea56d2c3f2ba54eb8bc0be04ac.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqvyc0ok7chld"
path.s3tc="res://.godot/imported/kenney-grey-checkerboard-cc0.png-e0b9147c60d57a064eba7df286f44f21.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png"
dest_files=["res://.godot/imported/kenney-grey-checkerboard-cc0.png-e0b9147c60d57a064eba7df286f44f21.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bxbtgil61x5sx"
path.s3tc="res://.godot/imported/kenney-orange-checkerboard-cc0.png-b451adc6009037708d3774ae5cabc4ed.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-orange-checkerboard-cc0.png"
dest_files=["res://.godot/imported/kenney-orange-checkerboard-cc0.png-b451adc6009037708d3774ae5cabc4ed.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

View File

@@ -1,35 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dydd6x64uxryr"
path.s3tc="res://.godot/imported/kenney-red-checkerboard-cc0.png-de703f131e4265ef1314045e85661e68.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/SimpleDungeons/sample_assets/kenney-red-checkerboard-cc0.png"
dest_files=["res://.godot/imported/kenney-red-checkerboard-cc0.png-de703f131e4265ef1314045e85661e68.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@@ -1,61 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://cq6rrn2gpph6y"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_i6opq"]
[ext_resource type="Texture2D" uid="uid://i7rtr0ewxm5j" path="res://addons/SimpleDungeons/sample_assets/blue-checkerboard-cc0.png" id="2_6nsgb"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_b5q7u"]
albedo_texture = ExtResource("2_6nsgb")
uv1_triplanar = true
[sub_resource type="GDScript" id="GDScript_3qcmr"]
script/source = "@tool
extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_dungeon_done_generating():
remove_unused_doors()
"
[node name="BlueRoom" type="Node3D"]
script = ExtResource("1_i6opq")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -5.68248e-07, 0, 0)
material_override = SubResource("StandardMaterial3D_b5q7u")
use_collision = true
size = Vector3(10, 10, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9.5, 9.5, 9.5)
[node name="DOOR?_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -5.25)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_3qcmr")
[node name="PlayerSpawnPoint" type="Node3D" parent="." groups=["player_spawn_point"]]
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_dungeon_done_generating"]

View File

@@ -1,59 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://b2t06bycrtldn"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_qqrlr"]
[ext_resource type="Texture2D" uid="uid://dqvyc0ok7chld" path="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png" id="2_jdiqu"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_oj2cm"]
albedo_texture = ExtResource("2_jdiqu")
uv1_triplanar = true
[sub_resource type="GDScript" id="GDScript_ldqn2"]
script/source = "@tool
extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_dungeon_done_generating():
remove_unused_doors()
"
[node name="Corridor" type="Node3D"]
script = ExtResource("1_qqrlr")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -5.68248e-07, 0, 0)
material_override = SubResource("StandardMaterial3D_oj2cm")
use_collision = true
size = Vector3(4, 4, 4)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(3.8, 3.8, 3.8)
[node name="DOOR?_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.05713e-07, -0.0762184, -1.75306)
operation = 2
size = Vector3(3.5, 3.65234, 0.5)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 1.75067, -0.0769043, -2.17967e-08)
operation = 2
size = Vector3(3.5, 3.65381, 0.5)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -1.50397, -0.0749512, -0.0690359)
operation = 2
size = Vector3(3.5, 3.6499, 1)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.05912e-07, -0.0749512, 1.50077)
operation = 2
size = Vector3(3.5, 3.6499, 1)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_ldqn2")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_dungeon_done_generating"]

View File

@@ -1,39 +0,0 @@
@tool
extends DungeonGenerator3D
func _ready():
self.custom_get_rooms_function = custom_get_rand_rooms
super._ready()
func _process(delta):
super._process(delta)
func custom_get_rand_rooms(room_instances : Array[DungeonRoom3D], rng_seeded : RandomNumberGenerator) -> Array[DungeonRoom3D]:
var num_blue_rooms : int = 30
var num_red_rooms : int = 30
var blue_room = room_instances.filter(func(r): return r.name == "BlueRoom")[0]
var red_room = room_instances.filter(func(r): return r.name == "RedRoom")[0]
var rooms : Array[DungeonRoom3D] = []
while num_red_rooms > 0:
var inst = red_room.create_clone_and_make_virtual_unless_visualizing()
rooms.push_back(inst)
# Set room_rotations before set_position_by_grid_pos as it is set by AABB positon. May change when rotated.
inst.room_rotations = rng_seeded.randi()
inst.set_position_by_grid_pos(
Vector3i(
(rng_seeded.randi() % dungeon_size.x) / 2,
rng_seeded.randi() % dungeon_size.y,
rng_seeded.randi() % dungeon_size.z))
num_red_rooms -= 1
while num_blue_rooms > 0:
var inst = blue_room.create_clone_and_make_virtual_unless_visualizing()
rooms.push_back(inst)
# Set room_rotations before set_position_by_grid_pos as it is set by AABB positon. May change when rotated.
inst.room_rotations = rng_seeded.randi()
inst.set_position_by_grid_pos(
Vector3i(
(rng_seeded.randi() % dungeon_size.x) / 2 + dungeon_size.x / 2,
rng_seeded.randi() % dungeon_size.y,
rng_seeded.randi() % dungeon_size.z))
num_blue_rooms -= 1
return rooms

View File

@@ -1,12 +0,0 @@
[gd_scene load_steps=6 format=3 uid="uid://d1k8jtivamexj"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/sample_dungeons/custom_random_room_function_example/dungeon_generator_3d_custom.gd" id="1_smyj4"]
[ext_resource type="PackedScene" uid="uid://cq6rrn2gpph6y" path="res://addons/SimpleDungeons/sample_dungeons/custom_random_room_function_example/blue_room.tscn" id="2_rfqbn"]
[ext_resource type="PackedScene" uid="uid://dh2i3dk3g0icv" path="res://addons/SimpleDungeons/sample_dungeons/custom_random_room_function_example/red_room.tscn" id="3_hfdry"]
[ext_resource type="PackedScene" uid="uid://br3pjd5mcjj11" path="res://addons/SimpleDungeons/sample_dungeons/custom_random_room_function_example/stairs.tscn" id="4_2jnyt"]
[ext_resource type="PackedScene" uid="uid://b2t06bycrtldn" path="res://addons/SimpleDungeons/sample_dungeons/custom_random_room_function_example/corridor.tscn" id="5_5efio"]
[node name="DungeonGenerator3dInheritedClass" type="Node3D"]
script = ExtResource("1_smyj4")
room_scenes = Array[PackedScene]([ExtResource("2_rfqbn"), ExtResource("3_hfdry"), ExtResource("4_2jnyt")])
corridor_room_scene = ExtResource("5_5efio")

View File

@@ -1,59 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://dh2i3dk3g0icv"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_ruwq1"]
[ext_resource type="Texture2D" uid="uid://dydd6x64uxryr" path="res://addons/SimpleDungeons/sample_assets/kenney-red-checkerboard-cc0.png" id="2_270m7"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yl5wx"]
albedo_texture = ExtResource("2_270m7")
uv1_triplanar = true
[sub_resource type="GDScript" id="GDScript_mnevw"]
script/source = "@tool
extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_dungeon_done_generating():
remove_unused_doors()
"
[node name="RedRoom" type="Node3D"]
script = ExtResource("1_ruwq1")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -5.68248e-07, 0, 0)
material_override = SubResource("StandardMaterial3D_yl5wx")
use_collision = true
size = Vector3(10, 10, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9.5, 9.5, 9.5)
[node name="DOOR?_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -5.25)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_mnevw")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_dungeon_done_generating"]

View File

@@ -1,42 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://br3pjd5mcjj11"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_pixse"]
[ext_resource type="Texture2D" uid="uid://dqvyc0ok7chld" path="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png" id="2_bdw51"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="3_dobr2"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6cnww"]
albedo_texture = ExtResource("2_bdw51")
uv1_triplanar = true
uv1_world_triplanar = true
[node name="Stairs" type="Node3D"]
script = ExtResource("1_pixse")
size_in_voxels = Vector3i(2, 2, 1)
max_count = 20
is_stair_room = true
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
material_override = SubResource("StandardMaterial3D_6cnww")
use_collision = true
size = Vector3(20, 20, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(19.5, 19.5, 9.5)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.8, -7.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.9, 2.25, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGStairMaker3D" parent="." instance=ExtResource("3_dobr2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.75, -4.75, 0)
use_collision = true
size = Vector3(14, 10, 9.5)
num_stairs = 32

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +0,0 @@
@tool
extends DungeonRoom3D
# Called when the node enters the scene tree for the first time.
func _ready():
super._ready()
dungeon_done_generating.connect(remove_unused_doors_and_walls)
func remove_unused_doors_and_walls():
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/F_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/B_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/R_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/L_WALL/torch_001.queue_free()
if get_door_by_node($"CSGBox3D/DOOR?_F_CUT").get_room_leads_to() != null: $Models/F_WALL.queue_free()
else: $Models/F_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_R_CUT").get_room_leads_to() != null: $Models/R_WALL.queue_free()
else: $Models/R_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_B_CUT").get_room_leads_to() != null: $Models/B_WALL.queue_free()
else: $Models/B_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_L_CUT").get_room_leads_to() != null: $Models/L_WALL.queue_free()
else: $Models/L_WALL.visible = true
for door in get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +0,0 @@
@tool
extends DungeonRoom3D
# Called when the node enters the scene tree for the first time.
func _ready():
super._ready()
dungeon_done_generating.connect(remove_unused_doors_and_walls)
func remove_unused_doors_and_walls():
if get_door_by_node($"CSGBox3D/DOOR?_B_CUT").get_room_leads_to() == null: $Models/B_DOOR.queue_free(); $Models/B_WALL.visible = true
else: $Models/B_WALL.queue_free(); $Models/B_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_RB_CUT").get_room_leads_to() == null: $Models/RB_DOOR.queue_free(); $Models/RB_WALL.visible = true
else: $Models/RB_WALL.queue_free(); $Models/RB_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_RF_CUT").get_room_leads_to() == null: $Models/RF_DOOR.queue_free(); $Models/RF_WALL.visible = true
else: $Models/RF_WALL.queue_free(); $Models/RF_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_LB_CUT").get_room_leads_to() == null: $Models/LB_DOOR.queue_free(); $Models/LB_WALL.visible = true
else: $Models/LB_WALL.queue_free(); $Models/LB_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_LF_CUT").get_room_leads_to() == null: $Models/LF_DOOR.queue_free(); $Models/LF_WALL.visible = true
else: $Models/LF_WALL.queue_free(); $Models/LF_DOOR.visible = true
for door in get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +0,0 @@
@tool
extends DungeonRoom3D
# Called when the node enters the scene tree for the first time.
func _ready():
super._ready()
dungeon_done_generating.connect(remove_unused_doors_and_walls)
func remove_unused_doors_and_walls():
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/F_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/B_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/R_WALL/torch_001.queue_free()
if RandomNumberGenerator.new().randf_range(0,10) > 2.5: $Models/L_WALL/torch_001.queue_free()
if get_door_by_node($"CSGBox3D/DOOR?_F_CUT").get_room_leads_to() != null: $Models/F_WALL.queue_free()
else: $Models/F_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_R_CUT").get_room_leads_to() != null: $Models/R_WALL.queue_free()
else: $Models/R_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_B_CUT").get_room_leads_to() != null: $Models/B_WALL.queue_free()
else: $Models/B_WALL.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_L_CUT").get_room_leads_to() != null: $Models/L_WALL.queue_free()
else: $Models/L_WALL.visible = true
for door in get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +0,0 @@
@tool
extends DungeonRoom3D
# Called when the node enters the scene tree for the first time.
func _ready():
super._ready()
dungeon_done_generating.connect(remove_unused_doors_and_walls)
func remove_unused_doors_and_walls():
if get_door_by_node($"CSGBox3D/DOOR?_B_CUT").get_room_leads_to() == null: $Models/B_DOOR.queue_free(); $Models/B_WALL.visible = true
else: $Models/B_WALL.queue_free(); $Models/B_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_RB_CUT").get_room_leads_to() == null: $Models/RB_DOOR.queue_free(); $Models/RB_WALL.visible = true
else: $Models/RB_WALL.queue_free(); $Models/RB_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_RF_CUT").get_room_leads_to() == null: $Models/RF_DOOR.queue_free(); $Models/RF_WALL.visible = true
else: $Models/RF_WALL.queue_free(); $Models/RF_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_LB_CUT").get_room_leads_to() == null: $Models/LB_DOOR.queue_free(); $Models/LB_WALL.visible = true
else: $Models/LB_WALL.queue_free(); $Models/LB_DOOR.visible = true
if get_door_by_node($"CSGBox3D/DOOR?_LF_CUT").get_room_leads_to() == null: $Models/LF_DOOR.queue_free(); $Models/LF_WALL.visible = true
else: $Models/LF_WALL.queue_free(); $Models/LF_DOOR.visible = true
for door in get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,35 +0,0 @@
@tool
extends Node3D
@export var dungeon_generator : DungeonGenerator3D
@export var show_delay_ms := 1000
var done_time
var done = false
func _update_to_dungeon():
if not dungeon_generator:
return
var realsize = Vector3(dungeon_generator.dungeon_size) * dungeon_generator.voxel_scale
$HouseWalls/InsideCut.size = realsize
$HouseWalls.size = realsize + Vector3(5,5,5)
$Roof.mesh.size = realsize + Vector3(10,10,10)
$Roof.mesh.size.y = 20
$Roof.position.y = realsize.y / 2 + 10 + 2.5
if not done_time and dungeon_generator.stage == DungeonGenerator3D.BuildStage.DONE:
done_time = Time.get_ticks_msec()
if not done and done_time and Time.get_ticks_msec() - done_time > show_delay_ms:
done = true
var entrance = dungeon_generator.get_node("MansionEntranceRoom")
var xform_to_global = dungeon_generator.global_transform * entrance.get_xform_to(DungeonRoom3D.SPACE.LOCAL_GRID, DungeonRoom3D.SPACE.DUNGEON_SPACE)
var corner_of_room = xform_to_global * Vector3(0,0,3)
$Entrance.position = corner_of_room
$Entrance/FrontDoorCut.reparent($HouseWalls)
self.visible = done
func _ready():
_update_to_dungeon()
func _process(delta):
_update_to_dungeon()

View File

@@ -1,86 +0,0 @@
[gd_scene load_steps=13 format=3 uid="uid://bv23twweurssv"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/sample_dungeons/mansion/house_exterior.gd" id="1_d4jd8"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="1_wv7fx"]
[ext_resource type="Texture2D" uid="uid://bwe02yta75ooa" path="res://addons/SimpleDungeons/sample_assets/kenney-green-checkerboar-cc0.png" id="2_dgnpe"]
[sub_resource type="Gradient" id="Gradient_0gfbx"]
colors = PackedColorArray(0.19, 0.0988, 0.0988, 1, 0.356863, 0.607843, 1, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_ay5m5"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_ikcx8"]
color_ramp = SubResource("Gradient_0gfbx")
noise = SubResource("FastNoiseLite_ay5m5")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cjtu7"]
albedo_texture = SubResource("NoiseTexture2D_ikcx8")
[sub_resource type="Gradient" id="Gradient_ks6gq"]
colors = PackedColorArray(0.352941, 0.25098, 0, 1, 0.190162, 0.128887, 0, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_f0vc6"]
gradient = SubResource("Gradient_ks6gq")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_570wq"]
albedo_texture = SubResource("GradientTexture2D_f0vc6")
uv1_triplanar = true
uv1_triplanar_sharpness = 0.00158643
uv1_world_triplanar = true
[sub_resource type="PrismMesh" id="PrismMesh_urnwm"]
material = SubResource("StandardMaterial3D_570wq")
size = Vector3(110, 20, 110)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_42bcn"]
albedo_texture = ExtResource("2_dgnpe")
[node name="HouseExterior" type="Node3D"]
script = ExtResource("1_d4jd8")
[node name="HouseWalls" type="CSGBox3D" parent="."]
material_override = SubResource("StandardMaterial3D_cjtu7")
use_collision = true
size = Vector3(105, 105, 105)
[node name="InsideCut" type="CSGBox3D" parent="HouseWalls"]
operation = 2
size = Vector3(100, 100, 100)
[node name="Roof" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 63, 0)
mesh = SubResource("PrismMesh_urnwm")
[node name="Entrance" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, -30, 50)
[node name="CSGStairMaker3D" parent="Entrance" instance=ExtResource("1_wv7fx")]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 10, -10, 12.5)
use_collision = true
size = Vector3(20, 20, 20)
num_stairs = 42
[node name="CSGBox3D" type="CSGBox3D" parent="Entrance"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, -20.5, 25)
use_collision = true
size = Vector3(66, 1, 45)
[node name="CSGBox3D2" type="CSGBox3D" parent="Entrance"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12.5, -13.5, 9)
material_override = SubResource("StandardMaterial3D_42bcn")
use_collision = true
size = Vector3(5, 14, 5)
[node name="CSGBox3D3" type="CSGBox3D" parent="Entrance"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 32.5, -13.5, 9)
material_override = SubResource("StandardMaterial3D_42bcn")
use_collision = true
size = Vector3(5, 14, 5)
[node name="PlayerSpawnPoint" type="Node3D" parent="Entrance" groups=["player_spawn_point"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, -14, 41)
[node name="FrontDoorCut" type="CSGBox3D" parent="Entrance"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 5, 0)
operation = 2
size = Vector3(20, 10, 10)

View File

@@ -1,86 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://b3tl36squaoel"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_1qelf"]
[ext_resource type="Texture2D" uid="uid://wvh1xmoax4ok" path="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-grid-cc0.png" id="7_x4518"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="8_tpxey"]
[ext_resource type="Texture2D" uid="uid://dydd6x64uxryr" path="res://addons/SimpleDungeons/sample_assets/kenney-red-checkerboard-cc0.png" id="9_68bqd"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cy7sd"]
albedo_texture = ExtResource("7_x4518")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_inmcr"]
albedo_color = Color(0.252028, 0.252028, 0.252028, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_geohb"]
albedo_color = Color(0.223103, 0.223103, 0.223103, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0fi8r"]
albedo_texture = ExtResource("9_68bqd")
[node name="MansionEntranceRoom" type="Node3D"]
script = ExtResource("1_1qelf")
size_in_voxels = Vector3i(2, 4, 3)
[node name="CSGBox3D" type="CSGBox3D" parent="."]
material_override = SubResource("StandardMaterial3D_cy7sd")
use_collision = true
size = Vector3(20, 40, 30)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(19, 39, 29)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.75, 13)
operation = 2
size = Vector3(19, 29.5, 7)
[node name="DOOR_R" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.25, -5, -9.95)
operation = 2
size = Vector3(9.2, 9.2, 2)
[node name="DOOR_L" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.75, -5, -9.95)
operation = 2
size = Vector3(9.2, 9.2, 2)
[node name="DOOR_R2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.25, -5, 10.05)
operation = 2
size = Vector3(9.2, 9.2, 2)
[node name="DOOR_L2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.75, -5, 10.05)
operation = 2
size = Vector3(9.2, 9.2, 2)
[node name="CSGStairMaker3D" parent="." instance=ExtResource("8_tpxey")]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.74228e-08, -14.9, 0)
material_override = SubResource("StandardMaterial3D_inmcr")
use_collision = true
size = Vector3(10, 10, 10)
num_stairs = 42
[node name="CSGBox3D4" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -14.5, -9.75)
use_collision = true
size = Vector3(19.8, 10, 9.5)
material = SubResource("StandardMaterial3D_geohb")
[node name="CSGBox3D5" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.5, -9.95, 5)
use_collision = true
size = Vector3(4, 0.7, 20)
material = SubResource("StandardMaterial3D_geohb")
[node name="CSGBox3D6" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.5, -9.95, 5)
use_collision = true
size = Vector3(4, 0.7, 20)
material = SubResource("StandardMaterial3D_geohb")
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -19.9, 10)
size = Vector3(6, 1, 9)
material = SubResource("StandardMaterial3D_0fi8r")

View File

@@ -1,103 +0,0 @@
[gd_scene load_steps=10 format=3 uid="uid://crr6031qmir35"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_1c8qy"]
[sub_resource type="Gradient" id="Gradient_omn5o"]
[sub_resource type="GradientTexture2D" id="GradientTexture2D_ls0we"]
gradient = SubResource("Gradient_omn5o")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1b4dt"]
albedo_texture = SubResource("GradientTexture2D_ls0we")
uv1_triplanar = true
uv1_world_triplanar = true
[sub_resource type="GDScript" id="GDScript_4tekc"]
script/source = "extends Node
func _ready():
$\"..\".connect(\"dungeon_done_generating\", remove_unused_doors)
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
"
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_sva35"]
albedo_color = Color(1, 0.44, 0.44, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8485v"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1qq54"]
albedo_color = Color(0.5, 0.243333, 0.225, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ca5kw"]
albedo_color = Color(0.5, 0.243333, 0.225, 1)
[node name="Bedroom" type="Node3D"]
script = ExtResource("1_1c8qy")
size_in_voxels = Vector3i(3, 1, 3)
min_count = 5
max_count = 10
[node name="CSGBox3D" type="CSGBox3D" parent="."]
use_collision = true
size = Vector3(30, 10, 30)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(29, 9, 29)
[node name="DOOR" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.5, 0, -14.5)
operation = 2
size = Vector3(10, 9, 2)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.42782, 0)
operation = 2
size = Vector3(29, 0.2, 29)
material = SubResource("StandardMaterial3D_1b4dt")
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_4tekc")
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="."]
use_collision = true
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.5, -3, 0)
size = Vector3(8, 1, 8)
material = SubResource("StandardMaterial3D_sva35")
[node name="CSGBox3D9" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.5, -3, 0)
size = Vector3(4, 1, 8)
material = SubResource("StandardMaterial3D_8485v")
[node name="CSGBox3D4" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.91372, -3.3, 0)
size = Vector3(13.1274, 1, 8.3)
material = SubResource("StandardMaterial3D_1qq54")
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, -2, 0)
size = Vector3(1, 3, 8)
material = SubResource("StandardMaterial3D_ca5kw")
[node name="CSGBox3D5" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, -4.3, 3.5)
material = SubResource("StandardMaterial3D_ca5kw")
[node name="CSGBox3D6" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.3, 3.5)
material = SubResource("StandardMaterial3D_ca5kw")
[node name="CSGBox3D7" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, -4.3, -3.63669)
material = SubResource("StandardMaterial3D_ca5kw")
[node name="CSGBox3D8" type="CSGBox3D" parent="CSGCombiner3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.3, -3.63669)
material = SubResource("StandardMaterial3D_ca5kw")

View File

@@ -1,76 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://bn6lta2vs6qh8"]
[ext_resource type="Script" uid="uid://ce73fuh74l81l" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_m0wvh"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8tlf5"]
albedo_color = Color(0, 0, 0, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4tt88"]
albedo_color = Color(0.176419, 0.176419, 0.176419, 1)
[sub_resource type="Gradient" id="Gradient_nd35r"]
offsets = PackedFloat32Array(0.276119, 1)
colors = PackedColorArray(0.301961, 0, 0, 1, 0.555762, 0.29171, 0.357937, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_4grxn"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_4g5kp"]
color_ramp = SubResource("Gradient_nd35r")
noise = SubResource("FastNoiseLite_4grxn")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ovx52"]
albedo_texture = SubResource("NoiseTexture2D_4g5kp")
[sub_resource type="GDScript" id="GDScript_nl3oo"]
script/source = "extends Node
func _ready():
$\"..\".connect(\"dungeon_done_generating\", remove_unused_doors)
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
"
[node name="Corridor" type="Node3D"]
script = ExtResource("1_m0wvh")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
use_collision = true
size = Vector3(10, 10, 10)
material = SubResource("StandardMaterial3D_8tlf5")
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9, 9, 9)
material = SubResource("StandardMaterial3D_4tt88")
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.52714, 0)
operation = 2
size = Vector3(9, 0.096, 9)
material = SubResource("StandardMaterial3D_ovx52")
[node name="DOOR?" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -5)
operation = 2
size = Vector3(9, 9, 3)
[node name="DOOR?2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 5)
operation = 2
size = Vector3(9, 9, 3)
[node name="DOOR?3" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 4, 0, 0)
operation = 2
size = Vector3(9, 9, 3)
[node name="DOOR?4" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -4, 0, 0)
operation = 2
size = Vector3(9, 9, 3)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_nl3oo")

View File

@@ -1,158 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://bnxjj8dpefmfd"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_vbc11"]
[ext_resource type="Texture2D" uid="uid://i7rtr0ewxm5j" path="res://addons/SimpleDungeons/sample_assets/blue-checkerboard-cc0.png" id="2_1ufaf"]
[sub_resource type="Shader" id="Shader_vq8fj"]
code = "// NOTE: Shader automatically converted from Godot Engine 4.2.2.stable's StandardMaterial3D.
shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
uniform vec4 albedo : source_color;
uniform sampler2D texture_albedo : source_color,filter_linear_mipmap,repeat_enable;
uniform float point_size : hint_range(0,128);
uniform float roughness : hint_range(0,1);
uniform sampler2D texture_metallic : hint_default_white,filter_linear_mipmap,repeat_enable;
uniform vec4 metallic_texture_channel;
uniform sampler2D texture_roughness : hint_roughness_r,filter_linear_mipmap,repeat_enable;
uniform float specular;
uniform float metallic;
varying vec3 uv1_triplanar_pos;
uniform float uv1_blend_sharpness;
varying vec3 uv1_power_normal;
uniform vec3 uv1_scale;
uniform vec3 uv1_offset;
uniform vec3 uv2_scale;
uniform vec3 uv2_offset;
void vertex() {
vec3 normal = MODEL_NORMAL_MATRIX * NORMAL;
TANGENT = vec3(0.0,0.0,-1.0) * abs(normal.x);
TANGENT+= vec3(1.0,0.0,0.0) * abs(normal.y);
TANGENT+= vec3(1.0,0.0,0.0) * abs(normal.z);
TANGENT = inverse(MODEL_NORMAL_MATRIX) * normalize(TANGENT);
BINORMAL = vec3(0.0,1.0,0.0) * abs(normal.x);
BINORMAL+= vec3(0.0,0.0,-1.0) * abs(normal.y);
BINORMAL+= vec3(0.0,1.0,0.0) * abs(normal.z);
BINORMAL = inverse(MODEL_NORMAL_MATRIX) * normalize(BINORMAL);
uv1_power_normal=pow(abs(normal),vec3(uv1_blend_sharpness));
uv1_triplanar_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset + TIME * 0.01;
uv1_power_normal/=dot(uv1_power_normal,vec3(1.0));
uv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);
}
vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos) {
vec4 samp=vec4(0.0);
samp+= texture(p_sampler,p_triplanar_pos.xy) * p_weights.z;
samp+= texture(p_sampler,p_triplanar_pos.xz) * p_weights.y;
samp+= texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;
return samp;
}
void fragment() {
vec4 albedo_tex = triplanar_texture(texture_albedo,uv1_power_normal,uv1_triplanar_pos);
ALBEDO = albedo.rgb * albedo_tex.rgb;
float metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);
METALLIC = metallic_tex * metallic;
vec4 roughness_texture_channel = vec4(1.0,0.0,0.0,0.0);
float roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);
ROUGHNESS = roughness_tex * roughness;
SPECULAR = specular;
}
"
[sub_resource type="FastNoiseLite" id="FastNoiseLite_fe7vc"]
noise_type = 2
frequency = 0.0165
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_bispe"]
seamless = true
noise = SubResource("FastNoiseLite_fe7vc")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_g7p6c"]
render_priority = 0
shader = SubResource("Shader_vq8fj")
shader_parameter/albedo = Color(1, 1, 1, 1)
shader_parameter/texture_albedo = SubResource("NoiseTexture2D_bispe")
shader_parameter/point_size = 1.0
shader_parameter/roughness = 1.0
shader_parameter/metallic_texture_channel = null
shader_parameter/specular = 0.5
shader_parameter/metallic = 0.0
shader_parameter/uv1_blend_sharpness = 1.0
shader_parameter/uv1_scale = Vector3(0.08, 0.08, 0.08)
shader_parameter/uv1_offset = Vector3(0, 0, 0)
shader_parameter/uv2_scale = Vector3(1, 1, 1)
shader_parameter/uv2_offset = Vector3(0, 0, 0)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_07mhj"]
albedo_texture = ExtResource("2_1ufaf")
[sub_resource type="GDScript" id="GDScript_17gef"]
script/source = "extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_living_room_dungeon_done_generating():
remove_unused_doors()
"
[node name="LivingRoom" type="Node3D"]
script = ExtResource("1_vbc11")
size_in_voxels = Vector3i(3, 1, 2)
min_count = 5
max_count = 10
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, 0, 0)
material_override = SubResource("ShaderMaterial_g7p6c")
use_collision = true
size = Vector3(30, 10, 20)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(29.5, 9.5, 19.5)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 9.75)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R2_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R1_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L2_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L1_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, -4.825, 0)
material_override = SubResource("StandardMaterial3D_07mhj")
use_collision = true
size = Vector3(8, 0.2, 13)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_17gef")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_living_room_dungeon_done_generating"]

View File

@@ -1,90 +0,0 @@
[gd_scene load_steps=12 format=3 uid="uid://najwcu57f6bc"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_ht4j3"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="3_1hkio"]
[sub_resource type="Gradient" id="Gradient_n7jmt"]
colors = PackedColorArray(1, 1, 1, 1, 0.5832, 0.5832, 0.81, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_xy5n2"]
noise_type = 2
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_wo5pc"]
color_ramp = SubResource("Gradient_n7jmt")
noise = SubResource("FastNoiseLite_xy5n2")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0vchj"]
albedo_texture = SubResource("NoiseTexture2D_wo5pc")
uv1_scale = Vector3(4.165, 4.165, 4.165)
uv1_triplanar = true
uv1_world_triplanar = true
[sub_resource type="Gradient" id="Gradient_i80ea"]
offsets = PackedFloat32Array(0, 0.442623)
colors = PackedColorArray(0, 0, 0, 1, 0.52, 0.1872, 0.275947, 1)
[sub_resource type="FastNoiseLite" id="FastNoiseLite_805tn"]
noise_type = 3
seed = 16
frequency = 0.029
fractal_type = 0
fractal_octaves = 1
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_wr6uy"]
in_3d_space = true
generate_mipmaps = false
seamless = true
color_ramp = SubResource("Gradient_i80ea")
noise = SubResource("FastNoiseLite_805tn")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_hudx1"]
albedo_texture = SubResource("NoiseTexture2D_wr6uy")
uv1_scale = Vector3(0.705, 0.705, 0.705)
uv1_triplanar = true
uv1_world_triplanar = true
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_i5qk8"]
albedo_texture = SubResource("NoiseTexture2D_wr6uy")
uv1_scale = Vector3(0.705, 0.705, 0.705)
uv1_triplanar = true
uv1_world_triplanar = true
[node name="Stair" type="Node3D"]
script = ExtResource("1_ht4j3")
size_in_voxels = Vector3i(2, 2, 1)
min_count = 5
max_count = 30
is_stair_room = true
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
material_override = SubResource("StandardMaterial3D_0vchj")
use_collision = true
size = Vector3(20, 20, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(19.5, 19.5, 9.5)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.8, -7.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.9, 2.25, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGStairMaker3D" parent="." instance=ExtResource("3_1hkio")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.75, -4.75, 0)
material_override = SubResource("StandardMaterial3D_hudx1")
use_collision = true
size = Vector3(14, 10, 9.5)
num_stairs = 32
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.98486, -9.75, 0)
material_override = SubResource("StandardMaterial3D_i5qk8")
use_collision = true
size = Vector3(5.96973, 0.1, 4.2)

View File

@@ -1,59 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://bsvhllycm8t4i"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_8cd2g"]
[ext_resource type="Texture2D" uid="uid://bwe02yta75ooa" path="res://addons/SimpleDungeons/sample_assets/kenney-green-checkerboar-cc0.png" id="2_ylbpf"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_orb45"]
albedo_texture = ExtResource("2_ylbpf")
uv1_triplanar = true
[sub_resource type="GDScript" id="GDScript_8geu6"]
script/source = "@tool
extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_corridor_dungeon_done_generating():
remove_unused_doors()
"
[node name="Corridor" type="Node3D"]
script = ExtResource("1_8cd2g")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -5.68248e-07, 0, 0)
material_override = SubResource("StandardMaterial3D_orb45")
use_collision = true
size = Vector3(10, 10, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9.5, 9.5, 9.5)
[node name="DOOR?_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -5.25)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_8geu6")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_corridor_dungeon_done_generating"]

View File

@@ -1,93 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://54kdovp1yfl5"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_defih"]
[ext_resource type="Texture2D" uid="uid://bwe02yta75ooa" path="res://addons/SimpleDungeons/sample_assets/kenney-green-checkerboar-cc0.png" id="2_lnti6"]
[ext_resource type="Texture2D" uid="uid://djhy80eyqjqpv" path="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-checkboard-cc0.png" id="3_yqsq5"]
[ext_resource type="Texture2D" uid="uid://i7rtr0ewxm5j" path="res://addons/SimpleDungeons/sample_assets/blue-checkerboard-cc0.png" id="4_h1rri"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_76ghj"]
albedo_texture = ExtResource("2_lnti6")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_x7cu1"]
albedo_texture = ExtResource("3_yqsq5")
[sub_resource type="GDScript" id="GDScript_17gef"]
script/source = "extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_living_room_dungeon_done_generating():
remove_unused_doors()
"
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ao0hg"]
transparency = 1
depth_draw_mode = 1
albedo_color = Color(1, 1, 1, 0.454902)
albedo_texture = ExtResource("4_h1rri")
clearcoat_roughness = 1.0
uv1_triplanar = true
uv1_world_triplanar = true
[node name="GreenRoom" type="Node3D"]
script = ExtResource("1_defih")
size_in_voxels = Vector3i(3, 1, 2)
min_count = 5
max_count = 15
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, -4.74656, 0)
material_override = SubResource("StandardMaterial3D_76ghj")
use_collision = true
size = Vector3(30, 0.496826, 20)
[node name="CSGBox3D3" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, 4.75344, 0)
material_override = SubResource("StandardMaterial3D_x7cu1")
use_collision = true
size = Vector3(30, 0.496826, 20)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_17gef")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, 0, 0)
material_override = SubResource("StandardMaterial3D_ao0hg")
use_collision = true
size = Vector3(30, 10, 20)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(29.5, 9.5, 19.5)
[node name="DOOR?_R2_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R1_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L2_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L1_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGBox3D4" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 0, 4.65546e-15)
operation = 1
size = Vector3(32, 9, 21)
[node name="PlayerSpawnPoint" type="Node3D" parent="." groups=["player_spawn_point"]]
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_living_room_dungeon_done_generating"]

View File

@@ -1,40 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://cdffbslc7qhgi"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_13awt"]
[ext_resource type="Texture2D" uid="uid://i7rtr0ewxm5j" path="res://addons/SimpleDungeons/sample_assets/blue-checkerboard-cc0.png" id="2_tjfvs"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="3_ldwg4"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wrp25"]
albedo_texture = ExtResource("2_tjfvs")
[node name="Stair" type="Node3D"]
script = ExtResource("1_13awt")
size_in_voxels = Vector3i(2, 2, 1)
max_count = 50
is_stair_room = true
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
material_override = SubResource("StandardMaterial3D_wrp25")
use_collision = true
size = Vector3(20, 20, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(19.5, 19.5, 9.5)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.8, -7.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.9, 2.25, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGStairMaker3D" parent="." instance=ExtResource("3_ldwg4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.75, -4.75, 0)
use_collision = true
size = Vector3(14, 10, 9.5)
num_stairs = 32

View File

@@ -1,45 +0,0 @@
[gd_scene load_steps=6 format=3 uid="uid://c74pq22tm4lts"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_xaivw"]
[ext_resource type="Texture2D" uid="uid://djhy80eyqjqpv" path="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-checkboard-cc0.png" id="2_n22k3"]
[ext_resource type="Texture2D" uid="uid://dqvyc0ok7chld" path="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png" id="3_x6cl1"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jbl8u"]
albedo_texture = ExtResource("2_n22k3")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p3xxc"]
albedo_texture = ExtResource("3_x6cl1")
uv1_triplanar = true
uv1_world_triplanar = true
[node name="BridgeRoom" type="Node3D"]
script = ExtResource("1_xaivw")
size_in_voxels = Vector3i(1, 1, 2)
[node name="bridge" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, -4.775, 0)
material_override = SubResource("StandardMaterial3D_jbl8u")
operation = 2
use_collision = true
size = Vector3(2, 0.1, 19.5)
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
material_override = SubResource("StandardMaterial3D_p3xxc")
use_collision = true
size = Vector3(10, 10, 20)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.25, 0)
operation = 2
size = Vector3(9.5, 9, 19.5)
[node name="DOOR_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.27374e-13, -2.75, 9.75)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -10.25)
operation = 2
size = Vector3(2, 4, 1)

View File

@@ -1,59 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://c30fpfkorbyl6"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_6msra"]
[ext_resource type="Texture2D" uid="uid://bxbtgil61x5sx" path="res://addons/SimpleDungeons/sample_assets/kenney-orange-checkerboard-cc0.png" id="2_6nmer"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_orb45"]
albedo_texture = ExtResource("2_6nmer")
uv1_triplanar = true
[sub_resource type="GDScript" id="GDScript_8geu6"]
script/source = "@tool
extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_corridor_dungeon_done_generating():
remove_unused_doors()
"
[node name="Corridor" type="Node3D"]
script = ExtResource("1_6msra")
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -5.68248e-07, 0, 0)
material_override = SubResource("StandardMaterial3D_orb45")
use_collision = true
size = Vector3(10, 10, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9.5, 9.5, 9.5)
[node name="DOOR?_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -5.25)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_8geu6")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_corridor_dungeon_done_generating"]

View File

@@ -1,50 +0,0 @@
[gd_scene load_steps=6 format=3 uid="uid://jcrublu8sywn"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_0k746"]
[ext_resource type="Texture2D" uid="uid://wvh1xmoax4ok" path="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-grid-cc0.png" id="2_y8cer"]
[ext_resource type="Texture2D" uid="uid://bxbtgil61x5sx" path="res://addons/SimpleDungeons/sample_assets/kenney-orange-checkerboard-cc0.png" id="3_b5jl3"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_57r0k"]
albedo_texture = ExtResource("2_y8cer")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_k8fsj"]
albedo_texture = ExtResource("3_b5jl3")
uv1_triplanar = true
uv1_world_triplanar = true
[node name="EntranceRoom" type="Node3D"]
script = ExtResource("1_0k746")
min_count = 1
max_count = 1
[node name="FrontDoor" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -1.83588e-06, -2.75, -4.5)
material_override = SubResource("StandardMaterial3D_57r0k")
size = Vector3(5, 4, 0.5)
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -1.44248e-06, 0, 0)
material_override = SubResource("StandardMaterial3D_k8fsj")
use_collision = true
size = Vector3(10, 10, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(9.5, 9.5, 9.5)
[node name="DOOR_F_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="PlayerSpawnPoint" type="Node3D" parent="." groups=["player_spawn_point"]]

View File

@@ -1,75 +0,0 @@
[gd_scene load_steps=7 format=3 uid="uid://b8bnbf7bch1s0"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_y1oj8"]
[ext_resource type="Texture2D" uid="uid://dqvyc0ok7chld" path="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png" id="2_bphhi"]
[ext_resource type="Texture2D" uid="uid://djhy80eyqjqpv" path="res://addons/SimpleDungeons/sample_assets/kenney-dark-grey-checkboard-cc0.png" id="3_edxhr"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n0t7s"]
albedo_texture = ExtResource("2_bphhi")
uv1_triplanar = true
uv1_world_triplanar = true
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_x7cu1"]
albedo_texture = ExtResource("3_edxhr")
[sub_resource type="GDScript" id="GDScript_17gef"]
script/source = "extends Node
func remove_unused_doors():
for door in $\"..\".get_doors():
if door.get_room_leads_to() == null:
door.door_node.queue_free()
func _on_living_room_dungeon_done_generating():
remove_unused_doors()
"
[node name="LivingRoom" type="Node3D"]
script = ExtResource("1_y1oj8")
size_in_voxels = Vector3i(3, 1, 2)
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, 0, 0)
material_override = SubResource("StandardMaterial3D_n0t7s")
use_collision = true
size = Vector3(30, 10, 20)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(29.5, 9.5, 19.5)
[node name="DOOR?_B_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.75, 9.75)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R2_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_R1_CUT2" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L2_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, -5)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L1_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -15, -2.75, 5)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGBox3D2" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.49155e-06, -4.825, 0)
material_override = SubResource("StandardMaterial3D_x7cu1")
use_collision = true
size = Vector3(8, 0.2, 13)
[node name="RemoveUnusedDoors" type="Node" parent="."]
script = SubResource("GDScript_17gef")
[connection signal="dungeon_done_generating" from="." to="RemoveUnusedDoors" method="_on_living_room_dungeon_done_generating"]

View File

@@ -1,42 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://dxmljn5pbp0b1"]
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_6obq4"]
[ext_resource type="Texture2D" uid="uid://dqvyc0ok7chld" path="res://addons/SimpleDungeons/sample_assets/kenney-grey-checkerboard-cc0.png" id="2_s2snu"]
[ext_resource type="PackedScene" uid="uid://bak8ltrhbmlv5" path="res://addons/SimpleDungeons/utils/CSGStairMaker3D.tscn" id="3_icb60"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ddeon"]
albedo_texture = ExtResource("2_s2snu")
uv1_triplanar = true
uv1_world_triplanar = true
[node name="Stair" type="Node3D"]
script = ExtResource("1_6obq4")
size_in_voxels = Vector3i(2, 2, 1)
max_count = 15
is_stair_room = true
[node name="CSGBox3D" type="CSGBox3D" parent="."]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
material_override = SubResource("StandardMaterial3D_ddeon")
use_collision = true
size = Vector3(20, 20, 10)
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGBox3D"]
operation = 2
size = Vector3(19.5, 19.5, 9.5)
[node name="DOOR?_R_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 9.8, -7.75, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="DOOR?_L_CUT" type="CSGBox3D" parent="CSGBox3D"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -9.9, 2.25, 0)
operation = 2
size = Vector3(2, 4, 1)
[node name="CSGStairMaker3D" parent="." instance=ExtResource("3_icb60")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.75, -4.75, 0)
use_collision = true
size = Vector3(14, 10, 9.5)
num_stairs = 32

View File

@@ -1,110 +0,0 @@
class_name AABBi
# AABB class using integer Vector3 (Vector3i)
# Member variables
var position : Vector3i
var size : Vector3i
var end : Vector3i:
set(v):
if position.clamp(position, v.clamp(v, position)) < position:
position = position.clamp(position, v.clamp(position, v))
size = v - position
get:
return size + position
# Constructor
func _init(position : Vector3i = Vector3i(0, 0, 0), size : Vector3i = Vector3i(0, 0, 0)) -> void:
self.position = position
self.size = size
static func from_AABB_rounded(aabb : AABB) -> AABBi:
return AABBi.new(round(aabb.position), round(aabb.size))
func to_AABB() -> AABB:
return AABB(self.position, self.size)
# Method to check if a point is inside the AABB
func contains_point(point : Vector3i) -> bool:
return (
point.x >= position.x and point.x < position.x + size.x and
point.y >= position.y and point.y < position.y + size.y and
point.z >= position.z and point.z < position.z + size.z
)
# Method to check if another AABB intersects with this one
func intersects(aabb : AABBi) -> bool:
aabb = aabb.normalized()
var my := self.normalized()
# Find separating axis
if aabb.position.x >= my.end.x or my.position.x >= aabb.end.x: return false
if aabb.position.y >= my.end.y or my.position.y >= aabb.end.y: return false
if aabb.position.z >= my.end.z or my.position.z >= aabb.end.z: return false
return true
# Method to push this AABB inside another AABB (returns new AABBi)
func push_within(aabb : AABBi, ignore_y : bool) -> AABBi:
aabb = aabb.normalized()
var new_aabb = AABBi.new(self.position, self.size).normalized()
new_aabb.position = new_aabb.position.clamp(aabb.position, aabb.end - new_aabb.size)
if ignore_y:
new_aabb.position.y = self.position.y
return new_aabb
func expand_to_include(point : Vector3i) -> AABBi:
var new_position : Vector3i = Vector3i(
min(position.x, point.x),
min(position.y, point.y),
min(position.z, point.z)
)
var new_end_position : Vector3i = Vector3i(
max(end.x, point.x + 1),
max(end.y, point.y + 1),
max(end.z, point.z + 1)
)
var new_size : Vector3i = new_end_position - new_position
return AABBi.new(new_position, new_size)
# Method to check if two AABBs are equal
func equals(aabb : AABBi) -> bool:
return position == aabb.position and size == aabb.size
# Method to check if this AABB encloses another AABB
func encloses(aabb : AABBi) -> bool:
aabb = aabb.normalized()
return (
self.normalized().contains_point(aabb.position) and
self.normalized().contains_point(aabb.end - Vector3i(1, 1, 1))
)
# Method to normalize the AABB (ensures size is positive)
func normalized() -> AABBi:
var new_position : Vector3i = position
var new_size : Vector3i = size
if size.x < 0:
new_position.x += size.x
new_size.x = -size.x
if size.y < 0:
new_position.y += size.y
new_size.y = -size.y
if size.z < 0:
new_position.z += size.z
new_size.z = -size.z
return AABBi.new(new_position, new_size)
# Method to grow the AABB by a specified amount in all directions
func grow(amount : int) -> AABBi:
var new_position : Vector3i = position - Vector3i(amount, amount, amount)
var new_size : Vector3i = size + Vector3i(amount * 2, amount * 2, amount * 2)
return AABBi.new(new_position, new_size)
# Method to grow the AABB by a specified amount in the x and z directions
func grow_xz(amount : int) -> AABBi:
var new_position : Vector3i = position - Vector3i(amount, 0, amount)
var new_size : Vector3i = size + Vector3i(amount * 2, 0, amount * 2)
return AABBi.new(new_position, new_size)
func translated(pos : Vector3i) -> AABBi:
return AABBi.new(self.position + pos, self.size)

View File

@@ -1 +0,0 @@
uid://cljr83dctbi18

View File

@@ -1,80 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://bak8ltrhbmlv5"]
[sub_resource type="GDScript" id="GDScript_0eugj"]
script/source = "@tool
extends CSGBox3D
@export var num_stairs : int = 10
var _cur_num_stairs = -1
var _cur_size : Vector3
func add_fresh_stairs_csg_poly():
if has_node('./StairsSubtractCSG'):
var mesh = $StairsSubtractCSG
remove_child(mesh)
mesh.queue_free()
#print(\"removed old stairs\")
#print(\"adding stairs\")
var poly = CSGPolygon3D.new()
poly.name = 'StairsSubtractCSG'
add_child(poly)
#poly.owner = get_tree().edited_scene_root
poly.operation = CSGShape3D.OPERATION_SUBTRACTION
return poly
func make_stairs():
#if not Engine.is_editor_hint():
#return
#
num_stairs = clampi(num_stairs, 0, 999)
var stairs_poly = $StairsSubtractCSG#add_fresh_stairs_csg_poly()
var point_arr : PackedVector2Array = PackedVector2Array()
var step_height = self.size.y / num_stairs
var step_width = self.size.x / num_stairs
if num_stairs == 0:
# For 0 stairs make a ramp
point_arr.append(Vector2(self.size.x, self.size.y))
point_arr.append(Vector2(0, self.size.y))
point_arr.append(Vector2(0, 0))
else:
# Creating the points for the stairs polygon
for i in range(num_stairs - 1):
point_arr.append(Vector2(i * step_width, (i + 1) * step_height))
if i < num_stairs:
point_arr.append(Vector2((i + 1) * step_width, (i + 1) * step_height))
# Closing the polygon by adding the last two points
point_arr.append(Vector2(self.size.x - step_width, self.size.y))
point_arr.append(Vector2(0, self.size.y))
stairs_poly.polygon = point_arr
stairs_poly.depth = self.size.z
stairs_poly.position.z = self.size.z / 2.0
stairs_poly.position.y = -self.size.y / 2.0
stairs_poly.position.x = -self.size.x / 2.0
_cur_num_stairs = num_stairs
_cur_size = self.size
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
if _cur_num_stairs != num_stairs or _cur_size != self.size:
make_stairs()
"
[node name="CSGStairMaker3D" type="CSGBox3D"]
script = SubResource("GDScript_0eugj")
num_stairs = 4
[node name="StairsSubtractCSG" type="CSGPolygon3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, -0.5, 0.5)
operation = 2
polygon = PackedVector2Array(0, 0.25, 0.25, 0.25, 0.25, 0.5, 0.5, 0.5, 0.5, 0.75, 0.75, 0.75, 0.75, 1, 0, 1)

View File

@@ -1,79 +0,0 @@
class_name DungeonAStar3D
extends AStar3D
var doors_list = []
var pt_id_to_vec3i = {}
var vec3i_to_pt_id = {}
var dungeon_generator : DungeonGenerator3D
var rooms_check_dict : Dictionary # Vector3i : DungeonRoom3D (Non-Corridor) instance
var corridors_check_dict : Dictionary # Vector3i : DungeonRoom3D (Corridor) instance
var cap_required_doors_phase := false
func can_walk_from_to(dungeon_generator : DungeonGenerator3D, pos_a : Vector3i, pos_b : Vector3i) -> bool:
if not dungeon_generator.get_grid_aabbi().contains_point(pos_a): return false
if not dungeon_generator.get_grid_aabbi().contains_point(pos_b): return false
var room_a = rooms_check_dict[pos_a] if rooms_check_dict.has(pos_a) else null
var room_b = rooms_check_dict[pos_b] if rooms_check_dict.has(pos_b) else null
if room_a == null and room_b == null: return pos_a.y == pos_b.y # Outside rooms, only move horizontal
if room_a == room_b: return true # Inside rooms, move anywhere, up and down, i.e. stairs
# Ensure walking through doorways if not a simple case:
var fits_room_a_door = room_a == null or room_a.get_doors_cached().filter(func(d): return d.grid_pos == pos_a and d.exit_pos_grid == pos_b).size() == 1
var fits_room_b_door = room_b == null or room_b.get_doors_cached().filter(func(d): return d.grid_pos == pos_b and d.exit_pos_grid == pos_a).size() == 1
return fits_room_a_door and fits_room_b_door
func _init(dungeon_generator : DungeonGenerator3D, rooms_check_dict : Dictionary, corridors_check_dict : Dictionary):
self.dungeon_generator = dungeon_generator
self.rooms_check_dict = rooms_check_dict
self.corridors_check_dict = corridors_check_dict
# Add points to the AStar3D grid
var point_id = 0
for x in range(dungeon_generator.dungeon_size.x):
for y in range(dungeon_generator.dungeon_size.y):
for z in range(dungeon_generator.dungeon_size.z):
add_point(point_id, Vector3(x,y,z))
pt_id_to_vec3i[point_id] = Vector3i(x,y,z)
vec3i_to_pt_id[Vector3i(x,y,z)] = point_id
point_id += 1
var xyz_dirs := [Vector3i(1,0,0), Vector3i(-1,0,0), Vector3i(0,0,1), Vector3i(0,0,-1), Vector3i(0,1,0), Vector3i(0,-1,0)] as Array[Vector3i]
# Connect points - allow walking in & out of all doors but don't connect where walls are.
for x in range(dungeon_generator.dungeon_size.x):
for y in range(dungeon_generator.dungeon_size.y):
for z in range(dungeon_generator.dungeon_size.z):
var cur_pt_id = get_closest_point(Vector3(x,y,z))
# Allow walk in/out of room, up & down too for stairs
for dir in xyz_dirs:
if can_walk_from_to(dungeon_generator, Vector3i(x,y,z), Vector3i(x,y,z) + dir):
connect_points(cur_pt_id, get_closest_point(Vector3(x,y,z) + Vector3(dir)))
func _estimate_cost(from_id : int, to_id : int) -> float:
if dungeon_generator.astar_heuristic == DungeonGenerator3D.AStarHeuristics.NONE_DIJKSTRAS:
return 0.0
elif dungeon_generator.astar_heuristic == DungeonGenerator3D.AStarHeuristics.MANHATTAN:
var diff := get_point_position(to_id) - get_point_position(from_id)
return (abs(diff.x) + abs(diff.y) + abs(diff.z)) * dungeon_generator.heuristic_scale
elif dungeon_generator.astar_heuristic == DungeonGenerator3D.AStarHeuristics.EUCLIDEAN:
var diff := get_point_position(to_id) - get_point_position(from_id)
return diff.length() * dungeon_generator.heuristic_scale
return 0.0
func _compute_cost(from_id : int, to_id : int) -> float:
var diff := get_point_position(to_id) - get_point_position(from_id)
var cost := diff.length()
if rooms_check_dict.has(Vector3i(get_point_position(to_id).round())):
if not cap_required_doors_phase:
cost *= dungeon_generator.room_cost_multiplier
else:
cost *= dungeon_generator.room_cost_at_end_for_required_doors
if corridors_check_dict.has(Vector3i(get_point_position(to_id).round())):
cost *= dungeon_generator.corridor_cost_multiplier
return cost
func get_vec3i_path(from : Vector3i, to : Vector3i) -> Array[Vector3i]:
var path = get_id_path(vec3i_to_pt_id[from], vec3i_to_pt_id[to])
var path_vec3i = Array(path).map(func(pt_id : int): return pt_id_to_vec3i[pt_id])
var typefix : Array[Vector3i] = [] as Array[Vector3i]
typefix.assign(path_vec3i)
return typefix

View File

@@ -1 +0,0 @@
uid://c4ce53xhlmem0

View File

@@ -1,24 +0,0 @@
class_name RandomNumberMultiplayer
extends Node
## Can be used to seed the DungeonGenerator to sync the seed with all clients on multiplayer
# Connect to DungeonGenerator generate(seed) function
signal got_random_int(num : int)
var random_number : int
func _ready():
if is_multiplayer_authority():
random_number = randi()
emit_random_number(random_number)
else:
request_random_number.rpc_id(get_multiplayer_authority())
@rpc("authority", "call_remote", "reliable", 0)
func emit_random_number(num : int):
got_random_int.emit(num)
@rpc("any_peer", "call_remote", "reliable", 0)
func request_random_number():
emit_random_number.rpc_id(multiplayer.get_remote_sender_id(), random_number)

Some files were not shown because too many files have changed in this diff Show More