Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions code/controllers/subsystems/mapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ SUBSYSTEM_DEF(mapping)
if(!TYPE_IS_ABSTRACT(template))
. += new template_type(type) // send name as a param to catch people doing illegal ad hoc creation

/datum/controller/subsystem/mapping/proc/get_template(var/template_name)
return map_templates[template_name]
/datum/controller/subsystem/mapping/proc/get_template(var/template)
return istype(template, /datum/map_template) ? template : map_templates[template]

/datum/controller/subsystem/mapping/proc/get_templates_by_category(var/temple_cat) // :33
return map_templates_by_category[temple_cat]
Expand Down
7 changes: 5 additions & 2 deletions code/game/objects/structures/wall_sconce.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
anchored = TRUE
/// Reference to the currently attached item.
var/obj/item/flame/light_source
/// Whether or not the light source, if present, is automatically lit on Initialize.
/// Whether or not the light source, if present, is automatically lit on Initialize. If a number above 1, is a random prob.
var/start_lit = FALSE

/obj/structure/wall_sconce/Initialize(var/ml, var/_mat, var/_reinf_mat, var/supplied_dir)

if(ispath(light_source))
light_source = new light_source(src)
if(start_lit && istype(light_source))
if(istype(light_source) && (start_lit == TRUE || prob(start_lit)))
light_source.light(null, no_message = TRUE)

. = ..()
Expand Down Expand Up @@ -132,6 +132,9 @@
/obj/structure/wall_sconce/lantern
light_source = /obj/item/flame/fuelled/lantern/filled

/obj/structure/wall_sconce/lantern/lit
start_lit = 30

/obj/structure/wall_sconce/candle
light_source = /obj/item/flame/candle

Expand Down
3 changes: 2 additions & 1 deletion code/game/turfs/turf_changing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
var/old_alpha_mask_state = get_movable_alpha_mask_state(null)
var/old_event_listeners = event_listeners
var/old_listening_to = _listening_to

var/old_maptext = maptext
var/old_ambience = ambient_light
var/old_ambience_mult = ambient_light_multiplier
var/old_ambient_light_old_r = ambient_light_old_r
Expand Down Expand Up @@ -185,6 +185,7 @@
if(changed_turf.zone_membership_candidate != old_zone_membership_candidate)
update_external_atmos_participation()

changed_turf.maptext = old_maptext
changed_turf.update_weather(force_update_below = changed_turf.is_open() != old_is_open)

if(keep_height)
Expand Down
6 changes: 6 additions & 0 deletions code/game/turfs/walls/wall_natural_subtypes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
color = "#41311b"
floor_type = /turf/floor/dirt

/turf/wall/natural/clay
material = /decl/material/solid/clay
color = "#807f7a"
floor_type = /turf/floor/clay

#define MATERIAL_NATURAL_TURFS(ID, MAT) \
/turf/floor/rock/##ID { \
color = /decl/material/##MAT::color; \
Expand Down Expand Up @@ -103,4 +108,5 @@ MATERIAL_NATURAL_TURFS(sandstone, solid/stone/sandstone)
MATERIAL_NATURAL_TURFS(basalt, solid/stone/basalt)
MATERIAL_NATURAL_TURFS(granite, solid/stone/granite)
MATERIAL_NATURAL_TURFS(marble, solid/stone/marble)
MATERIAL_NATURAL_TURFS(marble, solid/clay)
#undef MATERIAL_NATURAL_TURFS
4 changes: 3 additions & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ var/global/list/admin_verbs_spawn = list(
/client/proc/respawn_character,
/client/proc/respawn_as_self,
/client/proc/spawn_chemdisp_cartridge,
/datum/admins/proc/mass_debug_closet_icons
/datum/admins/proc/mass_debug_closet_icons,
/client/proc/place_modular_map_new_z,
/client/proc/place_modular_map_current_z
)
var/global/list/admin_verbs_server = list(
/datum/admins/proc/capture_map_part,
Expand Down
3 changes: 2 additions & 1 deletion code/modules/lighting/lighting_turf.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
// Builds a lighting overlay for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay(now = FALSE)
if (lighting_overlay)
CRASH("Attempted to create lighting_overlay on tile that already had one.")
//error("Attempted to create lighting_overlay on tile that already had one ([x],[y],[z]).")
return

if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src))
if (!lighting_corners_initialised || !corners)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/maps/helper_landmarks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ INITIALIZE_IMMEDIATE(/obj/abstract/landmark/map_load_mark)
var/turf/spawn_loc = get_turf(src)

if(istype(spawn_loc))
if(istext(template))
if(istext(template) || ispath(template))
template = SSmapping.get_template(template)
if(istype(template))
template.load(spawn_loc, centered = centered)
Expand Down
16 changes: 16 additions & 0 deletions code/modules/modular_map/__map_setup.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define MCF_BLOCKER BITFLAG(0)
#define MCF_HALL BITFLAG(1)
#define MCF_ROOM BITFLAG(2)
#define MCF_AQUEDUCT BITFLAG(3)
#define MCF_HALL_BEND BITFLAG(4)
#define MCF_BRIDGE BITFLAG(5)

