Skip to content

Commit

Permalink
Refactor the procedural generation of lavaland and turf/closed/minera…
Browse files Browse the repository at this point in the history
…l (#54915)

This replaces lavaland's old diagonal tunnel gen which was really
horrendously jammed into asteroid floor code (?????) with Cellular
Automata which runs in rust (PR for that here:
tgstation/rust-g#57 ). The new code is a bit
cleaner, but also looks better.

VID: https://streamable.com/a45ke2

Things to do:
- Make an icemoon version
- Fix the roundstart atmos adjacency issues

I asked AnturK if this was an acceptable PR for this month; he said it
was okay as long as I didn't add new areas, which I don't plan to do.
But if anyone thinks this PR breaks the spirit of the month I'll open
it again in december.
  • Loading branch information
Qustinnus authored and MarkSuckerberg committed Dec 13, 2020
1 parent 2814d65 commit 5df5585
Show file tree
Hide file tree
Showing 18 changed files with 266 additions and 308 deletions.
2 changes: 1 addition & 1 deletion _maps/RandomRuins/SpaceRuins/derelict4.dmm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/closed/mineral/random/no_caves,
/turf/closed/mineral/random,
/area/ruin/unpowered)
"c" = (
/turf/open/floor/plating/asteroid/airless,
Expand Down
4 changes: 2 additions & 2 deletions _maps/RandomRuins/SpaceRuins/derelict5.dmm
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/closed/mineral/random/no_caves,
/turf/closed/mineral/random,
/area/ruin/unpowered/no_grav)
"c" = (
/turf/open/floor/plating/asteroid/airless,
/area/ruin/unpowered/no_grav)
"d" = (
/turf/closed/mineral/random/no_caves,
/turf/closed/mineral/random,
/area/ruin/unpowered)
"e" = (
/turf/closed/wall,
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
/// If blobs can spawn there and if it counts towards their score.
#define BLOBS_ALLOWED (1<<1)
/// If mining tunnel generation is allowed in this area
#define TUNNELS_ALLOWED (1<<2)
#define CAVES_ALLOWED (1<<2)
/// If flora are allowed to spawn in this area randomly through tunnel generation
#define FLORA_ALLOWED (1<<3)
/// If mobs can be spawned by natural random generation
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,9 @@
#define STANDING_UP 0
/// Mob is lying down, usually associated with lying_angle values of 90 or 270.
#define LYING_DOWN 1

///How much a mob's sprite should be moved when they're lying down
#define PIXEL_Y_OFFSET_LYING -6

///Define for spawning megafauna instead of a mob for cave gen
#define SPAWN_MEGAFAUNA "bluh bluh huge boss"
44 changes: 37 additions & 7 deletions code/__DEFINES/rust_g.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,39 @@
#define RUST_G (__rust_g || __detect_rust_g())
#endif

#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
#define RUSTG_JOB_ERROR "JOB PANICKED"
/**
* This proc generates a cellular automata noise grid which can be used in procedural generation methods.
*
* Returns a single string that goes row by row, with values of 1 representing an alive cell, and a value of 0 representing a dead cell.
*
* Arguments:
* * percentage: The chance of a turf starting closed
* * smoothing_iterations: The amount of iterations the cellular automata simulates before returning the results
* * birth_limit: If the number of neighboring cells is higher than this amount, a cell is born
* * death_limit: If the number of neighboring cells is lower than this amount, a cell dies
* * width: The width of the grid.
* * height: The height of the grid.
*/
#define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \
call(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height)

#define rustg_dmi_strip_metadata(fname) call(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(path, width, height, data) call(RUST_G, "dmi_create_png")(path, width, height, data)
#define rustg_dmi_resize_png(path, width, height, resizetype) call(RUST_G, "dmi_resize_png")(path, width, height, resizetype)

#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
#define rustg_file_read(fname) call(RUST_G, "file_read")(fname)
#define rustg_file_exists(fname) call(RUST_G, "file_exists")(fname)
#define rustg_file_write(text, fname) call(RUST_G, "file_write")(text, fname)
#define rustg_file_append(text, fname) call(RUST_G, "file_append")(text, fname)

#ifdef RUSTG_OVERRIDE_BUILTINS
#define file2text(fname) rustg_file_read("[fname]")
#define text2file(text, fname) rustg_file_append(text, "[fname]")
#endif

#define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev)
#define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev)

#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format)
/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()

#define RUSTG_HTTP_METHOD_GET "get"
#define RUSTG_HTTP_METHOD_PUT "put"
#define RUSTG_HTTP_METHOD_DELETE "delete"
Expand All @@ -63,9 +81,21 @@
#define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers)
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)

#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
#define RUSTG_JOB_ERROR "JOB PANICKED"

#define rustg_json_is_valid(text) (call(RUST_G, "json_is_valid")(text) == "true")

#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format)
/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()

#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)

#define rustg_sql_connect_pool(options) call(RUST_G, "sql_connect_pool")(options)
#define rustg_sql_query_async(handle, query, params) call(RUST_G, "sql_query_async")(handle, query, params)
#define rustg_sql_query_blocking(handle, query, params) call(RUST_G, "sql_query_blocking")(handle, query, params)
#define rustg_sql_connected(handle) call(RUST_G, "sql_connected")(handle)
#define rustg_sql_disconnect_pool(handle) call(RUST_G, "sql_disconnect_pool")(handle)
#define rustg_sql_check_query(job_id) call(RUST_G, "sql_check_query")("[job_id]")

2 changes: 1 addition & 1 deletion code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ GLOBAL_LIST_INIT(bitfields, list(
"area_flags" = list(
"VALID_TERRITORY" = VALID_TERRITORY,
"BLOBS_ALLOWED" = BLOBS_ALLOWED,
"TUNNELS_ALLOWED" = TUNNELS_ALLOWED,
"CAVES_ALLOWED" = CAVES_ALLOWED,
"FLORA_ALLOWED" = FLORA_ALLOWED,
"MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED,
"MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED,
Expand Down
3 changes: 3 additions & 0 deletions code/_globalvars/lists/mapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ GLOBAL_LIST_EMPTY(sortedAreas)
GLOBAL_LIST_EMPTY_TYPED(areas_by_type, /area)

GLOBAL_LIST_EMPTY(all_abstract_markers)

/// Global list of megafauna spawns on cave gen
GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/megafauna/dragon = 4, /mob/living/simple_animal/hostile/megafauna/colossus = 2, /mob/living/simple_animal/hostile/megafauna/bubblegum = 6))
6 changes: 6 additions & 0 deletions code/controllers/subsystem/mapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ SUBSYSTEM_DEF(mapping)
repopulate_sorted_areas()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
run_map_generation()

#ifndef LOWMEMORYMODE
// Create space ruin levels
while (space_levels_so_far < config.space_ruin_levels)
Expand Down Expand Up @@ -290,6 +292,10 @@ GLOBAL_LIST_EMPTY(the_station_areas)
if(!GLOB.the_station_areas.len)
log_world("ERROR: Station areas list failed to generate!")

/datum/controller/subsystem/mapping/proc/run_map_generation()
for(var/area/A in world)
A.RunGeneration()

/datum/controller/subsystem/mapping/proc/maprotate()
if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
return
Expand Down
138 changes: 138 additions & 0 deletions code/datums/mapgen/CaveGenerator.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/datum/map_generator/cave_generator
var/name = "Cave Generator"
///Weighted list of the types that spawns if the turf is open
var/open_turf_types = list(/turf/open/floor/plating/asteroid = 1)
///Weighted list of the types that spawns if the turf is closed
var/closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)


///Weighted list of extra features that can spawn in the area, such as geysers.
var/list/feature_spawn_list = list(/obj/structure/geyser/random = 1)
///Weighted list of mobs that can spawn in the area.
var/list/mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
SPAWN_MEGAFAUNA = 4, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
///Weighted list of flora that can spawn in the area.
var/list/flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
// Weighted list of Megafauna that can spawn in the caves
var/list/megafauna_spawn_list


///Base chance of spawning a mob
var/mob_spawn_chance = 6
///Base chance of spawning flora
var/flora_spawn_chance = 2
///Base chance of spawning features
var/feature_spawn_chance = 0.1
///Unique ID for this spawner
var/string_gen

///Chance of cells starting closed
var/initial_closed_chance = 45
///Amount of smoothing iterations
var/smoothing_iterations = 20
///How much neighbours does a dead cell need to become alive
var/birth_limit = 4
///How little neighbours does a alive cell need to die
var/death_limit = 3

/datum/map_generator/cave_generator/New()
. = ..()
if(!megafauna_spawn_list)
megafauna_spawn_list = GLOB.megafauna_spawn_list

/datum/map_generator/cave_generator/generate_terrain(list/turfs)
. = ..()
var/start_time = REALTIMEOFDAY
string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data

for(var/i in turfs) //Go through all the turfs and generate them
var/turf/gen_turf = i

var/area/A = gen_turf.loc
if(!(A.area_flags & CAVES_ALLOWED))
continue

var/closed = text2num(string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x])

var/stored_flags
if(gen_turf.flags_1 & NO_RUINS_1)
stored_flags |= NO_RUINS_1

var/turf/new_turf = gen_turf.ChangeTurf(pickweight(closed ? closed_turf_types : open_turf_types), null, CHANGETURF_DEFER_CHANGE)
new_turf.flags_1 |= stored_flags

if(!closed)//Open turfs have some special behavior related to spawning flora and mobs.

var/turf/open/new_open_turf = new_turf

///Spawning isn't done in procs to save on overhead on the 60k turfs we're going through.

//FLORA SPAWNING HERE
var/atom/spawned_flora
if(flora_spawn_list && prob(flora_spawn_chance))
var/can_spawn = TRUE

if(!(A.area_flags & FLORA_ALLOWED))
can_spawn = FALSE
if(can_spawn)
spawned_flora = pickweight(flora_spawn_list)
spawned_flora = new spawned_flora(new_open_turf)

//FEATURE SPAWNING HERE
var/atom/spawned_feature
if(feature_spawn_list && prob(feature_spawn_chance))
var/can_spawn = TRUE

if(!(A.area_flags & FLORA_ALLOWED)) //checks the same flag because lol dunno
can_spawn = FALSE

var/atom/picked_feature = pickweight(feature_spawn_list)

