185 lines
6.7 KiB
Plaintext
185 lines
6.7 KiB
Plaintext
//Map exporter
|
|
//Inputting a list of turfs into convert_map_to_tgm() will output a string
|
|
//with the turfs and their objects / areas on said turf into the TGM mapping format
|
|
//for .dmm files. This file can then be opened in the map editor or imported
|
|
//back into the game.
|
|
//============================
|
|
//This has been made semi-modular so you should be able to use these functions
|
|
//elsewhere in code if you ever need to get a file in the .dmm format
|
|
/atom/proc/get_save_vars()
|
|
return
|
|
|
|
GLOBAL_LIST_INIT(save_file_chars, 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"
|
|
))
|
|
|
|
//Converts a list of turfs into TGM file format
|
|
/proc/convert_map_to_tgm(list/map,\
|
|
save_flag = SAVE_ALL, \
|
|
shuttle_area_flag = SAVE_SHUTTLEAREA_DONTCARE, \
|
|
list/vars_to_save = list("pixel_x", "pixel_y", "dir", "name", "req_access", "req_access_txt", "piping_layer", "color", "icon_state", "pipe_color", "amount"),\
|
|
list/obj_blacklist = list())
|
|
//Calculate the bounds
|
|
var/minx = 1024
|
|
var/miny = 1024
|
|
var/maxx = -1
|
|
var/maxy = -1
|
|
for(var/turf/place in map)
|
|
minx = min(place.x, minx)
|
|
miny = min(place.y, miny)
|
|
maxx = max(place.x, maxx)
|
|
maxy = max(place.y, maxy)
|
|
var/width = maxx - minx + 1
|
|
var/height = maxy - miny + 1
|
|
|
|
//Sort the map so weird shaped / bad inputted maps can be handled
|
|
var/list/sortedmap = sort_map(map, minx, miny, maxx, maxy)
|
|
|
|
//Step 0: Calculate the amount of letters we need (26 ^ n > turf count)
|
|
var/turfsNeeded = width * height
|
|
var/layers = FLOOR(log(GLOB.save_file_chars.len, turfsNeeded) + 0.999,1)
|
|
|
|
//Step 1: Run through the area and generate file data
|
|
var/list/header_chars = list() //The characters of the header
|
|
var/list/header_dat = list() //The data of the header, lines up with chars
|
|
var/header = "" //The actual header in text
|
|
var/contents = "" //The contents in text (bit at the end)
|
|
var/index = 1
|
|
for(var/x in 1 to width)
|
|
contents += "\n([x],1,1) = {\"\n"
|
|
for(var/y in height to 1 step -1)
|
|
//====Get turfs Data====
|
|
var/turf/place = sortedmap[x][y]
|
|
var/area/location
|
|
var/list/objects
|
|
//If there is nothing there, save as a noop (For odd shapes)
|
|
if(!place)
|
|
place = /turf/template_noop
|
|
location = /area/template_noop
|
|
objects = list()
|
|
//Ignore things in space, must be a space turf and the area has to be empty space
|
|
else if(istype(place, /turf/open/space) && istype(get_area(place), /area/space) && !(save_flag & SAVE_SPACE))
|
|
place = /turf/template_noop
|
|
location = /area/template_noop
|
|
//Stuff to add
|
|
else
|
|
var/area/location_type = get_area(place)
|
|
location = location_type.type
|
|
objects = place
|
|
place = place.type
|
|
//====Saving shuttles only / non shuttles only====
|
|
var/is_shuttle_area = istype(location, /area/shuttle)
|
|
if((is_shuttle_area && shuttle_area_flag == SAVE_SHUTTLEAREA_IGNORE) || (!is_shuttle_area && shuttle_area_flag == SAVE_SHUTTLEAREA_ONLY))
|
|
place = /turf/template_noop
|
|
location = /area/template_noop
|
|
objects = list()
|
|
//====For toggling not saving areas and turfs====
|
|
if(!(save_flag & SAVE_AREAS))
|
|
location = /area/template_noop
|
|
if(!(save_flag & SAVE_TURFS))
|
|
place = /turf/template_noop
|
|
//====Generate Header Character====
|
|
var/header_char = calculate_tgm_header_index(index, layers) //The characters of the header
|
|
var/current_header = "(\n" //The actual stuff inside the header
|
|
//Add objects to the header file
|
|
var/empty = TRUE
|
|
//====SAVING OBJECTS====
|
|
if(save_flag & SAVE_OBJECTS)
|
|
for(var/obj/thing in objects)
|
|
if(thing.type in obj_blacklist)
|
|
continue
|
|
var/metadata = generate_tgm_metadata(thing, vars_to_save)
|
|
current_header += "[empty?"":",\n"][thing.type][metadata]"
|
|
empty = FALSE
|
|
//====SAVING SPECIAL DATA====
|
|
//This is what causes lockers and machines to save stuff inside of them
|
|
if(save_flag & SAVE_OBJECT_PROPERTIES)
|
|
var/custom_data = thing.on_object_saved()
|
|
current_header += "[custom_data ? ",\n[custom_data]" : ""]"
|
|
//====SAVING MOBS====
|
|
if(save_flag & SAVE_MOBS)
|
|
for(var/mob/living/thing in objects)
|
|
if(istype(thing, /mob/living/carbon)) //Ignore people, but not animals
|
|
continue
|
|
var/metadata = generate_tgm_metadata(thing, vars_to_save)
|
|
current_header += "[empty?"":",\n"][thing.type][metadata]"
|
|
empty = FALSE
|
|
current_header += "[empty?"":",\n"][place],\n[location])\n"
|
|
//====Fill the contents file====
|
|
//Compression is done here
|
|
var/position_of_header = header_dat.Find(current_header)
|
|
if(position_of_header)
|
|
//If the header has already been saved, change the character to the other saved header
|
|
header_char = header_chars[position_of_header]
|
|
else
|
|
header += "\"[header_char]\" = [current_header]"
|
|
header_chars += header_char
|
|
header_dat += current_header
|
|
index ++
|
|
contents += "[header_char]\n"
|
|
contents += "\"}"
|
|
return "[header][contents]"
|
|
|
|
//Sorts maps in terms of their positions, so scrambled / odd shaped maps can be saved
|
|
/proc/sort_map(list/map, minx, miny, maxx, maxy)
|
|
var/width = maxx - minx + 1
|
|
var/height = maxy - miny + 1
|
|
var/allTurfs = new/list(width, height)
|
|
for(var/turf/place in map)
|
|
allTurfs[place.x - minx + 1][place.y - miny + 1] = place
|
|
return allTurfs
|
|
|
|
//vars_to_save = list() to save all vars
|
|
/proc/generate_tgm_metadata(atom/O, list/vars_to_save = list("pixel_x", "pixel_y", "dir", "name", "req_access", "req_access_txt", "piping_layer", "color", "icon_state", "pipe_color", "amount"))
|
|
var/dat = ""
|
|
var/data_to_add = list()
|
|
for(var/V in O.vars)
|
|
if(O.get_save_vars())
|
|
if(!(V in O.get_save_vars()))
|
|
continue
|
|
else
|
|
if(!(V in vars_to_save) && vars_to_save)
|
|
continue
|
|
var/value = O.vars[V]
|
|
if(!value)
|
|
continue
|
|
if(value == initial(O.vars[V]) || !issaved(O.vars[V]))
|
|
continue
|
|
var/symbol = ""
|
|
if(istext(value))
|
|
symbol = "\""
|
|
value = sanitize_simple(value, list("{"="", "}"="", "\""="", ";"="", ","=""))
|
|
else if(isicon(value) || isfile(value))
|
|
symbol = "'"
|
|
else if(!(isnum(value) || ispath(value)))
|
|
continue
|
|
//Prevent symbols from being because otherwise you can name something [";},/obj/item/gun/energy/laser/instakill{name="da epic gun] and spawn yourself an instakill gun.
|
|
data_to_add += "[V] = [symbol][value][symbol]"
|
|
//Process data to add
|
|
var/first = TRUE
|
|
for(var/data in data_to_add)
|
|
dat += "[first ? "" : ";\n"]\t[data]"
|
|
first = FALSE
|
|
if(dat)
|
|
dat = "{\n[dat]\n\t}"
|
|
return dat
|
|
|
|
/proc/calculate_tgm_header_index(index, layers)
|
|
var/output = ""
|
|
for(var/i in 1 to layers)
|
|
var/l = GLOB.save_file_chars.len
|
|
var/c = FLOOR((index-1) / (l ** (i - 1)), 1)
|
|
c = (c % l) + 1
|
|
output = "[GLOB.save_file_chars[c]][output]"
|
|
return output
|