#define TRANSLATE_MODMAP_COORD(X, Y, WIDTH) (((Y * WIDTH) + (X))+1)

var/global/list/_mcf_flags = list(
(MCF_HALL),
(MCF_ROOM),
(MCF_AQUEDUCT),
(MCF_HALL_BEND),
(MCF_BRIDGE)
)
150 changes: 150 additions & 0 deletions code/modules/modular_map/_map_generator.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// This decl is used for general structure for generating mazes/dungeons from a set of premapped templates.
// The actual meat of the generator logic is in map_run.dm.
// Example implementation in maps/modular_maps/debug/_debug.dm or maps/modular_maps/aqueduct/_aqueduct.dm

/decl/modular_map_generator
abstract_type = /decl/modular_map_generator
/// Human-readable identifier.
var/name
/// A level data type to pass to the new-z proc if needed.
var/level_data_type = /datum/level_data/empty
/// Datum type to use for run behavior/data tracking.
var/map_run_type = /datum/mm_run
/// Turfs per cell - functionally the grid size of the modular map.
var/grid_cell_size
/// Flag to avoid regenerating the template list unnecessarily.
var/templates_are_setup = FALSE
/// A list of template types that are available to this map generator.
var/list/cell_templates
/// A cache of template instances by their connection category for use in template selection.
var/list/templates_by_category = list()
/// A maximum number of templates forming a given path. <= 0 indicates no max.
var/max_generation = 0 //8
/// A minimum number of templates forming a given path. <= 0 indicates no max.
var/min_generation = 0 //5
/// Bool for placing map markers on each placed cell.
var/place_debug_markers = TRUE
/// List of random map datum types to run on the level after generating and applying.
var/list/post_run_generators
/// Horizontal gutter for placement on the actual map.
var/border_x = 8
/// Vertical gutter for placement on the actual map.
var/border_y = 8
// Whether or not the final grid can have trailing connections.
var/do_trim_trailing_connections = TRUE
// Number of cells with trailing connections after which to apply the trim multiplier.
var/trailing_connection_trim_threshold = 1 // 50
// Amount of cells with trailing connections to prune each run.
var/trailing_connection_trim_multiplier = 1 // 0.65
/// Maps without at least one path of this length or more will fail to validate.
var/min_path_length = 0 //50
/// Assoc list of template type to cell coordinate to place instead of a random initial template
var/list/mandatory_templates
/// How many isolated paths can the final map have?
var/maximum_paths = 1

/decl/modular_map_generator/proc/validate_template(template_type, datum/map_template/modular/template)
if(!istype(template))
PRINT_STACK_TRACE("Map generator [type] has template type [template_type] with no instance on SSmapping.")
return FALSE
. = TRUE
if(!template.connection_flag)
PRINT_STACK_TRACE("Map generator [type] has template [template.type] with unset connection_flag.")
. = FALSE
if(!length(template.cell_connections))
PRINT_STACK_TRACE("Map generator [type] has template [template.type] with empty cell_connections.")
. = FALSE

// Separate to Initialize() due to SSmapping dependency.
/decl/modular_map_generator/proc/setup_templates()
if(templates_are_setup || !SSmapping.initialized)
return

// Build our full template list
var/list/template_instances = list()
for(var/template_type in cell_templates)
var/datum/map_template/modular/template = SSmapping.map_templates_by_type[template_type]
if(validate_template(template_type, template))
template_instances |= template
LAZYDISTINCTADD(templates_by_category[num2text(template.connection_flag)], template)
cell_templates = template_instances

// Build our mandatory template list.
for(var/template_type in mandatory_templates)
if(!ispath(template_type))
continue
var/datum/map_template/modular/template = SSmapping.map_templates_by_type[template_type]
if(validate_template(template_type,template))
mandatory_templates[template] = mandatory_templates[template_type]
mandatory_templates -= template_type

templates_are_setup = TRUE

/decl/modular_map_generator/validate()
. = ..()
setup_templates()

if(!name)
. += "no name set"
if(!length(cell_templates))
. += "no templates to place"
if(!templates_by_category[num2text(MCF_ROOM)])
. += "no rooms to place"
if(!templates_by_category[num2text(MCF_HALL)])
. += "no hallways to place"
if(!isnum(grid_cell_size) || grid_cell_size < 0)
. += "invalid grid cell size ([grid_cell_size || "NULL"])"
if(!level_data_type)
. += "no level data type set"

if(length(mandatory_templates))
for(var/index in mandatory_templates)
if(!istype(index, /datum/map_template/modular))
. += "non-instance or non-typed key in mandatory template list: [index]"
var/list/coord = mandatory_templates[index]
if(!islist(coord) || length(coord) < 2 || !isnum(coord[1]) || !isnum(coord[2]))
. += "invalid or malformed coordinates for [index] in mandatory template list"

var/list/initial_templates = get_initial_templates()
if(!length(initial_templates))
. += "no initial templates returned to get_initial_templates()"

/decl/modular_map_generator/proc/get_initial_template()
return pick(templates_by_category[num2text(MCF_ROOM)])

