Introducing noitatsxoB (#12073)

* okay

* ok

* ok

* ok

* ok

* why am i doing this

* WHY DID I DO THIS

* mirror doesn't work

* compile

* shuttles should work

* fix engine

* fix engine

* off by 1

* ok

* adds proccall to write next map

* haha post processing funny

* fixes

* Update code/modules/mapping/map_template.dm

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>

* Update code/modules/mapping/map_template.dm

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>

* Update code/modules/mapping/map_template.dm

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>

* Update code/modules/mapping/map_template.dm

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
kevinz000
2020-05-06 08:50:50 -07:00
committed by GitHub
parent 3397c2284a
commit 5c5fc37959
15 changed files with 398 additions and 193 deletions

View File

@@ -0,0 +1,3 @@
//map template annihilate_bounds
#define MAP_TEMPLATE_ANNIHILATE_PRELOAD 1 //annihilate bounds before starting loading
#define MAP_TEMPLATE_ANNIHILATE_LOADING 2 //"sweeping" delete during loading

View File

@@ -196,7 +196,7 @@ SUBSYSTEM_DEF(mapping)
z_list = SSmapping.z_list
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE)
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE, orientation = SOUTH)
. = list()
var/start_time = REALTIMEOFDAY
@@ -236,7 +236,7 @@ SUBSYSTEM_DEF(mapping)
// load the maps
for (var/P in parsed_maps)
var/datum/parsed_map/pm = P
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE, orientation = orientation))
errorList |= pm.original_path
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
@@ -252,7 +252,7 @@ SUBSYSTEM_DEF(mapping)
// load the station
station_start = world.maxz + 1
INIT_ANNOUNCE("Loading [config.map_name]...")
LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION)
LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION, FALSE, config.orientation)
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")

View File

@@ -23,14 +23,14 @@
mappath = "[prefix][shuttle_id].dmm"
. = ..()
/datum/map_template/shuttle/preload_size(path, cache)
/datum/map_template/shuttle/preload_size(path = mappath, force_cache = FALSE)
. = ..(path, TRUE) // Done this way because we still want to know if someone actualy wanted to cache the map
if(!cached_map)
return
discover_port_offset()
if(!cache)
if(!cached_map)
cached_map = null
/datum/map_template/shuttle/proc/discover_port_offset()
@@ -53,12 +53,11 @@
++xcrd
--ycrd
/datum/map_template/shuttle/load(turf/T, centered, register=TRUE)
/datum/map_template/shuttle/load(turf/T, centered = FALSE, orientation = SOUTH, annihilate = default_annihilate, force_cache = FALSE, rotate_placement_to_orientation = FALSE, register = TRUE)
. = ..()
if(!.)
return
var/list/turfs = block( locate(.[MAP_MINX], .[MAP_MINY], .[MAP_MINZ]),
locate(.[MAP_MAXX], .[MAP_MAXY], .[MAP_MAXZ]))
var/list/turfs = get_last_loaded_turf_block()
for(var/i in 1 to turfs.len)
var/turf/place = turfs[i]
if(istype(place, /turf/open/space)) // This assumes all shuttles are loaded in a single spot then moved to their real destination.

View File

@@ -492,7 +492,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
if(!template)
return FALSE
testing("Room \"[template_name]\" placed at ([T.x], [T.y], [T.z])")
template.load(T, centered = FALSE)
template.load(T, centered = FALSE, orientation = dir, rotate_placement_to_orientation = TRUE)
template.loaded++
GLOB.stationroom_landmarks -= src
qdel(src)
@@ -504,7 +504,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
templates = list("Engine SM" = 3, "Engine Singulo" = 3, "Engine Tesla" = 3)
icon = 'icons/rooms/box/engine.dmi'
/obj/effect/landmark/stationroom/box/engine/New()
. = ..()
templates = CONFIG_GET(keyed_list/box_random_engine)

View File

@@ -18,9 +18,12 @@
var/image/item = image('icons/turf/overlays.dmi',S,"greenOverlay")
item.plane = ABOVE_LIGHTING_PLANE
preview += item
var/list/orientations = list("South" = SOUTH, "North" = NORTH, "East" = EAST, "West" = WEST)
var/choice = input(src, "Which orientation? Maps are normally facing SOUTH.", "Template Orientation", "South") as null|anything in orientations
var/orientation = orientations[choice]
images += preview
if(alert(src,"Confirm location.","Template Confirm","Yes","No") == "Yes")
if(template.load(T, centered = TRUE))
if(template.load(T, centered = TRUE, orientation = orientation))
message_admins("<span class='adminnotice'>[key_name_admin(src)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]</span>")
else
to_chat(src, "Failed to place map")

