Files
Bubberstation/code/controllers/subsystem/persistence.dm
AnturK 8b1ffd1e49 Adds explorer drones / adventures. (#57851)
A new side-job for cargo. Prepare and launch exploration drones at distant sites to encounter weird adventures, collect loot and expand the station cargo network. Adventures - the main content type here, can be written by anyone without any knowledge of programming. The purpose here is creating an way of interesting way of delivering lore (and adding some new job content/cargo expansion method).

Co-authored-by: tralezab <40974010+tralezab@users.noreply.github.com>
Co-authored-by: EOBGames <58124831+EOBGames@users.noreply.github.com>
Co-authored-by: Fikou <piotrbryla@onet.pl>
Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
2021-03-26 11:38:39 +02:00

414 lines
13 KiB
Plaintext

#define FILE_ANTAG_REP "data/AntagReputation.json"
#define FILE_RECENT_MAPS "data/RecentMaps.json"
#define KEEP_ROUNDS_MAP 3
SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
flags = SS_NO_FIRE
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
var/list/saved_modes = list(1,2,3)
var/list/saved_maps = list()
var/list/blocked_maps = list()
var/list/saved_trophies = list()
var/list/antag_rep = list()
var/list/antag_rep_change = list()
var/list/picture_logging_information = list()
var/list/obj/structure/sign/picture_frame/photo_frames
var/list/obj/item/storage/photo_album/photo_albums
var/list/obj/structure/sign/painting/painting_frames = list()
var/list/paintings = list()
/datum/controller/subsystem/persistence/Initialize()
LoadPoly()
LoadChiselMessages()
LoadTrophies()
LoadRecentModes()
LoadRecentMaps()
LoadPhotoPersistence()
if(CONFIG_GET(flag/use_antag_rep))
LoadAntagReputation()
LoadRandomizedRecipes()
LoadPaintings()
GLOB.explorer_drone_adventures = load_adventures()
return ..()
/datum/controller/subsystem/persistence/proc/LoadPoly()
for(var/mob/living/simple_animal/parrot/poly/P in GLOB.alive_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
/datum/controller/subsystem/persistence/proc/LoadChiselMessages()
var/list/saved_messages = list()
if(fexists("data/npc_saves/ChiselMessages.sav")) //legacy compatability to convert old format to new
var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
var/saved_json
chisel_messages_sav[SSmapping.config.map_name] >> saved_json
if(!saved_json)
return
saved_messages = json_decode(saved_json)
fdel("data/npc_saves/ChiselMessages.sav")
else
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_messages = json["data"]
for(var/item in saved_messages)
if(!islist(item))
continue
var/xvar = item["x"]
var/yvar = item["y"]
var/zvar = item["z"]
if(!xvar || !yvar || !zvar)
continue
var/turf/T = locate(xvar, yvar, zvar)
if(!isturf(T))
continue
if(locate(/obj/structure/chisel_message) in T)
continue
var/obj/structure/chisel_message/M = new(T)
if(!QDELETED(M))
M.unpack(item)
log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
/datum/controller/subsystem/persistence/proc/LoadTrophies()
if(fexists("data/npc_saves/TrophyItems.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/TrophyItems.sav")
var/saved_json
S >> saved_json
if(!saved_json)
return
saved_trophies = json_decode(saved_json)
fdel("data/npc_saves/TrophyItems.sav")
else
var/json_file = file("data/npc_saves/TrophyItems.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_trophies = json["data"]
SetUpTrophies(saved_trophies.Copy())
/datum/controller/subsystem/persistence/proc/LoadRecentModes()
var/json_file = file("data/RecentModes.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_modes = json["data"]
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
var/map_sav = FILE_RECENT_MAPS
if(!fexists(FILE_RECENT_MAPS))
return
var/list/json = json_decode(file2text(map_sav))
if(!json)
return
saved_maps = json["data"]
//Convert the mapping data to a shared blocking list, saves us doing this in several places later.
for(var/map in config.maplist)
var/datum/map_config/VM = config.maplist[map]
var/run = 0
if(VM.map_name == SSmapping.config.map_name)
run++
for(var/name in SSpersistence.saved_maps)
if(VM.map_name == name)
run++
if(run >= 2) //If run twice in the last KEEP_ROUNDS_MAP + 1 (including current) rounds, disable map for voting and rotation.
blocked_maps += VM.map_name
/datum/controller/subsystem/persistence/proc/LoadAntagReputation()
var/json = file2text(FILE_ANTAG_REP)
if(!json)
var/json_file = file(FILE_ANTAG_REP)
if(!fexists(json_file))
WARNING("Failed to load antag reputation. File likely corrupt.")
return
return
antag_rep = json_decode(json)
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
for(var/A in GLOB.trophy_cases)
var/obj/structure/displaycase/trophy/T = A
if (T.showpiece)
continue
T.added_roundstart = TRUE
var/trophy_data = pick_n_take(trophy_items)
if(!islist(trophy_data))
continue
var/list/chosen_trophy = trophy_data
if(!length(chosen_trophy)) //Malformed
continue
var/path = text2path(chosen_trophy["path"]) //If the item no longer exist, this returns null
if(!path)
continue
T.showpiece = new /obj/item/showpiece_dummy(T, path)
T.trophy_message = chosen_trophy["message"]
T.placer_key = chosen_trophy["placer_key"]
T.update_appearance()
/datum/controller/subsystem/persistence/proc/CollectData()
CollectChiselMessages()
CollectTrophies()
CollectRoundtype()
CollectMaps()
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
if(CONFIG_GET(flag/use_antag_rep))
CollectAntagReputation()
SaveRandomizedRecipes()
SavePaintings()
SaveScars()
/datum/controller/subsystem/persistence/proc/GetPhotoAlbums()
var/album_path = file("data/photo_albums.json")
if(fexists(album_path))
return json_decode(file2text(album_path))
/datum/controller/subsystem/persistence/proc/GetPhotoFrames()
var/frame_path = file("data/photo_frames.json")
if(fexists(frame_path))
return json_decode(file2text(frame_path))
/datum/controller/subsystem/persistence/proc/LoadPhotoPersistence()
var/album_path = file("data/photo_albums.json")
var/frame_path = file("data/photo_frames.json")
if(fexists(album_path))
var/list/json = json_decode(file2text(album_path))
if(json.len)
for(var/i in photo_albums)
var/obj/item/storage/photo_album/A = i
if(!A.persistence_id)
continue
if(json[A.persistence_id])
A.populate_from_id_list(json[A.persistence_id])
if(fexists(frame_path))
var/list/json = json_decode(file2text(frame_path))
if(json.len)
for(var/i in photo_frames)
var/obj/structure/sign/picture_frame/PF = i
if(!PF.persistence_id)
continue
if(json[PF.persistence_id])
PF.load_from_id(json[PF.persistence_id])
/datum/controller/subsystem/persistence/proc/SavePhotoPersistence()
var/album_path = file("data/photo_albums.json")
var/frame_path = file("data/photo_frames.json")
var/list/frame_json = list()
var/list/album_json = list()
if(fexists(album_path))
album_json = json_decode(file2text(album_path))
fdel(album_path)
for(var/i in photo_albums)
var/obj/item/storage/photo_album/A = i
if(!istype(A) || !A.persistence_id)
continue
var/list/L = A.get_picture_id_list()
album_json[A.persistence_id] = L
album_json = json_encode(album_json)
WRITE_FILE(album_path, album_json)
if(fexists(frame_path))
frame_json = json_decode(file2text(frame_path))
fdel(frame_path)
for(var/i in photo_frames)
var/obj/structure/sign/picture_frame/F = i
if(!istype(F) || !F.persistence_id)
continue
frame_json[F.persistence_id] = F.get_photo_id()
frame_json = json_encode(frame_json)
WRITE_FILE(frame_path, frame_json)
/datum/controller/subsystem/persistence/proc/CollectChiselMessages()
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
for(var/obj/structure/chisel_message/M in chisel_messages)
saved_messages += list(M.pack())
log_world("Saved [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
var/list/file_data = list()
file_data["data"] = saved_messages
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/SaveChiselMessage(obj/structure/chisel_message/M)
saved_messages += list(M.pack()) // dm eats one list
/datum/controller/subsystem/persistence/proc/CollectTrophies()
var/json_file = file("data/npc_saves/TrophyItems.json")
var/list/file_data = list()
file_data["data"] = remove_duplicate_trophies(saved_trophies)
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/remove_duplicate_trophies(list/trophies)
var/list/ukeys = list()
. = list()
for(var/trophy in trophies)
var/tkey = "[trophy["path"]]-[trophy["message"]]"
if(ukeys[tkey])
continue
else
. += list(trophy)
ukeys[tkey] = TRUE
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
if(!T.added_roundstart && T.showpiece)
var/list/data = list()
data["path"] = T.showpiece.type
data["message"] = T.trophy_message
data["placer_key"] = T.placer_key
saved_trophies += list(data)
/datum/controller/subsystem/persistence/proc/CollectRoundtype()
saved_modes[3] = saved_modes[2]
saved_modes[2] = saved_modes[1]
saved_modes[1] = SSticker.mode.config_tag
var/json_file = file("data/RecentModes.json")
var/list/file_data = list()
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectMaps()
if(length(saved_maps) > KEEP_ROUNDS_MAP) //Get rid of extras from old configs.
saved_maps.Cut(KEEP_ROUNDS_MAP+1)
var/mapstosave = min(length(saved_maps)+1, KEEP_ROUNDS_MAP)
if(length(saved_maps) < mapstosave) //Add extras if too short, one per round.
saved_maps += mapstosave
for(var/i = mapstosave; i > 1; i--)
saved_maps[i] = saved_maps[i-1]
saved_maps[1] = SSmapping.config.map_name
var/json_file = file(FILE_RECENT_MAPS)
var/list/file_data = list()
file_data["data"] = saved_maps
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectAntagReputation()
var/ANTAG_REP_MAXIMUM = CONFIG_GET(number/antag_rep_maximum)
for(var/p_ckey in antag_rep_change)
// var/start = antag_rep[p_ckey]
antag_rep[p_ckey] = max(0, min(antag_rep[p_ckey]+antag_rep_change[p_ckey], ANTAG_REP_MAXIMUM))
// WARNING("AR_DEBUG: [p_ckey]: Committed [antag_rep_change[p_ckey]] reputation, going from [start] to [antag_rep[p_ckey]]")
antag_rep_change = list()
fdel(FILE_ANTAG_REP)
text2file(json_encode(antag_rep), FILE_ANTAG_REP)
/datum/controller/subsystem/persistence/proc/LoadRandomizedRecipes()
var/json_file = file("data/RandomizedChemRecipes.json")
var/json
if(fexists(json_file))
json = json_decode(file2text(json_file))
for(var/randomized_type in subtypesof(/datum/chemical_reaction/randomized))
var/datum/chemical_reaction/randomized/R = new randomized_type
var/loaded = FALSE
if(R.persistent && json)
var/list/recipe_data = json["[R.type]"]
if(recipe_data)
if(R.LoadOldRecipe(recipe_data) && (daysSince(R.created) <= R.persistence_period))
loaded = TRUE
if(!loaded) //We do not have information for whatever reason, just generate new one
if(R.persistent)
log_game("Resetting persistent [randomized_type] random recipe.")
R.GenerateRecipe()
if(!R.HasConflicts()) //Might want to try again if conflicts happened in the future.
add_chemical_reaction(R)
else
log_game("Randomized recipe [randomized_type] resulted in conflicting recipes.")
/datum/controller/subsystem/persistence/proc/SaveRandomizedRecipes()
var/json_file = file("data/RandomizedChemRecipes.json")
var/list/file_data = list()
//asert globchems done
for(var/randomized_type in subtypesof(/datum/chemical_reaction/randomized))
var/datum/chemical_reaction/randomized/R = get_chemical_reaction(randomized_type) //ew, would be nice to add some simple tracking
if(R?.persistent)
var/recipe_data = list()
recipe_data["timestamp"] = R.created
recipe_data["required_reagents"] = R.required_reagents
recipe_data["required_catalysts"] = R.required_catalysts
recipe_data["required_temp"] = R.required_temp
recipe_data["is_cold_recipe"] = R.is_cold_recipe
recipe_data["results"] = R.results
recipe_data["required_container"] = "[R.required_container]"
file_data["[R.type]"] = recipe_data
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/LoadPaintings()
var/json_file = file("data/paintings.json")
if(fexists(json_file))
paintings = json_decode(file2text(json_file))
for(var/obj/structure/sign/painting/P in painting_frames)
P.load_persistent()
/datum/controller/subsystem/persistence/proc/SavePaintings()
for(var/obj/structure/sign/painting/P in painting_frames)
P.save_persistent()
var/json_file = file("data/paintings.json")
fdel(json_file)
WRITE_FILE(json_file, json_encode(paintings))
/datum/controller/subsystem/persistence/proc/SaveScars()
for(var/i in GLOB.joined_player_list)
var/mob/living/carbon/human/ending_human = get_mob_by_ckey(i)
if(!istype(ending_human) || !ending_human.mind?.original_character_slot_index || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars)
continue
var/mob/living/carbon/human/original_human = ending_human.mind.original_character
if(!original_human)
continue
if(original_human.stat == DEAD || !original_human.all_scars || original_human != ending_human)
original_human.save_persistent_scars(TRUE)
else
original_human.save_persistent_scars()