diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 06c7a7e6ec0..cb4e8c8163b 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -344,3 +344,13 @@ while(L.Remove(null)) continue return L + +//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 length(.)) + if(islist(.[i])) + .[i] = .(.[i]) diff --git a/code/modules/awaymissions/maploader/dmm_suite.dm b/code/modules/awaymissions/maploader/dmm_suite.dm index 71c9e6e788f..ba4664fa4aa 100644 --- a/code/modules/awaymissions/maploader/dmm_suite.dm +++ b/code/modules/awaymissions/maploader/dmm_suite.dm @@ -53,9 +53,10 @@ dmm_suite{ */ - verb/load_map(var/dmm_file as file, var/x_offset as num, var/y_offset as num, 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){ // 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). } 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). diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 5b5f8c1fd75..e153ad961d0 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -6,6 +6,14 @@ 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() /** * Construct the model map and control the loading process @@ -17,92 +25,94 @@ var/global/dmm_suite/preloader/_preloader = new * 2) Read the map line by line, parsing the result (using parse_grid) * */ -/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, 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) + var/tfile = dmm_file//the map file we're creating + if(isfile(tfile)) + tfile = file2text(tfile) if(!x_offset) - x_offset = 0 - + x_offset = 1 if(!y_offset) - y_offset = 0 - 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 + y_offset = 1 + if(!z_offset) + z_offset = world.maxz + 1 - /////////////////////////////////////////////////////////////////////////////////////// - //first let's map model keys (e.g "aa") to their contents (e.g /turf/open/space{variables}) - /////////////////////////////////////////////////////////////////////////////////////// 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 world.maxz) + if(cropMap) + continue + else + world.maxz = zcrd //create a new z_level if needed - 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) + var/list/gridLines = splittext(dmmRegex.group[6], "\n") - //if exceeding the world max x or y, increase it - var/x_depth = length(copytext(zgrid,1,findtext(zgrid,"\n",2,0))) - var/x_tilecount = x_depth/key_len - if(world.maxx 1) + gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines. - var/y_depth = z_depth / (x_depth+1)//x_depth + 1 because we're counting the '\n' characters in z_depth - if(world.maxy world.maxy) + world.maxy = ycrd // Expand Y here. X is expanded in the loop below + 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 - for(var/mpos in 1 to x_depth step key_len) - xcrd++ - var/model_key = copytext(grid_line,mpos,mpos+key_len) - parse_grid(grid_models[model_key],xcrd+x_offset,ycrd+y_offset,zcrd+z_offset) + if(xcrd >= 1) + var/model_key = copytext(line, tpos, tpos + key_len) + if(!grid_models[model_key]) + throw EXCEPTION("Undefined model key in DMM.") + parse_grid(grid_models[model_key], xcrd, ycrd, zcrd) + sleep(-1) - //reached end of current map - if(gpos+x_depth+1>z_depth) - break - - ycrd-- - CHECK_TICK - - - //reached End Of File - if(findtext(tfile,quote+"}",zpos,0)+2==tfile_len) - break + ++xcrd + --ycrd CHECK_TICK + return 1 /** * Fill a given tile with its area/turf/objects/mobs @@ -127,41 +137,56 @@ var/global/dmm_suite/preloader/_preloader = new 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 - //////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + //Constructing members and corresponding variables lists + //////////////////////////////////////////////////////// - var/index=1 - var/old_position = 1 - var/dpos + members = list() + members_attributes = list() + index = 1 - 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/old_position = 1 + var/dpos + //var/model_sl = replacetext(model, "\n", "") - 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 + 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 {...} - //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/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 - 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 = readlist(full_def, ";") + if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! + continue + members.Add(atom_def) - //then fill the members_attributes list with the corresponding variables - members_attributes.len++ - members_attributes[index++] = fields - CHECK_TICK + //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() - while(dpos != 0) + 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, ";") + + //then fill the members_attributes list with the corresponding variables + members_attributes.len++ + members_attributes[index++] = fields + + CHECK_TICK + while(dpos != 0) + + modelCache[model] = list(members, members_attributes) //////////////// @@ -187,7 +212,6 @@ var/global/dmm_suite/preloader/_preloader = new if(use_preloader && instance) _preloader.load(instance) - members.Remove(members[index]) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect @@ -203,7 +227,7 @@ var/global/dmm_suite/preloader/_preloader = new if(T) //if others /turf are presents, simulates the underlays piling effect index = first_turf_index + 1 - while(index <= members.len) + while(index <= members.len - 1) // Last item is an /area 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 @@ -236,16 +260,11 @@ var/global/dmm_suite/preloader/_preloader = new //text trimming (both directions) helper proc //optionally removes quotes before and after the text (for variable name) /dmm_suite/proc/trim_text(what as text,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)) 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 @@ -353,7 +372,10 @@ var/global/dmm_suite/preloader/_preloader = new /dmm_suite/preloader/proc/load(atom/what) for(var/attribute in attributes) - what.vars[attribute] = attributes[attribute] + var/value = attributes[attribute] + if(islist(value)) + value = deepCopyList(value) + what.vars[attribute] = value use_preloader = FALSE /area/template_noop