View File

@@ -1,63 +0,0 @@
dmm_suite{
/*
dmm_suite version 1.0
Released January 30th, 2011.
NOTE: Map saving functionality removed
defines the object /dmm_suite
- Provides the proc load_map()
- Loads the specified map file onto the specified z-level.
- provides the proc write_map()
- Returns a text string of the map in dmm format
ready for output to a file.
- provides the proc save_map()
- Returns a .dmm file if map is saved
- Returns FALSE if map fails to save
The dmm_suite provides saving and loading of map files in BYOND's native DMM map
format. It approximates the map saving and loading processes of the Dream Maker
and Dream Seeker programs so as to allow editing, saving, and loading of maps at
runtime.
------------------------
To save a map at runtime, create an instance of /dmm_suite, and then call
write_map(), which accepts three arguments:
- A turf representing one corner of a three dimensional grid (Required).
- Another turf representing the other corner of the same grid (Required).
- Any, or a combination, of several bit flags (Optional, see documentation).
The order in which the turfs are supplied does not matter, the /dmm_writer will
determine the grid containing both, in much the same way as DM's block() function.
write_map() will then return a string representing the saved map in dmm format;
this string can then be saved to a file, or used for any other purose.
------------------------
To load a map at runtime, create an instance of /dmm_suite, and then call load_map(),
which accepts two arguments:
- A .dmm file to load (Required).
- A number representing the z-level on which to start loading the map (Optional).
The /dmm_suite will load the map file starting on the specified z-level. If no
z-level was specified, world.maxz will be increased so as to fit the map. Note
that if you wish to load a map onto a z-level that already has objects on it,
you will have to handle the removal of those objects. Otherwise the new map will
simply load the new objects on top of the old ones.
Also note that all type paths specified in the .dmm file must exist in the world's
code, and that the /dmm_reader trusts that files to be loaded are in fact valid
.dmm files. Errors in the .dmm format will cause runtime errors.
*/
verb/load_map(var/dmm_file as file, var/x_offset as num, var/y_offset as num, var/z_offset as num, var/cropMap as num, var/measureOnly as num, no_changeturf as num){
// dmm_file: A .dmm file to load (Required).
// z_offset: A number representing the z-level on which to start loading the map (Optional).
// cropMap: When true, the map will be cropped to fit the existing world dimensions (Optional).
// measureOnly: When true, no changes will be made to the world (Optional).
// no_changeturf: When true, turf/AfterChange won't be called on loaded turfs
}
}

View File

@@ -38,6 +38,10 @@
var/year_offset = 540 //The offset of ingame year from the actual IRL year. You know you want to make a map that takes place in the 90's. Don't lie.
// "fun things"
/// Orientation to load in by default.
var/orientation = SOUTH //byond defaults to placing everyting SOUTH.
/proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
var/datum/map_config/config = new
if (default_to_box)
@@ -139,13 +143,18 @@
if ("minetype" in json)
minetype = json["minetype"]
if ("maptype" in json)
maptype = json["maptype"]
if ("announcertype" in json)
announcertype = json["announcertype"]
if ("orientation" in json)
orientation = json["orientation"]
if(!(orientation in GLOB.cardinals))
orientation = SOUTH
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
defaulted = FALSE
@@ -161,3 +170,23 @@
/datum/map_config/proc/MakeNextMap()
return config_filename == "data/next_map.json" || fcopy(config_filename, "data/next_map.json")
/// badmin moments. Keep up to date with LoadConfig()!
/datum/map_config/proc/WriteNextMap()
var/list/jsonlist = list()
jsonlist["map_name"] = map_name
jsonlist["map_path"] = map_path
jsonlist["map_file"] = map_file
jsonlist["shuttles"] = shuttles
jsonlist["traits"] = traits
jsonlist["space_ruin_levels"] = space_ruin_levels
jsonlist["year_offset"] = year_offset
jsonlist["minetype"] = minetype
jsonlist["maptype"] = maptype
jsonlist["announcertype"] = announcertype
jsonlist["orientation"] = orientation
jsonlist["allow_custom_shuttles"] = allow_custom_shuttles
if(fexists("data/next_map.json"))
fdel("data/next_map.json")
var/F = file("data/next_map.json")
WRITE_FILE(F, json_encode(jsonlist))

View File