/decl/modular_map_generator/proc/get_initial_templates(datum/mm_run/run)
if(islist(mandatory_templates))
return mandatory_templates
var/mx = run?.g_mx || 0
var/my = run?.g_my || 0
// Place a random room template at a random central-ish coordinate.
var/initial_template = get_initial_template()
var/x = round(rand(mx * 0.3, mx * 0.6))
var/y = round(rand(my * 0.3, my * 0.6))
log_debug("Placing random initial template [initial_template] at [x],[y] (map template: [istype(initial_template, /datum/map_template)], modular template: [istype(initial_template, /datum/map_template/modular)])")
return list((initial_template) = list(x, y))

/decl/modular_map_generator/proc/generate(target_z)

set waitfor = FALSE

if(!SSmapping.initialized)
to_chat(usr, SPAN_WARNING("Please wait until SSmapping initialization so template setup can complete."))
return TRUE

setup_templates()

var/datum/mm_run/run = new map_run_type(
src,
floor((world.maxx - (border_x*2)) / grid_cell_size),
floor((world.maxy - (border_y*2)) / grid_cell_size),
target_z || world.maxz+1
)
if(!run.generate_initial_map())
return FALSE
if(!run.perform_map_cleanup())
return FALSE
if(!run.finalize_run())
return FALSE
return TRUE

50 changes: 50 additions & 0 deletions code/modules/modular_map/map_cell.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// A single square on the dungeon map grid. Some map templates cover multiple cells.
/datum/mm_cell
var/cell_x
var/cell_y
var/offset_x
var/offset_y
var/datum/mm_path/path
var/datum/mm_cell/owner
var/datum/map_template/modular/template
var/generation
var/marked_for_cleanup
var/marked_for_refresh
var/list/finalized_connections
var/list/all_connections
VAR_PRIVATE/list/_open_connections

/datum/mm_cell/Destroy()
LAZYCLEARLIST(_open_connections)
template = null
path = null
return ..()

/datum/mm_cell/New(datum/mm_run/_run, _x, _y, _template, datum/mm_cell/_owner, _generation, _ox, _oy)
cell_x = _x
cell_y = _y
offset_x = _ox
offset_y = _oy
template = _template
generation = _generation
owner = _owner
// Initially assume all non-internal connections are freed; cell placement logic will close connections that are blocked by our placement.
for(var/datum/mm_connection/connection in template.cell_connections)
if(connection.offset_x == _ox && connection.offset_y == _oy)
LAZYDISTINCTADD(all_connections, connection)
open_connection(connection)

/datum/mm_cell/proc/get_open_connections(exclude_flags)
if(isnull(exclude_flags))
return _open_connections
for(var/datum/mm_connection/connection as anything in _open_connections)
if(connection.connection_flags & exclude_flags)
continue
LAZYADD(., connection)

/datum/mm_cell/proc/close_connection(connection)
LAZYREMOVE(_open_connections, connection)

/datum/mm_cell/proc/open_connection(connection)
if(connection in all_connections)
LAZYDISTINCTADD(_open_connections, connection)
64 changes: 64 additions & 0 deletions code/modules/modular_map/map_connection.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Connection data for use by the modular map generator and templates
/datum/mm_connection
/// Outgoing/facing direction of this connection.
var/direction_string
// Incoming/reverse facing direction of this connection.
var/reverse_direction_string
/// x-offset within the overall map structure (0 is hard left)
var/offset_x
/// y-offset within the overall map structure (0 is hard bottom).
var/offset_y
/// x-offset for the cell this connection would connect to.
var/target_x = 0
/// y-offset for the cell this connection would connect to.
var/target_y = 0
/// A list of valid connection constants for sections joining with this connection.
var/connection_flags
/// Reference to our owning template to simplify connection compatibility checking.
var/datum/map_template/modular/template

/datum/mm_connection/proc/can_connect_to(datum/mm_connection/other)

// Connections are not aligned; no dice.
if(direction_string != other.reverse_direction_string)
return FALSE

// Null connections just need to be facing another null connection, they don't care about template compat.
var/self_dummy = !!(connection_flags & MCF_BLOCKER)
var/other_dummy = !!(other.connection_flags & MCF_BLOCKER)
if(self_dummy || other_dummy)
return self_dummy == other_dummy

// Connection type is not permitted from this template; still no dice.
if(!(other.connection_flags & template.connection_flag))
return FALSE

// Connection type is not permitted to that template; still no dice.
if(!(connection_flags & other.template.connection_flag))
return FALSE

return TRUE

/datum/mm_connection/New(_dir, _x, _y, _connect)

offset_x = _x
offset_y = _y

direction_string = _dir
switch(direction_string)
if("NORTH")
reverse_direction_string = "SOUTH"
target_y = 1
if("SOUTH")
reverse_direction_string = "NORTH"
target_y = -1
if("EAST")
reverse_direction_string = "WEST"
target_x = 1
if("WEST")
reverse_direction_string = "EAST"
target_x = -1

connection_flags = _connect

/datum/mm_connection/closed // just making a stub type for ease of debugging
Loading
Loading