[MIRROR] Dehardcodes microwave cleaning, allows spray cleaner to work on dense objects such as windows [MDB IGNORE] (#24684)

* Dehardcodes microwave cleaning, allows spray cleaner to work on dense objects such as windows (#79354)

## About The Pull Request

- Dehardcodes microwave cleaning
- Instead of hard istyping for a rag, soap, or space cleaner, we just
use `wash`.

- Gets rid of a redundant signal
- `COMSIG_ATOM_WASHED`: not only was it misleading (only sent to items),
but it was pointless because `COMSIG_COMPONENT_CLEAN_ACT` is the same
signal.

- Improves microwave attackby code, splitting tool stuff into tool-procs

- Allows spray cleaner to work on dense objects such as windows
- Clicking on a dense object or mob adjacent to you with spray cleaner
will spawn the puff cloud on the target, rather than on your own
position.
- This will skip the moveloop and just clean everything on the target
turf alone.
- This means you can spray down a bloody window with a spray bottle, as
janitor gods intended.
- It also means you can fill the spray with other stuff to spray onto
dense objects directly, which might be worth noting. Especially for
stuff like Napalm.

Fixes #79261

## Why It's Good For The Game

Opens up the sandbox to allow more objects to clean microwaves + better
code.

## Changelog

🆑 Melbert
qol: Clicking on an adjacent dense object (or mob) with Spray Cleaner
will now spritz it rather than doing nothing. This means you can use
Spray Cleaner to clean bloodied windows, as the janitor gods intended.
It also means you can fill a spray bottle with Napalm, I guess.
refactor: Any cleaning object can now clean a microwave.
/🆑

* Dehardcodes microwave cleaning, allows spray cleaner to work on dense objects such as windows

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-10-31 10:26:57 -07:00
committed by GitHub
co-authored by MrMelbert
parent 8fb979c23e
commit b2921af343
6 changed files with 137 additions and 99 deletions
@@ -125,7 +125,5 @@
#define COMSIG_ATOM_GERM_EXPOSED "atom_germ_exposed"
/// when atom is picked up from the floor or moved to an elevated structure: (datum/component/germ_exposure)
#define COMSIG_ATOM_GERM_UNEXPOSED "atom_germ_unexposed"
/// when atom is washed
#define COMSIG_ATOM_WASHED "atom_washed"
/// signal sent to puzzle pieces by activator
#define COMSIG_PUZZLE_COMPLETED "puzzle_completed"
@@ -25,7 +25,7 @@ GLOBAL_LIST_INIT(floor_diseases, list(
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement))
RegisterSignal(parent, COMSIG_ATOM_WASHED, PROC_REF(wash)) //Wash germs off dirty things
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(wash)) //Wash germs off dirty things
RegisterSignals(parent, list(
COMSIG_ITEM_DROPPED, //Dropped into the world
@@ -46,13 +46,13 @@ GLOBAL_LIST_INIT(floor_diseases, list(
/datum/component/germ_sensitive/UnregisterFromParent()
REMOVE_TRAIT(parent, TRAIT_GERM_SENSITIVE, REF(src))
UnregisterSignal(parent, list(
COMSIG_ATOM_EXAMINE,
COMSIG_MOVABLE_MOVED,
COMSIG_ATOM_WASHED,
COMSIG_ITEM_DROPPED,
COMSIG_ATOM_EXITED,
COMSIG_ITEM_PICKUP,
COMSIG_ATOM_ENTERED,
COMSIG_ATOM_EXAMINE,
COMSIG_ATOM_EXITED,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_PICKUP,
COMSIG_MOVABLE_MOVED,
))
/datum/component/germ_sensitive/Destroy()
@@ -117,8 +117,10 @@ GLOBAL_LIST_INIT(floor_diseases, list(
parent.AddComponent(/datum/component/infective, new random_disease, weak = TRUE)
/datum/component/germ_sensitive/proc/wash()
SIGNAL_HANDLER
if(infective)
infective = FALSE
qdel(parent.GetComponent(/datum/component/infective))
return COMPONENT_CLEANED
#undef GERM_EXPOSURE_DELAY
+33 -20
View File
@@ -13,6 +13,8 @@
var/lifetime = INFINITY
///Are we a part of a stream?
var/stream
/// String used in combat logs containing reagents present for when the puff hits something
var/logging_string
/obj/effect/decal/chempuff/Destroy(force)
user = null
@@ -22,28 +24,39 @@
/obj/effect/decal/chempuff/blob_act(obj/structure/blob/B)
return
/obj/effect/decal/chempuff/proc/end_life(datum/move_loop/engine)
QDEL_IN(src, engine.delay) //Gotta let it stop drifting
animate(src, alpha = 0, time = engine.delay)
/obj/effect/decal/chempuff/proc/end_life(delay = 0.5 SECONDS)
QDEL_IN(src, delay) //Gotta let it stop drifting
animate(src, alpha = 0, time = delay)
/obj/effect/decal/chempuff/proc/loop_ended(datum/move_loop/source)
SIGNAL_HANDLER
if(QDELETED(src))
return
end_life(source)
end_life(source.delay)
/obj/effect/decal/chempuff/proc/check_move(datum/move_loop/source, result)
SIGNAL_HANDLER
if(QDELETED(src)) //Reasons PLEASE WORK I SWEAR TO GOD
return
if(result == MOVELOOP_FAILURE) //If we hit something
end_life(source)
end_life(source.delay)
return
var/puff_reagents_string = reagents.get_reagent_log_string()
var/travelled_max_distance = (source.lifetime - source.delay <= 0)
var/turf/our_turf = get_turf(src)
spray_down_turf(get_turf(src), travelled_max_distance = (source.lifetime - source.delay <= 0))
for(var/atom/movable/turf_atom in our_turf)
if(lifetime < 0) // Did we use up all the puff early?
end_life(source.delay)
/**
* Handles going through every movable on the passed turf and calling [spray_down_atom] on them.
*
* [travelled_max_distance] is used to determine if we're at the end of the life, as in some
* contexts an atom may or may not end up being exposed depending on how far we've travelled.
*/
/obj/effect/decal/chempuff/proc/spray_down_turf(turf/spraying, travelled_max_distance = FALSE)
for(var/atom/movable/turf_atom in spraying)
if(turf_atom == src || turf_atom.invisibility) //we ignore the puff itself and stuff below the floor
continue
@@ -51,8 +64,7 @@
break
if(!stream)
reagents.expose(turf_atom, VAPOR)
log_combat(user, turf_atom, "sprayed", sprayer, addition="which had [puff_reagents_string]")
spray_down_atom(turf_atom)
if(ismob(turf_atom))
lifetime -= 1
continue
@@ -65,23 +77,24 @@
if(turf_mob.body_position != STANDING_UP && !travelled_max_distance)
continue
reagents.expose(turf_mob, VAPOR)
log_combat(user, turf_mob, "sprayed", sprayer, addition="which had [puff_reagents_string]")
spray_down_atom(turf_atom)
lifetime -= 1
else if(travelled_max_distance)
reagents.expose(turf_atom, VAPOR)
log_combat(user, turf_atom, "sprayed", sprayer, addition="which had [puff_reagents_string]")
spray_down_atom(turf_atom)
lifetime -= 1
if(lifetime >= 0 && (!stream || travelled_max_distance))
reagents.expose(our_turf, VAPOR)
log_combat(user, our_turf, "sprayed", sprayer, addition="which had [puff_reagents_string]")
spray_down_atom(spraying)
lifetime -= 1
// Did we use up all the puff early?
if(lifetime < 0)
end_life(source)
/// Actually handles exposing the passed atom to the reagents and logging
/obj/effect/decal/chempuff/proc/spray_down_atom(atom/spraying)
if(isnull(logging_string))
logging_string = reagents.get_reagent_log_string()
reagents.expose(spraying, VAPOR)
log_combat(user, spraying, "sprayed", sprayer, addition = "which had [logging_string]")
/obj/effect/decal/fakelattice
name = "lattice"
+1 -4
View File
@@ -1374,12 +1374,9 @@
// Update icons if this is being carried by a mob
/obj/item/wash(clean_types)
. = ..()
SEND_SIGNAL(src, COMSIG_ATOM_WASHED)
if(ismob(loc))
var/mob/mob_loc = loc
mob_loc.regenerate_icons()
mob_loc.update_clothing(slot_flags)
/// Called on [/datum/element/openspace_item_click_handler/proc/on_afterattack]. Check the relative file for information.
/obj/item/proc/handle_openspace_click(turf/target, mob/user, proximity_flag, click_parameters)
@@ -218,7 +218,7 @@
. = ..()
// All of these will use a full icon state instead
if(panel_open || dirty == MAX_MICROWAVE_DIRTINESS || broken || dirty_anim_playing)
if(panel_open || dirty >= MAX_MICROWAVE_DIRTINESS || broken || dirty_anim_playing)
return .
var/ingredient_count = 0
@@ -296,7 +296,7 @@
icon_state = "[base_icon_state]mwb"
else if(dirty_anim_playing)
icon_state = "[base_icon_state]mwbloody1"
else if(dirty == MAX_MICROWAVE_DIRTINESS)
else if(dirty >= MAX_MICROWAVE_DIRTINESS)
icon_state = open ? "[base_icon_state]mwbloodyo" : "[base_icon_state]mwbloody"
else if(operating)
icon_state = "[base_icon_state]back_on"
@@ -310,80 +310,83 @@
return ..()
/obj/machinery/microwave/wrench_act(mob/living/user, obj/item/tool)
. = ..()
if(dirty >= MAX_MICROWAVE_DIRTINESS)
return FALSE
if(default_unfasten_wrench(user, tool))
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/microwave/crowbar_act(mob/living/user, obj/item/tool)
if(operating)
return
if(!default_deconstruction_crowbar(tool))
return
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/microwave/screwdriver_act(mob/living/user, obj/item/tool)
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/microwave/wirecutter_act(mob/living/user, obj/item/tool)
if(broken != REALLY_BROKEN)
return
user.visible_message(
span_notice("[user] starts to fix part of [src]."),
span_notice("You start to fix part of [src]..."),
)
if(!tool.use_tool(src, user, 2 SECONDS, volume = 50))
return TOOL_ACT_SIGNAL_BLOCKING
user.visible_message(
span_notice("[user] fixes part of [src]."),
span_notice("You fix part of [src]."),
)
broken = KINDA_BROKEN // Fix it a bit
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/microwave/welder_act(mob/living/user, obj/item/tool)
if(broken != KINDA_BROKEN)
return
user.visible_message(
span_notice("[user] starts to fix part of [src]."),
span_notice("You start to fix part of [src]..."),
)
if(!tool.use_tool(src, user, 2 SECONDS, amount = 1, volume = 50))
return TOOL_ACT_SIGNAL_BLOCKING
user.visible_message(
span_notice("[user] fixes [src]."),
span_notice("You fix [src]."),
)
broken = NOT_BROKEN
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/microwave/tool_act(mob/living/user, obj/item/tool, tool_type, is_right_clicking)
if(operating)
return
if(dirty >= MAX_MICROWAVE_DIRTINESS)
return
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
. = ..()
if(. & TOOL_ACT_MELEE_CHAIN_BLOCKING)
return
if(panel_open && is_wire_tool(tool))
wires.interact(user)
return TOOL_ACT_SIGNAL_BLOCKING
/obj/machinery/microwave/attackby(obj/item/item, mob/living/user, params)
if(operating)
return
if(panel_open && is_wire_tool(item))
wires.interact(user)
return TRUE
if(broken > NOT_BROKEN)
if(broken == REALLY_BROKEN && item.tool_behaviour == TOOL_WIRECUTTER) // If it's broken and they're using a TOOL_WIRECUTTER
user.visible_message(span_notice("[user] starts to fix part of \the [src]."), span_notice("You start to fix part of \the [src]..."))
if(item.use_tool(src, user, 20))
user.visible_message(span_notice("[user] fixes part of \the [src]."), span_notice("You fix part of \the [src]."))
broken = KINDA_BROKEN // Fix it a bit
else if(broken == KINDA_BROKEN && item.tool_behaviour == TOOL_WELDER) // If it's broken and they're doing the wrench
user.visible_message(span_notice("[user] starts to fix part of \the [src]."), span_notice("You start to fix part of \the [src]..."))
if(item.use_tool(src, user, 20))
user.visible_message(span_notice("[user] fixes \the [src]."), span_notice("You fix \the [src]."))
broken = NOT_BROKEN
update_appearance()
return FALSE //to use some fuel
else
if(IS_EDIBLE(item))
balloon_alert(user, "it's broken!")
return TRUE
return
if(istype(item, /obj/item/reagent_containers/spray))
var/obj/item/reagent_containers/spray/clean_spray = item
open(autoclose = 2 SECONDS)
if(clean_spray.reagents.has_reagent(/datum/reagent/space_cleaner, clean_spray.amount_per_transfer_from_this))
clean_spray.reagents.remove_reagent(/datum/reagent/space_cleaner, clean_spray.amount_per_transfer_from_this,1)
playsound(loc, 'sound/effects/spray3.ogg', 50, TRUE, -6)
user.visible_message(span_notice("[user] cleans \the [src]."), span_notice("You clean \the [src]."))
dirty = 0
update_appearance()
else
to_chat(user, span_warning("You need more space cleaner!"))
return TRUE
if(istype(item, /obj/item/soap) || istype(item, /obj/item/reagent_containers/cup/rag))
var/cleanspeed = 50
if(istype(item, /obj/item/soap))
var/obj/item/soap/used_soap = item
cleanspeed = used_soap.cleanspeed
user.visible_message(span_notice("[user] starts to clean \the [src]."), span_notice("You start to clean \the [src]..."))
open(autoclose = cleanspeed + 1 SECONDS)
if(do_after(user, cleanspeed, target = src))
user.visible_message(span_notice("[user] cleans \the [src]."), span_notice("You clean \the [src]."))
dirty = 0
update_appearance()
return TRUE
return ..()
if(istype(item, /obj/item/stock_parts/cell) && cell_powered)
var/swapped = FALSE
@@ -402,12 +405,16 @@
return TRUE
if(!anchored)
balloon_alert(user, "not secured!")
if(IS_EDIBLE(item))
balloon_alert(user, "not secured!")
return TRUE
return ..()
if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used!
balloon_alert(user, "it's too dirty!")
return TRUE
if(IS_EDIBLE(item))
balloon_alert(user, "it's too dirty!")
return TRUE
return ..()
if(vampire_charging_capable && istype(item, /obj/item/modular_computer/pda) && ingredients.len > 0)
balloon_alert(user, "max 1 pda!")
@@ -522,6 +529,15 @@
if("examine")
examine(user)
/obj/machinery/microwave/wash(clean_types)
. = ..()
if(operating || !(clean_types & CLEAN_SCRUB))
return .
dirty = 0
update_appearance()
return . || TRUE
/obj/machinery/microwave/proc/eject()
var/atom/drop_loc = drop_location()
for(var/atom/movable/movable_ingredient as anything in ingredients)
@@ -50,18 +50,23 @@
to_chat(user, span_warning("Not enough left!"))
return
spray(target, user)
if(proximity_flag && (target.density || ismob(target)))
// If we're spraying an adjacent mob or a dense object, we start the spray on ITS tile rather than OURs
// This is so we can use a spray bottle to clean stuff like windows without getting blocked by passflags
spray(target, user, get_turf(target))
else
spray(target, user)
playsound(src.loc, spray_sound, 50, TRUE, -6)
playsound(src, spray_sound, 50, TRUE, -6)
user.changeNext_move(CLICK_CD_RANGE*2)
user.newtonian_move(get_dir(target, user))
return
/// Handles creating a chem puff that travels towards the target atom, exposing reagents to everything it hits on the way.
/obj/item/reagent_containers/spray/proc/spray(atom/target, mob/user)
/obj/item/reagent_containers/spray/proc/spray(atom/target, mob/user, turf/start_turf = get_turf(src))
var/range = max(min(current_range, get_dist(src, target)), 1)
var/obj/effect/decal/chempuff/reagent_puff = new /obj/effect/decal/chempuff(get_turf(src))
var/obj/effect/decal/chempuff/reagent_puff = new(start_turf)
reagent_puff.create_reagents(amount_per_transfer_from_this)
var/puff_reagent_left = range //how many turf, mob or dense objet we can react with before we consider the chem puff consumed
@@ -74,9 +79,7 @@
var/wait_step = max(round(2+3/range), 2)
var/puff_reagent_string = reagent_puff.reagents.get_reagent_log_string()
var/turf/src_turf = get_turf(src)
log_combat(user, src_turf, "fired a puff of reagents from", src, addition="with a range of \[[range]\], containing [puff_reagent_string].")
log_combat(user, start_turf, "fired a puff of reagents from", src, addition="with a range of \[[range]\], containing [puff_reagent_string].")
user.log_message("fired a puff of reagents from \a [src] with a range of \[[range]\] and containing [puff_reagent_string].", LOG_ATTACK)
// do_spray includes a series of step_towards and sleeps. As a result, it will handle deletion of the chempuff.
@@ -84,11 +87,20 @@
/// Handles exposing atoms to the reagents contained in a spray's chempuff. Deletes the chempuff when it's completed.
/obj/item/reagent_containers/spray/proc/do_spray(atom/target, wait_step, obj/effect/decal/chempuff/reagent_puff, range, puff_reagent_left, mob/user)
var/datum/move_loop/our_loop = SSmove_manager.move_towards_legacy(reagent_puff, target, wait_step, timeout = range * wait_step, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
reagent_puff.user = user
reagent_puff.sprayer = src
reagent_puff.lifetime = puff_reagent_left
reagent_puff.stream = stream_mode
var/turf/target_turf = get_turf(target)
var/turf/start_turf = get_turf(reagent_puff)
if(target_turf == start_turf) // Don't need to bother movelooping if we don't move
reagent_puff.setDir(user.dir)
reagent_puff.spray_down_turf(target_turf)
reagent_puff.end_life()
return
var/datum/move_loop/our_loop = SSmove_manager.move_towards_legacy(reagent_puff, target, wait_step, timeout = range * wait_step, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
reagent_puff.RegisterSignal(our_loop, COMSIG_QDELETING, TYPE_PROC_REF(/obj/effect/decal/chempuff, loop_ended))
reagent_puff.RegisterSignal(our_loop, COMSIG_MOVELOOP_POSTPROCESS, TYPE_PROC_REF(/obj/effect/decal/chempuff, check_move))
@@ -181,7 +193,7 @@
if(do_after(user, 3 SECONDS, user))
if(reagents.total_volume >= amount_per_transfer_from_this)//if not empty
user.visible_message(span_suicide("[user] pulls the trigger!"))
spray(user)
spray(user, user)
return BRUTELOSS
else
user.visible_message(span_suicide("[user] pulls the trigger...but \the [src] is empty!"))