@@ -0,0 +1,46 @@
GLOBAL_LIST_INIT(map_orientation_patterns, list(
TEXT_NORTH = new /datum/map_orientation_pattern/north,
TEXT_SOUTH = new /datum/map_orientation_pattern/south,
TEXT_EAST = new /datum/map_orientation_pattern/east,
TEXT_WEST = new /datum/map_orientation_pattern/west
))
/datum/map_orientation_pattern
var/invert_x
var/invert_y
var/swap_xy
var/xi
var/yi
var/turn_angle
/datum/map_orientation_pattern/north
invert_y = TRUE
invert_x = TRUE
swap_xy = FALSE
xi = -1
yi = 1
turn_angle = 180
/datum/map_orientation_pattern/south
invert_y = FALSE
invert_x = FALSE
swap_xy = FALSE
xi = 1
yi = -1
turn_angle = 0
/datum/map_orientation_pattern/east
invert_y = TRUE
invert_x = FALSE
swap_xy = TRUE
xi = 1
yi = 1
turn_angle = 90
/datum/map_orientation_pattern/west
invert_y = FALSE
invert_x = TRUE
swap_xy = TRUE
xi = -1
yi = -1
turn_angle = 270

View File

@@ -1,11 +1,14 @@
/datum/map_template
var/name = "Default Template Name"
var/width = 0
var/width = 0 //all these are for SOUTH!
var/height = 0
var/mappath = null
var/zdepth = 1
var/mappath
var/loaded = 0 // Times loaded this round
var/datum/parsed_map/cached_map
var/keep_cached_map = FALSE
var/default_annihilate = FALSE
var/list/ztraits //zlevel traits for load_new_z
/datum/map_template/New(path = null, rename = null, cache = FALSE)
if(path)
@@ -15,16 +18,45 @@
if(rename)
name = rename
/datum/map_template/proc/preload_size(path, cache = FALSE)
/datum/map_template/Destroy()
QDEL_NULL(cached_map)
return ..()
/datum/map_template/proc/preload_size(path = mappath, force_cache = FALSE)
if(cached_map)
return cached_map.parsed_bounds
var/datum/parsed_map/parsed = new(file(path))
var/bounds = parsed?.bounds
var/bounds = parsed?.parsed_bounds
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
if(cache)
width = bounds[MAP_MAXX] - bounds[MAP_MINX] + 1
height = bounds[MAP_MAXY] - bounds[MAP_MINY] + 1
zdepth = bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1
if(force_cache || keep_cached_map)
cached_map = parsed
return bounds
/datum/map_template/proc/get_parsed_bounds()
return preload_size(mappath)
/datum/map_template/proc/get_last_loaded_bounds()
if(cached_map)
return cached_map.bounds
return get_parsed_bounds()
/datum/map_template/proc/get_last_loaded_turf_block()
if(!cached_map)
CRASH("Improper use of get_last_loaded_turf_block, no cached_map.")
var/list/B = cached_map.bounds
return block(locate(B[MAP_MINX], B[MAP_MINY], B[MAP_MINZ]), locate(B[MAP_MAXX], B[MAP_MAXY], B[MAP_MAXZ]))
/datum/map_template/proc/get_size(orientation = SOUTH)
if(!width || !height || !zdepth)
preload_size(mappath)
var/rotate = (orientation & (NORTH|SOUTH)) != NONE
if(rotate)
return list(height, width, zdepth)
return list(width, height, zdepth)
/datum/parsed_map/proc/initTemplateBounds()
var/list/obj/machinery/atmospherics/atmos_machines = list()
var/list/obj/structure/cable/cables = list()
@@ -55,12 +87,12 @@
SSmachines.setup_template_powernets(cables)
SSair.setup_template_machinery(atmos_machines)
/datum/map_template/proc/load_new_z(list/traits = list(ZTRAIT_AWAY = TRUE))
var/x = round((world.maxx - width)/2)
var/y = round((world.maxy - height)/2)
/datum/map_template/proc/load_new_z(orientation = SOUTH, list/ztraits = src.ztraits || list(ZTRAIT_AWAY = TRUE), centered = TRUE)
var/x = centered? max(round((world.maxx - width) / 2), 1) : 1
var/y = centered? max(round((world.maxy - height) / 2), 1) : 1
var/datum/space_level/level = SSmapping.add_new_zlevel(name, traits)
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, ztraits)
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop = TRUE, orientation = orientation)
var/list/bounds = parsed.bounds
if(!bounds)
return FALSE
@@ -71,31 +103,67 @@
parsed.initTemplateBounds()
smooth_zlevel(world.maxz)
log_game("Z-level [name] loaded at [x],[y],[world.maxz]")
on_map_loaded(world.maxz, parsed.bounds)
return level
/datum/map_template/proc/load(turf/T, centered = FALSE)
//Override for custom behavior
/datum/map_template/proc/on_map_loaded(z, list/bounds)
loaded++
/**
* Proc to trigger a load at a specific area. Calls on_map_loaded(T.z, loaded_bounds) afterwards.
*
* @params
* * turf/T - Turf to load at
* * centered - Center at T or load with the bottomright corner being at T
* * orientation - SOUTH is default, anything else rotates the map to face it with the point of reference being the map itself is facing south by default. Cardinals only, don't be a 4head and put in multiple flags. It won't work or be pretty if you try.
* * annihilate - Should we destroy stuff in our bounds while loading
* * force_cache - Should we force the parsed shuttle to cache instead of being GC'd post loading if it wasn't going to be cached by default
* * rotate_placement_to_orientation - Has no effect if centered. Should we rotate where we load it around the turf we're loading at? Used for stuff like engine submaps when the station is rotated.
*
*/
/datum/map_template/proc/load(turf/T, centered = FALSE, orientation = SOUTH, annihilate = default_annihilate, force_cache = FALSE, rotate_placement_to_orientation = FALSE)
var/old_T = T
if(centered)
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
T = locate(T.x - round(((orientation & (NORTH|SOUTH))? width : height) / 2) , T.y - round(((orientation & (NORTH|SOUTH)) ? height : width) / 2) , T.z) // %180 catches East/West (90,270) rotations on true, North/South (0,180) rotations on false
else if(rotate_placement_to_orientation && (orientation != SOUTH))
var/newx = T.x
var/newy = T.y
if(orientation == NORTH)
newx -= width
newy -= height - 1
else if(orientation == WEST)
newy -= width
else if(orientation == EAST)
newx -= height - 1
// eh let's not silently fail.
if(!ISINRANGE(newx, 1, world.maxx) || !ISINRANGE(newy, 1, world.maxy))
stack_trace("Warning: Rotation placed a map template load spot ([COORD(T)]) out of bounds of the game world. Clamping to world borders, this might cause issues.")
T = locate(clamp(newx, 1, world.maxx), clamp(newy, 1, world.maxy), T.z)
if(!T)
return
if(T.x+width > world.maxx)
if(T.x+width-1 > world.maxx)
return
if(T.y+height > world.maxy)
if(T.y+height-1 > world.maxy)
return
var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z),
locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z))
for(var/L in border)
var/turf/turf_to_disable = L
var/list/border = block(locate(max(T.x - 1, 1), max(T.y - 1, 1), T.z),
locate(min(T.x + width + 1, world.maxx), min(T.y + height + 1, world.maxy), T.z))
for(var/i in border)
var/turf/turf_to_disable = i
SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds()
turf_to_disable.atmos_adjacent_turfs?.Cut()
if(annihilate == MAP_TEMPLATE_ANNIHILATE_PRELOAD)
annihilate_bounds(old_T, centered, orientation)
// Accept cached maps, but don't save them automatically - we don't want
// ruins clogging up memory for the whole round.
var/datum/parsed_map/parsed = cached_map || new(file(mappath))
cached_map = keep_cached_map ? parsed : null
if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE))
var/is_cached = cached_map
var/datum/parsed_map/parsed = is_cached || new(file(mappath))
cached_map = (force_cache || keep_cached_map) ? parsed : is_cached
if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE, orientation = orientation, annihilate_tiles = (annihilate == MAP_TEMPLATE_ANNIHILATE_LOADING)))
return
var/list/bounds = parsed.bounds
if(!bounds)
@@ -108,19 +176,36 @@
parsed.initTemplateBounds()
log_game("[name] loaded at [T.x],[T.y],[T.z]")
on_map_loaded(T.z, parsed.bounds)
return bounds
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
var/turf/placement = T
if(centered)
var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
if(corner)
placement = corner
return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))
//This, get_affected_turfs, and load() calculations for bounds/center can probably be optimized. Later.
/datum/map_template/proc/annihilate_bounds(turf/origin, centered = FALSE, orientation = SOUTH)
var/deleted_atoms = 0
log_world("Annihilating objects in map loading location.")
var/list/turfs_to_clean = get_affected_turfs(origin, centered, orientation)
if(turfs_to_clean.len)
var/list/kill_these = list()
for(var/i in turfs_to_clean)
var/turf/T = i
kill_these += T.contents
for(var/i in kill_these)
qdel(i)
CHECK_TICK
deleted_atoms++
log_world("Annihilated [deleted_atoms] objects.")
//for your ever biggening badminnery kevinz000
//❤ - Cyberboss
/proc/load_new_z_level(file, name, list/traits)
/proc/load_new_z_level(file, name, orientation, list/ztraits)
var/datum/map_template/template = new(file, name)
return template.load_new_z(traits)
return template.load_new_z(orientation, ztraits)
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE, orientation = SOUTH)
var/turf/placement = T
if(centered)
var/turf/corner = locate(placement.x - round(((orientation & (NORTH|SOUTH))? width : height) / 2), placement.y - round(((orientation & (NORTH|SOUTH))? height : width) / 2), placement.z) // %180 catches East/West (90,270) rotations on true, North/South (0,180) rotations on false
if(corner)
placement = corner
return block(placement, locate(placement.x + ((orientation & (NORTH|SOUTH)) ? width : height) - 1, placement.y + ((orientation & (NORTH|SOUTH))? height : width) - 1, placement.z))