for(var/obj/structure/F in range(7, new_open_turf))
if(istype(F, picked_feature))
can_spawn = FALSE

if(can_spawn)
spawned_feature = new picked_feature(new_open_turf)

//MOB SPAWNING HERE

if(mob_spawn_list && !spawned_flora && !spawned_feature && prob(mob_spawn_chance))
var/can_spawn = TRUE

if(!(A.area_flags & MOB_SPAWN_ALLOWED))
can_spawn = FALSE

var/atom/picked_mob = pickweight(mob_spawn_list)

if(picked_mob == SPAWN_MEGAFAUNA) //
if((A.area_flags & MEGAFAUNA_SPAWN_ALLOWED) && megafauna_spawn_list?.len) //this is danger. it's boss time.
picked_mob = pickweight(megafauna_spawn_list)
else //this is not danger, don't spawn a boss, spawn something else
picked_mob = pickweight(mob_spawn_list - SPAWN_MEGAFAUNA) //What if we used 100% of the brain...and did something (slightly) less shit than a while loop?

for(var/thing in urange(12, new_open_turf)) //prevents mob clumps
if(!ishostile(thing) && !istype(thing, /obj/structure/spawner))
continue
if((ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(thing)) && get_dist(new_open_turf, thing) <= 7)
can_spawn = FALSE //if there's a megafauna within standard view don't spawn anything at all
break
if(ispath(picked_mob, /mob/living/simple_animal/hostile/asteroid) || istype(thing, /mob/living/simple_animal/hostile/asteroid))
can_spawn = FALSE //if the random is a standard mob, avoid spawning if there's another one within 12 tiles
break
if((ispath(picked_mob, /obj/structure/spawner/lavaland) || istype(thing, /obj/structure/spawner/lavaland)) && get_dist(new_open_turf, thing) <= 2)
can_spawn = FALSE //prevents tendrils spawning in each other's collapse range
break

if(can_spawn)
if(ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna/bubblegum)) //there can be only one bubblegum, so don't waste spawns on it
megafauna_spawn_list.Remove(picked_mob)

