Soap & clean proc refactor (#17744)

* Automatic changelog compile [ci skip]

* soap

* Washy wash

* Big wash

* washy

* Update soap.dm

* washiest

* .

* .

* Washes the linter

* scrubs the linter

* washes indentation

* .

* updaet

---------

Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com>
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
Guti
2025-05-31 10:31:15 -07:00
committed by GitHub
co-authored by vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com> ShadowLarkens Cameron Lennox
parent 5961096074
commit 9eb876de72
35 changed files with 448 additions and 311 deletions
+30 -12
View File
@@ -439,17 +439,8 @@
. = 1
return 1
/atom/proc/clean_blood()
if(!simulated)
return
fluorescent = 0
src.germ_level = 0
if(istype(blood_DNA, /list))
blood_DNA = null
return TRUE
/atom/proc/on_rag_wipe(var/obj/item/reagent_containers/glass/rag/R)
clean_blood()
wash(CLEAN_WASH)
R.reagents.splash(src, 1)
/atom/proc/get_global_map_pos()
@@ -799,7 +790,7 @@ GLOBAL_LIST_EMPTY(icon_dimensions)
GLOB.icon_dimensions[icon_path] = list("width" = my_icon.Width(), "height" = my_icon.Height())
return GLOB.icon_dimensions[icon_path]
///Returns the src and all recursive contents as a list.
/// Returns the src and all recursive contents as a list.
/atom/proc/get_all_contents(ignore_flag_1)
. = list(src)
var/i = 0
@@ -809,7 +800,7 @@ GLOBAL_LIST_EMPTY(icon_dimensions)
continue
. += checked_atom.contents
///identical to get_all_contents but returns a list of atoms of the type passed in the argument.
/// Identical to get_all_contents but returns a list of atoms of the type passed in the argument.
/atom/proc/get_all_contents_type(type)
var/list/processing_list = list(src)
. = list()
@@ -831,3 +822,30 @@ GLOBAL_LIST_EMPTY(icon_dimensions)
/atom/proc/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE)
. = list(EXTRAPOLATOR_RESULT_DISEASES = list())
SEND_SIGNAL(src, COMSIG_ATOM_EXTRAPOLATOR_ACT, user, extrapolator, dry_run, .)
/**
* 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)
SHOULD_CALL_PARENT(TRUE)
. = FALSE
if(SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, clean_types))
. = 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
if(istype(blood_DNA, /list))
blood_DNA = null
return TRUE
germ_level = 0
fluorescent = 0
@@ -430,15 +430,15 @@ GLOBAL_LIST_EMPTY(suit_cycler_typecache)
if(helmet)
if(radiation_level > 2)
helmet.decontaminate()
helmet.wash(CLEAN_TYPE_RADIATION)
if(radiation_level > 1)
helmet.clean_blood()
helmet.wash(CLEAN_SCRUB)
if(suit)
if(radiation_level > 2)
suit.decontaminate()
suit.wash(CLEAN_TYPE_RADIATION)
if(radiation_level > 1)
suit.clean_blood()
suit.wash(CLEAN_SCRUB)
. = TRUE
@@ -283,11 +283,11 @@
if(i==3) //End of the cycle
if(!issuperUV)
if(HELMET)
HELMET.clean_blood()
HELMET.wash(CLEAN_SCRUB)
if(SUIT)
SUIT.clean_blood()
SUIT.wash(CLEAN_SCRUB)
if(MASK)
MASK.clean_blood()
MASK.wash(CLEAN_SCRUB)
else //It was supercycling, destroy everything
if(HELMET)
HELMET = null
+6 -9
View File
@@ -58,12 +58,12 @@
update_icon()
to_chat(usr, "The washing machine starts a cycle.")
playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1)
sleep(200)
for(var/atom/A in washing)
A.clean_blood()
for(var/obj/item/I in washing)
I.decontaminate()
addtimer(CALLBACK(src, PROC_REF(finish_wash)), 2 SECONDS)
/obj/machinery/washing_machine/proc/finish_wash()
for(var/atom/A in washing)
A.wash(CLEAN_ALL)
//Tanning!
for(var/obj/item/stack/hairlesshide/HH in washing)
@@ -86,17 +86,14 @@
set category = "Object"
set src in usr.loc
sleep(20)
if(state in list(1,3,6))
if((state in list(1,3,6)) && do_after(usr, 20))
usr.loc = src.loc
/obj/machinery/washing_machine/update_icon()
//VOREStation Edit
cut_overlays()
icon_state = "wm_[state]"
if(panel_open)
add_overlay("panel")
//VOREStation Edit End
/obj/machinery/washing_machine/attackby(obj/item/W as obj, mob/user as mob)
if(state == 2 && washing.len < 1)
@@ -31,12 +31,12 @@ var/global/list/image/splatter_cache=list()
basecolor = COLOR_LUMINOL
update_icon()
/obj/effect/decal/cleanable/blood/clean_blood()
/obj/effect/decal/cleanable/blood/wash(clean_types)
. = ..()
fluorescent = 0
if(invisibility != INVISIBILITY_MAXIMUM)
invisibility = INVISIBILITY_MAXIMUM
amount = 0
..(ignore=1)
/obj/effect/decal/cleanable/blood/Initialize(mapload)
. = ..()
+10 -6
View File
@@ -13,6 +13,9 @@ generic_filth = TRUE means when the decal is saved, it will be switched out for
var/age = 0
var/list/random_icon_states = list()
///The type of cleaning required to clean the decal, CLEAN_TYPE_LIGHT_DECAL can be cleaned with mops and soap, CLEAN_TYPE_HARD_DECAL can be cleaned by soap, see __DEFINES/cleaning.dm for the others
var/clean_type = CLEAN_TYPE_LIGHT_DECAL
/obj/effect/decal/cleanable/Initialize(mapload, var/_age)
if(!isnull(_age))
age = _age
@@ -23,16 +26,17 @@ generic_filth = TRUE means when the decal is saved, it will be switched out for
. = ..()
update_icon()
/obj/effect/decal/cleanable/wash(clean_types)
. = ..()
if (. || (clean_types & clean_type))
qdel(src)
return TRUE
return .
/obj/effect/decal/cleanable/Destroy()
SSpersistence.forget_value(src, /datum/persistent/filth)
. = ..()
/obj/effect/decal/cleanable/clean_blood(var/ignore = 0)
if(!ignore)
qdel(src)
return
..()
/obj/effect/decal/cleanable/Initialize(mapload, _age)
. = ..()
if (random_icon_states && length(random_icon_states) > 0)
+7 -5
View File
@@ -712,11 +712,6 @@ var/list/global/slot_flags_enumeration = list(
M.eye_blurry += rand(3,4)
return
/obj/item/clean_blood()
. = ..()
if(blood_overlay)
overlays.Remove(blood_overlay)
/obj/item/reveal_blood()
if(was_bloodied && !fluorescent)
fluorescent = 1
@@ -1170,3 +1165,10 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen
transform = animation_matrix
animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = old_transform, time = 3, easing = CUBIC_EASING)
/obj/item/wash(clean_types)
. = ..()
if(cleanname)
name = cleanname
if(cleandesc)
name = cleandesc
+217
View File
@@ -0,0 +1,217 @@
/obj/item/soap
name = "soap"
desc = "A cheap bar of soap. Doesn't smell."
gender = PLURAL
icon = 'icons/obj/soap.dmi'
icon_state = "soap"
flags = NOCONDUCT
w_class = ITEMSIZE_SMALL
slot_flags = SLOT_HOLSTER
throwforce = 0
throw_speed = 4
throw_range = 20
var/randomize = TRUE
var/square_chance = 10
var/cleanspeed = 35
var/uses = 100
/obj/item/soap/Initialize(mapload)
if(randomize && prob(square_chance))
icon_state = "[icon_state]-alt"
create_reagents(5)
wet()
. = ..()
/obj/item/soap/examine(mob/user)
. = ..()
var/max_uses = initial(uses)
var/msg = "It looks like it just came out of the package."
if(uses != max_uses)
var/percentage_left = uses / max_uses
switch(percentage_left)
if(0 to 0.15)
msg = "There's just a tiny bit left of what it used to be, you're not sure it'll last much longer."
if(0.15 to 0.30)
msg = "It's dissolved quite a bit, but there's still some life to it."
if(0.30 to 0.50)
msg = "It's past its prime, but it's definitely still good."
if(0.50 to 0.75)
msg = "It's started to get a little smaller than it used to be, but it'll definitely still last for a while."
else
msg = "It's seen some light use, but it's still pretty fresh."
. += span_notice("[msg]")
/obj/item/soap/proc/wet()
reagents.add_reagent(REAGENT_ID_CLEANER, 5)
/obj/item/soap/Crossed(atom/movable/AM as mob|obj)
if(AM.is_incorporeal())
return
if(isliving(AM))
var/mob/living/M = AM
M.slip("the [src.name]",3)
/obj/item/soap/afterattack(atom/target, mob/user as mob, proximity)
. = ..()
if(!proximity)
return
//I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing.
//So this is a workaround. This also makes more sense from an IC standpoint. ~Carn
if(user.client && (target in user.client.screen))
to_chat(user, span_warning("You need to take that [target.name] off before cleaning it."))
else if(istype(target,/obj/effect/decal/cleanable))
user.visible_message("[user] begins to scrub \the [target.name] out with [src].", span_warning("You begin to scrub \the [target.name] out with [src]..."))
if(do_after(user, src.cleanspeed, target = target))
to_chat(user, span_notice("You scrub \the [target.name] out."))
qdel(target)
decreaseUses(user)
else if(istype(target,/turf))
user.visible_message("[user] begins to scrub \the [target.name] out with [src].", span_warning("You begin to scrub \the [target.name] out with [src]..."))
if(do_after(user, src.cleanspeed, target = target))
to_chat(user, span_notice("You scrub \the [target.name] clean."))
var/turf/T = target
T.wash(CLEAN_SCRUB)
decreaseUses(user)
else if(ishuman(target) && user.zone_sel.selecting == O_MOUTH)
if(target == user)
var/mob/living/carbon/human/H = user
to_chat(user, span_notice("You take a bite of \the [src] and swallow it."))
reagents.trans_to_holder(H.ingested, 1)
else
user.visible_message(span_danger("\The [user] washes \the [target]'s mouth out with \the [src]!"))
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
decreaseUses(user, 5)
else if(istype(target,/obj/structure/sink))
to_chat(user, span_notice("You wet \the [src] in the sink."))
wet()
else
user.visible_message("[user] begins to clean \the [target.name] with [src]...", span_notice("You begin to clean \the [target.name] with [src]..."))
if(do_after(user, src.cleanspeed, target = target))
target.wash(CLEAN_SCRUB)
decreaseUses(user)
return
/obj/item/soap/proc/decreaseUses(mob/user, var/used_up = 1)
uses -= used_up
if(uses <= 0)
to_chat(user, span_warning("[src] crumbles into tiny bits!"))
qdel(src)
/obj/item/soap/nanotrasen
name = "Soap (Nanotrasen)"
desc = "A NanoTrasen-brand bar of soap. Smells of phoron, a years-old marketing gimmick."
icon_state = "soapnt"
cleanspeed = 28
uses = 300 // Good soap, good soap
/obj/item/soap/deluxe
name = "Soap (Deluxe)"
icon_state = "soapdeluxe"
uses = 150 // Good soap
cleanspeed = 20 // But fast too
/obj/item/soap/deluxe/Initialize(mapload)
. = ..()
desc = "A deluxe Waffle Co. brand bar of soap. Smells of [pick("lavender", "vanilla", "strawberry", "chocolate" ,"space")]."
/obj/item/soap/syndie
name = "Soap (Syndicate)"
desc = "An untrustworthy bar of soap. Smells of fear."
icon_state = "soapsyndie"
cleanspeed = 5 // Nyoom soap
/obj/item/soap/space_soap
name = "Soap (Space)"
desc = "Smells like hot metal and walnuts."
icon_state = "space_soap"
/obj/item/soap/water_soap
name = "Soap (Pool)"
desc = "Smells like chlorine."
icon_state = "water_soap"
/obj/item/soap/fire_soap
name = "Soap (Fire)"
desc = "Smells like a campfire."
icon_state = "fire_soap"
/obj/item/soap/rainbow_soap
name = "Soap (Rainbow)"
desc = "Smells sickly sweet."
icon_state = "rainbow_soap"
/obj/item/soap/diamond_soap
name = "Soap (Diamond)"
desc = "Smells like saffron and vanilla."
icon_state = "diamond_soap"
/obj/item/soap/uranium_soap
name = "Soap (Uranium)"
desc = "Smells not great... Not terrible."
icon_state = "uranium_soap"
/obj/item/soap/silver_soap
name = "Soap (Silver)"
desc = "Smells like birch and amaranth."
icon_state = "silver_soap"
/obj/item/soap/brown_soap
name = "Soap (Brown)"
desc = "Smells like cinnamon and cognac."
icon_state = "brown_soap"
/obj/item/soap/white_soap
name = "Soap (Nutty)"
desc = "Smells like nutmeg and oats."
icon_state = "white_soap"
/obj/item/soap/grey_soap
name = "Soap (Grey)"
desc = "Smells like bergamot and lilies."
icon_state = "grey_soap"
/obj/item/soap/pink_soap
name = "Soap (Gum)"
desc = "Smells like bubblegum."
icon_state = "pink_soap"
/obj/item/soap/purple_soap
name = "Soap (Lavender)"
desc = "Smells like lavender."
icon_state = "purple_soap"
/obj/item/soap/blue_soap
name = "Soap (Blue)"
desc = "Smells like cardamom."
icon_state = "blue_soap"
/obj/item/soap/cyan_soap
name = "Soap (Berries)"
desc = "Smells like bluebells and peaches."
icon_state = "cyan_soap"
/obj/item/soap/green_soap
name = "Soap (Grass)"
desc = "Smells like a freshly mowed lawn."
icon_state = "green_soap"
/obj/item/soap/yellow_soap
name = "Soap (Lemon)"
desc = "Smells like citron and ginger."
icon_state = "yellow_soap"
/obj/item/soap/orange_soap
name = "Soap (Orange)"
desc = "Smells like oranges and dark chocolate."
icon_state = "orange_soap"
/obj/item/soap/red_soap
name = "Soap (Orange)"
desc = "Smells like cherries."
icon_state = "red_soap"
/obj/item/soap/golden_soap
name = "Soap (Honey)"
desc = "Smells like honey."
icon_state = "golden_soap"
@@ -14,56 +14,6 @@
if(isliving(AM))
var/mob/living/M = AM
M.slip("the [src.name]",4)
/*
* Soap
*/
/obj/item/soap/Initialize(mapload)
. = ..()
create_reagents(5)
wet()
/obj/item/soap/proc/wet()
reagents.add_reagent(REAGENT_ID_CLEANER, 5)
/obj/item/soap/Crossed(atom/movable/AM as mob|obj)
if(AM.is_incorporeal())
return
if(isliving(AM))
var/mob/living/M = AM
M.slip("the [src.name]",3)
/obj/item/soap/afterattack(atom/target, mob/user as mob, proximity)
if(!proximity) return
//I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing.
//So this is a workaround. This also makes more sense from an IC standpoint. ~Carn
if(user.client && (target in user.client.screen))
to_chat(user, span_notice("You need to take that [target.name] off before cleaning it."))
else if(istype(target,/obj/effect/decal/cleanable/blood))
to_chat(user, span_notice("You scrub \the [target.name] out."))
target.clean_blood()
return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals.
else if(istype(target,/obj/effect/decal/cleanable))
to_chat(user, span_notice("You scrub \the [target.name] out."))
qdel(target)
else if(istype(target,/turf))
to_chat(user, span_notice("You scrub \the [target.name] clean."))
var/turf/T = target
T.clean(src, user)
else if(istype(target,/obj/structure/sink))
to_chat(user, span_notice("You wet \the [src] in the sink."))
wet()
else
to_chat(user, span_notice("You clean \the [target.name]."))
target.clean_blood(TRUE)
return
//attack_as_weapon
/obj/item/soap/attack(mob/living/target, mob/living/user, var/target_zone)
if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" )
user.visible_message(span_danger("\The [user] washes \the [target]'s mouth out with soap!"))
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam
return
..()
/*
* Bike Horns
+2 -2
View File
@@ -34,7 +34,7 @@ GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/mop)
if(do_after(user, 40))
var/turf/T = get_turf(A)
if(T)
T.clean(src, user)
T.wash(CLEAN_SCRUB)
to_chat(user, span_notice("You have finished mopping!"))
@@ -75,5 +75,5 @@ GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/mop)
if(do_after(user, 20))
var/turf/T = get_turf(A)
if(T)
T.clean(src, user)
T.wash(CLEAN_SCRUB)
to_chat(user, span_notice("You have finished mopping!"))
@@ -22,7 +22,7 @@
/turf/proc/clean_deploy(atom/source)
if(source.reagents.has_reagent(REAGENT_ID_WATER, 1))
clean_blood()
wash(CLEAN_SCRUB)
if(istype(src, /turf/simulated))
var/turf/simulated/T = src
T.dirt = 0
+13
View File
@@ -221,3 +221,16 @@
var/shake_dir = pick(-1, 1)
animate(src, transform=turn(matrix(), 8*shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
/obj/item/wash(clean_types)
. = ..()
if(blood_overlay && clean_types & CLEAN_WASH)
overlays.Remove(blood_overlay)
if(gurgled && clean_types & CLEAN_WASH)
gurgled = FALSE
cut_overlay(gurgled_overlays[gurgled_color])
name = initial(name)
desc = initial(desc)
if(contaminated && clean_types & CLEAN_RAD) // Phoron and stuff, washing machine needed
contaminated = FALSE
cut_overlay(contamination_overlay)
+10 -10
View File
@@ -194,7 +194,7 @@
wash(M)
process_heat(M)
for (var/atom/movable/G in src.loc)
G.clean_blood(TRUE)
G.wash(CLEAN_SCRUB)
else
soundloop.stop()
@@ -247,7 +247,7 @@
//Yes, showers are super powerful as far as washing goes.
/obj/machinery/shower/proc/wash(atom/movable/O as obj|mob)
/obj/machinery/shower/proc/do_wash(atom/movable/O as obj|mob)
if(!on) return
if(isliving(O))
@@ -262,7 +262,7 @@
var/remove_amount = M.touching.maximum_volume * M.reagent_permeability() //take off your suit first
M.touching.remove_any(remove_amount)
M.clean_blood()
M.wash(CLEAN_SCRUB)
reagents.splash(O, 10, min_spill = 0, max_spill = 0)
@@ -270,7 +270,7 @@
if(!on) return
for(var/atom/movable/AM in loc)
if(AM.simulated)
wash(AM)
do_wash(AM)
if(isliving(AM))
var/mob/living/L = AM
process_heat(L)
@@ -282,7 +282,7 @@
return
is_washing = 1
var/turf/T = get_turf(src)
T.clean(src)
T.wash(CLEAN_SCRUB)
addtimer(VARSET_CALLBACK(src, is_washing, 0), 100, TIMER_DELETE_ME)
/obj/machinery/shower/proc/process_heat(mob/living/M)
@@ -548,21 +548,21 @@
var/mob/living/carbon/human/H = user
H.gunshot_residue = null
if(H.gloves)
H.gloves.clean_blood()
H.gloves.wash(CLEAN_SCRUB)
H.update_inv_gloves()
H.gloves.germ_level = 0
else
if(H.r_hand)
H.r_hand.clean_blood()
H.r_hand.wash(CLEAN_SCRUB)
if(H.l_hand)
H.l_hand.clean_blood()
H.l_hand.wash(CLEAN_SCRUB)
H.bloody_hands = 0
H.germ_level = 0
H.hand_blood_color = null
LAZYCLEARLIST(H.blood_DNA)
H.update_bloodied()
else
user.clean_blood()
user.wash(CLEAN_SCRUB)
for(var/mob/V in viewers(src, null))
V.show_message(span_notice("[user] washes their hands using \the [src]."))
@@ -617,7 +617,7 @@
return
busy = 0
O.clean_blood()
O.wash(CLEAN_SCRUB)
O.water_act(rand(1,10))
user.visible_message( \
span_notice("[user] washes \a [I] using \the [src]."), \
-5
View File
@@ -63,11 +63,6 @@
cut_overlay(wet_overlay)
wet_overlay = null
/turf/simulated/clean_blood()
for(var/obj/effect/decal/cleanable/blood/B in contents)
B.clean_blood()
..()
/turf/simulated/Initialize(mapload)
. = ..()
if(istype(loc, /area/chapel))
+18 -14
View File
@@ -329,20 +329,6 @@
return 1
return 0
//expects an atom containing the reagents used to clean the turf
/turf/proc/clean(atom/source, mob/user)
if(source.reagents.has_reagent(REAGENT_ID_WATER, 1) || source.reagents.has_reagent(REAGENT_ID_CLEANER, 1))
clean_blood()
if(istype(src, /turf/simulated))
var/turf/simulated/T = src
T.dirt = 0
for(var/obj/effect/O in src)
if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay))
qdel(O)
else
to_chat(user, span_warning("\The [source] is too dry to wash that."))
source.reagents.trans_to_turf(src, 1, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly.
/turf/proc/update_blood_overlays()
return
@@ -507,3 +493,21 @@
H.ingested.trans_to(V, H.ingested.total_volume / 10)
for(var/datum/reagent/R in H.ingested.reagent_list)
H.ingested.remove_reagent(R, min(R.volume, 10))
/**
* Called when this turf is being washed. Washing a turf will also wash any mopable floor decals
*/
/turf/wash(clean_types)
. = ..()
if(istype(src, /turf/simulated))
var/turf/simulated/T = src
T.dirt = 0
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)