View File

@@ -7,13 +7,21 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
parent_type = /datum
var/list/attributes
var/target_path
var/turn_angle
var/swap_x
var/swap_y
var/swap_xy
/world/proc/preloader_setup(list/the_attributes, path)
if(the_attributes.len)
/world/proc/preloader_setup(list/the_attributes, path, turn_angle, swap_x, swap_y, swap_xy)
if(length(the_attributes) || turn_angle)
GLOB.use_preloader = TRUE
var/datum/map_preloader/preloader_local = GLOB._preloader
preloader_local.attributes = the_attributes
preloader_local.target_path = path
preloader_local.turn_angle = turn_angle
preloader_local.swap_x = swap_x
preloader_local.swap_y = swap_y
preloader_local.swap_xy = swap_xy
/world/proc/preloader_load(atom/what)
GLOB.use_preloader = FALSE
@@ -29,6 +37,21 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
GLOB.dirty_vars += message
#endif
what.vars[attribute] = value
// handle post processing, so things like directions on subtypes don't break.
if(preloader_local.turn_angle) //safe way to check for if this is necessary
what.dir = turn(what.dir, preloader_local.turn_angle)
var/px = what.pixel_x
var/py = what.pixel_y
if(preloader_local.swap_y) //same order of operations as the load rotation, mirror and then x/y swapping.
py = -py
if(preloader_local.swap_x)
px = -px
if(preloader_local.swap_xy)
var/opx = px
px = py
py = opx
what.pixel_x = px
what.pixel_y = py
/area/template_noop
name = "Area Passthrough"

