Refactors disposal connection components, makes them more properly connected (#18912)

* Awawa I scopecreeped too hard

* functionality

* oops, missed a spot

* unglog

Ouuurrrrp my code so full of bad

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Reo Lozzot
2026-01-14 14:13:21 -06:00
committed by GitHub
parent 6a8b83a087
commit d8684edb94
11 changed files with 419 additions and 281 deletions
@@ -1,11 +1,20 @@
///called when a disposal connected object flushes its contents into the disposal pipe network
#define COMSIG_DISPOSAL_FLUSH "disposal_system_flushing"
///called when a disposal connected object gets a packet from the disposal pipe network
#define COMSIG_DISPOSAL_RECEIVING "disposal_system_receiving"
///called when a disposal trunk attempts to send a packet, to be recieved by an atom with a disposal network connection component.
#define COMSIG_DISPOSAL_SEND "disposal_system_sending"
///called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
///called when a disposal connected object recieves an object from it's connected trunk
#define COMSIG_DISPOSAL_RECEIVE "disposal_system_receiving"
///called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, atom/source)
//#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //This is in signals_atom_movable.dm but kept here for housekeeping.
///Called right after the atom is flushed into a disposal holder and sent through the disposal network: (/obj/structure/disposalholder)
#define COMSIG_ATOM_DISPOSAL_FLUSHED "atom_disposal_flushed"
///called when a disposal connected object attempts to link to a trunk: (/obj/structure/disposalpipe/trunk)
#define COMSIG_DISPOSAL_LINK "disposal_system_linkage"
///called when a disposal connected object should unlink from a trunk it's attached to.
#define COMSIG_DISPOSAL_UNLINK "disposal_system_unlink"
+5
View File
@@ -63,3 +63,8 @@
/datum/category_item/autolathe/tools/rsf
name = "rapid service fabricator"
path = /obj/item/rsf
/datum/category_item/autolathe/tools/dest_tagger
name = "destination tagger"
path = /obj/item/destTagger
resources = list(MAT_STEEL = 250, MAT_GLASS = 125)
@@ -1,75 +1,104 @@
///Disposal connection component, allows an atom to recieve and send disposal packages if attached to a disposal trunk.
/datum/component/disposal_system_connection
VAR_PROTECTED/obj/disposal_owner = null
//The connected trunk. Also determines if we're linked already or not.
var/obj/structure/disposalpipe/trunk/connected_trunk = null
/datum/component/disposal_system_connection/Initialize()
var/atom/disposal_owner
/// The proc that the owner has that'll accept a list of items from recieved disposal packets.
var/visible_connection
/datum/component/disposal_system_connection/Initialize(visibly_connects = TRUE)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
if(isarea(parent))
return COMPONENT_INCOMPATIBLE
disposal_owner = parent
RegisterSignal(disposal_owner, COMSIG_DISPOSAL_FLUSH, PROC_REF(on_flush))
RegisterSignal(disposal_owner, COMSIG_DISPOSAL_RECEIVING, PROC_REF(on_recieve))
RegisterSignal(disposal_owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
visible_connection = visibly_connects
/datum/component/disposal_system_connection/Destroy()
UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_FLUSH)
UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_RECEIVING)
UnregisterSignal(disposal_owner, COMSIG_ATOM_EXAMINE)
disposal_owner = null
if(connected_trunk)
UnregisterSignal(connected_trunk, COMSIG_DISPOSAL_SEND)
connected_trunk = null
. = ..()
disposal_owner = null
/datum/component/disposal_system_connection/RegisterWithParent()
RegisterSignal(disposal_owner, COMSIG_DISPOSAL_FLUSH, PROC_REF(on_flush))
RegisterSignal(disposal_owner, COMSIG_DISPOSAL_LINK, PROC_REF(link_to_trunk))
RegisterSignal(disposal_owner, COMSIG_DISPOSAL_UNLINK, PROC_REF(unlink_from_trunk))
RegisterSignal(disposal_owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
/datum/component/disposal_system_connection/UnregisterFromParent()
UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_FLUSH)
UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_LINK)
UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_UNLINK)
UnregisterSignal(disposal_owner, COMSIG_ATOM_EXAMINE)
// Signal handling
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/disposal_system_connection/proc/on_flush(datum/source,datum/gas_mixture/flush_gas)
/datum/component/disposal_system_connection/proc/on_flush(datum/source, list/flushed_items, datum/gas_mixture/flush_gas)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
if(!can_accept())
return FALSE
// Important note, the flush_gas will be passed to the disposal packet when it's made. Caller should make a fresh gasmix datum after flushing this one!
return handle_flush(flush_gas)
return handle_flush(flushed_items, flush_gas)
/datum/component/disposal_system_connection/proc/on_recieve(datum/source,obj/structure/disposalholder/packet)
/datum/component/disposal_system_connection/proc/link_to_trunk(datum/source, obj/structure/disposalpipe/trunk/trunk)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
if(!can_accept())
if(!trunk)
return FALSE
if(trunk.linked) //Already linked to something
return FALSE
connected_trunk = trunk
trunk.linked = disposal_owner
RegisterSignal(trunk, COMSIG_DISPOSAL_SEND, PROC_REF(on_recieve))
/datum/component/disposal_system_connection/proc/unlink_from_trunk(datum/source)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
if(connected_trunk)
UnregisterSignal(connected_trunk, COMSIG_DISPOSAL_SEND)
connected_trunk = null
/datum/component/disposal_system_connection/proc/on_recieve(datum/source, obj/structure/disposalholder/packet)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
return handle_expel(packet)
/datum/component/disposal_system_connection/proc/on_examine(datum/source, mob/user, list/examine_texts)
SIGNAL_HANDLER
var/has_connection = FALSE
if(can_accept() && (locate(/obj/structure/disposalpipe/trunk) in get_turf(disposal_owner)))
has_connection = TRUE
examine_texts += span_notice("It [has_connection ? "is connected" : "can be connected"] to a disposal pipe network.")
if(!visible_connection)
return
examine_texts += span_notice("It [connected_trunk ? "is connected" : "can be connected"] to a disposal pipe network.")
// Flush handling, can be override by subtypes but excepts parent proc to handle core logic
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/disposal_system_connection/proc/handle_flush(datum/gas_mixture/flush_gas)
/datum/component/disposal_system_connection/proc/handle_flush(list/flushed_items, datum/gas_mixture/flush_gas)
PROTECTED_PROC(TRUE)
SHOULD_CALL_PARENT(TRUE)
var/obj/structure/disposalholder/packet = new() // virtual holder object which actually travels through the pipes.
packet.init(get_flushed_item_list(), flush_gas)
finish_flush(packet)
return TRUE
packet.init(flushed_items, flush_gas)
/datum/component/disposal_system_connection/proc/finish_flush(obj/structure/disposalholder/packet)
PROTECTED_PROC(TRUE)
SHOULD_CALL_PARENT(TRUE)
// if no trunk connected, expel immediately
var/obj/structure/disposalpipe/trunk/trunk = locate() in get_turf(disposal_owner)
if(!trunk)
if(!connected_trunk)
handle_expel(packet)
return
return TRUE
// start the holder processing movement
packet.forceMove(trunk)
packet.forceMove(connected_trunk)
packet.active = TRUE
packet.set_dir(DOWN)
packet.move()
return TRUE
// Expel handling, can be override by subtypes but excepts parent proc to handle core logic
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/disposal_system_connection/proc/handle_expel(obj/structure/disposalholder/packet, delay_time = 0)
/datum/component/disposal_system_connection/proc/handle_expel(obj/structure/disposalholder/packet)
// Returns true if our owner could handle this packet, used by our child procs to animate our owner.
PROTECTED_PROC(TRUE)
SHOULD_CALL_PARENT(TRUE)
if(!packet || QDELETED(packet))
return FALSE
@@ -82,71 +111,5 @@
var/datum/gas_mixture/gas = new()
gas.copy_from(packet.gas)
qdel(packet)
// Handle expelling stuff, most things have a delay before ejecting.
if(delay_time <= 0) // Someone is going to pass a negative, I just know it. No, BAD, cutting this off before it gets the chance.
expel_finish(expelled_items, gas)
else
addtimer(CALLBACK(src, PROC_REF(expel_finish), expelled_items, gas), delay_time, TIMER_DELETE_ME)
SEND_SIGNAL(disposal_owner, COMSIG_DISPOSAL_RECEIVE, expelled_items, gas)
return TRUE
/datum/component/disposal_system_connection/proc/expel_finish(list/expelled_items,datum/gas_mixture/gas)
PROTECTED_PROC(TRUE)
SHOULD_CALL_PARENT(TRUE)
// This proc has some checks just incase we have a subtype that wants to handle expelled items or if it vents gasses itself.
// This way it can just pass nulls to this parent proc if it does it's own handling for those things. Like a machine that eats all stuff dumped into it instead of ejecting.
// Drop all those items
if(expelled_items.len)
var/turf/target = get_expel_target()
for(var/atom/movable/AM in expelled_items)
if(QDELETED(AM))
continue
AM.forceMove(get_turf(disposal_owner))
AM.pipe_eject(disposal_owner.dir)
if(istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z
continue
AM.throw_at(target, 3, 1)
expelled_items.Cut()
// vent gasses to turf
if(gas)
if(gas.return_pressure() > 0)
playsound(disposal_owner, 'sound/machines/hiss.ogg', 50, 0, 0)
var/turf/T = get_turf(disposal_owner)
if(T)
T.assume_air(gas)
qdel(gas)
// Overridable procs for easily customizing subtypes
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Override this to add additional checks if an object can flush or accept incoming packets
/datum/component/disposal_system_connection/proc/can_accept()
PROTECTED_PROC(TRUE)
return disposal_owner.anchored
/// Override this to launch items in a specific direction
/datum/component/disposal_system_connection/proc/get_expel_target()
PROTECTED_PROC(TRUE)
return get_offset_target_turf(get_turf(disposal_owner), rand(5)-rand(5), rand(5)-rand(5))
/// Override this to give a custom list of items to flush instead of just the contents of the owner
/datum/component/disposal_system_connection/proc/get_flushed_item_list()
PROTECTED_PROC(TRUE)
return disposal_owner.contents
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Subtypes for handling how things are animated, if items are spat out, or deciding what items inside an object are flushed
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Disposal outlets, needs to animate with a timed buzzer before stuff ejects
/datum/component/disposal_system_connection/disposaloutlet/handle_expel(obj/structure/disposalholder/packet, delay_time)
. = ..(packet,3 SECONDS)
if(.)
flick("outlet-open", disposal_owner)
playsound(disposal_owner, 'sound/machines/warning-buzzer.ogg', 50, 0, 0)
/datum/component/disposal_system_connection/disposaloutlet/get_expel_target()
return get_ranged_target_turf(get_turf(disposal_owner), disposal_owner.dir, 10)
+1 -1
View File
@@ -195,7 +195,7 @@
our_guy.visible_message(span_danger("[our_guy] slips on a spill near the [evil_disposal] and falls in!"), span_boldwarning("You slip on a spill near the [evil_disposal] and fall in!"))
living_guy.forceMove(evil_disposal)
evil_disposal.flush = TRUE
evil_disposal.update()
evil_disposal.update_icon()
living_guy.Stun(5)
consume_omen()
return
+1 -1
View File
@@ -28,7 +28,7 @@
M.Weaken(2)
M.forceMove(src)
M.stop_flying()
update()
update_icon()
/obj/structure/inflatable/stumble_into(mob/living/M)
playsound(src, "sound/effects/Glasshit.ogg", 25, 1, -1)
@@ -23,12 +23,12 @@
// Incin/space
/obj/machinery/disposal/burn_pit
name = "disposal(hazardous)"
name = "disposal (hazardous)"
desc = "A pneumatic waste disposal unit. This unit is either connected directly to the station's waste processor or dumped into space."
icon_state = "red"
/obj/machinery/disposal/wall/burn_pit
name = "disposal(hazardous)"
name = "disposal (hazardous)"
desc = "A pneumatic waste disposal unit. This unit is either connected directly to the station's waste processor or dumped into space."
icon_state = "redwall"
+46 -27
View File
@@ -14,6 +14,19 @@
flags = REMOTEVIEW_ON_ENTER
dir = 0
/obj/structure/disposalholder/Destroy()
QDEL_NULL(gas)
if(contents.len)
var/turf/qdelloc = get_turf(src)
if(qdelloc)
for(var/atom/movable/AM in contents)
AM.forceMove(qdelloc)
else
log_runtime("A disposal holder was deleted with contents in nullspace") //ideally, this should never happen
active = FALSE
return ..()
// initialize a holder from the contents of a disposal unit
/obj/structure/disposalholder/proc/init(list/flush_list, datum/gas_mixture/flush_gas)
gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please.
@@ -23,6 +36,7 @@
for(var/mob/living/M in flush_list)
if(M.stat != DEAD && !istype(M,/mob/living/silicon/robot/drone))
hasmob = TRUE
break
//Checks 1 contents level deep. This means that players can be sent through disposals...
//...but it should require a second person to open the package. (i.e. person inside a wrapped locker)
@@ -32,21 +46,23 @@
for(var/mob/living/M in O.contents)
if(M && M.stat != DEAD && !istype(M,/mob/living/silicon/robot/drone))
hasmob = TRUE
break
// now everything inside the disposal gets put into the holder
// note AM since can contain mobs or objs
for(var/atom/movable/AM in flush_list)
AM.forceMove(src)
// Mail will use it's sorting tag if dropped into disposals
if(istype(AM, /obj/structure/bigDelivery) && !hasmob)
var/obj/structure/bigDelivery/T = AM
src.destinationTag = T.sortTag
if(istype(AM, /obj/item/smallDelivery) && !hasmob)
var/obj/item/smallDelivery/T = AM
src.destinationTag = T.sortTag
if(istype(AM, /obj/item/mail) && !hasmob)
var/obj/item/mail/T = AM
src.destinationTag = T.sortTag
if(!hasmob)
if(istype(AM, /obj/structure/bigDelivery))
var/obj/structure/bigDelivery/T = AM
src.destinationTag = T.sortTag
if(istype(AM, /obj/item/smallDelivery))
var/obj/item/smallDelivery/T = AM
src.destinationTag = T.sortTag
if(istype(AM, /obj/item/mail))
var/obj/item/mail/T = AM
src.destinationTag = T.sortTag
// Drones can mail themselves through maint.
if(istype(AM, /mob/living/silicon/robot/drone))
var/mob/living/silicon/robot/drone/drone = AM
@@ -112,37 +128,40 @@
else
partialTag = new_tag
// Nolonger shall you be stuck forever in the tubes. ...It might just hurt a bit.
/obj/structure/disposalholder/container_resist(mob/living/escapee)
. = ..()
if(!istype(loc, /obj/structure/disposalpipe))
return //Somehow we're not in a pipe, shits probably fucked
var/obj/structure/disposalpipe/transport_cylinder = loc
if(active)
return
to_chat(escapee, span_warning("You push against the thin pipe walls..."))
playsound(loc, 'sound/machines/door/airlock_creaking.ogg', 30, FALSE, 3) //yeah I know but at least it sounds like metal being bent.
if(!do_after(escapee, 20 SECONDS, transport_cylinder))
return
for(var/mob/living/jailbird in contents)
jailbird.apply_damage(rand(5,15), BRUTE)
transport_cylinder.expel(src, get_turf(loc), NONE) //Direction: NONE, this makes the stuff spew everywhere (lol)
// called when player tries to move while in a pipe
/obj/structure/disposalholder/relaymove(mob/living/user)
if(!isliving(user))
return
if(user.stat || user.last_special <= world.time)
// If someone has sent you here to add a delay to this proc, inform them that it is a bug with /relaymove
// it's a minor bug, but it results in the proc being called much more frequently than it should, and when the bug is fixed the delay will work properly.
if(user.stat)
return
user.last_special = world.time+100
if(QDELETED(src))
return
for(var/mob/M in hearers(get_turf(src)))
to_chat(M, "<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>")
M.show_message("<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>", AUDIBLE_MESSAGE)
playsound(src, 'sound/effects/clang.ogg', 50, 0, 0)
// called to vent all gas in holder to a location
// called to vent all gas in holder to a location
/obj/structure/disposalholder/proc/vent_gas(atom/location)
location.assume_air(gas) // vent all gas to turf
return
/obj/structure/disposalholder/Destroy()
QDEL_NULL(gas)
if(contents.len)
var/turf/qdelloc = get_turf(src)
if(qdelloc)
for(var/atom/movable/AM in contents)
AM.forceMove(qdelloc)
else
log_and_message_admins("A disposal holder was deleted with contents in nullspace") //ideally, this should never happen
active = FALSE
return ..()
+204 -111
View File
@@ -27,6 +27,7 @@
var/flushing = FALSE // 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.
var/last_sound
active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W
idle_power_usage = 100
var/stat_tracking = TRUE
@@ -34,22 +35,37 @@
// create a new disposal
// find the attached trunk (if present) and init gas resvr.
/obj/machinery/disposal/Initialize(mapload)
/obj/machinery/disposal/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
var/obj/structure/disposalpipe/trunk/trunk = locate() in loc
if(!trunk)
if(make_from)
set_dir(make_from.dir)
/* //I dont wanna set this up yet.
make_from.moveToNullspace()
stored = make_from
*/
qdel(make_from)
mode = DISPOSALMODE_OFF
flush = FALSE
var/obj/structure/disposalpipe/trunk/trunk = locate() in loc
AddComponent(/datum/component/disposal_system_connection)
RegisterSignal(src, COMSIG_DISPOSAL_RECEIVE, PROC_REF(expel))
if(trunk)
SEND_SIGNAL(src, COMSIG_DISPOSAL_LINK, trunk)
air_contents = new(PRESSURE_TANK_VOLUME)
update()
AddComponent(/datum/component/disposal_system_connection)
update_icon()
/obj/machinery/disposal/Destroy()
eject()
return ..()
/obj/machinery/disposal/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FIVE)
deconstruct()
// attack by item places it in to disposal
/obj/machinery/disposal/attackby(obj/item/I, mob/user)
if(stat & BROKEN || !I || !user)
@@ -86,13 +102,7 @@
if(do_after(user, 2 SECONDS * W.toolspeed, target = src))
if(!src || !W.isOn()) return
to_chat(user, "You sliced the floorweld off the disposal unit.")
var/obj/structure/disposalconstruct/C = new (get_turf(src))
transfer_fingerprints_to(C)
C.ptype = 6 // 6 = disposal unit
C.anchored = TRUE
C.density = TRUE
C.update()
qdel(src)
deconstruct()
return
else
to_chat(user, "You need more welding fuel to complete this task.")
@@ -105,7 +115,7 @@
for(var/obj/item/O in T.contents)
T.remove_from_storage(O,src)
T.update_icon()
update()
update_icon()
return
if(istype(I, /obj/item/material/ashtray))
@@ -115,7 +125,7 @@
for(var/obj/item/O in A.contents)
O.forceMove(src)
A.update_icon()
update()
update_icon()
return
var/obj/item/grab/G = I
@@ -123,7 +133,7 @@
if(ismob(G.affecting))
var/mob/GM = G.affecting
for (var/mob/V in viewers(user))
V.show_message("[user] starts putting [GM.name] into the disposal.", 3)
V.visible_message("[user] starts putting [GM.name] into the disposal.", 3)
if(do_after(user, 2 SECONDS, target = src))
GM.forceMove(src)
for (var/mob/C in viewers(src))
@@ -143,14 +153,22 @@
if(istype(I, /obj/item/holder))
var/obj/item/holder/holder = I
var/mob/victim = holder.held_mob
if(ishuman(victim) || victim.client)
log_and_message_admins("placed [victim] inside \the [src]", user)
if(victim.client)
log_and_message_admins("placed [victim] inside \the [src]", user)
victim.forceMove(src)
qdel(I)
user.visible_message(
span_danger("[user] tosses \the [victim] into \the [src]."),
span_danger("You toss \the [victim] into \the [src]."),
span_warning("Pr-Thunk")
)
update_icon()
return 1
I.forceMove(src)
user.visible_message("[user] places \the [I] into the [src].", "You place \the [I] into the [src].","Ca-Clunk")
update()
update_icon()
// Transform into next machine type
/obj/machinery/disposal/proc/alter_bin_type(mob/user)
@@ -226,7 +244,7 @@
new_bin.stat = stat
new_bin.dir = new_dir
new_bin.mode = mode
new_bin.update() // sets up wall outlets
new_bin.update_icon() // sets up wall outlets
new_bin.update_icon()
new_bin.visible_message("\The [src] reconfigures into \a [new_bin]!")
// Effects
@@ -241,71 +259,66 @@
// mouse drop another mob or self
//
/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user)
/obj/machinery/disposal/MouseDrop_T(mob/living/target, mob/living/user)
if(istype(target))
stuff_mob_in(target, user)
/obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user)
//animals cannot put mobs other than themselves into disposal
if(isanimal(user) && target != user)
return
if(user.stat || !user.canmove || !istype(target))
return
if(target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1)
return
//animals cannot put mobs other than themselves into disposal
if(isanimal(user) && target != user)
return
add_fingerprint(user)
var/target_loc = target.loc
var/msg
for (var/mob/V in viewers(user))
if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
V.show_message("[user] starts climbing into the disposal.", 3)
if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
if(target.anchored) return
V.show_message("[user] starts stuffing [target.name] into the disposal.", 3)
if(!do_after(user, 2 SECONDS, target))
return
if(target_loc != target.loc)
return
if(target == user && !user.stat && !user.weakened && !user.stunned && !user.paralysis) // if drop self, then climbed in
// must be awake, not stunned or whatever
msg = "[user.name] climbs into the [src]."
to_chat(user, "You climb into the [src].")
log_and_message_admins("climbed into disposals!", user)
else if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
msg = "[user.name] stuffs [target.name] into the [src]!"
to_chat(user, "You stuff [target.name] into the [src]!")
add_attack_logs(user,target,"Disposals dunked")
if(user == target)
user.visible_message("[user] starts climbing into [src]")
else
return
target.visible_message(span_danger("[user] starts stuffing [target] into [src]."), span_userdanger("[user] starts stuffing you into [src]!"))
target.forceMove(src)
for (var/mob/C in viewers(src))
if(C == user)
continue
C.show_message(msg, 3)
update()
return
if(do_after(user, 2 SECONDS, target))
if(!loc)
return
target.forceMove(src)
if(user == target)
user.visible_message("[user] climbs into [src].", span_notice("You climb into [src]"))
log_and_message_admins("climbed into disposals!", user)
else
target.visible_message(span_danger("[user] stuffs [target] into \the [src]."), span_userdanger("[user] stuffs [target] into \the [src]."))
add_attack_logs(user,target,"Disposals dunked")
update_icon()
// attempt to move while inside
/obj/machinery/disposal/relaymove(mob/user)
attempt_escape(user)
// resist to escape the bin
/obj/machinery/disposal/container_resist(mob/living/user)
attempt_escape(user)
/obj/machinery/disposal/proc/attempt_escape(mob/user)
if(user.stat || flushing)
return
if(user.loc == src)
go_out(user)
return
go_out(user)
// leave the disposal
/obj/machinery/disposal/proc/go_out(mob/user)
user.forceMove(get_turf(src))
update()
return
update_icon()
// ai as human but can't flush
/obj/machinery/disposal/attack_ai(mob/user)
add_hiddenprint(user)
tgui_interact(user)
/*
/obj/machinery/disposal/attack_paw()
if(stat & BROKEN)
return
flush = !flush
update_icon()
*/
// human interact with machine
/obj/machinery/disposal/attack_hand(mob/user)
if(stat & BROKEN)
@@ -320,9 +333,19 @@
tgui_interact(user)
else
flush = !flush
update()
update_icon()
return
/obj/machinery/disposal/click_alt(mob/user)
/*
if(user.canUseTopic) //Later...
return
*/
if(get_dist(user, src) > 1 || user.stat) //Until the above exists...
return
flush = !flush
update_icon()
// user interaction
/obj/machinery/disposal/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
@@ -360,22 +383,22 @@
if(flushing)
return
if(isturf(loc))
if(action == "pumpOn")
switch(action)
if("pumpOn")
mode = DISPOSALMODE_CHARGING
update()
if(action == "pumpOff")
update_icon()
if("pumpOff")
mode = DISPOSALMODE_OFF
update()
update_icon()
if(action == "engageHandle")
if("engageHandle")
flush = TRUE
update()
if(action == "disengageHandle")
update_icon()
if("disengageHandle")
flush = FALSE
update()
update_icon()
if(action == "eject")
if("eject")
eject()
return TRUE
@@ -394,10 +417,10 @@
for(var/atom/movable/AM in src)
AM.forceMove(get_turf(src))
AM.pipe_eject(0)
update()
update_icon()
// update the icon & overlays to reflect mode & status
/obj/machinery/disposal/proc/update()
/obj/machinery/disposal/update_icon()
cut_overlays()
if(stat & BROKEN)
icon_state = "disposal-broken"
@@ -446,7 +469,7 @@
update_use_power(USE_POWER_IDLE)
else if(air_contents.return_pressure() >= SEND_PRESSURE)
mode = DISPOSALMODE_CHARGED //if full enough, switch to ready mode
update()
update_icon()
else
pressurize() //otherwise charge
@@ -468,61 +491,119 @@
// perform a flush
/obj/machinery/disposal/proc/flush()
if(flushing)
return
// We don't ever want digestion remains going through disposals, but people understandably thing they're doing right by trashing them
// So let's just delete them instead!
for(var/obj/item/digestion_remains/flushed_item in src)
qdel(flushed_item)
flushing = TRUE
flick("[icon_state]-flush", src)
// wait for animation to finish
addtimer(CALLBACK(src, PROC_REF(flush_animation)), 1 SECOND, TIMER_DELETE_ME)
flush_animation()
//Bit of a nasty way to do this. But sleep()s are nastier.
addtimer(CALLBACK(src, PROC_REF(flush_startup)), 1 SECOND)
/obj/machinery/disposal/proc/flush_animation()
PROTECTED_PROC(TRUE)
// wait for animation to finish
playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0)
addtimer(CALLBACK(src, PROC_REF(flush_resolve)), 0.5 SECOND, TIMER_DELETE_ME)
flick("[icon_state]-flush", src)
/obj/machinery/disposal/proc/flush_startup()
PROTECTED_PROC(TRUE)
if(last_sound < world.time + 1)
playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0)
last_sound = world.time
addtimer(CALLBACK(src, PROC_REF(flush_complete)), 0.5 SECONDS) // wait for animation to finish
/obj/machinery/disposal/proc/flush_complete()
PROTECTED_PROC(TRUE)
if(QDELETED(src))
return
// We don't ever want digestion remains going through disposals, but people understandably thing they're doing right by trashing them
// So let's just delete them instead!
for(var/obj/item/digestion_remains/bone in src)
qdel(bone)
var/list/flushed_items = list()
for(var/atom/movable/AM in src)
flushed_items += AM
/obj/machinery/disposal/proc/flush_resolve()
SEND_SIGNAL(src,COMSIG_DISPOSAL_FLUSH,air_contents)
air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. Disposal packet takes ownership of the original one!
if(stat_tracking)
GLOB.disposals_flush_shift_roundstat++
if(!SEND_SIGNAL(src, COMSIG_DISPOSAL_FLUSH, flushed_items, air_contents)) //If the signal isnt recieved, we'll just expel immediately.
if(length(contents))
expel(flushed_items)
air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. Disposal packet takes ownership of the original one!
flushing = FALSE
// now reset disposal state
flush = FALSE
if(mode == DISPOSALMODE_CHARGED) // if was ready,
mode = DISPOSALMODE_CHARGING // switch to charging
update()
return
update_icon()
// called when area power changes
/obj/machinery/disposal/power_change()
..() // do default setting/reset of stat NOPOWER bit
update() // update icon
update_icon() // update icon
return
/obj/machinery/disposal/hitby(atom/movable/source, datum/thrownthing/throwingdatum)
. = ..()
if(isitem(source) && !istype(source, /obj/item/projectile))
if(prob(75))
source.forceMove(src)
visible_message("\The [source] lands in \the [src].")
else
visible_message("\The [source] bounces off of \the [src]'s rim!")
// called when the bin expels items, generally from a disposal network, or trying to flush without a proper connection.
// should usually only occur if the pipe network if modified or delivering mail
/obj/machinery/disposal/proc/expel(list/expelled_items, datum/gas_mixture/gas)
var/turf/T = get_turf(src)
var/turf/target
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(isliving(source))
if(prob(75))
for(var/atom/movable/AM in expelled_items)
target = get_offset_target_turf(loc, rand(5)-rand(5), rand(5)-rand(5))
AM.forceMove(T)
AM.pipe_eject(0)
AM.throw_at(target, 5, 1)
T.assume_air(gas)
/obj/machinery/disposal/hitby(atom/movable/source, datum/thrownthing/throwingdatum)
if(!source.CanEnterDisposals())
return ..()
if(prob(75))
source.forceMove(src)
if(isliving(source))
var/mob/living/to_be_dunked = source
if(ishuman(source) ||to_be_dunked.client)
log_and_message_admins("[source] was thrown into \the [src]", null)
source.forceMove(src)
if(to_be_dunked.client)
log_and_message_admins("has thrown [source] into \the [src]", throwingdatum?.get_thrower())
//flush()
visible_message("\The [source] lands in \the [src].") // "and triggers the flush system!"
else
visible_message("\The [source] lands in \the [src].")
else
visible_message("\The [source] bounces off of \the [src]'s rim!")
return ..()
update_icon()
// Ideally, deconstruct would be a proc on /machinery, but you cant have nice things with polaris.
// AKA: FUKKIN CHANGE THIS WHEN THAT HAPPENS!!!!!1!! pls. -Reo
/obj/machinery/disposal/proc/deconstruct(disassembled = TRUE)
var/turf/T = loc
/* // More nice things... Someday we'll have flags_1 and then have proper support for anything being a hologram.
if(!(flags_1 & NODECONSTRUCT_1))
if(stored)
stored.forceMove(T)
src.transfer_fingerprints_to(stored)
stored.anchored = FALSE
stored.density = TRUE
stored.update_icon()
*/
//This is temporary until the above gets used. Or it's permanant if you're reading this 5 years from now.
var/obj/structure/disposalconstruct/C = new (src.loc/*, null, SOUTH, FALSE, src*/)
transfer_fingerprints_to(C)
C.ptype = 6 // 6 = disposal unit
C.update_icon()
C.anchored = TRUE
C.density = TRUE
//End of "temporary" code
for(var/atom/movable/AM in src)
AM.forceMove(T)
//..() //*cough
qdel(src) //Parent above should do this, but that's not a thing as of writing this.
/obj/machinery/disposal/proc/clean_items()
// Clean items before sending them
@@ -544,7 +625,7 @@
density = FALSE
/obj/machinery/disposal/wall/update()
/obj/machinery/disposal/wall/update_icon()
. = ..()
switch(dir)
if(NORTH)
@@ -567,3 +648,15 @@
#undef SEND_PRESSURE
#undef PRESSURE_TANK_VOLUME
#undef PUMP_MAX_FLOW_RATE
/atom/movable/proc/CanEnterDisposals()
return TRUE
/obj/item/projectile/CanEnterDisposals()
return FALSE
/obj/effect/CanEnterDisposals()
return FALSE
/obj/mecha/CanEnterDisposals()
return FALSE
+62 -8
View File
@@ -1,3 +1,6 @@
#define OUTLET_SCREWED 0
#define OUTLET_UNSCREWED 1
// the disposal outlet machine
/obj/structure/disposaloutlet
name = "disposal outlet"
@@ -6,38 +9,58 @@
icon_state = "outlet"
density = TRUE
anchored = TRUE
var/active = 0
var/active = FALSE // Code appendix.
var/turf/target // this will be where the output objects are 'thrown' to.
var/mode = 0
var/start_eject = 0
var/eject_range = 3 //Did you know, in TGcode, it's a default of 2 tiles?
/obj/structure/disposaloutlet/Initialize(mapload)
. = ..()
AddComponent(/datum/component/disposal_system_connection/disposaloutlet) // Special subtype for directional tossing and it's animation/buzzer
target = get_ranged_target_turf(src, dir, 10)
var/obj/structure/disposalpipe/trunk/trunk = locate() in get_turf(src)
AddComponent(/datum/component/disposal_system_connection)
RegisterSignal(src, COMSIG_DISPOSAL_RECEIVE, PROC_REF(expel))
if(trunk)
SEND_SIGNAL(src, COMSIG_DISPOSAL_LINK, trunk)
/obj/structure/disposaloutlet/Destroy()
target = null
. = ..()
/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user)
if(!I || !user)
return
src.add_fingerprint(user)
if(I.has_tool_quality(TOOL_SCREWDRIVER))
if(mode==0)
mode=1
if(mode == OUTLET_SCREWED)
mode = OUTLET_UNSCREWED
to_chat(user, "You remove the screws around the power connection.")
playsound(src, I.usesound, 50, 1)
return
else if(mode==1)
mode=0
else if(mode == OUTLET_UNSCREWED)
mode = OUTLET_SCREWED
to_chat(user, "You attach the screws around the power connection.")
playsound(src, I.usesound, 50, 1)
return
else if(I.has_tool_quality(TOOL_WELDER) && mode==1)
if(mode == OUTLET_SCREWED)
return ..()
if(I.has_tool_quality(TOOL_WELDER) && mode==1)
var/obj/item/weldingtool/W = I.get_welder()
if(!W.isOn())
to_chat(user, span_warning("Your [W] needs to be on to complete this task."))
if(W.remove_fuel(0,user))
playsound(src, W.usesound, 100, 1)
to_chat(user, "You start slicing the floorweld off the disposal outlet.")
if(do_after(user, 2 SECONDS * W.toolspeed, target = src))
if(!src || !W.isOn()) return
to_chat(user, "You sliced the floorweld off the disposal outlet.")
var/obj/structure/disposalconstruct/C = new (src.loc)
SEND_SIGNAL(src, COMSIG_DISPOSAL_UNLINK)
var/obj/structure/disposalconstruct/C = new (src.loc/*, null, SOUTH, FALSE, src*/)
src.transfer_fingerprints_to(C)
C.set_dir(dir)
C.ptype = 7 // 7 = outlet
C.update()
C.anchored = TRUE
@@ -47,3 +70,34 @@
else
to_chat(user, "You need more welding fuel to complete this task.")
return
else if(I.has_tool_quality(TOOL_MULTITOOL))
var/new_range = tgui_input_number(user, "Input a new ejection distance", "Set ejection strength", 3 , 5, 1, round_value = TRUE)
eject_range = new_range
to_chat(user, span_notice("You set the range on the [src] to [new_range] tiles."))
/obj/structure/disposaloutlet/proc/expel(datum/source, list/received_items, datum/gas_mixture/gas)
SIGNAL_HANDLER
flick("outlet-open", src)
if((start_eject + 30) < world.time)
start_eject = world.time
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0)
addtimer(CALLBACK(src, PROC_REF(expel_contents), received_items, gas, TRUE), 2 SECONDS)
else
addtimer(CALLBACK(src, PROC_REF(expel_contents), received_items, gas), 2 SECONDS)
/obj/structure/disposaloutlet/proc/expel_contents(list/ejected_items, datum/gas_mixture/gas, playsound = FALSE)
if(playsound)
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
var/turf/T = get_turf(src)
for(var/atom/movable/AM in ejected_items)
AM.forceMove(T)
AM.pipe_eject(dir)
AM.throw_at(target, eject_range, 1)
T.assume_air(gas)
#undef OUTLET_SCREWED
#undef OUTLET_UNSCREWED
+20 -28
View File
@@ -1,6 +1,7 @@
//a trunk joining to a disposal bin or outlet on the same turf
/obj/structure/disposalpipe/trunk
icon_state = "pipe-t"
var/atom/linked // The linked atom. It should have a /datum/component/disposal_connection to handle receiving disposal packets.
/obj/structure/disposalpipe/trunk/Initialize(mapload)
..()
@@ -10,30 +11,25 @@
/obj/structure/disposalpipe/trunk/LateInitialize()
update()
/obj/structure/disposalpipe/trunk/Destroy()
if(linked) //Linked to something, better unlink.
SEND_SIGNAL(linked, COMSIG_DISPOSAL_UNLINK)
linked = null
. = ..()
// Override attackby so we disallow trunkremoval when somethings ontop
/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user)
//Linked atom.
if(linked)
return
//Disposal constructors
var/turf/T = get_turf(src)
var/obj/structure/disposalconstruct/C = locate() in T
if(C && C.anchored)
return
for(var/obj/structure/disposalconstruct/C in T)
if(C.ptype == DISPOSAL_PIPE_BIN || C.ptype == DISPOSAL_PIPE_OUTLET || C.ptype == DISPOSAL_PIPE_CHUTE)
if(C.anchored)
return
if(!T.is_plating())
return // prevent interaction with T-scanner revealed pipes
add_fingerprint(user)
if(I.has_tool_quality(TOOL_WELDER))
var/obj/item/weldingtool/W = I.get_welder()
if(W.remove_fuel(0,user))
playsound(src, W.usesound, 100, 1)
to_chat(user, "Slicing the disposal pipe.")
if(do_after(user, 3 SECONDS * W.toolspeed, target = src))
if(!src || !W.isOn()) return
welded()
else
to_chat(user, "You must stay still while welding the pipe.")
else
to_chat(user, "You need more welding fuel to cut the pipe.")
return ..() //Run the check from parent, instead of copypasta code
// would transfer to next pipe segment, but we are in a trunk
// if not entering from disposal bin,
@@ -42,15 +38,11 @@
if(H.dir == DOWN) // we just entered from a disposer
return ..() // so do base transfer proc
// Find a disposal handler component. First come first serve.
var/transfered = FALSE
var/turf/T = get_turf(src)
for(var/obj/O in T)
if(SEND_SIGNAL(O,COMSIG_DISPOSAL_RECEIVING,H))
transfered = TRUE
break
if(!transfered) // Check if anything handled it, will be deleted if so.
expel(H, T, 0) // expel at turf if nothing handled it
if(linked)
if(SEND_SIGNAL(src, COMSIG_DISPOSAL_SEND, H))
return //Sent, and handled. Our job is done.
expel(H, get_turf(src), 0) // expel at turf if nothing handled it
return null
+4 -1
View File
@@ -9,7 +9,10 @@
/obj/machinery/disposal/deliveryChute/interact()
return
/obj/machinery/disposal/deliveryChute/update()
/obj/machinery/disposal/deliveryChute/update_icon()
return
/obj/machinery/disposal/deliveryChute/click_alt(mob/user) //No flushing the chute
return
/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute