diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 76254b13c44..a8a33784263 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -419,3 +419,26 @@ var/global/list/bad_blocks[0]
unique_enzymes = md5(character.real_name)
reg_dna[unique_enzymes] = character.real_name
+
+// Hmm, I wonder how to go about this without a huge convention break
+/datum/dna/proc/serialize()
+ var/data = list()
+ data["UE"] = unique_enzymes
+ data["SE"] = SE.Copy() // This is probably too lazy for my own good
+ data["UI"] = UI.Copy()
+ data["species"] = species // This works because `species` is a string, not a datum
+ // Because old DNA coders were insane or something
+ data["b_type"] = b_type
+ data["real_name"] = real_name
+ return data
+
+/datum/dna/proc/deserialize(data)
+ unique_enzymes = data["UE"]
+ // The de-serializer is unlikely to tamper with the lists
+ SE = data["SE"]
+ UI = data["UI"]
+ UpdateUI()
+ UpdateSE()
+ species = data["species"]
+ b_type = data["b_type"]
+ real_name = data["real_name"]
\ No newline at end of file
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 90ad26039db..aed9270bf57 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -113,7 +113,8 @@ var/list/admin_verbs_event = list(
var/list/admin_verbs_spawn = list(
/datum/admins/proc/spawn_atom, /*allows us to spawn instances*/
- /client/proc/respawn_character
+ /client/proc/respawn_character,
+ /client/proc/admin_deserialize
)
var/list/admin_verbs_server = list(
/client/proc/ToRban,
@@ -161,7 +162,9 @@ var/list/admin_verbs_debug = list(
/proc/machine_upgrade,
/client/proc/map_template_load,
/client/proc/map_template_upload,
- /client/proc/view_runtimes
+ /client/proc/view_runtimes,
+ /client/proc/admin_serialize,
+ /client/proc/admin_deserialize
)
var/list/admin_verbs_possess = list(
/proc/possess,
diff --git a/code/modules/admin/verbs/serialization.dm b/code/modules/admin/verbs/serialization.dm
new file mode 100644
index 00000000000..41ba5d11aa1
--- /dev/null
+++ b/code/modules/admin/verbs/serialization.dm
@@ -0,0 +1,26 @@
+/client/proc/admin_serialize()
+ set name = "Serialize Marked Datum"
+ set desc = "Turns your marked object into a JSON string you can later use to re-create the object"
+ set category = "Debug"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ if(!istype(holder.marked_datum, /atom/movable))
+ to_chat(src, "The marked datum is not an atom/movable!")
+ return
+
+ var/atom/movable/AM = holder.marked_datum
+ to_chat(src, json_encode(AM.serialize()))
+
+/client/proc/admin_deserialize()
+ set name = "Deserialize JSON datum"
+ set desc = "Creates an object from a JSON string"
+ set category = "Debug"
+
+ if(!check_rights(R_ADMIN|R_DEBUG))
+ return
+
+ var/json_text = input("Enter the JSON code:","Text") as message|null
+ if(json_text)
+ json_to_object(json_text, get_turf(usr))
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f9c448df5e3..12cabc68a5f 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -8,7 +8,7 @@
//why are these here and not in human_defines.dm
//var/list/hud_list[10]
var/datum/species/species //Contains icon generation and language information, set during New().
- var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us.
+ var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us.
var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call.
/mob/living/carbon/human/New(var/new_loc, var/new_species = null, var/delay_ready_dna = 0)
@@ -482,8 +482,8 @@
if(3 to 4) stunprob = 30
if(5 to 6) stunprob = 40
if(7 to 8) stunprob = 60
- if(9) stunprob = 70
- if(10) stunprob = 95
+ if(9) stunprob = 70
+ if(10) stunprob = 95
if(prob(stunprob))
M.powerlevel -= 3
@@ -944,7 +944,7 @@
for(var/datum/data/record/R in data_core.security)
if(R.fields["id"] == E.fields["id"])
if(hasHUD(usr,"security"))
- var/t1 = sanitize(copytext(input("Add Comment:", "Sec. records", null, null) as message,1,MAX_MESSAGE_LEN))
+ var/t1 = sanitize(copytext(input("Add Comment:", "Sec. records", null, null) as message,1,MAX_MESSAGE_LEN))
if( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"security")) )
return
var/counter = 1
@@ -1075,7 +1075,7 @@
for(var/datum/data/record/R in data_core.medical)
if(R.fields["id"] == E.fields["id"])
if(hasHUD(usr,"medical"))
- var/t1 = sanitize(copytext(input("Add Comment:", "Med. records", null, null) as message,1,MAX_MESSAGE_LEN))
+ var/t1 = sanitize(copytext(input("Add Comment:", "Med. records", null, null) as message,1,MAX_MESSAGE_LEN))
if( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"medical")) )
return
var/counter = 1
@@ -1285,8 +1285,8 @@
var/limb_path = organ_data["path"]
var/obj/item/organ/external/O = new limb_path(temp_holder)
if(H.get_limb_by_name(O.name)) //Check to see if the user already has an limb with the same name as the 'missing limb'. If they do, skip regrowth.
- continue //In an example, this will prevent duplication of the mob's right arm if the mob is a Human and they have a Diona right arm, since,
- //while the limb with the name 'right_arm' the mob has may not be listed in their species' bodyparts definition, it is still viable and has the appropriate limb name.
+ continue //In an example, this will prevent duplication of the mob's right arm if the mob is a Human and they have a Diona right arm, since,
+ //while the limb with the name 'right_arm' the mob has may not be listed in their species' bodyparts definition, it is still viable and has the appropriate limb name.
else
O = new limb_path(H) //Create the limb on the player.
O.owner = H
@@ -1704,7 +1704,7 @@
if((head_organ.species.name in tmp_hair.species_allowed) && (robohead.company in tmp_hair.models_allowed)) //Populate the list of available monitor styles only with styles that the monitor-head is allowed to use.
hair += i
- var/new_style = input(src, "Select a monitor display", "Monitor Display", head_organ.h_style) as null|anything in hair
+ var/new_style = input(src, "Select a monitor display", "Monitor Display", head_organ.h_style) as null|anything in hair
if(incapacitated())
to_chat(src, "You were interrupted while changing your monitor display.")
return
@@ -1909,7 +1909,7 @@
if(current_size >= STAGE_THREE)
var/list/handlist = list(l_hand, r_hand)
for(var/obj/item/hand in handlist)
- if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand))
+ if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand))
step_towards(hand, src)
to_chat(src, "\The [S] pulls \the [hand] from your grip!")
apply_effect(current_size * 3, IRRADIATE)
@@ -2031,3 +2031,59 @@
return 0
return .
+
+/mob/living/carbon/human/serialize()
+ // Currently: Limbs/organs only
+ var/list/data = ..()
+ var/list/limbs_list = list()
+ var/list/organs_list = list()
+ data["limbs"] = limbs_list
+ data["iorgans"] = organs_list
+ data["dna"] = dna.serialize()
+ data["ushirt"] = undershirt
+ for(var/limb in organs_by_name)
+ var/obj/item/organ/O = organs_by_name[limb]
+ if(!O)
+ limbs_list[limb] = "missing"
+ continue
+
+ limbs_list[limb] = O.serialize()
+
+ for(var/organ in internal_organs)
+ var/obj/item/organ/O = organ
+ organs_list[O.name] = O.serialize()
+
+ return json_encode(data)
+
+/mob/living/carbon/human/deserialize(list/data)
+ var/list/limbs_list = data["limbs"]
+ var/list/organs_list = data["iorgans"]
+ if(!islist(data["limbs"]))
+ throw EXCEPTION("Expected a limbs list, but found none")
+
+ if(islist(data["dna"]))
+ dna.deserialize(data["dna"])
+ real_name = dna.real_name
+ name = real_name
+ UpdateAppearance()
+ set_species(dna.species)
+ undershirt = data["ushirt"]
+ for(var/obj/item/organ/internal/iorgan in internal_organs)
+ qdel(iorgan)
+
+ for(var/obj/item/organ/external/organ in organs)
+ qdel(organ)
+
+ for(var/limb in limbs_list)
+ // Missing means skip this part - it's missing
+ if(limbs_list[limb] == "missing")
+ continue
+ // "New" code handles insertion and DNA sync'ing
+ var/obj/item/organ/external/E = list_to_object(limbs_list[limb], src)
+ E.sync_colour_to_dna()
+
+ for(var/organ in organs_list)
+ // As above, "New" code handles insertion, DNA sync
+ list_to_object(organs_list[organ], src)
+ update_icons()
+ ..()
diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm
new file mode 100644
index 00000000000..cc5a7684947
--- /dev/null
+++ b/code/modules/persistence/persistence.dm
@@ -0,0 +1,38 @@
+/*
+* Returns a byond list that can be passed to the "deserialize" proc
+* to bring a new instance of this atom to its original state
+*
+* If we want to store this info, we can pass it to `json_encode` or some other
+* interface that suits our fancy, to make it into an easily-handled string
+*/
+/atom/movable/proc/serialize()
+ return list("type" = "[type]")
+
+/*
+* This is given the byond list from above, to bring this atom to the state
+* described in the list.
+* This will be called after `New` but before `initialize`, so linking and stuff
+* would probably be handled in `initialize`
+*
+* Also, this should only be called by `json_to_object` in persistence.dm - at least
+* with current plans - that way it can actually initialize the type from the list
+*/
+/atom/movable/proc/deserialize(var/data)
+ return
+
+/proc/json_to_object(var/json_data, var/loc)
+ var/data = json_decode(json_data)
+ return list_to_object(data, loc)
+
+/proc/list_to_object(var/list/data, var/loc)
+ if(!islist(data))
+ throw EXCEPTION("You didn't give me a list, bucko")
+ if(!("type" in data))
+ throw EXCEPTION("No 'type' field in the data")
+ var/path = text2path(data["type"])
+ if(!path)
+ throw EXCEPTION("Path not found: [path]")
+
+ var/atom/movable/thing = new path(loc)
+ thing.deserialize(data)
+ return thing
diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm
index 5e49a97fc59..b49ccc6a838 100644
--- a/code/modules/surgery/organs/organ.dm
+++ b/code/modules/surgery/organs/organ.dm
@@ -343,3 +343,32 @@ I use this so that this can be made better once the organ overhaul rolls out --
if(!istype(owner)) // You're not the primary organ of ANYTHING, bucko
return 0
return src == O.get_int_organ(organ_tag)
+
+/obj/item/organ/serialize()
+ var/data = ..()
+ if(status != 0)
+ data["status"] = status
+ if(robotic > 0)
+ data["robotic"] = robotic
+
+ // Save the DNA datum if: The owner doesn't exist, or the dna doesn't match
+ // the owner
+ if(!(owner && dna.unique_enzymes == owner.dna.unique_enzymes))
+ data["dna"] = dna.serialize()
+ return data
+
+/obj/item/organ/deserialize(var/data)
+ switch(data["robotic"])
+ if(1)
+ mechassist()
+ if(2)
+ robotize()
+ else
+ // Nothing
+ if(isnum(data["status"]))
+ status = data["status"]
+ if(islist(data["dna"]))
+ // The only thing the official proc does is
+ //instantiate the list and call this proc
+ dna.deserialize(data["dna"])
+ ..()
diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm
index 10d7fe724ea..1c3f05f5816 100644
--- a/code/modules/surgery/organs/organ_external.dm
+++ b/code/modules/surgery/organs/organ_external.dm
@@ -836,12 +836,7 @@ Note that amputating the affected organ does in fact remove the infection from t
..()
if(company && istext(company))
- model = company
- var/datum/robolimb/R = all_robolimbs[company]
- if(R)
- force_icon = R.icon
- name = "[R.company] [initial(name)]"
- desc = "[R.desc]"
+ set_company(company)
cannot_break = 1
get_icon()
@@ -849,6 +844,16 @@ Note that amputating the affected organ does in fact remove the infection from t
if(T)
T.robotize()
+
+
+/obj/item/organ/external/proc/set_company(var/company)
+ model = company
+ var/datum/robolimb/R = all_robolimbs[company]
+ if(R)
+ force_icon = R.icon
+ name = "[R.company] [initial(name)]"
+ desc = "[R.desc]"
+
/obj/item/organ/external/proc/mutate()
src.status |= ORGAN_MUTATED
if(owner) owner.update_body()
@@ -969,3 +974,19 @@ Note that amputating the affected organ does in fact remove the infection from t
continue
wounds -= W
qdel(W)
+
+
+/obj/item/organ/external/serialize()
+ var/list/data = ..()
+ if(robotic == 2)
+ data["company"] = model
+ // If we wanted to store wound information, here is where it would go
+ return data
+
+/obj/item/organ/external/deserialize(list/data)
+ var/company = data["company"]
+ if(company && istext(company))
+ set_company(company)
+ ..() // Parent call loads in the DNA
+ if(data["dna"])
+ sync_colour_to_dna()
diff --git a/paradise.dme b/paradise.dme
index d79069d135d..5c184d781ea 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1043,6 +1043,7 @@
#include "code\modules\admin\verbs\possess.dm"
#include "code\modules\admin\verbs\pray.dm"
#include "code\modules\admin\verbs\randomverbs.dm"
+#include "code\modules\admin\verbs\serialization.dm"
#include "code\modules\admin\verbs\striketeam.dm"
#include "code\modules\admin\verbs\striketeam_syndicate.dm"
#include "code\modules\admin\verbs\ticklag.dm"
@@ -1739,6 +1740,7 @@
#include "code\modules\pda\pdas.dm"
#include "code\modules\pda\radio.dm"
#include "code\modules\pda\utilities.dm"
+#include "code\modules\persistence\persistence.dm"
#include "code\modules\pooling\pool.dm"
#include "code\modules\power\apc.dm"
#include "code\modules\power\cable.dm"