Blockwise mazes

This commit is contained in:
AffectedArc07
2021-01-24 19:03:53 +00:00
parent 1f96eeec75
commit 55612c43f7
8 changed files with 203 additions and 44 deletions
+1 -1
View File
@@ -14,5 +14,5 @@ SUBSYSTEM_DEF(late_mapping)
log_startup_progress("Generating mazes...")
for(var/i in maze_generators)
var/obj/effect/mazegen/generator/MG = i
MG.generate()
MG.run_generator()
return ..()
@@ -1,12 +1,14 @@
/obj/effect/mazegen/module_helper
name = "maze helper"
desc = "You should not be seeing this!"
/**
* Helper handler proc
*
* This exists as an overridable method so you can do custom helper logic.
* An example of this is removing all windows on a tile.
*
* Arguments:
* * blockwise - This will be TRUE if the maze this is running on is a blockwise maze
*/
/obj/effect/mazegen/module_helper/proc/helper_run()
/obj/effect/mazegen/module_helper/proc/helper_run(blockwise = FALSE, obj/effect/mazegen/host)
CRASH("spawn_loot() not overriden for [type]")
@@ -2,8 +2,12 @@
/obj/effect/mazegen/module_helper/entry_exit
name = "entrance/exit allocator"
/obj/effect/mazegen/module_helper/entry_exit/helper_run()
for(var/obj/structure/window/reinforced/mazeglass/MG in get_turf(src))
qdel(MG)
/obj/effect/mazegen/module_helper/entry_exit/helper_run(blockwise = FALSE, obj/effect/mazegen/host)
if(blockwise)
var/turf/T = get_turf(src)
var/obj/effect/mazegen/generator/blockwise/BWG = host
T.ChangeTurf(BWG.floor_material)
else
for(var/obj/structure/window/reinforced/mazeglass/MG in get_turf(src))
qdel(MG)
qdel(src)
@@ -1,6 +1,5 @@
/obj/effect/mazegen/module_loot
name = "maze loot"
desc = "You should not be seeing this!"
/// Probability for this to spawn in a dead end at all. 0-100%. I dont recommend using values higher than 10
var/spawn_probability = 0
+64 -36
View File
@@ -9,6 +9,15 @@
*/
// Nice way to format logs
#define LOG_MAZE_PROGRESS(proc2run, opname) \
do { \
var/timer = start_watch(); \
proc2run ;\
log_debug("\[MAZE] Operation '[opname]' on maze at [##x],[##y],[##z] took [stop_watch(timer)]s"); \
} while (FALSE)
// These defines are used to mark the cells as explored or not
#define MAZEGEN_TURF_UNSEARCHED "#ff0000"
#define MAZEGEN_TURF_CELL "#00ff00"
@@ -16,17 +25,16 @@
// Dont place this in the very corner of a map. It relies on adjacent turfs, and at the very edges you dont have turfs on all sides
/obj/effect/mazegen/generator
name = "maze generator"
desc = "You should not be seeing this!"
/// List of turfs to iterate in total
var/list/turf_list = list()
/// "Stack" structure to be used while iterating
var/list/working_stack = list()
/// List of all loot modules being used in this maze
var/list/obj/effect/mazegen/module_loot/loot_modules = list()
/// Total turfs
var/total_turfs = 0
/// Generation start time
var/start_time = 0
/// List of all helper modules being used in this maze
var/list/obj/effect/mazegen/module_helper/helper_modules = list()
/// Potential loot spots
var/list/turf/potential_loot_spots = list()
/// Maze width
var/mwidth = 0
/// Maze height
@@ -50,13 +58,21 @@
working_stack.Remove(T) // Take it off the top
return T // Send it back
// This is the main proc that does the work. It can get very performance intensive if youre not careful
// I was testing this and I managed to make the MC erase itself. Dont ask.
/obj/effect/mazegen/generator/proc/generate()
// Setup some initial vars
var/start_time = start_watch()
log_debug("Starting to generate maze at [x],[y],[z] (WxH: [mwidth]x[mheight])")
// Main generator runner. Override on children
/obj/effect/mazegen/generator/proc/run_generator()
ASSERT(ISODD(mwidth))
ASSERT(ISODD(mheight))
var/total_time = start_watch()
log_debug("\[MAZE] Started generation on maze at [x],[y],[z] | [mwidth * mheight] turfs total")
LOG_MAZE_PROGRESS(generate_path(), "Path Generation")
LOG_MAZE_PROGRESS(apply_helper_modules(FALSE), "Helper Modules")
if(length(loot_modules)) // Only bother with this if we have some
LOG_MAZE_PROGRESS(calculate_loot_spots(), "Loot Spot Calculation")
LOG_MAZE_PROGRESS(apply_loot_modules(), "Loot Modules")
log_debug("\[MAZE] Generation of maze at [x],[y],[z] complete within [stop_watch(total_time)]s")
qdel(src)
/obj/effect/mazegen/generator/proc/generate_path()
// Setup our turf list
var/width = clamp(mwidth, 0, (world.maxx - x)) // Make sure they dont loop off the world
var/height = clamp(mheight, 0, (world.maxy - y)) // Make sure they dont loop off the world
@@ -82,9 +98,6 @@
CHECK_TICK
CHECK_TICK
total_turfs = length(turf_list)
log_debug("Maze at [x],[y],[z] has [total_turfs] turfs to process")
// Do the actual work
push_turf(turf_list[1]) // Use the first turf as the stack base
while(length(working_stack))
@@ -134,41 +147,56 @@
loot_modules |= ML
CHECK_TICK
// Apply helper modules
// Gather helper modules
for(var/obj/effect/mazegen/module_helper/MH in T)
MH.helper_run()
helper_modules |= MH
CHECK_TICK
// Make sure we have adequate CPU
CHECK_TICK
// Apply them. Presence check the list first to save CPU.
if(length(loot_modules))
for(var/i in turf_list)
var/turf/T = i
// Gather the windows. If a spot has 3 windows on it, its a dead end
var/windowcount = 0
for(var/obj/structure/window/reinforced/mazeglass/W in T)
pass(W) // Stops DM whining about unused vars
windowcount++
CHECK_TICK
// Its a dead end. Apply loot.
if(windowcount == 3)
// Grab a random one
var/obj/effect/mazegen/module_loot/ML = pick(loot_modules)
if(prob(ML.spawn_probability))
ML.spawn_loot(T) // Spawn on the turf
/obj/effect/mazegen/generator/proc/apply_helper_modules(blockwise = FALSE)
// Apply helpers
for(var/i in helper_modules)
var/obj/effect/mazegen/module_helper/MH = i
helper_modules -= MH // Make it happy
MH.helper_run(blockwise, src)
qdel(MH)
/obj/effect/mazegen/generator/proc/calculate_loot_spots()
for(var/i in turf_list)
var/turf/T = i
// Gather the windows. If a spot has 3 windows on it, its a dead end
var/windowcount = 0
for(var/obj/structure/window/reinforced/mazeglass/W in T)
pass(W) // Stops DM whining about unused vars
windowcount++
CHECK_TICK
// Its a dead end. Mark for loot.
if(windowcount == 3)
potential_loot_spots |= T
CHECK_TICK
/obj/effect/mazegen/generator/proc/apply_loot_modules()
for(var/i in potential_loot_spots)
var/turf/T = i
// Apply loot modules
var/obj/effect/mazegen/module_loot/ML = pick(loot_modules)
if(prob(ML.spawn_probability))
ML.spawn_loot(T) // Spawn on the turf
CHECK_TICK
// Cleanup loot modules
for(var/obj/effect/mazegen/module_loot/ML in loot_modules)
for(var/i in loot_modules)
var/obj/effect/mazegen/module_loot/ML = i
loot_modules -= ML // Take it out to make the GC happy
qdel(ML)
CHECK_TICK
log_debug("Generation of maze at [x],[y],[z] completed within [stop_watch(start_time)] seconds")
qdel(src)
#undef MAZEGEN_TURF_UNSEARCHED
#undef MAZEGEN_TURF_CELL
@@ -0,0 +1,124 @@
// Everything in this file is related to the blockwise maze generator.
// It uses some components from the standard, but tweaks them to its own needs, since it needs to generate a half size maze then expand it out
#define MAZEGEN_TURF_UNSEARCHED "#ff0000"
#define MAZEGEN_TURF_CELL "#00ff00"
/obj/effect/mazegen/generator/blockwise
name = "blockwise maze generator"
/// Material to make the walls out of
var/turf/wall_material = /turf/unsimulated/wall
/// Material to make the floor out of
var/turf/floor_material = /turf/simulated/floor/engine
/// List of open spots (speeds up calculations)
var/list/turf/open_spots = list()
// First we need to override the main proc so it only calculates half
/obj/effect/mazegen/generator/blockwise/run_generator()
ASSERT(ISODD(mwidth))
ASSERT(ISODD(mheight))
var/total_time = start_watch()
log_debug("\[MAZE] Started generation on maze at [x],[y],[z] | [mwidth * mheight] turfs total")
// First we need to partition the maze out into forced walls and open cells
LOG_MAZE_PROGRESS(generate_path(), "Generate Path")
LOG_MAZE_PROGRESS(apply_helper_modules(TRUE), "Helper Modules")
if(length(loot_modules)) // Only bother with this if we have some
LOG_MAZE_PROGRESS(calculate_loot_spots(), "Loot Spot Calculation")
LOG_MAZE_PROGRESS(apply_loot_modules(), "Loot Modules")
log_debug("\[MAZE] Generation of maze at [x],[y],[z] complete within [stop_watch(total_time)]s")
qdel(src)
/obj/effect/mazegen/generator/blockwise/generate_path()
// Setup our turf list
var/width = clamp(mwidth, 0, (world.maxx - x)) // Make sure they dont loop off the world
var/height = clamp(mheight, 0, (world.maxy - y)) // Make sure they dont loop off the world
// Generate a turf stack
for(var/target_x in 0 to (width - 1)) // -1 so it spawns on the right tile
for(var/target_y in 0 to (height - 1))
var/turf/T = locate((x + target_x), (y + target_y), z)
// Mark as unsearched
T.color = MAZEGEN_TURF_UNSEARCHED
// Throw it in the list
turf_list |= T
CHECK_TICK
CHECK_TICK
// Do the actual work
push_turf(turf_list[1]) // Use the first turf as the stack base
while(length(working_stack))
var/turf/T = pop_turf()
// If you dont force cast these to strings, stuff cries. A lot.
var/list/cardinals = list("[NORTH]", "[SOUTH]", "[EAST]", "[WEST]")
// Unvisited turfs
var/list/turf/unvisited_neighbours = list()
// Check all cardinal turfs
for(var/D in cardinals)
var/turf/T2 = get_step(get_step(T, text2num(D)), text2num(D)) // Get the step TWICE because this is wallwise
if(T2?.color == MAZEGEN_TURF_UNSEARCHED)
unvisited_neighbours["[D]"] += T2
if(length(unvisited_neighbours))
push_turf(T)
var/D = pick(unvisited_neighbours)
var/turf/T3 = unvisited_neighbours["[D]"] // Pick random dir turf
// Remove the color between the two
var/turf/T4 = get_step(T3, GetOppositeDir(text2num(D)))
T4?.color = MAZEGEN_TURF_CELL
// Mark as visited
T3.color = MAZEGEN_TURF_CELL
push_turf(T3)
CHECK_TICK
// The walls have been generated, now cleanup turfs and gather helpers
for(var/i in turf_list)
var/turf/T = i
// Set correct turfs
if(T.color == MAZEGEN_TURF_CELL)
open_spots |= T
T.ChangeTurf(floor_material)
else
T.ChangeTurf(wall_material)
T.color = null
// Gather loot modules
for(var/obj/effect/mazegen/module_loot/ML in T)
loot_modules |= ML
CHECK_TICK
// Gather helper modules
for(var/obj/effect/mazegen/module_helper/MH in T)
helper_modules |= MH
CHECK_TICK
// Make sure we have adequate CPU
CHECK_TICK
/obj/effect/mazegen/generator/blockwise/calculate_loot_spots()
for(var/i in open_spots)
var/turf/T = i
// Gather amount of dense turfs on each side
var/dense_surrounding_turfs = 0
// Again, these gotta be casted like this
var/list/cardinals = list("[NORTH]", "[EAST]", "[SOUTH]", "[WEST]")
for(var/D in cardinals)
var/turf/T2 = get_step(T, text2num(D))
if(T2.density)
dense_surrounding_turfs++
CHECK_TICK
// Its a dead end. Mark for loot.
if(dense_surrounding_turfs == 3)
potential_loot_spots |= T
CHECK_TICK
#undef MAZEGEN_TURF_UNSEARCHED
#undef MAZEGEN_TURF_CELL
@@ -1,5 +1,6 @@
// Initial declaration
/obj/effect/mazegen
desc = "You should not be seeing this!"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "x2"
color = "#00FF00"
+1
View File
@@ -1721,6 +1721,7 @@
#include "code\modules\martial_arts\combos\sleeping_carp\stomach_knee.dm"
#include "code\modules\martial_arts\combos\sleeping_carp\wrist_wrench.dm"
#include "code\modules\maze_generation\maze_generator.dm"
#include "code\modules\maze_generation\maze_generator_blockwise.dm"
#include "code\modules\maze_generation\maze_helper_atoms.dm"
#include "code\modules\maze_generation\helper_modules\_helper_module_base.dm"
#include "code\modules\maze_generation\helper_modules\entry_exit.dm"