View File

@@ -19,9 +19,13 @@
/// Unoffset bounds. Null on parse failure.
var/list/parsed_bounds
var/width
var/height
/// Offset bounds. Same as parsed_bounds until load().
var/list/bounds
var/datum/map_template/template_host
// raw strings used to represent regexes more accurately
// '' used to avoid confusing syntax highlighting
var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}', "g")
@@ -41,14 +45,33 @@
/// - `no_changeturf`: When true, [turf/AfterChange] won't be called on loaded turfs
/// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the map) to crop to (Optional).
/// - `placeOnTop`: Whether to use [turf/PlaceOnTop] rather than [turf/ChangeTurf] (Optional).
/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num)
var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, measureOnly)
/proc/load_map(
dmm_file as file,
x_offset as num,
y_offset as num,
z_offset as num,
cropMap as num,
measureOnly as num,
no_changeturf as num,
x_lower = -INFINITY as num,
x_upper = INFINITY as num,
y_lower = -INFINITY as num,
y_upper = INFINITY as num,
placeOnTop = FALSE as num,
orientation = SOUTH as num,
annihilate_tiles = FALSE,
z_lower = -INFINITY as num,
z_upper = INFINITY as num
)
var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
if(parsed.bounds && !measureOnly)
parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, orientation, annihilate_tiles)
return parsed
/// Parse a map, possibly cropping it.
/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper=INFINITY, measureOnly=FALSE)
//WHY THE HECK DO WE EVEN SUPPORT NEGATIVE COORDINATES, ALL IT IS IS A WASTE OF TIME AND CPU!!!???
//DO NOT USE THIS TO TRIM MAPS UNLESS STRICTLY NEEDED! IT IS EXTREMELY EXPENSIVE TO DO SO!
/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, z_lower = -INFINITY, z_upper = INFINITY, measureOnly = FALSE)
if(isfile(tfile))
original_path = "[tfile]"
tfile = file2text(tfile)
@@ -57,6 +80,9 @@
return
bounds = parsed_bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
ASSERT(x_upper >= x_lower)
ASSERT(y_upper >= y_lower)
ASSERT(z_upper >= z_lower)
var/stored_index = 1
//multiz lool
@@ -82,20 +108,23 @@
CRASH("Coords before model definition in DMM")
var/curr_x = text2num(dmmRegex.group[3])
var/curr_y = text2num(dmmRegex.group[4])
var/curr_z = text2num(dmmRegex.group[5])
if(curr_x < x_lower || curr_x > x_upper)
if(curr_x < x_lower || curr_y < y_lower || curr_z < z_lower || curr_z > z_upper)
continue
var/datum/grid_set/gridSet = new
gridSet.xcrd = curr_x
//position of the currently processed square
gridSet.ycrd = text2num(dmmRegex.group[4])
gridSet.zcrd = text2num(dmmRegex.group[5])
gridSet.ycrd = curr_y
gridSet.zcrd = curr_z
bounds[MAP_MINX] = min(bounds[MAP_MINX], clamp(gridSet.xcrd, x_lower, x_upper))
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], gridSet.zcrd)
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], gridSet.zcrd)
bounds[MAP_MINX] = min(bounds[MAP_MINX], curr_x) //since down is up for y/gridlines, we now know the lower left corner.
bounds[MAP_MINY] = min(bounds[MAP_MINY], curr_y)
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], curr_z)
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], curr_z) //we know max z now
var/list/gridLines = splittext(dmmRegex.group[6], "\n")
gridSet.gridLines = gridLines
@@ -105,102 +134,132 @@
if(leadingBlanks > 1)
gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines.
if(!gridLines.len) // Skip it if only blank lines exist.
continue
gridSets += gridSet
if(gridLines.len && gridLines[gridLines.len] == "")
gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
var/lines = length(gridLines)
if(lines)
if(gridLines[gridLines.len] == "")
gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
var/right_length = y_upper - curr_y + 1
if(lines > right_length)
gridLines.len = right_length //this can't be negative due to our ASSERTions above, hopefully.
bounds[MAP_MINY] = min(bounds[MAP_MINY], clamp(gridSet.ycrd, y_lower, y_upper))
if(!gridLines.len) // Skip it if there's no content.
continue
//do not use curr_y after this point, ycrd has changed. use it before because local var.
gridSet.ycrd += gridLines.len - 1 // Start at the top and work down
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], clamp(gridSet.ycrd, y_lower, y_upper))
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], gridSet.ycrd) //we know max y now
var/maxx = gridSet.xcrd
if(gridLines.len) //Not an empty map
maxx = max(maxx, gridSet.xcrd + length(gridLines[1]) / key_len - 1)
var/linelength = length(gridLines[1]) //yes it only samples the first line, this is why you use TGM instead of DMM!
var/xlength = linelength / key_len
bounds[MAP_MAXX] = clamp(max(bounds[MAP_MAXX], maxx), x_lower, x_upper)
var/maxx = gridSet.xcrd + xlength - 1
if(maxx > x_upper)
for(var/i in 1 to length(gridLines))
gridLines[i] = copytext(gridLines[i], 1, key_len * (x_upper - curr_x + 1))
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], maxx)
CHECK_TICK
// Indicate failure to parse any coordinates by nulling bounds
if(bounds[1] == 1.#INF)
bounds = null
else
width = bounds[MAP_MAXX] - bounds[MAP_MINX] + 1
height = bounds[MAP_MAXY] - bounds[MAP_MINY] + 1
parsed_bounds = bounds
/datum/parsed_map/Destroy()
if(template_host && template_host.cached_map == src)
template_host.cached_map = null
return ..()
/// Load the parsed map into the world. See [/proc/load_map] for arguments.
/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, orientation, annihilate_tiles, datum/map_orientation_pattern/forced_pattern)
//How I wish for RAII
Master.StartLoadingMap()
. = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
. = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, orientation, annihilate_tiles, forced_pattern)
Master.StopLoadingMap()
// Do not call except via load() above.
/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE)
// Lower/upper here refers to the actual map template's parsed coordinates, NOT ACTUAL COORDINATES! Figure it out yourself my head hurts too much to implement that too.
/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE, orientation = SOUTH, annihilate_tiles = FALSE, datum/map_orientation_pattern/forced_pattern)
var/list/areaCache = list()
var/list/modelCache = build_cache(no_changeturf)
var/space_key = modelCache[SPACE_KEY]
var/list/bounds
src.bounds = bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
var/datum/map_orientation_pattern/mode = forced_pattern || GLOB.map_orientation_patterns["[orientation]"] || GLOB.map_orientation_patterns["[SOUTH]"]
var/invert_y = mode.invert_y
var/invert_x = mode.invert_x
var/swap_xy = mode.swap_xy
var/xi = mode.xi
var/yi = mode.yi
var/turn_angle = round(SIMPLIFY_DEGREES(mode.turn_angle), 90)
var/delta_swap = x_offset - y_offset
for(var/I in gridSets)
var/datum/grid_set/gset = I
var/ycrd = gset.ycrd + y_offset - 1
var/zcrd = gset.zcrd + z_offset - 1
if(!cropMap && ycrd > world.maxy)
world.maxy = ycrd // Expand Y here. X is expanded in the loop below
var/zexpansion = zcrd > world.maxz
for(var/__I in gridSets)
var/datum/grid_set/gridset = __I
var/parsed_z = gridset.zcrd + z_offset - 1
var/zexpansion = parsed_z > world.maxz
if(zexpansion)
if(cropMap)
continue
else
while (zcrd > world.maxz) //create a new z_level if needed
while(parsed_z > world.maxz)
world.incrementMaxZ()
if(!no_changeturf)
WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called")
//these values are the same until a new gridset is reached.
var/edge_dist_x = gridset.xcrd - 1 //from left side, 0 is right on the x_offset
var/edge_dist_y = gridset.ycrd - length(gridset.gridLines) //from bottom, 0 is right on the y_offset
var/actual_x_starting = invert_x? (x_offset + width - edge_dist_x - 1) : (x_offset + edge_dist_x) //this value is not changed, cache.
//this value is changed
var/actual_y = invert_y? (y_offset + edge_dist_y) : (y_offset + gridset.ycrd - 1)
for(var/line in gridset.gridLines)
var/actual_x = actual_x_starting
for(var/pos = 1 to (length(line) - key_len + 1) step key_len)
var/placement_x = swap_xy? (actual_y + delta_swap) : actual_x
var/placement_y = swap_xy? (actual_x - delta_swap) : actual_y
if(placement_x > world.maxx)
if(cropMap)
actual_x += xi
continue
else
world.maxx = placement_x
if(placement_y > world.maxy)
if(cropMap)
break
else
world.maxy = placement_y
if(placement_x < 1)
actual_x += xi
continue
if(placement_y < 1)
break
var/model_key = copytext(line, pos, pos + key_len)
var/no_afterchange = no_changeturf || zexpansion
if(!no_afterchange || (model_key != space_key))
var/list/cache = modelCache[model_key]
if(!cache)
CRASH("Undefined model key in DMM: [model_key]")
build_coordinate(areaCache, cache, locate(placement_x, placement_y, parsed_z), no_afterchange, placeOnTop, turn_angle, annihilate_tiles, swap_xy, invert_y, invert_x)
for(var/line in gset.gridLines)
if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping.
--ycrd
continue
if(ycrd <= world.maxy && ycrd >= 1)
var/xcrd = gset.xcrd + x_offset - 1
for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above.
++xcrd
continue //X cropping.
if(xcrd > world.maxx)
if(cropMap)
break
else
world.maxx = xcrd
if(xcrd >= 1)
var/model_key = copytext(line, tpos, tpos + key_len)
var/no_afterchange = no_changeturf || zexpansion
if(!no_afterchange || (model_key != space_key))
var/list/cache = modelCache[model_key]
if(!cache)
CRASH("Undefined model key in DMM: [model_key]")
build_coordinate(areaCache, cache, locate(xcrd, ycrd, zcrd), no_afterchange, placeOnTop)
// only bother with bounds that actually exist
bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrd)
bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], xcrd)
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
#ifdef TESTING
else
++turfsSkipped
#endif
CHECK_TICK
++xcrd
--ycrd
CHECK_TICK
// only bother with bounds that actually exist
bounds[MAP_MINX] = min(bounds[MAP_MINX], placement_x)
bounds[MAP_MINY] = min(bounds[MAP_MINY], placement_y)
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], parsed_z)
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], placement_x)
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], placement_y)
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], parsed_z)
#ifdef TESTING
else
++turfsSkipped
#endif
actual_x += xi
CHECK_TICK
actual_y += yi
CHECK_TICK
if(!no_changeturf)
for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
@@ -294,7 +353,7 @@
.[model_key] = list(members, members_attributes)
/datum/parsed_map/proc/build_coordinate(list/areaCache, list/model, turf/crds, no_changeturf as num, placeOnTop as num)
/datum/parsed_map/proc/build_coordinate(list/areaCache, list/model, turf/crds, no_changeturf as num, placeOnTop as num, turn_angle as num, annihilate_tiles = FALSE, swap_xy, invert_y, invert_x)
var/index
var/list/members = model[1]
var/list/members_attributes = model[2]
@@ -306,6 +365,8 @@
//The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
//first instance the /area and remove it from the members list
index = members.len
if(annihilate_tiles && crds)
crds.empty(null)
if(members[index] != /area/template_noop)
var/atype = members[index]
world.preloader_setup(members_attributes[index], atype)//preloader for assigning set variables on atom creation
@@ -332,20 +393,20 @@
//instanciate the first /turf
var/turf/T
if(members[first_turf_index] != /turf/template_noop)
T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf,placeOnTop)
T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf,placeOnTop,turn_angle, swap_xy, invert_y, invert_x)
if(T)
//if others /turf are presents, simulates the underlays piling effect
index = first_turf_index + 1
while(index <= members.len - 1) // Last item is an /area
var/underlay = T.appearance
T = instance_atom(members[index],members_attributes[index],crds,no_changeturf,placeOnTop)//instance new turf
T = instance_atom(members[index],members_attributes[index],crds,no_changeturf,placeOnTop,turn_angle, swap_xy, invert_y, invert_x)//instance new turf
T.underlays += underlay
index++
//finally instance all remainings objects/mobs
for(index in 1 to first_turf_index-1)
instance_atom(members[index],members_attributes[index],crds,no_changeturf,placeOnTop)
instance_atom(members[index],members_attributes[index],crds,no_changeturf,placeOnTop,turn_angle, swap_xy, invert_y, invert_x)
//Restore initialization to the previous value
SSatoms.map_loader_stop()
@@ -354,8 +415,8 @@
////////////////
//Instance an atom at (x,y,z) and gives it the variables in attributes
/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop)
world.preloader_setup(attributes, path)
/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop, turn_angle = 0, swap_xy, invert_y, invert_x)
world.preloader_setup(attributes, path, turn_angle, invert_x, invert_y, swap_xy)
if(crds)
if(ispath(path, /turf))