new picked_mob(new_open_turf)
CHECK_TICK

var/message = "[name] finished in [(REALTIMEOFDAY - start_time)/10]s!"
to_chat(world, "<span class='boldannounce'>[message]</span>")
log_world(message)
18 changes: 18 additions & 0 deletions code/datums/mapgen/Cavegens/IcemoonCaves.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/datum/map_generator/cave_generator/icemoon
open_turf_types = list(/turf/open/floor/plating/asteroid/snow/icemoon = 19, /turf/open/floor/plating/ice/icemoon = 1)
closed_turf_types = list(/turf/closed/mineral/random/snow = 1)


feature_spawn_list = list(/obj/structure/geyser/random = 1)
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
/mob/living/simple_animal/hostile/asteroid/polarbear = 30, /obj/structure/spawner/ice_moon/polarbear = 3, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10, \
/mob/living/simple_animal/hostile/asteroid/lobstrosity = 15)
flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/rock/icy = 2, /obj/structure/flora/rock/pile/icy = 2, /obj/structure/flora/grass/both = 6, /obj/structure/flora/ash/chilly = 2)

/datum/map_generator/cave_generator/icemoon/surface
flora_spawn_chance = 4
mob_spawn_list = null
initial_closed_chance = 40
birth_limit = 5
death_limit = 4
16 changes: 16 additions & 0 deletions code/datums/mapgen/Cavegens/LavalandGenerator.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/datum/map_generator/cave_generator/lavaland
open_turf_types = list(/turf/open/floor/plating/asteroid/basalt/lava_land_surface = 1)
closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)


feature_spawn_list = list(/obj/structure/geyser/random = 1)
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
SPAWN_MEGAFAUNA = 4, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)

initial_closed_chance = 45
smoothing_iterations = 50
birth_limit = 4
death_limit = 3
15 changes: 14 additions & 1 deletion code/game/area/areas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,22 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/LateInitialize()
power_change() // all machines set to current power level, also updates icon
update_beauty()

/area/proc/RunGeneration()
if(map_generator)
map_generator = new map_generator()
map_generator.generate_terrain(get_area_turfs(src))
var/list/turfs = list()
for(var/turf/T in contents)
turfs += T
map_generator.generate_terrain(turfs)

/area/proc/test_gen()
if(map_generator)
var/list/turfs = list()
for(var/turf/T in contents)
turfs += T
map_generator.generate_terrain(turfs)


/**
* Register this area as belonging to a z level
Expand Down
6 changes: 4 additions & 2 deletions code/game/area/areas/mining.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
outdoors = TRUE
flags_1 = NONE
ambientsounds = MINING
area_flags = VALID_TERRITORY | UNIQUE_AREA | FLORA_ALLOWED | TUNNELS_ALLOWED
area_flags = VALID_TERRITORY | UNIQUE_AREA | FLORA_ALLOWED | CAVES_ALLOWED

/area/mine/lobby
name = "Mining Station"
Expand Down Expand Up @@ -118,10 +118,12 @@

/area/lavaland/surface/outdoors/unexplored //monsters and ruins spawn here
icon_state = "unexplored"
area_flags = VALID_TERRITORY | UNIQUE_AREA | CAVES_ALLOWED | FLORA_ALLOWED | MOB_SPAWN_ALLOWED

/area/lavaland/surface/outdoors/unexplored/danger //megafauna will also spawn here
icon_state = "danger"
area_flags = VALID_TERRITORY | UNIQUE_AREA | TUNNELS_ALLOWED | FLORA_ALLOWED | MOB_SPAWN_ALLOWED | MEGAFAUNA_SPAWN_ALLOWED
area_flags = VALID_TERRITORY | UNIQUE_AREA | CAVES_ALLOWED | FLORA_ALLOWED | MOB_SPAWN_ALLOWED | MEGAFAUNA_SPAWN_ALLOWED
map_generator = /datum/map_generator/cave_generator/lavaland

/area/lavaland/surface/outdoors/explored
name = "Lavaland Labor Camp"
Expand Down
Loading

0 comments on commit 5df5585

Please sign in to comment.