diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm
index 3d24c1a3306..ea0ee8803cf 100644
--- a/code/__defines/chemistry.dm
+++ b/code/__defines/chemistry.dm
@@ -62,9 +62,10 @@
#define MAT_SOLVENT_MODERATE 2
#define MAT_SOLVENT_STRONG 3
-#define DIRTINESS_STERILE -2
-#define DIRTINESS_CLEAN -1
-#define DIRTINESS_NEUTRAL 0
+#define DIRTINESS_DECONTAMINATE -3
+#define DIRTINESS_STERILE -2
+#define DIRTINESS_CLEAN -1
+#define DIRTINESS_NEUTRAL 0
#define DEFAULT_GAS_ACCELERANT /decl/material/gas/hydrogen
#define DEFAULT_GAS_OXIDIZER /decl/material/gas/oxygen
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index a458bf40693..b4776030e65 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -197,11 +197,6 @@
#define WRINKLES_WRINKLY 1
#define WRINKLES_NONE 2
-//detergent states for clothes
-#define SMELL_DEFAULT 0
-#define SMELL_CLEAN 1
-#define SMELL_STINKY 2
-
//Shuttle mission stages
#define SHUTTLE_MISSION_PLANNED 1
#define SHUTTLE_MISSION_STARTED 2
diff --git a/code/datums/extensions/scent/_scent.dm b/code/datums/extensions/scent/_scent.dm
index b7ef9d3873d..3b1a52e50ee 100644
--- a/code/datums/extensions/scent/_scent.dm
+++ b/code/datums/extensions/scent/_scent.dm
@@ -6,7 +6,7 @@
Scent intensity
*****/
/decl/scent_intensity
- var/cooldown = 5 MINUTES
+ var/cooldown = 5 MINUTES
var/intensity = 1
/decl/scent_intensity/proc/PrintMessage(var/mob/user, var/descriptor, var/scent)
@@ -121,18 +121,24 @@ To add a scent extension to an atom using a reagent's info, where R. is the reag
*****/
/proc/set_scent_by_reagents(var/atom/smelly_atom)
+ var/decl/material/smelliest = get_smelliest_reagent(smelly_atom.reagents)
+ if(smelliest)
+ set_extension(smelly_atom, /datum/extension/scent/custom, smelliest.scent, smelliest.scent_intensity, smelliest.scent_descriptor, smelliest.scent_range)
+
+// Returns the smelliest reagent of a reagent holder.
+/proc/get_smelliest_reagent(var/datum/reagents/holder)
var/decl/material/smelliest
var/decl/material/scent_intensity
- if(!smelly_atom.reagents || !smelly_atom.reagents.total_volume)
+ if(!holder || !holder.total_volume)
return
- for(var/reagent_type in smelly_atom.reagents.reagent_volumes)
+ for(var/reagent_type in holder.reagent_volumes)
var/decl/material/R = GET_DECL(reagent_type)
if(!R.scent)
continue
var/decl/scent_intensity/SI = GET_DECL(R.scent_intensity)
- var/r_scent_intensity = REAGENT_VOLUME(smelly_atom.reagents, reagent_type) * SI.intensity
+ var/r_scent_intensity = REAGENT_VOLUME(holder, reagent_type) * SI.intensity
if(r_scent_intensity > scent_intensity)
smelliest = R
- scent_intensity = r_scent_intensity
- if(smelliest)
- set_extension(smelly_atom, /datum/extension/scent/custom, smelliest.scent, smelliest.scent_intensity, smelliest.scent_descriptor, smelliest.scent_range)
\ No newline at end of file
+ scent_intensity = r_scent_intensity
+
+ return smelliest
\ No newline at end of file
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 49bdaf4bba7..a36d4b5ab5b 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -1,37 +1,169 @@
#define WASHER_STATE_CLOSED 1
-#define WASHER_STATE_FULL 2
+#define WASHER_STATE_LOADED 2
#define WASHER_STATE_RUNNING 4
#define WASHER_STATE_BLOODY 8
-// WASHER_STATE_RUNNING implies WASHER_STATE_CLOSED | WASHER_STATE_FULL
+// WASHER_STATE_RUNNING implies WASHER_STATE_CLOSED | WASHER_STATE_LOADED
// if you break this assumption, you must update the icon file
// other states are independent.
/obj/machinery/washing_machine
name = "washing machine"
+ desc = "A commerical washing machine used to wash clothing items and linens. It requires detergent for efficient washing."
icon = 'icons/obj/machines/washing_machine.dmi'
icon_state = "wm_00"
density = TRUE
anchored = TRUE
construct_state = /decl/machine_construction/default/panel_closed
uncreated_component_parts = null
- stat_immune = 0
+ stat_immune = NOSCREEN
var/state = 0
- var/gibs_ready = 0
- var/obj/crayon
- var/obj/item/chems/pill/detergent/detergent
+ var/gibs_ready = FALSE
obj_flags = OBJ_FLAG_ANCHORABLE
clicksound = "button"
clickvol = 40
+ var/list/wash_whitelist = list(/obj/item/clothing/under,
+ /obj/item/clothing/mask,
+ /obj/item/clothing/head,
+ /obj/item/clothing/gloves,
+ /obj/item/clothing/shoes,
+ /obj/item/clothing/suit,
+ /obj/item/bedsheet,
+ /obj/item/underwear)
+
+ var/max_item_size = ITEM_SIZE_LARGE
+
+ var/list/wash_blacklist = list(/obj/item/clothing/suit/space,
+ /obj/item/clothing/suit/syndicatefake,
+ /obj/item/clothing/suit/bomb_suit,
+ /obj/item/clothing/suit/armor,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/smokable/cigarette,
+ /obj/item/clothing/head/helmet)
+
// Power
idle_power_usage = 10
active_power_usage = 150
-/obj/machinery/washing_machine/Destroy()
- QDEL_NULL(crayon)
- QDEL_NULL(detergent)
+/obj/machinery/washing_machine/Initialize(mapload, d, populate_parts)
+ create_reagents(100)
+ . = ..()
+
+/obj/machinery/washing_machine/examine(mob/user)
. = ..()
+ to_chat(user, SPAN_NOTICE("The detergent port is [atom_flags & ATOM_FLAG_OPEN_CONTAINER ? "open" : "closed"]."))
+
+/obj/machinery/washing_machine/proc/wash()
+ if(operable())
+ var/list/washing_atoms = get_contained_external_atoms()
+ var/amount_per_atom = FLOOR(reagents.total_volume / length(washing_atoms))
+
+ if(amount_per_atom > 0)
+ var/decl/material/smelliest = get_smelliest_reagent(reagents)
+ for(var/atom/A as anything in get_contained_external_atoms())
+
+ // Handles washing, decontamination, dyeing, etc.
+ reagents.trans_to(A, amount_per_atom)
+
+ if(istype(A, /obj/item/clothing))
+ var/obj/item/clothing/C = A
+ C.ironed_state = WRINKLES_WRINKLY
+ if(smelliest)
+ C.change_smell(smelliest)
+
+ // Clear out whatever remains of the reagents.
+ reagents.clear_reagents()
+
+ if(locate(/mob/living) in src)
+ gibs_ready = TRUE
+
+ state &= ~WASHER_STATE_RUNNING
+ update_use_power(POWER_USE_IDLE)
+
+/obj/machinery/washing_machine/attackby(obj/item/W, mob/user)
+ if(istype(W, /obj/item/chems/pill/detergent))
+ if(!(atom_flags & ATOM_FLAG_OPEN_CONTAINER))
+ to_chat(user, SPAN_WARNING("Open the detergent port first!"))
+ return
+ if(reagents.total_volume >= reagents.maximum_volume)
+ to_chat(user, SPAN_WARNING("The detergent port is full!"))
+ return
+ if(!user.try_unequip(W))
+ return
+ // Directly transfer to the holder to avoid touch reactions.
+ W.reagents?.trans_to_holder(reagents, W.reagents.total_volume)
+ to_chat(user, SPAN_NOTICE("You dissolve \the [W] in the detergent port."))
+ qdel(W)
+ return TRUE
+
+ if(state & WASHER_STATE_RUNNING)
+ to_chat(user, SPAN_WARNING("\The [src] is currently running."))
+ return TRUE
+
+ // If the detergent port is open and the item is an open container, assume we're trying to fill the detergent port.
+ if(!(state & WASHER_STATE_CLOSED) && !((atom_flags & W.atom_flags) & ATOM_FLAG_OPEN_CONTAINER))
+ var/list/washing_atoms = get_contained_external_atoms()
+ if(length(washing_atoms) < 5)
+ if(istype(W, /obj/item/holder)) // Mob holder
+ for(var/mob/living/doggy in W)
+ doggy.forceMove(src)
+ qdel(W)
+ state |= WASHER_STATE_LOADED
+ update_icon()
+ return TRUE
+
+ // An empty whitelist implies all items can be washed.
+ else if((!length(wash_whitelist) || is_type_in_list(W, wash_whitelist)) && !is_type_in_list(W, wash_blacklist))
+ if(W.w_class > max_item_size)
+ to_chat(user, SPAN_WARNING("\The [W] is too large for \the [src]!"))
+ return
+ if(!user.try_unequip(W, src))
+ return
+ state |= WASHER_STATE_LOADED
+ update_icon()
+ else
+ to_chat(user, SPAN_WARNING("You can't put \the [W] in \the [src]."))
+ return
+ else
+ to_chat(user, SPAN_NOTICE("\The [src] is full."))
+ return TRUE
+
+ return ..()
+
+/obj/machinery/washing_machine/physical_attack_hand(mob/user)
+ if(state & WASHER_STATE_RUNNING)
+ to_chat(user, SPAN_WARNING("\The [src] is currently running."))
+ return TRUE
+ if(state & WASHER_STATE_CLOSED)
+ state &= ~WASHER_STATE_CLOSED
+ if(gibs_ready)
+ gibs_ready = FALSE
+ var/mob/M = locate(/mob/living) in src
+ if(M)
+ M.gib()
+ dump_contents()
+ state &= ~WASHER_STATE_LOADED
+ update_icon()
+ return TRUE
+ state |= WASHER_STATE_CLOSED
+ update_icon()
+ return TRUE
+
+/obj/machinery/washing_machine/verb/climb_out()
+ set name = "Climb out"
+ set category = "Object"
+ set src in usr.loc
+
+ if(!CanPhysicallyInteract(usr))
+ return
+ if(state & WASHER_STATE_CLOSED)
+ to_chat(usr, SPAN_WARNING("\The [src] is closed."))
+ return
+ if(!do_after(usr, 2 SECONDS, src))
+ return
+ if(!(state & WASHER_STATE_CLOSED))
+ usr.dropInto(loc)
/obj/machinery/washing_machine/verb/start()
set name = "Start Washing"
@@ -41,24 +173,28 @@
if(!CanPhysicallyInteract(usr))
return
- if(!anchored)
- to_chat(usr, "\The [src] must be secured to the floor.")
- return
+ start_washing(usr)
+/obj/machinery/washing_machine/proc/start_washing(mob/user)
if(state & WASHER_STATE_RUNNING)
- to_chat(usr, "\The [src] is already running.")
- return
- if(!(state & WASHER_STATE_FULL))
- to_chat(usr, "Load \the [src] first!")
+ to_chat(user, SPAN_WARNING("\The [src] is already running!"))
return
if(!(state & WASHER_STATE_CLOSED))
- to_chat(usr, "You must first close the machine.")
+ to_chat(user, SPAN_WARNING("You must first close \the [src]."))
+ return
+ if(!(state & WASHER_STATE_LOADED))
+ to_chat(user, SPAN_WARNING("Load \the [src] first!!"))
+ return
+ if(!operable())
+ to_chat(user, SPAN_WARNING("\The [src] isn't functioning!"))
return
- if(stat & NOPOWER)
- to_chat(usr, SPAN_WARNING("\The [src] is unpowered."))
+ if(!reagents.total_volume)
+ to_chat(user, SPAN_WARNING("There are no cleaning products loaded in \the [src]!"))
return
+ atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER
+
state |= WASHER_STATE_RUNNING
if(locate(/mob/living) in src)
state |= WASHER_STATE_BLOODY
@@ -66,44 +202,55 @@
update_use_power(POWER_USE_ACTIVE)
addtimer(CALLBACK(src, /obj/machinery/washing_machine/proc/wash), 20 SECONDS)
-/obj/machinery/washing_machine/proc/wash()
- for(var/atom/A as anything in get_contained_external_atoms())
- if(detergent)
- A.clean_blood()
- if(isitem(A))
- var/obj/item/I = A
- if(detergent)
- I.decontaminate()
- if(crayon && iscolorablegloves(I))
- var/obj/item/clothing/gloves/C = I
- C.color = crayon.color
- if(istype(A, /obj/item/clothing))
- var/obj/item/clothing/C = A
- C.ironed_state = WRINKLES_WRINKLY
- if(detergent)
- C.change_smell(SMELL_CLEAN)
- addtimer(CALLBACK(C, /obj/item/clothing/proc/change_smell), detergent.smell_clean_time, TIMER_UNIQUE | TIMER_OVERRIDE)
- QDEL_NULL(detergent)
-
- if(locate(/mob/living) in src)
- gibs_ready = 1
- state &= ~WASHER_STATE_RUNNING
- update_use_power(POWER_USE_IDLE)
+ return TRUE
-/obj/machinery/washing_machine/verb/climb_out()
- set name = "Climb out"
+/obj/machinery/washing_machine/verb/toggle_port()
+ set name = "Toggle Detergent Port"
set category = "Object"
- set src in usr.loc
+ set src in oview(1)
if(!CanPhysicallyInteract(usr))
return
- if(state & WASHER_STATE_CLOSED)
- to_chat(usr, SPAN_WARNING("\The [src] is closed."))
- return
- if(!do_after(usr, 2 SECONDS, src))
+
+ toggle_detergent_port(usr)
+
+/obj/machinery/washing_machine/proc/toggle_detergent_port(mob/user)
+ if(state & WASHER_STATE_RUNNING)
+ to_chat(user, SPAN_WARNING("You can't open the detergent port while \the [src] is running!"))
return
- if(!(state & WASHER_STATE_CLOSED))
- usr.dropInto(loc)
+
+ if(atom_flags & ATOM_FLAG_OPEN_CONTAINER)
+ atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER
+ to_chat(user, SPAN_NOTICE("You close the detergent port on \the [src]."))
+ else
+ atom_flags |= ATOM_FLAG_OPEN_CONTAINER
+ to_chat(user, SPAN_NOTICE("You open the detergent port on \the [src]."))
+
+ return TRUE
+
+/obj/machinery/washing_machine/get_alt_interactions(mob/user)
+ . = ..()
+ LAZYADD(., /decl/interaction_handler/start_washer)
+ LAZYADD(., /decl/interaction_handler/toggle_open/washing_machine)
+
+/decl/interaction_handler/start_washer
+ name = "Start washer"
+ expected_target_type = /obj/machinery/washing_machine
+
+/decl/interaction_handler/start_washer/is_possible(obj/machinery/washing_machine/washer, mob/user)
+ . = ..()
+ if(.)
+ return washer.operable() && !(washer.state & WASHER_STATE_RUNNING)
+
+/decl/interaction_handler/start_washer/invoked(obj/machinery/washing_machine/washer, mob/user)
+ return washer.start_washing(user)
+
+/decl/interaction_handler/toggle_open/washing_machine
+ name = "Toggle detergent port"
+ expected_target_type = /obj/machinery/washing_machine
+
+/decl/interaction_handler/toggle_open/washing_machine/invoked(obj/machinery/washing_machine/washer, mob/user)
+ return washer.toggle_detergent_port(user)
/obj/machinery/washing_machine/on_update_icon()
icon_state = "wm_[state][panel_open]"
@@ -116,101 +263,19 @@
/obj/machinery/washing_machine/components_are_accessible(path)
return !(state & WASHER_STATE_RUNNING) && ..()
-/obj/machinery/washing_machine/attackby(obj/item/W, mob/user)
- if(!(state & WASHER_STATE_CLOSED))
- if(!crayon && IS_PEN(W))
- if(!user.try_unequip(W, src))
- return
- crayon = W
- return TRUE
- if(!detergent && istype(W,/obj/item/chems/pill/detergent))
- if(!user.try_unequip(W, src))
- return
- detergent = W
- return TRUE
- if(istype(W, /obj/item/holder)) // Mob holder
- for(var/mob/living/doggy in W)
- doggy.forceMove(src)
- qdel(W)
- state |= WASHER_STATE_FULL
- update_icon()
- return TRUE
-
- else if(istype(W,/obj/item/clothing/under) || \
- istype(W,/obj/item/clothing/mask) || \
- istype(W,/obj/item/clothing/head) || \
- istype(W,/obj/item/clothing/gloves) || \
- istype(W,/obj/item/clothing/shoes) || \
- istype(W,/obj/item/clothing/suit) || \
- istype(W,/obj/item/bedsheet) || \
- istype(W,/obj/item/underwear/))
-
- //YES, it's hardcoded... saves a var/can_be_washed for every single clothing item.
- if ( istype(W,/obj/item/clothing/suit/space ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/suit/syndicatefake ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/suit/bomb_suit ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/suit/armor ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/suit/armor ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/mask/gas ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/mask/smokable/cigarette ) )
- to_chat(user, "This item does not fit.")
- return
- if ( istype(W,/obj/item/clothing/head/helmet ) )
- to_chat(user, "This item does not fit.")
- return
+/obj/machinery/washing_machine/autoclave
+ name = "autoclave"
+ desc = "An industrial washing machine used to sterilize and decontaminate items. It requires detergent for efficient decontamination."
- if(contents.len < 5)
- if(!(state & WASHER_STATE_CLOSED))
- if(!user.try_unequip(W, src))
- return
- state |= WASHER_STATE_FULL
- update_icon()
- else
- to_chat(user, SPAN_NOTICE("You can't put the item in right now."))
- else
- to_chat(user, SPAN_NOTICE("\The [src] is full."))
- return TRUE
-
- if(state & WASHER_STATE_RUNNING)
- to_chat(user, SPAN_WARNING("\The [src] is currently running."))
- return TRUE
+ wash_whitelist = list()
+ wash_blacklist = list()
- return ..()
+ max_item_size = ITEM_SIZE_HUGE
-/obj/machinery/washing_machine/physical_attack_hand(mob/user)
- if(state & WASHER_STATE_RUNNING)
- to_chat(user, SPAN_WARNING("\The [src] is busy."))
- return TRUE
- if(state & WASHER_STATE_CLOSED)
- state &= ~WASHER_STATE_CLOSED
- if(gibs_ready)
- gibs_ready = 0
- var/mob/M = locate(/mob/living) in src
- if(M)
- M.gib()
- dump_contents()
- state &= ~WASHER_STATE_FULL
- update_icon()
- crayon = null
- detergent = null
- return TRUE
- state |= WASHER_STATE_CLOSED
- update_icon()
- return TRUE
+ idle_power_usage = 10
+ active_power_usage = 300
#undef WASHER_STATE_CLOSED
-#undef WASHER_STATE_FULL
+#undef WASHER_STATE_LOADED
#undef WASHER_STATE_RUNNING
#undef WASHER_STATE_BLOODY
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/circuitboards/machinery/household.dm b/code/game/objects/items/weapons/circuitboards/machinery/household.dm
index bcc108137fc..db7de25060f 100644
--- a/code/game/objects/items/weapons/circuitboards/machinery/household.dm
+++ b/code/game/objects/items/weapons/circuitboards/machinery/household.dm
@@ -90,6 +90,17 @@
/obj/item/stock_parts/matter_bin = 1,
/obj/item/pipe = 1)
+/obj/item/stock_parts/circuitboard/autoclave
+ name = "circuitboard (autoclave)"
+ build_path = /obj/machinery/washing_machine/autoclave
+ board_type = "machine"
+ origin_tech = "{'engineering':3, 'biotech':2}"
+ req_components = list(
+ /obj/item/stock_parts/manipulator = 2,
+ /obj/item/stock_parts/micro_laser = 1,
+ /obj/item/stock_parts/matter_bin = 2,
+ /obj/item/pipe = 1)
+
/obj/item/stock_parts/circuitboard/vending
name = "circuitboard (vending machine)"
build_path = /obj/machinery/vending/assist
diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm
index 782c800064e..6ea54072f55 100644
--- a/code/game/objects/items/weapons/soap.dm
+++ b/code/game/objects/items/weapons/soap.dm
@@ -12,7 +12,7 @@
throwforce = 0
throw_speed = 4
throw_range = 20
- material = /decl/material/liquid/cleaner
+ material = /decl/material/liquid/cleaner/soap
max_health = 5
var/key_data
@@ -105,13 +105,15 @@
/obj/item/soap/attackby(var/obj/item/I, var/mob/user)
if(istype(I, /obj/item/key))
- if(!key_data)
+ if(key_data)
+ to_chat(user, SPAN_WARNING("\The [src] already has a key imprint."))
+ else
to_chat(user, SPAN_NOTICE("You imprint \the [I] into \the [src]."))
var/obj/item/key/K = I
key_data = K.key_data
update_icon()
- return
- ..()
+ return TRUE
+ return ..()
/obj/item/soap/on_update_icon()
. = ..()
diff --git a/code/game/objects/structures/ironing_board.dm b/code/game/objects/structures/ironing_board.dm
index fe42db90178..2016ff845b8 100644
--- a/code/game/objects/structures/ironing_board.dm
+++ b/code/game/objects/structures/ironing_board.dm
@@ -150,4 +150,5 @@
name = "ironing board"
desc = "A collapsed ironing board that can be carried around."
icon = 'icons/obj/structures/ironing.dmi'
+ icon_state = "folded"
structure_form_type = /obj/structure/bed/roller/ironingboard
\ No newline at end of file
diff --git a/code/modules/ZAS/Contaminants.dm b/code/modules/ZAS/Contaminants.dm
index 4d99a5c6c09..0ab5091a175 100644
--- a/code/modules/ZAS/Contaminants.dm
+++ b/code/modules/ZAS/Contaminants.dm
@@ -114,13 +114,14 @@ var/global/image/contamination_overlay = image('icons/effects/contamination.dmi'
/mob/living/carbon/human/proc/contaminant_head_protected()
//Checks if the head is adequately sealed.
var/obj/item/head = get_equipped_item(slot_head_str)
- if(head)
- if(vsc.contaminant_control.STRICT_PROTECTION_ONLY)
- if(head.item_flags & ITEM_FLAG_NO_CONTAMINATION)
- return 1
- else if(head.body_parts_covered & SLOT_EYES)
- return 1
- return 0
+ if(!head)
+ return FALSE
+ // If strict protection is on, you must have a head item with ITEM_FLAG_NO_CONTAMINATION.
+ if(vsc.contaminant_control.STRICT_PROTECTION_ONLY)
+ if(!(head.item_flags & ITEM_FLAG_NO_CONTAMINATION))
+ return FALSE
+ // Regardless, the head item must cover the face and head. Eyes are checked seperately above.
+ return BIT_TEST_ALL(head.body_parts_covered, SLOT_HEAD|SLOT_FACE)
/mob/living/carbon/human/proc/contaminant_suit_protected()
//Checks if the suit is adequately sealed.
@@ -130,11 +131,11 @@ var/global/image/contamination_overlay = image('icons/effects/contamination.dmi'
if(!istype(protection))
continue
if(vsc.contaminant_control.STRICT_PROTECTION_ONLY && !(protection.item_flags & ITEM_FLAG_NO_CONTAMINATION))
- return 0
+ return FALSE
coverage |= protection.body_parts_covered
if(vsc.contaminant_control.STRICT_PROTECTION_ONLY)
- return 1
+ return TRUE
return BIT_TEST_ALL(coverage, SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_FEET|SLOT_ARMS|SLOT_HANDS)
diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm
index 2cb413e41d0..1dedcd609ab 100644
--- a/code/modules/clothing/_clothing.dm
+++ b/code/modules/clothing/_clothing.dm
@@ -17,7 +17,6 @@
var/blood_overlay_type = "uniformblood"
var/visible_name = "Unknown"
var/ironed_state = WRINKLES_DEFAULT
- var/smell_state = SMELL_DEFAULT
var/move_trail = /obj/effect/decal/cleanable/blood/tracks/footprints // if this item covers the feet, the footprints it should leave
var/volume_multiplier = 1
var/markings_icon // simple colored overlay that would be applied to the icon
@@ -105,8 +104,14 @@
if(LAZYLEN(new_overlays))
add_overlay(new_overlays)
-/obj/item/clothing/proc/change_smell(smell = SMELL_DEFAULT)
- smell_state = smell
+// Used by washing machines to temporarily make clothes smell
+/obj/item/clothing/proc/change_smell(decl/material/odorant, time = 10 MINUTES)
+ if(!odorant || !odorant.scent)
+ remove_extension(src, /datum/extension/scent)
+ return
+
+ set_extension(src, /datum/extension/scent/custom, odorant.scent, odorant.scent_intensity, odorant.scent_descriptor, odorant.scent_range)
+ addtimer(CALLBACK(src, /obj/item/clothing/proc/change_smell), time, TIMER_UNIQUE | TIMER_OVERRIDE)
/obj/item/clothing/proc/get_fibers()
. = "material from \a [name]"
@@ -192,12 +197,6 @@
if(WRINKLES_NONE)
to_chat(user, "It's completely wrinkle-free!")
- switch(smell_state)
- if(SMELL_CLEAN)
- to_chat(user, "It smells clean!")
- if(SMELL_STINKY)
- to_chat(user, "It's quite stinky!")
-
var/rags = RAG_COUNT(src)
if(rags)
to_chat(user, SPAN_SUBTLE("With a sharp object, you could cut \the [src] up into [rags] rag\s."))
diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm
index 5bed5fbad53..dc96ba35557 100644
--- a/code/modules/clothing/suits/utility.dm
+++ b/code/modules/clothing/suits/utility.dm
@@ -137,7 +137,7 @@
/obj/item/clothing/head/chem_hood
name = "chemical hood"
- desc = "A hood that protects the head from chemical comtaminants."
+ desc = "A hood that protects the head from chemical contaminants."
icon = 'icons/clothing/head/chem_hood.dmi'
permeability_coefficient = 0
armor = list(
@@ -146,7 +146,7 @@
)
flags_inv = HIDEEARS|BLOCK_HEAD_HAIR
item_flags = ITEM_FLAG_THICKMATERIAL
- body_parts_covered = SLOT_HEAD|SLOT_EARS
+ body_parts_covered = SLOT_HEAD|SLOT_EARS|SLOT_FACE
siemens_coefficient = 0.9
matter = list(
/decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT,
diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm
index f14dba7c369..e4b7a8c647a 100644
--- a/code/modules/fabrication/designs/general/designs_general.dm
+++ b/code/modules/fabrication/designs/general/designs_general.dm
@@ -167,4 +167,9 @@
path = /obj/item/stack/package_wrap/gift
/datum/fabricator_recipe/network_pos
- path = /obj/item/network_pos
\ No newline at end of file
+ path = /obj/item/network_pos
+/datum/fabricator_recipe/clothes_iron
+ path = /obj/item/ironingiron
+
+/datum/fabricator_recipe/ironing_board
+ path = /obj/item/roller/ironingboard
diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm
index 9da9958314b..e3113f148e1 100644
--- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm
+++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm
@@ -341,6 +341,9 @@
/datum/fabricator_recipe/imprinter/circuit/washer
path = /obj/item/stock_parts/circuitboard/washer
+/datum/fabricator_recipe/imprinter/circuit/autoclave
+ path = /obj/item/stock_parts/circuitboard/autoclave
+
/datum/fabricator_recipe/imprinter/circuit/microwave
path = /obj/item/stock_parts/circuitboard/microwave
diff --git a/code/modules/locks/key.dm b/code/modules/locks/key.dm
index 70a2f9869b7..b4ded50c995 100644
--- a/code/modules/locks/key.dm
+++ b/code/modules/locks/key.dm
@@ -4,21 +4,20 @@
icon = 'icons/obj/items/key.dmi'
icon_state = "keys"
w_class = ITEM_SIZE_TINY
- material = DEFAULT_FURNITURE_MATERIAL
+ material = /decl/material/solid/metal/brass
+ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC
var/key_data = ""
-/obj/item/key/Initialize(mapload,var/data)
- . = ..(mapload)
- if(data)
- key_data = data
+/obj/item/key/Initialize(var/mapload, var/material_key, var/new_key_data)
+ . = ..(mapload, material_key)
+ if(new_key_data)
+ key_data = new_key_data
/obj/item/key/proc/get_data(var/mob/user)
return key_data
/obj/item/key/soap
- name = "soap key"
- desc = "a fragile key made using a bar of soap."
- material = /decl/material/liquid/cleaner
+ material = /decl/material/liquid/cleaner/soap
var/uses = 0
/obj/item/key/soap/get_data(var/mob/user)
diff --git a/code/modules/locks/lock_construct.dm b/code/modules/locks/lock_construct.dm
index 393fd09c1a1..beacb06758b 100644
--- a/code/modules/locks/lock_construct.dm
+++ b/code/modules/locks/lock_construct.dm
@@ -14,7 +14,7 @@
lock_data = generateRandomString(round(material.integrity/50))
/obj/item/lock_construct/attackby(var/obj/item/I, var/mob/user)
- if(istype(I,/obj/item/key))
+ if(istype(I, /obj/item/key))
var/obj/item/key/K = I
if(!K.key_data)
to_chat(user, SPAN_NOTICE("You fashion \the [I] to unlock \the [src]."))
diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm
index 4cd7ceb25aa..da7ec8fbe50 100644
--- a/code/modules/materials/_materials.dm
+++ b/code/modules/materials/_materials.dm
@@ -226,6 +226,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay)
var/fruit_descriptor // String added to fruit desc if this chemical is present.
var/dirtiness = DIRTINESS_NEUTRAL // How dirty turfs are after being exposed to this material. Negative values cause a cleaning/sterilizing effect.
+ var/decontamination_dose = 0 // Amount required for a decontamination effect, if any.
var/solvent_power = MAT_SOLVENT_NONE
var/solvent_melt_dose = 0
var/solvent_max_damage = 0
@@ -572,6 +573,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay)
else if(defoliant && istype(O, /obj/effect/vine))
qdel(O)
else
+ if(dirtiness <= DIRTINESS_DECONTAMINATE)
+ if(amount >= decontamination_dose && istype(O, /obj/item))
+ var/obj/item/I = O
+ if(I.contaminated)
+ I.decontaminate()
if(dirtiness <= DIRTINESS_STERILE)
O.germ_level -= min(REAGENT_VOLUME(holder, type)*20, O.germ_level)
O.was_bloodied = null
diff --git a/code/modules/reagents/chems/chems_cleaner.dm b/code/modules/reagents/chems/chems_cleaner.dm
index 2b977a8380b..5bd2e9454e5 100644
--- a/code/modules/reagents/chems/chems_cleaner.dm
+++ b/code/modules/reagents/chems/chems_cleaner.dm
@@ -9,3 +9,31 @@
turf_touch_threshold = 0.1
uid = "chem_cleaner"
exoplanet_rarity_gas = MAT_RARITY_EXOTIC
+
+/decl/material/liquid/contaminant_cleaner
+ name = "akaline detergent"
+ lore_text = "A highly akaline hydrazine based detergent. Able to clean contaminants, but may release ammonia gas if used in open air."
+ taste_description = "bleach"
+ vapor_products = list(/decl/material/gas/ammonia = 0.5)
+ color = "#213799"
+ touch_met = 5
+ toxicity = 5
+ scent = "clean linen"
+ scent_descriptor = SCENT_DESC_FRAGRANCE
+ value = 0.25
+ dirtiness = DIRTINESS_DECONTAMINATE
+ decontamination_dose = 5
+ turf_touch_threshold = 0.1
+ uid = "chem_contaminant_cleaner"
+ exoplanet_rarity_gas = MAT_RARITY_EXOTIC
+
+/decl/material/liquid/cleaner/soap
+ name = "soap"
+ lore_text = "A soft solid compound used to clean things. Usually derived from oil or fat."
+ taste_description = "waxy blandness"
+ color = COLOR_BEIGE
+ uid = "chem_soap"
+ melting_point = 323
+ ignition_point = 353
+ boiling_point = 373
+ accelerant_value = 0.3
diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm
index 79add600924..1e04eee2dd2 100644
--- a/code/modules/reagents/chems/chems_nutriment.dm
+++ b/code/modules/reagents/chems/chems_nutriment.dm
@@ -10,9 +10,16 @@
uid = "chem_nutriment"
exoplanet_rarity_gas = MAT_RARITY_NOWHERE // Please, no more animal protein or glowsap or corn oil atmosphere.
+ // Technically a room-temperature solid, but saves
+ // repathing it to /solid all over the codebase.
+ melting_point = 323
+ ignition_point = 353
+ boiling_point = 373
+ accelerant_value = 0.65
+
var/nutriment_factor = 10 // Per unit
var/hydration_factor = 0 // Per unit
- var/injectable = 0
+ var/injectable = FALSE
/decl/material/liquid/nutriment/mix_data(var/datum/reagents/reagents, var/list/newdata, var/newamount)
@@ -102,6 +109,8 @@
taste_description = "egg"
color = "#ffffaa"
uid = "chem_nutriment_egg"
+ melting_point = 273
+ boiling_point = 373
//vegetamarian alternative that is safe for vegans to ingest//rewired it from its intended nutriment/protein/egg/softtofu because it would not actually work, going with plan B, more recipes.
@@ -120,6 +129,8 @@
color = "#ffff00"
fruit_descriptor = "rich"
uid = "chem_nutriment_honey"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/flour
name = "flour"
@@ -145,6 +156,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_batter"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/batter/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder)
..()
@@ -257,6 +270,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_soysauce"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/ketchup
name = "ketchup"
@@ -267,6 +282,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_ketchup"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/banana_cream
name = "banana cream"
@@ -276,6 +293,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_bananacream"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/barbecue
name = "barbecue sauce"
@@ -286,6 +305,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_bbqsauce"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/garlicsauce
name = "garlic sauce"
@@ -296,6 +317,8 @@
exoplanet_rarity_plant = MAT_RARITY_NOWHERE
exoplanet_rarity_gas = MAT_RARITY_NOWHERE
uid = "chem_nutriment_garlicsauce"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/rice
name = "rice"
@@ -326,6 +349,8 @@
color = "#801e28"
fruit_descriptor = "sweet"
uid = "chem_nutriment_cherryjelly"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/cornoil
name = "corn oil"
@@ -336,6 +361,8 @@
color = "#302000"
slipperiness = 8
uid = "chem_nutriment_cornoil"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/sprinkles
name = "sprinkles"
@@ -369,6 +396,8 @@
color = "#e8dfd0"
taste_mult = 3
uid = "chem_nutriment_vinegar"
+ melting_point = 273
+ boiling_point = 373
/decl/material/liquid/nutriment/mayo
name = "mayonnaise"
diff --git a/code/modules/reagents/reactions/reaction_drugs.dm b/code/modules/reagents/reactions/reaction_drugs.dm
index 03eac2e230b..854f479a657 100644
--- a/code/modules/reagents/reactions/reaction_drugs.dm
+++ b/code/modules/reagents/reactions/reaction_drugs.dm
@@ -191,6 +191,12 @@
mix_message = "The solution becomes slick and soapy."
result_amount = 2
+/decl/chemical_reaction/contaminant_cleaner
+ name = "Akaline Detergent"
+ result = /decl/material/liquid/contaminant_cleaner
+ required_reagents = list(/decl/material/solid/sodium = 1, /decl/material/liquid/surfactant = 1)
+ result_amount = 2
+
/decl/chemical_reaction/plantbgone
name = "Plant-B-Gone"
result = /decl/material/liquid/weedkiller
diff --git a/code/modules/reagents/reactions/reaction_other.dm b/code/modules/reagents/reactions/reaction_other.dm
index 6088fb606f1..f7dd8eb027a 100644
--- a/code/modules/reagents/reactions/reaction_other.dm
+++ b/code/modules/reagents/reactions/reaction_other.dm
@@ -13,7 +13,7 @@
/decl/chemical_reaction/soap_key/on_reaction(var/datum/reagents/holder)
var/obj/item/soap/S = holder.get_reaction_loc(chemical_reaction_flags)
if(istype(S) && S.key_data)
- var/obj/item/key/soap/key = new(get_turf(S), S.key_data)
+ var/obj/item/key/soap/key = new(get_turf(S), null, S.key_data)
key.uses = strength
..()
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index 712e8426a1c..176964aaaaf 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -259,14 +259,13 @@
name = "detergent pod"
desc = "Put in water to get space cleaner. Do not eat. Really."
icon_state = "pod21"
- var/smell_clean_time = 10 MINUTES
// Don't overwrite the custom name.
/obj/item/chems/pill/detergent/update_container_name()
return
/obj/item/chems/pill/detergent/populate_reagents()
- reagents.add_reagent(/decl/material/gas/ammonia, 30)
+ reagents.add_reagent(/decl/material/liquid/contaminant_cleaner, 30)
/obj/item/chems/pill/pod
name = "master flavorpod item"
diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm
index eada8b40341..b3facf35c84 100644
--- a/code/modules/vehicles/cargo_train.dm
+++ b/code/modules/vehicles/cargo_train.dm
@@ -6,19 +6,20 @@
on = 0
powered = 1
locked = 0
-
load_item_visible = 1
load_offset_x = 0
buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 7)
-
- var/car_limit = 3 //how many cars an engine can pull before performance degrades
charge_use = 1 KILOWATTS
active_engines = 1
+ var/car_limit = 3 //how many cars an engine can pull before performance degrades
var/obj/item/key/cargo_train/key
/obj/item/key/cargo_train
- name = "key"
- desc = "A keyring with a small steel key, and a yellow fob reading \"Choo Choo!\"."
+ desc = "A small key on a yellow fob reading \"Choo Choo!\"."
+ material = /decl/material/solid/metal/steel
+ matter = list(
+ /decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT
+ )
icon = 'icons/obj/vehicles.dmi'
icon_state = "train_keys"
w_class = ITEM_SIZE_TINY
diff --git a/mods/persistence/modules/clothing/_clothing.dm b/mods/persistence/modules/clothing/_clothing.dm
index 1f6e33b8a39..35be9cc2473 100644
--- a/mods/persistence/modules/clothing/_clothing.dm
+++ b/mods/persistence/modules/clothing/_clothing.dm
@@ -1,5 +1,5 @@
/obj/item/clothing/Initialize()
- // Call on_attached on each accessory to allow for the addition of verbs etc.
+ // Call on_attached on each accessory to allow for the addition of verbs etc.
if(persistent_id)
if(islist(starting_accessories))
starting_accessories.Cut()
@@ -16,7 +16,6 @@
SAVED_VAR(/obj/item/clothing, tint)
SAVED_VAR(/obj/item/clothing, accessories)
SAVED_VAR(/obj/item/clothing, ironed_state)
-SAVED_VAR(/obj/item/clothing, smell_state)
//Underwears are dumb
SAVED_VAR(/obj/item/underwear, icon)