From b2921af3434a8a43dcff9f6d2c18aa8d7a9cf23c Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:26:57 +0100 Subject: [PATCH] [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 :cl: 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. /:cl: * Dehardcodes microwave cleaning, allows spray cleaner to work on dense objects such as windows --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> --- .../signals/signals_atom/signals_atom_main.dm | 2 - code/datums/components/food/germ_sensitive.dm | 16 ++- code/game/objects/effects/decals/misc.dm | 53 ++++--- code/game/objects/items.dm | 5 +- .../food_and_drinks/machinery/microwave.dm | 130 ++++++++++-------- .../reagents/reagent_containers/spray.dm | 30 ++-- 6 files changed, 137 insertions(+), 99 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index f51e59f93d3..c3ec1aa8a84 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -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" diff --git a/code/datums/components/food/germ_sensitive.dm b/code/datums/components/food/germ_sensitive.dm index 22ba793c1ce..d0acc49714a 100644 --- a/code/datums/components/food/germ_sensitive.dm +++ b/code/datums/components/food/germ_sensitive.dm @@ -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 diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index 4254d99fdc3..112f3b228dc 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -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" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a8aeab47614..9249a4a12da 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -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) diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm index 8488025149a..c77f2e9e23e 100644 --- a/code/modules/food_and_drinks/machinery/microwave.dm +++ b/code/modules/food_and_drinks/machinery/microwave.dm @@ -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) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 28c26c5d356..a554dca6a84 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -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!"))