Cleanable cleanup (#52477)

* Don't initialize the atom_colours list on atoms until it's actually needed

* Moved bloody_hands var to mob/living/carbon/human instead

* Added COMSIG_COMPONENT_CLEAN_RADIATION signal to reduce moms spaghetti

The shower and suit storage unit now calls this signal instead of either doing it manually or doing it via the washed proc

* Cleaned up carbon washing, renamed washed to wash

* The wash proc now doesn't take the washer as first arg because that wasn't used anywhere
* The wash strength is no longer optional
* Carbons now overrides the wash proc instead of using the signal
* Properly check for obscuredness before washing any equipped items
* Properly wash all items and bloody hands etc
* Added clean_lips proc for humans for cleaning any lipstick

* Cleaned up washing. Washy stuff now calls wash instead of calling the clean signal directly

* Renamed is_cleanable to ismopable, gives this category a more fitting purpose. Many things beyond floor decals are cleanable. It is now also determined using the atom layer instead to make it more generic.
* Properly utilize the is_cleanable define
* Added wash override for turfs where they also wash any mopables on the same tile
* Space cleaner and cleaning element etc now simply washes the mob instead of doing its own manual cleaning on ~some~ equipped items

* Non-component washables now simply override wash instead of registering for the clean signal

* Fixed some left over clean signal registers not returning true

* Added clean_strength var to space cleaner

* Moved human wash proc next to the other washing procs

* Also wash glasses and mask if not obscured when washing face

* Fixed attempting to "scoop up" cleanable decals using a rag

* Fixed plasmaman spacehelm icon not updating when washed

Also removed a duplicated worn_overlays proc

* Fixed head icon not updating when washing lipstick

* Moved radioactive clean signal register to where it should be

* Added atom radiate VV verb for debugging

* Redesigned the CLEAN constants into a more sensible flags setup

This makes it more dynamic, cleaning apparatuses can clean more specific than just a cleaning strength.

* CLEAN_TYPE_* flags indicate a specific cleanable, such as blood, fingerprints or disease
* CLEAN_* consts consist of a combination of cleaning types to make cleaning apparatuses have a consistent behaviour on what they clean

* Fixed broken rad removal logic in showers

* Apply suggestions from code review

Co-authored-by: Rohesie <rohesie@gmail.com>

* Removed unneccesary bool from sink code

* Fixed wrongly named variable in turf wash

* Renamed bloody_hands to blood_in_hands

Co-authored-by: Rohesie <rohesie@gmail.com>
This commit is contained in:
Daniel Hultgren
2020-07-29 14:31:38 +02:00
committed by GitHub
parent cb9f81e43c
commit 0741b1977f
41 changed files with 303 additions and 248 deletions
+19 -17
View File
@@ -1,18 +1,20 @@
//Cleaning tool strength
// 1 is also a valid cleaning strength but completely unused so left undefined
#define CLEAN_WEAK 2
/// Acceptable tools
#define CLEAN_MEDIUM 3
/// Industrial strength
#define CLEAN_STRONG 4
/// Cleaning strong enough your granny would be proud
#define CLEAN_IMPRESSIVE 5
/// Cleans things spotless down to the atomic structure
#define CLEAN_GOD 6
/// Never cleaned
#define CLEAN_NEVER 7
//How strong things have to be to wipe forensic evidence...
#define CLEAN_STRENGTH_FINGERPRINTS CLEAN_IMPRESSIVE
#define CLEAN_STRENGTH_BLOOD CLEAN_WEAK
#define CLEAN_STRENGTH_FIBERS CLEAN_IMPRESSIVE
// Cleaning flags
// Different kinds of things that can be cleaned.
// Use these when overriding the wash proc or registering for the clean signals to check if your thing should be cleaned
#define CLEAN_TYPE_BLOOD (1 << 0)
#define CLEAN_TYPE_RUNES (1 << 1)
#define CLEAN_TYPE_FINGERPRINTS (1 << 2)
#define CLEAN_TYPE_FIBERS (1 << 3)
#define CLEAN_TYPE_RADIATION (1 << 4)
#define CLEAN_TYPE_DISEASE (1 << 5)
#define CLEAN_TYPE_WEAK (1 << 6) // Special type, add this flag to make some cleaning processes non-instant. Currently only used for showers when removing radiation.
#define CLEAN_TYPE_PAINT (1 << 7)
// Different cleaning methods.
// Use these when calling the wash proc for your cleaning apparatus
#define CLEAN_WASH (CLEAN_TYPE_BLOOD | CLEAN_TYPE_RUNES | CLEAN_TYPE_DISEASE)
#define CLEAN_SCRUB (CLEAN_WASH | CLEAN_TYPE_FINGERPRINTS | CLEAN_TYPE_FIBERS | CLEAN_TYPE_PAINT)
#define CLEAN_RAD CLEAN_TYPE_RADIATION
#define CLEAN_ALL (ALL & ~CLEAN_TYPE_WEAK)
+2 -1
View File
@@ -613,7 +613,8 @@
#define COMSIG_TURF_IS_WET "check_turf_wet"
///(max_strength, immediate, duration_decrease = INFINITY): Returns bool.
#define COMSIG_TURF_MAKE_DRY "make_turf_try"
///called on an object to clean it of cleanables. Usualy with soap: (num/strength)
///Called on an object to "clean it", such as removing blood decals/overlays, etc. The clean types bitfield is sent with it. Return TRUE if any cleaning was necessary and thus performed.
#define COMSIG_COMPONENT_CLEAN_ACT "clean_act"
//Creamed
+1 -1
View File
@@ -172,7 +172,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define ismecha(A) (istype(A, /obj/mecha))
#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable
#define ismopable(A) (A.layer <= HIGH_SIGIL_LAYER) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
#define isorgan(A) (istype(A, /obj/item/organ))
+1
View File
@@ -83,6 +83,7 @@
#define VV_HK_TRIGGER_EMP "empulse"
#define VV_HK_TRIGGER_EXPLOSION "explode"
#define VV_HK_AUTO_RENAME "auto_rename"
#define VV_HK_RADIATE "radiate"
// /obj
#define VV_HK_OSAY "osay"
+3 -2
View File
@@ -57,6 +57,7 @@ GLOBAL_LIST_INIT(creamable, typecacheof(list(
COMSIG_COMPONENT_CLEAN_FACE_ACT))
///Callback to remove pieface
/datum/component/creamed/proc/clean_up(datum/source, strength)
if(strength >= CLEAN_WEAK)
/datum/component/creamed/proc/clean_up(datum/source, clean_types)
if(clean_types & CLEAN_TYPE_BLOOD)
qdel(src)
return TRUE
+5 -4
View File
@@ -7,7 +7,7 @@
var/first_dir // This only stores the dir arg from init
/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_NEVER, _color, _layer=TURF_LAYER, _description, _alpha=255)
/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=FALSE, _color, _layer=TURF_LAYER, _description, _alpha=255)
if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha))
return COMPONENT_INCOMPATIBLE
first_dir = _dir
@@ -19,7 +19,7 @@
/datum/component/decal/RegisterWithParent()
if(first_dir)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable != CLEAN_NEVER)
if(cleanable != FALSE)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
if(description)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
@@ -80,9 +80,10 @@
pic.dir = turn(pic.dir, dir2angle(old_dir) - dir2angle(new_dir))
apply()
/datum/component/decal/proc/clean_react(datum/source, strength)
if(strength >= cleanable)
/datum/component/decal/proc/clean_react(datum/source, clean_types)
if(clean_types & cleanable)
qdel(src)
return TRUE
/datum/component/decal/proc/examine(datum/source, mob/user, list/examine_list)
examine_list += description
+1 -1
View File
@@ -1,7 +1,7 @@
/datum/component/decal/blood
dupe_mode = COMPONENT_DUPE_UNIQUE
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_STRENGTH_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_TYPE_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
+7 -4
View File
@@ -51,13 +51,16 @@
fibers = null
return TRUE
/datum/component/forensics/proc/clean_act(datum/source, strength)
if(strength >= CLEAN_STRENGTH_FINGERPRINTS)
/datum/component/forensics/proc/clean_act(datum/source, clean_types)
if(clean_types & CLEAN_TYPE_FINGERPRINTS)
wipe_fingerprints()
if(strength >= CLEAN_STRENGTH_BLOOD)
. = TRUE
if(clean_types & CLEAN_TYPE_BLOOD)
wipe_blood_DNA()
if(strength >= CLEAN_STRENGTH_FIBERS)
. = TRUE
if(clean_types & CLEAN_TYPE_FIBERS)
wipe_fibers()
. = TRUE
/datum/component/forensics/proc/add_fingerprint_list(list/_fingerprints) //list(text)
if(!length(_fingerprints))
+5 -4
View File
@@ -2,7 +2,7 @@
dupe_mode = COMPONENT_DUPE_ALLOWED
var/list/datum/disease/diseases //make sure these are the static, non-processing versions!
var/expire_time
var/min_clean_strength = CLEAN_WEAK
var/required_clean_types = CLEAN_TYPE_DISEASE
/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in)
if(islist(_diseases))
@@ -12,7 +12,7 @@
if(expire_in)
expire_time = world.time + expire_in
QDEL_IN(src, expire_in)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean)
@@ -34,9 +34,10 @@
eater.ForceContractDisease(V)
try_infect(feeder, BODY_ZONE_L_ARM)
/datum/component/infective/proc/clean(datum/source, clean_strength)
if(clean_strength >= min_clean_strength)
/datum/component/infective/proc/clean(datum/source, clean_types)
if(clean_types & required_clean_types)
qdel(src)
return TRUE
/datum/component/infective/proc/try_infect_buckle(datum/source, mob/M, force)
if(isliving(M))
+16
View File
@@ -18,6 +18,7 @@
can_contaminate = _can_contaminate
if(istype(parent, /atom))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/rad_examine)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/rad_clean)
if(istype(parent, /obj/item))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/rad_attack)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_OBJ, .proc/rad_attack)
@@ -90,6 +91,21 @@
return
strength -= strength / hl3_release_date
/datum/component/radioactive/proc/rad_clean(datum/source, clean_types)
if(QDELETED(src))
return
if(!(clean_types & CLEAN_TYPE_RADIATION))
return
if(!(clean_types & CLEAN_TYPE_WEAK))
qdel(src)
return
strength = max(0, (strength - (RAD_BACKGROUND_RADIATION * 2)))
if(strength <= RAD_BACKGROUND_RADIATION)
qdel(src)
#undef RAD_AMOUNT_LOW
#undef RAD_AMOUNT_MEDIUM
#undef RAD_AMOUNT_HIGH
+1
View File
@@ -82,6 +82,7 @@
/datum/component/thermite/proc/clean_react(datum/source, strength)
//Thermite is just some loose powder, you could probably clean it with your hands. << todo?
qdel(src)
return TRUE
/datum/component/thermite/proc/flame_react(datum/source, exposed_temperature, exposed_volume)
if(exposed_temperature > 1922) // This is roughly the real life requirement to ignite thermite
+7 -17
View File
@@ -14,27 +14,17 @@
if(!isturf(tile))
return
SEND_SIGNAL(tile, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
tile.wash(CLEAN_WASH)
for(var/A in tile)
if(is_cleanable(A))
qdel(A)
else if(istype(A, /obj/item))
// Clean small items that are lying on the ground
if(isitem(A))
var/obj/item/I = A
SEND_SIGNAL(I, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
if(ismob(I.loc))
var/mob/M = I.loc
M.regenerate_icons()
if(I.w_class <= WEIGHT_CLASS_SMALL && !ismob(I.loc))
I.wash(CLEAN_WASH)
// Clean humans that are lying down
else if(ishuman(A))
var/mob/living/carbon/human/cleaned_human = A
if(!(cleaned_human.mobility_flags & MOBILITY_STAND))
if(cleaned_human.head)
SEND_SIGNAL(cleaned_human.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
if(cleaned_human.wear_suit)
SEND_SIGNAL(cleaned_human.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
else if(cleaned_human.w_uniform)
SEND_SIGNAL(cleaned_human.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
if(cleaned_human.shoes)
SEND_SIGNAL(cleaned_human.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
SEND_SIGNAL(cleaned_human, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
cleaned_human.wash(CLEAN_WASH)
cleaned_human.regenerate_icons()
to_chat(cleaned_human, "<span class='danger'>[AM] cleans your face!</span>")
+24 -18
View File
@@ -933,8 +933,7 @@
///Removes an instance of colour_type from the atom's atom_colours list
/atom/proc/remove_atom_colour(colour_priority, coloration)
if(!atom_colours)
atom_colours = list()
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
return
if(colour_priority > atom_colours.len)
return
if(coloration && atom_colours[colour_priority] != coloration)
@@ -945,10 +944,9 @@
///Resets the atom's color to null, and then sets it to the highest priority colour available
/atom/proc/update_atom_colour()
if(!atom_colours)
atom_colours = list()
atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently.
color = null
if(!atom_colours)
return
for(var/C in atom_colours)
if(islist(C))
var/list/L = C
@@ -960,20 +958,23 @@
return
///Proc for being washed by a shower
/atom/proc/washed(var/atom/washer, wash_strength = CLEAN_WEAK)
SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, wash_strength)
remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
/**
* Wash this atom
*
* This will clean it off any temporary stuff like blood. Override this in your item to add custom cleaning behavior.
* Returns true if any washing was necessary and thus performed
* Arguments:
* * clean_types: any of the CLEAN_ constants
*/
/atom/proc/wash(clean_types)
. = FALSE
if(SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, clean_types))
. = TRUE
var/datum/component/radioactive/healthy_green_glow = GetComponent(/datum/component/radioactive)
if(!QDELETED(healthy_green_glow))
healthy_green_glow.strength -= max(0, (healthy_green_glow.strength - (RAD_BACKGROUND_RADIATION * 2)))
if(healthy_green_glow.strength <= RAD_BACKGROUND_RADIATION)
qdel(healthy_green_glow)
/// we got to return true because of mob code
/// and not all code that uses COMSIG_COMPONENT_CLEAN_ACT returns true half the time
return TRUE
// Basically "if has washable coloration"
if(length(atom_colours) >= WASHABLE_COLOUR_PRIORITY && atom_colours[WASHABLE_COLOUR_PRIORITY])
remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
return TRUE
/**
* call back when a var is edited on this atom
@@ -1010,6 +1011,7 @@
VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
VV_DROPDOWN_OPTION(VV_HK_RADIATE, "Radiate")
/atom/vv_do_topic(list/href_list)
. = ..()
@@ -1050,6 +1052,10 @@
usr.client.cmd_admin_explosion(src)
if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
usr.client.cmd_admin_emp(src)
if(href_list[VV_HK_RADIATE] && check_rights(R_FUN))
var/strength = input(usr, "Choose the radiation strength.", "Choose the strength.") as num|null
if(!isnull(strength))
AddComponent(/datum/component/radioactive, strength, src)
if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT))
var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
var/matrix/M = transform
+3 -5
View File
@@ -296,11 +296,9 @@
if(occupant)
things_to_clear += occupant
things_to_clear += occupant.GetAllContents()
for(var/atom/movable/AM in things_to_clear) //Scorches away blood and forensic evidence, although the SSU itself is unaffected
SEND_SIGNAL(AM, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG)
var/datum/component/radioactive/contamination = AM.GetComponent(/datum/component/radioactive)
if(contamination)
qdel(contamination)
for(var/am in things_to_clear) //Scorches away blood and forensic evidence, although the SSU itself is unaffected
var/atom/movable/dirty_movable = am
dirty_movable.wash(CLEAN_ALL)
open_machine(FALSE)
if(occupant)
dump_contents()
+5 -7
View File
@@ -143,10 +143,6 @@ GLOBAL_LIST_INIT(dye_registry, list(
var/obj/item/color_source
var/max_wash_capacity = 5
/obj/machinery/washing_machine/ComponentInitialize()
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_blood)
/obj/machinery/washing_machine/examine(mob/user)
. = ..()
if(!busy)
@@ -186,15 +182,17 @@ GLOBAL_LIST_INIT(dye_registry, list(
M.Translate(rand(-3, 3), rand(-1, 3))
animate(src, transform=M, time=2)
/obj/machinery/washing_machine/proc/clean_blood()
if(!busy)
/obj/machinery/washing_machine/wash(clean_types)
. = ..()
if(!busy && bloody_mess && (clean_types & CLEAN_TYPE_BLOOD))
bloody_mess = FALSE
update_icon()
. = TRUE
/obj/machinery/washing_machine/proc/wash_cycle()
for(var/X in contents)
var/atom/movable/AM = X
SEND_SIGNAL(AM, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
AM.wash(CLEAN_WASH)
AM.machine_wash(src)
busy = FALSE
@@ -43,7 +43,7 @@
return TRUE
/obj/effect/decal/cleanable/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/reagent_containers/glass) || istype(W, /obj/item/reagent_containers/food/drinks))
if((istype(W, /obj/item/reagent_containers/glass) && !istype(W, /obj/item/reagent_containers/glass/rag)) || istype(W, /obj/item/reagent_containers/food/drinks))
if(src.reagents && W.reagents)
. = 1 //so the containers don't splash their content on the src while scooping.
if(!src.reagents.total_volume)
@@ -100,9 +100,11 @@
S.blood_state = blood_state
update_icon()
H.update_inv_shoes()
/atom/effect/decal/cleanable/washed(atom/washer)
. = ..()
/obj/effect/decal/cleanable/wash(clean_types)
..()
qdel(src)
return TRUE
/obj/effect/decal/cleanable/proc/can_bloodcrawl_in()
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
+1 -1
View File
@@ -45,4 +45,4 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddComponent(/datum/component/decal, icon, icon_state, dir, CLEAN_NEVER, color, null, null, alpha)
T.AddComponent(/datum/component/decal, icon, icon_state, dir, FALSE, color, null, null, alpha)
+8
View File
@@ -1109,3 +1109,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
return discover_after
#undef MAX_BONUS_MATS_PER_BITE
// Update icons if this is being carried by a mob
/obj/item/wash(clean_types)
. = ..()
if(ismob(loc))
var/mob/mob_loc = loc
mob_loc.regenerate_icons()
+1 -1
View File
@@ -174,7 +174,7 @@
to_chat(user, "<span class='notice'>You need to get closer!</span>")
return
if(use_paint(user) && isturf(F))
F.AddComponent(/datum/component/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_STRONG, color, null, null, alpha)
F.AddComponent(/datum/component/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_TYPE_PAINT, color, null, null, alpha)
/obj/item/airlock_painter/decal/AltClick(mob/user)
. = ..()
+4 -4
View File
@@ -144,11 +144,11 @@
user.visible_message("<span class='notice'>[user] begins to clean \the [target.name] with [src]...</span>", "<span class='notice'>You begin to clean \the [target.name] with [src]...</span>")
if(do_after(user, clean_speedies, target = target))
to_chat(user, "<span class='notice'>You clean \the [target.name].</span>")
for(var/obj/effect/decal/cleanable/C in target)
user?.mind.adjust_experience(/datum/skill/cleaning, round(C.beauty/CLEAN_SKILL_BEAUTY_ADJUSTMENT))
qdel(C)
if(user && isturf(target))
for(var/obj/effect/decal/cleanable/cleanable_decal in target)
user.mind.adjust_experience(/datum/skill/cleaning, round(cleanable_decal.beauty / CLEAN_SKILL_BEAUTY_ADJUSTMENT))
target.wash(CLEAN_SCRUB)
target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
SEND_SIGNAL(target, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
user?.mind.adjust_experience(/datum/skill/cleaning, CLEAN_SKILL_GENERIC_WASH_XP)
decreaseUses(user)
return
+4 -6
View File
@@ -26,12 +26,10 @@
/obj/item/mop/proc/clean(turf/A, mob/living/cleaner)
if(reagents.has_reagent(/datum/reagent/water, 1) || reagents.has_reagent(/datum/reagent/water/holywater, 1) || reagents.has_reagent(/datum/reagent/consumable/ethanol/vodka, 1) || reagents.has_reagent(/datum/reagent/space_cleaner, 1))
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
for(var/obj/effect/O in A)
if(is_cleanable(O))
var/obj/effect/decal/cleanable/C = O
cleaner?.mind.adjust_experience(/datum/skill/cleaning, max(round(C.beauty/CLEAN_SKILL_BEAUTY_ADJUSTMENT,1),0)) //it is intentional that the mop rounds xp but soap does not, USE THE SACRED TOOL
qdel(O)
for(var/obj/effect/decal/cleanable/cleanable_decal in A)
cleaner?.mind.adjust_experience(/datum/skill/cleaning, max(round(cleanable_decal.beauty / CLEAN_SKILL_BEAUTY_ADJUSTMENT, 1), 0)) //it is intentional that the mop rounds xp but soap does not, USE THE SACRED TOOL
A.wash(CLEAN_SCRUB)
reagents.expose(A, TOUCH, 10) //Needed for proper floor wetting.
var/val2remove = 1
if(cleaner?.mind)
+7 -3
View File
@@ -98,7 +98,9 @@
wash_atom(AM)
/obj/machinery/shower/proc/wash_atom(atom/A)
A.washed(src)
A.wash(CLEAN_RAD | CLEAN_TYPE_WEAK) // Clean radiation non-instantly
A.wash(CLEAN_WASH)
SEND_SIGNAL(A, COMSIG_ADD_MOOD_EVENT, "shower", /datum/mood_event/nice_shower)
reagents.expose(A, TOUCH, reaction_volume)
if(isliving(A))
@@ -107,8 +109,10 @@
/obj/machinery/shower/process()
if(on)
wash_atom(loc)
for(var/AM in loc)
wash_atom(AM)
for(var/am in loc)
var/atom/movable/movable_content = am
if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above
wash_atom(movable_content)
else
return PROCESS_KILL
+12 -13
View File
@@ -279,21 +279,20 @@
busy = FALSE
if(washing_face)
SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_WASH)
user.drowsyness = max(user.drowsyness - rand(2,3), 0) //Washing your face wakes you up if you're falling asleep
else if(ishuman(user))
var/mob/living/carbon/human/human_user = user
if(!human_user.wash_hands(CLEAN_WASH))
to_chat(user, "<span class='warning'>Your hands are covered by something!</span>")
return
else
user.wash(CLEAN_WASH)
user.visible_message("<span class='notice'>[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src].</span>", \
"<span class='notice'>You wash your [washing_face ? "face" : "hands"] using [src].</span>")
if(washing_face)
SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_STRENGTH_BLOOD)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.lip_style = null //Washes off lipstick
H.lip_color = initial(H.lip_color)
H.regenerate_icons()
user.drowsyness = max(user.drowsyness - rand(2,3), 0) //Washing your face wakes you up if you're falling asleep
else
user.washed(src)
/obj/structure/sink/attackby(obj/item/O, mob/living/user, params)
if(busy)
to_chat(user, "<span class='warning'>Someone's already washing here!</span>")
@@ -357,7 +356,7 @@
busy = FALSE
return 1
busy = FALSE
O.washed(src)
O.wash(CLEAN_WASH)
O.acid_level = 0
create_reagents(5)
reagents.add_reagent(dispensedreagent, 5)
+6 -4
View File
@@ -185,10 +185,12 @@
for(var/mob/living/simple_animal/slime/M in src)
M.apply_water()
SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
for(var/obj/effect/O in src)
if(is_cleanable(O))
qdel(O)
wash(CLEAN_WASH)
for(var/am in src)
var/atom/movable/movable_content = am
if(ismopable(movable_content)) // Will have already been washed by the wash call above at this point.
continue
movable_content.wash(CLEAN_WASH)
return TRUE
/turf/open/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube, paralyze_amount, force_drop)
+14
View File
@@ -559,3 +559,17 @@ GLOBAL_LIST_EMPTY(station_turfs)
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_turf(src, reagents[R])
/**
* Called when this turf is being washed. Washing a turf will also wash any mopable floor decals
*/
/turf/wash(clean_types)
. = ..()
for(var/am in src)
if(am == src)
continue
var/atom/movable/movable_content = am
if(!ismopable(movable_content))
continue
movable_content.wash(clean_types)
+4 -7
View File
@@ -11,14 +11,11 @@
strip_delay = 20
equip_delay_other = 40
/obj/item/clothing/gloves/ComponentInitialize()
/obj/item/clothing/gloves/wash(clean_types)
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_blood)
/obj/item/clothing/gloves/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
return
transfer_blood = 0
if((clean_types & CLEAN_TYPE_BLOOD) && transfer_blood > 0)
transfer_blood = 0
return TRUE
/obj/item/clothing/gloves/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>\the [src] are forcing [user]'s hands around [user.p_their()] neck! It looks like the gloves are possessed!</span>")
+4 -6
View File
@@ -25,10 +25,6 @@
///any alerts we have active
var/obj/screen/alert/our_alert
/obj/item/clothing/shoes/ComponentInitialize()
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_blood)
/obj/item/clothing/shoes/suicide_act(mob/living/carbon/user)
if(rand(2)>1)
user.visible_message("<span class='suicide'>[user] begins tying \the [src] up waaay too tightly! It looks like [user.p_theyre()] trying to commit suicide!</span>")
@@ -100,14 +96,16 @@
var/mob/M = loc
M.update_inv_shoes()
/obj/item/clothing/shoes/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
/obj/item/clothing/shoes/wash(clean_types)
. = ..()
if(!(clean_types & CLEAN_TYPE_BLOOD) || blood_state == BLOOD_STATE_NOT_BLOODY)
return
bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
blood_state = BLOOD_STATE_NOT_BLOODY
if(ismob(loc))
var/mob/M = loc
M.update_inv_shoes()
return TRUE
/obj/item/proc/negates_gravity()
return FALSE
+5 -16
View File
@@ -77,13 +77,6 @@
playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing
update_icon()
/obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(isinhands)
. = ..()
if(!isinhands && !up)
. += mutable_appearance('icons/mob/clothing/head.dmi', visor_icon)
else
cut_overlays()
/obj/item/clothing/head/helmet/space/plasmaman/update_overlays()
. = ..()
. += visor_icon
@@ -99,8 +92,7 @@
smile_color = CR.paint_color
to_chat(user, "You draw a smiley on the helmet visor.")
update_icon()
return
if(smile == TRUE)
else
to_chat(user, "<span class='warning'>Seems like someone already drew something on this helmet's visor!</span>")
/obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(isinhands)
@@ -114,15 +106,12 @@
else
cut_overlays()
/obj/item/clothing/head/helmet/space/plasmaman/ComponentInitialize()
/obj/item/clothing/head/helmet/space/plasmaman/wash(clean_types)
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/wipe_that_smile_off_your_face)
///gets called when receiving the CLEAN_ACT signal from something, i.e soap or a shower. exists to remove any smiley faces drawn on the helmet.
/obj/item/clothing/head/helmet/space/plasmaman/proc/wipe_that_smile_off_your_face()
if(smile)
if(smile && (clean_types & CLEAN_TYPE_PAINT))
smile = FALSE
cut_overlays()
update_icon()
return TRUE
/obj/item/clothing/head/helmet/space/plasmaman/attack_self(mob/user)
on = !on
+3 -3
View File
@@ -46,10 +46,10 @@
if(G.transfer_blood > 1) //bloodied gloves transfer blood to touched objects
if(add_blood_DNA(G.return_blood_DNA()) && length(G.return_blood_DNA()) > old) //only reduces the bloodiness of our gloves if the item wasn't already bloody
G.transfer_blood--
else if(M.bloody_hands > 1)
else if(M.blood_in_hands > 1)
old = length(M.return_blood_DNA())
if(add_blood_DNA(M.return_blood_DNA()) && length(M.return_blood_DNA()) > old)
M.bloody_hands--
M.blood_in_hands--
var/datum/component/forensics/D = AddComponent(/datum/component/forensics)
. = D.add_fibers(M)
@@ -92,7 +92,7 @@
G.add_blood_DNA(blood_dna)
else if(length(blood_dna))
AddComponent(/datum/component/forensics, null, null, blood_dna)
bloody_hands = rand(2, 4)
blood_in_hands = rand(2, 4)
update_inv_gloves() //handles bloody hands overlays and updating
return TRUE
@@ -42,4 +42,4 @@
user.visible_message("<span class='notice'>[user] starts to wipe down [A] with [src]!</span>", "<span class='notice'>You start to wipe down [A] with [src]...</span>")
if(do_after(user,30, target = A))
user.visible_message("<span class='notice'>[user] finishes wiping off [A]!</span>", "<span class='notice'>You finish wiping off [A].</span>")
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
A.wash(CLEAN_SCRUB)
+1 -1
View File
@@ -144,7 +144,7 @@
/atom/movable/lighting_object/onTransitZ()
return
/atom/movable/lighting_object/washed(var/washer)
/atom/movable/lighting_object/wash(clean_types)
return
// Override here to prevent things accidentally moving around overlays.
+28 -13
View File
@@ -1106,35 +1106,50 @@
if(mood.sanity < SANITY_UNSTABLE)
return TRUE
/mob/living/carbon/washed(var/atom/washer)
/mob/living/carbon/wash(clean_types)
. = ..()
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "shower", /datum/mood_event/nice_shower)
for(var/obj/item/I in held_items)
I.washed(washer)
// Wash equipped stuff that cannot be covered
for(var/i in held_items)
var/obj/item/held_thing = i
if(held_thing.wash(clean_types))
. = TRUE
if(back && back.washed(washer))
if(back?.wash(clean_types))
update_inv_back(0)
. = TRUE
if(head?.wash(clean_types))
update_inv_head()
. = TRUE
// Check and wash stuff that can be covered
var/list/obscured = check_obscured_slots()
if(head && head.washed(washer))
update_inv_head()
if(glasses && !(ITEM_SLOT_EYES in obscured) && glasses.washed(washer))
// If the eyes are covered by anything but glasses, that thing will be covering any potential glasses as well.
if(glasses && is_eyes_covered(FALSE, TRUE, TRUE) && glasses.wash(clean_types))
update_inv_glasses()
. = TRUE
if(wear_mask && !(ITEM_SLOT_MASK in obscured && wear_mask.washed(washer)))
if(wear_mask && !(ITEM_SLOT_MASK in obscured) && wear_mask.wash(clean_types))
update_inv_wear_mask()
. = TRUE
if(ears && !(HIDEEARS in obscured) && ears.washed(washer))
if(ears && !(ITEM_SLOT_EARS in obscured) && ears.wash(clean_types))
update_inv_ears()
. = TRUE
if(wear_neck && !(ITEM_SLOT_NECK in obscured) && wear_neck.washed(washer))
if(wear_neck && !(ITEM_SLOT_NECK in obscured) && wear_neck.wash(clean_types))
update_inv_neck()
. = TRUE
if(shoes && !(HIDESHOES in obscured) && shoes.washed(washer))
if(shoes && !(ITEM_SLOT_FEET in obscured) && shoes.wash(clean_types))
update_inv_shoes()
. = TRUE
if(gloves && !(ITEM_SLOT_GLOVES in obscured) && gloves.wash(clean_types))
update_inv_gloves()
. = TRUE
/// if any of our bodyparts are bleeding
/mob/living/carbon/proc/is_bleeding()
+75 -27
View File
@@ -18,7 +18,7 @@
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_blood)
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, .proc/clean_face)
AddComponent(/datum/component/personal_crafting)
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HUMAN, 1, 2)
GLOB.human_list += src
@@ -700,16 +700,82 @@
if(..())
dropItemToGround(I)
/mob/living/carbon/human/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
return
/**
* Wash the hands, cleaning either the gloves if equipped and not obscured, otherwise the hands themselves if they're not obscured.
*
* Returns false if we couldn't wash our hands due to them being obscured, otherwise true
*/
/mob/living/carbon/human/proc/wash_hands(clean_types)
var/list/obscured = check_obscured_slots()
if(ITEM_SLOT_GLOVES in obscured)
return FALSE
if(gloves)
if(SEND_SIGNAL(gloves, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
update_inv_gloves()
else
if(bloody_hands)
bloody_hands = 0
if(gloves.wash(clean_types))
update_inv_gloves()
else if((clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0)
blood_in_hands = 0
update_inv_gloves()
return TRUE
/**
* Cleans the lips of any lipstick. Returns TRUE if the lips had any lipstick and was thus cleaned
*/
/mob/living/carbon/human/proc/clean_lips()
if(isnull(lip_style) && lip_color == initial(lip_color))
return FALSE
lip_style = null
lip_color = initial(lip_color)
update_body()
return TRUE
/**
* Called on the COMSIG_COMPONENT_CLEAN_FACE_ACT signal
*/
/mob/living/carbon/human/proc/clean_face(datum/source, clean_types)
if(!is_mouth_covered() && clean_lips())
. = TRUE
if(glasses && is_eyes_covered(FALSE, TRUE, TRUE) && glasses.wash(clean_types))
update_inv_glasses()
. = TRUE
var/list/obscured = check_obscured_slots()
if(wear_mask && !(ITEM_SLOT_MASK in obscured) && wear_mask.wash(clean_types))
update_inv_wear_mask()
. = TRUE
/**
* Called when this human should be washed
*/
/mob/living/carbon/human/wash(clean_types)
. = ..()
// Wash equipped stuff that cannot be covered
if(wear_suit?.wash(clean_types))
update_inv_wear_suit()
. = TRUE
if(belt?.wash(clean_types))
update_inv_belt()
. = TRUE
// Check and wash stuff that can be covered
var/list/obscured = check_obscured_slots()
if(w_uniform && !(ITEM_SLOT_ICLOTHING in obscured) && w_uniform.wash(clean_types))
update_inv_w_uniform()
. = TRUE
if(!is_mouth_covered() && clean_lips())
. = TRUE
// Wash hands if exposed
if(!gloves && (clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0 && !(ITEM_SLOT_GLOVES in obscured))
blood_in_hands = 0
update_inv_gloves()
. = TRUE
//Turns a mob black, flashes a skeleton overlay
//Just like a cartoon!
@@ -1103,24 +1169,6 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
/mob/living/carbon/human/washed(var/atom/washer)
. = ..()
if(wear_suit && wear_suit.washed(washer))
update_inv_wear_suit()
else if(w_uniform && w_uniform.washed(washer))
update_inv_w_uniform()
if(!is_mouth_covered())
lip_style = null
update_body()
if(belt && belt.washed(washer))
update_inv_belt()
var/list/obscured = check_obscured_slots()
if(gloves && !(HIDEGLOVES in obscured) && gloves.washed(washer))
update_inv_gloves()
/mob/living/carbon/human/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
return FALSE
@@ -64,3 +64,7 @@
var/hardcore_survival_score = 0
/// For agendered spessmen, which body type to use
var/body_type = MALE
/// How many "units of blood" we have on our hands
var/blood_in_hands = 0
@@ -173,7 +173,7 @@ There are several things that need to be remembered:
var/obj/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_GLOVES) + 1]
inv.update_icon()
if(!gloves && bloody_hands)
if(!gloves && blood_in_hands)
var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER)
if(get_num_arms(FALSE) < 2)
if(has_left_hand(FALSE))
-5
View File
@@ -1167,11 +1167,6 @@
//Mobs on Fire end
//Washing
/mob/living/washed(var/atom/washer)
. = ..()
SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
// used by secbot and monkeys Crossed
/mob/living/proc/knockOver(var/mob/living/carbon/C)
if(C.key) //save us from monkey hordes
@@ -312,18 +312,14 @@
target_types = typecacheof(target_types)
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A)
if(is_cleanable(A))
if(ismopable(A))
icon_state = "cleanbot-c"
mode = BOT_CLEANING
var/turf/T = get_turf(A)
if(do_after(src, 1, target = T))
SEND_SIGNAL(T, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
T.wash(CLEAN_WASH)
visible_message("<span class='notice'>[src] cleans \the [T].</span>")
for(var/atom/dirtything in T)
if(is_cleanable(dirtything))
qdel(dirtything)
target = null
mode = BOT_IDLE
@@ -234,7 +234,7 @@ Maintenance panel is [open ? "opened" : "closed"]"}
if(emagged)
A.fire_act() //lol pranked no cleaning besides that
else
A.washed(src)
A.wash(CLEAN_WASH)
@@ -177,11 +177,9 @@
/mob/living/simple_animal/hostile/alien/maid/AttackingTarget()
if(ismovable(target))
target.wash(CLEAN_WASH)
if(istype(target, /obj/effect/decal/cleanable))
visible_message("<span class='notice'>[src] cleans up \the [target].</span>")
qdel(target)
return TRUE
var/atom/movable/M = target
SEND_SIGNAL(M, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
visible_message("<span class='notice'>[src] polishes \the [target].</span>")
else
visible_message("<span class='notice'>[src] polishes \the [target].</span>")
return TRUE
-2
View File
@@ -205,8 +205,6 @@
var/datum/h_sandbox/sandbox = null
var/bloody_hands = 0
var/datum/focus //What receives our keyboard inputs. src by default
/// Used for tracking last uses of emotes for cooldown purposes
@@ -1063,55 +1063,26 @@
color = "#A5F0EE" // rgb: 165, 240, 238
taste_description = "sourness"
reagent_weight = 0.6 //so it sprays further
var/clean_types = CLEAN_WASH
/datum/reagent/space_cleaner/expose_obj(obj/O, reac_volume)
if(istype(O, /obj/effect/decal/cleanable))
qdel(O)
else
if(O)
O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
SEND_SIGNAL(O, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
O?.wash(clean_types)
/datum/reagent/space_cleaner/expose_turf(turf/T, reac_volume)
if(reac_volume >= 1)
T.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
SEND_SIGNAL(T, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
for(var/obj/effect/decal/cleanable/C in T)
qdel(C)
T.wash(clean_types)
for(var/am in T)
var/atom/movable/movable_content
if(ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash
continue
movable_content.wash(clean_types)
for(var/mob/living/simple_animal/slime/M in T)
M.adjustToxLoss(rand(5,10))
/datum/reagent/space_cleaner/expose_mob(mob/living/M, method=TOUCH, reac_volume)
if(method == TOUCH || method == VAPOR)
M.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
if(iscarbon(M))
var/mob/living/carbon/C = M
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.lip_style)
H.lip_style = null
H.update_body()
for(var/obj/item/I in C.held_items)
SEND_SIGNAL(I, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
if(C.wear_mask)
if(SEND_SIGNAL(C.wear_mask, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
C.update_inv_wear_mask()
if(ishuman(M))
var/mob/living/carbon/human/H = C
if(H.head)
if(SEND_SIGNAL(H.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
H.update_inv_head()
if(H.wear_suit)
if(SEND_SIGNAL(H.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
H.update_inv_wear_suit()
else if(H.w_uniform)
if(SEND_SIGNAL(H.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
H.update_inv_w_uniform()
if(H.shoes)
if(SEND_SIGNAL(H.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD))
H.update_inv_shoes()
SEND_SIGNAL(M, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
M.wash(clean_types)
/datum/reagent/space_cleaner/ez_clean
name = "EZ Clean"