mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-02-05 05:58:41 +00:00
Update DMM Suite (#2711)
Updates the DMM Suite from /tg/ upstream, adding the ability to load tgm format maps, tick-checks, and better compatibility with SSatoms / Initialize(). Also adds some framework for random ruin generation, though it is not functional yet.
This commit is contained in:
@@ -1341,9 +1341,9 @@
|
||||
#include "code\modules\lighting\~lighting_undefs.dm"
|
||||
#include "code\modules\liquid\splash_simulation.dm"
|
||||
#include "code\modules\maps\dmm_suite.dm"
|
||||
#include "code\modules\maps\map_template.dm"
|
||||
#include "code\modules\maps\reader.dm"
|
||||
#include "code\modules\maps\swapmaps.dm"
|
||||
#include "code\modules\maps\writer.dm"
|
||||
#include "code\modules\materials\material_recipes.dm"
|
||||
#include "code\modules\materials\material_sheets.dm"
|
||||
#include "code\modules\materials\material_synth.dm"
|
||||
|
||||
@@ -335,3 +335,11 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
|
||||
|
||||
#define ADD_VERB_IN(the_atom,time,verb) addtimer(CALLBACK(the_atom, /atom/.proc/add_verb, verb), time, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT)
|
||||
#define ADD_VERB_IN_IF(the_atom,time,verb,callback) addtimer(CALLBACK(the_atom, /atom/.proc/add_verb, verb, callback), time, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT)
|
||||
|
||||
// Maploader bounds indices
|
||||
#define MAP_MINX 1
|
||||
#define MAP_MINY 2
|
||||
#define MAP_MINZ 3
|
||||
#define MAP_MAXX 4
|
||||
#define MAP_MAXY 5
|
||||
#define MAP_MAXZ 6
|
||||
|
||||
@@ -662,5 +662,12 @@ proc/dd_sortedTextList(list/incoming)
|
||||
|
||||
return L
|
||||
|
||||
#define LAZYINITLIST(L) if (!L) L = list()
|
||||
#define UNSETEMPTY(L) if (L && !L.len) L = null
|
||||
//Copies a list, and all lists inside it recusively
|
||||
//Does not copy any other reference type
|
||||
/proc/deepCopyList(list/l)
|
||||
if(!islist(l))
|
||||
return l
|
||||
. = l.Copy()
|
||||
for(var/i = 1 to l.len)
|
||||
if(islist(.[i]))
|
||||
.[i] = .(.[i])
|
||||
|
||||
@@ -464,3 +464,59 @@ proc/TextPreview(var/string,var/len=40)
|
||||
t = replacetext(t, "</font>", "\[/small\]")
|
||||
|
||||
return t
|
||||
|
||||
//Used for applying byonds text macros to strings that are loaded at runtime
|
||||
/proc/apply_text_macros(string)
|
||||
var/next_backslash = findtext(string, "\\")
|
||||
if(!next_backslash)
|
||||
return string
|
||||
|
||||
var/leng = length(string)
|
||||
|
||||
var/next_space = findtext(string, " ", next_backslash + 1)
|
||||
if(!next_space)
|
||||
next_space = leng - next_backslash
|
||||
|
||||
if(!next_space) //trailing bs
|
||||
return string
|
||||
|
||||
var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash)
|
||||
var/macro = lowertext(copytext(string, next_backslash + 1, next_space))
|
||||
var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1)
|
||||
|
||||
//See http://www.byond.com/docs/ref/info.html#/DM/text/macros
|
||||
switch(macro)
|
||||
//prefixes/agnostic
|
||||
if("the")
|
||||
rest = text("\the []", rest)
|
||||
if("a")
|
||||
rest = text("\a []", rest)
|
||||
if("an")
|
||||
rest = text("\an []", rest)
|
||||
if("proper")
|
||||
rest = text("\proper []", rest)
|
||||
if("improper")
|
||||
rest = text("\improper []", rest)
|
||||
if("roman")
|
||||
rest = text("\roman []", rest)
|
||||
//postfixes
|
||||
if("th")
|
||||
base = text("[]\th", rest)
|
||||
if("s")
|
||||
base = text("[]\s", rest)
|
||||
if("he")
|
||||
base = text("[]\he", rest)
|
||||
if("she")
|
||||
base = text("[]\she", rest)
|
||||
if("his")
|
||||
base = text("[]\his", rest)
|
||||
if("himself")
|
||||
base = text("[]\himself", rest)
|
||||
if("herself")
|
||||
base = text("[]\herself", rest)
|
||||
if("hers")
|
||||
base = text("[]\hers", rest)
|
||||
|
||||
. = base
|
||||
if(rest)
|
||||
. += .(rest)
|
||||
|
||||
@@ -17,3 +17,9 @@
|
||||
|
||||
// Called when SSexplosives finishes processing all queued explosions.
|
||||
/datum/controller/proc/ExplosionEnd()
|
||||
|
||||
//when we enter dmm_suite.load_map
|
||||
/datum/controller/proc/StartLoadingMap()
|
||||
|
||||
//when we exit dmm_suite.load_map
|
||||
/datum/controller/proc/StopLoadingMap()
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
*
|
||||
**/
|
||||
var/datum/controller/master/Master = new()
|
||||
var/MC_restart_clear = 0
|
||||
var/MC_restart_timeout = 0
|
||||
var/MC_restart_count = 0
|
||||
|
||||
|
||||
//current tick limit, assigned by the queue controller before running a subsystem.
|
||||
//used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
|
||||
@@ -53,6 +49,11 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
|
||||
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
|
||||
var/queue_priority_count_bg = 0 //Same, but for background subsystems
|
||||
var/map_loading = FALSE //Are we loading in a new map?
|
||||
|
||||
var/static/restart_clear = 0
|
||||
var/static/restart_timeout = 0
|
||||
var/static/restart_count = 0
|
||||
|
||||
/datum/controller/master/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
@@ -79,14 +80,14 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
/proc/Recreate_MC()
|
||||
. = -1 //so if we runtime, things know we failed
|
||||
if (world.time < MC_restart_timeout)
|
||||
if (world.time < Master.restart_timeout)
|
||||
return 0
|
||||
if (world.time < MC_restart_clear)
|
||||
MC_restart_count *= 0.5
|
||||
if (world.time < Master.restart_clear)
|
||||
Master.restart_count *= 0.5
|
||||
|
||||
var/delay = 50 * ++MC_restart_count
|
||||
MC_restart_timeout = world.time + delay
|
||||
MC_restart_clear = world.time + (delay * 2)
|
||||
var/delay = 50 * ++Master.restart_count
|
||||
Master.restart_timeout = world.time + delay
|
||||
Master.restart_clear = world.time + (delay * 2)
|
||||
Master.processing = 0 //stop ticking this one
|
||||
try
|
||||
new/datum/controller/master()
|
||||
@@ -553,3 +554,18 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
if (SSticker.current_state >= GAME_STATE_PLAYING)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/master/StartLoadingMap()
|
||||
//disallow more than one map to load at once, multithreading it will just cause race conditions
|
||||
while(map_loading)
|
||||
stoplag()
|
||||
for(var/S in subsystems)
|
||||
var/datum/controller/subsystem/SS = S
|
||||
SS.StartLoadingMap()
|
||||
map_loading = TRUE
|
||||
|
||||
/datum/controller/master/StopLoadingMap(bounds = null)
|
||||
map_loading = FALSE
|
||||
for(var/S in subsystems)
|
||||
var/datum/controller/subsystem/SS = S
|
||||
SS.StopLoadingMap()
|
||||
|
||||
@@ -144,6 +144,13 @@ var/datum/controller/subsystem/atoms/SSatoms
|
||||
InitializeAtoms()
|
||||
old_initialized = SSatoms.old_initialized
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_begin()
|
||||
old_initialized = initialized
|
||||
initialized = INITIALIZATION_INSSATOMS
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_stop()
|
||||
initialized = old_initialized
|
||||
|
||||
#undef BAD_INIT_QDEL_BEFORE
|
||||
#undef BAD_INIT_DIDNT_INIT
|
||||
#undef BAD_INIT_SLEPT
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
flags = SS_NO_FIRE | SS_NO_DISPLAY
|
||||
|
||||
/datum/controller/subsystem/misc_late/Initialize(timeofday)
|
||||
// Sort the area list.
|
||||
sortTim(all_areas, /proc/cmp_name_asc)
|
||||
// Generate the area list.
|
||||
resort_all_areas()
|
||||
|
||||
var/turf/picked
|
||||
// Setup the teleport locs.
|
||||
@@ -38,3 +38,15 @@
|
||||
shuttle_controller.setup_shuttle_docks()
|
||||
|
||||
..(timeofday, TRUE)
|
||||
|
||||
/proc/resort_all_areas()
|
||||
all_areas = list()
|
||||
for (var/area/A in world)
|
||||
all_areas += A
|
||||
|
||||
sortTim(all_areas, /proc/cmp_text_asc)
|
||||
|
||||
/proc/sorted_add_area(area/A)
|
||||
all_areas += A
|
||||
|
||||
sortTim(all_areas, /proc/cmp_text_asc)
|
||||
|
||||
@@ -148,6 +148,14 @@
|
||||
out += "LT:{T:[processes_this_tick]|P:[powerusers_this_tick]}"
|
||||
..(out.Join("\n\t"))
|
||||
|
||||
/datum/controller/subsystem/machinery/proc/setup_template_powernets(list/cables)
|
||||
for(var/A in cables)
|
||||
var/obj/structure/cable/PC = A
|
||||
if(!PC.powernet)
|
||||
var/datum/powernet/NewPN = new()
|
||||
NewPN.add_cable(PC)
|
||||
propagate_network(PC, PC.powernet)
|
||||
|
||||
/proc/add_machine(obj/machinery/M)
|
||||
if (QDELETED(M))
|
||||
crash_with("Attempted add of QDELETED machine [M ? M : "NULL"] to machines list, ignoring.")
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
icon_state = "white"
|
||||
layer = 10
|
||||
uid = ++global_uid
|
||||
all_areas += src
|
||||
blend_mode = BLEND_MULTIPLY
|
||||
|
||||
if(!requires_power)
|
||||
|
||||
@@ -130,6 +130,8 @@ move an amendment</a> to the drawing.</p>
|
||||
|
||||
A.always_unpowered = 0
|
||||
|
||||
sorted_add_area(A)
|
||||
|
||||
spawn(5)
|
||||
//ma = A.master ? "[A.master]" : "(null)"
|
||||
//world << "DEBUG: create_area(5): <br>A.name=[A.name]<br>A.tag=[A.tag]<br>A.master=[ma]"
|
||||
@@ -155,6 +157,7 @@ move an amendment</a> to the drawing.</p>
|
||||
return
|
||||
set_area_machinery_title(A,str,prevname)
|
||||
A.name = str
|
||||
sortTim(all_areas, /proc/cmp_text_asc)
|
||||
usr << "<span class='notice'>You set the area '[prevname]' title to '[str]'.</span>"
|
||||
interact()
|
||||
return
|
||||
|
||||
@@ -34,13 +34,14 @@ proc/createRandomZlevel()
|
||||
potentialRandomZlevels.Add(name)
|
||||
|
||||
|
||||
var/static/dmm_suite/loader = new
|
||||
if(potentialRandomZlevels.len)
|
||||
admin_notice("<span class='danger'>Loading away mission...</span>", R_DEBUG)
|
||||
|
||||
var/map = pick(potentialRandomZlevels)
|
||||
var/file = file(map)
|
||||
if(isfile(file))
|
||||
maploader.load_map(file)
|
||||
loader.load_map(file)
|
||||
world.log << "away mission loaded: [map]"
|
||||
|
||||
for(var/obj/effect/landmark/L in landmarks_list)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
var/global/dmm_suite/maploader = new
|
||||
|
||||
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.
|
||||
@@ -53,21 +53,11 @@ dmm_suite{
|
||||
|
||||
*/
|
||||
|
||||
verb/load_map(var/dmm_file as file, var/z_offset as num)
|
||||
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).
|
||||
|
||||
verb/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num){
|
||||
// t1: A turf representing one corner of a three dimensional grid (Required).
|
||||
// t2: Another turf representing the other corner of the same grid (Required).
|
||||
// flags: Any, or a combination, of several bit flags (Optional, see documentation).
|
||||
// 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
|
||||
}
|
||||
|
||||
// save_map is included as a legacy proc. Use write_map instead.
|
||||
verb/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){
|
||||
// t1: A turf representing one corner of a three dimensional grid (Required).
|
||||
// t2: Another turf representing the other corner of the same grid (Required).
|
||||
// map_name: A valid name for the map to be saved, such as "castle" (Required).
|
||||
// flags: Any, or a combination, of several bit flags (Optional, see documentation).
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
code/modules/maps/map_template.dm
Normal file
81
code/modules/maps/map_template.dm
Normal file
@@ -0,0 +1,81 @@
|
||||
/datum/map_template
|
||||
var/name = "Default Template Name"
|
||||
var/width = 0
|
||||
var/height = 0
|
||||
var/mappath = null
|
||||
var/loaded = 0 // Times loaded this round
|
||||
var/static/dmm_suite/maploader = new
|
||||
|
||||
/datum/map_template/New(path = null, rename = null)
|
||||
if(path)
|
||||
mappath = path
|
||||
if(mappath)
|
||||
preload_size(mappath)
|
||||
if(rename)
|
||||
name = rename
|
||||
|
||||
/datum/map_template/proc/preload_size(path)
|
||||
var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE)
|
||||
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]
|
||||
return bounds
|
||||
|
||||
/datum/map_template/proc/initTemplateBounds(var/list/bounds)
|
||||
var/list/obj/structure/cable/cables = list()
|
||||
var/list/atom/atoms = list()
|
||||
|
||||
for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]),
|
||||
locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
|
||||
var/turf/B = L
|
||||
atoms += B
|
||||
for(var/A in B)
|
||||
atoms += A
|
||||
if(istype(A,/obj/structure/cable))
|
||||
cables += A
|
||||
|
||||
SSatoms.InitializeAtoms(atoms)
|
||||
SSmachinery.setup_template_powernets(cables)
|
||||
|
||||
/datum/map_template/proc/load_new_z()
|
||||
var/x = round(world.maxx/2)
|
||||
var/y = round(world.maxy/2)
|
||||
|
||||
var/list/bounds = maploader.load_map(file(mappath), x, y)
|
||||
if(!bounds)
|
||||
return FALSE
|
||||
|
||||
smooth_zlevel(world.maxz)
|
||||
resort_all_areas()
|
||||
|
||||
//initialize things that are normally initialized after map load
|
||||
initTemplateBounds(bounds)
|
||||
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
|
||||
|
||||
/datum/map_template/proc/load(turf/T, centered = FALSE)
|
||||
if(centered)
|
||||
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
|
||||
if(!T)
|
||||
return
|
||||
if(T.x+width > world.maxx)
|
||||
return
|
||||
if(T.y+height > world.maxy)
|
||||
return
|
||||
|
||||
var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE)
|
||||
if(!bounds)
|
||||
return
|
||||
|
||||
//initialize things that are normally initialized after map load
|
||||
initTemplateBounds(bounds)
|
||||
|
||||
log_game("[name] loaded at at [T.x],[T.y],[T.z]")
|
||||
return TRUE
|
||||
|
||||
/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))
|
||||
@@ -3,8 +3,21 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
//global datum that will preload variables on atoms instanciation
|
||||
var/global/dmm_suite/preloader/_preloader = null
|
||||
var/global/use_preloader = FALSE
|
||||
var/global/dmm_suite/preloader/_preloader = new
|
||||
|
||||
/dmm_suite
|
||||
// /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
|
||||
var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g")
|
||||
// /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g
|
||||
var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g")
|
||||
// /^[\s\n]+|[\s\n]+$/
|
||||
var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g")
|
||||
var/static/list/modelCache = list()
|
||||
var/static/space_key
|
||||
#ifdef TESTING
|
||||
var/static/turfsSkipped
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Construct the model map and control the loading process
|
||||
@@ -16,82 +29,145 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
* 2) Read the map line by line, parsing the result (using parse_grid)
|
||||
*
|
||||
*/
|
||||
/dmm_suite/load_map(var/dmm_file as file, var/z_offset as num)
|
||||
if(!z_offset)//what z_level we are creating the map on
|
||||
z_offset = world.maxz+1
|
||||
/dmm_suite/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)
|
||||
//How I wish for RAII
|
||||
Master.StartLoadingMap()
|
||||
space_key = null
|
||||
#ifdef TESTING
|
||||
turfsSkipped = 0
|
||||
#endif
|
||||
. = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf)
|
||||
#ifdef TESTING
|
||||
if(turfsSkipped)
|
||||
testing("Skipped loading [turfsSkipped] default turfs")
|
||||
#endif
|
||||
Master.StopLoadingMap()
|
||||
|
||||
var/quote = ascii2text(34)
|
||||
var/tfile = file2text(dmm_file)//the map file we're creating
|
||||
var/tfile_len = length(tfile)
|
||||
var/lpos = 1 // the models definition index
|
||||
/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf)
|
||||
var/tfile = dmm_file//the map file we're creating
|
||||
if(isfile(tfile))
|
||||
tfile = file2text(tfile)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//first let's map model keys (e.g "aa") to their contents (e.g /turf/space{variables})
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
if(!x_offset)
|
||||
x_offset = 1
|
||||
if(!y_offset)
|
||||
y_offset = 1
|
||||
if(!z_offset)
|
||||
z_offset = world.maxz + 1
|
||||
|
||||
var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
||||
var/list/grid_models = list()
|
||||
var/key_len = length(copytext(tfile,2,findtext(tfile,quote,2,0)))//the length of the model key (e.g "aa" or "aba")
|
||||
var/key_len = 0
|
||||
|
||||
//proceed line by line
|
||||
for(lpos=1; lpos<tfile_len; lpos=findtext(tfile,"\n",lpos,0)+1)
|
||||
var/tline = copytext(tfile,lpos,findtext(tfile,"\n",lpos,0))
|
||||
if(copytext(tline,1,2) != quote)//we reached the map "layout"
|
||||
break
|
||||
var/model_key = copytext(tline,2,2+key_len)
|
||||
var/model_contents = copytext(tline,findtext(tfile,"=")+3,length(tline))
|
||||
grid_models[model_key] = model_contents
|
||||
sleep(-1)
|
||||
var/stored_index = 1
|
||||
while(dmmRegex.Find(tfile, stored_index))
|
||||
stored_index = dmmRegex.next
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//now let's fill the map with turf and objects using the constructed model map
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// "aa" = (/type{vars=blah})
|
||||
if(dmmRegex.group[1]) // Model
|
||||
var/key = dmmRegex.group[1]
|
||||
if(grid_models[key]) // Duplicate model keys are ignored in DMMs
|
||||
continue
|
||||
if(key_len != length(key))
|
||||
if(!key_len)
|
||||
key_len = length(key)
|
||||
else
|
||||
throw EXCEPTION("Inconsistant key length in DMM")
|
||||
if(!measureOnly)
|
||||
grid_models[key] = dmmRegex.group[2]
|
||||
|
||||
//position of the currently processed square
|
||||
var/zcrd=-1
|
||||
var/ycrd=0
|
||||
var/xcrd=0
|
||||
// (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
||||
else if(dmmRegex.group[3]) // Coords
|
||||
if(!key_len)
|
||||
throw EXCEPTION("Coords before model definition in DMM")
|
||||
|
||||
for(var/zpos=findtext(tfile,"\n(1,1,",lpos,0);zpos!=0;zpos=findtext(tfile,"\n(1,1,",zpos+1,0)) //in case there's several maps to load
|
||||
var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1
|
||||
//position of the currently processed square
|
||||
var/xcrd
|
||||
var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1
|
||||
var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
|
||||
|
||||
zcrd++
|
||||
world.maxz = max(world.maxz, zcrd+z_offset)//create a new z_level if needed
|
||||
var/zexpansion = zcrd > world.maxz
|
||||
if(zexpansion)
|
||||
if(cropMap)
|
||||
continue
|
||||
else
|
||||
world.maxz = zcrd //create a new z_level if needed
|
||||
if(!no_changeturf)
|
||||
WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called")
|
||||
|
||||
var/zgrid = copytext(tfile,findtext(tfile,quote+"\n",zpos,0)+2,findtext(tfile,"\n"+quote,zpos,0)+1) //copy the whole map grid
|
||||
var/z_depth = length(zgrid)
|
||||
bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart)
|
||||
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
|
||||
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
|
||||
|
||||
//if exceeding the world max x or y, increase it
|
||||
var/x_depth = length(copytext(zgrid,1,findtext(zgrid,"\n",2,0)))
|
||||
if(world.maxx<x_depth)
|
||||
world.maxx=x_depth
|
||||
var/list/gridLines = splittext(dmmRegex.group[6], "\n")
|
||||
|
||||
var/y_depth = z_depth / (x_depth+1)//x_depth + 1 because we're counting the '\n' characters in z_depth
|
||||
if(world.maxy<y_depth)
|
||||
world.maxy=y_depth
|
||||
var/leadingBlanks = 0
|
||||
while(leadingBlanks < gridLines.len && gridLines[++leadingBlanks] == "")
|
||||
if(leadingBlanks > 1)
|
||||
gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines.
|
||||
|
||||
//then proceed it line by line, starting from top
|
||||
ycrd = y_depth
|
||||
if(!gridLines.len) // Skip it if only blank lines exist.
|
||||
continue
|
||||
|
||||
for(var/gpos=1;gpos!=0;gpos=findtext(zgrid,"\n",gpos,0)+1)
|
||||
var/grid_line = copytext(zgrid,gpos,findtext(zgrid,"\n",gpos,0))
|
||||
if(gridLines.len && gridLines[gridLines.len] == "")
|
||||
gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
|
||||
|
||||
//fill the current square using the model map
|
||||
xcrd=0
|
||||
for(var/mpos=1;mpos<=x_depth;mpos+=key_len)
|
||||
xcrd++
|
||||
var/model_key = copytext(grid_line,mpos,mpos+key_len)
|
||||
parse_grid(grid_models[model_key],xcrd,ycrd,zcrd+z_offset)
|
||||
bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
|
||||
ycrd += gridLines.len - 1 // Start at the top and work down
|
||||
|
||||
//reached end of current map
|
||||
if(gpos+x_depth+1>z_depth)
|
||||
break
|
||||
if(!cropMap && ycrd > world.maxy)
|
||||
if(!measureOnly)
|
||||
world.maxy = ycrd // Expand Y here. X is expanded in the loop below
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
|
||||
else
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], min(ycrd, world.maxy))
|
||||
|
||||
ycrd--
|
||||
var/maxx = xcrdStart
|
||||
if(measureOnly)
|
||||
for(var/line in gridLines)
|
||||
maxx = max(maxx, xcrdStart + length(line) / key_len - 1)
|
||||
else
|
||||
for(var/line in gridLines)
|
||||
if(ycrd <= world.maxy && ycrd >= 1)
|
||||
xcrd = xcrdStart
|
||||
for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
|
||||
if(xcrd > world.maxx)
|
||||
if(cropMap)
|
||||
break
|
||||
else
|
||||
world.maxx = xcrd
|
||||
|
||||
sleep(-1)
|
||||
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))
|
||||
if(!grid_models[model_key])
|
||||
throw EXCEPTION("Undefined model key in DMM.")
|
||||
parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion)
|
||||
#ifdef TESTING
|
||||
else
|
||||
++turfsSkipped
|
||||
#endif
|
||||
CHECK_TICK
|
||||
maxx = max(maxx, xcrd)
|
||||
++xcrd
|
||||
--ycrd
|
||||
|
||||
//reached End Of File
|
||||
if(findtext(tfile,quote+"}",zpos,0)+2==tfile_len)
|
||||
break
|
||||
sleep(-1)
|
||||
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
if(bounds[1] == 1.#INF) // Shouldn't need to check every item
|
||||
return null
|
||||
else
|
||||
if(!measureOnly)
|
||||
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])))
|
||||
var/turf/T = t
|
||||
//we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
|
||||
T.post_change(TRUE)
|
||||
return bounds
|
||||
|
||||
/**
|
||||
* Fill a given tile with its area/turf/objects/mobs
|
||||
@@ -110,47 +186,82 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
* 4) Instanciates the atom with its variables
|
||||
*
|
||||
*/
|
||||
/dmm_suite/proc/parse_grid(var/model as text,var/xcrd as num,var/ycrd as num,var/zcrd as num)
|
||||
/dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num)
|
||||
/*Method parse_grid()
|
||||
- Accepts a text string containing a comma separated list of type paths of the
|
||||
same construction as those contained in a .dmm file, and instantiates them.
|
||||
*/
|
||||
|
||||
var/list/members = list()//will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored)
|
||||
var/list/members_attributes = list()//will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
|
||||
var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored)
|
||||
var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
|
||||
var/list/cached = modelCache[model]
|
||||
var/index
|
||||
|
||||
if(cached)
|
||||
members = cached[1]
|
||||
members_attributes = cached[2]
|
||||
else
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//Constructing members and corresponding variables lists
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
members = list()
|
||||
members_attributes = list()
|
||||
index = 1
|
||||
|
||||
var/old_position = 1
|
||||
var/dpos
|
||||
|
||||
do
|
||||
//finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/explored)
|
||||
dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...}
|
||||
|
||||
var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp}
|
||||
var/variables_start = findtext(full_def, "{")
|
||||
var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar
|
||||
old_position = dpos + 1
|
||||
|
||||
if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
|
||||
continue
|
||||
members.Add(atom_def)
|
||||
|
||||
//transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
|
||||
var/list/fields = list()
|
||||
|
||||
if(variables_start)//if there's any variable
|
||||
full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}'
|
||||
fields = readlist(full_def, ";")
|
||||
if(fields.len)
|
||||
if(!trim(fields[fields.len]))
|
||||
--fields.len
|
||||
for(var/I in fields)
|
||||
var/value = fields[I]
|
||||
if(istext(value))
|
||||
fields[I] = apply_text_macros(value)
|
||||
|
||||
//then fill the members_attributes list with the corresponding variables
|
||||
members_attributes.len++
|
||||
members_attributes[index++] = fields
|
||||
|
||||
CHECK_TICK
|
||||
while(dpos != 0)
|
||||
|
||||
//check and see if we can just skip this turf
|
||||
//So you don't have to understand this horrid statement, we can do this if
|
||||
// 1. no_changeturf is set
|
||||
// 2. the space_key isn't set yet
|
||||
// 3. there are exactly 2 members
|
||||
// 4. with no attributes
|
||||
// 5. and the members are world.turf and world.area
|
||||
// Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default)
|
||||
// We can skip calling this proc every time we see XXX
|
||||
if(no_changeturf && !space_key && members.len == 2 && members_attributes.len == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members))
|
||||
space_key = model_key
|
||||
return
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//Constructing members and corresponding variables lists
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
var/index=1
|
||||
var/old_position = 1
|
||||
var/dpos
|
||||
|
||||
do
|
||||
//finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/explored)
|
||||
dpos= find_next_delimiter_position(model,old_position,",","{","}")//find next delimiter (comma here) that's not within {...}
|
||||
|
||||
var/full_def = copytext(model,old_position,dpos)//full definition, e.g : /obj/foo/bar{variables=derp}
|
||||
var/atom_def = text2path(copytext(full_def,1,findtext(full_def,"{")))//path definition, e.g /obj/foo/bar
|
||||
members.Add(atom_def)
|
||||
old_position = dpos + 1
|
||||
|
||||
//transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
|
||||
var/list/fields = list()
|
||||
|
||||
var/variables_start = findtext(full_def,"{")
|
||||
if(variables_start)//if there's any variable
|
||||
full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}'
|
||||
fields = text2list(full_def,";")
|
||||
|
||||
//then fill the members_attributes list with the corresponding variables
|
||||
members_attributes.len++
|
||||
members_attributes[index++] = fields
|
||||
|
||||
sleep(-1)
|
||||
while(dpos != 0)
|
||||
modelCache[model] = list(members, members_attributes)
|
||||
|
||||
|
||||
////////////////
|
||||
@@ -158,23 +269,25 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
////////////////
|
||||
|
||||
//The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
|
||||
|
||||
//in case of multiples turfs on one tile,
|
||||
//will contains the images of all underlying turfs, to simulate the DMM multiple tiles piling
|
||||
var/list/turfs_underlays = list()
|
||||
var/turf/crds = locate(xcrd,ycrd,zcrd)
|
||||
|
||||
//first instance the /area and remove it from the members list
|
||||
index = members.len
|
||||
var/atom/instance
|
||||
_preloader = new(members_attributes[index])//preloader for assigning set variables on atom creation
|
||||
if(members[index] != /area/template_noop)
|
||||
var/atom/instance
|
||||
_preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
|
||||
var/atype = members[index]
|
||||
for(var/area/A in world)
|
||||
if(A.type == atype)
|
||||
instance = A
|
||||
break
|
||||
if(!instance)
|
||||
instance = new atype(null)
|
||||
if(crds)
|
||||
instance.contents.Add(crds)
|
||||
|
||||
instance = locate(members[index])
|
||||
instance.contents.Add(locate(xcrd,ycrd,zcrd))
|
||||
|
||||
if(_preloader && instance)
|
||||
_preloader.load(instance)
|
||||
|
||||
members.Remove(members[index])
|
||||
if(use_preloader && instance)
|
||||
_preloader.load(instance)
|
||||
|
||||
//then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
|
||||
|
||||
@@ -182,55 +295,67 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
while(!ispath(members[first_turf_index],/turf)) //find first /turf object in members
|
||||
first_turf_index++
|
||||
|
||||
//turn off base new Initialization until the whole thing is loaded
|
||||
SSatoms.map_loader_begin()
|
||||
//instanciate the first /turf
|
||||
var/turf/T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],xcrd,ycrd,zcrd)
|
||||
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)
|
||||
|
||||
//if others /turf are presents, simulates the underlays piling effect
|
||||
index = first_turf_index + 1
|
||||
while(index <= members.len)
|
||||
turfs_underlays.Insert(1,image(T.icon,null,T.icon_state,T.layer,T.dir))//add the current turf image to the underlays list
|
||||
var/turf/UT = instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)//instance new turf
|
||||
add_underlying_turf(UT,T,turfs_underlays)//simulates the DMM piling effect
|
||||
T = UT
|
||||
index++
|
||||
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)//instance new turf
|
||||
T.underlays += underlay
|
||||
index++
|
||||
|
||||
//finally instance all remainings objects/mobs
|
||||
for(index=1,index < first_turf_index,index++)
|
||||
instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)
|
||||
for(index in 1 to first_turf_index-1)
|
||||
instance_atom(members[index],members_attributes[index],crds,no_changeturf)
|
||||
//Restore initialization to the previous value
|
||||
SSatoms.map_loader_stop()
|
||||
|
||||
////////////////
|
||||
//Helpers procs
|
||||
////////////////
|
||||
|
||||
//Instance an atom at (x,y,z) and gives it the variables in attributes
|
||||
/dmm_suite/proc/instance_atom(var/path,var/list/attributes, var/x, var/y, var/z)
|
||||
var/atom/instance
|
||||
_preloader = new(attributes, path)
|
||||
/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf)
|
||||
_preloader.setup(attributes, path)
|
||||
|
||||
instance = new path (locate(x,y,z))//first preloader pass
|
||||
if(crds)
|
||||
if(!no_changeturf && ispath(path, /turf))
|
||||
. = crds.ChangeTurf(path, TRUE)
|
||||
else
|
||||
. = create_atom(path, crds)//first preloader pass
|
||||
|
||||
if(_preloader && instance)//second preloader pass, for those atoms that don't ..() in New()
|
||||
_preloader.load(instance)
|
||||
if(use_preloader && .)//second preloader pass, for those atoms that don't ..() in New()
|
||||
_preloader.load(.)
|
||||
|
||||
return instance
|
||||
//custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize
|
||||
if(TICK_CHECK)
|
||||
SSatoms.map_loader_stop()
|
||||
stoplag()
|
||||
SSatoms.map_loader_begin()
|
||||
|
||||
/dmm_suite/proc/create_atom(path, crds)
|
||||
set waitfor = FALSE
|
||||
. = new path (crds)
|
||||
|
||||
//text trimming (both directions) helper proc
|
||||
//optionally removes quotes before and after the text (for variable name)
|
||||
/dmm_suite/proc/trim_text(var/what as text,var/trim_quotes=0)
|
||||
while(length(what) && (findtext(what," ",1,2)))
|
||||
what=copytext(what,2,0)
|
||||
while(length(what) && (findtext(what," ",length(what),0)))
|
||||
what=copytext(what,1,length(what))
|
||||
/dmm_suite/proc/trim_text(what as text,trim_quotes=0)
|
||||
if(trim_quotes)
|
||||
while(length(what) && (findtext(what,quote,1,2)))
|
||||
what=copytext(what,2,0)
|
||||
while(length(what) && (findtext(what,quote,length(what),0)))
|
||||
what=copytext(what,1,length(what))
|
||||
return what
|
||||
return trimQuotesRegex.Replace(what, "")
|
||||
else
|
||||
return trimRegex.Replace(what, "")
|
||||
|
||||
|
||||
//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape
|
||||
//returns 0 if reached the last delimiter
|
||||
/dmm_suite/proc/find_next_delimiter_position(var/text as text,var/initial_position as num, var/delimiter=",",var/opening_escape=quote,var/closing_escape=quote)
|
||||
/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"")
|
||||
var/position = initial_position
|
||||
var/next_delimiter = findtext(text,delimiter,position,0)
|
||||
var/next_opening = findtext(text,opening_escape,position,0)
|
||||
@@ -245,7 +370,7 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
|
||||
//build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
|
||||
//return the filled list
|
||||
/dmm_suite/proc/text2list(var/text as text,var/delimiter=",")
|
||||
/dmm_suite/proc/readlist(text as text, delimiter=",")
|
||||
|
||||
var/list/to_return = list()
|
||||
|
||||
@@ -266,8 +391,8 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable
|
||||
|
||||
//Check for string
|
||||
if(findtext(trim_right,quote,1,2))
|
||||
trim_right = copytext(trim_right,2,findtext(trim_right,quote,3,0))
|
||||
if(findtext(trim_right,"\"",1,2))
|
||||
trim_right = copytext(trim_right,2,findtext(trim_right,"\"",3,0))
|
||||
|
||||
//Check for number
|
||||
else if(isnum(text2num(trim_right)))
|
||||
@@ -279,12 +404,16 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
|
||||
//Check for list
|
||||
else if(copytext(trim_right,1,5) == "list")
|
||||
trim_right = text2list(copytext(trim_right,6,length(trim_right)))
|
||||
trim_right = readlist(copytext(trim_right,6,length(trim_right)))
|
||||
|
||||
//Check for file
|
||||
else if(copytext(trim_right,1,2) == "'")
|
||||
trim_right = file(copytext(trim_right,2,length(trim_right)))
|
||||
|
||||
//Check for path
|
||||
else if(ispath(text2path(trim_right)))
|
||||
trim_right = text2path(trim_right)
|
||||
|
||||
to_return[trim_left] = trim_right
|
||||
|
||||
else//simple var
|
||||
@@ -294,13 +423,9 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
|
||||
return to_return
|
||||
|
||||
//simulates the DM multiple turfs on one tile underlaying
|
||||
/dmm_suite/proc/add_underlying_turf(var/turf/placed,var/turf/underturf, var/list/turfs_underlays)
|
||||
if(underturf.density)
|
||||
placed.density = 1
|
||||
if(underturf.opacity)
|
||||
placed.set_opacity(1)
|
||||
placed.underlays += turfs_underlays
|
||||
/dmm_suite/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
//////////////////
|
||||
//Preloader datum
|
||||
@@ -311,15 +436,22 @@ var/global/dmm_suite/preloader/_preloader = null
|
||||
var/list/attributes
|
||||
var/target_path
|
||||
|
||||
/dmm_suite/preloader/New(var/list/the_attributes, var/path)
|
||||
.=..()
|
||||
if(!the_attributes.len)
|
||||
Del()
|
||||
return
|
||||
attributes = the_attributes
|
||||
target_path = path
|
||||
/dmm_suite/preloader/proc/setup(list/the_attributes, path)
|
||||
if(the_attributes.len)
|
||||
use_preloader = TRUE
|
||||
attributes = the_attributes
|
||||
target_path = path
|
||||
|
||||
/dmm_suite/preloader/proc/load(atom/what)
|
||||
for(var/attribute in attributes)
|
||||
what.vars[attribute] = attributes[attribute]
|
||||
Del()
|
||||
var/value = attributes[attribute]
|
||||
if(islist(value))
|
||||
value = deepCopyList(value)
|
||||
what.vars[attribute] = value
|
||||
use_preloader = FALSE
|
||||
|
||||
/area/template_noop
|
||||
name = "Area Passthrough"
|
||||
|
||||
/turf/template_noop
|
||||
name = "Turf Passthrough"
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
#define DMM_IGNORE_AREAS 1
|
||||
#define DMM_IGNORE_TURFS 2
|
||||
#define DMM_IGNORE_OBJS 4
|
||||
#define DMM_IGNORE_NPCS 8
|
||||
#define DMM_IGNORE_PLAYERS 16
|
||||
#define DMM_IGNORE_MOBS 24
|
||||
dmm_suite{
|
||||
var{
|
||||
quote = "\""
|
||||
list/letter_digits = list(
|
||||
"a","b","c","d","e",
|
||||
"f","g","h","i","j",
|
||||
"k","l","m","n","o",
|
||||
"p","q","r","s","t",
|
||||
"u","v","w","x","y",
|
||||
"z",
|
||||
"A","B","C","D","E",
|
||||
"F","G","H","I","J",
|
||||
"K","L","M","N","O",
|
||||
"P","Q","R","S","T",
|
||||
"U","V","W","X","Y",
|
||||
"Z"
|
||||
)
|
||||
}
|
||||
save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){
|
||||
//Check for illegal characters in file name... in a cheap way.
|
||||
if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name))){
|
||||
CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.")
|
||||
}
|
||||
//Check for valid turfs.
|
||||
if(!isturf(t1) || !isturf(t2)){
|
||||
CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.")
|
||||
}
|
||||
var/file_text = write_map(t1,t2,flags)
|
||||
if(fexists("[map_name].dmm")){
|
||||
fdel("[map_name].dmm")
|
||||
}
|
||||
var/saved_map = file("[map_name].dmm")
|
||||
saved_map << file_text
|
||||
return saved_map
|
||||
}
|
||||
write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num){
|
||||
//Check for valid turfs.
|
||||
if(!isturf(t1) || !isturf(t2)){
|
||||
CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.")
|
||||
}
|
||||
var/turf/nw = locate(min(t1.x,t2.x),max(t1.y,t2.y),min(t1.z,t2.z))
|
||||
var/turf/se = locate(max(t1.x,t2.x),min(t1.y,t2.y),max(t1.z,t2.z))
|
||||
var/list/templates[0]
|
||||
var/template_buffer = {""}
|
||||
var/dmm_text = {""}
|
||||
for(var/pos_z=nw.z;pos_z<=se.z;pos_z++){
|
||||
for(var/pos_y=nw.y;pos_y>=se.y;pos_y--){
|
||||
for(var/pos_x=nw.x;pos_x<=se.x;pos_x++){
|
||||
var/turf/test_turf = locate(pos_x,pos_y,pos_z)
|
||||
var/test_template = make_template(test_turf, flags)
|
||||
var/template_number = templates.Find(test_template)
|
||||
if(!template_number){
|
||||
templates.Add(test_template)
|
||||
template_number = templates.len
|
||||
}
|
||||
template_buffer += "[template_number],"
|
||||
}
|
||||
template_buffer += ";"
|
||||
}
|
||||
template_buffer += "."
|
||||
}
|
||||
var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1)
|
||||
var/list/keys[templates.len]
|
||||
for(var/key_pos=1;key_pos<=templates.len;key_pos++){
|
||||
keys[key_pos] = get_model_key(key_pos,key_length)
|
||||
dmm_text += {""[keys[key_pos]]" = ([templates[key_pos]])\n"}
|
||||
}
|
||||
var/z_level = 0
|
||||
for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer,".",z_pos)+1){
|
||||
if(z_pos>=length(template_buffer)){break}
|
||||
if(z_level){dmm_text+={"\n"}}
|
||||
dmm_text += {"\n(1,1,[++z_level]) = {"\n"}
|
||||
var/z_block = copytext(template_buffer,z_pos,findtext(template_buffer,".",z_pos))
|
||||
for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1){
|
||||
if(y_pos>=length(z_block)){break}
|
||||
var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos))
|
||||
for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1){
|
||||
if(x_pos>=length(y_block)){break}
|
||||
var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos))
|
||||
var/key_number = text2num(x_block)
|
||||
var/temp_key = keys[key_number]
|
||||
dmm_text += temp_key
|
||||
sleep(-1)
|
||||
}
|
||||
dmm_text += {"\n"}
|
||||
sleep(-1)
|
||||
}
|
||||
dmm_text += {"\"}"}
|
||||
sleep(-1)
|
||||
}
|
||||
return dmm_text
|
||||
}
|
||||
proc{
|
||||
make_template(var/turf/model as turf, var/flags as num){
|
||||
var/template = ""
|
||||
var/obj_template = ""
|
||||
var/mob_template = ""
|
||||
var/turf_template = ""
|
||||
if(!(flags & DMM_IGNORE_TURFS)){
|
||||
turf_template = "[model.type][check_attributes(model)],"
|
||||
} else{ turf_template = "[world.turf],"}
|
||||
var/area_template = ""
|
||||
if(!(flags & DMM_IGNORE_OBJS)){
|
||||
for(var/obj/O in model.contents){
|
||||
obj_template += "[O.type][check_attributes(O)],"
|
||||
}
|
||||
}
|
||||
for(var/mob/M in model.contents){
|
||||
if(M.client){
|
||||
if(!(flags & DMM_IGNORE_PLAYERS)){
|
||||
mob_template += "[M.type][check_attributes(M)],"
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(!(flags & DMM_IGNORE_NPCS)){
|
||||
mob_template += "[M.type][check_attributes(M)],"
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!(flags & DMM_IGNORE_AREAS)){
|
||||
var/area/m_area = model.loc
|
||||
area_template = "[m_area.type][check_attributes(m_area)]"
|
||||
} else{ area_template = "[world.area]"}
|
||||
template = "[obj_template][mob_template][turf_template][area_template]"
|
||||
return template
|
||||
}
|
||||
check_attributes(var/atom/A){
|
||||
var/attributes_text = {"{"}
|
||||
for(var/V in A.vars){
|
||||
sleep(-1)
|
||||
if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))){continue}
|
||||
if(istext(A.vars[V])){
|
||||
attributes_text += {"[V] = "[A.vars[V]]""}
|
||||
}
|
||||
else if(isnum(A.vars[V])||ispath(A.vars[V])){
|
||||
attributes_text += {"[V] = [A.vars[V]]"}
|
||||
}
|
||||
else if(isicon(A.vars[V])||isfile(A.vars[V])){
|
||||
attributes_text += {"[V] = '[A.vars[V]]'"}
|
||||
}
|
||||
else{
|
||||
continue
|
||||
}
|
||||
if(attributes_text != {"{"}){
|
||||
attributes_text+={"; "}
|
||||
}
|
||||
}
|
||||
if(attributes_text=={"{"}){
|
||||
return
|
||||
}
|
||||
if(copytext(attributes_text, length(attributes_text)-1, 0) == {"; "}){
|
||||
attributes_text = copytext(attributes_text, 1, length(attributes_text)-1)
|
||||
}
|
||||
attributes_text += {"}"}
|
||||
return attributes_text
|
||||
}
|
||||
get_model_key(var/which as num, var/key_length as num){
|
||||
var/key = ""
|
||||
var/working_digit = which-1
|
||||
for(var/digit_pos=key_length;digit_pos>=1;digit_pos--){
|
||||
var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1)))
|
||||
working_digit-=place_value*(letter_digits.len**(digit_pos-1))
|
||||
key = "[key][letter_digits[place_value+1]]"
|
||||
}
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user