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/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 0ee843fe85c..d1721c5c247 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -105,13 +105,13 @@
var/photo
var/dat
var/stamped = 0
-
+
var/obj/item/weapon/card/id/guest/guest_pass = null // Guest pass attached to the ID
/obj/item/weapon/card/id/New()
..()
spawn(30)
- if(ishuman(loc))
+ if(ishuman(loc) && blood_type == "\[UNSET\]")
var/mob/living/carbon/human/H = loc
SetOwnerInfo(H)
@@ -242,10 +242,10 @@
set name = "Remove Guest Pass"
set category = "Object"
set src in range(0)
-
+
if(usr.stat || !usr.canmove || usr.restrained())
return
-
+
if(guest_pass)
to_chat(usr, "You remove the guest pass from this ID.")
guest_pass.forceMove(get_turf(src))
@@ -253,6 +253,37 @@
else
to_chat(usr, "There is no guest pass attached to this ID")
+/obj/item/weapon/card/id/serialize()
+ var/list/data = ..()
+
+ data["sex"] = sex
+ data["age"] = age
+ data["btype"] = blood_type
+ data["dna_hash"] = dna_hash
+ data["fprint_hash"] = fingerprint_hash
+ data["access"] = access
+ data["job"] = assignment
+ data["account"] = associated_account_number
+ data["owner"] = registered_name
+ data["mining"] = mining_points
+ return data
+
+/obj/item/weapon/card/id/deserialize(list/data)
+ sex = data["sex"]
+ age = data["age"]
+ blood_type = data["btype"]
+ dna_hash = data["dna_hash"]
+ fingerprint_hash = data["fprint_hash"]
+ access = data["access"] // No need for a copy, the list isn't getting touched
+ assignment = data["job"]
+ associated_account_number = data["account"]
+ registered_name = data["owner"]
+ mining_points = data["mining"]
+ // We'd need to use icon serialization(b64) to save the photo, and I don't feel like i
+ UpdateName()
+ RebuildHTML()
+ ..()
+
/obj/item/weapon/card/id/silver
name = "identification card"
desc = "A silver card which shows honour and dedication."
@@ -767,4 +798,4 @@
if("TDgreen")
return "Thunderdome Green"
else
- return capitalize(skin)
\ No newline at end of file
+ return capitalize(skin)
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index a0e3d4dd69e..23f8f9ea685 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -37,7 +37,9 @@
src.add_fingerprint(usr)
return
-
+/obj/item/weapon/storage/belt/deserialize(list/data)
+ ..()
+ update_icon()
/obj/item/weapon/storage/belt/utility
name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing.
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 878adcaa414..c34b392430e 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -547,3 +547,34 @@
return depth
+/obj/item/weapon/storage/serialize()
+ var data = ..()
+ var/list/content_list = list()
+ data["content"] = content_list
+ data["slots"] = storage_slots
+ data["max_w_class"] = max_w_class
+ data["max_c_w_class"] = max_combined_w_class
+ for(var/thing in contents)
+ var/atom/movable/AM = thing
+ // This code does not watch out for infinite loops
+ // But then again a tesseract would destroy the server anyways
+ // Also I wish I could just insert a list instead of it reading it the wrong way
+ content_list.len++
+ content_list[content_list.len] = AM.serialize()
+ return data
+
+/obj/item/weapon/storage/deserialize(list/data)
+ if(isnum(data["slots"]))
+ storage_slots = data["slots"]
+ if(isnum(data["max_w_class"]))
+ max_w_class = data["max_w_class"]
+ if(isnum(data["max_c_w_class"]))
+ max_combined_w_class = data["max_c_w_class"]
+ for(var/thing in contents)
+ qdel(thing) // out with the old
+ for(var/thing in data["content"])
+ if(islist(thing))
+ list_to_object(thing, src)
+ else
+ log_debug("Non-list thing: [thing]. We are a [name]")
+ ..()
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/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm
index 360c9b2915f..faf5f389e9d 100644
--- a/code/modules/clothing/suits/storage.dm
+++ b/code/modules/clothing/suits/storage.dm
@@ -50,3 +50,12 @@
if(istype(G.gift, /obj/item/weapon/storage))
L += G.gift:return_inv()
return L
+
+/obj/item/clothing/suit/storage/serialize()
+ var/list/data = ..()
+ data["pockets"] = pockets.serialize()
+ return data
+
+/obj/item/clothing/suit/storage/deserialize(list/data)
+ qdel(pockets)
+ pockets = list_to_object(data["pockets"], src)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f9c448df5e3..c63c03d8321 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,103 @@
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()
+ var/list/equip_list = list()
+ data["limbs"] = limbs_list
+ data["iorgans"] = organs_list
+ data["equip"] = equip_list
+
+ data["dna"] = dna.serialize()
+ data["age"] = age
+
+ // No being naked
+ data["ushirt"] = undershirt
+ data["socks"] = socks
+ data["uwear"] = underwear
+
+ // Limbs
+ 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()
+
+ // Internal organs/augments
+ for(var/organ in internal_organs)
+ var/obj/item/organ/O = organ
+ organs_list[O.name] = O.serialize()
+
+ // Equipment
+ equip_list.len = slots_amt
+ for(var/i = 1, i < slots_amt, i++)
+ var/obj/item/thing = get_item_by_slot(i)
+ if(thing != null)
+ equip_list[i] = thing.serialize()
+
+ return data
+
+/mob/living/carbon/human/deserialize(list/data)
+ var/list/limbs_list = data["limbs"]
+ var/list/organs_list = data["iorgans"]
+ var/list/equip_list = data["equip"]
+ var/turf/T = get_turf(src)
+ 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)
+ age = data["age"]
+ undershirt = data["ushirt"]
+ underwear = data["uwear"]
+ socks = data["socks"]
+ 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)
+
+
+ // De-serialize equipment
+ // #1: Jumpsuit
+ // #2: Outer suit
+ // #3+: Everything else
+ if(islist(equip_list[slot_w_uniform]))
+ var/obj/item/clothing/C = list_to_object(equip_list[slot_w_uniform], T)
+ equip_to_slot_if_possible(C, slot_w_uniform)
+
+ if(islist(equip_list[slot_wear_suit]))
+ var/obj/item/clothing/C = list_to_object(equip_list[slot_wear_suit], T)
+ equip_to_slot_if_possible(C, slot_wear_suit)
+
+ for(var/i = 1, i < slots_amt, i++)
+ if(i == slot_w_uniform || i == slot_wear_suit)
+ continue
+ if(islist(equip_list[i]))
+ var/obj/item/clothing/C = list_to_object(equip_list[i], T)
+ equip_to_slot_if_possible(C, i)
+ 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 2a80f5326e1..6c8dd8d73bf 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\space_transitions.dm"
#include "code\modules\admin\verbs\striketeam.dm"
#include "code\modules\admin\verbs\striketeam_syndicate.dm"
@@ -1740,6 +1741,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"