View File

@@ -196,12 +196,16 @@
switch(tdir)
if(NORTH)
pixel_x = 0
pixel_y = 23
if(SOUTH)
pixel_x = 0
pixel_y = -23
if(EAST)
pixel_y = 0
pixel_x = 24
if(WEST)
pixel_y = 0
pixel_x = -25
/obj/machinery/power/apc/Destroy()

View File

@@ -91,6 +91,21 @@ By design, d1 is the smallest direction and d2 is the highest
d1 = _d1
d2 = _d2
if(dir != SOUTH)
var/angle_to_turn = dir2angle(dir)
if(angle_to_turn == 0 || angle_to_turn == 180)
angle_to_turn += 180
// direct dir set instead of setDir intentional
dir = SOUTH
if(d1)
d1 = turn(d1, angle_to_turn)
if(d2)
d2 = turn(d2, angle_to_turn)
if(d1 > d2)
var/temp = d2
d2 = d1
d1 = temp
var/turf/T = get_turf(src) // hide if turf is not intact
if(level==1)
hide(T.intact)

View File

@@ -270,7 +270,7 @@
var/turf/landmark_turf = get_turf(locate(/obj/effect/landmark/shuttle_import) in GLOB.landmarks_list)
S.load(landmark_turf, centered = TRUE, register = FALSE)
var/affected = S.get_affected_turfs(landmark_turf, centered=TRUE)
var/affected = S.get_affected_turfs(landmark_turf, centered = TRUE)
var/found = 0
// Search the turfs for docking ports

View File

@@ -124,6 +124,7 @@
#include "code\__DEFINES\dcs\helpers.dm"
#include "code\__DEFINES\dcs\signals.dm"
#include "code\__DEFINES\flags\shields.dm"
#include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\misc\return_values.dm"
#include "code\__DEFINES\skills\skills.dm"
#include "code\__HELPERS\_cit_helpers.dm"
@@ -348,7 +349,6 @@
#include "code\datums\forced_movement.dm"
#include "code\datums\holocall.dm"
#include "code\datums\hud.dm"
#include "code\datums\map_config.dm"
#include "code\datums\mind.dm"
#include "code\datums\mutable_appearance.dm"
#include "code\datums\numbered_display.dm"
@@ -2148,7 +2148,8 @@
#include "code\modules\lighting\lighting_setup.dm"
#include "code\modules\lighting\lighting_source.dm"
#include "code\modules\lighting\lighting_turf.dm"
#include "code\modules\mapping\dmm_suite.dm"
#include "code\modules\mapping\map_config.dm"
#include "code\modules\mapping\map_orientation_pattern.dm"
#include "code\modules\mapping\map_template.dm"
#include "code\modules\mapping\minimaps.dm"
#include "code\modules\mapping\preloader.dm"