diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 647f48e0a62..5899bb7a4b7 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -1,11 +1,19 @@ -// Disposal bin -// Holds items for disposal into pipe system -// Draws air from turf, gradually charges internal reservoir -// Once full (~1 atm), uses air resv to flush items into the pipes -// Automatically recharges air (unless off), will flush when ready if pre-set -// Can hold items and human size things, no other draggables -// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE +//////////////////////////////////////// +// MARK: Disposal unit +//////////////////////////////////////// +/* +* Holds items for disposal into pipe system +* Draws air from turf, gradually charges internal reservoir +* Once full (~1 atm), uses air resv to flush items into the pipes +* Automatically recharges air (unless off), will flush when ready if pre-set +* Can hold items and human size things, no other draggables +* Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation +*/ +#define SEND_PRESSURE 0.05 * ONE_ATMOSPHERE +#define DISPOSALS_UNSCREWED -1 +#define DISPOSALS_OFF 0 +#define DISPOSALS_RECHARGING 1 +#define DISPOSALS_CHARGED 2 /obj/machinery/disposal name = "disposal unit" @@ -22,22 +30,30 @@ active_power_consumption = 600 idle_power_consumption = 100 - var/datum/gas_mixture/air_contents // internal reservoir - var/mode = 1 // item mode 0=off 1=charging 2=charged - var/flush = 0 // true if flush handle is pulled - var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk - var/flushing = 0 // true if flushing in progress - var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush - var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. + /// Internal gas reservoir. + var/datum/gas_mixture/air_contents + /// Can be one of: DISPOSALS_UNSCREWED (power off and panel unscrewed), DISPOSALS_OFF (power switched off), DISPOSALS_RECHARGING (active and charging), or DISPOSALS_CHARGED (active and ready to flush) + var/mode = DISPOSALS_RECHARGING + /// Has the flush handle been pulled? + var/flush = FALSE + /// The attached pipe trunk. + var/obj/structure/disposalpipe/trunk/trunk = null + /// Is the disposals bin flushing right now? + var/flushing = FALSE + /// After the specified number of ticks, the disposals bin will check if it is ready to flush. + var/flush_every_ticks = 30 + /// Counter that increments by 1 per tick. Resets when it reaches the value of flush_every_ticks and attempts to perform a flush. + var/flush_count = 0 var/last_sound = 0 - var/required_mode_to_deconstruct = -1 + var/required_mode_to_deconstruct = DISPOSALS_UNSCREWED + /// What does this drop when deconstructed? var/deconstructs_to = PIPE_DISPOSALS_BIN /obj/machinery/disposal/proc/trunk_check() var/obj/structure/disposalpipe/trunk/T = locate() in loc if(!T) - mode = 0 - flush = 0 + mode = DISPOSALS_OFF + flush = FALSE else mode = initial(mode) flush = initial(flush) @@ -52,7 +68,10 @@ if(T.intact) var/turf/simulated/floor/F = T F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + T.visible_message( + "The floortile is ripped from the floor!", + "You hear a loud bang!" + ) if(trunk) trunk.remove_trunk_links() var/obj/structure/disposalconstruct/C = new (loc) @@ -118,64 +137,114 @@ var/obj/item/storage/S = I if(!S.removal_allowed_check(user)) return + if((S.allow_quick_empty || S.allow_quick_gather) && length(S.contents)) S.hide_from(user) - user.visible_message("[user] empties \the [S] into \the [src].", "You empty \the [S] into \the [src].") + user.visible_message( + "[user] empties [S] into the disposal unit.", + "You empty [S] into disposal unit.", + "You hear someone emptying something into a disposal unit." + ) for(var/obj/item/O in S.contents) S.remove_from_storage(O, src) S.update_icon() // For content-sensitive icons update() return + // Borg using their gripper to throw stuff away. + if(istype(I, /obj/item/gripper/)) + var/obj/item/gripper/gripper = I + // Gripper is empty. + if(!gripper.gripped_item) + to_chat(user, "There's nothing in your gripper to throw away!") + return + + gripper.gripped_item.forceMove(src) + user.visible_message( + "[user] places [gripper.gripped_item] into the disposal unit.", + "You place [gripper.gripped_item] into the disposal unit.", + "You hear someone dropping something into a disposal unit." + ) + return + + // Someone has a mob in a grab. var/obj/item/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for(var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM] into the disposal.", 3) - if(do_after(usr, 20, target = GM)) - GM.forceMove(src) - for(var/mob/C in viewers(src)) - C.show_message("[GM] has been placed in [src] by [user].", 3) - qdel(G) - add_attack_logs(usr, GM, "Disposal'ed", !!GM.ckey ? null : ATKLOG_ALL) + if(istype(G)) + // If there's not actually a mob in the grab, stop it. Get some help. + if(!ismob(G.affecting)) + return + + var/mob/GM = G.affecting + user.visible_message( + "[user] starts stuffing [GM] into the disposal unit!", + "You start stuffing [GM] into the disposal unit.", + "You hear someone trying to stuff someone else into a disposal unit!" + ) + + // Abort if the target manages to scurry away. + if(!do_after(user, 2 SECONDS, target = GM)) + return + + GM.forceMove(src) + user.visible_message( + "[GM] has been stuffed into the disposal unit by [user]!", + "You stuff [GM] into the disposal unit.", + "You hear someone being stuffed into a disposal unit!" + ) + qdel(G) + update() + add_attack_logs(user, GM, "Disposal'ed", !GM.ckey ? null : ATKLOG_ALL) return if(!user.drop_item() || QDELETED(I)) return + // If we're here, it's an item without any special interactions, drop it in the bin without any further delay. I.forceMove(src) - user.visible_message("[user] places [I] into [src].", "You place [I] into [src].") + user.visible_message( + "[user] places [I] into the disposal unit.", + "You place [I] into the disposal unit.", + "You hear someone dropping something into a disposal unit." + ) update() - - - /obj/machinery/disposal/screwdriver_act(mob/user, obj/item/I) - if(mode>0) // It's on + if(mode != DISPOSALS_OFF) // It's on + to_chat(user, "You need to turn the disposal unit off first!") return + . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return + if(length(contents) > 0) - to_chat(user, "Eject the items first!") + to_chat(user, "You need to empty the contents of the disposal unit first!") return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - else if(mode==-1) - mode=0 - to_chat(user, "You [mode ? "unfasten": "fasten"] the screws around the power connection.") + + if(mode == DISPOSALS_OFF) // It's off but still not unscrewed + mode = DISPOSALS_UNSCREWED + else if(mode == DISPOSALS_UNSCREWED) + mode = DISPOSALS_OFF + user.visible_message( + "[user] [mode ? "unfastens": "fastens"] the screws around the power connection of the disposal unit.", + "You [mode ? "unfasten": "fasten"] the screws around the power connection of the disposal unit.", + "You hear screws being [mode ? "unfastened": "fastened"]." + ) update() /obj/machinery/disposal/welder_act(mob/user, obj/item/I) - if(mode != required_mode_to_deconstruct) + if(mode != DISPOSALS_UNSCREWED) + to_chat(user, "You need to unscrew the disposal unit first!") return + . = TRUE if(length(contents) > 0) - to_chat(user, "Eject the items first!") + to_chat(user, "You need to empty the contents of the disposal unit first!") return + if(!I.tool_use_check(user, 0)) return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE if(I.use_tool(src, user, 20, volume = I.tool_volume)) WELDER_FLOOR_SLICE_SUCCESS_MESSAGE @@ -188,9 +257,9 @@ /obj/machinery/disposal/shove_impact(mob/living/target, mob/living/attacker) target.visible_message( - "[attacker] shoves [target] inside of [src]!", - "[attacker] shoves you inside of [src]!", - "You hear the sound of something being thrown in the trash." + "[attacker] shoves [target] inside of the disposal unit!", + "[attacker] shoves you inside of the disposal unit!", + "You hear the sound of someone being thrown into a disposal unit." ) target.forceMove(src) add_attack_logs(attacker, target, "Shoved into disposals", target.ckey ? null : ATKLOG_ALL) @@ -203,32 +272,54 @@ /obj/machinery/disposal/MouseDrop_T(mob/living/target, mob/living/user) if(!istype(target) || target.buckled || target.has_buckled_mobs() || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || isAI(user)) return + + // Animals cannot put mobs other than themselves into disposals. if(isanimal(user) && target != user) - return //animals cannot put mobs other than themselves into disposal + return + src.add_fingerprint(user) - for(var/mob/V in viewers(usr)) - if(target == user && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(target == user && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) + user.visible_message( + "[user] starts climbing into the disposal unit.", + "You start climbing into the disposal unit.", + "You hear someone trying to climb into a disposal unit." + ) + + if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) + if(target.anchored) + return + user.visible_message("[user] starts stuffing [target.name] into the disposal.") + user.visible_message( + "[user] starts stuffing [target] into the disposal unit!", + "You start stuffing [target] into the disposal unit.", + "You hear someone trying to stuff someone else into a disposal unit!" + ) INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/machinery/disposal, put_in), target, user) return TRUE /obj/machinery/disposal/proc/put_in(mob/living/target, mob/living/user) // need this proc to use INVOKE_ASYNC in other proc. You're not recommended to use that one - var/msg - var/target_loc = target.loc - if(!do_after(usr, 20, target = target)) + if(!do_after(user, 20, target = target)) return + + var/target_loc = target.loc if(QDELETED(src) || target_loc != target.loc) return - if(target == user && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into [src]." - to_chat(user, "You climb into [src].") + + // All the extra checks ensure you cannot disposal yourself/others whilst incapacitated. + if(target == user && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) + user.visible_message( + "[user] climbs into the disposal unit.", + "You climb into the disposal unit.", + "You hear someone climbing into a disposal unit." + ) + else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.IsStunned() && !user.IsParalyzed()) - msg = "[user.name] stuffs [target.name] into [src]!" - to_chat(user, "You stuff [target.name] into [src]!") + user.visible_message( + "[user] stuffs [target] into the disposal unit.", + "You stuff [target] into the disposal unit.", + "You hear the sound of someone being stuffed into a disposal unit." + ) + if(!iscarbon(user)) target.LAssailant = null else @@ -236,14 +327,9 @@ add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) else return + QDEL_LIST_CONTENTS(target.grabbed_by) target.forceMove(src) - - for(var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - update() // attempt to move while inside @@ -311,11 +397,11 @@ if(..()) return if(usr.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") + to_chat(usr, "You cannot reach the controls from inside!") return - if(mode==-1 && action != "eject") // If the mode is -1, only allow ejection - to_chat(usr, "The disposal units power is disabled.") + if(mode == DISPOSALS_UNSCREWED && action != "eject") // If the mode is DISPOSALS_UNSCREWED, only allow ejection + to_chat(usr, "The disposal unit's power is disabled!") return if(stat & BROKEN) @@ -328,18 +414,18 @@ if(isturf(src.loc)) if(action == "pumpOn") - mode = 1 + mode = DISPOSALS_RECHARGING update() if(action == "pumpOff") - mode = 0 + mode = DISPOSALS_OFF update() if(!issilicon(usr)) if(action == "engageHandle") - flush = 1 + flush = TRUE update() if(action == "disengageHandle") - flush = 0 + flush = FALSE update() if(action == "eject") @@ -358,21 +444,24 @@ if(!Adjacent(user) || !ishuman(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) return user.visible_message( - "[user] tries to eject the contents of [src] manually.", - "You operate the manual ejection lever on [src]." + "[user] tries to eject the contents of the disposal unit manually.", + "You operate the manual ejection lever on the disposal unit.", + "You hear a lever being pulled." ) + if(do_after(user, 5 SECONDS, target = src)) user.visible_message( - "[user] ejects the contents of [src].", - "You eject the contents of [src]." + "[user] ejects the contents of the disposal unit.", + "You eject the contents of the disposal unit.", + "You hear a sudden gush of air and the clattering of objects." ) eject() // update the icon & overlays to reflect mode & status /obj/machinery/disposal/proc/update() if(stat & BROKEN) - mode = 0 - flush = 0 + mode = DISPOSALS_OFF + flush = FALSE update_icon() @@ -392,7 +481,7 @@ if(flush) . += "dispover-handle" - if(stat & (NOPOWER|BROKEN) || mode == -1) + if(stat & (NOPOWER|BROKEN) || mode == DISPOSALS_UNSCREWED) return // check for items in disposal - occupied light @@ -402,12 +491,12 @@ // charging and ready light switch(mode) - if(-1) + if(DISPOSALS_UNSCREWED) . += "dispover-unscrewed" - if(1) + if(DISPOSALS_RECHARGING) . += "dispover-charge" underlays += emissive_appearance(icon, "dispover-charge") - if(2) + if(DISPOSALS_CHARGED) . += "dispover-ready" underlays += emissive_appearance(icon, "dispover-ready") @@ -421,7 +510,7 @@ flush_count++ if(flush_count >= flush_every_ticks) if(length(contents)) - if(mode == 2) + if(mode == DISPOSALS_CHARGED) spawn(0) flush() flush_count = 0 @@ -436,7 +525,7 @@ change_power_mode(IDLE_POWER_USE) - if(mode != 1) // if off or ready, no need to charge + if(mode != DISPOSALS_RECHARGING) // if off or ready, no need to charge return // otherwise charge @@ -445,7 +534,6 @@ var/datum/milla_safe/disposal_suck_air/milla = new() milla.invoke_async(src) - // perform a flush /obj/machinery/disposal/proc/flush() @@ -481,8 +569,8 @@ flushing = 0 // now reset disposal state flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging + if(mode == DISPOSALS_CHARGED) // if was ready, + mode = DISPOSALS_RECHARGING // switch to charging update() return @@ -527,16 +615,24 @@ var/obj/item/I = mover if(isprojectile(I)) return + var/atom/movable/thrower = mover.throwing?.get_thrower() if(prob(75) || (istype(thrower) && (HAS_TRAIT(thrower, TRAIT_BADASS) || HAS_TRAIT(thrower, TRAIT_NEVER_MISSES_DISPOSALS)))) I.forceMove(src) - for(var/mob/M in viewers(src)) - M.show_message("[I] lands in [src].", 3) + visible_message( + "[I] lands in [src].", + "You hear something being tossed into a disposal unit." + ) update() + return + else - for(var/mob/M in viewers(src)) - M.show_message("\the [I] bounces off of \the [src]'s rim!", 3) - return 0 + visible_message( + "[I] bounces off of [src]'s rim!", + "You hear something bouncing off the rim of a disposal unit!" + ) + return + else return ..(mover, target, height) @@ -552,11 +648,18 @@ /obj/machinery/disposal/force_eject_occupant(mob/target) target.forceMove(get_turf(src)) +#undef DISPOSALS_UNSCREWED +#undef DISPOSALS_OFF +#undef DISPOSALS_RECHARGING +#undef DISPOSALS_CHARGED + +//////////////////////////////////////// +// MARK: Disposal holder +//////////////////////////////////////// // virtual disposal object // travels through pipes in lieu of actual items // contents will be items flushed by the disposal // this allows the gas flushed to be tracked - /obj/structure/disposalholder invisibility = INVISIBILITY_MAXIMUM resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF @@ -720,7 +823,9 @@ if(istype(location)) location.blind_release_air(gas) -// Disposal pipes +//////////////////////////////////////// +// MARK: Disposals pipes +//////////////////////////////////////// /obj/structure/disposalpipe icon = 'icons/obj/pipes/disposal.dmi' @@ -1013,7 +1118,9 @@ /obj/structure/disposalpipe/segment/corner icon_state = "pipe-c" -//a three-way junction with dir being the dominant direction +//////////////////////////////////////// +// MARK: Disposals junction +//////////////////////////////////////// /obj/structure/disposalpipe/junction icon_state = "pipe-j1" @@ -1062,7 +1169,9 @@ else return mask & (~setbit) -//a three-way junction that sorts objects +//////////////////////////////////////// +// MARK: Sorting junction +//////////////////////////////////////// /obj/structure/disposalpipe/sortjunction name = "disposal sort junction" icon_state = "pipe-j1s" @@ -1197,7 +1306,9 @@ return P - +//////////////////////////////////////// +// MARK: Wrap sort junction +//////////////////////////////////////// //a three-way junction that sorts objects destined for the mail office mail table (tomail = 1) /obj/structure/disposalpipe/wrapsortjunction desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." @@ -1363,7 +1474,9 @@ else return 0 -// a broken pipe +//////////////////////////////////////// +// MARK: Broken pipe +//////////////////////////////////////// /obj/structure/disposalpipe/broken icon_state = "pipe-b" dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes @@ -1382,8 +1495,9 @@ qdel(src) return TRUE -// the disposal outlet machine - +//////////////////////////////////////// +// MARK: Disposals outlet +//////////////////////////////////////// /obj/structure/disposaloutlet name = "disposal outlet" desc = "An outlet for the pneumatic disposal system." @@ -1475,7 +1589,11 @@ if(T.intact) var/turf/simulated/floor/F = T F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + T.visible_message( + "The floortile is ripped from the floor!", + "You hear a loud bang!" + ) + if(linkedtrunk) linkedtrunk.remove_trunk_links() var/obj/structure/disposalconstruct/C = new (loc)