mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
[MDB IGNORE] Procedurally Generated Maints (Sorry no Gax in this PR just the tools) (#20555)
* i think it's ready * this rustg define should match what it will be after the rust code is merged * adding some documentation * ready again * my rust dll was out of date lmao * fix things that makes biome cry * i think it's ready * this rustg define should match what it will be after the rust code is merged * will this fix the linter? * was that the issue? * rust update * ready again * rip multi-z gax, you will return later * my rust dll was out of date lmao * fix things that makes biome cry * why do these changes keep lingering * that's already done * is this alphabetical now * why was there duplicates that required me to use the web editor to fix * how did you get deleted * that would have been bad lmao * probably not super necessary but whatever * that was bothering me * that's already defined * ok i think that's all the cleanup * lastly stairs
This commit is contained in:
22832
_maps/map_files/debug/MaintStation.dmm
Normal file
22832
_maps/map_files/debug/MaintStation.dmm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -77,6 +77,7 @@ Always compile, always use that verb, and always make sure that it works for wha
|
||||
#define ZTRAIT_STATION "Station"
|
||||
#define ZTRAIT_MINING "Mining"
|
||||
#define ZTRAIT_REEBE "Reebe"
|
||||
#define ZTRAIT_PROCEDURAL_MAINTS "Random Maints"
|
||||
#define ZTRAIT_RESERVED "Transit/Reserved"
|
||||
#define ZTRAIT_AWAY "Away Mission"
|
||||
#define ZTRAIT_SPACE_RUINS "Space Ruins"
|
||||
@@ -143,6 +144,14 @@ Always compile, always use that verb, and always make sure that it works for wha
|
||||
|
||||
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.60)
|
||||
|
||||
#define ZTRAITS_BACKROOM_MAINTS list(\
|
||||
ZTRAIT_PROCEDURAL_MAINTS = TRUE, \
|
||||
ZTRAIT_LINKAGE = SELFLOOPING, \
|
||||
ZTRAIT_GRAVITY = TRUE, \
|
||||
ZTRAIT_NOPHASE = TRUE, \
|
||||
ZTRAIT_NOXRAY = TRUE, \
|
||||
ZTRAIT_BASETURF = /turf/open/floor/plating)
|
||||
|
||||
///Z level traits for Away Missions
|
||||
#define ZTRAITS_AWAY list(ZTRAIT_AWAY = TRUE)
|
||||
///Z level traits for Secret Away Missions
|
||||
|
||||
@@ -91,6 +91,31 @@
|
||||
*/
|
||||
#define rustg_acreplace_with_replacements(key, text, replacements) RUSTG_CALL(RUST_G, "acreplace_with_replacements")(key, text, json_encode(replacements))
|
||||
|
||||
/**
|
||||
* This proc generates rooms in a specified area of random size and placement. Essential for procedurally generated areas, BSP works by cutting a given area in half,
|
||||
* then cutting one of those subsections in half, and repeating this process until a minimum size is reached, then backtracking to other subsections that are not of
|
||||
* the minimum size yet. These cuts are offset by small random amounts so that the sections are all varied in size and shape.
|
||||
*
|
||||
* BSP excels at creating rooms or areas with a relatively even distribution over an area, so there won't be too much blank open area. However if you discard rooms that
|
||||
* overlap pre-existing map structures or areas, you may still get blank areas where nothing interesting appears.
|
||||
*
|
||||
* Return:
|
||||
* * a json list of room data to be processed by json_decode in byond and further processed there.
|
||||
*
|
||||
* Arguments:
|
||||
* * width: the width of the area to generate in
|
||||
* * height: the height of the area to generate in
|
||||
* * hash: the rng seed the generator will use for this instance
|
||||
* * map_subsection_min_size: The minimum size of a map subsection. When using this for rooms with walls, the minimum possible square will be a 5x5 room. Barring walls,
|
||||
* this will be a 3x3 room. The maximum size will be 9x9, because a further cut could reduce this size beneath the minimum size.
|
||||
* * map_subsection_min_room_width: The minimum room width once the subsections are finalized. Room width and height are random between this amount, and the subsection
|
||||
* max size
|
||||
* * map_subsection_min_room_height: The minimum room height once the subsections are finalized. Room width and height are random between this amount, and the subsection
|
||||
* max size
|
||||
*/
|
||||
#define rustg_bsp_generate(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) \
|
||||
RUSTG_CALL(RUST_G, "bsp_generate")(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height)
|
||||
|
||||
/**
|
||||
* This proc generates a cellular automata noise grid which can be used in procedural generation methods.
|
||||
*
|
||||
@@ -216,6 +241,31 @@
|
||||
*/
|
||||
#define rustg_generate_path_astar(start_node_id, goal_node_id) RUSTG_CALL(RUST_G, "generate_path_astar")(start_node_id, goal_node_id)
|
||||
|
||||
/**
|
||||
* This proc generates rooms in a specified area of random size and placement. Used in procedural generation, but far less intensively than Binary Space Partitioning
|
||||
* due to Random Room Placement being far more simple and unreliable for area coverage. These rooms will not overlap one another, but that is the only logic
|
||||
* they do. The room dimensions returned by this call are hardcoded to be the dimensions of maint ruins so that I could sprinkle pre-generated areas over
|
||||
* the binary space rooms that are random.
|
||||
* These dimensions are:
|
||||
* * 3x3
|
||||
* * 3x5
|
||||
* * 5x3
|
||||
* * 5x4
|
||||
* * 10x5
|
||||
* * 10x10
|
||||
*
|
||||
* Return:
|
||||
* * a json list of room data to be processed by json_decode in byond and further processed there.
|
||||
*
|
||||
* Arguments:
|
||||
* * width: the width of the area to generate in
|
||||
* * height: the height of the area to generate in
|
||||
* * desired_room_count: the number of rooms you want generated and returned
|
||||
* * hash: the rng seed the generator will use for this instance
|
||||
*/
|
||||
#define rustg_random_room_generate(width, height, desired_room_count, hash) \
|
||||
RUSTG_CALL(RUST_G, "random_room_generate")(width, height, desired_room_count, hash)
|
||||
|
||||
#define RUSTG_REDIS_ERROR_CHANNEL "RUSTG_REDIS_ERROR_CHANNEL"
|
||||
|
||||
#define rustg_redis_connect(addr) RUSTG_CALL(RUST_G, "redis_connect")(addr)
|
||||
@@ -285,5 +335,3 @@
|
||||
*/
|
||||
#define rustg_worley_generate(region_size, threshold, node_per_region_chance, size, node_min, node_max) \
|
||||
RUSTG_CALL(RUST_G, "worley_generate")(region_size, threshold, node_per_region_chance, size, node_min, node_max)
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
#define INIT_ORDER_QUIRKS 73
|
||||
#define INIT_ORDER_EVENTS 70
|
||||
#define INIT_ORDER_JOBS 65
|
||||
#define INIT_ORDER_PATH 61
|
||||
#define INIT_ORDER_TICKER 55
|
||||
#define INIT_ORDER_MAPPING 50
|
||||
#define INIT_ORDER_EARLY_ASSETS 48
|
||||
@@ -172,7 +173,6 @@
|
||||
#define INIT_ORDER_SHUTTLE -21
|
||||
#define INIT_ORDER_MINOR_MAPPING -40
|
||||
#define INIT_ORDER_BLUESPACE_LOCKER -45
|
||||
#define INIT_ORDER_PATH -50
|
||||
#define INIT_ORDER_DISCORD -60
|
||||
#define INIT_ORDER_EXPLOSIONS -69
|
||||
#define INIT_ORDER_STATPANELS -97
|
||||
|
||||
37
code/__DEFINES/{yogs_defines}/mapping.dm
Normal file
37
code/__DEFINES/{yogs_defines}/mapping.dm
Normal file
@@ -0,0 +1,37 @@
|
||||
/** Defines for the Dungeon Generator.
|
||||
* These bitflags control which elements are present in each room theme so the parent room of the theme knows which furnishing procs it needs to call
|
||||
*/
|
||||
///If the room has specific floor tiles
|
||||
#define ROOM_HAS_FLOORS (1<<0)
|
||||
///If the room has specific wall tiles
|
||||
#define ROOM_HAS_WALLS (1<<1)
|
||||
///If the room has windows and specific window type
|
||||
#define ROOM_HAS_WINDOWS (1<<2)
|
||||
///If the room has windows and specific window type, but no walls to spawn, so this room should just be a glass box
|
||||
#define ROOM_HAS_ONLY_WINDOWS (1<<3)
|
||||
///If the room has specific door type
|
||||
#define ROOM_HAS_DOORS (1<<4)
|
||||
///If we want the room shape, but then the room to do something very specific. This is how I handle ruin spawning
|
||||
#define ROOM_HAS_SPECIAL_FEATURES (1<<5)
|
||||
///If the room has specific decorative features like blood splatters or loot spawners
|
||||
#define ROOM_HAS_FEATURES (1<<6)
|
||||
///If the room has mobs to spawn, whether they're hostile spiders or harmless rats
|
||||
#define ROOM_HAS_MOBS (1<<7)
|
||||
|
||||
//The danger the room presents ranging from always safe to you should die NOW
|
||||
#define ROOM_RATING_SAFE (1<<0)
|
||||
#define ROOM_RATING_HOSTILE (1<<1)
|
||||
#define ROOM_RATING_EXTREMELY_HOSTILE (1<<2)
|
||||
#define ROOM_RATING_DEATH (1<<3)
|
||||
|
||||
//For categorizing rooms
|
||||
#define ROOM_TYPE_RANDOM "random"
|
||||
#define ROOM_TYPE_SPACE "space"
|
||||
#define ROOM_TYPE_RUIN "ruin"
|
||||
#define ROOM_TYPE_SECRET "secret"
|
||||
|
||||
#define VERTICAL "vertical"
|
||||
#define HORIZONTAL "horizontal"
|
||||
|
||||
#define ALGORITHM_BSP "Binary Search Partition"
|
||||
#define ALGORITHM_RRP "Random Room Placement"
|
||||
@@ -234,3 +234,7 @@ Actual Adjacent procs :
|
||||
if(!istype(D, /obj/structure/window) && D.density) //had to do it silly like this so rwindows didn't stop it outright
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/turf/proc/wiringTurfTest(caller, turf/T, ID, simulated_only)
|
||||
if(T && !T.density && !istype(T.loc, /area/space))
|
||||
return TRUE
|
||||
|
||||
274
code/datums/mapgen/DungeonGenerator.dm
Normal file
274
code/datums/mapgen/DungeonGenerator.dm
Normal file
@@ -0,0 +1,274 @@
|
||||
/datum/map_generator/dungeon_generator
|
||||
var/name = "Dungeon Generator"
|
||||
|
||||
///The original area that kicked off the whole generator. Used as a basis for all area based decisions
|
||||
var/area/area_ref
|
||||
///The area that this generator is working in
|
||||
var/list/areas_included = list()
|
||||
///Turfs that the generator can use to generate stuff. As rooms are created, they are deducted from this list so that in the end there is one "main" area left over
|
||||
var/list/working_turfs = list()
|
||||
|
||||
///Width of the area being generated. Dynamically determined at init by getting the min and max x values
|
||||
var/width
|
||||
///Height of the area being generated. Dynamically determined at init by getting the min and max y values
|
||||
var/height
|
||||
|
||||
///Minimum and Maximum X and Y coordinates, and the Z-level of the area being generated in. Generate_Terrain() will get these values for you automatically
|
||||
var/min_x
|
||||
var/max_x
|
||||
var/min_y
|
||||
var/max_y
|
||||
var/z_level
|
||||
|
||||
///The rng Seed of the generator, randomly chosen between 1 and 1000 when the terrain is generated
|
||||
var/hash = 1
|
||||
|
||||
/**
|
||||
* Binary space partition works by cutting the map area in half, then that chunk in half, then that chunk in half, over and over until the next cut would
|
||||
* create subsections smaller than the min specified size. so the size can be between the min size, and the min_size*2-1. The number of rooms you get back
|
||||
* will be random based on how the algorithm decides to slice
|
||||
*/
|
||||
var/map_subsection_min_size = 6
|
||||
|
||||
/**
|
||||
* Minimum width of a room generated in a map subsection, walls included. A minimum width of 5 is a 3 wide room with walls on each side.
|
||||
* THIS VALUE SHOULD NOT BE LARGER THAN THE map_subsection_min_size!!!!!!!!!!!
|
||||
*/
|
||||
var/map_subsection_min_room_width = 5
|
||||
/**
|
||||
* Minimum height of a room generated in a map subsection, walls included. A minimum height of 5 is a 3 tall room with walls on each side.
|
||||
* THIS VALUE SHOULD NOT BE LARGER THAN THE map_subsection_min_size!!!!!!!!!!!
|
||||
*/
|
||||
var/map_subsection_min_room_height = 5
|
||||
|
||||
/**
|
||||
* The number of rooms the random room placement algorithm will attempt to generate when the function is called,
|
||||
* unless the function is called with specific arguments otherwise
|
||||
*/
|
||||
var/desired_room_count = 20
|
||||
|
||||
///A list of the room datums generated by the Rust-g algorithm, and stored here
|
||||
var/list/generated_rooms = list()
|
||||
var/list/bsp_generated_rooms = list()
|
||||
var/list/rrp_generated_rooms = list()
|
||||
|
||||
///All mobs created by the generator. Either from the base generator or itself or the subsequent rooms created should add their mobs here
|
||||
var/list/datum/weakref/owned_mobs = list()
|
||||
|
||||
/**
|
||||
* The path to the corresponding room datum for the generator. If you make a new generator subtype, make a new room subtype to go with it
|
||||
* For example if you made:
|
||||
* * /datum/map_generator/dungeon_generator/ice_dungeon
|
||||
*
|
||||
* You should make:
|
||||
* * /datum/dungeon_room/ice_room
|
||||
*
|
||||
* And set the room_datum_path to that path
|
||||
*/
|
||||
var/room_datum_path = /datum/dungeon_room
|
||||
var/room_theme_path = /datum/dungeon_room_theme
|
||||
|
||||
///Weighted list of the types that spawns if the turf is open
|
||||
var/weighted_open_turf_types = list(/turf/open/floor/plating = 10)
|
||||
///Expanded list of the types that spawns if the turf is open
|
||||
var/open_turf_types
|
||||
///Weighted list of the types that spawns if the turf is closed
|
||||
var/weighted_closed_turf_types = list(/turf/closed/wall = 10)
|
||||
///Expanded list of the types that spawns if the turf is closed
|
||||
var/closed_turf_types
|
||||
|
||||
///If this is set to TRUE then it will only change turfs that are /turf/open/genturf, for more flexability in design
|
||||
var/gen_gurf_only = TRUE
|
||||
var/static/list/overlappable_areas = typecacheof(list(/area/space, /area/procedurally_generated))
|
||||
|
||||
/datum/map_generator/dungeon_generator/New(area/generate_in)
|
||||
. = ..()
|
||||
open_turf_types = expand_weights(weighted_open_turf_types)
|
||||
closed_turf_types = expand_weights(weighted_closed_turf_types)
|
||||
hash = rand(0, 1000)
|
||||
area_ref = generate_in
|
||||
z_level = generate_in.z
|
||||
areas_included += generate_in
|
||||
|
||||
|
||||
/datum/map_generator/dungeon_generator/combine_local_areas()
|
||||
for(var/area/procedurally_generated/current_area in GLOB.areas)
|
||||
if(istype(src, current_area.map_generator) && current_area.z == area_ref.z)
|
||||
areas_included |= current_area
|
||||
current_area.map_generator = src
|
||||
|
||||
/datum/map_generator/dungeon_generator/generate_terrain()
|
||||
var/start_time = REALTIMEOFDAY
|
||||
|
||||
for(var/area/procedurally_generated/pg in areas_included)
|
||||
pg.shared_generator_initialized = TRUE
|
||||
for(var/turf/t in pg.contents)
|
||||
working_turfs |= t
|
||||
|
||||
for (var/turf/t in working_turfs)
|
||||
if(!min_x || t.x<min_x)
|
||||
min_x = t.x
|
||||
if(!max_x || t.x>max_x)
|
||||
max_x = t.x
|
||||
if(!min_y || t.y<min_y)
|
||||
min_y = t.y
|
||||
if(!max_y || t.y>max_y)
|
||||
max_y = t.y
|
||||
if(min_x && max_x && (max_x-min_x>0))
|
||||
width = max_x-min_x
|
||||
if(min_y && max_y && (max_y-min_y>0))
|
||||
height = max_y-min_y
|
||||
|
||||
desired_room_count = ROUND_UP((width*height)*0.005)
|
||||
|
||||
bsp_generated_rooms = generate_rooms_with_bsp(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height)
|
||||
|
||||
rrp_generated_rooms = generate_rooms_with_rrp(width, height, desired_room_count, hash)
|
||||
|
||||
generated_rooms = bsp_generated_rooms + rrp_generated_rooms
|
||||
|
||||
for(var/turf/gen_turf in working_turfs)
|
||||
if(gen_gurf_only && !istype(gen_turf, /turf/open/genturf))
|
||||
continue
|
||||
gen_turf.ChangeTurf(pick(open_turf_types), flags = CHANGETURF_DEFER_CHANGE)
|
||||
|
||||
build_dungeon()
|
||||
toggle_owned_mob_ai(AI_ON)
|
||||
CHECK_TICK
|
||||
var/message = "[name] finished in [(REALTIMEOFDAY - start_time)/10]s!"
|
||||
to_chat(world, span_boldannounce("[message]"))
|
||||
log_world(message)
|
||||
|
||||
/**
|
||||
* With the area we're going to be working in paved with the proper tiles, we now begin constructing the actual dungeon
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/build_dungeon()
|
||||
|
||||
for(var/datum/dungeon_room/room in generated_rooms)
|
||||
if(room.generate())
|
||||
//if the room is successfully built, we want to remove that area from the working turfs so we can furnish the "main" dungeon area at the end
|
||||
working_turfs -= (room.interior | room.exterior)
|
||||
CHECK_TICK
|
||||
|
||||
return
|
||||
|
||||
/**
|
||||
* Returns a list of rooms using the Binary Space Partition(bsp) algorithm. Binary space gives a pretty even spread of rooms with minimal overlapping by
|
||||
* subsecting the area in half, and then that section in half, and that in half, until the next subsequent cut would be smaller than the minimum size.
|
||||
* Each of these cuts have minor deviations for randomness, and alternate between vertical and horizontal cuts.
|
||||
* Rooms are then generated in those sections of random height and width UP TO the size of the containing section. Rooms may border each other but will never overlap.
|
||||
* Since the map area becomes a checkerboard of cuts, area coverage is extremely good
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/generate_rooms_with_bsp(
|
||||
width = src.width,
|
||||
height = src.height,
|
||||
hash = src.hash,
|
||||
map_subsection_min_size = src.map_subsection_min_size,
|
||||
map_subsection_min_room_width = src.map_subsection_min_room_width,
|
||||
map_subsection_min_room_height = src.map_subsection_min_room_height,
|
||||
)
|
||||
|
||||
var/rooms_json = rustg_bsp_generate(
|
||||
"[width]",
|
||||
"[height]",
|
||||
"[hash]",
|
||||
"[map_subsection_min_size]",
|
||||
"[map_subsection_min_room_width]",
|
||||
"[map_subsection_min_room_height]")
|
||||
|
||||
return parse_rooms_json(rooms_json, ALGORITHM_BSP)
|
||||
|
||||
/**
|
||||
* Returns a list of rooms using the Random Room Placement(rrp) algorithm. Random room placement simply generates rooms of random height and width, then selects
|
||||
* a random X and Y value to be the bottom left corner of it. The only validation check this algorith performs is if a newly generated room overlaps with an existing room
|
||||
* that the algorithm generated previously. If it does, the algorithm will generate a new room and coordinates for it and try again.
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/generate_rooms_with_rrp(
|
||||
width = src.width,
|
||||
height = src.height,
|
||||
desired_room_count = src.desired_room_count,
|
||||
hash = src.hash,
|
||||
)
|
||||
|
||||
var/rooms_json = rustg_random_room_generate(
|
||||
"[width]",
|
||||
"[height]",
|
||||
"[desired_room_count]",
|
||||
"[hash]")
|
||||
|
||||
return parse_rooms_json(rooms_json, ALGORITHM_RRP)
|
||||
|
||||
/**
|
||||
* Converts the json list of room objects into a list of
|
||||
*
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/parse_rooms_json(json_of_rooms, algorithm_type)
|
||||
var/list/rooms = json_decode(json_of_rooms)
|
||||
var/list/parsed_rooms = list()
|
||||
|
||||
//the response is a list of room objects in JSON format. you can't iterate through the list in the for var/i in list fashion
|
||||
//because you need to keep track of the index you're on. each element doesn't have its own identifier
|
||||
for(var/index in 1 to rooms.len)
|
||||
var/room = rooms[index]
|
||||
var/room_id = "[room["id"]]: [index]"
|
||||
var/x1 = text2num(room["x"]) + min_x
|
||||
var/y1 = text2num(room["y"]) + min_y
|
||||
//byond starts counting from 1 so we need to subtract 1 from our top end coordinates
|
||||
var/x2 = text2num(room["x2"]) + min_x - 1
|
||||
var/y2 = text2num(room["y2"]) + min_y - 1
|
||||
var/room_width = text2num(room["width"])
|
||||
var/room_height = text2num(room["height"])
|
||||
|
||||
//We take the center of the room and if it's outside the generated area, we don't add it to our list
|
||||
//var/turf/center = locate(ROUND_UP(x2-room_width/2), ROUND_UP(y2-room_height/2), z_level)
|
||||
|
||||
var/datum/dungeon_room/potential_room = new room_datum_path(
|
||||
_id = room_id,
|
||||
_x1 = x1,
|
||||
_y1 = y1,
|
||||
_x2 = x2,
|
||||
_y2 = y2,
|
||||
_z = z_level,
|
||||
_width = room_width,
|
||||
_height = room_height,
|
||||
_generator_ref = src,
|
||||
)
|
||||
|
||||
if(valid_room_check(potential_room))
|
||||
parsed_rooms += potential_room
|
||||
potential_room.Initialize()
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
return parsed_rooms
|
||||
|
||||
/**
|
||||
* Designate behavior for whether or not you want the rooms kept or discarded here. At base this will just ensure the center of the room falls within the workable area
|
||||
* the generator has access to
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/valid_room_check(datum/dungeon_room/room_to_check)
|
||||
return working_turfs.Find(room_to_check.center)
|
||||
|
||||
/datum/map_generator/dungeon_generator/proc/rooms_intersect(datum/dungeon_room/room_to_check, datum/dungeon_room/other_room)
|
||||
var/intersect = FALSE
|
||||
|
||||
return intersect
|
||||
|
||||
/**
|
||||
* Toggle the AI setting of all mobs in the generator. If the assistant starts crying like a child lost in a haunted house, you can turn off the mobs and escort them
|
||||
* to their parent.
|
||||
*
|
||||
* For quick refence the commands are:
|
||||
* * 1: Turn mob AI on
|
||||
* * 2: Set mob AI to idle
|
||||
* * 3: Turn mob AI off completely
|
||||
* * 4: Turn mob AI off until a player enters the z-level which will cause them to flip back to on
|
||||
*/
|
||||
/datum/map_generator/dungeon_generator/proc/toggle_owned_mob_ai(togglestatus)
|
||||
if(!togglestatus)
|
||||
return togglestatus
|
||||
for(var/datum/weakref/mob_ref as anything in owned_mobs)
|
||||
var/mob/living/simple_animal/mob_to_toggle = mob_ref.resolve()
|
||||
if(mob_to_toggle)
|
||||
mob_to_toggle.toggle_ai(togglestatus)
|
||||
return togglestatus
|
||||
@@ -5,6 +5,10 @@
|
||||
/datum/map_generator/proc/generate_terrain(list/turfs, area/generate_in)
|
||||
return
|
||||
|
||||
///This proc will aggregate areas on the same z-level to share turfs for the purpose of generating the area. Currently just for procedural generation with maints
|
||||
/datum/map_generator/proc/combine_local_areas()
|
||||
return
|
||||
|
||||
/turf/open/genturf
|
||||
name = "ungenerated turf"
|
||||
desc = "If you see this, and you're not a ghost, yell at coders"
|
||||
|
||||
351
code/datums/mapgen/dungeon_generators/dungeon_room.dm
Normal file
351
code/datums/mapgen/dungeon_generators/dungeon_room.dm
Normal file
@@ -0,0 +1,351 @@
|
||||
/**
|
||||
* This datum represents a "room" which is created by the Dungeon Generator process. Whether you use Binary Space Partition(BSP) or Random Room Placement(RRP),
|
||||
* the Rust algorith will populate the given area with rooms, which are just squares and rectangles of various dimensions with X and Y coordinates. I created
|
||||
* this datum to give better control over the data the generator is using. So you can look through the list of rooms for feedback or to understand what's happening.
|
||||
*
|
||||
* The datum has procs to construct itself at the given coordinates, and procs to furnish itself according to the randomly assigned "theme" it chooses for itself
|
||||
* on init.
|
||||
*/
|
||||
/datum/dungeon_room
|
||||
///Some kind of identifier. Will default to the number of the rooms in the list, like "Random room 5"
|
||||
var/id
|
||||
|
||||
///X coordinate of the bottom left corner
|
||||
var/x1
|
||||
///Y coordinate of the bottom left corner
|
||||
var/y1
|
||||
|
||||
///X coordinate of the top right corner
|
||||
var/x2
|
||||
///Y coordinate of the top right corner
|
||||
var/y2
|
||||
|
||||
///z-level of the room
|
||||
var/z
|
||||
|
||||
///Tile width of the room, including walls. A 5 width room is a 3 tile wide interior with a wall on each side
|
||||
var/width
|
||||
///Tile height of the room, including walls. A 5 height room is a 3 tile tall interior with a wall on each side
|
||||
var/height
|
||||
|
||||
///The center tile of a room, or at least as close to center as it can get if the room is of different dimensions, or equal dimensions with no center
|
||||
var/turf/center = null
|
||||
|
||||
/**
|
||||
* A list of the outermost turfs. This is used to generate walls and doors
|
||||
*
|
||||
* * exterior = Full room - interior
|
||||
* * [X][X][X][X] = [X][X][X][X] - [0][0][0][0]
|
||||
* * [X][0][0][X] = [X][X][X][X] - [0][X][X][0]
|
||||
* * [X][0][0][X] = [X][X][X][X] - [0][X][X][0]
|
||||
* * [X][X][X][X] = [X][X][X][X] - [0][0][0][0]
|
||||
*/
|
||||
var/list/exterior = list()
|
||||
|
||||
/**
|
||||
* A list of the turfs inside the outer walls. This is the room for activities and decorations
|
||||
* * Interior area of full room
|
||||
* * [0][0][0][0]
|
||||
* * [0][X][X][0]
|
||||
* * [0][X][X][0]
|
||||
* * [0][0][0][0]
|
||||
*/
|
||||
var/list/interior = list()
|
||||
|
||||
var/min_doors = 1
|
||||
var/max_doors = 4
|
||||
///A list of the doors belonging to a room
|
||||
var/list/datum/weakref/doors = list()
|
||||
var/list/datum/weakref/features = list()
|
||||
var/list/datum/weakref/mobs = list()
|
||||
|
||||
/**
|
||||
* List of protected atom types. Rooms use turf.empty() to replace turfs and delete their contents at the same time. This is so that if a room overlaps another,
|
||||
* lingering elements like walls, doors, windows, and even mobs will be removed. This is also helpful if you want to generate over elements that are already
|
||||
* placed by map editors, like in the case of pipes and cables for atmos and station power.
|
||||
*/
|
||||
var/static/list/protected_atoms = typecacheof(list(
|
||||
/obj/machinery/atmospherics/pipe,
|
||||
/obj/structure/cable,
|
||||
))
|
||||
|
||||
///If this room needed to get trimmed because it overlapped an area that shouldn't be touched
|
||||
var/completed_room = TRUE
|
||||
|
||||
var/datum/map_generator/dungeon_generator/generator_ref
|
||||
|
||||
var/list/weighted_open_turf_types = list()
|
||||
var/list/weighted_closed_turf_types = list()
|
||||
|
||||
var/datum/dungeon_room_theme/room_theme
|
||||
|
||||
///for differentiating room types yourself. like maybe 1 is a ruin, and 2 is empty
|
||||
var/room_type
|
||||
|
||||
var/room_danger_level
|
||||
|
||||
var/area/area_ref = null
|
||||
|
||||
/datum/dungeon_room/New(
|
||||
_id = "randomly generated room",
|
||||
_x1 = 1,
|
||||
_y1 = 1,
|
||||
_x2 = 1,
|
||||
_y2 = 1,
|
||||
_z = 1,
|
||||
_width = 1,
|
||||
_height = 1,
|
||||
_room_type,
|
||||
_room_danger_level, datum/map_generator/dungeon_generator/_generator_ref)
|
||||
|
||||
id = _id
|
||||
x1 = _x1
|
||||
y1 = _y1
|
||||
x2 = _x2
|
||||
y2 = _y2
|
||||
z = _z
|
||||
width = _width
|
||||
height = _height
|
||||
|
||||
center = locate(ROUND_UP(x1+width/2), ROUND_UP(y1+height/2), z)
|
||||
|
||||
area_ref = get_area(center)
|
||||
if(_room_type)
|
||||
room_type = _room_type
|
||||
if(_room_danger_level)
|
||||
room_danger_level = _room_danger_level
|
||||
|
||||
interior = block(locate(x1+1,y1+1,z),locate(x2-1,y2-1,z))
|
||||
exterior = block(locate(x1,y1,z),locate(x2,y2,z)) - interior
|
||||
|
||||
generator_ref = _generator_ref
|
||||
|
||||
if(generator_ref.open_turf_types)
|
||||
weighted_open_turf_types = generator_ref.open_turf_types
|
||||
if(generator_ref.closed_turf_types)
|
||||
weighted_closed_turf_types = generator_ref.closed_turf_types
|
||||
|
||||
///Override this for each new room type so you know whether to trim or discard rooms that overlap areas that should remain untouched
|
||||
/datum/dungeon_room/proc/Initialize()
|
||||
validation_check()
|
||||
|
||||
generate_room_theme()
|
||||
|
||||
|
||||
/datum/dungeon_room/proc/generate_room_theme()
|
||||
if(!room_danger_level)
|
||||
room_danger_level = ROOM_RATING_SAFE
|
||||
if(!room_type)
|
||||
room_type = ROOM_TYPE_RANDOM
|
||||
if(!room_theme)
|
||||
room_theme = new generator_ref.room_theme_path(src)
|
||||
room_theme.Initialize()
|
||||
|
||||
///checks if the room overlaps areas it shouldn't and if it does remove that tile from the room
|
||||
/datum/dungeon_room/proc/validation_check()
|
||||
for(var/turf/tile in exterior)
|
||||
var/area/area_to_check = get_area(tile)
|
||||
if(!is_type_in_typecache(area_to_check, generator_ref.overlappable_areas))
|
||||
exterior -= tile
|
||||
completed_room = FALSE
|
||||
|
||||
for(var/turf/tile in interior)
|
||||
var/area/area_to_check = get_area(tile)
|
||||
if(!is_type_in_typecache(area_to_check, generator_ref.overlappable_areas))
|
||||
interior -= tile
|
||||
completed_room = FALSE
|
||||
|
||||
///If you're making an area that has ruin map files, you can use this to determine if it should be a ruin room
|
||||
/datum/dungeon_room/proc/is_ruin_compatible()
|
||||
return FALSE
|
||||
|
||||
///Construct yourself in the game world NOW. Assuming you have a theme to pull from, otherwise stay theoretical.
|
||||
/datum/dungeon_room/proc/generate()
|
||||
if(isnull(room_theme))
|
||||
return FALSE
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_FLOORS)
|
||||
build_flooring()
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_WALLS || room_theme.room_flags & ROOM_HAS_WINDOWS)
|
||||
build_walls()
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_DOORS)
|
||||
add_doors()
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_SPECIAL_FEATURES)
|
||||
add_special_features()
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_FEATURES)
|
||||
add_features()
|
||||
|
||||
if(room_theme.room_flags & ROOM_HAS_MOBS)
|
||||
add_mobs()
|
||||
|
||||
room_theme.post_generate()
|
||||
|
||||
generator_ref.owned_mobs |= mobs
|
||||
|
||||
/**
|
||||
* This loop is for setting the inside of a room to match the area of its center. The reason we also fuck around with the lighting overlays is because
|
||||
* at the time of generation, the lighting subsystem isn't initialized yet, so we'll handle calling the building and clearing of area lighting diffs ourselves.
|
||||
*/
|
||||
for(var/turf/room_turf in (interior + exterior))
|
||||
var/area/old_area = get_area(room_turf)
|
||||
if(area_ref != old_area && !generator_ref.areas_included.Find(old_area))
|
||||
area_ref.contents += room_turf
|
||||
area_ref.contained_turfs += room_turf
|
||||
old_area.turfs_to_uncontain += room_turf
|
||||
room_turf.change_area(old_area, area_ref)
|
||||
|
||||
return TRUE
|
||||
|
||||
///For each tile in the exterior, build a wall to keep the assistants out. Or a window if the room theme calls for it
|
||||
/datum/dungeon_room/proc/build_walls()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_WALLS || room_theme.room_flags & ROOM_HAS_WINDOWS))
|
||||
return
|
||||
for(var/turf/room_turf in exterior)
|
||||
//we use the generator flooring for this because the space rooms only have space flooring
|
||||
room_turf.empty(pickweight(weighted_open_turf_types), ignore_typecache = protected_atoms, flags = CHANGETURF_DEFER_CHANGE | CHANGETURF_IGNORE_AIR)
|
||||
if(room_theme.room_flags & ROOM_HAS_WINDOWS && prob(room_theme.window_weight) || room_theme.room_flags & ROOM_HAS_ONLY_WINDOWS)
|
||||
|
||||
var/obj/window_to_spawn = room_theme.get_random_window()
|
||||
new window_to_spawn(room_turf)
|
||||
else
|
||||
if(!room_theme.weighted_possible_wall_types.Find(room_turf.type))
|
||||
|
||||
room_turf.place_on_top(pick(room_theme.get_random_wall()), flags = CHANGETURF_DEFER_CHANGE | CHANGETURF_IGNORE_AIR)
|
||||
return
|
||||
|
||||
///Build the flooring for the room. Potentially not necessary based on the build area, but making sure the room doesn't construct over space or gen turf is a good idea
|
||||
/datum/dungeon_room/proc/build_flooring()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_FLOORS))
|
||||
return
|
||||
for(var/turf/room_turf in interior)
|
||||
//we want to remove everything in the loc but don't want to change the loc type in this way
|
||||
room_turf.empty(null, ignore_typecache = protected_atoms)
|
||||
room_turf.place_on_top(pick(room_theme.get_random_flooring()), flags = CHANGETURF_DEFER_CHANGE | CHANGETURF_IGNORE_AIR)
|
||||
|
||||
return
|
||||
|
||||
///Add a random number of doors to the room. If you're lucky, people will actually be able to access the room. If not, it's a ~super secret room~
|
||||
/datum/dungeon_room/proc/add_doors()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_DOORS))
|
||||
return
|
||||
var/num_of_doors_to_gen = rand(min_doors, max_doors)
|
||||
var/list/directions = GLOB.cardinals.Copy()
|
||||
var/max_attempts = 5
|
||||
var/attempts_left = 5
|
||||
while(doors.len < num_of_doors_to_gen && directions.len && attempts_left > 0 )
|
||||
var/turf/door_spot = null
|
||||
var/wall_side = pick_n_take(directions)
|
||||
switch(wall_side)
|
||||
if(NORTH)
|
||||
door_spot = locate(ROUND_UP(x2-width/2),y2,z)
|
||||
if(SOUTH)
|
||||
door_spot = locate(ROUND_UP(x2-width/2),y1,z)
|
||||
if(EAST)
|
||||
door_spot = locate(x2,ROUND_UP(y2-height/2),z)
|
||||
if(WEST)
|
||||
door_spot = locate(x1,ROUND_UP(y2-height/2),z)
|
||||
|
||||
if(exterior.Find(door_spot))
|
||||
var/turf/other_side_of_door = get_step(door_spot, wall_side)
|
||||
if(istype(other_side_of_door, /turf/open/space))
|
||||
num_of_doors_to_gen--
|
||||
continue
|
||||
door_spot.empty(pick(room_theme.get_random_flooring()), ignore_typecache = protected_atoms, flags = CHANGETURF_DEFER_CHANGE | CHANGETURF_IGNORE_AIR)
|
||||
var/door_path = room_theme.get_random_door()
|
||||
if(ispath(door_path))
|
||||
var/obj/machinery/door/new_door = new door_path(door_spot)
|
||||
doors += WEAKREF(new_door)
|
||||
attempts_left = max_attempts
|
||||
attempts_left--
|
||||
|
||||
///Sprinkle in the flavor from the room's theme like blood splatters, trash, or items. The number of flavor items is based on the room size and feature weight
|
||||
/datum/dungeon_room/proc/add_special_features()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_SPECIAL_FEATURES) || !interior.len)
|
||||
return
|
||||
|
||||
var/special_feature_path = room_theme.get_special_feature()
|
||||
if(ispath(special_feature_path))
|
||||
var/obj/special_feature = new special_feature_path(locate(x1+1, y1+1, z))
|
||||
features += WEAKREF(special_feature)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
///Sprinkle in the flavor from the room's theme like blood splatters, trash, or items. The number of flavor items is based on the room size and feature weight
|
||||
/datum/dungeon_room/proc/add_features()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_FEATURES) || !interior.len)
|
||||
return
|
||||
|
||||
var/features_to_spawn = round( (interior.len) * (room_theme.feature_weight * 0.01), 1 )
|
||||
var/max_attempts = 10
|
||||
var/attempts_left = 10
|
||||
while(features_to_spawn > 0 && attempts_left > 0)
|
||||
var/turf/spawn_point = pick(interior)
|
||||
if(!spawn_point.is_blocked_turf())
|
||||
var/selected_feature_path = room_theme.get_random_feature()
|
||||
if(islist(selected_feature_path))
|
||||
for(var/path_to_check in selected_feature_path)
|
||||
if(ispath(path_to_check))
|
||||
var/obj/new_feature = new path_to_check(spawn_point)
|
||||
features += WEAKREF(new_feature)
|
||||
features_to_spawn = clamp(features_to_spawn-1, 0, max_attempts)
|
||||
//if we successfully spawn something, reset the attempt count
|
||||
attempts_left = max_attempts
|
||||
|
||||
else if(ispath(selected_feature_path))
|
||||
var/obj/new_feature = new selected_feature_path(spawn_point)
|
||||
features += WEAKREF(new_feature)
|
||||
features_to_spawn = clamp(features_to_spawn-1, 0, max_attempts)
|
||||
//if we successfully spawn something, reset the attempt count
|
||||
attempts_left = max_attempts
|
||||
|
||||
else
|
||||
attempts_left = clamp(attempts_left-1, 0, max_attempts)
|
||||
|
||||
///Add mobs if the room's theme has any, be it rats or space dragons lmao. The number of mobs is based on the room size and mob weight
|
||||
/datum/dungeon_room/proc/add_mobs()
|
||||
if(!(room_theme.room_flags & ROOM_HAS_MOBS) || !interior.len)
|
||||
return
|
||||
|
||||
var/mobs_to_spawn = round( log(interior.len) * ((interior.len/100) + 1), 1 )
|
||||
var/max_attempts = 10
|
||||
var/attempts_left = 10
|
||||
while(mobs_to_spawn > 0 && attempts_left > 0)
|
||||
if(!prob(room_theme.mob_spawn_chance))
|
||||
mobs_to_spawn = clamp(mobs_to_spawn-1, 0, mobs_to_spawn)
|
||||
continue
|
||||
var/turf/spawn_point = pick(interior)
|
||||
if(!spawn_point.is_blocked_turf())
|
||||
var/selected_mob_path = room_theme.get_random_mob()
|
||||
if(ispath(selected_mob_path, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/new_mob = new selected_mob_path(spawn_point)
|
||||
/**
|
||||
* We toggle the mob AI off as they're created because if the generator is being ran slowly and taking a while to generate,
|
||||
* mobs can break out of their rooms and start fighting before the generator is even done
|
||||
*/
|
||||
new_mob.toggle_ai(AI_OFF)
|
||||
mobs += WEAKREF(new_mob)
|
||||
mobs_to_spawn = clamp(mobs_to_spawn-1, 0, mobs_to_spawn)
|
||||
//if we successfully spawn a mob, reset the attempt count
|
||||
attempts_left = max_attempts
|
||||
attempts_left = clamp(attempts_left-1, 0, max_attempts)
|
||||
|
||||
|
||||
/**
|
||||
* Experimental and buggy, but calling this SHOULD remove the room from the game world, along with all the room's tiles and items.
|
||||
* leaving the area looking how it did before generation. just thanos snap the room off the map like it never existed. however with rooms overlapping
|
||||
* and intersecting this may just remove a chunk of another room
|
||||
*/
|
||||
/datum/dungeon_room/proc/delete_self()
|
||||
for(var/turf/room_turf in interior)
|
||||
//again we want to clean the tile of any mobs or props before reverting it to the pre-room setting
|
||||
room_turf.empty(null, ignore_typecache = protected_atoms)
|
||||
room_turf.ScrapeAway()
|
||||
|
||||
for(var/turf/room_turf in exterior)
|
||||
//again we want to clean the tile of any mobs or props before reverting it to the pre-room setting
|
||||
room_turf.empty(null, ignore_typecache = protected_atoms)
|
||||
room_turf.ScrapeAway()
|
||||
175
code/datums/mapgen/dungeon_generators/dungeon_room_theme.dm
Normal file
175
code/datums/mapgen/dungeon_generators/dungeon_room_theme.dm
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Dungeon room themes determine the style of the generated rooms. If you want to create a new dungeon generator for say, Icemeta,
|
||||
* Then you would want to create a new subtype of room theme with maybe snow floor tiles, ice or snow walls and some fitting kind of door. Then from there further subtype
|
||||
* into hostile, neutral, beneficial and whatever other types of rooms you want to appear in that area.
|
||||
*
|
||||
* TLDR: This datum is basically a box of furniture that randomly generted rooms will pull from to build themselves with a cohesive style
|
||||
*/
|
||||
/datum/dungeon_room_theme
|
||||
var/datum/dungeon_room/dungeon_room_ref
|
||||
///Contains flags pertaining to the qualities of the room, such as ROOM_HAS_FLOORS if it, y'know, has floors
|
||||
var/room_flags = 0
|
||||
|
||||
var/room_type = ROOM_TYPE_RANDOM
|
||||
|
||||
var/room_danger_level = ROOM_RATING_SAFE
|
||||
///Weighted list of floorings for the room to choose from
|
||||
var/list/weighted_possible_floor_types = list()
|
||||
///Weighted list of walls for the room to choose from
|
||||
var/list/weighted_possible_wall_types = list()
|
||||
///If you want the theme or rooms to include windows, specify the window type here, be it the basic window obj, or a spawner for a preset window
|
||||
var/list/weighted_possible_window_types = list()
|
||||
///percent chance of spawning a window instead of a wall
|
||||
var/window_weight = 0
|
||||
///Weighted list of doors for the room to choose from
|
||||
var/list/weighted_possible_door_types = list(/obj/machinery/door/airlock = 1)
|
||||
|
||||
///For room themes that call for very specific features appearing. Currently just for handling ruins via their landmark object
|
||||
var/special_feature = null
|
||||
|
||||
/**
|
||||
* The number of features spawned in a room is:
|
||||
* (area inside the room * feature_weight as a percent) rounded up
|
||||
* the higher this number, the more features spawned
|
||||
*
|
||||
* I.E. room with a 4x5 inner area has an area of 20, so with a feature weight of 75 results in 75% of 20, therefore 15 features will be spawned in random open spots
|
||||
*/
|
||||
var/feature_weight = 0
|
||||
///Weighted list of features you want to spawn. Like unfinished machines, broken mechs, blood splatters. Keep in mind these will be placed randomly
|
||||
var/list/weighted_feature_spawn_list = list()
|
||||
|
||||
var/list/expanded_weighted_feature_spawn_list = list()
|
||||
|
||||
/**
|
||||
* The upper limit of mobs spawned in a room is determined by a logarithmic function using the interior size of the room. This variable
|
||||
* is the chance that a mob spawns in each iteration of the spawning loop.
|
||||
* For example if a room has an interior size of 20 and has a limit of 5 mobs for the room, If the spawn chance is 50 then each of those 5 spawn attempts has
|
||||
* a 50% chance to actually spawn a mob, otherwise it will decrease the number of spawnable mobs by 1 and try again.
|
||||
*
|
||||
*/
|
||||
var/mob_spawn_chance = 0
|
||||
///Weighted list of features you want to spawn. Spiders in a room that has webbing, or possums in a room full of trash.
|
||||
var/list/weighted_mob_spawn_list = list()
|
||||
|
||||
/** Initialize the new room by sanity checking values and then assigning the bitflags that describe room behavior to the parent room*/
|
||||
/datum/dungeon_room_theme/New(datum/dungeon_room/_dungeon_room_ref)
|
||||
//No negatives pls
|
||||
feature_weight = clamp(feature_weight, 0, 100)
|
||||
|
||||
mob_spawn_chance = clamp(mob_spawn_chance, 0, 100)
|
||||
|
||||
dungeon_room_ref = _dungeon_room_ref
|
||||
|
||||
if(isemptylist(weighted_possible_floor_types))
|
||||
weighted_possible_floor_types = dungeon_room_ref.weighted_open_turf_types
|
||||
if(isemptylist(weighted_possible_wall_types))
|
||||
weighted_possible_wall_types = dungeon_room_ref.weighted_closed_turf_types
|
||||
|
||||
/**
|
||||
* Any sort of room specific logic you need to do before running the Initialize() that will assign room flags. For example if you want to generate a list
|
||||
* using subtypes or typesof, which is something you can't do from the base definition and needs to be done in a proc.
|
||||
*/
|
||||
/datum/dungeon_room_theme/proc/pre_initialize()
|
||||
return
|
||||
|
||||
/**
|
||||
* Room flags are assigned based on which lists have any elements.
|
||||
* These flags are checked by the room that owns the theme to determine which furnishing procs need to be called
|
||||
*/
|
||||
/datum/dungeon_room_theme/proc/Initialize()
|
||||
pre_initialize()
|
||||
if(dungeon_room_ref.room_danger_level & ROOM_RATING_SAFE)
|
||||
var/list/blatently_hostile_mob_paths = (typesof(/mob/living/simple_animal/hostile) - typesof(/mob/living/simple_animal/hostile/retaliate))
|
||||
for(var/mob_path in weighted_mob_spawn_list)
|
||||
if(blatently_hostile_mob_paths.Find(mob_path))
|
||||
weighted_mob_spawn_list.Remove(mob_path)
|
||||
|
||||
if(weighted_possible_floor_types.len)
|
||||
room_flags |= ROOM_HAS_FLOORS
|
||||
if(weighted_possible_wall_types.len)
|
||||
room_flags |= ROOM_HAS_WALLS
|
||||
if(weighted_possible_window_types.len)
|
||||
room_flags |= ROOM_HAS_WINDOWS
|
||||
if(room_flags & ROOM_HAS_WINDOWS && !(room_flags & ROOM_HAS_WALLS))
|
||||
room_flags |= ROOM_HAS_ONLY_WINDOWS
|
||||
if(weighted_possible_door_types.len)
|
||||
room_flags |= ROOM_HAS_DOORS
|
||||
if(special_feature)
|
||||
room_flags |= ROOM_HAS_SPECIAL_FEATURES
|
||||
if(weighted_feature_spawn_list.len && feature_weight > 0)
|
||||
room_flags |= ROOM_HAS_FEATURES
|
||||
if(weighted_mob_spawn_list.len && mob_spawn_chance > 0)
|
||||
room_flags |= ROOM_HAS_MOBS
|
||||
|
||||
///Return a random flooring /turf from the list of flooring options, selected based on weight. Defaults to basic plating if no flooring is found
|
||||
/datum/dungeon_room_theme/proc/get_random_flooring()
|
||||
if(!weighted_possible_floor_types.len)
|
||||
return /turf/open/floor/plating
|
||||
var/turf/flooring = pickweight(weighted_possible_floor_types)
|
||||
return flooring
|
||||
|
||||
///Return a random wall /turf from the list of wall options, selected based on weight. Defaults to basic wall if no wall is found
|
||||
/datum/dungeon_room_theme/proc/get_random_wall()
|
||||
if(!weighted_possible_wall_types.len)
|
||||
return /turf/closed/wall
|
||||
var/wall_path = pickweight(weighted_possible_wall_types)
|
||||
return wall_path
|
||||
|
||||
///Return a random window /obj from the list of window options, selected based on weight. Defaults to basic window spawner if no window is found
|
||||
/datum/dungeon_room_theme/proc/get_random_window()
|
||||
if(!weighted_possible_window_types.len)
|
||||
return /obj/effect/spawner/structure/window
|
||||
var/window_path = pickweight(weighted_possible_window_types)
|
||||
return window_path
|
||||
|
||||
///Return a random door /obj from the list of door options, selected based on weight. Defaults to basic door if no door is found
|
||||
/datum/dungeon_room_theme/proc/get_random_door()
|
||||
if(!weighted_possible_wall_types.len)
|
||||
return /obj/machinery/door/airlock
|
||||
var/door_path = pickweight(weighted_possible_door_types)
|
||||
return door_path
|
||||
|
||||
///Return the path for the special feature of the room. Returns null if nothing is found
|
||||
/datum/dungeon_room_theme/proc/get_special_feature()
|
||||
if(!special_feature)
|
||||
return null
|
||||
|
||||
return special_feature
|
||||
|
||||
///Return a random feature /obj the list of feature options, selected based on weight. Returns null if nothing is found
|
||||
/datum/dungeon_room_theme/proc/get_random_feature()
|
||||
if(!weighted_feature_spawn_list.len)
|
||||
return null
|
||||
|
||||
var/feature_path = pickweight(weighted_feature_spawn_list)
|
||||
|
||||
// else
|
||||
weighted_feature_spawn_list[feature_path]--
|
||||
if(!weighted_feature_spawn_list[feature_path])
|
||||
weighted_feature_spawn_list.Cut(weighted_feature_spawn_list.Find(feature_path), weighted_feature_spawn_list.Find(feature_path)+1)
|
||||
|
||||
return feature_path
|
||||
|
||||
/**
|
||||
* Return a random mob /atom the list of mob options, selected based on weight. Returns null if nothing is found
|
||||
* The reason it returns an atom rather than a mob specifically is because there are some weird cases where mobs are objects, like specific silicons
|
||||
*/
|
||||
/datum/dungeon_room_theme/proc/get_random_mob()
|
||||
if(!weighted_mob_spawn_list.len)
|
||||
return null
|
||||
|
||||
var/mob_path = pickweight(weighted_mob_spawn_list)
|
||||
|
||||
weighted_mob_spawn_list[mob_path]--
|
||||
if(!weighted_mob_spawn_list[mob_path])
|
||||
weighted_mob_spawn_list.Remove(mob_path)
|
||||
|
||||
return mob_path
|
||||
|
||||
/**
|
||||
* If you want to do anything fancy with the mobs or items in the room after you know they exist.
|
||||
* Like renaming items, reassigning factions, calling specific procs from them.
|
||||
* Whatever your little heart desires
|
||||
*/
|
||||
/datum/dungeon_room_theme/proc/post_generate()
|
||||
return
|
||||
@@ -0,0 +1,263 @@
|
||||
/datum/map_generator/dungeon_generator/maintenance
|
||||
weighted_open_turf_types = list(
|
||||
/turf/open/floor/plating = 10,
|
||||
/turf/open/floor/plating/rust = 1,
|
||||
)
|
||||
weighted_closed_turf_types = list(/turf/closed/wall = 5, /turf/closed/wall/rust = 2 )
|
||||
room_datum_path = /datum/dungeon_room/maintenance
|
||||
room_theme_path = /datum/dungeon_room_theme/maintenance
|
||||
|
||||
//var/list/used_spawn_points = list()
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/build_dungeon()
|
||||
. = ..()
|
||||
add_firelocks()
|
||||
add_apcs()
|
||||
wire_apcs()
|
||||
add_maint_loot()
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/proc/add_firelocks()
|
||||
///we only want to look to place firedoors every 5 tiles, so we don't place too many
|
||||
var/step_increment = 5
|
||||
var/consecutive_firedoor_limit = 3
|
||||
var/list/fire_door_spawn_points = list()
|
||||
var/fire_doors_path = /obj/effect/mapping_helpers/firedoor_border_spawner
|
||||
for(var/y in min_y to max_y step step_increment)
|
||||
fire_door_spawn_points = list()
|
||||
for(var/turf/current_turf in block(locate(min_x,y,z_level),locate(max_x,y,z_level)))
|
||||
if(working_turfs.Find(current_turf) && !current_turf.is_blocked_turf(TRUE))
|
||||
fire_door_spawn_points |= current_turf
|
||||
if(current_turf.is_blocked_turf(TRUE) && fire_door_spawn_points.len <= consecutive_firedoor_limit)
|
||||
for(var/turf/spawn_point in fire_door_spawn_points)
|
||||
new fire_doors_path(spawn_point)
|
||||
fire_door_spawn_points -= spawn_point
|
||||
if(fire_door_spawn_points.len > consecutive_firedoor_limit && current_turf.is_blocked_turf(TRUE))
|
||||
fire_door_spawn_points = list()
|
||||
|
||||
fire_doors_path = /obj/effect/mapping_helpers/firedoor_border_spawner/horizontal
|
||||
for(var/x in min_x to max_x step step_increment)
|
||||
fire_door_spawn_points = list()
|
||||
for(var/turf/current_turf in block(locate(x,min_y,z_level),locate(x,max_y,z_level)))
|
||||
if(working_turfs.Find(current_turf) && !current_turf.is_blocked_turf(TRUE))
|
||||
fire_door_spawn_points |= current_turf
|
||||
if(current_turf.is_blocked_turf(TRUE) && fire_door_spawn_points.len <= consecutive_firedoor_limit)
|
||||
for(var/turf/spawn_point in fire_door_spawn_points)
|
||||
new fire_doors_path(spawn_point)
|
||||
fire_door_spawn_points -= spawn_point
|
||||
if(fire_door_spawn_points.len > consecutive_firedoor_limit && current_turf.is_blocked_turf(TRUE))
|
||||
fire_door_spawn_points = list()
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/proc/add_maint_loot()
|
||||
//Gax maints typically ends up being about 2000 turfs, so this would end up being ~200 items, structures, and decals to decorate maint with
|
||||
var/list/valid_spawn_points = typecache_filter_list(working_turfs, typecacheof(/turf/open/floor))
|
||||
var/items_to_spawn = ROUND_UP(valid_spawn_points.len * 0.15)
|
||||
var/max_attempts = 5
|
||||
var/attempts = max_attempts
|
||||
|
||||
while(items_to_spawn>0 && attempts>0 && valid_spawn_points.len > 0)
|
||||
attempts--
|
||||
var/turf/spawn_point = pick_n_take(valid_spawn_points)
|
||||
var/against_wall = FALSE
|
||||
var/blocking_passage = FALSE
|
||||
var/brazil = FALSE
|
||||
var/blocked_directions = 0
|
||||
if(spawn_point?.is_blocked_turf() || spawn_point.contents.len>0)
|
||||
continue
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/turf/neighbor = get_step(spawn_point, direction)
|
||||
if(neighbor?.is_blocked_turf(TRUE, ignore_atoms = list(/obj/structure), type_list = TRUE))
|
||||
blocked_directions |= direction
|
||||
|
||||
if( blocked_directions )
|
||||
//probably
|
||||
against_wall = TRUE
|
||||
|
||||
if( ((blocked_directions & (NORTH|SOUTH)) == (NORTH|SOUTH)) || ((blocked_directions & (EAST|WEST)) == (EAST|WEST)) )
|
||||
//definitely
|
||||
blocking_passage = TRUE
|
||||
|
||||
if( (blocked_directions & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST) )
|
||||
//what the fuck how did you get here
|
||||
brazil = TRUE
|
||||
|
||||
if(brazil)
|
||||
new /obj/item/toy/plush/lizard/azeel(spawn_point)
|
||||
items_to_spawn--
|
||||
|
||||
else if(blocking_passage)
|
||||
switch(rand(1,10))
|
||||
if(1 to 6)
|
||||
new /obj/structure/grille/broken(spawn_point)
|
||||
|
||||
else
|
||||
new /obj/structure/grille(spawn_point)
|
||||
items_to_spawn--
|
||||
else if(against_wall)
|
||||
switch(rand(1,10))
|
||||
if(1 to 3)
|
||||
if(prob(50))
|
||||
new /obj/structure/table(spawn_point)
|
||||
else
|
||||
new /obj/structure/rack(spawn_point)
|
||||
items_to_spawn--
|
||||
new /obj/effect/spawner/lootdrop/maintenance(spawn_point)
|
||||
if(4 to 5)
|
||||
new /obj/machinery/space_heater(spawn_point)
|
||||
if(6 to 7)
|
||||
new /obj/structure/closet/emcloset(spawn_point)
|
||||
if(8 to 9)
|
||||
new /obj/structure/closet/firecloset(spawn_point)
|
||||
if(10)
|
||||
new /obj/structure/closet/toolcloset(spawn_point)
|
||||
items_to_spawn--
|
||||
else
|
||||
switch(rand(1,10))
|
||||
if(1 to 3)
|
||||
new /obj/structure/grille/broken(spawn_point)
|
||||
if(4 to 6)
|
||||
new /obj/structure/girder/displaced(spawn_point)
|
||||
if(7 to 9)
|
||||
new /obj/structure/grille(spawn_point)
|
||||
else
|
||||
new /obj/effect/spawner/lootdrop/maintenance(spawn_point)
|
||||
items_to_spawn--
|
||||
//used_spawn_points += spawn_point
|
||||
attempts = max_attempts
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/proc/add_apcs()
|
||||
for(var/area/current_area in areas_included)
|
||||
var/list/available_turfs = (working_turfs & typecache_filter_list(current_area.contents, typecacheof(/turf/open/floor)))
|
||||
if(current_area.get_apc() || available_turfs.len <= 0)
|
||||
continue
|
||||
var/obj/machinery/power/apc/apc_placed = null
|
||||
var/attempts = 10
|
||||
while(!apc_placed && attempts>0)
|
||||
var/turf/apc_spot = pick(available_turfs)
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/turf/neighbor = get_step(apc_spot, direction)
|
||||
if(istype(neighbor, /turf/closed/wall) && !apc_placed)
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
apc_placed = new /obj/machinery/power/apc/auto_name/north(apc_spot)
|
||||
if(SOUTH)
|
||||
apc_placed = new /obj/machinery/power/apc/auto_name/south(apc_spot)
|
||||
if(EAST)
|
||||
apc_placed = new /obj/machinery/power/apc/auto_name/east(apc_spot)
|
||||
if(WEST)
|
||||
apc_placed = new /obj/machinery/power/apc/auto_name/west(apc_spot)
|
||||
attempts--
|
||||
|
||||
//We're not actually building these so they can break if Init doesn't trigger right like using a generator mid round
|
||||
if(!apc_placed.area)
|
||||
apc_placed.area = get_area(apc_placed.loc)
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/proc/wire_apcs()
|
||||
var/list/nearby_apcs = list()
|
||||
var/list/gen_area_apcs = list()
|
||||
for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
|
||||
if(apc.z == z_level)
|
||||
if(areas_included.Find(get_area(apc)))
|
||||
//log_world("[apc] added to gen area apcs")
|
||||
gen_area_apcs += apc
|
||||
else
|
||||
//log_world("[apc] added to nearby apcs")
|
||||
nearby_apcs += apc
|
||||
if(!nearby_apcs.len)
|
||||
return
|
||||
for(var/obj/machinery/power/apc/our_apc in gen_area_apcs)
|
||||
//log_world("finding path for [our_apc]")
|
||||
var/obj/machinery/power/apc/closest_apc = null
|
||||
var/min_dist = 0
|
||||
|
||||
for(var/obj/machinery/power/apc/target_apc in nearby_apcs)
|
||||
if(!closest_apc)
|
||||
closest_apc = target_apc
|
||||
if(!min_dist)
|
||||
min_dist = get_dist(our_apc, closest_apc)
|
||||
if(get_dist(our_apc, target_apc) < min_dist)
|
||||
closest_apc = target_apc
|
||||
min_dist = get_dist(our_apc, closest_apc)
|
||||
|
||||
var/access_card = new /obj/item/card/id/captains_spare
|
||||
|
||||
//We have to rawdog the Astar pathfinding and skip the wrapper proc because that's made specifically for mobs
|
||||
var/list/cable_path = AStar(
|
||||
caller = our_apc,
|
||||
_end = closest_apc,
|
||||
dist = /turf/proc/Distance_cardinal,
|
||||
maxnodes = 0,
|
||||
maxnodedepth = 0,
|
||||
mintargetdist = 0,
|
||||
adjacent = /turf/proc/wiringTurfTest,
|
||||
id = access_card,
|
||||
exclude = null,
|
||||
simulated_only = FALSE,
|
||||
get_best_attempt = TRUE)
|
||||
|
||||
if(!cable_path || cable_path.len <= 1)
|
||||
//log_world("Cable path for [our_apc] either null or only 1 tile")
|
||||
continue
|
||||
|
||||
for(var/i in 1 to cable_path.len)
|
||||
var/turf/cable_step = cable_path[i]
|
||||
var/turf/cable_step_prev
|
||||
var/turf/cable_step_next
|
||||
var/d1 = 0
|
||||
var/d2 = NORTH
|
||||
if (i == 1)
|
||||
cable_step_next = cable_path[i+1]
|
||||
d1 = 0
|
||||
d2 = get_dir(cable_step, cable_step_next)
|
||||
else if (i == cable_path.len)
|
||||
cable_step_prev = cable_path[i-1]
|
||||
d1 = 0
|
||||
d2 = get_dir(cable_step, cable_step_prev)
|
||||
else
|
||||
cable_step_prev = cable_path[i-1]
|
||||
cable_step_next = cable_path[i+1]
|
||||
d1 = get_dir(cable_step, cable_step_prev)
|
||||
d2 = get_dir(cable_step, cable_step_next)
|
||||
//cables fucking suck and require the smaller direction value be d1 and larger direction value be d2
|
||||
if(d1>d2)
|
||||
var/tmp_dir = d1
|
||||
d1 = d2
|
||||
d2 = tmp_dir
|
||||
var/reached_existing_powernet = FALSE
|
||||
for(var/obj/structure/cable/existing_cable in cable_step)
|
||||
//if there's a duplicate cable in the exact position and orientation we're about to use, we have tied into existing cabling on the map and can stop
|
||||
if(existing_cable.icon_state == "[d1]-[d2]")
|
||||
reached_existing_powernet = TRUE
|
||||
if(reached_existing_powernet)
|
||||
//this could really be continue or break, but if you continue, it might make more unnecessary cables later to get to the same spot
|
||||
break
|
||||
var/obj/structure/cable/new_cable = new(cable_step)
|
||||
new_cable.icon_state = "[d1]-[d2]"
|
||||
if(istype(cable_step, /turf/open/space))
|
||||
for(var/obj/structure/lattice/existing_lat in cable_step.contents)
|
||||
qdel(existing_lat)
|
||||
new /obj/structure/lattice/catwalk(cable_step)
|
||||
//create a new powernet with the cable, if needed it will be merged later
|
||||
//new_cable.mergeConnectedNetworks(new_cable.d1)
|
||||
|
||||
/datum/map_generator/dungeon_generator/maintenance/proc/check_adjacent_turfs(turf/turf_to_check)
|
||||
var/against_wall = FALSE
|
||||
var/blocking_passage = FALSE
|
||||
var/brazil = FALSE
|
||||
var/blocked_directions = 0
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/turf/neighbor = get_step(turf_to_check, direction)
|
||||
if(neighbor?.is_blocked_turf(TRUE, ignore_atoms = list(/obj/structure), type_list = TRUE))
|
||||
blocked_directions |= direction
|
||||
|
||||
if( blocked_directions )
|
||||
//probably
|
||||
against_wall = TRUE
|
||||
|
||||
if( ((blocked_directions & (NORTH|SOUTH)) == (NORTH|SOUTH)) || ((blocked_directions & (EAST|WEST)) == (EAST|WEST)) )
|
||||
//definitely
|
||||
blocking_passage = TRUE
|
||||
|
||||
if( (blocked_directions & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST) )
|
||||
//what the fuck how did you get here
|
||||
brazil = TRUE
|
||||
return "blocked directions: [blocked_directions], against a wall: [against_wall], in a one tile hallway: [blocking_passage], brazil: [brazil]"
|
||||
@@ -0,0 +1,49 @@
|
||||
/datum/dungeon_room/maintenance
|
||||
min_doors = 2
|
||||
|
||||
/datum/dungeon_room/maintenance/generate_room_theme()
|
||||
|
||||
if(!room_type)
|
||||
if(completed_room && is_ruin_compatible() && prob(75))
|
||||
//because ruins are a special type we overwrite the previous flags so the only possible theme is the ruin type
|
||||
room_type = ROOM_TYPE_RUIN
|
||||
|
||||
else if(completed_room && prob(20))
|
||||
room_type = ROOM_TYPE_SPACE
|
||||
else
|
||||
room_type = ROOM_TYPE_RANDOM
|
||||
|
||||
if(!room_danger_level)
|
||||
if(completed_room && prob(50))
|
||||
room_danger_level = ROOM_RATING_HOSTILE
|
||||
|
||||
else
|
||||
room_danger_level = ROOM_RATING_SAFE
|
||||
|
||||
var/list/possible_themes = list()
|
||||
for(var/datum/dungeon_room_theme/potential_theme as anything in typecacheof(list(generator_ref.room_theme_path), TRUE))
|
||||
if((room_type == initial(potential_theme.room_type)) && (room_danger_level & (initial(potential_theme.room_danger_level))))
|
||||
possible_themes |= potential_theme
|
||||
|
||||
var/new_theme_path = pick(possible_themes)
|
||||
room_theme = new new_theme_path(src)
|
||||
room_theme.Initialize()
|
||||
|
||||
/datum/dungeon_room/maintenance/is_ruin_compatible()
|
||||
var/width_without_walls = width-2
|
||||
var/height_without_walls = height-2
|
||||
var/compatible = FALSE
|
||||
switch("[width_without_walls]x[height_without_walls]")
|
||||
if("3x3")
|
||||
compatible = TRUE
|
||||
if("3x5")
|
||||
compatible = TRUE
|
||||
if("5x3")
|
||||
compatible = TRUE
|
||||
if("5x4")
|
||||
compatible = TRUE
|
||||
if("10x5")
|
||||
compatible = TRUE
|
||||
if("10x10")
|
||||
compatible = TRUE
|
||||
return (compatible && completed_room)
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/dungeon_room_theme/maintenance
|
||||
room_type = ROOM_TYPE_RANDOM
|
||||
room_danger_level = ROOM_RATING_SAFE | ROOM_RATING_HOSTILE
|
||||
|
||||
weighted_possible_floor_types = list(/turf/open/floor/plasteel/dark = 5, /turf/open/floor/plating = 5)
|
||||
|
||||
weighted_possible_window_types = list(/obj/effect/spawner/structure/window/reinforced = 2, /obj/effect/spawner/structure/window = 5)
|
||||
window_weight = 10
|
||||
|
||||
weighted_possible_door_types = list(/obj/machinery/door/airlock/maintenance_hatch = 1)
|
||||
feature_weight = 80
|
||||
mob_spawn_chance = 50
|
||||
@@ -0,0 +1,263 @@
|
||||
/datum/dungeon_room_theme/maintenance/botany
|
||||
weighted_possible_floor_types = list(
|
||||
/turf/open/floor/plating = 3,
|
||||
/turf/open/floor/grass = 5,
|
||||
/turf/open/floor/wood = 5
|
||||
)
|
||||
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/machinery/hydroponics/soil = 4,
|
||||
/obj/structure/closet/secure_closet/hydroponics = 2,
|
||||
/obj/item/queen_bee = 1,
|
||||
/obj/item/seeds/random = 3,
|
||||
list(/obj/structure/table, /obj/item/reagent_containers/glass/bottle/nutrient/ez, /obj/item/reagent_containers/glass/bottle/nutrient/rh) = 1,
|
||||
list(/obj/structure/table, /obj/item/cultivator, /obj/item/hatchet) = 1,
|
||||
/obj/item/reagent_containers/glass/bottle/ammonia = 1,
|
||||
/obj/item/reagent_containers/glass/bottle/diethylamine = 1,
|
||||
)
|
||||
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/butterfly = 3,
|
||||
/mob/living/simple_animal/cow = 2,
|
||||
/mob/living/simple_animal/chick = 1,
|
||||
/mob/living/simple_animal/chicken = 1,
|
||||
/mob/living/simple_animal/sheep = 1
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/botany/pre_initialize()
|
||||
. = ..()
|
||||
for(var/i in 1 to 5)
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/seeds) - /obj/item/seeds/lavaland)
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/material_storeroom
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/item/stack/rods/fifty = 2,
|
||||
/obj/item/stack/cable_coil/random = 2,
|
||||
/obj/item/stack/sheet/metal/fifty = 2,
|
||||
/obj/item/stack/sheet/glass/fifty = 2,
|
||||
/obj/item/stack/sheet/mineral/wood = 2,
|
||||
/obj/item/stack/sheet/mineral/plasma/ten = 2,
|
||||
/obj/machinery/power/port_gen/pacman = 1,
|
||||
/obj/structure/frame/machine = 2,
|
||||
/obj/structure/frame/computer = 1,
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/material_storeroom/pre_initialize()
|
||||
. = ..()
|
||||
for(var/i in 1 to 3)
|
||||
if(prob(10))
|
||||
//if i include all types of every stock part and subtype, it gets super bogged down, so one from each pool
|
||||
weighted_feature_spawn_list[/obj/item/storage/toolbox/syndicate]++
|
||||
else
|
||||
weighted_feature_spawn_list[/obj/item/storage/toolbox/mechanical]++
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/material_storeroom/post_generate()
|
||||
. = ..()
|
||||
for(var/obj/item/stack/stack_to_randomize in dungeon_room_ref.features)
|
||||
if(stack_to_randomize.amount == 1)
|
||||
stack_to_randomize.amount = rand(1, 25)
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/gym
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/item/reagent_containers/glass/beaker/waterbottle = 5,
|
||||
/obj/item/reagent_containers/food/snacks/bearsteak = 1,
|
||||
/obj/structure/punching_bag = 1,
|
||||
/obj/structure/weightmachine/stacklifter = 2,
|
||||
/obj/structure/closet/boxinggloves = 1,
|
||||
/obj/item/reagent_containers/glass/rag = 1,
|
||||
/obj/structure/holohoop = 1,
|
||||
/obj/item/toy/beach_ball/holoball = 1,
|
||||
//steroids are based
|
||||
/obj/item/dnainjector/strong = 1,
|
||||
)
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/hostile/carp = 1,
|
||||
/mob/living/simple_animal/mouse = 2,
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/gym/post_generate()
|
||||
. = ..()
|
||||
for(var/datum/weakref/mob_ref as anything in dungeon_room_ref.mobs)
|
||||
var/mob/living/simple_animal/gym_mob = mob_ref.resolve()
|
||||
if(gym_mob)
|
||||
gym_mob.faction |= "gym"
|
||||
if(istype(gym_mob, /mob/living/simple_animal/hostile/carp))
|
||||
gym_mob.name = "\improper Gym Shark"
|
||||
gym_mob.desc = "The only thing this up and coming shark hits harder than the weights is anyone who interrupts their sets."
|
||||
if(istype(gym_mob, /mob/living/simple_animal/mouse))
|
||||
gym_mob.name = "\improper Gym Rat"
|
||||
gym_mob.desc = "He's not about to settle for Gouda-nough."
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/junk
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/item/reagent_containers/food/drinks/soda_cans/grey_bull = 1,
|
||||
)
|
||||
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/cockroach = 3,
|
||||
/mob/living/simple_animal/hostile/glockroach = 2,
|
||||
/mob/living/simple_animal/mouse = 3,
|
||||
/mob/living/simple_animal/opossum = 1,
|
||||
/mob/living/simple_animal/hostile/retaliate/goat = 1,
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/junk/pre_initialize()
|
||||
. = ..()
|
||||
weighted_feature_spawn_list |= subtypesof(/obj/item/trash)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/junk/post_generate()
|
||||
. = ..()
|
||||
for(var/datum/weakref/mob_ref in dungeon_room_ref.mobs)
|
||||
var/mob/living/simple_animal/trash_animal = mob_ref.resolve()
|
||||
if(trash_animal)
|
||||
trash_animal.faction |= "trash"
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/medical
|
||||
weighted_possible_floor_types = list(
|
||||
/turf/open/floor/plasteel = 5,
|
||||
/turf/open/floor/plating = 4,
|
||||
)
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/structure/bed/roller = 2,
|
||||
/obj/structure/table/optable = 1,
|
||||
/obj/machinery/iv_drip = 2,
|
||||
/obj/machinery/stasis = 1,
|
||||
/obj/machinery/sleeper = 1,
|
||||
)
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/hostile/zombie = 3,
|
||||
/mob/living/simple_animal/bot/medbot = 1
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/medical/pre_initialize()
|
||||
. = ..()
|
||||
var/list/pills_here = list(
|
||||
/obj/structure/table,
|
||||
pick(subtypesof(/obj/item/reagent_containers/pill)),
|
||||
pick(subtypesof(/obj/item/reagent_containers/pill)),
|
||||
pick(subtypesof(/obj/item/reagent_containers/pill)))
|
||||
weighted_feature_spawn_list += list(pills_here)
|
||||
|
||||
var/list/operating_table = list(/obj/structure/table)
|
||||
switch(rand(1,20))
|
||||
if(1 to 2)
|
||||
//full set with the drip
|
||||
operating_table += /obj/item/storage/backpack/duffelbag/med/surgery
|
||||
if(3 to 5)
|
||||
//come on baby give me the mini e-sword
|
||||
operating_table += pick(/obj/item/scalpel/advanced, /obj/item/retractor/advanced, /obj/item/cautery/advanced)
|
||||
if(6 to 10)
|
||||
//everything you need
|
||||
operating_table |= list(/obj/item/scalpel, /obj/item/retractor, /obj/item/cautery, /obj/item/circular_saw, /obj/item/bonesetter)
|
||||
if(11 to 20)
|
||||
//the absolute basics and sterilizine for when you try to cut open their skull with a scalpel
|
||||
operating_table |= list(/obj/item/scalpel, /obj/item/retractor, /obj/item/cautery, /obj/item/reagent_containers/medspray/sterilizine)
|
||||
|
||||
weighted_feature_spawn_list += list(operating_table)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/medical/post_generate()
|
||||
. = ..()
|
||||
for(var/datum/weakref/mob_ref in dungeon_room_ref.mobs)
|
||||
var/mob/living/simple_animal/medical_professional = mob_ref.resolve()
|
||||
if(medical_professional)
|
||||
//I AM A SURGEON DR HAN
|
||||
medical_professional.faction |= "surgeon"
|
||||
if(istype(medical_professional, /mob/living/simple_animal/hostile/zombie) && prob(1))
|
||||
medical_professional.desc = "Oh my god he IS a surgeon..."
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/robotics
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/effect/decal/cleanable/robot_debris = 2,
|
||||
/obj/effect/decal/cleanable/oil = 3,
|
||||
/obj/item/stack/cable_coil/random = 2,
|
||||
/obj/item/weldingtool/largetank = 1,
|
||||
/obj/structure/frame/machine = 1,
|
||||
/obj/structure/frame/computer = 1,
|
||||
)
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/hostile/hivebot = 5,
|
||||
/mob/living/simple_animal/hostile/hivebot/range = 2,
|
||||
/mob/living/simple_animal/hostile/hivebot/rapid = 1,
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/robotics/pre_initialize()
|
||||
. = ..()
|
||||
if(prob(25))
|
||||
//if i include all types of every stock part and subtype, it gets super bogged down, so one from each pool
|
||||
weighted_feature_spawn_list |= pick(typesof(/obj/item/storage/part_replacer/bluespace))
|
||||
else
|
||||
weighted_feature_spawn_list |= list(/obj/item/storage/part_replacer = 1)
|
||||
|
||||
weighted_feature_spawn_list |= pick( (subtypesof(/mob/living/simple_animal/bot) - /mob/living/simple_animal/bot/secbot/grievous) )
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/borg/upgrade) - typesof(/obj/item/borg/upgrade/modkit))
|
||||
//all wrecks except the god damn ones that explode upon existing
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/structure/mecha_wreckage) - list(/obj/structure/mecha_wreckage/gygax/dark, /obj/structure/mecha_wreckage/mauler))
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/stock_parts/capacitor))
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/stock_parts/scanning_module))
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/stock_parts/manipulator))
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/stock_parts/micro_laser))
|
||||
weighted_feature_spawn_list |= pick(subtypesof(/obj/item/stock_parts/matter_bin))
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/spiders
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/structure/spider/stickyweb = 5,
|
||||
/obj/structure/spider/cocoon = 5,
|
||||
/obj/structure/spider/spiderling = 2,
|
||||
)
|
||||
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider = 10,
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter = 4,
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/ice = 2,
|
||||
)
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/xenobio
|
||||
weighted_possible_floor_types = list(
|
||||
/turf/open/floor/plasteel/dark = 3,
|
||||
/turf/open/floor/plasteel = 5,
|
||||
/turf/open/floor/plating = 3,
|
||||
)
|
||||
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/machinery/processor/slime = 1,
|
||||
/obj/item/extinguisher = 2,
|
||||
list(/obj/structure/table, /obj/item/slime_extract/grey, /obj/item/storage/box/monkeycubes, /obj/item/reagent_containers/syringe/plasma) = 1,
|
||||
list(/obj/structure/table, /obj/item/reagent_containers/glass/beaker/waterbottle, /obj/item/reagent_containers/food/snacks/monkeycube) = 1,
|
||||
/obj/structure/frame/machine = 1,
|
||||
/obj/structure/frame/computer = 1,
|
||||
)
|
||||
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/carbon/monkey = 2,
|
||||
)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/xenobio/pre_initialize()
|
||||
. = ..()
|
||||
switch(rand(1,10))
|
||||
if(1)
|
||||
weighted_feature_spawn_list += pick(subtypesof(/obj/item/slimecross))
|
||||
if(2)
|
||||
weighted_feature_spawn_list += /obj/item/slime_extract/rainbow
|
||||
if(3)
|
||||
weighted_feature_spawn_list += pick(subtypesof(/obj/item/slime_extract))
|
||||
if(4 to 7)
|
||||
for(var/x in 1 to 3)
|
||||
weighted_feature_spawn_list += pick(
|
||||
/obj/item/slime_extract/grey,
|
||||
/obj/item/slime_extract/orange,
|
||||
/obj/item/slime_extract/purple,
|
||||
/obj/item/slime_extract/blue,
|
||||
/obj/item/slime_extract/metal)
|
||||
else
|
||||
weighted_feature_spawn_list[/obj/item/slime_extract/grey] = 2
|
||||
|
||||
for(var/x in 1 to 3)
|
||||
if(prob(10))
|
||||
weighted_mob_spawn_list[/mob/living/simple_animal/slime/random]++
|
||||
else
|
||||
weighted_mob_spawn_list[/mob/living/simple_animal/slime]++
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/dungeon_room_theme/maintenance/ruin
|
||||
room_type = ROOM_TYPE_RUIN
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/ruin/pre_initialize()
|
||||
. = ..()
|
||||
var/width_without_walls = dungeon_room_ref.width-2
|
||||
var/height_without_walls = dungeon_room_ref.height-2
|
||||
|
||||
switch("[width_without_walls]x[height_without_walls]")
|
||||
if("3x3")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/threexthree
|
||||
if("3x5")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/threexfive
|
||||
if("5x3")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/fivexthree
|
||||
if("5x4")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/fivexfour
|
||||
if("10x5")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/tenxfive
|
||||
if("10x10")
|
||||
special_feature = /obj/effect/landmark/stationroom/maint/tenxten
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/ruin/post_generate()
|
||||
. = ..()
|
||||
for(var/datum/weakref/spawner_ref as anything in dungeon_room_ref.features)
|
||||
var/obj/effect/landmark/stationroom/spawner = spawner_ref.resolve()
|
||||
if(spawner)
|
||||
spawner.unique = FALSE
|
||||
spawner.load()
|
||||
// if(spawner.load())
|
||||
// dungeon_room_ref.features -= spawner
|
||||
@@ -0,0 +1,74 @@
|
||||
/datum/dungeon_room_theme/maintenance/space
|
||||
room_type = ROOM_TYPE_SPACE
|
||||
weighted_possible_floor_types = list(/turf/open/space/basic = 1)
|
||||
//no doors to space rooms
|
||||
weighted_possible_door_types = list()
|
||||
weighted_possible_window_types = list(/obj/effect/spawner/structure/window/reinforced/shutter = 1)
|
||||
//no mobs in space, althought if i find a way to percent chance spawn a mob, i might make a space cat spawn
|
||||
weighted_mob_spawn_list = list()
|
||||
window_weight = 80
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/space/pre_initialize()
|
||||
. = ..()
|
||||
for(var/turf/turf_between_space in dungeon_room_ref.exterior)
|
||||
var/turf/turf_outside_room = get_step(turf_between_space, get_dir(dungeon_room_ref.center, turf_between_space))
|
||||
if(isspaceturf(turf_outside_room))
|
||||
dungeon_room_ref.exterior -= turf_between_space
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/space/post_generate()
|
||||
. = ..()
|
||||
for(var/turf/space_to_check in dungeon_room_ref.interior)
|
||||
var/needs_catwalk = FALSE
|
||||
|
||||
if(locate(/obj/machinery/atmospherics/pipe) in space_to_check)
|
||||
needs_catwalk = TRUE
|
||||
else if (locate(/obj/structure/cable) in space_to_check)
|
||||
needs_catwalk = TRUE
|
||||
else
|
||||
continue
|
||||
if(needs_catwalk && !locate(/obj/structure/lattice/catwalk) in space_to_check)
|
||||
for(var/obj/structure/lattice/existing_lattice in space_to_check)
|
||||
qdel(existing_lattice)
|
||||
new /obj/structure/lattice/catwalk(space_to_check)
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/space/basic
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/structure/lattice = 10,
|
||||
/obj/item/stack/ore/glass = 3,
|
||||
/obj/item/tank/internals/emergency_oxygen = 1,
|
||||
)
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/space/basic/pre_initialize()
|
||||
. = ..()
|
||||
switch(rand(1,10))
|
||||
if(1)
|
||||
//the blessed space cat
|
||||
weighted_feature_spawn_list += /mob/living/simple_animal/pet/cat/space
|
||||
if(2)
|
||||
//a "dolphin"
|
||||
weighted_feature_spawn_list += /mob/living/simple_animal/hostile/retaliate/dolphin/bouncer
|
||||
if(3 to 4)
|
||||
//TWO DOLPHS
|
||||
for(var/x in 1 to 3)
|
||||
weighted_feature_spawn_list += pick(/mob/living/simple_animal/hostile/retaliate/dolphin, /mob/living/simple_animal/hostile/retaliate/dolphin/manatee)
|
||||
if(5 to 6)
|
||||
//a dolphino
|
||||
weighted_feature_spawn_list += pick(/mob/living/simple_animal/hostile/retaliate/dolphin, /mob/living/simple_animal/hostile/retaliate/dolphin/manatee)
|
||||
else
|
||||
//you get nothing good day sir
|
||||
|
||||
|
||||
/datum/dungeon_room_theme/maintenance/space/hostile
|
||||
room_type = ROOM_RATING_HOSTILE
|
||||
weighted_feature_spawn_list = list(
|
||||
/obj/structure/lattice = 10,
|
||||
/obj/item/stack/ore/glass = 3,
|
||||
/obj/item/tank/internals/emergency_oxygen = 1,
|
||||
/obj/effect/mob_spawn/human/corpse = 2,
|
||||
)
|
||||
weighted_mob_spawn_list = list(
|
||||
/mob/living/simple_animal/hostile/carp = 1,
|
||||
)
|
||||
mob_spawn_chance = 50
|
||||
@@ -374,10 +374,12 @@
|
||||
|
||||
/datum/status_effect/exercised/on_creation(mob/living/new_owner, ...)
|
||||
. = ..()
|
||||
owner.faction |= "gym"
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
START_PROCESSING(SSprocessing, src) //this lasts 20 minutes, so SSfastprocess isn't needed.
|
||||
|
||||
/datum/status_effect/exercised/Destroy()
|
||||
owner.faction &= "gym"
|
||||
. = ..()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
lighting_colour_tube = "#ffe5cb"
|
||||
lighting_colour_bulb = "#ffdbb4"
|
||||
|
||||
|
||||
//Departments
|
||||
|
||||
/area/maintenance/department/chapel
|
||||
|
||||
22
code/game/area/areas/procedurally_generated.dm
Normal file
22
code/game/area/areas/procedurally_generated.dm
Normal file
@@ -0,0 +1,22 @@
|
||||
/area/procedurally_generated
|
||||
name = "Dungeon"
|
||||
sound_environment = SOUND_AREA_LARGE_ENCLOSED
|
||||
map_generator = /datum/map_generator/dungeon_generator
|
||||
unique = FALSE
|
||||
|
||||
///If there's other areas on the same z-level that share a generator, their turfs will be aggregated together into a combined working area
|
||||
var/shared_generator_initialized = FALSE
|
||||
|
||||
/area/procedurally_generated/RunGeneration()
|
||||
if(ispath(map_generator))
|
||||
map_generator = new map_generator(src)
|
||||
map_generator.combine_local_areas()
|
||||
|
||||
if(!shared_generator_initialized)
|
||||
map_generator.generate_terrain()
|
||||
|
||||
/area/procedurally_generated/test_gen()
|
||||
if(!ispath(map_generator))
|
||||
map_generator.generate_terrain()
|
||||
|
||||
|
||||
171
code/game/area/areas/procedurally_generated_areas/maintenance.dm
Normal file
171
code/game/area/areas/procedurally_generated_areas/maintenance.dm
Normal file
@@ -0,0 +1,171 @@
|
||||
/area/procedurally_generated/maintenance
|
||||
name = "Unfinished Maintenance"
|
||||
ambience_index = AMBIENCE_MAINT
|
||||
sound_environment = SOUND_AREA_TUNNEL_ENCLOSED
|
||||
valid_territory = FALSE
|
||||
minimap_color = "#4f4e3a"
|
||||
airlock_wires = /datum/wires/airlock/maint
|
||||
ambient_buzz = 'sound/ambience/source_corridor2.ogg'
|
||||
ambient_buzz_vol = 10
|
||||
min_ambience_cooldown = 20 SECONDS
|
||||
max_ambience_cooldown = 35 SECONDS
|
||||
|
||||
map_generator = /datum/map_generator/dungeon_generator/maintenance
|
||||
|
||||
/area/procedurally_generated/maintenance/department/chapel
|
||||
name = "Chapel Maintenance"
|
||||
icon_state = "maint_chapel"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/chapel/monastery
|
||||
name = "Monastery Maintenance"
|
||||
icon_state = "maint_monastery"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/crew_quarters/bar
|
||||
name = "Bar Maintenance"
|
||||
icon_state = "maint_bar"
|
||||
sound_environment = SOUND_AREA_WOODFLOOR
|
||||
|
||||
/area/procedurally_generated/maintenance/department/crew_quarters/dorms
|
||||
name = "Dormitory Maintenance"
|
||||
icon_state = "maint_dorms"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/eva
|
||||
name = "EVA Maintenance"
|
||||
icon_state = "maint_eva"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/electrical
|
||||
name = "Electrical Maintenance"
|
||||
icon_state = "maint_electrical"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/tcoms
|
||||
name = "Telecommunications Maintenance"
|
||||
icon_state = "tcomsatmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/engine/atmos
|
||||
name = "Atmospherics Maintenance"
|
||||
icon_state = "maint_atmos"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/security
|
||||
name = "Security Maintenance"
|
||||
icon_state = "maint_sec"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/security/brig
|
||||
name = "Brig Maintenance"
|
||||
icon_state = "maint_brig"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/medical
|
||||
name = "Medbay Maintenance"
|
||||
icon_state = "medbay_maint"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/medical/central
|
||||
name = "Central Medbay Maintenance"
|
||||
icon_state = "medbay_maint_central"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/medical/morgue
|
||||
name = "Morgue Maintenance"
|
||||
icon_state = "morgue_maint"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/science
|
||||
name = "Science Maintenance"
|
||||
icon_state = "maint_sci"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/science/central
|
||||
name = "Central Science Maintenance"
|
||||
icon_state = "maint_sci_central"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/cargo
|
||||
name = "Cargo Maintenance"
|
||||
icon_state = "maint_cargo"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/bridge
|
||||
name = "Bridge Maintenance"
|
||||
icon_state = "maint_bridge"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/engine
|
||||
name = "Engineering Maintenance"
|
||||
icon_state = "maint_engi"
|
||||
|
||||
/area/procedurally_generated/maintenance/department/science/xenobiology
|
||||
name = "Xenobiology Maintenance"
|
||||
icon_state = "xenomaint"
|
||||
xenobiology_compatible = TRUE
|
||||
|
||||
|
||||
//Maintenance - Generic
|
||||
|
||||
/area/procedurally_generated/maintenance/aft
|
||||
name = "Aft (S) Maintenance"
|
||||
icon_state = "amaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/aft/secondary
|
||||
name = "Aft (S) Maintenance"
|
||||
icon_state = "amaint_2"
|
||||
|
||||
/area/procedurally_generated/maintenance/central
|
||||
name = "Central Maintenance"
|
||||
icon_state = "maintcentral"
|
||||
|
||||
/area/procedurally_generated/maintenance/central/secondary
|
||||
name = "Central Maintenance"
|
||||
icon_state = "maintcentral"
|
||||
clockwork_warp_allowed = FALSE
|
||||
|
||||
/area/procedurally_generated/maintenance/fore
|
||||
name = "Fore (N) Maintenance"
|
||||
icon_state = "fmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/fore/secondary
|
||||
name = "Fore (N) Maintenance"
|
||||
icon_state = "fmaint_2"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard
|
||||
name = "Starboard (E) Maintenance"
|
||||
icon_state = "smaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard/central
|
||||
name = "Central Starboard (E) Maintenance"
|
||||
icon_state = "smaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard/secondary
|
||||
name = "Secondary Starboard (E) Maintenance"
|
||||
icon_state = "smaint_2"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard/aft
|
||||
name = "Starboard Quarter (SE) Maintenance"
|
||||
icon_state = "asmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard/aft/secondary
|
||||
name = "Secondary Starboard Quarter (SE) Maintenance"
|
||||
icon_state = "asmaint_2"
|
||||
|
||||
/area/procedurally_generated/maintenance/starboard/fore
|
||||
name = "Starboard Bow (NE) Maintenance"
|
||||
icon_state = "fsmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/port
|
||||
name = "Port (W) Maintenance"
|
||||
icon_state = "pmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/port/central
|
||||
name = "Central Port (W) Maintenance"
|
||||
icon_state = "maintcentral"
|
||||
|
||||
/area/procedurally_generated/maintenance/port/aft
|
||||
name = "Port Quarter (SW) Maintenance"
|
||||
icon_state = "apmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/port/fore
|
||||
name = "Port Bow (NW) Maintenance"
|
||||
icon_state = "fpmaint"
|
||||
|
||||
/area/procedurally_generated/maintenance/disposal
|
||||
name = "Waste Disposal"
|
||||
icon_state = "disposal"
|
||||
|
||||
/area/procedurally_generated/maintenance/disposal/incinerator
|
||||
name = "Incinerator"
|
||||
icon_state = "incinerator"
|
||||
|
||||
/area/procedurally_generated/maintenance/the_backrooms
|
||||
name = "The Backrooms"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#define SINGLE "single"
|
||||
#define VERTICAL "vertical"
|
||||
#define HORIZONTAL "horizontal"
|
||||
|
||||
#define METAL 1
|
||||
#define WOOD 2
|
||||
@@ -214,8 +212,6 @@
|
||||
|
||||
|
||||
#undef SINGLE
|
||||
#undef VERTICAL
|
||||
#undef HORIZONTAL
|
||||
|
||||
#undef METAL
|
||||
#undef WOOD
|
||||
|
||||
@@ -6,6 +6,7 @@ again.
|
||||
|
||||
/obj/effect/spawner/structure
|
||||
name = "map structure spawner"
|
||||
density = TRUE
|
||||
var/list/spawn_list
|
||||
|
||||
/obj/effect/spawner/structure/Initialize(mapload)
|
||||
|
||||
@@ -169,6 +169,8 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
|
||||
point_value = 25
|
||||
merge_type = /obj/item/stack/sheet/mineral/plasma
|
||||
|
||||
/obj/item/stack/sheet/mineral/plasma/ten
|
||||
amount = 10
|
||||
/obj/item/stack/sheet/mineral/plasma/fifty
|
||||
amount = 50
|
||||
|
||||
|
||||
@@ -308,6 +308,8 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
|
||||
if(!wood_stack && replace)
|
||||
user.put_in_hands(new_item)
|
||||
|
||||
/obj/item/stack/sheet/mineral/wood/ten
|
||||
amount = 10
|
||||
/obj/item/stack/sheet/mineral/wood/fifty
|
||||
amount = 50
|
||||
|
||||
|
||||
@@ -675,9 +675,15 @@ GLOBAL_LIST_EMPTY(lockers)
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/CanAStarPass(ID, dir, caller)
|
||||
if(can_open(caller) || allowed(caller))
|
||||
return TRUE
|
||||
//The parent function just checks if it's not dense, and if a closet is open then it's not dense
|
||||
. = ..()
|
||||
if(!.)
|
||||
if(ismob(caller))
|
||||
//i'm hilarious, but fr only mobs should be passed to allowed()
|
||||
var/mob/mobchamp = caller
|
||||
return can_open(mobchamp) && allowed(mobchamp)
|
||||
else
|
||||
return can_open(caller) && check_access(ID)
|
||||
|
||||
/// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Unlock and open up when we get knock casted.
|
||||
/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, mob/living/caster)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
pass_flags = LETPASSTHROW|PASSSTRUCTURE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
pixel_y = -16
|
||||
|
||||
///Boolean on whether the railing should be cimable.
|
||||
|
||||
@@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
allowed_contents -= src
|
||||
for(var/i in 1 to allowed_contents.len)
|
||||
var/thing = allowed_contents[i]
|
||||
qdel(thing, force=TRUE)
|
||||
qdel(thing)
|
||||
|
||||
if(turf_type)
|
||||
ChangeTurf(turf_type, baseturf_type, flags)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
/area/construction,
|
||||
/area/vacant_room/commissary,
|
||||
/area/survivalpod,
|
||||
/area/procedurally_generated/maintenance,
|
||||
))
|
||||
if(is_type_in_typecache(A, engine_dirt_areas))
|
||||
if(prob(3))
|
||||
@@ -91,6 +92,7 @@
|
||||
/area/ai_monitored/turret_protected,
|
||||
/area/security,
|
||||
/area/crew_quarters/heads/hos,
|
||||
/area/procedurally_generated/maintenance,
|
||||
))
|
||||
if(is_type_in_typecache(A, gib_covered_areas))
|
||||
if(prob(20))
|
||||
|
||||
0
code/game/turfs/turf.dm
Executable file → Normal file
0
code/game/turfs/turf.dm
Executable file → Normal file
@@ -271,3 +271,26 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
return
|
||||
level.traits |= traits_to_add
|
||||
SSweather.update_z_level(level) //in case of someone adding a weather for the level, we want SSweather to update for that
|
||||
|
||||
/obj/effect/mapping_helpers/firedoor_border_spawner
|
||||
var/orientation = VERTICAL
|
||||
var/obj/machinery/door/firedoor/border_only/door1
|
||||
var/obj/machinery/door/firedoor/border_only/door2
|
||||
|
||||
/obj/effect/mapping_helpers/firedoor_border_spawner/Initialize(mapload, _orientation)
|
||||
. = ..()
|
||||
if(_orientation)
|
||||
orientation = _orientation
|
||||
door1 = new(loc)
|
||||
door2 = new(loc)
|
||||
switch(orientation)
|
||||
if(VERTICAL)
|
||||
//door1 is naturally the right orientation
|
||||
door2.dir = 1
|
||||
if(HORIZONTAL)
|
||||
door1.dir = 4
|
||||
door2.dir = 8
|
||||
|
||||
|
||||
/obj/effect/mapping_helpers/firedoor_border_spawner/horizontal
|
||||
orientation = HORIZONTAL
|
||||
|
||||
@@ -41,3 +41,14 @@
|
||||
if (z_list && z >= 1 && z <= z_list.len)
|
||||
return z_list[z]
|
||||
CRASH("Unmanaged z-level [z]! maxz = [world.maxz], z_list.len = [z_list ? z_list.len : "null"]")
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/generate_backrooms()
|
||||
var/datum/space_level/backrooms = add_new_zlevel("The Backrooms", ZTRAITS_BACKROOM_MAINTS, contain_turfs = FALSE)
|
||||
backrooms.set_linkage(SELFLOOPING)
|
||||
var/area/procedurally_generated/maintenance/the_backrooms/suffer = new()
|
||||
suffer.setup("The Backrooms")
|
||||
for(var/turf/to_contain as anything in Z_TURFS(backrooms.z_value))
|
||||
var/area/old_area = to_contain.loc
|
||||
to_contain.ChangeTurf(/turf/open/genturf, flags = CHANGETURF_DEFER_CHANGE)
|
||||
to_contain.change_area(old_area, suffer)
|
||||
suffer.RunGeneration()
|
||||
|
||||
@@ -102,8 +102,10 @@
|
||||
name = "adminordrazine pill"
|
||||
desc = "It's magic. We don't have to explain it."
|
||||
icon_state = "pill16"
|
||||
volume = 50
|
||||
list_reagents = list(/datum/reagent/medicine/adminordrazine = 50)
|
||||
volume = 5
|
||||
grind_results = null
|
||||
dissolvable = FALSE
|
||||
list_reagents = list(/datum/reagent/medicine/adminordrazine = 5)
|
||||
|
||||
/obj/item/reagent_containers/pill/morphine
|
||||
name = "morphine pill"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
@@ -229,6 +229,7 @@
|
||||
#include "code\__DEFINES\{yogs_defines}\jungle.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\layers.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\logging.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\mapping.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\maps.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\mentor.dm"
|
||||
#include "code\__DEFINES\{yogs_defines}\misc.dm"
|
||||
@@ -789,9 +790,18 @@
|
||||
#include "code\datums\looping_sounds\weather.dm"
|
||||
#include "code\datums\mapgen\_MapGenerator.dm"
|
||||
#include "code\datums\mapgen\CaveGenerator.dm"
|
||||
#include "code\datums\mapgen\DungeonGenerator.dm"
|
||||
#include "code\datums\mapgen\biomes\_biome.dm"
|
||||
#include "code\datums\mapgen\Cavegens\IcemoonCaves.dm"
|
||||
#include "code\datums\mapgen\Cavegens\LavalandGenerator.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\dungeon_room.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\dungeon_room_theme.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_generator.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_room.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_room_theme.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_room_themes\random.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_room_themes\ruin.dm"
|
||||
#include "code\datums\mapgen\dungeon_generators\maintenance_generator\maintenance_room_themes\space.dm"
|
||||
#include "code\datums\martial\boxing.dm"
|
||||
#include "code\datums\martial\buster_style.dm"
|
||||
#include "code\datums\martial\cqc.dm"
|
||||
@@ -926,7 +936,9 @@
|
||||
#include "code\game\area\areas\centcom.dm"
|
||||
#include "code\game\area\areas\holodeck.dm"
|
||||
#include "code\game\area\areas\mining.dm"
|
||||
#include "code\game\area\areas\procedurally_generated.dm"
|
||||
#include "code\game\area\areas\shuttles.dm"
|
||||
#include "code\game\area\areas\procedurally_generated_areas\maintenance.dm"
|
||||
#include "code\game\area\areas\ruins\_ruins.dm"
|
||||
#include "code\game\area\areas\ruins\icemoon.dm"
|
||||
#include "code\game\area\areas\ruins\lavaland.dm"
|
||||
@@ -4014,8 +4026,8 @@
|
||||
#include "yogstation\code\datums\mutations\alcohol.dm"
|
||||
#include "yogstation\code\datums\mutations\extendoarm.dm"
|
||||
#include "yogstation\code\datums\ruins\free_miners.dm"
|
||||
#include "yogstation\code\datums\ruins\jungle_fluff.dm"
|
||||
#include "yogstation\code\datums\ruins\jungle.dm"
|
||||
#include "yogstation\code\datums\ruins\jungle_fluff.dm"
|
||||
#include "yogstation\code\datums\ruins\station.dm"
|
||||
#include "yogstation\code\datums\status_effects\buffs.dm"
|
||||
#include "yogstation\code\datums\status_effects\neutral.dm"
|
||||
|
||||
Reference in New Issue
Block a user