diff --git a/code/__defines/map.dm b/code/__defines/map.dm index fce63b9955c..6ae9fd7c6f4 100644 --- a/code/__defines/map.dm +++ b/code/__defines/map.dm @@ -7,6 +7,7 @@ #define MAP_LEVEL_EMPTY 0x020 // Empty Z-levels that may be used for various things (currently used by bluespace jump) #define MAP_LEVEL_CONSOLES 0x040 // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset. #define MAP_LEVEL_XENOARCH_EXEMPT 0x080 // Z-levels exempt from xenoarch digsite generation. +#define MAP_LEVEL_PERSIST 0x100 // Z-levels where SSpersistence should persist between rounds // Misc map defines. #define SUBMAP_MAP_EDGE_PAD 8 // Automatically created submaps are forbidden from being this close to the main map's edge. //VOREStation Edit \ No newline at end of file diff --git a/code/controllers/subsystems/persistence.dm b/code/controllers/subsystems/persistence.dm index 8ea9e0b5829..1667aa83203 100644 --- a/code/controllers/subsystems/persistence.dm +++ b/code/controllers/subsystems/persistence.dm @@ -32,8 +32,7 @@ SUBSYSTEM_DEF(persistence) if(!A || (A.flags & AREA_FLAG_IS_NOT_PERSISTENT)) return -// if((!T.z in GLOB.using_map.station_levels) || !initialized) - if(!(T.z in using_map.station_levels)) + if(!(T.z in using_map.persist_levels)) return if(!tracking_values[track_type]) diff --git a/code/modules/food/kitchen/smartfridge/engineering.dm b/code/modules/food/kitchen/smartfridge/engineering.dm index 22983611f03..8141ca496df 100644 --- a/code/modules/food/kitchen/smartfridge/engineering.dm +++ b/code/modules/food/kitchen/smartfridge/engineering.dm @@ -9,6 +9,9 @@ /obj/machinery/smartfridge/sheets/persistent persistent = /datum/persistent/storage/smartfridge/sheet_storage +/obj/machinery/smartfridge/sheets/persistent_lossy + persistent = /datum/persistent/storage/smartfridge/sheet_storage/lossy + /obj/machinery/smartfridge/sheets/accept_check(var/obj/item/O) return istype(O, /obj/item/stack/material) diff --git a/code/modules/food/kitchen/smartfridge/hydroponics.dm b/code/modules/food/kitchen/smartfridge/hydroponics.dm index ebf706c25ef..9b50136d796 100644 --- a/code/modules/food/kitchen/smartfridge/hydroponics.dm +++ b/code/modules/food/kitchen/smartfridge/hydroponics.dm @@ -1,5 +1,5 @@ /obj/machinery/smartfridge/produce - name = "\improper SmartFridge" + name = "\improper Smart Produce Storage" desc = "For storing all sorts of perishable foods!" icon = 'icons/obj/vending.dmi' icon_state = "fridge_food" @@ -9,6 +9,9 @@ /obj/machinery/smartfridge/produce/persistent persistent = /datum/persistent/storage/smartfridge/produce +/obj/machinery/smartfridge/produce/persistent_lossy + persistent = /datum/persistent/storage/smartfridge/produce/lossy + /obj/machinery/smartfridge/produce/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/)) return TRUE diff --git a/code/modules/food/kitchen/smartfridge/smartfridge.dm b/code/modules/food/kitchen/smartfridge/smartfridge.dm index 2475d4caef5..f43c9c49317 100644 --- a/code/modules/food/kitchen/smartfridge/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge/smartfridge.dm @@ -32,8 +32,8 @@ icon_base = "fridge_sci" icon_contents = "chem" -/obj/machinery/smartfridge/New() - ..() +/obj/machinery/smartfridge/Initialize() + . = ..() if(persistent) SSpersistence.track_value(src, persistent) if(is_secure) diff --git a/code/modules/persistence/datum/persistence_datum.dm b/code/modules/persistence/datum/persistence_datum.dm index 8499caef0bb..a52b57fb66c 100644 --- a/code/modules/persistence/datum/persistence_datum.dm +++ b/code/modules/persistence/datum/persistence_datum.dm @@ -5,13 +5,9 @@ /datum/persistent var/name = null var/filename - var/tokens_per_line var/entries_expire_at // Set in rounds, this controls when the item is finally removed permanently regardless if cleaned or not. var/entries_decay_at // Set in rounds. This controls when item messages start getting scrambled. var/entry_decay_weight = 0.5 - var/file_entry_split_character = "\t" - var/file_entry_substitute_character = " " - var/file_line_split_character = "\n" var/has_admin_data /datum/persistent/New() @@ -20,65 +16,57 @@ /datum/persistent/proc/SetFilename() if(name) - filename = "data/persistent/[lowertext(using_map.name)]-[lowertext(name)].txt" + filename = "data/persistent/[lowertext(using_map.name)]-[lowertext(name)].json" if(!isnull(entries_decay_at) && !isnull(entries_expire_at)) entries_decay_at = round(entries_expire_at * entries_decay_at) -// Reads in list of text tokens taken from file, generates labelled list of actual token values -/datum/persistent/proc/LabelTokens(var/list/tokens) - var/list/labelled_tokens = list() - labelled_tokens["x"] = text2num(tokens[1]) - labelled_tokens["y"] = text2num(tokens[2]) - labelled_tokens["z"] = text2num(tokens[3]) - labelled_tokens["age"] = text2num(tokens[4]) - return labelled_tokens - -/datum/persistent/proc/GetValidTurf(var/turf/T, var/list/tokens) - if(T && CheckTurfContents(T, tokens)) +/datum/persistent/proc/GetValidTurf(var/turf/T, var/list/token) + if(T && CheckTurfContents(T, token)) return T -/datum/persistent/proc/CheckTurfContents(var/turf/T, var/list/tokens) +/datum/persistent/proc/CheckTurfContents(var/turf/T, var/list/token) return TRUE -/datum/persistent/proc/CheckTokenSanity(var/list/tokens) +/datum/persistent/proc/CheckTokenSanity(var/list/token) return ( \ - !isnull(tokens["x"]) && \ - !isnull(tokens["y"]) && \ - !isnull(tokens["z"]) && \ - !isnull(tokens["age"]) && \ - tokens["age"] <= entries_expire_at \ + !isnull(token["x"]) && \ + !isnull(token["y"]) && \ + !isnull(token["z"]) && \ + !isnull(token["age"]) && \ + token["age"] <= entries_expire_at \ ) // Restores saved data to world -/datum/persistent/proc/CreateEntryInstance(var/turf/creating, var/list/tokens) +/datum/persistent/proc/CreateEntryInstance(var/turf/creating, var/list/token) return /datum/persistent/proc/ProcessAndApplyTokens(var/list/tokens) // If it's old enough we start to trim down any textual information and scramble strings. - if(tokens["message"] && !isnull(entries_decay_at) && !isnull(entry_decay_weight)) - var/_n = tokens["age"] - var/_message = tokens["message"] - if(_n >= entries_decay_at) - var/decayed_message = "" - for(var/i = 1 to length(_message)) - var/char = copytext(_message, i, i + 1) - if(prob(round(_n * entry_decay_weight))) - if(prob(99)) - decayed_message += pick(".",",","-","'","\\","/","\"",":",";") - else - decayed_message += char - _message = decayed_message - if(length(_message)) - tokens["message"] = _message - else - return + for(var/list/token in tokens) + if(token["message"] && !isnull(entries_decay_at) && !isnull(entry_decay_weight)) + var/_n = token["age"] + var/_message = token["message"] + if(_n >= entries_decay_at) + var/decayed_message = "" + for(var/i = 1 to length(_message)) + var/char = copytext(_message, i, i + 1) + if(prob(round(_n * entry_decay_weight))) + if(prob(99)) + decayed_message += pick(".",",","-","'","\\","/","\"",":",";") + else + decayed_message += char + _message = decayed_message + if(length(_message)) + token["message"] = _message + else + return - var/_z = tokens["z"] - if(_z in using_map.station_levels) - . = GetValidTurf(locate(tokens["x"], tokens["y"], _z), tokens) - if(.) - CreateEntryInstance(., tokens) + var/_z = token["z"] + if(_z in using_map.station_levels) + . = GetValidTurf(locate(token["x"], token["y"], _z), token) + if(.) + CreateEntryInstance(., token) /datum/persistent/proc/IsValidEntry(var/atom/entry) if(!istype(entry)) @@ -99,46 +87,45 @@ /datum/persistent/proc/CompileEntry(var/atom/entry) var/turf/T = get_turf(entry) return list( - T.x, - T.y, - T.z, - GetEntryAge(entry) + "x" = T.x, + "y" = T.y, + "z" = T.z, + "age" = GetEntryAge(entry) ) /datum/persistent/proc/Initialize() if(fexists(filename)) - for(var/entry_line in file2list(filename, file_line_split_character)) - if(!entry_line) - continue - var/list/tokens = splittext(entry_line, file_entry_split_character) - if(LAZYLEN(tokens) < tokens_per_line) - continue - tokens = LabelTokens(tokens) - if(!CheckTokenSanity(tokens)) - continue - ProcessAndApplyTokens(tokens) + var/list/tokens = json_decode(file2text(filename)) + for(var/list/token in tokens) + if(!CheckTokenSanity(token)) + tokens -= token + ProcessAndApplyTokens(tokens) /datum/persistent/proc/Shutdown() if(fexists(filename)) fdel(filename) - var/write_file = file(filename) + + var/list/to_store = list() for(var/thing in SSpersistence.tracking_values[type]) - if(IsValidEntry(thing)) - var/list/entry = CompileEntry(thing) - if(tokens_per_line == PERSISTENCE_VARIABLE_TOKEN_LENGTH || \ - LAZYLEN(entry) == tokens_per_line) - for(var/i = 1 to LAZYLEN(entry)) - if(istext(entry[i])) - entry[i] = replacetext(entry[i], file_entry_split_character, file_entry_substitute_character) - to_file(write_file, jointext(entry, file_entry_split_character)) + if(!IsValidEntry(thing)) + continue + to_store[++to_store.len] = CompileEntry(thing) + + if(to_store.len) + to_file(file(filename), json_encode(to_store)) /datum/persistent/proc/RemoveValue(var/atom/value) qdel(value) /datum/persistent/proc/GetAdminSummary(var/mob/user, var/can_modify) + var/list/my_tracks = SSpersistence.tracking_values[type] + if(!my_tracks?.len) + return + . = list("[capitalize(name)]") . += "
" - for(var/thing in SSpersistence.tracking_values[type]) + + for(var/thing in my_tracks) . += "[GetAdminDataStringFor(thing, can_modify, user)]" . += "
" diff --git a/code/modules/persistence/effects/filth.dm b/code/modules/persistence/effects/filth.dm index 10b304e18ee..166c374c02c 100644 --- a/code/modules/persistence/effects/filth.dm +++ b/code/modules/persistence/effects/filth.dm @@ -1,26 +1,22 @@ /datum/persistent/filth name = "filth" - tokens_per_line = 5 entries_expire_at = 4 // 4 rounds, 24 hours. -/datum/persistent/filth/LabelTokens(var/list/tokens) - var/list/labelled_tokens = ..() - labelled_tokens["path"] = text2path(tokens[LAZYLEN(labelled_tokens)+1]) - return labelled_tokens - /datum/persistent/filth/IsValidEntry(var/atom/entry) . = ..() && entry.invisibility == 0 -/datum/persistent/filth/CheckTokenSanity(var/list/tokens) - return ..() && ispath(tokens["path"]) +/datum/persistent/filth/CheckTokenSanity(var/list/token) + // byond's json implementation is "questionable", and uses types as keys and values without quotes sometimes even though they aren't valid json + token["path"] = istext(token["path"]) ? text2path(token["path"]) : token["path"] + return ..() && ispath(token["path"]) -/datum/persistent/filth/CheckTurfContents(var/turf/T, var/list/tokens) - var/_path = tokens["path"] +/datum/persistent/filth/CheckTurfContents(var/turf/T, var/list/token) + var/_path = token["path"] return (locate(_path) in T) ? FALSE : TRUE -/datum/persistent/filth/CreateEntryInstance(var/turf/creating, var/list/tokens) - var/_path = tokens["path"] - new _path(creating, tokens["age"]+1) +/datum/persistent/filth/CreateEntryInstance(var/turf/creating, var/list/token) + var/_path = token["path"] + new _path(creating, token["age"]+1) /datum/persistent/filth/GetEntryAge(var/atom/entry) var/obj/effect/decal/cleanable/filth = entry @@ -32,4 +28,4 @@ /datum/persistent/filth/CompileEntry(var/atom/entry) . = ..() - LAZYADD(., "[GetEntryPath(entry)]") \ No newline at end of file + LAZYADDASSOC(., "path", "[GetEntryPath(entry)]") \ No newline at end of file diff --git a/code/modules/persistence/effects/graffiti.dm b/code/modules/persistence/effects/graffiti.dm index 6ca1bbf32f9..369f9156b49 100644 --- a/code/modules/persistence/effects/graffiti.dm +++ b/code/modules/persistence/effects/graffiti.dm @@ -1,22 +1,14 @@ /datum/persistent/graffiti name = "graffiti" - tokens_per_line = 6 entries_expire_at = 4 // This previously was at 50 rounds??? Over 10 days. has_admin_data = TRUE -/datum/persistent/graffiti/LabelTokens(var/list/tokens) - var/list/labelled_tokens = ..() - var/entries = LAZYLEN(labelled_tokens) - labelled_tokens["author"] = tokens[entries+1] - labelled_tokens["message"] = tokens[entries+2] - return labelled_tokens - -/datum/persistent/graffiti/GetValidTurf(var/turf/T, var/list/tokens) +/datum/persistent/graffiti/GetValidTurf(var/turf/T, var/list/token) var/turf/checking_turf = ..() if(istype(checking_turf) && checking_turf.can_engrave()) return checking_turf -/datum/persistent/graffiti/CheckTurfContents(var/turf/T, var/list/tokens) +/datum/persistent/graffiti/CheckTurfContents(var/turf/T, var/list/token) var/too_much_graffiti = 0 for(var/obj/effect/decal/writing/W in .) too_much_graffiti++ @@ -24,8 +16,10 @@ return FALSE return TRUE -/datum/persistent/graffiti/CreateEntryInstance(var/turf/creating, var/list/tokens) - new /obj/effect/decal/writing(creating, tokens["age"]+1, tokens["message"], tokens["author"]) +/datum/persistent/graffiti/CreateEntryInstance(var/turf/creating, var/list/token) + var/obj/effect/decal/writing/inst = new /obj/effect/decal/writing(creating, token["age"]+1, token["message"], token["author"]) + if(token["icon_state"]) + inst.icon_state = token["icon_state"] /datum/persistent/graffiti/IsValidEntry(var/atom/entry) . = ..() @@ -40,8 +34,9 @@ /datum/persistent/graffiti/CompileEntry(var/atom/entry, var/write_file) . = ..() var/obj/effect/decal/writing/save_graffiti = entry - LAZYADD(., "[save_graffiti.author ? save_graffiti.author : "unknown"]") - LAZYADD(., "[save_graffiti.message]") + LAZYADDASSOC(., "author", "[save_graffiti.author ? save_graffiti.author : "unknown"]") + LAZYADDASSOC(., "message", "[save_graffiti.message]") + LAZYADDASSOC(., "icon_state", "[save_graffiti.icon_state]") /datum/persistent/graffiti/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) var/obj/effect/decal/writing/save_graffiti = thing diff --git a/code/modules/persistence/effects/paper.dm b/code/modules/persistence/effects/paper.dm index fe2ef813681..95968274eba 100644 --- a/code/modules/persistence/effects/paper.dm +++ b/code/modules/persistence/effects/paper.dm @@ -1,32 +1,24 @@ /datum/persistent/paper name = "paper" - tokens_per_line = 7 entries_expire_at = 50 has_admin_data = TRUE var/paper_type = /obj/item/weapon/paper var/requires_noticeboard = TRUE -/datum/persistent/paper/LabelTokens(var/list/tokens) - var/list/labelled_tokens = ..() - var/entries = LAZYLEN(labelled_tokens) - labelled_tokens["author"] = tokens[entries+1] - labelled_tokens["message"] = tokens[entries+2] - labelled_tokens["title"] = tokens[entries+3] - return labelled_tokens - -/datum/persistent/paper/CheckTurfContents(var/turf/T, var/list/tokens) +/datum/persistent/paper/CheckTurfContents(var/turf/T, var/list/token) if(requires_noticeboard && !(locate(/obj/structure/noticeboard) in T)) new /obj/structure/noticeboard(T) . = ..() -/datum/persistent/paper/CreateEntryInstance(var/turf/creating, var/list/tokens) +/datum/persistent/paper/CreateEntryInstance(var/turf/creating, var/list/token) var/obj/structure/noticeboard/board = locate() in creating if(requires_noticeboard && LAZYLEN(board.notices) >= board.max_notices) return var/obj/item/weapon/paper/paper = new paper_type(creating) - paper.info = tokens["message"] - paper.name = tokens["title"] - paper.last_modified_ckey = tokens["author"] + paper.info = token["message"] + paper.name = token["title"] + paper.last_modified_ckey = token["author"] + paper.age = token["age"]+1 if(requires_noticeboard) board.add_paper(paper) if(!paper.was_maploaded) // If we were created/loaded when the map was made, skip us! @@ -40,9 +32,9 @@ /datum/persistent/paper/CompileEntry(var/atom/entry, var/write_file) . = ..() var/obj/item/weapon/paper/paper = entry - LAZYADD(., "[paper.last_modified_ckey ? paper.last_modified_ckey : "unknown"]") - LAZYADD(., "[paper.info]") - LAZYADD(., "[paper.name]") + LAZYADDASSOC(., "author", "[paper.last_modified_ckey ? paper.last_modified_ckey : "unknown"]") + LAZYADDASSOC(., "message", "[paper.info]") + LAZYADDASSOC(., "name", "[paper.name]") /datum/persistent/paper/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) var/obj/item/weapon/paper/paper = thing diff --git a/code/modules/persistence/effects/paper_sticky.dm b/code/modules/persistence/effects/paper_sticky.dm index 1871eb4a037..08fb9050093 100644 --- a/code/modules/persistence/effects/paper_sticky.dm +++ b/code/modules/persistence/effects/paper_sticky.dm @@ -2,27 +2,22 @@ name = "stickynotes" paper_type = /obj/item/weapon/paper/sticky requires_noticeboard = FALSE - tokens_per_line = 10 -/datum/persistent/paper/sticky/LabelTokens(var/list/tokens) - var/list/labelled_tokens = ..() - var/entries = LAZYLEN(labelled_tokens) - labelled_tokens["offset_x"] = tokens[entries+1] - labelled_tokens["offset_y"] = tokens[entries+2] - labelled_tokens["color"] = tokens[entries+3] - return labelled_tokens - -/datum/persistent/paper/sticky/CreateEntryInstance(var/turf/creating, var/list/tokens) +/datum/persistent/paper/sticky/CreateEntryInstance(var/turf/creating, var/list/token) var/atom/paper = ..() if(paper) - paper.pixel_x = text2num(tokens["offset_x"]) - paper.pixel_y = text2num(tokens["offset_y"]) - paper.color = tokens["color"] + if(prob(90)) + paper.pixel_x = token["offset_x"] + paper.pixel_y = token["offset_y"] + else + paper.pixel_x = rand(-5,5) + paper.pixel_y = rand(-5,5) + paper.color = token["color"] return paper /datum/persistent/paper/sticky/CompileEntry(var/atom/entry, var/write_file) . = ..() var/obj/item/weapon/paper/sticky/paper = entry - LAZYADD(., "[paper.pixel_x]") - LAZYADD(., "[paper.pixel_y]") - LAZYADD(., "[paper.color]") \ No newline at end of file + LAZYADDASSOC(., "offset_x", paper.pixel_x) + LAZYADDASSOC(., "offset_y", paper.pixel_y) + LAZYADDASSOC(., "color", paper.color) \ No newline at end of file diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/serialize.dm similarity index 100% rename from code/modules/persistence/persistence.dm rename to code/modules/persistence/serialize.dm diff --git a/code/modules/persistence/storage/smartfridge.dm b/code/modules/persistence/storage/smartfridge.dm index 0b2eee0ca1f..0584ab9e79e 100644 --- a/code/modules/persistence/storage/smartfridge.dm +++ b/code/modules/persistence/storage/smartfridge.dm @@ -6,9 +6,9 @@ for(var/datum/stored_item/I in entry.item_records) .[I.item_path] = I.get_amount() -/datum/persistent/storage/smartfridge/CreateEntryInstance(var/turf/creating, var/list/tokens) +/datum/persistent/storage/smartfridge/CreateEntryInstance(var/turf/creating, var/list/token) var/obj/machinery/smartfridge/S = find_specific_instance(creating) - var/list/L = generate_items(tokens["items"]) + var/list/L = generate_items(token["items"]) for(var/atom/A in L) if(S.accept_check(A)) S.stock(A) @@ -20,50 +20,64 @@ /datum/persistent/storage/smartfridge/sheet_storage - name = "sheet_storage" + name = "sheet storage" max_storage = 50 store_per_type = TRUE target_type = /obj/machinery/smartfridge/sheets + var/stacks_go_missing = FALSE // Variable rate depletion of stacks inter-round + +/datum/persistent/storage/smartfridge/sheet_storage/lossy + name = "sheet storage lossy" + max_storage = 250 + stacks_go_missing = TRUE + /datum/persistent/storage/smartfridge/sheet_storage/generate_items(var/list/L) . = list() for(var/obj/item/stack/material/S as anything in L) - if(!ispath(S, /obj/item/stack/material)) + var/real_path = istext(S) ? text2path(S) : S + if(!ispath(real_path, /obj/item/stack/material)) log_debug("Warning: Sheet_storage persistent datum tried to create [S]") continue - var/count = L[S] - while(count > 0) - S = new S - S.amount = min(count, S.get_max_amount()) - count -= S.get_amount() - . += S + // Skip entire stack if we hit the chance + if(prob(go_missing_chance)) + continue + var/count = L[S] + + var/obj/item/stack/material/inst = real_path + var/max_amount = initial(inst.max_amount) + + // Delete some stacks if we want + if(stacks_go_missing) + var/fuzzy = rand(-5,5) + switch(count / max_amount) + if(0 to 1) + count -= 10+fuzzy // 1 stack or less, lose 10 + if(1 to 10) + count -= max_amount+fuzzy // 1 to 10 stacks, lose a stack + if(10 to INFINITY) + count -= max_amount*3+fuzzy // 10+ stacks, lose 3 stacks + if(count <= 0) + continue + + while(count > 0) + inst = new real_path + inst.amount = min(count, max_amount) + count -= inst.get_amount() + . += inst /datum/persistent/storage/smartfridge/produce - name = "fruit_storage" + name = "fruit storage" max_storage = 50 store_per_type = FALSE target_type = /obj/machinery/smartfridge/produce -/datum/persistent/storage/smartfridge/produce/assemble_token(var/T) - var/list/subtok = splittext(T, " ") - if(subtok.len != 2) - return null - - if(!istype(SSplants)) // No seed controller means the fruit will come out all wonky if at all - return null - - subtok[2] = text2num(subtok[2]) - - // Ensure we've found a token describing the quantity of a path - if(subtok.len != 2 || \ - !istype(SSplants.seeds[subtok[1]], /datum/seed) || \ - !isnum(subtok[2])) - return null - - return subtok +/datum/persistent/storage/smartfridge/produce/lossy + name = "fruit storage lossy" + go_missing_chance = 12.5 // 10% loss between rounds /datum/persistent/storage/smartfridge/produce/create_item(var/seedtype) return new /obj/item/weapon/reagent_containers/food/snacks/grown(null, seedtype) // Smartfridge will be stock()ed with it, loc is unimportant @@ -74,6 +88,8 @@ . = list() for(var/datum/stored_item/I in entry.item_records) + if(prob(go_missing_chance)) + continue if(LAZYLEN(I.instances)) var/obj/item/weapon/reagent_containers/food/snacks/grown/G = I.instances[1] if(!istype(G)) diff --git a/code/modules/persistence/storage/storage.dm b/code/modules/persistence/storage/storage.dm index 9ad9531cbc7..58b23a6ce94 100644 --- a/code/modules/persistence/storage/storage.dm +++ b/code/modules/persistence/storage/storage.dm @@ -1,40 +1,23 @@ /datum/persistent/storage + name = "storage" entries_expire_at = 1 + has_admin_data = TRUE + + // Don't use these for storage persistence. If someone takes some sheets out and puts them back in mixed in with + // new sheets, how do you know the age of the stack? If you want sheets to 'decay', see go_missing_chance entries_decay_at = 0 entry_decay_weight = 0 - tokens_per_line = PERSISTENCE_VARIABLE_TOKEN_LENGTH - + // // // // + var/max_storage = 0 var/store_per_type = FALSE // If true, will store up to max_storage for each type stored var/target_type = null // Path of the thing that this expects to put stuff into + var/go_missing_chance = 0 // Chance an item will fail to be spawned in from persistence and need to be restocked + /datum/persistent/storage/SetFilename() if(name) - filename = "data/persistent/storage/[lowertext(using_map.name)]-[lowertext(name)].txt" - -/datum/persistent/storage/LabelTokens(var/list/tokens) - . = ..() - .["items"] = list() - for(var/T in tokens) - var/list/L = assemble_token(T) - if(LAZYLEN(L)) - .["items"][L[1]] = text2num(L[2]) - -/datum/persistent/storage/proc/assemble_token(var/T) - var/list/subtok = splittext(T, " ") - if(subtok.len != 2) - return null - - subtok[1] = text2path(subtok[1]) - subtok[2] = text2num( subtok[2]) - - // Ensure we've found a token describing the quantity of a path - if(subtok.len != 2 || \ - !ispath(subtok[1]) || \ - !isnum(subtok[2])) - return null - - return subtok + filename = "data/persistent/storage/[lowertext(using_map.name)]-[lowertext(name)].json" /datum/persistent/storage/IsValidEntry(var/atom/entry) return ..() && istype(entry, target_type) @@ -52,9 +35,8 @@ if(!store_per_type) stored = max(stored - item_list[item], 0) - for(var/item in storage_list) - . += "[item] [storage_list[item]]" - + LAZYADDASSOC(., "items", storage_list) + // Usage: returns list with structure: // list( // [type1] = [stored_quantity], @@ -66,16 +48,27 @@ /datum/persistent/storage/proc/find_specific_instance(var/turf/T) return locate(target_type) in T -/datum/persistent/storage/CheckTurfContents(var/turf/T, var/list/tokens) +/datum/persistent/storage/CheckTurfContents(var/turf/T, var/list/token) return istype(find_specific_instance(T), target_type) /datum/persistent/storage/proc/generate_items(var/list/L) . = list() for(var/path in L) + // byond's json implementation is "questionable", and uses types as keys and values without quotes sometimes even though they aren't valid json + var/real_path = istext(path) ? text2path(path) : path for(var/i in 1 to L[path]) - var/atom/A = create_item(path) + if(prob(go_missing_chance)) + continue + var/atom/A = create_item(real_path) if(!QDELETED(A)) . += A /datum/persistent/storage/proc/create_item(var/path) - return new path() \ No newline at end of file + return new path() + +/datum/persistent/storage/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) + var/atom/T = thing + if(!istype(T)) + return "" + else + . = "[T.name][T.x],[T.y],[T.z]" diff --git a/maps/offmap_vr/talon/talon_v2.dmm b/maps/offmap_vr/talon/talon_v2.dmm index 3c9382a08d8..1e00adc6a00 100644 --- a/maps/offmap_vr/talon/talon_v2.dmm +++ b/maps/offmap_vr/talon/talon_v2.dmm @@ -1679,6 +1679,18 @@ /obj/structure/closet/walllocker_double/hydrant/west, /turf/simulated/floor/tiled/techmaint, /area/talon_v2/secure_storage) +"fk" = ( +/obj/structure/railing/grey{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/smartfridge/sheets/persistent_lossy{ + layer = 3.3 + }, +/turf/simulated/floor/tiled/techfloor, +/area/talon_v2/refining) "fm" = ( /obj/machinery/door/airlock/glass_external{ req_one_access = list(301) @@ -26593,7 +26605,7 @@ ca rj fW jS -tX +fk VS mQ ZR diff --git a/maps/tether/submaps/_tether_submaps.dm b/maps/tether/submaps/_tether_submaps.dm index cbcc1b5c369..f1f848972c1 100644 --- a/maps/tether/submaps/_tether_submaps.dm +++ b/maps/tether/submaps/_tether_submaps.dm @@ -527,6 +527,6 @@ /datum/map_z_level/tether_lateload/talon_v2 name = "Talon" - flags = MAP_LEVEL_PLAYER + flags = MAP_LEVEL_PLAYER|MAP_LEVEL_PERSIST base_turf = /turf/space z = Z_LEVEL_OFFMAP1 \ No newline at end of file diff --git a/maps/tether/tether-03-surface3.dmm b/maps/tether/tether-03-surface3.dmm index 40b1869e5c4..dd0c6dd9ea1 100644 --- a/maps/tether/tether-03-surface3.dmm +++ b/maps/tether/tether-03-surface3.dmm @@ -18423,13 +18423,13 @@ /turf/simulated/floor/tiled, /area/tether/surfacebase/surface_three_hall) "aDX" = ( -/obj/machinery/smartfridge, /obj/effect/floor_decal/borderfloor{ dir = 1 }, /obj/effect/floor_decal/corner/lime/border{ dir = 1 }, +/obj/machinery/smartfridge/produce/persistent_lossy, /turf/simulated/floor/tiled, /area/tether/surfacebase/botanystorage) "aDY" = ( diff --git a/maps/tether/tether-07-station3.dmm b/maps/tether/tether-07-station3.dmm index 9717e5f4f0e..af77aa1e468 100644 --- a/maps/tether/tether-07-station3.dmm +++ b/maps/tether/tether-07-station3.dmm @@ -19061,7 +19061,6 @@ /turf/simulated/floor, /area/maintenance/station/ai) "aSj" = ( -/obj/structure/closet/emcloset, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, @@ -19534,6 +19533,7 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 }, +/obj/structure/closet/emcloset, /turf/simulated/floor/tiled, /area/quartermaster/belterdock) "aTA" = ( @@ -23197,6 +23197,10 @@ }, /turf/simulated/floor/tiled/eris/dark/techfloor, /area/shuttle/medivac/engines) +"kPM" = ( +/obj/machinery/smartfridge/sheets/persistent_lossy, +/turf/simulated/wall, +/area/quartermaster/belterdock) "kPW" = ( /obj/machinery/door/blast/regular{ dir = 4; @@ -38467,7 +38471,7 @@ aHB aMz aBU aBU -aQG +kPM aSj aOu aOL diff --git a/maps/tether/tether_areas.dm b/maps/tether/tether_areas.dm index 71c93bb2894..16ca71ce364 100644 --- a/maps/tether/tether_areas.dm +++ b/maps/tether/tether_areas.dm @@ -698,6 +698,7 @@ name = "\improper South Maintenance" /area/maintenance/lower/trash_pit name = "\improper Trash Pit" + flags = RAD_SHIELDED|AREA_FLAG_IS_NOT_PERSISTENT /area/maintenance/lower/solars name = "\improper Solars Maintenance" /area/maintenance/lower/mining_eva diff --git a/maps/tether/tether_defines.dm b/maps/tether/tether_defines.dm index 58a05910ab3..caab265dfb6 100644 --- a/maps/tether/tether_defines.dm +++ b/maps/tether/tether_defines.dm @@ -299,14 +299,14 @@ // We have a bunch of stuff common to the station z levels /datum/map_z_level/tether/station - flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_XENOARCH_EXEMPT + flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_XENOARCH_EXEMPT|MAP_LEVEL_PERSIST holomap_legend_x = 220 holomap_legend_y = 160 /datum/map_z_level/tether/station/surface_low z = Z_LEVEL_SURFACE_LOW name = "Surface 1" - flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT + flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT|MAP_LEVEL_PERSIST base_turf = /turf/simulated/floor/outdoors/rocks/virgo3b holomap_offset_x = TETHER_HOLOMAP_MARGIN_X holomap_offset_y = TETHER_HOLOMAP_MARGIN_Y + TETHER_MAP_SIZE*0 @@ -314,7 +314,7 @@ /datum/map_z_level/tether/station/surface_mid z = Z_LEVEL_SURFACE_MID name = "Surface 2" - flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT + flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT|MAP_LEVEL_PERSIST base_turf = /turf/simulated/open holomap_offset_x = TETHER_HOLOMAP_MARGIN_X holomap_offset_y = TETHER_HOLOMAP_MARGIN_Y + TETHER_MAP_SIZE*1 @@ -322,7 +322,7 @@ /datum/map_z_level/tether/station/surface_high z = Z_LEVEL_SURFACE_HIGH name = "Surface 3" - flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT + flags = MAP_LEVEL_STATION|MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_CONSOLES|MAP_LEVEL_SEALED|MAP_LEVEL_XENOARCH_EXEMPT|MAP_LEVEL_PERSIST base_turf = /turf/simulated/open holomap_offset_x = TETHER_HOLOMAP_MARGIN_X holomap_offset_y = TETHER_HOLOMAP_MARGIN_Y + TETHER_MAP_SIZE*2 @@ -359,11 +359,11 @@ /datum/map_z_level/tether/mine z = Z_LEVEL_SURFACE_MINE name = "Mining Outpost" - flags = MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_SEALED + flags = MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_SEALED|MAP_LEVEL_PERSIST base_turf = /turf/simulated/floor/outdoors/rocks/virgo3b /datum/map_z_level/tether/solars z = Z_LEVEL_SOLARS name = "Solar Field" - flags = MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_SEALED + flags = MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER|MAP_LEVEL_SEALED|MAP_LEVEL_PERSIST base_turf = /turf/simulated/floor/outdoors/rocks/virgo3b diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm index 07c72230261..b7a320a6bd7 100644 --- a/maps/~map_system/maps.dm +++ b/maps/~map_system/maps.dm @@ -33,6 +33,7 @@ var/list/all_maps = list() var/static/list/player_levels = list() // Z-levels a character can typically reach var/static/list/sealed_levels = list() // Z-levels that don't allow random transit at edge var/static/list/xenoarch_exempt_levels = list() //Z-levels exempt from xenoarch finds and digsites spawning. + var/static/list/persist_levels = list() // Z-levels where SSpersistence should persist between rounds. Defaults to station_levels if unset. var/static/list/empty_levels = null // Empty Z-levels that may be used for various things (currently used by bluespace jump) // End Static Lists @@ -135,8 +136,10 @@ var/list/all_maps = list() if(zlevel_datum_type) for(var/type in subtypesof(zlevel_datum_type)) new type(src) - if(!map_levels) + if(!map_levels?.len) map_levels = station_levels.Copy() + if(!persist_levels?.len) + persist_levels = station_levels.Copy() if(!allowed_jobs || !allowed_jobs.len) allowed_jobs = subtypesof(/datum/job) if(default_skybox) //Type was specified @@ -295,6 +298,7 @@ var/list/all_maps = list() if(flags & MAP_LEVEL_PLAYER) map.player_levels += z if(flags & MAP_LEVEL_SEALED) map.sealed_levels += z if(flags & MAP_LEVEL_XENOARCH_EXEMPT) map.xenoarch_exempt_levels += z + if(flags & MAP_LEVEL_PERSIST) map.persist_levels += z if(flags & MAP_LEVEL_EMPTY) if(!map.empty_levels) map.empty_levels = list() map.empty_levels += z diff --git a/vorestation.dme b/vorestation.dme index ee0866c3ffd..9b7f6c7107b 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -68,7 +68,6 @@ #include "code\__defines\objects.dm" #include "code\__defines\overmap.dm" #include "code\__defines\pda.dm" -#include "code\__defines\persistence.dm" #include "code\__defines\planets.dm" #include "code\__defines\planets_vr.dm" #include "code\__defines\plants.dm" @@ -3287,7 +3286,7 @@ #include "code\modules\persistence\filth.dm" #include "code\modules\persistence\graffiti.dm" #include "code\modules\persistence\noticeboard.dm" -#include "code\modules\persistence\persistence.dm" +#include "code\modules\persistence\serialize.dm" #include "code\modules\persistence\datum\persistence_datum.dm" #include "code\modules\persistence\effects\filth.dm" #include "code\modules\persistence\effects\graffiti.dm"