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!"))