diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index 05de6b9ac69..cc3cc05e809 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -1,3 +1,63 @@
+/*
+* Datum that holds the instances and information about the items in the smartfridge.
+*/
+/datum/data/stored_item
+ var/item_name = "name" //Name of the item(s) displayed
+ var/item_path = null
+ var/amount = 0
+ var/list/instances //What items are actually stored
+ var/fridge //The attatched fridge
+
+/datum/data/stored_item/New(var/fridge, var/path, var/name = null, var/amount = 0)
+ ..()
+
+ src.item_path = path
+
+ if(!name)
+ var/atom/tmp = path
+ src.item_name = initial(tmp.name)
+ else
+ src.item_name = name
+
+ src.amount = amount
+ src.fridge = fridge
+
+/datum/data/stored_item/Destroy()
+ fridge = null
+ if(instances)
+ for(var/product in instances)
+ qdel(product)
+ instances.Cut()
+ . = ..()
+
+/datum/data/stored_item/proc/get_amount()
+ return instances ? instances.len : amount
+
+/datum/data/stored_item/proc/get_product(var/product_location)
+ if(!get_amount() || !product_location)
+ return
+ init_products()
+
+ var/atom/movable/product = instances[instances.len] // Remove the last added product
+ instances -= product
+ product.forceMove(product_location)
+
+/datum/data/stored_item/proc/add_product(var/atom/movable/product)
+ if(product.type != item_path)
+ return 0
+ init_products()
+ product.forceMove(fridge)
+ instances += product
+
+/datum/data/stored_item/proc/init_products()
+ if(instances)
+ return
+ instances = list()
+ for(var/i = 1 to amount)
+ var/new_product = new item_path(fridge)
+ instances += new_product
+
+
/* SmartFridge. Much todo
*/
/obj/machinery/smartfridge
@@ -15,7 +75,8 @@
var/icon_on = "smartfridge"
var/icon_off = "smartfridge-off"
var/icon_panel = "smartfridge-panel"
- var/item_quants = list()
+ var/list/item_records = list()
+ var/datum/data/stored_item/currently_vending = null //What we're putting out of the machine.
var/seconds_electrified = 0;
var/shoot_inventory = 0
var/locked = 0
@@ -68,11 +129,14 @@
/obj/machinery/smartfridge/secure/extract/New()
..()
+ var/datum/data/stored_item/I = new(src, /obj/item/xenoproduct/slime/core)
+ item_records.Add(I)
for(var/i=1 to 5)
var/obj/item/xenoproduct/slime/core/C = new(src)
C.traits = new()
C.nameVar = "grey"
- item_quants[C.name]++
+ I.add_product(C)
+
/obj/machinery/smartfridge/secure/medbay
name = "\improper Refrigerated Medicine Storage"
@@ -162,20 +226,19 @@
overlays += "drying_rack_drying"
/obj/machinery/smartfridge/drying_rack/proc/dry()
- for(var/obj/item/weapon/reagent_containers/food/snacks/S in contents)
- if(S.dry) continue
- if(S.dried_type == S.type)
- S.dry = 1
- item_quants[S.name]--
- S.name = "dried [S.name]"
- S.color = "#AAAAAA"
- S.loc = loc
- else
- var/D = S.dried_type
- new D(loc)
- item_quants[S.name]--
- qdel(S)
- return
+ for(var/datum/data/stored_item/I in item_records)
+ for(var/obj/item/weapon/reagent_containers/food/snacks/S in I.instances)
+ if(S.dry) continue
+ if(S.dried_type == S.type)
+ S.dry = 1
+ S.name = "dried [S.name]"
+ S.color = "#AAAAAA"
+ I.get_product(loc)
+ else
+ var/D = S.dried_type
+ new D(loc)
+ qdel(S)
+ return
return
/obj/machinery/smartfridge/process()
@@ -222,35 +285,38 @@
return
if(accept_check(O))
- if(contents.len >= max_n_of_items)
- user << "\The [src] is full."
- return 1
- else
- user.remove_from_mob(O)
- O.loc = src
- if(item_quants[O.name])
- item_quants[O.name]++
- else
- item_quants[O.name] = 1
- user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
+ user.remove_from_mob(O)
+ var/hasRecord = FALSE //Check to see if this passes or not.
+ for(var/datum/data/stored_item/I in item_records)
+ if(istype(O, I.item_path))
+ stock(O, I)
+ qdel(O)
+ return
+ if(!hasRecord)
+ var/datum/data/stored_item/item = new/datum/data/stored_item(src, O.type)
+ stock(O, item)
+ item_records.Add(item)
+
+ user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
- nanomanager.update_uis(src)
+ nanomanager.update_uis(src)
else if(istype(O, /obj/item/weapon/storage/bag))
var/obj/item/weapon/storage/bag/P = O
var/plants_loaded = 0
for(var/obj/G in P.contents)
if(accept_check(G))
- if(contents.len >= max_n_of_items)
- user << "\The [src] is full."
- return 1
- else
- P.remove_from_storage(G,src)
- if(item_quants[G.name])
- item_quants[G.name]++
- else
- item_quants[G.name] = 1
- plants_loaded++
+ plants_loaded++
+ var/hasRecord = FALSE //Check to see if this passes or not.
+ for(var/datum/data/stored_item/I in item_records)
+ if(istype(G, I.item_path))
+ stock(G, I)
+ hasRecord = TRUE
+ if(!hasRecord)
+ var/datum/data/stored_item/item = new/datum/data/stored_item(src, G.type)
+ stock(G, item)
+ item_records.Add(item)
+
if(plants_loaded)
user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].")
@@ -269,6 +335,14 @@
locked = -1
user << "You short out the product lock on [src]."
return 1
+
+/obj/machinery/smartfridge/proc/stock(obj/item/O, var/datum/data/stored_item/I)
+ I.add_product(O)
+ nanomanager.update_uis(src)
+
+/obj/machinery/smartfridge/proc/vend(datum/data/stored_item/I)
+ I.get_product(get_turf(src))
+ nanomanager.update_uis(src)
/obj/machinery/smartfridge/attack_ai(mob/user as mob)
attack_hand(user)
@@ -294,11 +368,11 @@
data["secure"] = is_secure
var/list/items[0]
- for (var/i=1 to length(item_quants))
- var/K = item_quants[i]
- var/count = item_quants[K]
+ for (var/i=1 to length(item_records))
+ var/datum/data/stored_item/I = item_records[i]
+ var/count = I.get_amount()
if(count > 0)
- items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count)))
+ items.Add(list(list("display_name" = html_encode(capitalize(I.item_name)), "vend" = i, "quantity" = count)))
if(items.len > 0)
data["contents"] = items
@@ -325,20 +399,15 @@
if(href_list["vend"])
var/index = text2num(href_list["vend"])
var/amount = text2num(href_list["amount"])
- var/K = item_quants[index]
- var/count = item_quants[K]
+ var/datum/data/stored_item/I = item_records[index]
+ var/count = I.get_amount()
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
if(count > 0)
- item_quants[K] = max(count - amount, 0)
-
- var/i = amount
- for(var/obj/O in contents)
- if(O.name == K)
- O.loc = loc
- i--
- if(i <= 0)
- return 1
+ if((count - amount) < 0)
+ amount = count
+ for(var/i = 1 to amount)
+ vend(I)
return 1
return 0
@@ -349,17 +418,12 @@
if(!target)
return 0
- for (var/O in item_quants)
- if(item_quants[O] <= 0) //Try to use a record that actually has something to dump.
+ for(var/datum/data/stored_item/I in src.item_records)
+ throw_item = I.get_product(loc)
+ if (!throw_item)
continue
-
- item_quants[O]--
- for(var/obj/T in contents)
- if(T.name == O)
- T.loc = src.loc
- throw_item = T
- break
break
+
if(!throw_item)
return 0
spawn(0)
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 8b349959b5b..d7e530b761f 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -108,6 +108,10 @@ steam.start() -- spawns the effect
if (istype(T, /turf))
T.hotspot_expose(1000,100)
+/obj/effect/effect/sparks/initialize()
+ ..()
+ schedule_task_in(5 SECONDS, /proc/qdel, list(src))
+
/obj/effect/effect/sparks/Destroy()
var/turf/T = src.loc
if (istype(T, /turf))
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index e9f784f4451..9f6992d4046 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -275,7 +275,7 @@
/obj/item/weapon/storage/box/metalfoam/New()
..()
for(var/i = 1 to 7)
- new /obj/item/weapon/grenade/chem_grenade/metalfoam
+ new /obj/item/weapon/grenade/chem_grenade/metalfoam(src)
/obj/item/weapon/storage/box/trackimp
name = "boxed tracking implant kit"
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index 6968fde34bc..73ceddb70f6 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -184,6 +184,9 @@
icon_state = "doublebed"
base_icon = "doublebed"
+/obj/structure/bed/double/padded/New(var/newloc)
+ ..(newloc,"wood","cotton")
+
/obj/structure/bed/double/post_buckle_mob(mob/living/M as mob)
if(M == buckled_mob)
M.pixel_y = 13
diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm
index 656f54d4778..27aa76cc306 100644
--- a/code/modules/client/preference_setup/general/01_basic.dm
+++ b/code/modules/client/preference_setup/general/01_basic.dm
@@ -107,7 +107,7 @@ datum/preferences/proc/set_biological_gender(var/gender)
else if(href_list["metadata"])
var/new_metadata = sanitize(input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , pref.metadata)) as message|null
if(new_metadata && CanUseTopic(user))
- pref.metadata = sanitize(new_metadata)
+ pref.metadata = new_metadata
return TOPIC_REFRESH
return ..()
diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm
index 85be34332b2..b99833670d5 100644
--- a/code/modules/paperwork/faxmachine.dm
+++ b/code/modules/paperwork/faxmachine.dm
@@ -14,6 +14,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
use_power = 1
idle_power_usage = 30
active_power_usage = 200
+ circuit = /obj/item/weapon/circuitboard/fax
var/obj/item/weapon/card/id/scan = null // identification
var/authenticated = 0
@@ -22,13 +23,11 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
var/destination = null // the department we're sending to
/obj/machinery/photocopier/faxmachine/New()
- ..()
allfaxes += src
if(!destination) destination = "[boss_name]"
if( !(("[department]" in alldepartments) || ("[department]" in admin_departments)) )
alldepartments |= department
-
- circuit = new circuit(src)
+ ..()
/obj/machinery/photocopier/faxmachine/attack_hand(mob/user as mob)
user.set_machine(src)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 93f928b94f8..5c37577c039 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -82,6 +82,13 @@
var/tmp/told_cant_shoot = 0 //So that it doesn't spam them with the fact they cannot hit them.
var/tmp/lock_time = -100
+ var/dna_lock = 0 //whether or not the gun is locked to dna
+ var/list/stored_dna = list() //list of the dna stored in the gun, used to allow users to use it or not
+ var/safety_level = 0 //either 0 or 1, at 0 the game buzzes and tells the user they can't use it, at 1 it self destructs after 10 seconds
+ var/controller_dna = null //The dna of the person who is the primary controller of the gun
+ var/controller_lock = 0 //whether or not the gun is locked by the primar controller, 0 or 1, at 1 it is locked and does not allow
+ var/exploding = 0
+
/obj/item/weapon/gun/New()
..()
for(var/i in 1 to firemodes.len)
@@ -90,6 +97,11 @@
if(isnull(scoped_accuracy))
scoped_accuracy = accuracy
+ if(!dna_lock)
+ verbs -= /obj/item/weapon/gun/verb/remove_dna
+ verbs -= /obj/item/weapon/gun/verb/give_dna
+ verbs -= /obj/item/weapon/gun/verb/allow_dna
+
/obj/item/weapon/gun/update_held_icon()
if(requires_two_hands)
var/mob/living/M = loc
@@ -114,6 +126,20 @@
return 0
var/mob/living/M = user
+ if(dna_lock && stored_dna)
+ if(!authorized_user(user))
+ if(safety_level == 0)
+ M << "\The [src] buzzes in dissapoint and displays an invalid DNA symbol."
+ return 0
+ if(!exploding)
+ if(safety_level == 1)
+ M << "\The [src] hisses in dissapointment."
+ visible_message("\The [src] announces, \"Self-destruct occurring in ten seconds.\"", "\The [src] announces, \"Self-destruct occurring in ten seconds.\"")
+ sleep(100)
+ explosion(src, -1, 0, 2, 3, 0)
+ exploding = 1
+ qdel(src)
+ return 0
if(HULK in M.mutations)
M << "Your fingers are much too large for the trigger guard!"
return 0
@@ -429,3 +455,80 @@
/obj/item/weapon/gun/attack_self(mob/user)
switch_firemodes(user)
+<<<<<<< HEAD
+=======
+
+/obj/item/weapon/gun/proc/get_dna(mob/user)
+ var/mob/living/M = user
+ if(!controller_lock)
+
+ if(!stored_dna && !(M.dna in stored_dna))
+ M << "\The [src] buzzes and displays a symbol showing the gun already contains your DNA."
+ return 0
+ else
+ stored_dna += M.dna
+ M << "\The [src] pings and a needle flicks out from the grip, taking a DNA sample from you."
+ if(!controller_dna)
+ controller_dna = M.dna
+ M << "\The [src] processes the dna sample and pings, acknowledging you as the primary controller."
+ return 1
+ else
+ M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA samples at this time."
+ return 0
+
+/obj/item/weapon/gun/verb/give_dna()
+ set name = "Give DNA"
+ set category = "Object"
+ set src in usr
+ get_dna(usr)
+
+/obj/item/weapon/gun/proc/clear_dna(mob/user)
+ var/mob/living/M = user
+ if(!controller_lock)
+ if(!authorized_user(M))
+ M << "\The [src] buzzes and displays an invalid user symbol."
+ return 0
+ else
+ stored_dna -= user.dna
+ M << "\The [src] beeps and clears the DNA it has stored."
+ if(M.dna == controller_dna)
+ controller_dna = null
+ M << "\The [src] beeps and removes you as the primary controller."
+ if(controller_lock)
+ controller_lock = 0
+ return 1
+ else
+ M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA modifcation at this time."
+ return 0
+
+/obj/item/weapon/gun/verb/remove_dna()
+ set name = "Remove DNA"
+ set category = "Object"
+ set src in usr
+ clear_dna(usr)
+
+/obj/item/weapon/gun/proc/toggledna(mob/user)
+ var/mob/living/M = user
+ if(authorized_user(M) && user.dna == controller_dna)
+ if(!controller_lock)
+ controller_lock = 1
+ M << "\The [src] beeps and displays a locked symbol, informing you it will no longer allow DNA samples."
+ else
+ controller_lock = 0
+ M << "\The [src] beeps and displays an unlocked symbol, informing you it will now allow DNA samples."
+ else
+ M << "\The [src] buzzes and displays an invalid user symbol."
+
+/obj/item/weapon/gun/verb/allow_dna()
+ set name = "Toggle DNA Samples Allowance"
+ set category = "Object"
+ set src in usr
+ toggledna(usr)
+
+/obj/item/weapon/gun/proc/authorized_user(mob/user)
+ if(!stored_dna || !stored_dna.len)
+ return 1
+ if(!(user.dna in stored_dna))
+ return 0
+ return 1
+>>>>>>> refs/remotes/PolarisSS13/master
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index af531881907..c0ecdbfc3d5 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -510,7 +510,8 @@ other types of metals and chemistry for reagents).
/datum/design/item/weapon/decloner
id = "decloner"
req_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 7, TECH_BIO = 5, TECH_POWER = 6)
- materials = list("gold" = 5000,"uranium" = 10000, "mutagen" = 40)
+ materials = list("gold" = 5000,"uranium" = 10000)
+ chemicals = list("mutagen" = 40)
build_path = /obj/item/weapon/gun/energy/decloner
sort_string = "TAAAE"
diff --git a/code/modules/xenobio2/_xeno_setup.dm b/code/modules/xenobio2/_xeno_setup.dm
index a08cb546812..e77230b3e5c 100644
--- a/code/modules/xenobio2/_xeno_setup.dm
+++ b/code/modules/xenobio2/_xeno_setup.dm
@@ -93,6 +93,12 @@ var/global/list/xenoChemList = list("mutationtoxin",
var/val = traits["[trait]"]
return val
+/datum/xeno/traits/proc/copy_traits(var/datum/xeno/traits/target)
+ target.traits = traits
+ target.chemlist = chemlist
+ target.chems = chems
+ target.source = source
+
/datum/xeno/traits/New()
..()
set_trait(TRAIT_XENO_COLOR, "#CACACA")
diff --git a/code/modules/xenobio2/machinery/core_extractor.dm b/code/modules/xenobio2/machinery/core_extractor.dm
index 421c8ed22f5..e01bc6f8520 100644
--- a/code/modules/xenobio2/machinery/core_extractor.dm
+++ b/code/modules/xenobio2/machinery/core_extractor.dm
@@ -110,7 +110,8 @@
icon_state = "scanner_1old"
for(var/i=1 to occupant.cores)
var/obj/item/xenoproduct/slime/core/C = new(src)
- C.traits = occupant.traitdat
+ C.traits = new()
+ occupant.traitdat.copy_traits(C.traits)
C.nameVar = occupant.nameVar
diff --git a/code/modules/xenobio2/mob/slime/slime procs.dm b/code/modules/xenobio2/mob/slime/slime procs.dm
index 56ad5d60504..adc8f4e5baf 100644
--- a/code/modules/xenobio2/mob/slime/slime procs.dm
+++ b/code/modules/xenobio2/mob/slime/slime procs.dm
@@ -13,11 +13,10 @@ Slime specific procs go here.
traitdat.traits[TRAIT_XENO_HUNGER] = rand(1, 20)
traitdat.traits[TRAIT_XENO_STARVEDAMAGE] = rand(1, 4)
traitdat.traits[TRAIT_XENO_EATS] = prob(95) //Odds are, that thing'll need to eat.
- traitdat.traits[TRAIT_XENO_CHROMATIC] = prob(5)
- if(traitdat.traits[TRAIT_XENO_CHROMATIC])
+ traitdat.traits[TRAIT_XENO_HOSTILE] = prob(60) //Let's increase this probabilty.
+ if(prob(10))
+ traitdat.traits[TRAIT_XENO_CHROMATIC] = 1
traitdat.traits[TRAIT_XENO_HOSTILE] = 0
- else
- traitdat.traits[TRAIT_XENO_HOSTILE] = prob(30)
traitdat.traits[TRAIT_XENO_GLOW_STRENGTH] = round(rand(1,3))
traitdat.traits[TRAIT_XENO_GLOW_RANGE] = round(rand(1,3))
traitdat.traits[TRAIT_XENO_STRENGTH] = round(rand(4,9))
diff --git a/code/modules/xenobio2/mob/xeno procs.dm b/code/modules/xenobio2/mob/xeno procs.dm
index e20373c21ce..94da479f5a5 100644
--- a/code/modules/xenobio2/mob/xeno procs.dm
+++ b/code/modules/xenobio2/mob/xeno procs.dm
@@ -29,10 +29,8 @@ Procs for targeting
set_light(traitdat.traits[TRAIT_XENO_GLOW_RANGE], traitdat.traits[TRAIT_XENO_GLOW_STRENGTH], traitdat.traits[TRAIT_XENO_BIO_COLOR])
else
set_light(0, 0, "#000000") //Should kill any light that shouldn't be there.
- if(chromatic)
- hostile = 0 //No. No laser-reflecting hostile creatures. Bad.
- else
- hostile = traitdat.traits[TRAIT_XENO_HOSTILE]
+
+ hostile = traitdat.traits[TRAIT_XENO_HOSTILE]
speed = traitdat.traits[TRAIT_XENO_SPEED]
diff --git a/code/modules/xenobio2/tools/xeno_trait_scanner.dm b/code/modules/xenobio2/tools/xeno_trait_scanner.dm
index 8063b6071bb..565db473644 100644
--- a/code/modules/xenobio2/tools/xeno_trait_scanner.dm
+++ b/code/modules/xenobio2/tools/xeno_trait_scanner.dm
@@ -111,7 +111,7 @@
dat += "It bears no characters indicating resilience to damage.
"
if(growth_max)
- if(growth_level < 25)
+ if(growth_level < 35)
dat += "It appears to be far to growing up.
"
else if(growth_level > 40)
dat += "It appears to be close to growing up.
"
diff --git a/code/unit_tests/research_tests.dm b/code/unit_tests/research_tests.dm
index 34c63b9d108..93bf79a0da6 100644
--- a/code/unit_tests/research_tests.dm
+++ b/code/unit_tests/research_tests.dm
@@ -40,3 +40,33 @@
issues++
return issues
+
+/datum/unit_test/research_designs_have_valid_materials
+ name = "RESEARCH: Designs Shall Have Valid Materials and Chemicals"
+
+/datum/unit_test/research_designs_have_valid_materials/start_test()
+ var/number_of_issues = 0
+
+ for(var/design_type in typesof(/datum/design) - /datum/design)
+ var/datum/design/design = design_type
+ if(initial(design.id) == "id")
+ continue
+ design = new design_type() // Unfortunately we have to actually instantiate to get a list.
+
+ for(var/material_name in design.materials)
+ var/material/material = get_material_by_name(material_name)
+ if(!material)
+ log_unit_test("The entry [design_type] has invalid material type [material_name]")
+ number_of_issues++
+
+ for(var/reagent_name in design.chemicals)
+ if(!(reagent_name in chemical_reagents_list))
+ log_unit_test("The entry [design_type] has invalid chemical type [reagent_name]")
+ number_of_issues++
+
+ if(number_of_issues)
+ fail("[number_of_issues] issues with research designs found.")
+ else
+ pass("All research designs have valid materials.")
+
+ return 1