diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm
index 16d622926f..78c0a86c81 100644
--- a/code/__defines/dcs/signals.dm
+++ b/code/__defines/dcs/signals.dm
@@ -265,6 +265,10 @@
///from /datum/controller/subsystem/motion_tracker/notice() (/datum/weakref/source_atom,/turf/echo_turf_location)
#define COMSIG_MOVABLE_MOTIONTRACKER "move_motiontracker"
+///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 the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
#define COMSIG_MOVABLE_DISPOSING "movable_disposing"
diff --git a/code/datums/components/machinery/disposal_connection.dm b/code/datums/components/machinery/disposal_connection.dm
new file mode 100644
index 0000000000..35b5e2f3c5
--- /dev/null
+++ b/code/datums/components/machinery/disposal_connection.dm
@@ -0,0 +1,157 @@
+///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
+
+/datum/component/disposal_system_connection/Initialize()
+ 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_PARENT_EXAMINE, PROC_REF(on_examine))
+
+/datum/component/disposal_system_connection/Destroy()
+ UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_FLUSH)
+ UnregisterSignal(disposal_owner, COMSIG_DISPOSAL_RECEIVING)
+ UnregisterSignal(disposal_owner, COMSIG_PARENT_EXAMINE)
+ disposal_owner = null
+ . = ..()
+
+// Signal handling
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/datum/component/disposal_system_connection/proc/on_flush(datum/source,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)
+
+/datum/component/disposal_system_connection/proc/on_recieve(datum/source,obj/structure/disposalholder/packet)
+ SIGNAL_HANDLER
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(!can_accept())
+ return FALSE
+ 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.")
+
+// 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)
+ 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
+
+/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)
+ handle_expel(packet)
+ return
+
+ // start the holder processing movement
+ packet.forceMove(trunk)
+ packet.active = TRUE
+ packet.set_dir(DOWN)
+ packet.move()
+
+// 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)
+ // 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
+
+ // We need to store the data in the packet to handle it with delays, then delete the packet so nothing else can handle it
+ packet.active = FALSE // So it stops trying to move
+ var/list/expelled_items = list()
+ for(var/atom/movable/AM in packet)
+ expelled_items += AM
+ AM.forceMove(disposal_owner)
+ // Handle client eye we get a black screen between trunk and eject if we have delay on ejection!
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.client)
+ M.client.eye = disposal_owner
+ 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)
+ 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)
diff --git a/code/modules/recycling/destination_tagger.dm b/code/modules/recycling/destination_tagger.dm
new file mode 100644
index 0000000000..f0738ae174
--- /dev/null
+++ b/code/modules/recycling/destination_tagger.dm
@@ -0,0 +1,56 @@
+/obj/item/destTagger
+ name = "destination tagger"
+ desc = "Used to set the destination of properly wrapped packages."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "dest_tagger"
+ var/currTag = 0
+
+ w_class = ITEMSIZE_SMALL
+ item_state = "electronic"
+ slot_flags = SLOT_BELT
+ pickup_sound = 'sound/items/pickup/device.ogg'
+ drop_sound = 'sound/items/drop/device.ogg'
+
+/obj/item/destTagger/tgui_state(mob/user)
+ return GLOB.tgui_inventory_state
+
+/obj/item/destTagger/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "DestinationTagger", name)
+ ui.open()
+
+/obj/item/destTagger/tgui_static_data(mob/user)
+ var/list/data = ..()
+ var/list/taggers = list()
+ var/list/tagger_levels = list()
+ for(var/tag in GLOB.tagger_locations)
+ var/z_level = GLOB.tagger_locations[tag]
+ taggers += list(list("tag" = tag, "level" = z_level))
+ tagger_levels += list(list("z" = z_level, "location" = using_map.get_zlevel_name(z_level)))
+ data["taggerLevels"] = tagger_levels
+ data["taggerLocs"] = taggers
+
+ return data
+
+/obj/item/destTagger/tgui_data(mob/user, datum/tgui/ui)
+ var/list/data = ..()
+
+ data["currTag"] = currTag
+
+ return data
+
+/obj/item/destTagger/attack_self(mob/user as mob)
+ tgui_interact(user)
+
+/obj/item/destTagger/tgui_act(action, params, datum/tgui/ui)
+ if(..())
+ return TRUE
+ add_fingerprint(ui.user)
+ switch(action)
+ if("set_tag")
+ var/new_tag = params["tag"]
+ if(!(new_tag in GLOB.tagger_locations))
+ return FALSE
+ currTag = new_tag
+ . = TRUE
diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm
index 788d7d229d..6935927376 100644
--- a/code/modules/recycling/disposal-construction.dm
+++ b/code/modules/recycling/disposal-construction.dm
@@ -17,7 +17,7 @@
var/dpdir = 0 // directions as disposalpipe
var/base_state = "pipe-s"
-/obj/structure/disposalconstruct/Initialize(mapload, var/newtype, var/newdir, var/flipped, var/newsubtype)
+/obj/structure/disposalconstruct/Initialize(mapload, newtype, newdir, flipped, newsubtype)
. = ..()
ptype = newtype
dir = newdir
@@ -114,7 +114,7 @@
// hide called by levelupdate if turf intact status changes
// change visibility status and force update of icon
-/obj/structure/disposalconstruct/hide(var/intact)
+/obj/structure/disposalconstruct/hide(intact)
invisibility = (intact && level==1) ? INVISIBILITY_ABSTRACT: INVISIBILITY_NONE // hide if floor is intact
update()
@@ -224,7 +224,7 @@
// attackby item
// wrench: (un)anchor
// weldingtool: convert to real pipe
-/obj/structure/disposalconstruct/attackby(var/obj/item/I, var/mob/user)
+/obj/structure/disposalconstruct/attackby(obj/item/I, mob/user)
var/nicetype = "pipe"
var/ispipe = 0 // Indicates if we should change the level of this pipe
src.add_fingerprint(user)
@@ -338,9 +338,6 @@
var/obj/structure/disposaloutlet/P = new /obj/structure/disposaloutlet(src.loc)
src.transfer_fingerprints_to(P)
P.set_dir(dir)
- P.target = get_ranged_target_turf(src, dir, 10) //TODO: replace this with a proc parameter or other cleaner
- var/obj/structure/disposalpipe/trunk/Trunk = CP
- Trunk.linked = P
else if(ptype==DISPOSAL_PIPE_CHUTE)
var/obj/machinery/disposal/deliveryChute/P = new /obj/machinery/disposal/deliveryChute(src.loc)
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index b1aa9c6ee3..84e1122984 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -1,687 +1,4 @@
-// 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) //kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added.
-#define PRESSURE_TANK_VOLUME 150 //L
-#define PUMP_MAX_FLOW_RATE 11.25 //L/s - 4 m/s using a 15 cm by 15 cm inlet //NOTE: I reduced the send pressure from 801 to 101.05 which is about 1/8 there was originally, and this was 90 before that. 90/8 is about 11.25, so that's the new value. -Reo
-
-/obj/machinery/disposal
- name = "disposal unit"
- desc = "A pneumatic waste disposal unit."
- icon = 'icons/obj/pipes/disposal.dmi'
- icon_state = "disposal"
- anchored = TRUE
- density = TRUE
- 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.
- var/last_sound = 0
- active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W
- idle_power_usage = 100
-
-// create a new disposal
-// find the attached trunk (if present) and init gas resvr.
-/obj/machinery/disposal/Initialize(mapload)
- . = ..()
-
- trunk = locate() in loc
- if(!trunk)
- mode = 0
- flush = 0
- else
- trunk.linked = src // link the pipe trunk to self
-
- air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME)
- update()
-
-/obj/machinery/disposal/Destroy()
- eject()
- if(trunk)
- trunk.linked = null
- trunk = null
- return ..()
-
-// attack by item places it in to disposal
-/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user)
- if(stat & BROKEN || !I || !user)
- return
-
- src.add_fingerprint(user)
- if(mode<=0) // It's off
- if(I.has_tool_quality(TOOL_SCREWDRIVER))
- if(contents.len > 0)
- to_chat(user, "Eject the items first!")
- return
- if(mode==0) // It's off but still not unscrewed
- mode=-1 // Set it to doubleoff l0l
- playsound(src, I.usesound, 50, 1)
- to_chat(user, "You remove the screws around the power connection.")
- return
- else if(mode==-1)
- mode=0
- playsound(src, I.usesound, 50, 1)
- to_chat(user, "You attach the screws around the power connection.")
- return
- else if(I.has_tool_quality(TOOL_WELDER) && mode==-1)
- if(contents.len > 0)
- to_chat(user, "Eject the items first!")
- return
- var/obj/item/weldingtool/W = I.get_welder()
- if(W.remove_fuel(0,user))
- playsound(src, W.usesound, 100, 1)
- to_chat(user, "You start slicing the floorweld off the disposal unit.")
-
- 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 (src.loc)
- src.transfer_fingerprints_to(C)
- C.ptype = 6 // 6 = disposal unit
- C.anchored = TRUE
- C.density = TRUE
- C.update()
- qdel(src)
- return
- else
- to_chat(user, "You need more welding fuel to complete this task.")
- return
-
- if(istype(I, /obj/item/melee/energy/blade))
- to_chat(user, "You can't place that item inside the disposal unit.")
- return
-
- if(istype(I, /obj/item/storage/bag/trash))
- var/obj/item/storage/bag/trash/T = I
- to_chat(user, span_blue("You empty the bag."))
- for(var/obj/item/O in T.contents)
- T.remove_from_storage(O,src)
- T.update_icon()
- update()
- return
-
- if(istype(I, /obj/item/material/ashtray))
- var/obj/item/material/ashtray/A = I
- if(A.contents.len > 0)
- user.visible_message(span_infoplain(span_bold("\The [user]") + " empties \the [A] into [src]."))
- for(var/obj/item/O in A.contents)
- O.forceMove(src)
- A.update_icon()
- update()
- return
-
- 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(user))
- V.show_message("[user] starts putting [GM.name] into the disposal.", 3)
- if(do_after(user, 2 SECONDS, target = src))
- if (GM.client)
- GM.client.perspective = EYE_PERSPECTIVE
- GM.client.eye = src
- GM.forceMove(src)
- for (var/mob/C in viewers(src))
- C.show_message(span_red("[GM.name] has been placed in the [src] by [user]."), 3)
- qdel(G)
-
- add_attack_logs(user,GM,"Disposals dunked")
- return
-
- if(isrobot(user))
- return
- if(!I || I.anchored || !I.canremove)
- return
-
- user.drop_item()
- if(I)
- 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)
- victim.forceMove(src)
-
- I.forceMove(src)
-
- user.visible_message("[user] places \the [I] into the [src].", "You place \the [I] into the [src].","Ca-Clunk")
- update()
-
-// mouse drop another mob or self
-//
-/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user)
- 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
-
- src.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")
- else
- return
- if (target.client)
- target.client.perspective = EYE_PERSPECTIVE
- target.client.eye = src
-
- target.forceMove(src)
-
- for (var/mob/C in viewers(src))
- if(C == user)
- continue
- C.show_message(msg, 3)
-
- update()
- return
-
-// attempt to move while inside
-/obj/machinery/disposal/relaymove(mob/user as mob)
- if(user.stat || src.flushing)
- return
- if(user.loc == src)
- src.go_out(user)
- return
-
-// leave the disposal
-/obj/machinery/disposal/proc/go_out(mob/user)
- if (user.client)
- user.client.eye = user.client.mob
- user.client.perspective = MOB_PERSPECTIVE
- user.forceMove(src.loc)
- update()
- return
-
-// ai as human but can't flush
-/obj/machinery/disposal/attack_ai(mob/user as mob)
- add_hiddenprint(user)
- tgui_interact(user)
-
-// human interact with machine
-/obj/machinery/disposal/attack_hand(mob/user as mob)
- if(stat & BROKEN)
- return
-
- if(user && user.loc == src)
- to_chat(user, span_red("You cannot reach the controls from inside."))
- return
-
- // Clumsy folks can only flush it.
- if(user.IsAdvancedToolUser(1))
- tgui_interact(user)
- else
- flush = !flush
- update()
- return
-
-// user interaction
-/obj/machinery/disposal/tgui_interact(mob/user, datum/tgui/ui = null)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "DisposalBin")
- ui.open()
-
-/obj/machinery/disposal/tgui_data(mob/user)
- var/list/data = list()
-
- data["isAI"] = isAI(user)
- data["flushing"] = flush
- data["mode"] = mode
- data["pressure"] = round(clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100),1)
-
- return data
-
-/obj/machinery/disposal/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
- if(..())
- return
-
- if(ui.user.loc == src)
- to_chat(ui.user, span_warning("You cannot reach the controls from inside."))
- return TRUE
-
- if(mode==-1 && action != "eject") // If the mode is -1, only allow ejection
- to_chat(ui.user, span_warning("The disposal units power is disabled."))
- return
-
- if(stat & BROKEN)
- return
-
- add_fingerprint(ui.user)
-
- if(flushing)
- return
-
- if(isturf(loc))
- if(action == "pumpOn")
- mode = 1
- update()
- if(action == "pumpOff")
- mode = 0
- update()
-
- if(action == "engageHandle")
- flush = 1
- update()
- if(action == "disengageHandle")
- flush = 0
- update()
-
- if(action == "eject")
- eject()
-
- return TRUE
-
-// eject the contents of the disposal unit
-
-/obj/machinery/disposal/verb/force_eject()
- set src in oview(1)
- set category = "Object"
- set name = "Force Eject"
- if(flushing)
- return
- eject()
-
-/obj/machinery/disposal/proc/eject()
- for(var/atom/movable/AM in src)
- AM.forceMove(src.loc)
- AM.pipe_eject(0)
- update()
-
-// update the icon & overlays to reflect mode & status
-/obj/machinery/disposal/proc/update()
- cut_overlays()
- if(stat & BROKEN)
- icon_state = "disposal-broken"
- mode = 0
- flush = 0
- return
-
- // flush handle
- if(flush)
- add_overlay("[initial(icon_state)]-handle")
-
- // only handle is shown if no power
- if(stat & NOPOWER || mode == -1)
- return
-
- // check for items in disposal - occupied light
- if(contents.len > 0)
- add_overlay("[initial(icon_state)]-full")
-
- // charging and ready light
- if(mode == 1)
- add_overlay("[initial(icon_state)]-charge")
- else if(mode == 2)
- add_overlay("[initial(icon_state)]-ready")
-
-// timed process
-// charge the gas reservoir and perform flush if ready
-/obj/machinery/disposal/process()
- if(!air_contents || (stat & BROKEN)) // nothing can happen if broken
- update_use_power(USE_POWER_OFF)
- return
-
- flush_count++
- if( flush_count >= flush_every_ticks )
- if( contents.len )
- if(mode == 2)
- spawn(0)
- feedback_inc("disposal_auto_flush",1)
- flush()
- flush_count = 0
-
- src.updateDialog()
-
- if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power
- flush()
-
- if(mode != 1) //if off or ready, no need to charge
- update_use_power(USE_POWER_IDLE)
- else if(air_contents.return_pressure() >= SEND_PRESSURE)
- mode = 2 //if full enough, switch to ready mode
- update()
- else
- src.pressurize() //otherwise charge
-
-/obj/machinery/disposal/proc/pressurize()
- if(stat & NOPOWER) // won't charge if no power
- update_use_power(USE_POWER_OFF)
- return
-
- var/atom/L = loc // recharging from loc turf
- var/datum/gas_mixture/env = L.return_air()
-
- var/power_draw = -1
- if(env && env.temperature > 0)
- var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here
- power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage)
-
- if (power_draw > 0)
- use_power(power_draw)
-
-// perform a flush
-/obj/machinery/disposal/proc/flush()
-
- flushing = 1
- flick("[icon_state]-flush", src)
-
- var/wrapcheck = 0
- var/obj/structure/disposalholder/H = new() // virtual holder object which actually
- // travels through the pipes.
- //Hacky test to get drones to mail themselves through disposals.
- for(var/mob/living/silicon/robot/drone/D in src)
- wrapcheck = 1
-
- for(var/obj/item/smallDelivery/O in src)
- wrapcheck = 1
-
- if(wrapcheck == 1)
- H.tomail = 1
-
-
- sleep(10)
- if(last_sound < world.time + 1)
- playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0)
- last_sound = world.time
- sleep(5) // wait for animation to finish
- GLOB.disposals_flush_shift_roundstat++
-
-
- H.init(src, air_contents) // copy the contents of disposer to holder
- air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv.
-
- H.start(src) // start the holder processing movement
- flushing = 0
- // now reset disposal state
- flush = 0
- if(mode == 2) // if was ready,
- mode = 1 // switch to charging
- update()
- return
-
-
-// called when area power changes
-/obj/machinery/disposal/power_change()
- ..() // do default setting/reset of stat NOPOWER bit
- update() // update icon
- return
-
-
-// called when holder is expelled from a disposal
-// should usually only occur if the pipe network is modified
-/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H)
-
- var/turf/target
- playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
- if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it
- for(var/atom/movable/AM in H)
- target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5))
-
- AM.forceMove(src.loc)
- AM.pipe_eject(0)
- if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z
- spawn(1)
- if(AM)
- AM.throw_at(target, 5, 1)
-
- H.vent_gas(loc)
- qdel(H)
-
-/obj/machinery/disposal/hitby(atom/movable/AM)
- . = ..()
- if(istype(AM, /obj/item) && !istype(AM, /obj/item/projectile))
- if(prob(75))
- AM.forceMove(src)
- visible_message("\The [AM] lands in \the [src].")
- else
- visible_message("\The [AM] bounces off of \the [src]'s rim!")
-
- if(istype(AM,/mob/living))
- if(prob(75))
- var/mob/living/to_be_dunked = AM
- if(ishuman(AM) ||to_be_dunked.client)
- log_and_message_admins("[AM] was thrown into \the [src]", null)
- AM.forceMove(src)
- visible_message("\The [AM] lands in \the [src].")
-
-/obj/machinery/disposal/wall
- name = "inset disposal unit"
- icon_state = "wall"
-
- density = FALSE
-
-/obj/machinery/disposal/wall/Initialize(mapload)
- . = ..()
-
- spawn(1 SECOND) // Fixfix for weird interaction with buildmode or other late-spawning.
- update()
-
-/obj/machinery/disposal/wall/update()
- ..()
-
- switch(dir)
- if(1)
- pixel_x = 0
- pixel_y = -32
- if(2)
- pixel_x = 0
- pixel_y = 32
- if(4)
- pixel_x = -32
- pixel_y = 0
- if(8)
- pixel_x = 32
- pixel_y = 0
-
-// 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_ABSTRACT
- var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point
- var/active = 0 // true if the holder is moving, otherwise inactive
- dir = 0
- var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops)
- var/destinationTag = "" // changes if contains a delivery container
- var/tomail = 0 //changes if contains wrapped package
- var/hasmob = 0 //If it contains a mob
-
- var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again.
-
-
- // initialize a holder from the contents of a disposal unit
-/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/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.
-
- //Check for any living mobs trigger hasmob.
- //hasmob effects whether the package goes to cargo or its tagged destination.
- for(var/mob/living/M in D)
- if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone))
- hasmob = 1
-
- //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)
- for(var/obj/O in D)
- if(O.contents)
- for(var/mob/living/M in O.contents)
- if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone))
- hasmob = 1
-
- // now everything inside the disposal gets put into the holder
- // note AM since can contain mobs or objs
- for(var/atom/movable/AM in D)
- AM.forceMove(src)
- 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
- //Drones can mail themselves through maint.
- if(istype(AM, /mob/living/silicon/robot/drone))
- var/mob/living/silicon/robot/drone/drone = AM
- src.destinationTag = drone.mail_destination
-
- if(istype(AM, /obj/item/mail) && !hasmob)
- var/obj/item/mail/T = AM
- src.destinationTag = T.sortTag
-
-
-// start the movement process
-// argument is the disposal unit the holder started in
-/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D)
- if(!D.trunk)
- D.expel(src) // no trunk connected, so expel immediately
- return
-
- forceMove(D.trunk)
- active = 1
- set_dir(DOWN)
- spawn(1)
- move() // spawn off the movement process
-
- return
-
-// movement process, persists while holder is moving through pipes
-/obj/structure/disposalholder/proc/move()
- var/obj/structure/disposalpipe/last
- while(active)
- sleep(1) // was 1
- if(!loc) return // check if we got GC'd
-
- if(hasmob && prob(3))
- for(var/mob/living/H in src)
- if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system,
- H.take_overall_damage(20, 0, "Blunt Trauma")//horribly maim any living creature jumping down disposals. c'est la vie
-
- var/obj/structure/disposalpipe/curr = loc
- last = curr
- curr = curr.transfer(src)
-
- if(!loc) return //side effects
-
- if(!curr)
- last.expel(src, loc, dir)
-
- //
- if(!(count--))
- active = 0
- return
-
-
-
-// find the turf which should contain the next pipe
-/obj/structure/disposalholder/proc/nextloc()
- return get_step(loc,dir)
-
-// find a matching pipe on a turf
-/obj/structure/disposalholder/proc/findpipe(var/turf/T)
-
- if(!T)
- return null
-
- var/fdir = turn(dir, 180) // flip the movement direction
- for(var/obj/structure/disposalpipe/P in T)
- if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir
- return P
- // if no matching pipe, return null
- return null
-
-// merge two holder objects
-// used when a a holder meets a stuck holder
-/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other)
- for(var/atom/movable/AM in other)
- AM.forceMove(src) // move everything in other holder to this one
- if(ismob(AM))
- var/mob/M = AM
- if(M.client) // if a client mob, update eye to follow this holder
- M.client.eye = src
-
- qdel(other)
-
-
-/obj/structure/disposalholder/proc/settag(var/new_tag)
- destinationTag = new_tag
-
-/obj/structure/disposalholder/proc/setpartialtag(var/new_tag)
- if(partialTag == new_tag)
- destinationTag = new_tag
- partialTag = ""
- else
- partialTag = new_tag
-
-
-// called when player tries to move while in a pipe
-/obj/structure/disposalholder/relaymove(mob/user as mob)
-
- if(!isliving(user))
- return
-
- var/mob/living/U = user
-
- if (U.stat || U.last_special <= world.time)
- return
-
- U.last_special = world.time+100
-
- if (src.loc)
- for (var/mob/M in hearers(src.loc.loc))
- to_chat(M, "CLONG, clong!")
-
- playsound(src, 'sound/effects/clang.ogg', 50, 0, 0)
-
- // called to vent all gas in holder to a location
-/obj/structure/disposalholder/proc/vent_gas(var/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.loc = qdelloc
- else
- log_and_message_admins("A disposal holder was deleted with contents in nullspace") //ideally, this should never happen
-
- active = 0
- return ..()
-
// Disposal pipes
-
/obj/structure/disposalpipe
icon = 'icons/obj/pipes/disposal.dmi'
name = "disposal pipe"
@@ -711,8 +28,8 @@
var/obj/structure/disposalholder/H = locate() in src
if(H)
// holder was present
- H.active = 0
- var/turf/T = src.loc
+ H.active = FALSE
+ var/turf/T = get_turf(src)
if(T.density)
// deleting pipe is inside a dense turf (wall)
// this is unlikely, but just dump out everything into the turf in case
@@ -736,7 +53,6 @@
// transfer the holder through this pipe segment
// overriden for special behaviour
-//
/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir)
H.set_dir(nextdir)
@@ -759,7 +75,7 @@
// update the icon_state to reflect hidden status
/obj/structure/disposalpipe/proc/update()
- var/turf/T = src.loc
+ var/turf/T = get_turf(src)
hide(!T.is_plating() && !istype(T,/turf/space)) // space never hides pipes
// hide called by levelupdate if turf intact status changes
@@ -783,7 +99,7 @@
// expel the held objects into a turf
// called when there is a break in the pipe
-/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction)
+/obj/structure/disposalpipe/proc/expel(obj/structure/disposalholder/H, turf/T, direction)
if(!istype(H))
return
@@ -796,11 +112,9 @@
qdel(H)
return
-
if(!T.is_plating() && istype(T,/turf/simulated/floor)) //intact floor, pop the tile
var/turf/simulated/floor/F = T
- F.break_tile()
- new /obj/item/stack/tile(H) // add to holder so it will be thrown with other stuff
+ F.make_plating(TRUE)
var/turf/target
if(direction) // direction is specified
@@ -812,11 +126,12 @@
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(H)
for(var/atom/movable/AM in H)
+ if(QDELETED(AM))
+ continue
AM.forceMove(T)
AM.pipe_eject(direction)
- spawn(1)
- if(AM)
- AM.throw_at(target, 100, 1)
+ AM.throw_at(target, 100, 1)
+
H.vent_gas(T)
qdel(H)
@@ -825,13 +140,12 @@
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(H)
for(var/atom/movable/AM in H)
+ if(QDELETED(AM))
+ continue
target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5))
-
AM.forceMove(T)
AM.pipe_eject(0)
- spawn(1)
- if(AM)
- AM.throw_at(target, 5, 1)
+ AM.throw_at(target, 5, 1)
H.vent_gas(T) // all gas vent to turf
qdel(H)
@@ -842,19 +156,19 @@
// will expel any holder inside at the time
// then delete the pipe
// remains : set to leave broken pipe pieces in place
-/obj/structure/disposalpipe/proc/broken(var/remains = 0)
+/obj/structure/disposalpipe/proc/broken(remains = 0)
if(remains)
for(var/D in GLOB.cardinal)
if(D & dpdir)
- var/obj/structure/disposalpipe/broken/P = new(src.loc)
+ var/obj/structure/disposalpipe/broken/P = new(get_turf(src))
P.set_dir(D)
- src.invisibility = INVISIBILITY_ABSTRACT // make invisible (since we won't delete the pipe immediately)
+ invisibility = INVISIBILITY_ABSTRACT // make invisible (since we won't delete the pipe immediately)
var/obj/structure/disposalholder/H = locate() in src
if(H)
// holder was present
- H.active = 0
- var/turf/T = src.loc
+ H.active = FALSE
+ var/turf/T = get_turf(src)
if(T.density)
// broken pipe is inside a dense turf (wall)
// this is unlikely, but just dump out everything into the turf in case
@@ -890,7 +204,7 @@
return
- // test health for brokenness
+// test health for brokenness
/obj/structure/disposalpipe/proc/healthcheck()
if(health < -2)
broken(0)
@@ -898,15 +212,14 @@
broken(1)
return
- //attack by item
- //weldingtool: unfasten and convert to obj/disposalconstruct
+//attack by item
+//weldingtool: unfasten and convert to obj/disposalconstruct
+/obj/structure/disposalpipe/attackby(obj/item/I, mob/user)
-/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user)
-
- var/turf/T = src.loc
+ var/turf/T = get_turf(src)
if(!T.is_plating())
return // prevent interaction with T-scanner revealed pipes
- src.add_fingerprint(user)
+ add_fingerprint(user)
if(I.has_tool_quality(TOOL_WELDER))
var/obj/item/weldingtool/W = I.get_welder()
if(W.remove_fuel(0,user))
@@ -922,10 +235,10 @@
to_chat(user, "You need more welding fuel to cut the pipe.")
return
- // called when pipe is cut with welder
+// called when pipe is cut with welder
/obj/structure/disposalpipe/proc/welded()
- var/obj/structure/disposalconstruct/C = new (src.loc)
+ var/obj/structure/disposalconstruct/C = new (get_turf(src))
switch(base_icon_state)
if("pipe-s")
C.ptype = 0
@@ -955,8 +268,8 @@
C.ptype = 13
if("pipe-tagger-partial")
C.ptype = 14
- C.subtype = src.subtype
- src.transfer_fingerprints_to(C)
+ C.subtype = subtype
+ transfer_fingerprints_to(C)
C.set_dir(dir)
C.density = FALSE
C.anchored = TRUE
@@ -970,8 +283,8 @@
var/obj/structure/disposalholder/H = locate() in src
if(H)
// holder was present
- H.active = 0
- var/turf/T = src.loc
+ H.active = FALSE
+ var/turf/T = get_turf(src)
if(T.density)
// deleting pipe is inside a dense turf (wall)
// this is unlikely, but just dump out everything into the turf in case
@@ -1009,6 +322,8 @@
update()
+
+
///// Z-Level stuff
/obj/structure/disposalpipe/up
icon_state = "pipe-u"
@@ -1018,7 +333,7 @@
dpdir = dir
update()
-/obj/structure/disposalpipe/up/nextdir(var/fromdir)
+/obj/structure/disposalpipe/up/nextdir(fromdir)
var/nextdir
if(fromdir == 11)
nextdir = dir
@@ -1026,7 +341,7 @@
nextdir = 12
return nextdir
-/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H)
+/obj/structure/disposalpipe/up/transfer(obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir)
H.set_dir(nextdir)
@@ -1043,7 +358,7 @@
P = F
else
- T = get_step(src.loc, H.dir)
+ T = get_step(get_turf(src), H.dir)
P = H.findpipe(T)
if(P)
@@ -1067,7 +382,7 @@
dpdir = dir
update()
-/obj/structure/disposalpipe/down/nextdir(var/fromdir)
+/obj/structure/disposalpipe/down/nextdir(fromdir)
var/nextdir
if(fromdir == 12)
nextdir = dir
@@ -1075,7 +390,7 @@
nextdir = 11
return nextdir
-/obj/structure/disposalpipe/down/transfer(var/obj/structure/disposalholder/H)
+/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir)
H.dir = nextdir
@@ -1085,14 +400,14 @@
if(nextdir == 11)
T = GetBelow(src)
if(!T)
- H.forceMove(src.loc)
+ H.forceMove(get_turf(src))
return
else
for(var/obj/structure/disposalpipe/up/F in T)
P = F
else
- T = get_step(src.loc, H.dir)
+ T = get_step(get_turf(src), H.dir)
P = H.findpipe(T)
if(P)
@@ -1107,354 +422,8 @@
return null
return P
-///// Z-Level stuff
-/obj/structure/disposalpipe/junction/yjunction
- icon_state = "pipe-y"
-//a three-way junction with dir being the dominant direction
-/obj/structure/disposalpipe/junction
- icon_state = "pipe-j1"
-
-/obj/structure/disposalpipe/junction/Initialize(mapload)
- . = ..()
- if(icon_state == "pipe-j1")
- dpdir = dir | turn(dir, -90) | turn(dir,180)
- else if(icon_state == "pipe-j2")
- dpdir = dir | turn(dir, 90) | turn(dir,180)
- else // pipe-y
- dpdir = dir | turn(dir,90) | turn(dir, -90)
- update()
- return
-
-
-// next direction to move
-// if coming in from secondary dirs, then next is primary dir
-// if coming in from primary dir, then next is equal chance of other dirs
-
-/obj/structure/disposalpipe/junction/nextdir(var/fromdir)
- var/flipdir = turn(fromdir, 180)
- if(flipdir != dir) // came from secondary dir
- return dir // so exit through primary
- else // came from primary
- // so need to choose either secondary exit
- var/mask = ..(fromdir)
-
- // find a bit which is set
- var/setbit = 0
- if(mask & NORTH)
- setbit = NORTH
- else if(mask & SOUTH)
- setbit = SOUTH
- else if(mask & EAST)
- setbit = EAST
- else
- setbit = WEST
-
- if(prob(50)) // 50% chance to choose the found bit or the other one
- return setbit
- else
- return mask & (~setbit)
-
-
-/obj/structure/disposalpipe/tagger
- name = "package tagger"
- icon_state = "pipe-tagger"
- var/sort_tag = ""
- var/partial = 0
-
-/obj/structure/disposalpipe/tagger/proc/updatedesc()
- desc = initial(desc)
- if(sort_tag)
- desc += "\nIt's tagging objects with the '[sort_tag]' tag."
-
-/obj/structure/disposalpipe/tagger/proc/updatename()
- if(sort_tag)
- name = "[initial(name)] ([sort_tag])"
- else
- name = initial(name)
-
-/obj/structure/disposalpipe/tagger/Initialize(mapload)
- . = ..()
- dpdir = dir | turn(dir, 180)
- if(sort_tag) GLOB.tagger_locations |= list("[sort_tag]" = get_z(src))
- updatename()
- updatedesc()
- update()
-
-/obj/structure/disposalpipe/tagger/attackby(var/obj/item/I, var/mob/user)
- if(..())
- return
-
- if(istype(I, /obj/item/destTagger))
- var/obj/item/destTagger/O = I
-
- if(O.currTag)// Tag set
- sort_tag = O.currTag
- playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
- to_chat(user, span_blue("Changed tag to '[sort_tag]'."))
- updatename()
- updatedesc()
-
-/obj/structure/disposalpipe/tagger/transfer(var/obj/structure/disposalholder/H)
- if(sort_tag)
- if(partial)
- H.setpartialtag(sort_tag)
- else
- H.settag(sort_tag)
- return ..()
-
-/obj/structure/disposalpipe/tagger/partial //needs two passes to tag
- name = "partial package tagger"
- icon_state = "pipe-tagger-partial"
- partial = 1
-
-//a three-way junction that sorts objects
-/obj/structure/disposalpipe/sortjunction
- name = "sorting junction"
- icon_state = "pipe-j1s"
- desc = "An underfloor disposal pipe with a package sorting mechanism."
-
- var/sortdir = 0
-
- var/last_sort = FALSE
- var/sort_scan = TRUE
- var/panel_open = FALSE
- var/datum/wires/wires = null // ...Why isnt this defined on /atom...
-
-/obj/structure/disposalpipe/sortjunction/proc/updatedesc()
- desc = initial(desc)
- if(sortType)
- desc += "\nIt's filtering objects with the '[sortType]' tag."
-
-/obj/structure/disposalpipe/sortjunction/proc/updatename()
- if(sortType)
- name = "[initial(name)] ([sortType])"
- else
- name = initial(name)
-
-/obj/structure/disposalpipe/sortjunction/Destroy()
- QDEL_NULL(wires)
- . = ..()
-
-/obj/structure/disposalpipe/sortjunction/proc/updatedir()
- var/negdir = turn(dir, 180)
-
- if(icon_state == "pipe-j1s")
- sortdir = turn(dir, -90)
- else if(icon_state == "pipe-j2s")
- sortdir = turn(dir, 90)
-
- dpdir = sortdir | dir | negdir
-
-/obj/structure/disposalpipe/sortjunction/Initialize(mapload)
- . = ..()
- if(sortType) GLOB.tagger_locations |= list("[sortType]" = get_z(src))
-
- wires = new /datum/wires/disposals(src)
-
- updatedir()
- updatename()
- updatedesc()
- update()
-
-/obj/structure/disposalpipe/sortjunction/attackby(var/obj/item/I, var/mob/user)
- if(..())
- return
-
- if(I.has_tool_quality(TOOL_SCREWDRIVER)) //Who is screwdriver_act()?
- panel_open = !panel_open
- playsound(src, I.usesound, 100, 1)
- to_chat(user, span_notice("You [panel_open ? "open" : "close"] the wire panel."))
- update_icon()
- return
-
- if(panel_open && is_wire_tool(I))
- wires.Interact(user)
- return TRUE
-
- if(istype(I, /obj/item/destTagger))
- var/obj/item/destTagger/O = I
-
- if(O.currTag)// Tag set
- sortType = O.currTag
- playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
- to_chat(user, span_blue("Changed filter to '[sortType]'."))
- updatename()
- updatedesc()
-
-/obj/structure/disposalpipe/sortjunction/proc/divert_check(var/checkTag)
- return sortType == checkTag
-
-// next direction to move
-// if coming in from negdir then next is primary dir or sortdir
-// if coming in from dir, Then next is negdir or sortdir
-// if coming in from sortdir, always go to posdir
-
-/obj/structure/disposalpipe/sortjunction/nextdir(var/fromdir, var/sortTag)
- if(sort_scan)
- if(divert_check(sortTag))
- if(!wires.is_cut(WIRE_SORT_SIDE))
- last_sort = TRUE
- else
- if(!wires.is_cut(WIRE_SORT_FORWARD))
- last_sort = FALSE
- if(fromdir != sortdir && last_sort)
- return sortdir
- // so go with the flow to positive direction
- return dir
-
-/obj/structure/disposalpipe/sortjunction/proc/reset_scan()
- if(!wires.is_cut(WIRE_SORT_SCAN))
- sort_scan = TRUE
-
-/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H)
- var/nextdir = nextdir(H.dir, H.destinationTag)
- H.set_dir(nextdir)
- var/turf/T = H.nextloc()
- var/obj/structure/disposalpipe/P = H.findpipe(T)
-
- if(P)
- // find other holder in next loc, if inactive merge it with current
- var/obj/structure/disposalholder/H2 = locate() in P
- if(H2 && !H2.active)
- H.merge(H2)
-
- H.forceMove(P)
- else // if wasn't a pipe, then set loc to turf
- H.forceMove(T)
- return null
-
- return P
-
-/obj/structure/disposalpipe/sortjunction/update_icon()
- cut_overlays()
- . = ..()
- if(panel_open)
- add_overlay("[icon_state]-open")
-
-//a three-way junction that filters all wrapped and tagged items
-/obj/structure/disposalpipe/sortjunction/wildcard
- name = "wildcard sorting junction"
- desc = "An underfloor disposal pipe which filters all wrapped and tagged items."
- subtype = 1
-
-/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(var/checkTag)
- return checkTag != ""
-
-//junction that filters all untagged items
-/obj/structure/disposalpipe/sortjunction/untagged
- name = "untagged sorting junction"
- desc = "An underfloor disposal pipe which filters all untagged items."
- subtype = 2
-
-/obj/structure/disposalpipe/sortjunction/untagged/divert_check(var/checkTag)
- return checkTag == ""
-
-/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping
- icon_state = "pipe-j2s"
-
-/obj/structure/disposalpipe/sortjunction/wildcard/flipped
- icon_state = "pipe-j2s"
-
-/obj/structure/disposalpipe/sortjunction/untagged/flipped
- icon_state = "pipe-j2s"
-
-//a trunk joining to a disposal bin or outlet on the same turf
-/obj/structure/disposalpipe/trunk
- icon_state = "pipe-t"
- var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet
-
-/obj/structure/disposalpipe/trunk/Initialize(mapload)
- ..()
- dpdir = dir
- return INITIALIZE_HINT_LATELOAD
-
-/obj/structure/disposalpipe/trunk/LateInitialize()
- if(!linked)
- getlinked()
- update()
-
-/obj/structure/disposalpipe/trunk/Destroy()
- if(linked)
- if(istype(linked, /obj/machinery/disposal))
-
- var/obj/machinery/disposal/D = linked
- D.trunk = null
- linked = null
- return ..()
-
-/obj/structure/disposalpipe/trunk/proc/getlinked()
- linked = null
- var/obj/machinery/disposal/D = locate() in loc
- if(D)
- linked = D
- if(!D.trunk)
- D.trunk = src
-
- var/obj/structure/disposaloutlet/O = locate() in loc
- if(O)
- linked = O
-
-// Override attackby so we disallow trunkremoval when somethings ontop
-/obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user)
- //Disposal constructors
- var/obj/structure/disposalconstruct/C = locate() in src.loc
- if(C && C.anchored)
- return
-
- var/turf/T = src.loc
- if(!T.is_plating())
- return // prevent interaction with T-scanner revealed pipes
- src.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)
- // check if anything changed over 2 seconds
- var/turf/uloc = user.loc
- var/atom/wloc = W.loc
- to_chat(user, "Slicing the disposal pipe.")
- sleep(30)
- if(!W.isOn()) return
- if(user.loc == uloc && wloc == W.loc)
- 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
-
- // would transfer to next pipe segment, but we are in a trunk
- // if not entering from disposal bin,
- // transfer to linked object (outlet or bin)
-
-/obj/structure/disposalpipe/trunk/transfer(var/obj/structure/disposalholder/H)
-
- if(H.dir == DOWN) // we just entered from a disposer
- return ..() // so do base transfer proc
- // otherwise, go to the linked object
- if(linked)
- var/obj/structure/disposaloutlet/O = linked
- if(istype(O) && (H))
- O.expel(H) // expel at outlet
- else
- var/obj/machinery/disposal/D = linked
- if(H)
- D.expel(H) // expel at disposal
- else
- if(H)
- src.expel(H, src.loc, 0) // expel at turf
- return null
-
- // nextdir
-
-/obj/structure/disposalpipe/trunk/nextdir(var/fromdir)
- if(fromdir == DOWN)
- return dir
- else
- return 0
// a broken pipe
/obj/structure/disposalpipe/broken
@@ -1468,128 +437,33 @@
update()
// called when welded
-// for broken pipe, remove and turn into scrap
/obj/structure/disposalpipe/broken/welded()
-// var/obj/item/scrap/S = new(src.loc)
-// S.set_components(200,0,0)
qdel(src)
-// the disposal outlet machine
-
-/obj/structure/disposaloutlet
- name = "disposal outlet"
- desc = "An outlet for the pneumatic disposal system."
- icon = 'icons/obj/pipes/disposal.dmi'
- icon_state = "outlet"
- density = TRUE
- anchored = TRUE
- var/active = 0
- var/turf/target // this will be where the output objects are 'thrown' to.
- var/mode = 0
-
-/obj/structure/disposaloutlet/Initialize(mapload)
- . = ..()
-
- target = get_ranged_target_turf(src, dir, 10)
-
-
- var/obj/structure/disposalpipe/trunk/trunk = locate() in loc
- if(trunk)
- trunk.linked = src // link the pipe trunk to self
-
-/obj/structure/disposaloutlet/Destroy()
- var/obj/structure/disposalpipe/trunk/trunk = locate() in loc
- if(trunk && trunk.linked == src)
- trunk.linked = null
- return ..()
-
- // expel the contents of the holder object, then delete it
- // called when the holder exits the outlet
-/obj/structure/disposaloutlet/proc/expel(var/obj/structure/disposalholder/H)
- flick("outlet-open", src)
- playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0)
- sleep(20) //wait until correct animation frame
- playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
-
- if(H)
- for(var/atom/movable/AM in H)
- AM.forceMove(src.loc)
- AM.pipe_eject(dir)
- if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z
- spawn(5)
- AM.throw_at(target, 3, 1)
- H.vent_gas(src.loc)
- qdel(H)
-
- return
-
-/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user)
- if(!I || !user)
- return
- src.add_fingerprint(user)
- if(I.has_tool_quality(TOOL_SCREWDRIVER))
- if(mode==0)
- mode=1
- to_chat(user, "You remove the screws around the power connection.")
- playsound(src, I.usesound, 50, 1)
- return
- else if(mode==1)
- mode=0
- 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)
- var/obj/item/weldingtool/W = I.get_welder()
- 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)
- src.transfer_fingerprints_to(C)
- C.ptype = 7 // 7 = outlet
- C.update()
- C.anchored = TRUE
- C.density = TRUE
- qdel(src)
- return
- else
- to_chat(user, "You need more welding fuel to complete this task.")
- return
-
// called when movable is expelled from a disposal pipe or outlet
// by default does nothing, override for special behaviour
-
-/atom/movable/proc/pipe_eject(var/direction)
+/atom/movable/proc/pipe_eject(direction)
return
// check if mob has client, if so restore client view on eject
/mob/pipe_eject(var/direction)
- if (src.client)
- src.client.perspective = MOB_PERSPECTIVE
- src.client.eye = src
-
+ if (client)
+ client.perspective = MOB_PERSPECTIVE
+ client.eye = src
return
-/obj/effect/decal/cleanable/blood/gibs/pipe_eject(var/direction)
+/obj/effect/decal/cleanable/blood/gibs/pipe_eject(direction)
var/list/dirs
if(direction)
dirs = list( direction, turn(direction, -45), turn(direction, 45))
else
dirs = GLOB.alldirs.Copy()
+ streak(dirs)
- src.streak(dirs)
-
-/obj/effect/decal/cleanable/blood/gibs/robot/pipe_eject(var/direction)
+/obj/effect/decal/cleanable/blood/gibs/robot/pipe_eject(direction)
var/list/dirs
if(direction)
dirs = list( direction, turn(direction, -45), turn(direction, 45))
else
dirs = GLOB.alldirs.Copy()
-
- src.streak(dirs)
-
-#undef SEND_PRESSURE
-#undef PRESSURE_TANK_VOLUME
-#undef PUMP_MAX_FLOW_RATE
+ streak(dirs)
diff --git a/code/modules/recycling/disposal_holder.dm b/code/modules/recycling/disposal_holder.dm
new file mode 100644
index 0000000000..db6f549df6
--- /dev/null
+++ b/code/modules/recycling/disposal_holder.dm
@@ -0,0 +1,153 @@
+// 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_ABSTRACT
+ var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point
+ var/active = FALSE // true if the holder is moving, otherwise inactive
+ var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops)
+ var/destinationTag = "" // changes if contains a delivery container
+ var/hasmob = FALSE //If it contains a mob
+ var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again.
+ dir = 0
+
+ // 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.
+
+ //Check for any living mobs trigger hasmob.
+ //hasmob effects whether the package goes to cargo or its tagged destination.
+ for(var/mob/living/M in flush_list)
+ if(M.stat != DEAD && !istype(M,/mob/living/silicon/robot/drone))
+ hasmob = TRUE
+ if(M.client)
+ M.client.perspective = EYE_PERSPECTIVE
+ M.client.eye = src
+
+ //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)
+ for(var/obj/O in flush_list)
+ if(!O.contents)
+ continue
+ for(var/mob/living/M in O.contents)
+ if(M && M.stat != DEAD && !istype(M,/mob/living/silicon/robot/drone))
+ hasmob = TRUE
+
+ // 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
+ // Drones can mail themselves through maint.
+ if(istype(AM, /mob/living/silicon/robot/drone))
+ var/mob/living/silicon/robot/drone/drone = AM
+ src.destinationTag = drone.mail_destination
+
+// movement process, persists while holder is moving through pipes
+/obj/structure/disposalholder/proc/move()
+ if(!active)
+ return
+
+ // Clonk n' bonk
+ if(hasmob && prob(3))
+ for(var/mob/living/H in src)
+ if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system,
+ H.take_overall_damage(20, 0, "Blunt Trauma")//horribly maim any living creature jumping down disposals. c'est la vie
+
+ // Transfer to next segment
+ var/obj/structure/disposalpipe/last = loc
+ var/obj/structure/disposalpipe/curr = last.transfer(src)
+ if(!active)
+ return // Handled by a machine connected to a trunk during transfer()
+ if(!curr)
+ last.expel(src, get_turf(loc), dir)
+ return
+
+ // Onto the next segment
+ if(!(count--))
+ active = FALSE
+ return
+ addtimer(CALLBACK(src, PROC_REF(move)), 1, TIMER_DELETE_ME)
+
+
+// find the turf which should contain the next pipe
+/obj/structure/disposalholder/proc/nextloc()
+ return get_step(loc,dir)
+
+// find a matching pipe on a turf
+/obj/structure/disposalholder/proc/findpipe(turf/T)
+ if(!T)
+ return null
+ var/fdir = turn(dir, 180) // flip the movement direction
+ for(var/obj/structure/disposalpipe/P in T)
+ if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir
+ return P
+ // if no matching pipe, return null
+ return null
+
+// merge two holder objects
+// used when a a holder meets a stuck holder
+/obj/structure/disposalholder/proc/merge(obj/structure/disposalholder/other)
+ for(var/atom/movable/AM in other)
+ AM.forceMove(src) // move everything in other holder to this one
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.client) // if a client mob, update eye to follow this holder
+ M.client.eye = src
+ qdel(other)
+
+/obj/structure/disposalholder/proc/settag(new_tag)
+ destinationTag = new_tag
+
+/obj/structure/disposalholder/proc/setpartialtag(new_tag)
+ if(partialTag == new_tag)
+ destinationTag = new_tag
+ partialTag = ""
+ else
+ partialTag = new_tag
+
+
+// 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)
+ return
+ user.last_special = world.time+100
+
+ if(QDELETED(src))
+ return
+
+ for(var/mob/M in hearers(get_turf(src)))
+ to_chat(M, "CLONG, clong!")
+ playsound(src, 'sound/effects/clang.ogg', 50, 0, 0)
+
+ // 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 ..()
diff --git a/code/modules/recycling/disposal_junctions.dm b/code/modules/recycling/disposal_junctions.dm
new file mode 100644
index 0000000000..d5f16be7a2
--- /dev/null
+++ b/code/modules/recycling/disposal_junctions.dm
@@ -0,0 +1,197 @@
+/obj/structure/disposalpipe/junction/yjunction
+ icon_state = "pipe-y"
+
+//a three-way junction with dir being the dominant direction
+/obj/structure/disposalpipe/junction
+ icon_state = "pipe-j1"
+
+/obj/structure/disposalpipe/junction/Initialize(mapload)
+ . = ..()
+ if(icon_state == "pipe-j1")
+ dpdir = dir | turn(dir, -90) | turn(dir,180)
+ else if(icon_state == "pipe-j2")
+ dpdir = dir | turn(dir, 90) | turn(dir,180)
+ else // pipe-y
+ dpdir = dir | turn(dir,90) | turn(dir, -90)
+ update()
+ return
+
+
+// next direction to move
+// if coming in from secondary dirs, then next is primary dir
+// if coming in from primary dir, then next is equal chance of other dirs
+
+/obj/structure/disposalpipe/junction/nextdir(fromdir)
+ var/flipdir = turn(fromdir, 180)
+ if(flipdir != dir) // came from secondary dir
+ return dir // so exit through primary
+ else // came from primary
+ // so need to choose either secondary exit
+ var/mask = ..(fromdir)
+
+ // find a bit which is set
+ var/setbit = 0
+ if(mask & NORTH)
+ setbit = NORTH
+ else if(mask & SOUTH)
+ setbit = SOUTH
+ else if(mask & EAST)
+ setbit = EAST
+ else
+ setbit = WEST
+
+ if(prob(50)) // 50% chance to choose the found bit or the other one
+ return setbit
+ else
+ return mask & (~setbit)
+
+//a three-way junction that sorts objects
+/obj/structure/disposalpipe/sortjunction
+ name = "sorting junction"
+ icon_state = "pipe-j1s"
+ desc = "An underfloor disposal pipe with a package sorting mechanism."
+
+ var/sortdir = 0
+
+ var/last_sort = FALSE
+ var/sort_scan = TRUE
+ var/panel_open = FALSE
+ var/datum/wires/wires = null // ...Why isnt this defined on /atom...
+
+/obj/structure/disposalpipe/sortjunction/proc/updatedesc()
+ desc = initial(desc)
+ if(sortType)
+ desc += "\nIt's filtering objects with the '[sortType]' tag."
+
+/obj/structure/disposalpipe/sortjunction/proc/updatename()
+ if(sortType)
+ name = "[initial(name)] ([sortType])"
+ else
+ name = initial(name)
+
+/obj/structure/disposalpipe/sortjunction/Destroy()
+ QDEL_NULL(wires)
+ . = ..()
+
+/obj/structure/disposalpipe/sortjunction/proc/updatedir()
+ var/negdir = turn(dir, 180)
+
+ if(icon_state == "pipe-j1s")
+ sortdir = turn(dir, -90)
+ else if(icon_state == "pipe-j2s")
+ sortdir = turn(dir, 90)
+
+ dpdir = sortdir | dir | negdir
+
+/obj/structure/disposalpipe/sortjunction/Initialize(mapload)
+ . = ..()
+ if(sortType) GLOB.tagger_locations |= list("[sortType]" = get_z(src))
+
+ wires = new /datum/wires/disposals(src)
+
+ updatedir()
+ updatename()
+ updatedesc()
+ update()
+
+/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user)
+ if(..())
+ return
+
+ if(I.has_tool_quality(TOOL_SCREWDRIVER)) //Who is screwdriver_act()?
+ panel_open = !panel_open
+ playsound(src, I.usesound, 100, 1)
+ to_chat(user, span_notice("You [panel_open ? "open" : "close"] the wire panel."))
+ update_icon()
+ return
+
+ if(panel_open && is_wire_tool(I))
+ wires.Interact(user)
+ return TRUE
+
+ if(istype(I, /obj/item/destTagger))
+ var/obj/item/destTagger/O = I
+
+ if(O.currTag)// Tag set
+ sortType = O.currTag
+ playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
+ to_chat(user, span_blue("Changed filter to '[sortType]'."))
+ updatename()
+ updatedesc()
+
+/obj/structure/disposalpipe/sortjunction/proc/divert_check(checkTag)
+ return sortType == checkTag
+
+// next direction to move
+// if coming in from negdir then next is primary dir or sortdir
+// if coming in from dir, Then next is negdir or sortdir
+// if coming in from sortdir, always go to posdir
+
+/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag)
+ if(sort_scan)
+ if(divert_check(sortTag))
+ if(!wires.is_cut(WIRE_SORT_SIDE))
+ last_sort = TRUE
+ else
+ if(!wires.is_cut(WIRE_SORT_FORWARD))
+ last_sort = FALSE
+ if(fromdir != sortdir && last_sort)
+ return sortdir
+ // so go with the flow to positive direction
+ return dir
+
+/obj/structure/disposalpipe/sortjunction/proc/reset_scan()
+ if(!wires.is_cut(WIRE_SORT_SCAN))
+ sort_scan = TRUE
+
+/obj/structure/disposalpipe/sortjunction/transfer(obj/structure/disposalholder/H)
+ var/nextdir = nextdir(H.dir, H.destinationTag)
+ H.set_dir(nextdir)
+ var/turf/T = H.nextloc()
+ var/obj/structure/disposalpipe/P = H.findpipe(T)
+
+ if(P)
+ // find other holder in next loc, if inactive merge it with current
+ var/obj/structure/disposalholder/H2 = locate() in P
+ if(H2 && !H2.active)
+ H.merge(H2)
+
+ H.forceMove(P)
+ else // if wasn't a pipe, then set loc to turf
+ H.forceMove(T)
+ return null
+
+ return P
+
+/obj/structure/disposalpipe/sortjunction/update_icon()
+ cut_overlays()
+ . = ..()
+ if(panel_open)
+ add_overlay("[icon_state]-open")
+
+//a three-way junction that filters all wrapped and tagged items
+/obj/structure/disposalpipe/sortjunction/wildcard
+ name = "wildcard sorting junction"
+ desc = "An underfloor disposal pipe which filters all wrapped and tagged items."
+ subtype = 1
+
+/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(checkTag)
+ return checkTag != ""
+
+//junction that filters all untagged items
+/obj/structure/disposalpipe/sortjunction/untagged
+ name = "untagged sorting junction"
+ desc = "An underfloor disposal pipe which filters all untagged items."
+ subtype = 2
+
+/obj/structure/disposalpipe/sortjunction/untagged/divert_check(checkTag)
+ return checkTag == ""
+
+/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping
+ icon_state = "pipe-j2s"
+
+/obj/structure/disposalpipe/sortjunction/wildcard/flipped
+ icon_state = "pipe-j2s"
+
+/obj/structure/disposalpipe/sortjunction/untagged/flipped
+ icon_state = "pipe-j2s"
diff --git a/code/modules/recycling/disposal_machines.dm b/code/modules/recycling/disposal_machines.dm
new file mode 100644
index 0000000000..199fcf2201
--- /dev/null
+++ b/code/modules/recycling/disposal_machines.dm
@@ -0,0 +1,492 @@
+// 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) //kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added.
+#define PRESSURE_TANK_VOLUME 150 //L
+#define PUMP_MAX_FLOW_RATE 11.25 //L/s - 4 m/s using a 15 cm by 15 cm inlet //NOTE: I reduced the send pressure from 801 to 101.05 which is about 1/8 there was originally, and this was 90 before that. 90/8 is about 11.25, so that's the new value. -Reo
+#define DISPOSALMODE_EJECTONLY -1
+#define DISPOSALMODE_OFF 0
+#define DISPOSALMODE_CHARGING 1
+#define DISPOSALMODE_CHARGED 2
+
+/obj/machinery/disposal
+ name = "disposal unit"
+ desc = "A pneumatic waste disposal unit."
+ icon = 'icons/obj/pipes/disposal.dmi'
+ icon_state = "disposal"
+ anchored = TRUE
+ density = TRUE
+ var/datum/gas_mixture/air_contents // internal reservoir
+ var/mode = DISPOSALMODE_CHARGING
+ var/flush = FALSE // true if flush handle is pulled
+ 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.
+ active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W
+ idle_power_usage = 100
+ var/stat_tracking = TRUE
+
+// create a new disposal
+// find the attached trunk (if present) and init gas resvr.
+/obj/machinery/disposal/Initialize(mapload)
+ . = ..()
+
+ var/obj/structure/disposalpipe/trunk/trunk = locate() in loc
+ if(!trunk)
+ mode = DISPOSALMODE_OFF
+ flush = FALSE
+
+ air_contents = new(PRESSURE_TANK_VOLUME)
+ update()
+ AddComponent(/datum/component/disposal_system_connection)
+
+/obj/machinery/disposal/Destroy()
+ eject()
+ return ..()
+
+// attack by item places it in to disposal
+/obj/machinery/disposal/attackby(obj/item/I, mob/user)
+ if(stat & BROKEN || !I || !user)
+ return
+
+ add_fingerprint(user)
+ if(mode <= DISPOSALMODE_OFF) // It's off
+ if(I.has_tool_quality(TOOL_SCREWDRIVER))
+ if(contents.len > 0)
+ to_chat(user, "Eject the items first!")
+ return
+ if(mode == DISPOSALMODE_OFF) // It's off but still not unscrewed
+ mode = DISPOSALMODE_EJECTONLY // Set it to doubleoff l0l
+ playsound(src, I.usesound, 50, 1)
+ to_chat(user, "You remove the screws around the power connection.")
+ return
+ else if(mode == DISPOSALMODE_EJECTONLY)
+ mode = DISPOSALMODE_OFF
+ playsound(src, I.usesound, 50, 1)
+ to_chat(user, "You attach the screws around the power connection.")
+ return
+ else if(I.has_tool_quality(TOOL_WELDER) && mode == DISPOSALMODE_EJECTONLY)
+ if(contents.len > 0)
+ to_chat(user, "Eject the items first!")
+ return
+ var/obj/item/weldingtool/W = I.get_welder()
+ if(W.remove_fuel(0,user))
+ playsound(src, W.usesound, 100, 1)
+ to_chat(user, "You start slicing the floorweld off the disposal unit.")
+
+ 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)
+ return
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
+
+
+ if(istype(I, /obj/item/storage/bag/trash))
+ var/obj/item/storage/bag/trash/T = I
+ to_chat(user, span_blue("You empty the bag."))
+ for(var/obj/item/O in T.contents)
+ T.remove_from_storage(O,src)
+ T.update_icon()
+ update()
+ return
+
+ if(istype(I, /obj/item/material/ashtray))
+ var/obj/item/material/ashtray/A = I
+ if(A.contents.len > 0)
+ user.visible_message(span_infoplain(span_bold("\The [user]") + " empties \the [A] into [src]."))
+ for(var/obj/item/O in A.contents)
+ O.forceMove(src)
+ A.update_icon()
+ update()
+ return
+
+ 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(user))
+ V.show_message("[user] starts putting [GM.name] into the disposal.", 3)
+ if(do_after(user, 2 SECONDS, target = src))
+ if (GM.client)
+ GM.client.perspective = EYE_PERSPECTIVE
+ GM.client.eye = src
+ GM.forceMove(src)
+ for (var/mob/C in viewers(src))
+ C.show_message(span_red("[GM.name] has been placed in the [src] by [user]."), 3)
+ qdel(G)
+
+ add_attack_logs(user,GM,"Disposals dunked")
+ return
+
+ if(isrobot(user))
+ return
+ if(!I || I.anchored || !I.canremove)
+ return
+
+ user.drop_item()
+ if(I)
+ 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)
+ victim.forceMove(src)
+
+ I.forceMove(src)
+
+ user.visible_message("[user] places \the [I] into the [src].", "You place \the [I] into the [src].","Ca-Clunk")
+ update()
+
+// mouse drop another mob or self
+//
+/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user)
+ 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")
+ else
+ return
+ if (target.client)
+ target.client.perspective = EYE_PERSPECTIVE
+ target.client.eye = src
+
+ target.forceMove(src)
+
+ for (var/mob/C in viewers(src))
+ if(C == user)
+ continue
+ C.show_message(msg, 3)
+
+ update()
+ return
+
+// attempt to move while inside
+/obj/machinery/disposal/relaymove(mob/user)
+ if(user.stat || flushing)
+ return
+ if(user.loc == src)
+ go_out(user)
+ return
+
+// leave the disposal
+/obj/machinery/disposal/proc/go_out(mob/user)
+ if (user.client)
+ user.client.eye = user.client.mob
+ user.client.perspective = MOB_PERSPECTIVE
+ user.forceMove(get_turf(src))
+ update()
+ return
+
+// ai as human but can't flush
+/obj/machinery/disposal/attack_ai(mob/user)
+ add_hiddenprint(user)
+ tgui_interact(user)
+
+// human interact with machine
+/obj/machinery/disposal/attack_hand(mob/user)
+ if(stat & BROKEN)
+ return
+
+ if(user && user.loc == src)
+ to_chat(user, span_red("You cannot reach the controls from inside."))
+ return
+
+ // Clumsy folks can only flush it.
+ if(user.IsAdvancedToolUser(1))
+ tgui_interact(user)
+ else
+ flush = !flush
+ update()
+ return
+
+// user interaction
+/obj/machinery/disposal/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "DisposalBin")
+ ui.open()
+
+/obj/machinery/disposal/tgui_data(mob/user)
+ var/list/data = list()
+
+ data["isAI"] = isAI(user)
+ data["flushing"] = flush
+ data["mode"] = mode
+ data["pressure"] = round(clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100),1)
+
+ return data
+
+/obj/machinery/disposal/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return
+
+ if(ui.user.loc == src)
+ to_chat(ui.user, span_warning("You cannot reach the controls from inside."))
+ return TRUE
+
+ if(mode == DISPOSALMODE_EJECTONLY && action != "eject") // If the mode is -1, only allow ejection
+ to_chat(ui.user, span_warning("The disposal units power is disabled."))
+ return
+
+ if(stat & BROKEN)
+ return
+
+ add_fingerprint(ui.user)
+
+ if(flushing)
+ return
+
+ if(isturf(loc))
+ if(action == "pumpOn")
+ mode = DISPOSALMODE_CHARGING
+ update()
+ if(action == "pumpOff")
+ mode = DISPOSALMODE_OFF
+ update()
+
+ if(action == "engageHandle")
+ flush = TRUE
+ update()
+ if(action == "disengageHandle")
+ flush = FALSE
+ update()
+
+ if(action == "eject")
+ eject()
+
+ return TRUE
+
+// eject the contents of the disposal unit
+
+/obj/machinery/disposal/verb/force_eject()
+ set src in oview(1)
+ set category = "Object"
+ set name = "Force Eject"
+ if(flushing)
+ return
+ eject()
+
+/obj/machinery/disposal/proc/eject()
+ for(var/atom/movable/AM in src)
+ AM.forceMove(get_turf(src))
+ AM.pipe_eject(0)
+ update()
+
+// update the icon & overlays to reflect mode & status
+/obj/machinery/disposal/proc/update()
+ cut_overlays()
+ if(stat & BROKEN)
+ icon_state = "disposal-broken"
+ mode = DISPOSALMODE_OFF
+ flush = 0
+ return
+
+ // flush handle
+ if(flush)
+ add_overlay("[initial(icon_state)]-handle")
+
+ // only handle is shown if no power
+ if(stat & NOPOWER || mode == DISPOSALMODE_EJECTONLY)
+ return
+
+ // check for items in disposal - occupied light
+ if(contents.len > 0)
+ add_overlay("[initial(icon_state)]-full")
+
+ // charging and ready light
+ if(mode == DISPOSALMODE_CHARGING)
+ add_overlay("[initial(icon_state)]-charge")
+ else if(mode == DISPOSALMODE_CHARGED)
+ add_overlay("[initial(icon_state)]-ready")
+
+// timed process
+// charge the gas reservoir and perform flush if ready
+/obj/machinery/disposal/process()
+ if(!air_contents || (stat & BROKEN)) // nothing can happen if broken
+ update_use_power(USE_POWER_OFF)
+ return
+
+ flush_count++
+ if( flush_count >= flush_every_ticks )
+ if( contents.len )
+ if(mode == DISPOSALMODE_CHARGED)
+ spawn(0)
+ feedback_inc("disposal_auto_flush",1)
+ flush()
+ flush_count = 0
+
+ if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power
+ flush()
+
+ if(mode != DISPOSALMODE_CHARGING) //if off or ready, no need to charge
+ 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()
+ else
+ pressurize() //otherwise charge
+
+/obj/machinery/disposal/proc/pressurize()
+ if(stat & NOPOWER) // won't charge if no power
+ update_use_power(USE_POWER_OFF)
+ return
+
+ var/atom/L = loc // recharging from loc turf
+ var/datum/gas_mixture/env = L.return_air()
+
+ var/power_draw = -1
+ if(env && env.temperature > 0)
+ var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here
+ power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage)
+
+ if (power_draw > 0)
+ use_power(power_draw)
+
+// perform a flush
+/obj/machinery/disposal/proc/flush()
+ if(flushing)
+ return
+
+ flushing = TRUE
+ flick("[icon_state]-flush", src)
+ // wait for animation to finish
+ addtimer(CALLBACK(src, PROC_REF(flush_animation)), 1 SECOND, TIMER_DELETE_ME)
+
+/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)
+
+/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++
+ flushing = FALSE
+
+ // now reset disposal state
+ flush = FALSE
+ if(mode == DISPOSALMODE_CHARGED) // if was ready,
+ mode = DISPOSALMODE_CHARGING // switch to charging
+ update()
+ return
+
+// called when area power changes
+/obj/machinery/disposal/power_change()
+ ..() // do default setting/reset of stat NOPOWER bit
+ update() // update icon
+ return
+
+/obj/machinery/disposal/hitby(atom/movable/AM)
+ . = ..()
+ if(istype(AM, /obj/item) && !istype(AM, /obj/item/projectile))
+ if(prob(75))
+ AM.forceMove(src)
+ visible_message("\The [AM] lands in \the [src].")
+ else
+ visible_message("\The [AM] bounces off of \the [src]'s rim!")
+
+ if(istype(AM,/mob/living))
+ if(prob(75))
+ var/mob/living/to_be_dunked = AM
+ if(ishuman(AM) ||to_be_dunked.client)
+ log_and_message_admins("[AM] was thrown into \the [src]", null)
+ AM.forceMove(src)
+ visible_message("\The [AM] lands in \the [src].")
+
+/obj/machinery/disposal/wall
+ name = "inset disposal unit"
+ icon_state = "wall"
+
+ density = FALSE
+
+/obj/machinery/disposal/wall/update()
+ ..()
+
+ switch(dir)
+ if(1)
+ pixel_x = 0
+ pixel_y = -32
+ if(2)
+ pixel_x = 0
+ pixel_y = 32
+ if(4)
+ pixel_x = -32
+ pixel_y = 0
+ if(8)
+ pixel_x = 32
+ pixel_y = 0
+
+
+// Cleaner subtype
+/obj/machinery/disposal/wall/cleaner
+ name = "resleeving equipment deposit"
+ desc = "Automatically cleans and transports items to the local resleeving facilities."
+ icon_state = "bluewall"
+
+/obj/machinery/disposal/wall/cleaner/flush()
+ if(flushing)
+ return
+
+ // Clean items before sending them
+ for(var/obj/item/flushed_item in src)
+ if(istype(flushed_item, /obj/item/storage))
+ var/obj/item/storage/storage_flushed = flushed_item
+ var/list/storage_items = storage_flushed.return_inv()
+ for(var/obj/item/item in storage_items)
+ item.wash(CLEAN_WASH)
+ continue
+ if(istype(flushed_item, /obj/item))
+ flushed_item.wash(CLEAN_WASH)
+
+ . = ..()
+
+#undef DISPOSALMODE_EJECTONLY
+#undef DISPOSALMODE_OFF
+#undef DISPOSALMODE_CHARGING
+#undef DISPOSALMODE_CHARGED
+#undef SEND_PRESSURE
+#undef PRESSURE_TANK_VOLUME
+#undef PUMP_MAX_FLOW_RATE
diff --git a/code/modules/recycling/disposal_mail.dm b/code/modules/recycling/disposal_mail.dm
new file mode 100644
index 0000000000..419477e17c
--- /dev/null
+++ b/code/modules/recycling/disposal_mail.dm
@@ -0,0 +1,230 @@
+/obj/structure/bigDelivery
+ desc = "A big wrapped package."
+ name = "large parcel"
+ icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit
+ icon_state = "deliverycloset"
+ var/obj/wrapped = null
+ density = TRUE
+ var/sortTag = null
+ flags = NOBLUDGEON
+ mouse_drag_pointer = MOUSE_ACTIVE_POINTER
+ var/examtext = null
+ var/nameset = 0
+ var/label_y
+ var/label_x
+ var/tag_x
+
+/obj/structure/bigDelivery/attack_hand(mob/user as mob)
+ unwrap()
+
+/obj/structure/bigDelivery/proc/unwrap()
+ playsound(src, 'sound/items/package_unwrap.ogg', 50, 1)
+ // Destroy will drop our wrapped object on the turf, so let it.
+ qdel(src)
+
+/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob)
+ if(istype(W, /obj/item/destTagger))
+ var/obj/item/destTagger/O = W
+ if(O.currTag)
+ if(src.sortTag != O.currTag)
+ to_chat(user, span_notice("You have labeled the destination as [O.currTag]."))
+ if(!src.sortTag)
+ src.sortTag = O.currTag
+ update_icon()
+ else
+ src.sortTag = O.currTag
+ playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
+ else
+ to_chat(user, span_warning("The package is already labeled for [O.currTag]."))
+ else
+ to_chat(user, span_warning("You need to set a destination first!"))
+
+ else if(istype(W, /obj/item/pen))
+ switch(tgui_alert(user, "What would you like to alter?","Select Alteration",list("Title","Description","Cancel")))
+ if("Title")
+ var/str = sanitizeSafe(tgui_input_text(user,"Label text?","Set label","", MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
+ if(!str || !length(str))
+ to_chat(user, span_warning(" Invalid text."))
+ return
+ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
+ span_notice("You title \the [src]: \"[str]\""),\
+ "You hear someone scribbling a note.")
+ playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
+ name = "[name] ([str])"
+ if(!examtext && !nameset)
+ nameset = 1
+ update_icon()
+ else
+ nameset = 1
+ if("Description")
+ var/str = tgui_input_text(user,"Label text?","Set label","", MAX_MESSAGE_LEN)
+ if(!str || !length(str))
+ to_chat(user, span_red("Invalid text."))
+ return
+ if(!examtext && !nameset)
+ examtext = str
+ update_icon()
+ else
+ examtext = str
+ user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\
+ span_notice("You label \the [src]: \"[examtext]\""),\
+ "You hear someone scribbling a note.")
+ playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
+ return
+
+/obj/structure/bigDelivery/update_icon()
+ cut_overlays()
+ if(nameset || examtext)
+ var/image/I = new/image('icons/obj/storage.dmi',"delivery_label")
+ if(icon_state == "deliverycloset")
+ I.pixel_x = 2
+ if(label_y == null)
+ label_y = rand(-6, 11)
+ I.pixel_y = label_y
+ else if(icon_state == "deliverycrate")
+ if(label_x == null)
+ label_x = rand(-8, 6)
+ I.pixel_x = label_x
+ I.pixel_y = -3
+ add_overlay(I)
+ if(src.sortTag)
+ var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag")
+ if(icon_state == "deliverycloset")
+ if(tag_x == null)
+ tag_x = rand(-2, 3)
+ I.pixel_x = tag_x
+ I.pixel_y = 9
+ else if(icon_state == "deliverycrate")
+ if(tag_x == null)
+ tag_x = rand(-8, 6)
+ I.pixel_x = tag_x
+ I.pixel_y = -3
+ add_overlay(I)
+
+/obj/structure/bigDelivery/examine(mob/user)
+ . = ..()
+ if(get_dist(user, src) <= 4)
+ if(sortTag)
+ . += span_notice("It is labeled \"[sortTag]\"")
+ if(examtext)
+ . += span_notice("It has a note attached which reads, \"[examtext]\"")
+
+/obj/structure/bigDelivery/Destroy()
+ if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
+ wrapped.forceMove(get_turf(src))
+ if(istype(wrapped, /obj/structure/closet))
+ var/obj/structure/closet/O = wrapped
+ O.sealed = 0
+ wrapped = null
+ var/turf/T = get_turf(src)
+ for(var/atom/movable/AM in contents)
+ AM.forceMove(T)
+ return ..()
+
+/obj/item/smallDelivery
+ desc = "A small wrapped package."
+ name = "small parcel"
+ icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit
+ icon_state = "deliverycrate3"
+ drop_sound = 'sound/items/drop/cardboardbox.ogg'
+ pickup_sound = 'sound/items/pickup/cardboardbox.ogg'
+ var/obj/item/wrapped = null
+ var/sortTag = null
+ var/examtext = null
+ var/nameset = 0
+ var/tag_x
+
+/obj/item/smallDelivery/attack_self(mob/user as mob)
+ if (src.wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
+ wrapped.loc = user.loc
+ if(ishuman(user))
+ user.put_in_hands(wrapped)
+ else
+ wrapped.loc = get_turf(src)
+
+ qdel(src)
+ return
+
+/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob)
+ if(istype(W, /obj/item/destTagger))
+ var/obj/item/destTagger/O = W
+ if(O.currTag)
+ if(src.sortTag != O.currTag)
+ to_chat(user, span_notice("You have labeled the destination as [O.currTag]."))
+ if(!src.sortTag)
+ src.sortTag = O.currTag
+ update_icon()
+ else
+ src.sortTag = O.currTag
+ playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
+ else
+ to_chat(user, span_warning("The package is already labeled for [O.currTag]."))
+ else
+ to_chat(user, span_warning("You need to set a destination first!"))
+
+ else if(istype(W, /obj/item/pen))
+ switch(tgui_alert(user, "What would you like to alter?","Select Alteration",list("Title","Description","Cancel")))
+ if("Title")
+ var/str = sanitizeSafe(tgui_input_text(user,"Label text?","Set label","", MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
+ if(!str || !length(str))
+ to_chat(user, span_warning(" Invalid text."))
+ return
+ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
+ span_notice("You title \the [src]: \"[str]\""),\
+ "You hear someone scribbling a note.")
+ playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
+ name = "[name] ([str])"
+ if(!examtext && !nameset)
+ nameset = 1
+ update_icon()
+ else
+ nameset = 1
+
+ if("Description")
+ var/str = tgui_input_text(user,"Label text?","Set label","", MAX_MESSAGE_LEN)
+ if(!str || !length(str))
+ to_chat(user, span_red("Invalid text."))
+ return
+ if(!examtext && !nameset)
+ examtext = str
+ update_icon()
+ else
+ examtext = str
+ user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\
+ span_notice("You label \the [src]: \"[examtext]\""),\
+ "You hear someone scribbling a note.")
+ playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
+ return
+
+/obj/item/smallDelivery/update_icon()
+ cut_overlays()
+ if((nameset || examtext) && icon_state != "deliverycrate1")
+ var/image/I = new/image('icons/obj/storage.dmi',"delivery_label")
+ if(icon_state == "deliverycrate5")
+ I.pixel_y = -1
+ add_overlay(I)
+ if(src.sortTag)
+ var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag")
+ switch(icon_state)
+ if("deliverycrate1")
+ I.pixel_y = -5
+ if("deliverycrate2")
+ I.pixel_y = -2
+ if("deliverycrate3")
+ I.pixel_y = 0
+ if("deliverycrate4")
+ if(tag_x == null)
+ tag_x = rand(0,5)
+ I.pixel_x = tag_x
+ I.pixel_y = 3
+ if("deliverycrate5")
+ I.pixel_y = -3
+ add_overlay(I)
+
+/obj/item/smallDelivery/examine(mob/user)
+ . = ..()
+ if(get_dist(user, src) <= 4)
+ if(sortTag)
+ . += span_notice("It is labeled \"[sortTag]\"")
+ if(examtext)
+ . += span_notice("It has a note attached which reads, \"[examtext]\"")
diff --git a/code/modules/recycling/disposal_outlet.dm b/code/modules/recycling/disposal_outlet.dm
new file mode 100644
index 0000000000..4415379117
--- /dev/null
+++ b/code/modules/recycling/disposal_outlet.dm
@@ -0,0 +1,49 @@
+// the disposal outlet machine
+/obj/structure/disposaloutlet
+ name = "disposal outlet"
+ desc = "An outlet for the pneumatic disposal system."
+ icon = 'icons/obj/pipes/disposal.dmi'
+ icon_state = "outlet"
+ density = TRUE
+ anchored = TRUE
+ var/active = 0
+ var/mode = 0
+
+/obj/structure/disposaloutlet/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/disposal_system_connection/disposaloutlet) // Special subtype for directional tossing and it's animation/buzzer
+
+/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
+ to_chat(user, "You remove the screws around the power connection.")
+ playsound(src, I.usesound, 50, 1)
+ return
+ else if(mode==1)
+ mode=0
+ 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)
+ var/obj/item/weldingtool/W = I.get_welder()
+ 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)
+ src.transfer_fingerprints_to(C)
+ C.ptype = 7 // 7 = outlet
+ C.update()
+ C.anchored = TRUE
+ C.density = TRUE
+ qdel(src)
+ return
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
diff --git a/code/modules/recycling/disposal_tagger.dm b/code/modules/recycling/disposal_tagger.dm
new file mode 100644
index 0000000000..4a2ba5d9dc
--- /dev/null
+++ b/code/modules/recycling/disposal_tagger.dm
@@ -0,0 +1,51 @@
+/obj/structure/disposalpipe/tagger
+ name = "package tagger"
+ icon_state = "pipe-tagger"
+ var/sort_tag = ""
+ var/partial = 0
+
+/obj/structure/disposalpipe/tagger/proc/updatedesc()
+ desc = initial(desc)
+ if(sort_tag)
+ desc += "\nIt's tagging objects with the '[sort_tag]' tag."
+
+/obj/structure/disposalpipe/tagger/proc/updatename()
+ if(sort_tag)
+ name = "[initial(name)] ([sort_tag])"
+ else
+ name = initial(name)
+
+/obj/structure/disposalpipe/tagger/Initialize(mapload)
+ . = ..()
+ dpdir = dir | turn(dir, 180)
+ if(sort_tag) GLOB.tagger_locations |= list("[sort_tag]" = get_z(src))
+ updatename()
+ updatedesc()
+ update()
+
+/obj/structure/disposalpipe/tagger/attackby(obj/item/I, mob/user)
+ if(..())
+ return
+
+ if(istype(I, /obj/item/destTagger))
+ var/obj/item/destTagger/O = I
+
+ if(O.currTag)// Tag set
+ sort_tag = O.currTag
+ playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
+ to_chat(user, span_blue("Changed tag to '[sort_tag]'."))
+ updatename()
+ updatedesc()
+
+/obj/structure/disposalpipe/tagger/transfer(obj/structure/disposalholder/H)
+ if(sort_tag)
+ if(partial)
+ H.setpartialtag(sort_tag)
+ else
+ H.settag(sort_tag)
+ return ..()
+
+/obj/structure/disposalpipe/tagger/partial //needs two passes to tag
+ name = "partial package tagger"
+ icon_state = "pipe-tagger-partial"
+ partial = 1
diff --git a/code/modules/recycling/disposal_trunk.dm b/code/modules/recycling/disposal_trunk.dm
new file mode 100644
index 0000000000..947e9c6f83
--- /dev/null
+++ b/code/modules/recycling/disposal_trunk.dm
@@ -0,0 +1,62 @@
+//a trunk joining to a disposal bin or outlet on the same turf
+/obj/structure/disposalpipe/trunk
+ icon_state = "pipe-t"
+
+/obj/structure/disposalpipe/trunk/Initialize(mapload)
+ ..()
+ dpdir = dir
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/structure/disposalpipe/trunk/LateInitialize()
+ update()
+
+// Override attackby so we disallow trunkremoval when somethings ontop
+/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user)
+ //Disposal constructors
+ var/turf/T = get_turf(src)
+ var/obj/structure/disposalconstruct/C = locate() in T
+ if(C && 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.")
+
+// would transfer to next pipe segment, but we are in a trunk
+// if not entering from disposal bin,
+// transfer to linked object (outlet or bin)
+/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H)
+ 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
+
+ return null
+
+// nextdir
+/obj/structure/disposalpipe/trunk/nextdir(fromdir)
+ if(fromdir == DOWN)
+ return dir
+ else
+ return 0
diff --git a/code/modules/recycling/disposal_vr.dm b/code/modules/recycling/disposal_vr.dm
deleted file mode 100644
index 292fff7fd6..0000000000
--- a/code/modules/recycling/disposal_vr.dm
+++ /dev/null
@@ -1,19 +0,0 @@
-/obj/machinery/disposal/wall/cleaner
- name = "resleeving equipment deposit"
- desc = "Automatically cleans and transports items to the local resleeving facilities."
- icon = 'icons/obj/pipes/disposal_vr.dmi'
- icon_state = "bluewall"
-
-/obj/machinery/disposal/wall/cleaner/flush()
- flick("[icon_state]-flush", src)
- for(var/obj/item/storage/i in src)
- if(istype(i, /obj/item/storage))
- var/list/storage_items = i.return_inv()
-
- for(var/obj/item/item in storage_items)
- item.wash(CLEAN_WASH)
-
- for(var/obj/item/i in src)
- if(istype(i, /obj/item))
- i.wash(CLEAN_WASH)
- . = ..()
diff --git a/code/modules/recycling/packagewrap.dm b/code/modules/recycling/packagewrap.dm
new file mode 100644
index 0000000000..be914f5e0d
--- /dev/null
+++ b/code/modules/recycling/packagewrap.dm
@@ -0,0 +1,97 @@
+/obj/item/packageWrap
+ name = "package wrapper"
+ desc = "Like wrapping paper, but less festive."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "deliveryPaper"
+ w_class = ITEMSIZE_NORMAL
+ var/amount = 25.0
+ drop_sound = 'sound/items/drop/wrapper.ogg'
+
+/obj/item/packageWrap/afterattack(obj/target, mob/user, proximity)
+ if(!proximity) return
+ if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete
+ return
+ if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \
+ || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag))
+ return
+ if(target.anchored)
+ return
+ if(!isturf(target.loc)) //no wrapping things inside other things, just breaks things, put it on the ground first.
+ return
+ if(user in target) //no wrapping closets that you are inside - it's not physically possible
+ return
+
+ user.attack_log += text("\[[time_stamp()]\] [span_blue("Has used [src.name] on \ref[target]")]")
+
+ if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box)))
+ var/obj/item/O = target
+ if (src.amount > 1)
+ var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
+ if(!istype(O.loc, /turf))
+ if(user.client)
+ user.client.screen -= O
+ P.wrapped = O
+ O.forceMove(P)
+ P.w_class = O.w_class
+ var/i = round(O.w_class)
+ if(i in list(1,2,3,4,5))
+ P.icon_state = "deliverycrate[i]"
+ switch(i)
+ if(1) P.name = "tiny parcel"
+ if(3) P.name = "normal-sized parcel"
+ if(4) P.name = "large parcel"
+ if(5) P.name = "huge parcel"
+ if(i < 1)
+ P.icon_state = "deliverycrate1"
+ P.name = "tiny parcel"
+ if(i > 5)
+ P.icon_state = "deliverycrate5"
+ P.name = "huge parcel"
+ P.add_fingerprint(user)
+ O.add_fingerprint(user)
+ src.add_fingerprint(user)
+ src.amount -= 1
+ user.visible_message("\The [user] wraps \a [target] with \a [src].",\
+ span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
+ "You hear someone taping paper around a small object.")
+ playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
+ else if (istype(target, /obj/structure/closet/crate))
+ var/obj/structure/closet/crate/O = target
+ if (src.amount > 3 && !O.opened)
+ var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
+ P.icon_state = "deliverycrate"
+ P.wrapped = O
+ O.loc = P
+ src.amount -= 3
+ user.visible_message("\The [user] wraps \a [target] with \a [src].",\
+ span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
+ "You hear someone taping paper around a large object.")
+ playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
+ else if(src.amount < 3)
+ to_chat(user, span_warning("You need more paper."))
+ else if (istype (target, /obj/structure/closet))
+ var/obj/structure/closet/O = target
+ if (src.amount > 3 && !O.opened)
+ var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
+ P.wrapped = O
+ O.sealed = 1
+ O.loc = P
+ src.amount -= 3
+ user.visible_message("\The [user] wraps \a [target] with \a [src].",\
+ span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
+ "You hear someone taping paper around a large object.")
+ playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
+ else if(src.amount < 3)
+ to_chat(user, span_warning("You need more paper."))
+ else
+ to_chat(user, span_blue("The object you are trying to wrap is unsuitable for the sorting machinery!"))
+ if (src.amount <= 0)
+ new /obj/item/c_tube( src.loc )
+ qdel(src)
+ return
+ return
+
+/obj/item/packageWrap/examine(mob/user)
+ . = ..()
+ if(get_dist(user, src) <= 0)
+ . += span_blue("There are [amount] units of package wrap left!")
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 1258a73f37..d7536808b2 100755
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -1,404 +1,10 @@
-/obj/structure/bigDelivery
- desc = "A big wrapped package."
- name = "large parcel"
- icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit
- icon_state = "deliverycloset"
- var/obj/wrapped = null
- density = TRUE
- var/sortTag = null
- flags = NOBLUDGEON
- mouse_drag_pointer = MOUSE_ACTIVE_POINTER
- var/examtext = null
- var/nameset = 0
- var/label_y
- var/label_x
- var/tag_x
-
-/obj/structure/bigDelivery/attack_hand(mob/user as mob)
- unwrap()
-
-/obj/structure/bigDelivery/proc/unwrap()
- playsound(src, 'sound/items/package_unwrap.ogg', 50, 1)
- // Destroy will drop our wrapped object on the turf, so let it.
- qdel(src)
-
-/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob)
- if(istype(W, /obj/item/destTagger))
- var/obj/item/destTagger/O = W
- if(O.currTag)
- if(src.sortTag != O.currTag)
- to_chat(user, span_notice("You have labeled the destination as [O.currTag]."))
- if(!src.sortTag)
- src.sortTag = O.currTag
- update_icon()
- else
- src.sortTag = O.currTag
- playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
- else
- to_chat(user, span_warning("The package is already labeled for [O.currTag]."))
- else
- to_chat(user, span_warning("You need to set a destination first!"))
-
- else if(istype(W, /obj/item/pen))
- switch(tgui_alert(user, "What would you like to alter?","Select Alteration",list("Title","Description","Cancel")))
- if("Title")
- var/str = sanitizeSafe(tgui_input_text(user,"Label text?","Set label","", MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
- if(!str || !length(str))
- to_chat(user, span_warning(" Invalid text."))
- return
- user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
- span_notice("You title \the [src]: \"[str]\""),\
- "You hear someone scribbling a note.")
- playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
- name = "[name] ([str])"
- if(!examtext && !nameset)
- nameset = 1
- update_icon()
- else
- nameset = 1
- if("Description")
- var/str = tgui_input_text(user,"Label text?","Set label","", MAX_MESSAGE_LEN)
- if(!str || !length(str))
- to_chat(user, span_red("Invalid text."))
- return
- if(!examtext && !nameset)
- examtext = str
- update_icon()
- else
- examtext = str
- user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\
- span_notice("You label \the [src]: \"[examtext]\""),\
- "You hear someone scribbling a note.")
- playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
- return
-
-/obj/structure/bigDelivery/update_icon()
- cut_overlays()
- if(nameset || examtext)
- var/image/I = new/image('icons/obj/storage.dmi',"delivery_label")
- if(icon_state == "deliverycloset")
- I.pixel_x = 2
- if(label_y == null)
- label_y = rand(-6, 11)
- I.pixel_y = label_y
- else if(icon_state == "deliverycrate")
- if(label_x == null)
- label_x = rand(-8, 6)
- I.pixel_x = label_x
- I.pixel_y = -3
- add_overlay(I)
- if(src.sortTag)
- var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag")
- if(icon_state == "deliverycloset")
- if(tag_x == null)
- tag_x = rand(-2, 3)
- I.pixel_x = tag_x
- I.pixel_y = 9
- else if(icon_state == "deliverycrate")
- if(tag_x == null)
- tag_x = rand(-8, 6)
- I.pixel_x = tag_x
- I.pixel_y = -3
- add_overlay(I)
-
-/obj/structure/bigDelivery/examine(mob/user)
- . = ..()
- if(get_dist(user, src) <= 4)
- if(sortTag)
- . += span_notice("It is labeled \"[sortTag]\"")
- if(examtext)
- . += span_notice("It has a note attached which reads, \"[examtext]\"")
-
-/obj/item/smallDelivery
- desc = "A small wrapped package."
- name = "small parcel"
- icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit
- icon_state = "deliverycrate3"
- drop_sound = 'sound/items/drop/cardboardbox.ogg'
- pickup_sound = 'sound/items/pickup/cardboardbox.ogg'
- var/obj/item/wrapped = null
- var/sortTag = null
- var/examtext = null
- var/nameset = 0
- var/tag_x
-
-/obj/item/smallDelivery/attack_self(mob/user as mob)
- if (src.wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
- wrapped.loc = user.loc
- if(ishuman(user))
- user.put_in_hands(wrapped)
- else
- wrapped.loc = get_turf(src)
-
- qdel(src)
- return
-
-/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob)
- if(istype(W, /obj/item/destTagger))
- var/obj/item/destTagger/O = W
- if(O.currTag)
- if(src.sortTag != O.currTag)
- to_chat(user, span_notice("You have labeled the destination as [O.currTag]."))
- if(!src.sortTag)
- src.sortTag = O.currTag
- update_icon()
- else
- src.sortTag = O.currTag
- playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
- else
- to_chat(user, span_warning("The package is already labeled for [O.currTag]."))
- else
- to_chat(user, span_warning("You need to set a destination first!"))
-
- else if(istype(W, /obj/item/pen))
- switch(tgui_alert(user, "What would you like to alter?","Select Alteration",list("Title","Description","Cancel")))
- if("Title")
- var/str = sanitizeSafe(tgui_input_text(user,"Label text?","Set label","", MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
- if(!str || !length(str))
- to_chat(user, span_warning(" Invalid text."))
- return
- user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
- span_notice("You title \the [src]: \"[str]\""),\
- "You hear someone scribbling a note.")
- playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
- name = "[name] ([str])"
- if(!examtext && !nameset)
- nameset = 1
- update_icon()
- else
- nameset = 1
-
- if("Description")
- var/str = tgui_input_text(user,"Label text?","Set label","", MAX_MESSAGE_LEN)
- if(!str || !length(str))
- to_chat(user, span_red("Invalid text."))
- return
- if(!examtext && !nameset)
- examtext = str
- update_icon()
- else
- examtext = str
- user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\
- span_notice("You label \the [src]: \"[examtext]\""),\
- "You hear someone scribbling a note.")
- playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20)
- return
-
-/obj/item/smallDelivery/update_icon()
- cut_overlays()
- if((nameset || examtext) && icon_state != "deliverycrate1")
- var/image/I = new/image('icons/obj/storage.dmi',"delivery_label")
- if(icon_state == "deliverycrate5")
- I.pixel_y = -1
- add_overlay(I)
- if(src.sortTag)
- var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag")
- switch(icon_state)
- if("deliverycrate1")
- I.pixel_y = -5
- if("deliverycrate2")
- I.pixel_y = -2
- if("deliverycrate3")
- I.pixel_y = 0
- if("deliverycrate4")
- if(tag_x == null)
- tag_x = rand(0,5)
- I.pixel_x = tag_x
- I.pixel_y = 3
- if("deliverycrate5")
- I.pixel_y = -3
- add_overlay(I)
-
-/obj/item/smallDelivery/examine(mob/user)
- . = ..()
- if(get_dist(user, src) <= 4)
- if(sortTag)
- . += span_notice("It is labeled \"[sortTag]\"")
- if(examtext)
- . += span_notice("It has a note attached which reads, \"[examtext]\"")
-
-/obj/item/packageWrap
- name = "package wrapper"
- desc = "Like wrapping paper, but less festive."
- icon = 'icons/obj/items.dmi'
- icon_state = "deliveryPaper"
- w_class = ITEMSIZE_NORMAL
- var/amount = 25.0
- drop_sound = 'sound/items/drop/wrapper.ogg'
-
-
-/obj/item/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity)
- if(!proximity) return
- if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete
- return
- if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \
- || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag))
- return
- if(target.anchored)
- return
- if(!isturf(target.loc)) //no wrapping things inside other things, just breaks things, put it on the ground first.
- return
- if(user in target) //no wrapping closets that you are inside - it's not physically possible
- return
-
- user.attack_log += text("\[[time_stamp()]\] [span_blue("Has used [src.name] on \ref[target]")]")
-
-
- if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box)))
- var/obj/item/O = target
- if (src.amount > 1)
- var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
- if(!istype(O.loc, /turf))
- if(user.client)
- user.client.screen -= O
- P.wrapped = O
- O.forceMove(P)
- P.w_class = O.w_class
- var/i = round(O.w_class)
- if(i in list(1,2,3,4,5))
- P.icon_state = "deliverycrate[i]"
- switch(i)
- if(1) P.name = "tiny parcel"
- if(3) P.name = "normal-sized parcel"
- if(4) P.name = "large parcel"
- if(5) P.name = "huge parcel"
- if(i < 1)
- P.icon_state = "deliverycrate1"
- P.name = "tiny parcel"
- if(i > 5)
- P.icon_state = "deliverycrate5"
- P.name = "huge parcel"
- P.add_fingerprint(user)
- O.add_fingerprint(user)
- src.add_fingerprint(user)
- src.amount -= 1
- user.visible_message("\The [user] wraps \a [target] with \a [src].",\
- span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
- "You hear someone taping paper around a small object.")
- playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
- else if (istype(target, /obj/structure/closet/crate))
- var/obj/structure/closet/crate/O = target
- if (src.amount > 3 && !O.opened)
- var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
- P.icon_state = "deliverycrate"
- P.wrapped = O
- O.loc = P
- src.amount -= 3
- user.visible_message("\The [user] wraps \a [target] with \a [src].",\
- span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
- "You hear someone taping paper around a large object.")
- playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
- else if(src.amount < 3)
- to_chat(user, span_warning("You need more paper."))
- else if (istype (target, /obj/structure/closet))
- var/obj/structure/closet/O = target
- if (src.amount > 3 && !O.opened)
- var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
- P.wrapped = O
- O.sealed = 1
- O.loc = P
- src.amount -= 3
- user.visible_message("\The [user] wraps \a [target] with \a [src].",\
- span_notice("You wrap \the [target], leaving [amount] units of paper on \the [src]."),\
- "You hear someone taping paper around a large object.")
- playsound(src, 'sound/items/package_wrap.ogg', 50, 1)
- else if(src.amount < 3)
- to_chat(user, span_warning("You need more paper."))
- else
- to_chat(user, span_blue("The object you are trying to wrap is unsuitable for the sorting machinery!"))
- if (src.amount <= 0)
- new /obj/item/c_tube( src.loc )
- qdel(src)
- return
- return
-
-/obj/item/packageWrap/examine(mob/user)
- . = ..()
- if(get_dist(user, src) <= 0)
- . += span_blue("There are [amount] units of package wrap left!")
-
-/obj/structure/bigDelivery/Destroy()
- if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
- wrapped.forceMove(get_turf(src))
- if(istype(wrapped, /obj/structure/closet))
- var/obj/structure/closet/O = wrapped
- O.sealed = 0
- wrapped = null
- var/turf/T = get_turf(src)
- for(var/atom/movable/AM in contents)
- AM.forceMove(T)
- return ..()
-
-/obj/item/destTagger
- name = "destination tagger"
- desc = "Used to set the destination of properly wrapped packages."
- icon = 'icons/obj/device.dmi'
- icon_state = "dest_tagger"
- var/currTag = 0
-
- w_class = ITEMSIZE_SMALL
- item_state = "electronic"
- slot_flags = SLOT_BELT
- pickup_sound = 'sound/items/pickup/device.ogg'
- drop_sound = 'sound/items/drop/device.ogg'
-
-/obj/item/destTagger/tgui_state(mob/user)
- return GLOB.tgui_inventory_state
-
-/obj/item/destTagger/tgui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "DestinationTagger", name)
- ui.open()
-
-/obj/item/destTagger/tgui_static_data(mob/user)
- var/list/data = ..()
- var/list/taggers = list()
- var/list/tagger_levels = list()
- for(var/tag in GLOB.tagger_locations)
- var/z_level = GLOB.tagger_locations[tag]
- taggers += list(list("tag" = tag, "level" = z_level))
- tagger_levels += list(list("z" = z_level, "location" = using_map.get_zlevel_name(z_level)))
- data["taggerLevels"] = tagger_levels
- data["taggerLocs"] = taggers
-
- return data
-
-/obj/item/destTagger/tgui_data(mob/user, datum/tgui/ui)
- var/list/data = ..()
-
- data["currTag"] = currTag
-
- return data
-
-/obj/item/destTagger/attack_self(mob/user as mob)
- tgui_interact(user)
-
-/obj/item/destTagger/tgui_act(action, params, datum/tgui/ui)
- if(..())
- return TRUE
- add_fingerprint(ui.user)
- switch(action)
- if("set_tag")
- var/new_tag = params["tag"]
- if(!(new_tag in GLOB.tagger_locations))
- return FALSE
- currTag = new_tag
- . = TRUE
-
/obj/machinery/disposal/deliveryChute
name = "Delivery chute"
desc = "A chute for big and small packages alike!"
density = TRUE
icon_state = "intake"
-
- var/c_mode = 0
-
-/obj/machinery/disposal/deliveryChute/Initialize(mapload)
- . = ..()
- trunk = locate() in src.loc
- if(trunk)
- trunk.linked = src // link the pipe trunk to self
+ stat_tracking = FALSE
+ var/c_mode = FALSE
/obj/machinery/disposal/deliveryChute/interact()
return
@@ -418,13 +24,9 @@
if(WEST)
if(AM.loc.x != src.loc.x-1) return
- if(istype(AM, /obj))
- var/obj/O = AM
- O.loc = src
- else if(istype(AM, /mob))
- var/mob/M = AM
- M.loc = src
- src.flush()
+ if(isobj(AM) || ismob(AM))
+ AM.forceMove(src)
+ flush()
/obj/machinery/disposal/deliveryChute/hitby(atom/movable/AM)
if(!QDELETED(AM) || (istype(AM, /obj/item) || istype(AM, /mob/living)) && !istype(AM, /obj/item/projectile))
@@ -438,29 +40,7 @@
if(WEST)
if(AM.loc.x != src.loc.x-1) return ..()
AM.forceMove(src)
- src.flush()
-
-/obj/machinery/disposal/deliveryChute/flush()
- flushing = 1
- flick("intake-closing", src)
- var/obj/structure/disposalholder/H = new() // virtual holder object which actually
- // travels through the pipes.
- air_contents = new() // new empty gas resv.
-
- sleep(10)
- playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0)
- sleep(5) // wait for animation to finish
-
- H.init(src) // copy the contents of disposer to holder
-
- H.start(src) // start the holder processing movement
- flushing = 0
- // now reset disposal state
- flush = 0
- if(mode == 2) // if was ready,
- mode = 1 // switch to charging
- update()
- return
+ flush()
/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user)
if(!I || !user)
@@ -471,7 +51,7 @@
playsound(src, I.usesound, 50, 1)
to_chat(user, "You [c_mode ? "remove" : "attach"] the screws around the power connection.")
return
- if(I.has_tool_quality(TOOL_WELDER) && c_mode==1)
+ if(I.has_tool_quality(TOOL_WELDER) && c_mode == TRUE)
var/obj/item/weldingtool/W = I.get_welder()
if(!W.remove_fuel(0,user))
to_chat(user, "You need more welding fuel to complete this task.")
@@ -488,8 +68,3 @@
C.density = TRUE
qdel(src)
return
-
-/obj/machinery/disposal/deliveryChute/Destroy()
- if(trunk)
- trunk.linked = null
- . = ..()
diff --git a/icons/obj/pipes/disposal.dmi b/icons/obj/pipes/disposal.dmi
index a20b8c65c3..f242efe8fe 100644
Binary files a/icons/obj/pipes/disposal.dmi and b/icons/obj/pipes/disposal.dmi differ
diff --git a/icons/obj/pipes/disposal_vr.dmi b/icons/obj/pipes/disposal_vr.dmi
deleted file mode 100644
index 71c05c2871..0000000000
Binary files a/icons/obj/pipes/disposal_vr.dmi and /dev/null differ
diff --git a/vorestation.dme b/vorestation.dme
index 650bfd3856..62bd96455c 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -618,6 +618,7 @@
#include "code\datums\components\disabilities\pollen.dm"
#include "code\datums\components\disabilities\rotting.dm"
#include "code\datums\components\disabilities\tourettes.dm"
+#include "code\datums\components\machinery\disposal_connection.dm"
#include "code\datums\components\materials\material_container.dm"
#include "code\datums\components\materials\remote_materials.dm"
#include "code\datums\components\reagent_hose\connector.dm"
@@ -4212,9 +4213,17 @@
#include "code\modules\reagents\reagents\vore_vr.dm"
#include "code\modules\reagents\reagents\withdrawl.dm"
#include "code\modules\recycling\conveyor2.dm"
+#include "code\modules\recycling\destination_tagger.dm"
#include "code\modules\recycling\disposal-construction.dm"
#include "code\modules\recycling\disposal.dm"
-#include "code\modules\recycling\disposal_vr.dm"
+#include "code\modules\recycling\disposal_holder.dm"
+#include "code\modules\recycling\disposal_junctions.dm"
+#include "code\modules\recycling\disposal_machines.dm"
+#include "code\modules\recycling\disposal_mail.dm"
+#include "code\modules\recycling\disposal_outlet.dm"
+#include "code\modules\recycling\disposal_tagger.dm"
+#include "code\modules\recycling\disposal_trunk.dm"
+#include "code\modules\recycling\packagewrap.dm"
#include "code\modules\recycling\recycling.dm"
#include "code\modules\recycling\sortingmachinery.dm"
#include "code\modules\refinery\core\industrial_reagent_filter.dm"