diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 74b68d6b05b..4d6d82147f2 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -12,3 +12,4 @@ var/global/list/mob_list = list() //all mobs, including clientless
var/global/list/living_mob_list = list() //all alive mobs, including clientless. Excludes /mob/new_player
var/global/list/dead_mob_list = list() //all dead mobs, including clientless. Excludes /mob/new_player
var/global/list/joined_player_list = list() //all clients that have joined the game at round-start or as a latejoin.
+var/global/list/silicon_mobs = list() //all silicon mobs
\ No newline at end of file
diff --git a/code/game/alternate_appearance.dm b/code/game/alternate_appearance.dm
new file mode 100644
index 00000000000..005876e8ccc
--- /dev/null
+++ b/code/game/alternate_appearance.dm
@@ -0,0 +1,155 @@
+/*
+ Alternate Appearances! By RemieRichards
+ A framework for replacing an atom (and it's overlays) with an override = 1 image, that's less shit!
+ Example uses:
+ * hallucinating all mobs looking like skeletons
+ * people wearing cardborg suits appearing as Standard Cyborgs to the other Silicons
+ * !!use your imagination!!
+
+*/
+
+
+//This datum is built on-the-fly by some of the procs below
+//no need to instantiate it
+/datum/alternate_appearance
+ var/key = ""
+ var/image/img
+ var/list/viewers = list()
+ var/atom/owner = null
+
+
+/*
+ Displays the alternate_appearance
+ displayTo - a list of MOBS to show this appearance to
+*/
+/datum/alternate_appearance/proc/display_to(list/displayTo)
+ if(!displayTo || !displayTo.len)
+ return
+ for(var/m in displayTo)
+ var/mob/M = m
+ if(!M.viewing_alternate_appearances)
+ M.viewing_alternate_appearances = list()
+ viewers |= M
+ M.viewing_alternate_appearances[key] = src
+ if(M.client)
+ M.client.images |= img
+
+/*
+ Hides the alternate_appearance
+ hideFrom - optional list of MOBS to hide it from the list's mobs specifically
+*/
+/datum/alternate_appearance/proc/hide(list/hideFrom)
+ var/list/hiding = viewers
+ if(hideFrom)
+ hiding = hideFrom
+
+ for(var/m in hiding)
+ var/mob/M = m
+ if(M.client)
+ M.client.images -= img
+ if(M.viewing_alternate_appearances && M.viewing_alternate_appearances.len)
+ M.viewing_alternate_appearances -= key
+ if(!M.viewing_alternate_appearances.len)
+ M.viewing_alternate_appearances = null
+ viewers -= M
+
+
+/*
+ Removes the alternate_appearance from its owner's alternate_appearances list, hiding it also
+*/
+/datum/alternate_appearance/proc/remove()
+ hide()
+ if(owner && owner.alternate_appearances)
+ owner.alternate_appearances -= key
+ if(!owner.alternate_appearances.len)
+ owner.alternate_appearances = null
+
+
+/datum/alternate_appearance/Destroy()
+ remove()
+ return ..()
+
+
+
+/atom
+ var/list/alternate_appearances //the alternate appearances we own
+ var/list/viewing_alternate_appearances //the alternate appearances we're viewing, stored here to reestablish them after Logout()s
+ //these lists are built/destroyed as necessary, so atoms aren't all lugging around lists full of datums
+
+/*
+ Builds an alternate_appearance datum for the supplied args, optionally displaying it straight away
+ key - the key to the assoc list of key = /datum/alternate_appearances
+ img - the image file to be the "alternate appearance"
+ WORKS BEST IF:
+ * it has override = 1 set
+ * the image's loc is the atom that will use the appearance (otherwise... it's not exactly an alt appearance of this atom is it?)
+ displayTo - optional list of MOBS to display to immediately
+
+ Example:
+ var/image/I = image(icon = 'disguise.dmi', icon_state = "disguise", loc = src)
+ I.override = 1
+ add_alt_appearance("super_secret_disguise", I, players)
+
+*/
+/atom/proc/add_alt_appearance(key, img, list/displayTo = list())
+ if(!key || !img)
+ return
+ if(!alternate_appearances)
+ alternate_appearances = list()
+
+ var/datum/alternate_appearance/AA = new()
+ AA.img = img
+ AA.key = key
+ AA.owner = src
+
+ alternate_appearances[key] = AA
+ if(displayTo && displayTo.len)
+ display_alt_appearance(key, displayTo)
+
+
+//////////////
+// WRAPPERS //
+//////////////
+
+/*
+ Removes an alternate_appearance from src's alternate_appearances list
+ Wrapper for: alternate_appearance/remove()
+ key - the key to the assoc list of key = /datum/alternate_appearance
+*/
+/atom/proc/remove_alt_appearance(key)
+ if(alternate_appearances)
+ if(alternate_appearances[key])
+ var/datum/alternate_appearance/AA = alternate_appearances[key]
+ qdel(AA)
+
+
+/*
+ Displays an alternate appearance from src's alternate_appearances list
+ Wrapper for: alternate_appearance/display_to()
+ key - the key to the assoc list of key = /datum/alternate_appearance
+ displayTo - a list of MOBS to show this appearance to
+*/
+/atom/proc/display_alt_appearance(key, list/displayTo)
+ if(!alternate_appearances || !key)
+ return
+ var/datum/alternate_appearance/AA = alternate_appearances[key]
+ if(!AA || !AA.img)
+ return
+ AA.display_to(displayTo)
+
+
+/*
+ Hides an alternate appearance from src's alternate_appearances list
+ Wrapper for: alternate_appearance/hide()
+ key - the key to the assoc list of key = /datum/alternate_appearance
+ hideFrom - optional list of MOBS to hide it from the list's mobs specifically
+*/
+/atom/proc/hide_alt_appearance(key, list/hideFrom)
+ if(!alternate_appearances || !key)
+ return
+ var/datum/alternate_appearance/AA = alternate_appearances[key]
+ if(!AA)
+ return
+ AA.hide(hideFrom)
+
+
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 2a78972e001..6dd095996f9 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1,393 +1,404 @@
-/atom
- layer = 2
- var/level = 2
- var/flags = 0
- var/list/fingerprints
- var/list/fingerprintshidden
- var/fingerprintslast = null
- var/list/blood_DNA
-
- ///Chemistry.
- var/datum/reagents/reagents = null
-
- //This atom's HUD (med/sec, etc) images. Associative list.
- var/list/image/hud_list = list()
- //HUD images that this atom can provide.
- var/list/hud_possible
-
- //Value used to increment ex_act() if reactionary_explosions is on
- var/explosion_block = 0
-
-/atom/proc/onCentcom()
- var/turf/T = get_turf(src)
- if(!T)
- return 0
-
- if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
- return 0
-
- //check for centcomm shuttles
- for(var/A in SSshuttle.mobile)
- var/obj/docking_port/mobile/M = A
- if(M.launch_status == ENDGAME_LAUNCHED && T in M.areaInstance)
- return 1
-
- //finally check for centcom itself
- return istype(T.loc,/area/centcom)
-
-/atom/proc/onSyndieBase()
- var/turf/T = get_turf(src)
- if(!T)
- return 0
-
- if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
- return 0
-
- if(istype(T.loc,/area/shuttle/syndicate) || istype(T.loc,/area/syndicate_mothership))
- return 1
-
- return 0
-
-/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
- if(do_attack_animation)
- hulk.changeNext_move(CLICK_CD_MELEE)
- add_logs(hulk, src, "punched", "hulk powers")
- hulk.do_attack_animation(src)
- return
-
-/atom/proc/CheckParts()
- return
-
-/atom/proc/assume_air(datum/gas_mixture/giver)
- qdel(giver)
- return null
-
-/atom/proc/remove_air(amount)
- return null
-
-/atom/proc/return_air()
- if(loc)
- return loc.return_air()
- else
- return null
-
-/atom/proc/check_eye(mob/user)
- return
-
-/atom/proc/on_reagent_change()
- return
-
-/atom/proc/Bumped(AM as mob|obj)
- return
-
-// Convenience proc to see if a container is open for chemistry handling
-// returns true if open
-// false if closed
-/atom/proc/is_open_container()
- return flags & OPENCONTAINER
-
-/*//Convenience proc to see whether a container can be accessed in a certain way.
-
-/atom/proc/can_subract_container()
- return flags & EXTRACT_CONTAINER
-
-/atom/proc/can_add_container()
- return flags & INSERT_CONTAINER
-*/
-
-
-/atom/proc/allow_drop()
- return 1
-
-/atom/proc/CheckExit()
- return 1
-
-/atom/proc/HasProximity(atom/movable/AM as mob|obj)
- return
-
-/atom/proc/emp_act(severity)
- return
-
-/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
- . = P.on_hit(src, 0, def_zone)
-
-/atom/proc/in_contents_of(container)//can take class or object instance as argument
- if(ispath(container))
- if(istype(src.loc, container))
- return 1
- else if(src in container)
- return 1
- return
-
-/*
- * atom/proc/search_contents_for(path,list/filter_path=null)
- * Recursevly searches all atom contens (including contents contents and so on).
- *
- * ARGS: path - search atom contents for atoms of this type
- * list/filter_path - if set, contents of atoms not of types in this list are excluded from search.
- *
- * RETURNS: list of found atoms
- */
-
-/atom/proc/search_contents_for(path,list/filter_path=null)
- var/list/found = list()
- for(var/atom/A in src)
- if(istype(A, path))
- found += A
- if(filter_path)
- var/pass = 0
- for(var/type in filter_path)
- pass |= istype(A, type)
- if(!pass)
- continue
- if(A.contents.len)
- found += A.search_contents_for(path,filter_path)
- return found
-
-
-/atom/proc/examine(mob/user)
- //This reformat names to get a/an properly working on item descriptions when they are bloody
- var/f_name = "\a [src]."
- if(src.blood_DNA && !istype(src, /obj/effect/decal))
- if(gender == PLURAL)
- f_name = "some "
- else
- f_name = "a "
- f_name += "blood-stained [name]!"
-
- user << "\icon[src] That's [f_name]"
-
- if(desc)
- user << desc
- // *****RM
- //user << "[name]: Dn:[density] dir:[dir] cont:[contents] icon:[icon] is:[icon_state] loc:[loc]"
-
- if(reagents && is_open_container()) //is_open_container() isn't really the right proc for this, but w/e
- user << "It contains:"
- if(reagents.reagent_list.len)
- if(user.can_see_reagents()) //Show each individual reagent
- for(var/datum/reagent/R in reagents.reagent_list)
- user << "[R.volume] units of [R.name]"
- else //Otherwise, just show the total volume
- var/total_volume = 0
- for(var/datum/reagent/R in reagents.reagent_list)
- total_volume += R.volume
- user << "[total_volume] units of various reagents"
- else
- user << "Nothing."
-
-/atom/proc/relaymove()
- return
-
-/atom/proc/contents_explosion(severity, target)
- for(var/atom/A in contents)
- A.ex_act(severity, target)
- CHECK_TICK
-
-/atom/proc/ex_act(severity, target)
- contents_explosion(severity, target)
-
-/atom/proc/blob_act()
- return
-
-/atom/proc/fire_act()
- return
-
-/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
- if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
- spawn(2) //very short wait, so we can actually see the impact.
- if(AM && isturf(AM.loc))
- step(AM, turn(AM.dir, 180))
-
-var/list/blood_splatter_icons = list()
-
-/atom/proc/blood_splatter_index()
- return "\ref[initial(icon)]-[initial(icon_state)]"
-
-/atom/proc/add_blood_list(mob/living/carbon/M)
- // Returns 0 if we have that blood already
- if(!istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
- blood_DNA = list()
- //if this blood isn't already in the list, add it
- if(blood_DNA[M.dna.unique_enzymes])
- return 0 //already bloodied with this blood. Cannot add more.
- blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type
- return 1
-
-//returns 1 if made bloody, returns 0 otherwise
-/atom/proc/add_blood(mob/living/carbon/M)
- if(!M || !M.has_dna() || rejects_blood())
- return 0
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(NOBLOOD in H.dna.species.specflags)
- return 0
- return 1
-
-/obj/add_blood(mob/living/carbon/M)
- if(!..())
- return 0
- return add_blood_list(M)
-
-/obj/item/add_blood(mob/living/carbon/M)
- var/blood_count = !blood_DNA ? 0 : blood_DNA.len
- if(!..())
- return 0
- //apply the blood-splatter overlay if it isn't already in there
- if(!blood_count && initial(icon) && initial(icon_state))
- //try to find a pre-processed blood-splatter. otherwise, make a new one
- var/index = blood_splatter_index()
- var/icon/blood_splatter_icon = blood_splatter_icons[index]
- if(!blood_splatter_icon)
- blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object
- blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
- blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
- blood_splatter_icon = fcopy_rsc(blood_splatter_icon)
- blood_splatter_icons[index] = blood_splatter_icon
- overlays += blood_splatter_icon
- return 1 //we applied blood to the item
-
-/obj/item/clothing/gloves/add_blood(mob/living/carbon/M)
- if(!..())
- return 0
- transfer_blood = rand(2, 4)
- bloody_hands_mob = M
- return 1
-
-/turf/add_blood(mob/living/carbon/human/M)
- if(!..())
- return 0
-
- var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter
- if(!B)
- blood_splatter(src,M.get_blood(M.vessel),1)
- B = locate(/obj/effect/decal/cleanable/blood) in contents
- B.add_blood_list(M)
- return 1 //we bloodied the floor
-
-/mob/living/carbon/human/add_blood(mob/living/carbon/M)
- if(!..())
- return 0
- add_blood_list(M)
- bloody_hands = rand(2, 4)
- bloody_hands_mob = M
- update_inv_gloves() //handles bloody hands overlays and updating
- return 1 //we applied blood to the item
-
-/atom/proc/rejects_blood()
- return 0
-
-// Only adds blood on the floor -- Skie
-/atom/proc/add_blood_floor(mob/living/carbon/M)
- if(istype(src, /turf))
- if(M.has_dna()) //mobs with dna = (monkeys + humans at time of writing)
- var/obj/effect/decal/cleanable/blood/B = locate() in contents
- if(!B)
- blood_splatter(src,M,1)
- B = locate(/obj/effect/decal/cleanable/blood) in contents
- B.blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type
- else if(istype(M, /mob/living/carbon/alien))
- var/obj/effect/decal/cleanable/xenoblood/B = locate() in contents
- if(!B)
- B = new(src)
- B.blood_DNA["UNKNOWN BLOOD"] = "X*"
- else if(istype(M, /mob/living/silicon/robot))
- var/obj/effect/decal/cleanable/oil/B = locate() in contents
- if(!B)
- B = new(src)
-
-/atom/proc/clean_blood()
- if(istype(blood_DNA, /list))
- blood_DNA = null
- return 1
-
-
-/atom/proc/get_global_map_pos()
- if(!islist(global_map) || isemptylist(global_map)) return
- var/cur_x = null
- var/cur_y = null
- var/list/y_arr = null
- for(cur_x=1,cur_x<=global_map.len,cur_x++)
- y_arr = global_map[cur_x]
- cur_y = y_arr.Find(src.z)
- if(cur_y)
- break
-// world << "X = [cur_x]; Y = [cur_y]"
- if(cur_x && cur_y)
- return list("x"=cur_x,"y"=cur_y)
- else
- return 0
-
-/atom/proc/isinspace()
- if(istype(get_turf(src), /turf/open/space))
- return 1
- else
- return 0
-
-/atom/proc/handle_fall()
- return
-
-/atom/proc/handle_slip()
- return
-/atom/proc/singularity_act()
- return
-
-/atom/proc/singularity_pull()
- return
-
-/atom/proc/acid_act(acidpwr, toxpwr, acid_volume)
- return
-
-/atom/proc/emag_act()
- return
-
-/atom/proc/narsie_act()
- return
-
-/atom/proc/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user)
- return 0
-
-//This proc is called on the location of an atom when the atom is Destroy()'d
-/atom/proc/handle_atom_del(atom/A)
-
-// Byond seemingly calls stat, each tick.
-// Calling things each tick can get expensive real quick.
-// So we slow this down a little.
-// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat
-/atom/Stat()
- . = ..()
- sleep(1)
- stoplag()
-
-//This is called just before maps and objects are initialized, use it to spawn other mobs/objects
-//effects at world start up without causing runtimes
-/atom/proc/spawn_atom_to_world()
-
-//This will be called after the map and objects are loaded
-/atom/proc/initialize()
- return
-
-//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it)
-/atom/proc/get_remote_view_fullscreens(mob/user)
- return
-
-//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera)
-/atom/proc/update_remote_sight(mob/living/user)
- return
-
-/atom/proc/add_vomit_floor(mob/living/carbon/M, toxvomit = 0)
- if(istype(src,/turf) )
- var/obj/effect/decal/cleanable/vomit/V = PoolOrNew(/obj/effect/decal/cleanable/vomit, src)
- // Make toxins vomit look different
- if(toxvomit)
- V.icon_state = "vomittox_[pick(1,4)]"
- if(M.reagents)
- clear_reagents_to_vomit_pool(M,V)
-
-/atom/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V)
- M.reagents.trans_to(V, M.reagents.total_volume / 10)
- for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food
- if(istype(R, /datum/reagent/consumable))
- var/datum/reagent/consumable/nutri_check = R
- if(nutri_check.nutriment_factor >0)
- M.reagents.remove_reagent(R.id,R.volume)
+/atom
+ layer = 2
+ var/level = 2
+ var/flags = 0
+ var/list/fingerprints
+ var/list/fingerprintshidden
+ var/fingerprintslast = null
+ var/list/blood_DNA
+
+ ///Chemistry.
+ var/datum/reagents/reagents = null
+
+ //This atom's HUD (med/sec, etc) images. Associative list.
+ var/list/image/hud_list = list()
+ //HUD images that this atom can provide.
+ var/list/hud_possible
+
+ //Value used to increment ex_act() if reactionary_explosions is on
+ var/explosion_block = 0
+
+
+/atom/Destroy()
+ if(alternate_appearances)
+ for(var/aakey in alternate_appearances)
+ var/datum/alternate_appearance/AA = alternate_appearances[aakey]
+ qdel(AA)
+ alternate_appearances = null
+
+ return ..()
+
+
+/atom/proc/onCentcom()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return 0
+
+ if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
+ return 0
+
+ //check for centcomm shuttles
+ for(var/A in SSshuttle.mobile)
+ var/obj/docking_port/mobile/M = A
+ if(M.launch_status == ENDGAME_LAUNCHED && T in M.areaInstance)
+ return 1
+
+ //finally check for centcom itself
+ return istype(T.loc,/area/centcom)
+
+/atom/proc/onSyndieBase()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return 0
+
+ if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
+ return 0
+
+ if(istype(T.loc,/area/shuttle/syndicate) || istype(T.loc,/area/syndicate_mothership))
+ return 1
+
+ return 0
+
+/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
+ if(do_attack_animation)
+ hulk.changeNext_move(CLICK_CD_MELEE)
+ add_logs(hulk, src, "punched", "hulk powers")
+ hulk.do_attack_animation(src)
+ return
+
+/atom/proc/CheckParts()
+ return
+
+/atom/proc/assume_air(datum/gas_mixture/giver)
+ qdel(giver)
+ return null
+
+/atom/proc/remove_air(amount)
+ return null
+
+/atom/proc/return_air()
+ if(loc)
+ return loc.return_air()
+ else
+ return null
+
+/atom/proc/check_eye(mob/user)
+ return
+
+/atom/proc/on_reagent_change()
+ return
+
+/atom/proc/Bumped(AM as mob|obj)
+ return
+
+// Convenience proc to see if a container is open for chemistry handling
+// returns true if open
+// false if closed
+/atom/proc/is_open_container()
+ return flags & OPENCONTAINER
+
+/*//Convenience proc to see whether a container can be accessed in a certain way.
+
+/atom/proc/can_subract_container()
+ return flags & EXTRACT_CONTAINER
+
+/atom/proc/can_add_container()
+ return flags & INSERT_CONTAINER
+*/
+
+
+/atom/proc/allow_drop()
+ return 1
+
+/atom/proc/CheckExit()
+ return 1
+
+/atom/proc/HasProximity(atom/movable/AM as mob|obj)
+ return
+
+/atom/proc/emp_act(severity)
+ return
+
+/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
+ . = P.on_hit(src, 0, def_zone)
+
+/atom/proc/in_contents_of(container)//can take class or object instance as argument
+ if(ispath(container))
+ if(istype(src.loc, container))
+ return 1
+ else if(src in container)
+ return 1
+ return
+
+/*
+ * atom/proc/search_contents_for(path,list/filter_path=null)
+ * Recursevly searches all atom contens (including contents contents and so on).
+ *
+ * ARGS: path - search atom contents for atoms of this type
+ * list/filter_path - if set, contents of atoms not of types in this list are excluded from search.
+ *
+ * RETURNS: list of found atoms
+ */
+
+/atom/proc/search_contents_for(path,list/filter_path=null)
+ var/list/found = list()
+ for(var/atom/A in src)
+ if(istype(A, path))
+ found += A
+ if(filter_path)
+ var/pass = 0
+ for(var/type in filter_path)
+ pass |= istype(A, type)
+ if(!pass)
+ continue
+ if(A.contents.len)
+ found += A.search_contents_for(path,filter_path)
+ return found
+
+
+/atom/proc/examine(mob/user)
+ //This reformat names to get a/an properly working on item descriptions when they are bloody
+ var/f_name = "\a [src]."
+ if(src.blood_DNA && !istype(src, /obj/effect/decal))
+ if(gender == PLURAL)
+ f_name = "some "
+ else
+ f_name = "a "
+ f_name += "blood-stained [name]!"
+
+ user << "\icon[src] That's [f_name]"
+
+ if(desc)
+ user << desc
+ // *****RM
+ //user << "[name]: Dn:[density] dir:[dir] cont:[contents] icon:[icon] is:[icon_state] loc:[loc]"
+
+ if(reagents && is_open_container()) //is_open_container() isn't really the right proc for this, but w/e
+ user << "It contains:"
+ if(reagents.reagent_list.len)
+ if(user.can_see_reagents()) //Show each individual reagent
+ for(var/datum/reagent/R in reagents.reagent_list)
+ user << "[R.volume] units of [R.name]"
+ else //Otherwise, just show the total volume
+ var/total_volume = 0
+ for(var/datum/reagent/R in reagents.reagent_list)
+ total_volume += R.volume
+ user << "[total_volume] units of various reagents"
+ else
+ user << "Nothing."
+
+/atom/proc/relaymove()
+ return
+
+/atom/proc/contents_explosion(severity, target)
+ for(var/atom/A in contents)
+ A.ex_act(severity, target)
+ CHECK_TICK
+
+/atom/proc/ex_act(severity, target)
+ contents_explosion(severity, target)
+
+/atom/proc/blob_act()
+ return
+
+/atom/proc/fire_act()
+ return
+
+/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
+ if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
+ spawn(2) //very short wait, so we can actually see the impact.
+ if(AM && isturf(AM.loc))
+ step(AM, turn(AM.dir, 180))
+
+var/list/blood_splatter_icons = list()
+
+/atom/proc/blood_splatter_index()
+ return "\ref[initial(icon)]-[initial(icon_state)]"
+
+/atom/proc/add_blood_list(mob/living/carbon/M)
+ // Returns 0 if we have that blood already
+ if(!istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
+ blood_DNA = list()
+ //if this blood isn't already in the list, add it
+ if(blood_DNA[M.dna.unique_enzymes])
+ return 0 //already bloodied with this blood. Cannot add more.
+ blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type
+ return 1
+
+//returns 1 if made bloody, returns 0 otherwise
+/atom/proc/add_blood(mob/living/carbon/M)
+ if(!M || !M.has_dna() || rejects_blood())
+ return 0
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(NOBLOOD in H.dna.species.specflags)
+ return 0
+ return 1
+
+/obj/add_blood(mob/living/carbon/M)
+ if(!..())
+ return 0
+ return add_blood_list(M)
+
+/obj/item/add_blood(mob/living/carbon/M)
+ var/blood_count = !blood_DNA ? 0 : blood_DNA.len
+ if(!..())
+ return 0
+ //apply the blood-splatter overlay if it isn't already in there
+ if(!blood_count && initial(icon) && initial(icon_state))
+ //try to find a pre-processed blood-splatter. otherwise, make a new one
+ var/index = blood_splatter_index()
+ var/icon/blood_splatter_icon = blood_splatter_icons[index]
+ if(!blood_splatter_icon)
+ blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object
+ blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
+ blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
+ blood_splatter_icon = fcopy_rsc(blood_splatter_icon)
+ blood_splatter_icons[index] = blood_splatter_icon
+ overlays += blood_splatter_icon
+ return 1 //we applied blood to the item
+
+/obj/item/clothing/gloves/add_blood(mob/living/carbon/M)
+ if(!..())
+ return 0
+ transfer_blood = rand(2, 4)
+ bloody_hands_mob = M
+ return 1
+
+/turf/add_blood(mob/living/carbon/human/M)
+ if(!..())
+ return 0
+
+ var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter
+ if(!B)
+ blood_splatter(src,M.get_blood(M.vessel),1)
+ B = locate(/obj/effect/decal/cleanable/blood) in contents
+ B.add_blood_list(M)
+ return 1 //we bloodied the floor
+
+/mob/living/carbon/human/add_blood(mob/living/carbon/M)
+ if(!..())
+ return 0
+ add_blood_list(M)
+ bloody_hands = rand(2, 4)
+ bloody_hands_mob = M
+ update_inv_gloves() //handles bloody hands overlays and updating
+ return 1 //we applied blood to the item
+
+/atom/proc/rejects_blood()
+ return 0
+
+// Only adds blood on the floor -- Skie
+/atom/proc/add_blood_floor(mob/living/carbon/M)
+ if(istype(src, /turf))
+ if(M.has_dna()) //mobs with dna = (monkeys + humans at time of writing)
+ var/obj/effect/decal/cleanable/blood/B = locate() in contents
+ if(!B)
+ blood_splatter(src,M,1)
+ B = locate(/obj/effect/decal/cleanable/blood) in contents
+ B.blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type
+ else if(istype(M, /mob/living/carbon/alien))
+ var/obj/effect/decal/cleanable/xenoblood/B = locate() in contents
+ if(!B)
+ B = new(src)
+ B.blood_DNA["UNKNOWN BLOOD"] = "X*"
+ else if(istype(M, /mob/living/silicon/robot))
+ var/obj/effect/decal/cleanable/oil/B = locate() in contents
+ if(!B)
+ B = new(src)
+
+/atom/proc/clean_blood()
+ if(istype(blood_DNA, /list))
+ blood_DNA = null
+ return 1
+
+
+/atom/proc/get_global_map_pos()
+ if(!islist(global_map) || isemptylist(global_map)) return
+ var/cur_x = null
+ var/cur_y = null
+ var/list/y_arr = null
+ for(cur_x=1,cur_x<=global_map.len,cur_x++)
+ y_arr = global_map[cur_x]
+ cur_y = y_arr.Find(src.z)
+ if(cur_y)
+ break
+// world << "X = [cur_x]; Y = [cur_y]"
+ if(cur_x && cur_y)
+ return list("x"=cur_x,"y"=cur_y)
+ else
+ return 0
+
+/atom/proc/isinspace()
+ if(istype(get_turf(src), /turf/open/space))
+ return 1
+ else
+ return 0
+
+/atom/proc/handle_fall()
+ return
+
+/atom/proc/handle_slip()
+ return
+/atom/proc/singularity_act()
+ return
+
+/atom/proc/singularity_pull()
+ return
+
+/atom/proc/acid_act(acidpwr, toxpwr, acid_volume)
+ return
+
+/atom/proc/emag_act()
+ return
+
+/atom/proc/narsie_act()
+ return
+
+/atom/proc/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user)
+ return 0
+
+//This proc is called on the location of an atom when the atom is Destroy()'d
+/atom/proc/handle_atom_del(atom/A)
+
+// Byond seemingly calls stat, each tick.
+// Calling things each tick can get expensive real quick.
+// So we slow this down a little.
+// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat
+/atom/Stat()
+ . = ..()
+ sleep(1)
+ stoplag()
+
+//This is called just before maps and objects are initialized, use it to spawn other mobs/objects
+//effects at world start up without causing runtimes
+/atom/proc/spawn_atom_to_world()
+
+//This will be called after the map and objects are loaded
+/atom/proc/initialize()
+ return
+
+//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it)
+/atom/proc/get_remote_view_fullscreens(mob/user)
+ return
+
+//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera)
+/atom/proc/update_remote_sight(mob/living/user)
+ return
+
+/atom/proc/add_vomit_floor(mob/living/carbon/M, toxvomit = 0)
+ if(istype(src,/turf) )
+ var/obj/effect/decal/cleanable/vomit/V = PoolOrNew(/obj/effect/decal/cleanable/vomit, src)
+ // Make toxins vomit look different
+ if(toxvomit)
+ V.icon_state = "vomittox_[pick(1,4)]"
+ if(M.reagents)
+ clear_reagents_to_vomit_pool(M,V)
+
+/atom/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V)
+ M.reagents.trans_to(V, M.reagents.total_volume / 10)
+ for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food
+ if(istype(R, /datum/reagent/consumable))
+ var/datum/reagent/consumable/nutri_check = R
+ if(nutri_check.nutriment_factor >0)
+ M.reagents.remove_reagent(R.id,R.volume)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index d6bce4278bf..f634e1eca8b 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -388,7 +388,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
/obj/item/proc/on_found(mob/finder)
return
-// called after an item is placed in an equipment slot
+// called after an item is placed in an equipment slot //NOPE, for example, if you put a helmet in slot_head, it is NOT in user's head variable yet, how stupid.
// user is mob that equipped it
// slot uses the slot_X defines found in setup.dm
// for items that can be placed in multiple slots
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index e0b2c348d8f..83eed76aeee 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -62,14 +62,6 @@
desc = "A plastic replica of a Syndicate agent's space helmet. You'll look just like a real murderous Syndicate agent in this! This is a toy, it is not made for use in space!"
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
-/obj/item/clothing/head/cardborg
- name = "cardborg helmet"
- desc = "A helmet made out of a box."
- icon_state = "cardborg_h"
- item_state = "cardborg_h"
- flags_cover = HEADCOVERSEYES
- flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
-
/obj/item/clothing/head/snowman
name = "Snowman Head"
desc = "A ball of white styrofoam. So festive."
diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm
index 9b660fb015c..fb35aceb6b6 100644
--- a/code/modules/clothing/head/misc_special.dm
+++ b/code/modules/clothing/head/misc_special.dm
@@ -5,7 +5,7 @@
* Ushanka
* Pumpkin head
* Kitty ears
- *
+ * Cardborg disguise
*/
/*
@@ -147,3 +147,24 @@
flags_inv = 0
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
brightness_on = 1 //luminosity when on
+
+
+/obj/item/clothing/head/cardborg
+ name = "cardborg helmet"
+ desc = "A helmet made out of a box."
+ icon_state = "cardborg_h"
+ item_state = "cardborg_h"
+ flags_cover = HEADCOVERSEYES
+ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
+
+/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot)
+ ..()
+ if(ishuman(user) && slot == slot_head)
+ var/mob/living/carbon/human/H = user
+ if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg))
+ var/obj/item/clothing/suit/cardborg/CB = H.wear_suit
+ CB.disguise(user, src)
+
+/obj/item/clothing/head/cardborg/dropped(mob/living/user)
+ ..()
+ user.remove_alt_appearance("standard_borg_disguise")
\ No newline at end of file
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 91f2d49a6c2..cd62fb539d3 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -155,7 +155,6 @@
allowed = list(/obj/item/weapon/storage/book/bible, /obj/item/weapon/nullrod, /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, /obj/item/weapon/storage/fancy/candle_box, /obj/item/candle, /obj/item/weapon/tank/internals/emergency_oxygen)
-
/obj/item/clothing/suit/cardborg
name = "cardborg suit"
desc = "An ordinary cardboard box with holes cut in the sides."
@@ -164,6 +163,26 @@
body_parts_covered = CHEST|GROIN
flags_inv = HIDEJUMPSUIT
+/obj/item/clothing/suit/cardborg/equipped(mob/living/user, slot)
+ ..()
+ if(slot == slot_wear_suit)
+ disguise(user)
+
+/obj/item/clothing/suit/cardborg/dropped(mob/living/user)
+ ..()
+ user.remove_alt_appearance("standard_borg_disguise")
+
+/obj/item/clothing/suit/cardborg/proc/disguise(mob/living/carbon/human/H, obj/item/clothing/head/cardborg/borghead)
+ if(istype(H))
+ if(!borghead)
+ borghead = H.head
+ if(istype(borghead, /obj/item/clothing/head/cardborg)) //why is this done this way? because equipped() is called BEFORE THE ITEM IS IN THE SLOT WHYYYY
+ var/image/I = image(icon = 'icons/mob/robots.dmi' , icon_state = "robot", loc = H)
+ I.override = 1
+ I.overlays += image(icon = 'icons/mob/robots.dmi' , icon_state = "eyes-standard") //gotta look realistic
+ H.add_alt_appearance("standard_borg_disguise", I, silicon_mobs+H) //you look like a robot to robots! (including yourself because you're totally a robot)
+
+
/obj/item/clothing/suit/snowman
name = "snowman outfit"
desc = "Two white spheres covered in white glitter. 'Tis the season."
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index c2aa78a467a..f25becac68c 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -35,6 +35,7 @@
/mob/living/silicon/New()
..()
+ silicon_mobs |= src
var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC]
diag_hud.add_to_hud(src)
diag_hud_set_status()
@@ -43,6 +44,7 @@
/mob/living/silicon/Destroy()
radio = null
aicamera = null
+ silicon_mobs -= src
return ..()
/mob/living/silicon/contents_explosion(severity, target)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index ed0e6f7dac4..1ce7aed14f9 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -64,3 +64,9 @@
winset(src, null, "mainwindow.macro=[macro_hotkeys] mapwindow.map.focus=true input.background-color=#e0e0e0")
else
winset(src, null, "mainwindow.macro=[macro_default] input.focus=true input.background-color=#d3b5b5")
+
+ if(viewing_alternate_appearances && viewing_alternate_appearances.len)
+ for(var/aakey in viewing_alternate_appearances)
+ var/datum/alternate_appearance/AA = viewing_alternate_appearances[aakey]
+ if(AA)
+ AA.display_to(list(src))
diff --git a/tgstation.dme b/tgstation.dme
index 56c7529a01b..1d122ea5942 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -233,6 +233,7 @@
#include "code\datums\wires\syndicatebomb.dm"
#include "code\datums\wires\vending.dm"
#include "code\datums\wires\wires.dm"
+#include "code\game\alternate_appearance.dm"
#include "code\game\asteroid.dm"
#include "code\game\atoms.dm"
#include "code\game\atoms_movable.dm"