From c8cef909f782df0f10e6df1bbb7da45b4cd8d3bd Mon Sep 17 00:00:00 2001
From: silicons <2003111+silicons@users.noreply.github.com>
Date: Tue, 3 Sep 2024 21:32:17 -0400
Subject: [PATCH] preliminary reorganization of conveyors, disposals,
packaging, etc (#6706)
factorio gaming
---------
Co-authored-by: LordME <58342752+TheLordME@users.noreply.github.com>
---
citadel.dme | 26 +-
code/__DEFINES/turfs/turfs.dm | 12 +
code/modules/industry/README.md | 9 +
.../conveyors/conveyor.dm} | 195 +--
.../industry/conveyors/conveyor_switch.dm | 128 ++
code/modules/industry/destination_tagger.dm | 45 +
code/modules/industry/disposals/disposal.dm | 28 +
.../industry/disposals/disposal/chute.dm | 513 ++++++
.../industry/disposals/disposal/intake.dm | 105 ++
.../industry/disposals/disposal/outlet.dm | 74 +
.../disposals/disposal_frame.dm} | 0
.../industry/disposals/disposal_holder.dm | 150 ++
.../industry/disposals/disposal_pipe.dm | 265 +++
.../disposals/disposal_pipe/broken.dm | 13 +
.../industry/disposals/disposal_pipe/down.dm | 48 +
.../disposals/disposal_pipe/junction.dm | 45 +
.../disposals/disposal_pipe/segment.dm | 10 +
.../disposals/disposal_pipe/sortjunction.dm | 116 ++
.../disposals/disposal_pipe/tagger.dm | 50 +
.../industry/disposals/disposal_pipe/trunk.dm | 99 ++
.../industry/disposals/disposal_pipe/up.dm | 48 +
code/modules/industry/package_wrapper.dm | 93 +
.../modules/industry/packages/large_parcel.dm | 118 ++
.../modules/industry/packages/small_parcel.dm | 107 ++
code/modules/mining/machine_unloading.dm | 2 +-
code/modules/recycling/disposal.dm | 1496 -----------------
code/modules/recycling/sortingmachinery.dm | 469 ------
27 files changed, 2134 insertions(+), 2130 deletions(-)
create mode 100644 code/__DEFINES/turfs/turfs.dm
create mode 100644 code/modules/industry/README.md
rename code/modules/{recycling/conveyor2.dm => industry/conveyors/conveyor.dm} (53%)
create mode 100644 code/modules/industry/conveyors/conveyor_switch.dm
create mode 100644 code/modules/industry/destination_tagger.dm
create mode 100644 code/modules/industry/disposals/disposal.dm
create mode 100644 code/modules/industry/disposals/disposal/chute.dm
create mode 100644 code/modules/industry/disposals/disposal/intake.dm
create mode 100644 code/modules/industry/disposals/disposal/outlet.dm
rename code/modules/{recycling/disposal-construction.dm => industry/disposals/disposal_frame.dm} (100%)
create mode 100644 code/modules/industry/disposals/disposal_holder.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/broken.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/down.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/junction.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/segment.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/sortjunction.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/tagger.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/trunk.dm
create mode 100644 code/modules/industry/disposals/disposal_pipe/up.dm
create mode 100644 code/modules/industry/package_wrapper.dm
create mode 100644 code/modules/industry/packages/large_parcel.dm
create mode 100644 code/modules/industry/packages/small_parcel.dm
delete mode 100644 code/modules/recycling/disposal.dm
delete mode 100644 code/modules/recycling/sortingmachinery.dm
diff --git a/citadel.dme b/citadel.dme
index 1fc66a0118f..faa460e3c9a 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -338,6 +338,7 @@
#include "code\__DEFINES\traits\sources.dm"
#include "code\__DEFINES\traits\unsorted.dm"
#include "code\__DEFINES\turfs\change_turf.dm"
+#include "code\__DEFINES\turfs\turfs.dm"
#include "code\__DEFINES\turfs\type_generation.dm"
#include "code\__HELPERS\_auxtools_api.dm"
#include "code\__HELPERS\_global_objects.dm"
@@ -2931,6 +2932,27 @@
#include "code\modules\hydroponics\trays\tray_update_icons.dm"
#include "code\modules\identification\identification.dm"
#include "code\modules\identification\item_procs.dm"
+#include "code\modules\industry\destination_tagger.dm"
+#include "code\modules\industry\package_wrapper.dm"
+#include "code\modules\industry\conveyors\conveyor.dm"
+#include "code\modules\industry\conveyors\conveyor_switch.dm"
+#include "code\modules\industry\disposals\disposal.dm"
+#include "code\modules\industry\disposals\disposal_frame.dm"
+#include "code\modules\industry\disposals\disposal_holder.dm"
+#include "code\modules\industry\disposals\disposal_pipe.dm"
+#include "code\modules\industry\disposals\disposal\chute.dm"
+#include "code\modules\industry\disposals\disposal\intake.dm"
+#include "code\modules\industry\disposals\disposal\outlet.dm"
+#include "code\modules\industry\disposals\disposal_pipe\broken.dm"
+#include "code\modules\industry\disposals\disposal_pipe\down.dm"
+#include "code\modules\industry\disposals\disposal_pipe\junction.dm"
+#include "code\modules\industry\disposals\disposal_pipe\segment.dm"
+#include "code\modules\industry\disposals\disposal_pipe\sortjunction.dm"
+#include "code\modules\industry\disposals\disposal_pipe\tagger.dm"
+#include "code\modules\industry\disposals\disposal_pipe\trunk.dm"
+#include "code\modules\industry\disposals\disposal_pipe\up.dm"
+#include "code\modules\industry\packages\large_parcel.dm"
+#include "code\modules\industry\packages\small_parcel.dm"
#include "code\modules\instruments\instrument_data\_instrument_data.dm"
#include "code\modules\instruments\instrument_data\_instrument_key.dm"
#include "code\modules\instruments\instrument_data\brass.dm"
@@ -4572,10 +4594,6 @@
#include "code\modules\reagents\reagent_containers\syringes_vr.dm"
#include "code\modules\reagents\reagent_containers\unidentified_hypospray.dm"
#include "code\modules\reagents\reagent_containers\glass\bottle_vr.dm"
-#include "code\modules\recycling\conveyor2.dm"
-#include "code\modules\recycling\disposal-construction.dm"
-#include "code\modules\recycling\disposal.dm"
-#include "code\modules\recycling\sortingmachinery.dm"
#include "code\modules\research\design.dm"
#include "code\modules\research\research.dm"
#include "code\modules\research\designs\ai_holders.dm"
diff --git a/code/__DEFINES/turfs/turfs.dm b/code/__DEFINES/turfs/turfs.dm
new file mode 100644
index 00000000000..5ec000ffce9
--- /dev/null
+++ b/code/__DEFINES/turfs/turfs.dm
@@ -0,0 +1,12 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2024 Citadel Station Developers *//
+
+/// Turf crowding limit.
+///
+/// Modules that move a lot of objects should respect this,
+/// as performance falls quadratically as turfs get more full.
+#define TURF_CROWDING_SOFT_LIMIT 15
+/// Turf catastrophic crowding limit.
+///
+/// Modules that move a lot of objects should flat out refuse to operate on more objects than this.
+#define TURF_CROWDING_HARD_LIMIT 75
diff --git a/code/modules/industry/README.md b/code/modules/industry/README.md
new file mode 100644
index 00000000000..cbfb13208ed
--- /dev/null
+++ b/code/modules/industry/README.md
@@ -0,0 +1,9 @@
+# Industry Module
+
+tl;dr if it belongs in a game of factorio it belongs here
+
+contains:
+
+- disposals
+- conveyors
+- general package code; while this is equally belonging in cargo and here, it's used here more.
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/industry/conveyors/conveyor.dm
similarity index 53%
rename from code/modules/recycling/conveyor2.dm
rename to code/modules/industry/conveyors/conveyor.dm
index fb9373ecc1a..1f7c9b381b1 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/industry/conveyors/conveyor.dm
@@ -4,6 +4,7 @@
//conveyor2 is pretty much like the original, except it supports corners, but not diverters.
//note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards.
+// todo: main-like stacks of conveyor belts.
/obj/machinery/conveyor
icon = 'icons/obj/recycling.dmi'
@@ -26,11 +27,6 @@
var/id = "" // the control ID - must match controller ID
-/obj/machinery/conveyor/centcom_auto
- id = "round_end_belt"
-
-
-
// create a conveyor
/obj/machinery/conveyor/Initialize(mapload, newdir, on = 0)
. = ..()
@@ -52,6 +48,12 @@
component_parts += new /obj/item/stack/cable_coil(src,5)
RefreshParts()
+/obj/machinery/conveyor/examine(mob/user, dist)
+ . = ..()
+ // give a hint about catastrophic crowding
+ if(length(loc?.contents) > TURF_CROWDING_HARD_LIMIT)
+ . += SPAN_WARNING("There's far too many things on [src] for it to move!")
+
/obj/machinery/conveyor/proc/setmove()
if(operating == FORWARDS)
movedir = forwards
@@ -97,36 +99,43 @@
AM.set_glide_size(conveyor_glide_size)
icon_state = "conveyor[operating]"
- // machine process
- // move items to the target location
+// machine process
+// move items to the target location
/obj/machinery/conveyor/process(delta_time)
if(machine_stat & (BROKEN | NOPOWER))
return
if(!operating)
return
use_power(10)
- var/list/affecting = loc.contents - src // moved items will be all in loc
- addtimer(CALLBACK(src, PROC_REF(convey), affecting), 1)
+ // check catastrophic crowding
+ if(length(loc.contents) > TURF_CROWDING_HARD_LIMIT)
+ return
+ // todo: this is still kind of tick-dependent, and will result in issues
+ // todo: conveyors should be on their own subsystem that lets it run a collect-sweep cycle?
+ addtimer(CALLBACK(src, PROC_REF(convey), loc.contents.Copy()), 1)
-/obj/machinery/conveyor/proc/convey(list/affecting)
- var/turf/T = get_step(src, movedir)
- if(!T)
+/**
+ * Conveys a list of movables.
+ *
+ * * This does filter to make sure the movables in question are still in us.
+ * * This is done in a separate proc so that order of operations from process() is canonical.
+ */
+/obj/machinery/conveyor/proc/convey(list/atom/movable/to_convey)
+ var/turf/target_turf = get_step(src, movedir)
+ if(!target_turf)
return
- affecting.len = max(min(affecting.len, 150 - T.contents.len), 0)
- if(!affecting.len)
+ // limit items to soft crowding limit
+ to_convey.len = clamp(TURF_CROWDING_SOFT_LIMIT - length(target_turf.contents), 0, length(to_convey))
+ if(!length(to_convey))
return
- var/items_moved = 0
- for(var/atom/movable/A in affecting)
- if(!A.anchored)
- if(A.loc == src.loc) // prevents the object from being affected if it's not currently here.
- step(A,movedir)
- ++items_moved
- if(items_moved >= 50)
- break
-/*
- if((A.loc == loc) && A.has_gravity())
- A.ConveyorMove(movedir)
-*/
+ // move items
+ for(var/atom/movable/AM in to_convey)
+ // todo: movement force check?
+ if(AM.anchored)
+ continue
+ if(AM.loc != loc)
+ continue
+ step(AM, movedir)
// attack with item, place item on conveyor
/obj/machinery/conveyor/attackby(var/obj/item/I, mob/user)
@@ -200,137 +209,3 @@
/obj/machinery/conveyor/power_change()
..()
update()
-
-// the conveyor control switch
-//
-//
-
-/obj/machinery/conveyor_switch
-
- name = "conveyor switch"
- desc = "A conveyor control switch."
- icon = 'icons/obj/recycling.dmi'
- icon_state = "switch-off"
- var/position = 0 // 0 off, -1 reverse, 1 forward
- var/last_pos = -1 // last direction setting
- var/operated = 1 // true if just operated
-
- var/id = "" // must match conveyor IDs to control them
-
- var/list/conveyors // the list of converyors that are controlled by this switch
- anchored = 1
-
-/obj/machinery/conveyor_switch/two_way_on
- position = 1
-
-
-/obj/machinery/conveyor_switch/Initialize(mapload)
- . = ..()
- update()
- return INITIALIZE_HINT_LATELOAD
-
-/obj/machinery/conveyor_switch/LateInitialize()
- conveyors = list()
- for(var/obj/machinery/conveyor/C in GLOB.machines)
- if(C.id == id)
- conveyors += C
-
-// update the icon depending on the position
-
-/obj/machinery/conveyor_switch/proc/update()
- if(position<0)
- icon_state = "switch-rev"
- else if(position>0)
- icon_state = "switch-fwd"
- else
- icon_state = "switch-off"
-
-
-// timed process
-// if the switch changed, update the linked conveyors
-
-/obj/machinery/conveyor_switch/process(delta_time)
- if(!operated)
- return
- operated = 0
-
- for(var/obj/machinery/conveyor/C in conveyors)
- C.operating = position
- C.setmove()
-
-// attack with hand, switch position
-/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params)
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- if(position == 0)
- if(last_pos < 0)
- position = 1
- last_pos = 0
- else
- position = -1
- last_pos = 0
- else
- last_pos = position
- position = 0
-
- operated = 1
- update()
-
- // find any switches with same id as this one, and set their positions to match us
- for(var/obj/machinery/conveyor_switch/S in GLOB.machines)
- if(S.id == src.id)
- S.position = position
- S.update()
-
-/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user)
- if(default_deconstruction_screwdriver(user, I))
- return
-
- if(istype(I, /obj/item/weldingtool))
- if(panel_open)
- var/obj/item/weldingtool/WT = I
- if(!WT.remove_fuel(0, user))
- to_chat(user, "The welding tool must be on to complete this task.")
- return
- playsound(src, WT.tool_sound, 50, 1)
- if(do_after(user, 20 * WT.tool_speed))
- if(!src || !WT.isOn()) return
- to_chat(user, "You deconstruct the frame.")
- new /obj/item/stack/material/steel( src.loc, 2 )
- qdel(src)
- return
-
- if(istype(I, /obj/item/multitool))
- if(panel_open)
- var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id))
- if(!input)
- to_chat(user, "No input found. Please hang up and try your call again.")
- return
- id = input
- conveyors = list() // Clear list so they aren't double added.
- for(var/obj/machinery/conveyor/C in GLOB.machines)
- if(C.id == id)
- conveyors += C
- return
-
-/obj/machinery/conveyor_switch/oneway
- var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.)
- desc = "A conveyor control switch. It appears to only go in one direction."
-
-// attack with hand, switch position
-/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params)
- if(position == 0)
- position = convdir
- else
- position = 0
-
- operated = 1
- update()
-
- // find any switches with same id as this one, and set their positions to match us
- for(var/obj/machinery/conveyor_switch/S in GLOB.machines)
- if(S.id == src.id)
- S.position = position
- S.update()
diff --git a/code/modules/industry/conveyors/conveyor_switch.dm b/code/modules/industry/conveyors/conveyor_switch.dm
new file mode 100644
index 00000000000..6992ecf1ed2
--- /dev/null
+++ b/code/modules/industry/conveyors/conveyor_switch.dm
@@ -0,0 +1,128 @@
+/obj/machinery/conveyor_switch
+
+ name = "conveyor switch"
+ desc = "A conveyor control switch."
+ icon = 'icons/obj/recycling.dmi'
+ icon_state = "switch-off"
+ var/position = 0 // 0 off, -1 reverse, 1 forward
+ var/last_pos = -1 // last direction setting
+ var/operated = 1 // true if just operated
+
+ var/id = "" // must match conveyor IDs to control them
+
+ var/list/conveyors // the list of converyors that are controlled by this switch
+ anchored = 1
+
+/obj/machinery/conveyor_switch/two_way_on
+ position = 1
+
+
+/obj/machinery/conveyor_switch/Initialize(mapload)
+ . = ..()
+ update()
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/conveyor_switch/LateInitialize()
+ conveyors = list()
+ for(var/obj/machinery/conveyor/C in GLOB.machines)
+ if(C.id == id)
+ conveyors += C
+
+// update the icon depending on the position
+
+/obj/machinery/conveyor_switch/proc/update()
+ if(position<0)
+ icon_state = "switch-rev"
+ else if(position>0)
+ icon_state = "switch-fwd"
+ else
+ icon_state = "switch-off"
+
+// timed process
+// if the switch changed, update the linked conveyors
+
+/obj/machinery/conveyor_switch/process(delta_time)
+ if(!operated)
+ return
+ operated = 0
+
+ for(var/obj/machinery/conveyor/C in conveyors)
+ C.operating = position
+ C.setmove()
+
+// attack with hand, switch position
+/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params)
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ if(position == 0)
+ if(last_pos < 0)
+ position = 1
+ last_pos = 0
+ else
+ position = -1
+ last_pos = 0
+ else
+ last_pos = position
+ position = 0
+
+ operated = 1
+ update()
+
+ // find any switches with same id as this one, and set their positions to match us
+ for(var/obj/machinery/conveyor_switch/S in GLOB.machines)
+ if(S.id == src.id)
+ S.position = position
+ S.update()
+
+/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user)
+ if(default_deconstruction_screwdriver(user, I))
+ return
+
+ if(istype(I, /obj/item/weldingtool))
+ if(panel_open)
+ var/obj/item/weldingtool/WT = I
+ if(!WT.remove_fuel(0, user))
+ to_chat(user, "The welding tool must be on to complete this task.")
+ return
+ playsound(src, WT.tool_sound, 50, 1)
+ if(do_after(user, 20 * WT.tool_speed))
+ if(!src || !WT.isOn()) return
+ to_chat(user, "You deconstruct the frame.")
+ new /obj/item/stack/material/steel( src.loc, 2 )
+ qdel(src)
+ return
+
+ if(istype(I, /obj/item/multitool))
+ if(panel_open)
+ var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id))
+ if(!input)
+ to_chat(user, "No input found. Please hang up and try your call again.")
+ return
+ id = input
+ conveyors = list() // Clear list so they aren't double added.
+ for(var/obj/machinery/conveyor/C in GLOB.machines)
+ if(C.id == id)
+ conveyors += C
+ return
+
+/obj/machinery/conveyor_switch/oneway
+ var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.)
+ desc = "A conveyor control switch. It appears to only go in one direction."
+
+// attack with hand, switch position
+/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params)
+ if(position == 0)
+ position = convdir
+ else
+ position = 0
+
+ operated = 1
+ update()
+
+ // find any switches with same id as this one, and set their positions to match us
+ for(var/obj/machinery/conveyor_switch/S in GLOB.machines)
+ if(S.id == src.id)
+ S.position = position
+ S.update()
diff --git a/code/modules/industry/destination_tagger.dm b/code/modules/industry/destination_tagger.dm
new file mode 100644
index 00000000000..afa4406dde4
--- /dev/null
+++ b/code/modules/industry/destination_tagger.dm
@@ -0,0 +1,45 @@
+/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 = WEIGHT_CLASS_SMALL
+ item_state = "electronic"
+ slot_flags = SLOT_BELT
+
+/obj/item/destTagger/ui_state()
+ return GLOB.inventory_state
+
+/obj/item/destTagger/ui_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/ui_data(mob/user, datum/tgui/ui)
+ var/list/data = ..()
+
+ data["currTag"] = currTag
+ data["taggerLocs"] = GLOB.tagger_locations
+
+ return data
+
+/obj/item/destTagger/attack_self(mob/user)
+ . = ..()
+ if(.)
+ return
+ ui_interact(user)
+
+/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui)
+ if(..())
+ return TRUE
+ add_fingerprint(usr)
+ 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/industry/disposals/disposal.dm b/code/modules/industry/disposals/disposal.dm
new file mode 100644
index 00000000000..d449a288ed2
--- /dev/null
+++ b/code/modules/industry/disposals/disposal.dm
@@ -0,0 +1,28 @@
+// todo: /machinery/disposal that trunks connect to
+
+// called when movable is expelled from a disposal pipe or outlet
+// by default does nothing, override for special behaviour
+/atom/movable/proc/pipe_eject(direction)
+ return
+
+// check if mob has client, if so restore client view on eject
+/mob/pipe_eject(direction)
+ update_perspective()
+
+/obj/effect/debris/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()
+
+ src.streak(dirs)
+
+/obj/effect/debris/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)
diff --git a/code/modules/industry/disposals/disposal/chute.dm b/code/modules/industry/disposals/disposal/chute.dm
new file mode 100644
index 00000000000..572b165c5b2
--- /dev/null
+++ b/code/modules/industry/disposals/disposal/chute.dm
@@ -0,0 +1,513 @@
+// 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
+///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added.
+#define SEND_PRESSURE (700 + ONE_ATMOSPHERE)
+///L
+#define PRESSURE_TANK_VOLUME 150
+///L/s - 4 m/s using a 15 cm by 15 cm inlet
+#define PUMP_MAX_FLOW_RATE 90
+// todo: /obj/machinery/disposal/chute
+/obj/machinery/disposal
+ name = "disposal unit"
+ desc = "A pneumatic waste disposal unit."
+ icon = 'icons/obj/pipes/disposal.dmi'
+ icon_state = "disposal"
+ atom_colouration_system = FALSE
+ anchored = TRUE
+ density = TRUE
+ pass_flags_self = ATOM_PASS_OVERHEAD_THROW
+ var/datum/gas_mixture/air_contents // internal reservoir
+ var/mode = 1 // item mode 0=off 1=charging 2=charged
+ var/flush = FALSE // true if flush handle is pulled
+ var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk
+ var/flushing = FALSE // true if flushing in progress
+ var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush
+ var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush.
+ var/last_sound = 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, newdir)
+ . = ..()
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/disposal/LateInitialize()
+ . = ..()
+ trunk = locate() in src.loc
+ if(!trunk)
+ mode = 0
+ flush = FALSE
+ 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
+ return ..()
+
+// attack by item places it in to disposal
+/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user)
+ if(user.a_intent != INTENT_HELP)
+ return ..()
+
+ . = CLICKCHAIN_DO_NOT_PROPAGATE
+
+ if(machine_stat & BROKEN || !I || !user)
+ return
+
+ add_fingerprint(user, 0, I)
+ if(mode<=0) // It's off
+ if(I.is_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.tool_sound, 50, 1)
+ to_chat(user, "You remove the screws around the power connection.")
+ return
+ else if(mode==-1)
+ mode=0
+ playsound(src, I.tool_sound, 50, 1)
+ to_chat(user, "You attach the screws around the power connection.")
+ return
+ else if(istype(I, /obj/item/weldingtool) && mode==-1)
+ if(contents.len > 0)
+ to_chat(user, "Eject the items first!")
+ return
+ var/obj/item/weldingtool/W = I
+ if(W.remove_fuel(0,user))
+ playsound(src, W.tool_sound, 100, 1)
+ to_chat(user, "You start slicing the floorweld off the disposal unit.")
+
+ if(do_after(user,20 * W.tool_speed))
+ 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 = 1
+ C.density = 1
+ 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, "You empty the bag.")
+ for(var/obj/item/O in T.contents)
+ T.obj_storage.remove(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("\The [user] empties \the [A.name] 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(usr))
+ V.show_message("[usr] starts putting [GM.name] into the disposal.", 3)
+ if(do_after(usr, 20))
+ GM.forceMove(src)
+ GM.update_perspective()
+ for (var/mob/C in viewers(src))
+ C.show_message("[GM.name] has been placed in \the [src] by [user].", 3)
+ qdel(G)
+
+ add_attack_logs(user,GM,"Disposals dunked")
+ return
+
+ if(!user.attempt_insert_item_for_installation(I, src))
+ return
+
+ to_chat(user, "You place \the [I] into \the [src].")
+ for(var/mob/M in viewers(src))
+ if(M == user)
+ continue
+ M.show_message("[user.name] places \the [I] into \the [src].", 3)
+
+ update()
+
+// mouse drop another mob or self
+//
+/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user)
+ if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !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(usr))
+ if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
+ V.show_message("[usr] starts climbing into the disposal.", 3)
+ if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
+ if(target.anchored) return
+ V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3)
+ if(!do_after(usr, 20))
+ return
+ if(target_loc != target.loc)
+ return
+ if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // 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].")
+ else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
+ 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
+ target.forceMove(src)
+ target.update_perspective()
+
+ 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)
+ user.forceMove(loc)
+ user.update_perspective()
+ update()
+ return
+
+// ai as human but can't flush
+/obj/machinery/disposal/attack_ai(mob/user as mob)
+ interact(user, 1)
+
+// human interact with machine
+/obj/machinery/disposal/attack_hand(mob/user, list/params)
+
+ if(machine_stat & BROKEN)
+ return
+
+ if(user && user.loc == src)
+ to_chat(user, "You cannot reach the controls from inside.")
+ return
+
+ // Clumsy folks can only flush it.
+ if(user.IsAdvancedToolUser(1))
+ interact(user, 0)
+ else
+ flush = !flush
+ update()
+ return
+
+// user interaction
+/obj/machinery/disposal/interact(mob/user, var/ai=0)
+
+ src.add_fingerprint(user)
+ if(machine_stat & BROKEN)
+ user.unset_machine()
+ return
+
+ var/dat = "
Waste Disposal UnitWaste Disposal Unit
"
+
+ if(!ai) // AI can't pull flush handle
+ if(flush)
+ dat += "Disposal handle: Disengage Engaged"
+ else
+ dat += "Disposal handle: Disengaged Engage"
+
+ dat += "
Eject contents
"
+
+ if(mode <= 0)
+ dat += "Pump: Off On
"
+ else if(mode == 1)
+ dat += "Pump: Off On (pressurizing)
"
+ else
+ dat += "Pump: Off On (idle)
"
+
+ var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE)
+
+ dat += "Pressure: [round(per, 1)]%
"
+
+
+ user.set_machine(src)
+ user << browse(dat, "window=disposal;size=360x170")
+ onclose(user, "disposal")
+
+// handle machine interaction
+
+/obj/machinery/disposal/Topic(href, href_list)
+ if(usr.loc == src)
+ to_chat(usr, "You cannot reach the controls from inside.")
+ return
+
+ if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1
+ to_chat(usr, "The disposal units power is disabled.")
+ return
+ if(..())
+ return
+
+ if(machine_stat & BROKEN)
+ return
+ if(usr.stat || usr.restrained() || src.flushing)
+ return
+
+ if(istype(src.loc, /turf))
+ usr.set_machine(src)
+
+ if(href_list["close"])
+ usr.unset_machine()
+ usr << browse(null, "window=disposal")
+ return
+
+ if(href_list["pump"])
+ if(text2num(href_list["pump"]))
+ mode = 1
+ else
+ mode = 0
+ update()
+
+ if(!isAI(usr))
+ if(href_list["handle"])
+ flush = text2num(href_list["handle"])
+ update()
+
+ if(href_list["eject"])
+ eject()
+ else
+ usr << browse(null, "window=disposal")
+ usr.unset_machine()
+ return
+ return
+
+// eject the contents of the disposal unit
+/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(machine_stat & BROKEN)
+ icon_state = "disposal-broken"
+ mode = 0
+ flush = 0
+ return
+
+ var/list/overlays_to_add = list()
+
+ // flush handle
+ if(flush)
+ overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle")
+
+ // only handle is shown if no power
+ if(machine_stat & NOPOWER || mode == -1)
+ add_overlay(overlays_to_add)
+ return
+
+ // check for items in disposal - occupied light
+ if(contents.len > 0)
+ overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full")
+
+ // charging and ready light
+ if(mode == 1)
+ overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge")
+ else if(mode == 2)
+ overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready")
+
+ add_overlay(overlays_to_add)
+
+// timed process
+// charge the gas reservoir and perform flush if ready
+/obj/machinery/disposal/process(delta_time)
+ if(!air_contents || (machine_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(machine_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
+
+
+ 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(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_old(target, 5, 1)
+
+ H.vent_gas(loc)
+ qdel(H)
+
+/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT)
+ if(istype(AM, /obj/item) && !istype(AM, /obj/projectile))
+ if(prob(75))
+ AM.forceMove(src)
+ visible_message("\The [AM] lands in \the [src].")
+ return COMPONENT_THROW_HIT_TERMINATE
+ else
+ visible_message("\The [AM] bounces off of \the [src]'s rim!")
+ return ..()
+ return ..()
+
+/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target)
+ if(istype(mover, /obj/projectile))
+ return TRUE
+ return ..()
+
+/obj/machinery/disposal/wall
+ name = "inset disposal unit"
+ icon_state = "wall"
+
+ density = FALSE
+
+/obj/machinery/disposal/wall/Initialize()
+ . = ..()
+
+ 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
diff --git a/code/modules/industry/disposals/disposal/intake.dm b/code/modules/industry/disposals/disposal/intake.dm
new file mode 100644
index 00000000000..6b1ba1c8172
--- /dev/null
+++ b/code/modules/industry/disposals/disposal/intake.dm
@@ -0,0 +1,105 @@
+
+// todo: /obj/machinery/disposal/chute/intake
+/obj/machinery/disposal/deliveryChute
+ name = "Delivery chute"
+ desc = "A chute for big and small packages alike!"
+ density = 1
+ icon_state = "intake"
+
+ var/c_mode = 0
+
+/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir)
+ . = ..()
+ spawn(5)
+ trunk = locate() in src.loc
+ if(trunk)
+ trunk.linked = src // link the pipe trunk to self
+
+/obj/machinery/disposal/deliveryChute/interact()
+ return
+
+/obj/machinery/disposal/deliveryChute/update()
+ return
+
+/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute
+ if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return
+ switch(dir)
+ if(NORTH)
+ if(AM.loc.y != src.loc.y+1) return
+ if(EAST)
+ if(AM.loc.x != src.loc.x+1) return
+ if(SOUTH)
+ if(AM.loc.y != src.loc.y-1) return
+ if(WEST)
+ if(AM.loc.x != src.loc.x-1) return
+
+// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36
+
+ 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()
+
+/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
+
+/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user)
+ if(!I || !user)
+ return
+
+ if(I.is_screwdriver())
+ if(c_mode==0)
+ c_mode=1
+ playsound(src.loc, I.tool_sound, 50, 1)
+ to_chat(user, "You remove the screws around the power connection.")
+ return
+ else if(c_mode==1)
+ c_mode=0
+ playsound(src.loc, I.tool_sound, 50, 1)
+ to_chat(user, "You attach the screws around the power connection.")
+ return
+ else if(istype(I, /obj/item/weldingtool) && c_mode==1)
+ var/obj/item/weldingtool/W = I
+ if(W.remove_fuel(0,user))
+ playsound(src.loc, W.tool_sound, 50, 1)
+ to_chat(user, "You start slicing the floorweld off the delivery chute.")
+ if(do_after(user,20 * W.tool_speed))
+ if(!src || !W.isOn()) return
+ to_chat(user, "You sliced the floorweld off the delivery chute.")
+ var/obj/structure/disposalconstruct/C = new (src.loc)
+ C.ptype = 8 // 8 = Delivery chute
+ C.update()
+ C.anchored = 1
+ C.density = 1
+ qdel(src)
+ return
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
+
+/obj/machinery/disposal/deliveryChute/Destroy()
+ if(trunk)
+ trunk.linked = null
+ ..()
diff --git a/code/modules/industry/disposals/disposal/outlet.dm b/code/modules/industry/disposals/disposal/outlet.dm
new file mode 100644
index 00000000000..f1a389f60ea
--- /dev/null
+++ b/code/modules/industry/disposals/disposal/outlet.dm
@@ -0,0 +1,74 @@
+// the disposal outlet machine
+// todo: /obj/machinery/disposal/outlet
+
+/obj/structure/disposaloutlet
+ name = "disposal outlet"
+ desc = "An outlet for the pneumatic disposal system."
+ icon = 'icons/obj/pipes/disposal.dmi'
+ icon_state = "outlet"
+ density = 1
+ anchored = 1
+ var/active = 0
+ var/turf/target // this will be where the output objects are 'thrown' to.
+ var/mode = 0
+
+/obj/structure/disposaloutlet/LateInitialize()
+ 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
+
+// expel the contents of the holder object, then delete it
+// called when the holder exits the outlet
+/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H)
+
+ target = get_ranged_target_turf(src, dir, 10)
+ 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_old(target, 3, 1)
+ H.vent_gas(src.loc)
+ qdel(H)
+
+/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user)
+ if(!I || !user)
+ return
+ src.add_fingerprint(user, 0, I)
+ if(I.is_screwdriver())
+ if(mode==0)
+ mode=1
+ to_chat(user, "You remove the screws around the power connection.")
+ playsound(src, I.tool_sound, 50, 1)
+ return
+ else if(mode==1)
+ mode=0
+ to_chat(user, "You attach the screws around the power connection.")
+ playsound(src, I.tool_sound, 50, 1)
+ return
+ else if(istype(I, /obj/item/weldingtool) && mode==1)
+ var/obj/item/weldingtool/W = I
+ if(W.remove_fuel(0,user))
+ playsound(src, W.tool_sound, 100, 1)
+ to_chat(user, "You start slicing the floorweld off the disposal outlet.")
+ if(do_after(user,20 * W.tool_speed))
+ 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 = 1
+ C.density = 1
+ qdel(src)
+ return
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/industry/disposals/disposal_frame.dm
similarity index 100%
rename from code/modules/recycling/disposal-construction.dm
rename to code/modules/industry/disposals/disposal_frame.dm
diff --git a/code/modules/industry/disposals/disposal_holder.dm b/code/modules/industry/disposals/disposal_holder.dm
new file mode 100644
index 00000000000..a89da28a3d2
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_holder.dm
@@ -0,0 +1,150 @@
+// 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
+
+// todo: /atom/movable/disposal_holder
+/obj/structure/disposalholder
+ invisibility = 101
+ 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.
+
+/obj/structure/disposalholder/Destroy()
+ QDEL_NULL(gas)
+ active = 0
+ return ..()
+
+// 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
+
+// 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
+ setDir(DOWN)
+ spawn(1)
+ move() // spawn off the movement process
+
+// movement process, persists while holder is moving through pipes
+/obj/structure/disposalholder/proc/move()
+ var/obj/structure/disposalpipe/last
+ // todo: while this is fucking awful?
+ 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, weapon_descriptor = "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
+
+// 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
+ M.update_perspective()
+ 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(!istype(user,/mob/living))
+ return
+
+ var/mob/living/U = user
+ if (U.stat || U.last_special <= world.time)
+ return
+ U.last_special = world.time+100
+
+ if(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
diff --git a/code/modules/industry/disposals/disposal_pipe.dm b/code/modules/industry/disposals/disposal_pipe.dm
new file mode 100644
index 00000000000..ec16874fdf6
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe.dm
@@ -0,0 +1,265 @@
+// Disposal pipes
+/// todo: /obj/structure/disposal_pipe
+/obj/structure/disposalpipe
+ icon = 'icons/obj/pipes/disposal.dmi'
+ name = "disposal pipe"
+ desc = "An underfloor disposal pipe."
+ anchored = 1
+ density = 0
+ hides_underfloor = OBJ_UNDERFLOOR_ALWAYS
+ dir = 0 // dir will contain dominant direction for junction pipes
+ plane = TURF_PLANE
+ layer = DISPOSAL_LAYER // slightly lower than wires and other pipes.
+
+ integrity = 100
+ integrity_max = 100
+
+ #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors.
+ alpha = 128 // Set for the benefit of mapping.
+ #endif
+
+ /// Bitmask of pipe directions.
+ var/dpdir = 0
+ var/sortType = ""
+ var/subtype = 0
+ // new pipe, set the icon_state as on map
+
+/obj/structure/disposalpipe/Initialize(mapload, dir)
+ . = ..()
+ base_icon_state = icon_state
+ if(!isnull(dir))
+ setDir(dir)
+
+// pipe is deleted
+// ensure if holder is present, it is expelled
+/obj/structure/disposalpipe/Destroy()
+ var/obj/structure/disposalholder/H = locate() in src
+ if(H)
+ // holder was present
+ H.active = 0
+ var/turf/T = src.loc
+ if(T.density)
+ // deleting pipe is inside a dense turf (wall)
+ // this is unlikely, but just dump out everything into the turf in case
+
+ for(var/atom/movable/AM in H)
+ AM.forceMove(T)
+ AM.pipe_eject(0)
+ qdel(H)
+ ..()
+ return
+
+ // otherwise, do normal expel from turf
+ if(H)
+ expel(H, T, 0)
+ ..()
+
+// returns the direction of the next pipe object, given the entrance dir
+// by default, returns the bitmask of remaining directions
+/obj/structure/disposalpipe/proc/nextdir(var/fromdir)
+ return dpdir & (~turn(fromdir, 180))
+
+// 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.setDir(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
+
+// update actual icon_state depending on visibility
+// if invisible, append "f" to icon_state to show faded version
+// this will be revealed if a T-scanner is used
+// if visible, use regular icon_state
+/obj/structure/disposalpipe/proc/updateicon()
+ icon_state = base_icon_state
+
+// 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)
+ if(!istype(H))
+ return
+
+ // Empty the holder if it is expelled into a dense turf.
+ // Leaving it intact and sitting in a wall is stupid.
+ if(T.density)
+ for(var/atom/movable/AM in H)
+ AM.loc = T
+ AM.pipe_eject(0)
+ 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
+
+ var/turf/target
+ if(direction) // direction is specified
+ if(istype(T, /turf/space)) // if ended in space, then range is unlimited
+ target = get_edge_target_turf(T, direction)
+ else // otherwise limit to 10 tiles
+ target = get_ranged_target_turf(T, direction, 10)
+
+ playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
+ if(H)
+ for(var/atom/movable/AM in H)
+ AM.forceMove(T)
+ AM.pipe_eject(direction)
+ spawn(1)
+ if(AM)
+ AM.throw_at_old(target, 100, 1)
+ H.vent_gas(T)
+ qdel(H)
+
+ else // no specified direction, so throw in random direction
+
+ playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
+ if(H)
+ for(var/atom/movable/AM in H)
+ 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_old(target, 5, 1)
+
+ H.vent_gas(T) // all gas vent to turf
+ qdel(H)
+
+/obj/structure/disposalpipe/deconstructed(method)
+ . = ..()
+ src.invisibility = 101 // 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
+ if(T.density)
+ // broken pipe is inside a dense turf (wall)
+ // this is unlikely, but just dump out everything into the turf in case
+
+ for(var/atom/movable/AM in H)
+ AM.forceMove(T)
+ AM.pipe_eject(0)
+ qdel(H)
+ return
+
+ // otherwise, do normal expel from turf
+ if(H)
+ expel(H, T, 0)
+ return ..()
+
+/obj/structure/disposalpipe/drop_products(method, atom/where)
+ . = ..()
+ if(method != ATOM_DECONSTRUCT_DISASSEMBLED)
+ new /obj/structure/disposalpipe/broken(where, dir)
+
+//attack by item
+//weldingtool: unfasten and convert to obj/disposalconstruct
+
+/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user)
+
+ var/turf/T = src.loc
+ if(!T.is_plating())
+ return // prevent interaction with T-scanner revealed pipes
+ src.add_fingerprint(user, 0, I)
+ if(istype(I, /obj/item/weldingtool))
+ var/obj/item/weldingtool/W = I
+
+ if(W.remove_fuel(0,user))
+ playsound(src, W.tool_sound, 50, 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
+
+// called when pipe is cut with welder
+/obj/structure/disposalpipe/proc/welded()
+
+ var/obj/structure/disposalconstruct/C = new (src.loc)
+ switch(base_icon_state)
+ if("pipe-s")
+ C.ptype = 0
+ if("pipe-c")
+ C.ptype = 1
+ if("pipe-j1")
+ C.ptype = 2
+ if("pipe-j2")
+ C.ptype = 3
+ if("pipe-y")
+ C.ptype = 4
+ if("pipe-t")
+ C.ptype = 5
+ if("pipe-j1s")
+ C.ptype = 9
+ C.sortType = sortType
+ if("pipe-j2s")
+ C.ptype = 10
+ C.sortType = sortType
+///// Z-Level stuff
+ if("pipe-u")
+ C.ptype = 11
+ if("pipe-d")
+ C.ptype = 12
+///// Z-Level stuff
+ if("pipe-tagger")
+ C.ptype = 13
+ if("pipe-tagger-partial")
+ C.ptype = 14
+ C.subtype = src.subtype
+ src.transfer_fingerprints_to(C)
+ C.setDir(dir)
+ C.density = 0
+ C.anchored = 1
+ C.update()
+
+ qdel(src)
+
+// pipe is deleted
+// ensure if holder is present, it is expelled
+/obj/structure/disposalpipe/Destroy()
+ var/obj/structure/disposalholder/H = locate() in src
+ if(H)
+ // holder was present
+ H.active = 0
+ var/turf/T = src.loc
+ if(T.density)
+ // deleting pipe is inside a dense turf (wall)
+ // this is unlikely, but just dump out everything into the turf in case
+
+ for(var/atom/movable/AM in H)
+ AM.forceMove(T)
+ AM.pipe_eject(0)
+ qdel(H)
+ ..()
+ return
+
+ // otherwise, do normal expel from turf
+ if(H)
+ expel(H, T, 0)
+ ..()
diff --git a/code/modules/industry/disposals/disposal_pipe/broken.dm b/code/modules/industry/disposals/disposal_pipe/broken.dm
new file mode 100644
index 00000000000..2914d4da38e
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/broken.dm
@@ -0,0 +1,13 @@
+// a broken pipe
+/obj/structure/disposalpipe/broken
+ icon_state = "pipe-b"
+ dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes
+ // i.e. will be treated as an empty turf
+ desc = "A broken piece of disposal pipe."
+
+// 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)
diff --git a/code/modules/industry/disposals/disposal_pipe/down.dm b/code/modules/industry/disposals/disposal_pipe/down.dm
new file mode 100644
index 00000000000..5bc47b24e8a
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/down.dm
@@ -0,0 +1,48 @@
+/obj/structure/disposalpipe/down
+ icon_state = "pipe-d"
+
+/obj/structure/disposalpipe/down/New()
+ ..()
+ dpdir = dir
+ return
+
+/obj/structure/disposalpipe/down/nextdir(fromdir)
+ var/nextdir
+ if(fromdir == 12)
+ nextdir = dir
+ else
+ nextdir = 11
+ return nextdir
+
+/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H)
+ var/nextdir = nextdir(H.dir)
+ H.dir = nextdir
+
+ var/turf/T
+ var/obj/structure/disposalpipe/P
+
+ if(nextdir == 11)
+ T = get_vertical_step(src, DOWN)
+ if(!T)
+ H.forceMove(src.loc)
+ return
+ else
+ for(var/obj/structure/disposalpipe/up/F in T)
+ P = F
+
+ else
+ T = get_step(src.loc, H.dir)
+ 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
diff --git a/code/modules/industry/disposals/disposal_pipe/junction.dm b/code/modules/industry/disposals/disposal_pipe/junction.dm
new file mode 100644
index 00000000000..0d1937f2110
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/junction.dm
@@ -0,0 +1,45 @@
+/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/New()
+ ..()
+ 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)
+
+// 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/junction/flipped //for easier and cleaner mapping
+ icon_state = "pipe-j2"
diff --git a/code/modules/industry/disposals/disposal_pipe/segment.dm b/code/modules/industry/disposals/disposal_pipe/segment.dm
new file mode 100644
index 00000000000..eb3bad08b81
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/segment.dm
@@ -0,0 +1,10 @@
+// a straight or bent segment
+/obj/structure/disposalpipe/segment
+ icon_state = "pipe-s"
+
+/obj/structure/disposalpipe/segment/New()
+ ..()
+ if(icon_state == "pipe-s")
+ dpdir = dir | turn(dir, 180)
+ else
+ dpdir = dir | turn(dir, -90)
diff --git a/code/modules/industry/disposals/disposal_pipe/sortjunction.dm b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm
new file mode 100644
index 00000000000..02b2f813936
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm
@@ -0,0 +1,116 @@
+//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/posdir = 0
+ var/negdir = 0
+ var/sortdir = 0
+
+/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/proc/updatedir()
+ posdir = dir
+ negdir = turn(posdir, 180)
+
+ if(icon_state == "pipe-j1s")
+ sortdir = turn(posdir, -90)
+ else if(icon_state == "pipe-j2s")
+ sortdir = turn(posdir, 90)
+
+ dpdir = sortdir | posdir | negdir
+
+/obj/structure/disposalpipe/sortjunction/New()
+ . = ..()
+ if(sortType) GLOB.tagger_locations |= sortType
+
+ updatedir()
+ updatename()
+ updatedesc()
+
+/obj/structure/disposalpipe/sortjunction/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
+ sortType = O.currTag
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1)
+ to_chat(user, "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 posdir, then flip around and go back to posdir
+// if coming in from sortdir, go to posdir
+/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag)
+ if(fromdir != sortdir) // probably came from the negdir
+ if(divert_check(sortTag))
+ return sortdir
+ else
+ return posdir
+ else // came from sortdir
+ // so go with the flow to positive direction
+ return posdir
+
+/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H)
+ var/nextdir = nextdir(H.dir, H.destinationTag)
+ H.setDir(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
+
+//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/industry/disposals/disposal_pipe/tagger.dm b/code/modules/industry/disposals/disposal_pipe/tagger.dm
new file mode 100644
index 00000000000..8a24e6badf7
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/tagger.dm
@@ -0,0 +1,50 @@
+/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/New()
+ . = ..()
+ dpdir = dir | turn(dir, 180)
+ if(sort_tag) GLOB.tagger_locations |= sort_tag
+ updatename()
+ updatedesc()
+
+/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.loc, 'sound/machines/twobeep.ogg', 100, 1)
+ to_chat(user, "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/industry/disposals/disposal_pipe/trunk.dm b/code/modules/industry/disposals/disposal_pipe/trunk.dm
new file mode 100644
index 00000000000..3d0b03a73a1
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/trunk.dm
@@ -0,0 +1,99 @@
+//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()
+ . = ..()
+ getlinked()
+/obj/structure/disposalpipe/trunk/proc/getlinked()
+ linked = null
+ var/obj/machinery/disposal/D = locate() in src.loc
+ if(D)
+ linked = D
+ if (!D.trunk)
+ D.trunk = src
+
+ var/obj/structure/disposaloutlet/O = locate() in src.loc
+ if(O)
+ linked = O
+ return
+
+ // Override attackby so we disallow trunkremoval when somethings ontop
+/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user)
+
+ //Disposal bins or chutes
+ /*
+ These shouldn't be required
+ var/obj/machinery/disposal/D = locate() in src.loc
+ if(D && D.anchored)
+ return
+
+ //Disposal outlet
+ var/obj/structure/disposaloutlet/O = locate() in src.loc
+ if(O && O.anchored)
+ return
+ */
+
+ //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, 0, I)
+ if(istype(I, /obj/item/weldingtool))
+ var/obj/item/weldingtool/W = I
+
+ if(W.remove_fuel(0,user))
+ playsound(src, W.tool_sound, 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
diff --git a/code/modules/industry/disposals/disposal_pipe/up.dm b/code/modules/industry/disposals/disposal_pipe/up.dm
new file mode 100644
index 00000000000..31816509527
--- /dev/null
+++ b/code/modules/industry/disposals/disposal_pipe/up.dm
@@ -0,0 +1,48 @@
+/obj/structure/disposalpipe/up
+ icon_state = "pipe-u"
+
+/obj/structure/disposalpipe/up/New()
+ ..()
+ dpdir = dir
+ return
+
+/obj/structure/disposalpipe/up/nextdir(fromdir)
+ var/nextdir
+ if(fromdir == 11)
+ nextdir = dir
+ else
+ nextdir = 12
+ return nextdir
+
+/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H)
+ var/nextdir = nextdir(H.dir)
+ H.setDir(nextdir)
+
+ var/turf/T
+ var/obj/structure/disposalpipe/P
+
+ if(nextdir == 12)
+ T = get_vertical_step(src, UP)
+ if(!T)
+ H.forceMove(loc)
+ return
+ else
+ for(var/obj/structure/disposalpipe/down/F in T)
+ P = F
+
+ else
+ T = get_step(src.loc, H.dir)
+ 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
diff --git a/code/modules/industry/package_wrapper.dm b/code/modules/industry/package_wrapper.dm
new file mode 100644
index 00000000000..6d18bf75cbe
--- /dev/null
+++ b/code/modules/industry/package_wrapper.dm
@@ -0,0 +1,93 @@
+// todo: /obj/item/package_wrapper
+/obj/item/packageWrap
+ name = "package wrapper"
+ icon = 'icons/obj/items.dmi'
+ icon_state = "deliveryPaper"
+ w_class = WEIGHT_CLASS_NORMAL
+ var/amount = 25
+
+/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params)
+ if(!(clickchain_flags & CLICKCHAIN_HAS_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(target in user)
+ return
+ if(user in target) //no wrapping closets that you are inside - it's not physically possible
+ return
+
+ user.attack_log += "\[[time_stamp()]\] Has used [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.set_weight_class(O.w_class)
+ var/i = round(O.get_weight_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(usr)
+ O.add_fingerprint(usr)
+ src.add_fingerprint(usr)
+ src.amount -= 1
+ user.visible_message("\The [user] wraps \a [target] with \a [src].",\
+ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
+ "You hear someone taping paper around a small object.")
+ 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].",\
+ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
+ "You hear someone taping paper around a large object.")
+ else if(src.amount < 3)
+ to_chat(user, "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].",\
+ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
+ "You hear someone taping paper around a large object.")
+ else if(src.amount < 3)
+ to_chat(user, "You need more paper.")
+ else
+ to_chat(user, "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, dist)
+ . = ..()
+ . += "There are [amount] units of package wrap left!"
diff --git a/code/modules/industry/packages/large_parcel.dm b/code/modules/industry/packages/large_parcel.dm
new file mode 100644
index 00000000000..268d5a6f94b
--- /dev/null
+++ b/code/modules/industry/packages/large_parcel.dm
@@ -0,0 +1,118 @@
+// todo: /obj/structure/large_parcel
+/obj/structure/bigDelivery
+ desc = "A big wrapped package."
+ name = "large parcel"
+ icon = 'icons/obj/storage.dmi'
+ icon_state = "deliverycloset"
+ var/obj/wrapped = null
+ density = 1
+ var/sortTag = null
+ mouse_drag_pointer = MOUSE_ACTIVE_POINTER
+ var/examtext = null
+ var/nameset = 0
+ var/label_y
+ var/label_x
+ var/tag_x
+
+/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/structure/bigDelivery/attack_hand(mob/user, list/params)
+ unwrap()
+
+/obj/structure/bigDelivery/proc/unwrap()
+ // 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, "You have labeled the destination as [O.currTag].")
+ if(!src.sortTag)
+ src.sortTag = O.currTag
+ update_icon()
+ else
+ src.sortTag = O.currTag
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1)
+ else
+ to_chat(user, "The package is already labeled for [O.currTag].")
+ else
+ to_chat(user, "You need to set a destination first!")
+
+ else if(istype(W, /obj/item/pen))
+ switch(alert("What would you like to alter?",,"Title","Description", "Cancel"))
+ if("Title")
+ var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN)
+ if(!str || !length(str))
+ to_chat(user, " Invalid text.")
+ return
+ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
+ "You title \the [src]: \"[str]\"",\
+ "You hear someone scribbling a note.")
+ name = "[name] ([str])"
+ if(!examtext && !nameset)
+ nameset = 1
+ update_icon()
+ else
+ nameset = 1
+ if("Description")
+ var/str = sanitize(input(usr,"Label text?","Set label",""))
+ if(!str || !length(str))
+ to_chat(user, "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]\"",\
+ "You label \the [src]: \"[examtext]\"",\
+ "You hear someone scribbling a note.")
+ 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(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, dist)
+ . = ..()
+ if(sortTag)
+ . += "It is labeled \"[sortTag]\""
+ if(examtext)
+ . += "It has a note attached which reads, \"[examtext]\""
diff --git a/code/modules/industry/packages/small_parcel.dm b/code/modules/industry/packages/small_parcel.dm
new file mode 100644
index 00000000000..433b8444952
--- /dev/null
+++ b/code/modules/industry/packages/small_parcel.dm
@@ -0,0 +1,107 @@
+// todo: /obj/item/small_parcel
+/obj/item/smallDelivery
+ desc = "A small wrapped package."
+ name = "small parcel"
+ icon = 'icons/obj/storage.dmi'
+ 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)
+ . = ..()
+ if(.)
+ return
+ if (wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
+ if(ishuman(user))
+ user.put_in_hands_or_drop(wrapped)
+ else
+ wrapped.forceMove(drop_location())
+ wrapped = null
+
+ qdel(src)
+
+/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, "You have labeled the destination as [O.currTag].")
+ if(!src.sortTag)
+ src.sortTag = O.currTag
+ update_icon()
+ else
+ src.sortTag = O.currTag
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1)
+ else
+ to_chat(user, "The package is already labeled for [O.currTag].")
+ else
+ to_chat(user, "You need to set a destination first!")
+
+ else if(istype(W, /obj/item/pen))
+ switch(alert("What would you like to alter?",,"Title","Description", "Cancel"))
+ if("Title")
+ var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN)
+ if(!str || !length(str))
+ to_chat(user, " Invalid text.")
+ return
+ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
+ "You title \the [src]: \"[str]\"",\
+ "You hear someone scribbling a note.")
+ name = "[name] ([str])"
+ if(!examtext && !nameset)
+ nameset = 1
+ update_icon()
+ else
+ nameset = 1
+
+ if("Description")
+ var/str = sanitize(input(usr,"Label text?","Set label",""))
+ if(!str || !length(str))
+ to_chat(user, "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]\"",\
+ "You label \the [src]: \"[examtext]\"",\
+ "You hear someone scribbling a note.")
+ 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(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, dist)
+ . = ..()
+ if(sortTag)
+ . += "It is labeled \"[sortTag]\""
+ if(examtext)
+ . += "It has a note attached which reads, \"[examtext]\""
diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm
index 0f281c96052..382d641d0c6 100644
--- a/code/modules/mining/machine_unloading.dm
+++ b/code/modules/mining/machine_unloading.dm
@@ -27,7 +27,7 @@
/obj/machinery/mineral/unloading_machine/process(delta_time)
if(output && input)
- if(length(output.loc.contents) > 100) // let's not!
+ if(length(output.loc.contents) > TURF_CROWDING_SOFT_LIMIT) // let's not!
return
var/obj/structure/ore_box/O = locate() in input.loc
if(O)
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
deleted file mode 100644
index 885b1df8321..00000000000
--- a/code/modules/recycling/disposal.dm
+++ /dev/null
@@ -1,1496 +0,0 @@
-// 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
-///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added.
-#define SEND_PRESSURE (700 + ONE_ATMOSPHERE)
-///L
-#define PRESSURE_TANK_VOLUME 150
-///L/s - 4 m/s using a 15 cm by 15 cm inlet
-#define PUMP_MAX_FLOW_RATE 90
-/obj/machinery/disposal
- name = "disposal unit"
- desc = "A pneumatic waste disposal unit."
- icon = 'icons/obj/pipes/disposal.dmi'
- icon_state = "disposal"
- atom_colouration_system = FALSE
- anchored = TRUE
- density = TRUE
- pass_flags_self = ATOM_PASS_OVERHEAD_THROW
- var/datum/gas_mixture/air_contents // internal reservoir
- var/mode = 1 // item mode 0=off 1=charging 2=charged
- var/flush = FALSE // true if flush handle is pulled
- var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk
- var/flushing = FALSE // true if flushing in progress
- var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush
- var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush.
- var/last_sound = 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, newdir)
- . = ..()
- return INITIALIZE_HINT_LATELOAD
-
-/obj/machinery/disposal/LateInitialize()
- . = ..()
- trunk = locate() in src.loc
- if(!trunk)
- mode = 0
- flush = FALSE
- 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
- return ..()
-
-// attack by item places it in to disposal
-/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user)
- if(user.a_intent != INTENT_HELP)
- return ..()
-
- . = CLICKCHAIN_DO_NOT_PROPAGATE
-
- if(machine_stat & BROKEN || !I || !user)
- return
-
- add_fingerprint(user, 0, I)
- if(mode<=0) // It's off
- if(I.is_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.tool_sound, 50, 1)
- to_chat(user, "You remove the screws around the power connection.")
- return
- else if(mode==-1)
- mode=0
- playsound(src, I.tool_sound, 50, 1)
- to_chat(user, "You attach the screws around the power connection.")
- return
- else if(istype(I, /obj/item/weldingtool) && mode==-1)
- if(contents.len > 0)
- to_chat(user, "Eject the items first!")
- return
- var/obj/item/weldingtool/W = I
- if(W.remove_fuel(0,user))
- playsound(src, W.tool_sound, 100, 1)
- to_chat(user, "You start slicing the floorweld off the disposal unit.")
-
- if(do_after(user,20 * W.tool_speed))
- 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 = 1
- C.density = 1
- 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, "You empty the bag.")
- for(var/obj/item/O in T.contents)
- T.obj_storage.remove(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("\The [user] empties \the [A.name] 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(usr))
- V.show_message("[usr] starts putting [GM.name] into the disposal.", 3)
- if(do_after(usr, 20))
- GM.forceMove(src)
- GM.update_perspective()
- for (var/mob/C in viewers(src))
- C.show_message("[GM.name] has been placed in \the [src] by [user].", 3)
- qdel(G)
-
- add_attack_logs(user,GM,"Disposals dunked")
- return
-
- if(!user.attempt_insert_item_for_installation(I, src))
- return
-
- to_chat(user, "You place \the [I] into \the [src].")
- for(var/mob/M in viewers(src))
- if(M == user)
- continue
- M.show_message("[user.name] places \the [I] into \the [src].", 3)
-
- update()
-
-// mouse drop another mob or self
-//
-/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user)
- if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !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(usr))
- if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
- V.show_message("[usr] starts climbing into the disposal.", 3)
- if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
- if(target.anchored) return
- V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3)
- if(!do_after(usr, 20))
- return
- if(target_loc != target.loc)
- return
- if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // 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].")
- else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE))
- 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
- target.forceMove(src)
- target.update_perspective()
-
- 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)
- user.forceMove(loc)
- user.update_perspective()
- update()
- return
-
-// ai as human but can't flush
-/obj/machinery/disposal/attack_ai(mob/user as mob)
- interact(user, 1)
-
-// human interact with machine
-/obj/machinery/disposal/attack_hand(mob/user, list/params)
-
- if(machine_stat & BROKEN)
- return
-
- if(user && user.loc == src)
- to_chat(user, "You cannot reach the controls from inside.")
- return
-
- // Clumsy folks can only flush it.
- if(user.IsAdvancedToolUser(1))
- interact(user, 0)
- else
- flush = !flush
- update()
- return
-
-// user interaction
-/obj/machinery/disposal/interact(mob/user, var/ai=0)
-
- src.add_fingerprint(user)
- if(machine_stat & BROKEN)
- user.unset_machine()
- return
-
- var/dat = "Waste Disposal UnitWaste Disposal Unit
"
-
- if(!ai) // AI can't pull flush handle
- if(flush)
- dat += "Disposal handle: Disengage Engaged"
- else
- dat += "Disposal handle: Disengaged Engage"
-
- dat += "
Eject contents
"
-
- if(mode <= 0)
- dat += "Pump: Off On
"
- else if(mode == 1)
- dat += "Pump: Off On (pressurizing)
"
- else
- dat += "Pump: Off On (idle)
"
-
- var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE)
-
- dat += "Pressure: [round(per, 1)]%
"
-
-
- user.set_machine(src)
- user << browse(dat, "window=disposal;size=360x170")
- onclose(user, "disposal")
-
-// handle machine interaction
-
-/obj/machinery/disposal/Topic(href, href_list)
- if(usr.loc == src)
- to_chat(usr, "You cannot reach the controls from inside.")
- return
-
- if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1
- to_chat(usr, "The disposal units power is disabled.")
- return
- if(..())
- return
-
- if(machine_stat & BROKEN)
- return
- if(usr.stat || usr.restrained() || src.flushing)
- return
-
- if(istype(src.loc, /turf))
- usr.set_machine(src)
-
- if(href_list["close"])
- usr.unset_machine()
- usr << browse(null, "window=disposal")
- return
-
- if(href_list["pump"])
- if(text2num(href_list["pump"]))
- mode = 1
- else
- mode = 0
- update()
-
- if(!isAI(usr))
- if(href_list["handle"])
- flush = text2num(href_list["handle"])
- update()
-
- if(href_list["eject"])
- eject()
- else
- usr << browse(null, "window=disposal")
- usr.unset_machine()
- return
- return
-
-// eject the contents of the disposal unit
-/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(machine_stat & BROKEN)
- icon_state = "disposal-broken"
- mode = 0
- flush = 0
- return
-
- var/list/overlays_to_add = list()
-
- // flush handle
- if(flush)
- overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle")
-
- // only handle is shown if no power
- if(machine_stat & NOPOWER || mode == -1)
- add_overlay(overlays_to_add)
- return
-
- // check for items in disposal - occupied light
- if(contents.len > 0)
- overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full")
-
- // charging and ready light
- if(mode == 1)
- overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge")
- else if(mode == 2)
- overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready")
-
- add_overlay(overlays_to_add)
-
-// timed process
-// charge the gas reservoir and perform flush if ready
-/obj/machinery/disposal/process(delta_time)
- if(!air_contents || (machine_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(machine_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
-
-
- 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(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_old(target, 5, 1)
-
- H.vent_gas(loc)
- qdel(H)
-
-/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT)
- if(istype(AM, /obj/item) && !istype(AM, /obj/projectile))
- if(prob(75))
- AM.forceMove(src)
- visible_message("\The [AM] lands in \the [src].")
- return COMPONENT_THROW_HIT_TERMINATE
- else
- visible_message("\The [AM] bounces off of \the [src]'s rim!")
- return ..()
- return ..()
-
-/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target)
- if(istype(mover, /obj/projectile))
- return TRUE
- return ..()
-
-/obj/machinery/disposal/wall
- name = "inset disposal unit"
- icon_state = "wall"
-
- density = FALSE
-
-/obj/machinery/disposal/wall/Initialize()
- . = ..()
-
- 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 = 101
- 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
-
-
-// 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
- setDir(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
- // todo: while this is fucking awful?
- 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, weapon_descriptor = "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
- M.update_perspective()
-
- 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(!istype(user,/mob/living))
- 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.loc, '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)
- active = 0
- return ..()
-
-// Disposal pipes
-
-/obj/structure/disposalpipe
- icon = 'icons/obj/pipes/disposal.dmi'
- name = "disposal pipe"
- desc = "An underfloor disposal pipe."
- anchored = 1
- density = 0
- hides_underfloor = OBJ_UNDERFLOOR_ALWAYS
- dir = 0 // dir will contain dominant direction for junction pipes
- plane = TURF_PLANE
- layer = DISPOSAL_LAYER // slightly lower than wires and other pipes.
-
- integrity = 100
- integrity_max = 100
-
- #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors.
- alpha = 128 // Set for the benefit of mapping.
- #endif
-
- /// Bitmask of pipe directions.
- var/dpdir = 0
- var/sortType = ""
- var/subtype = 0
- // new pipe, set the icon_state as on map
-
-/obj/structure/disposalpipe/Initialize(mapload, dir)
- . = ..()
- base_icon_state = icon_state
- if(!isnull(dir))
- setDir(dir)
-
-// pipe is deleted
-// ensure if holder is present, it is expelled
-/obj/structure/disposalpipe/Destroy()
- var/obj/structure/disposalholder/H = locate() in src
- if(H)
- // holder was present
- H.active = 0
- var/turf/T = src.loc
- if(T.density)
- // deleting pipe is inside a dense turf (wall)
- // this is unlikely, but just dump out everything into the turf in case
-
- for(var/atom/movable/AM in H)
- AM.forceMove(T)
- AM.pipe_eject(0)
- qdel(H)
- ..()
- return
-
- // otherwise, do normal expel from turf
- if(H)
- expel(H, T, 0)
- ..()
-
-// returns the direction of the next pipe object, given the entrance dir
-// by default, returns the bitmask of remaining directions
-/obj/structure/disposalpipe/proc/nextdir(var/fromdir)
- return dpdir & (~turn(fromdir, 180))
-
-// 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.setDir(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
-
-// update actual icon_state depending on visibility
-// if invisible, append "f" to icon_state to show faded version
-// this will be revealed if a T-scanner is used
-// if visible, use regular icon_state
-/obj/structure/disposalpipe/proc/updateicon()
-/* if(invisibility) //we hide things with alpha now, no need for transparent icons
- icon_state = "[base_icon_state]f"
- else
- icon_state = base_icon_state*/
- icon_state = base_icon_state
- return
-
-
-// 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)
- if(!istype(H))
- return
-
- // Empty the holder if it is expelled into a dense turf.
- // Leaving it intact and sitting in a wall is stupid.
- if(T.density)
- for(var/atom/movable/AM in H)
- AM.loc = T
- AM.pipe_eject(0)
- 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
-
- var/turf/target
- if(direction) // direction is specified
- if(istype(T, /turf/space)) // if ended in space, then range is unlimited
- target = get_edge_target_turf(T, direction)
- else // otherwise limit to 10 tiles
- target = get_ranged_target_turf(T, direction, 10)
-
- playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
- if(H)
- for(var/atom/movable/AM in H)
- AM.forceMove(T)
- AM.pipe_eject(direction)
- spawn(1)
- if(AM)
- AM.throw_at_old(target, 100, 1)
- H.vent_gas(T)
- qdel(H)
-
- else // no specified direction, so throw in random direction
-
- playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
- if(H)
- for(var/atom/movable/AM in H)
- 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_old(target, 5, 1)
-
- H.vent_gas(T) // all gas vent to turf
- qdel(H)
-
-/obj/structure/disposalpipe/deconstructed(method)
- . = ..()
- src.invisibility = 101 // 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
- if(T.density)
- // broken pipe is inside a dense turf (wall)
- // this is unlikely, but just dump out everything into the turf in case
-
- for(var/atom/movable/AM in H)
- AM.forceMove(T)
- AM.pipe_eject(0)
- qdel(H)
- return
-
- // otherwise, do normal expel from turf
- if(H)
- expel(H, T, 0)
- return ..()
-
-/obj/structure/disposalpipe/drop_products(method, atom/where)
- . = ..()
- if(method != ATOM_DECONSTRUCT_DISASSEMBLED)
- new /obj/structure/disposalpipe/broken(where, dir)
-
-//attack by item
-//weldingtool: unfasten and convert to obj/disposalconstruct
-
-/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user)
-
- var/turf/T = src.loc
- if(!T.is_plating())
- return // prevent interaction with T-scanner revealed pipes
- src.add_fingerprint(user, 0, I)
- if(istype(I, /obj/item/weldingtool))
- var/obj/item/weldingtool/W = I
-
- if(W.remove_fuel(0,user))
- playsound(src, W.tool_sound, 50, 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
-
-// called when pipe is cut with welder
-/obj/structure/disposalpipe/proc/welded()
-
- var/obj/structure/disposalconstruct/C = new (src.loc)
- switch(base_icon_state)
- if("pipe-s")
- C.ptype = 0
- if("pipe-c")
- C.ptype = 1
- if("pipe-j1")
- C.ptype = 2
- if("pipe-j2")
- C.ptype = 3
- if("pipe-y")
- C.ptype = 4
- if("pipe-t")
- C.ptype = 5
- if("pipe-j1s")
- C.ptype = 9
- C.sortType = sortType
- if("pipe-j2s")
- C.ptype = 10
- C.sortType = sortType
-///// Z-Level stuff
- if("pipe-u")
- C.ptype = 11
- if("pipe-d")
- C.ptype = 12
-///// Z-Level stuff
- if("pipe-tagger")
- C.ptype = 13
- if("pipe-tagger-partial")
- C.ptype = 14
- C.subtype = src.subtype
- src.transfer_fingerprints_to(C)
- C.setDir(dir)
- C.density = 0
- C.anchored = 1
- C.update()
-
- qdel(src)
-
-// pipe is deleted
-// ensure if holder is present, it is expelled
-/obj/structure/disposalpipe/Destroy()
- var/obj/structure/disposalholder/H = locate() in src
- if(H)
- // holder was present
- H.active = 0
- var/turf/T = src.loc
- if(T.density)
- // deleting pipe is inside a dense turf (wall)
- // this is unlikely, but just dump out everything into the turf in case
-
- for(var/atom/movable/AM in H)
- AM.forceMove(T)
- AM.pipe_eject(0)
- qdel(H)
- ..()
- return
-
- // otherwise, do normal expel from turf
- if(H)
- expel(H, T, 0)
- ..()
-
-// a straight or bent segment
-/obj/structure/disposalpipe/segment
- icon_state = "pipe-s"
-
-/obj/structure/disposalpipe/segment/New()
- ..()
- if(icon_state == "pipe-s")
- dpdir = dir | turn(dir, 180)
- else
- dpdir = dir | turn(dir, -90)
- return
-
-///// Z-Level stuff
-/obj/structure/disposalpipe/up
- icon_state = "pipe-u"
-
-/obj/structure/disposalpipe/up/New()
- ..()
- dpdir = dir
- return
-
-/obj/structure/disposalpipe/up/nextdir(fromdir)
- var/nextdir
- if(fromdir == 11)
- nextdir = dir
- else
- nextdir = 12
- return nextdir
-
-/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H)
- var/nextdir = nextdir(H.dir)
- H.setDir(nextdir)
-
- var/turf/T
- var/obj/structure/disposalpipe/P
-
- if(nextdir == 12)
- T = get_vertical_step(src, UP)
- if(!T)
- H.forceMove(loc)
- return
- else
- for(var/obj/structure/disposalpipe/down/F in T)
- P = F
-
- else
- T = get_step(src.loc, H.dir)
- 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/down
- icon_state = "pipe-d"
-
-/obj/structure/disposalpipe/down/New()
- ..()
- dpdir = dir
- return
-
-/obj/structure/disposalpipe/down/nextdir(fromdir)
- var/nextdir
- if(fromdir == 12)
- nextdir = dir
- else
- nextdir = 11
- return nextdir
-
-/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H)
- var/nextdir = nextdir(H.dir)
- H.dir = nextdir
-
- var/turf/T
- var/obj/structure/disposalpipe/P
-
- if(nextdir == 11)
- T = get_vertical_step(src, DOWN)
- if(!T)
- H.forceMove(src.loc)
- return
- else
- for(var/obj/structure/disposalpipe/up/F in T)
- P = F
-
- else
- T = get_step(src.loc, H.dir)
- 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
-///// 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/New()
- ..()
- 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)
- 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/junction/flipped //for easier and cleaner mapping
- icon_state = "pipe-j2"
-
-/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/New()
- . = ..()
- dpdir = dir | turn(dir, 180)
- if(sort_tag) GLOB.tagger_locations |= sort_tag
- updatename()
- updatedesc()
-
-/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.loc, 'sound/machines/twobeep.ogg', 100, 1)
- to_chat(user, "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
-
-//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/posdir = 0
- var/negdir = 0
- var/sortdir = 0
-
-/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/proc/updatedir()
- posdir = dir
- negdir = turn(posdir, 180)
-
- if(icon_state == "pipe-j1s")
- sortdir = turn(posdir, -90)
- else if(icon_state == "pipe-j2s")
- sortdir = turn(posdir, 90)
-
- dpdir = sortdir | posdir | negdir
-
-/obj/structure/disposalpipe/sortjunction/New()
- . = ..()
- if(sortType) GLOB.tagger_locations |= sortType
-
- updatedir()
- updatename()
- updatedesc()
-
-/obj/structure/disposalpipe/sortjunction/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
- sortType = O.currTag
- playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1)
- to_chat(user, "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 posdir, then flip around and go back to posdir
-// if coming in from sortdir, go to posdir
-/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag)
- if(fromdir != sortdir) // probably came from the negdir
- if(divert_check(sortTag))
- return sortdir
- else
- return posdir
- else // came from sortdir
- // so go with the flow to positive direction
- return posdir
-
-/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H)
- var/nextdir = nextdir(H.dir, H.destinationTag)
- H.setDir(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
-
-//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"
-
-//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()
- . = ..()
- getlinked()
-/obj/structure/disposalpipe/trunk/proc/getlinked()
- linked = null
- var/obj/machinery/disposal/D = locate() in src.loc
- if(D)
- linked = D
- if (!D.trunk)
- D.trunk = src
-
- var/obj/structure/disposaloutlet/O = locate() in src.loc
- if(O)
- linked = O
- return
-
- // Override attackby so we disallow trunkremoval when somethings ontop
-/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user)
-
- //Disposal bins or chutes
- /*
- These shouldn't be required
- var/obj/machinery/disposal/D = locate() in src.loc
- if(D && D.anchored)
- return
-
- //Disposal outlet
- var/obj/structure/disposaloutlet/O = locate() in src.loc
- if(O && O.anchored)
- return
- */
-
- //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, 0, I)
- if(istype(I, /obj/item/weldingtool))
- var/obj/item/weldingtool/W = I
-
- if(W.remove_fuel(0,user))
- playsound(src, W.tool_sound, 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
- icon_state = "pipe-b"
- dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes
- // i.e. will be treated as an empty turf
- desc = "A broken piece of disposal pipe."
-
-// 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 = 1
- anchored = 1
- var/active = 0
- var/turf/target // this will be where the output objects are 'thrown' to.
- var/mode = 0
-
-/obj/structure/disposaloutlet/LateInitialize()
- 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
-
-// expel the contents of the holder object, then delete it
-// called when the holder exits the outlet
-/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H)
-
- target = get_ranged_target_turf(src, dir, 10)
- 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_old(target, 3, 1)
- H.vent_gas(src.loc)
- qdel(H)
-
- return
-
-/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user)
- if(!I || !user)
- return
- src.add_fingerprint(user, 0, I)
- if(I.is_screwdriver())
- if(mode==0)
- mode=1
- to_chat(user, "You remove the screws around the power connection.")
- playsound(src, I.tool_sound, 50, 1)
- return
- else if(mode==1)
- mode=0
- to_chat(user, "You attach the screws around the power connection.")
- playsound(src, I.tool_sound, 50, 1)
- return
- else if(istype(I, /obj/item/weldingtool) && mode==1)
- var/obj/item/weldingtool/W = I
- if(W.remove_fuel(0,user))
- playsound(src, W.tool_sound, 100, 1)
- to_chat(user, "You start slicing the floorweld off the disposal outlet.")
- if(do_after(user,20 * W.tool_speed))
- 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 = 1
- C.density = 1
- 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(direction)
- return
-
-// check if mob has client, if so restore client view on eject
-/mob/pipe_eject(direction)
- update_perspective()
-
-/obj/effect/debris/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()
-
- src.streak(dirs)
-
-/obj/effect/debris/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)
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
deleted file mode 100644
index 8b793c7df65..00000000000
--- a/code/modules/recycling/sortingmachinery.dm
+++ /dev/null
@@ -1,469 +0,0 @@
-/obj/structure/bigDelivery
- desc = "A big wrapped package."
- name = "large parcel"
- icon = 'icons/obj/storage.dmi'
- icon_state = "deliverycloset"
- var/obj/wrapped = null
- density = 1
- var/sortTag = null
- 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, list/params)
- unwrap()
-
-/obj/structure/bigDelivery/proc/unwrap()
- // 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, "You have labeled the destination as [O.currTag].")
- if(!src.sortTag)
- src.sortTag = O.currTag
- update_icon()
- else
- src.sortTag = O.currTag
- playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1)
- else
- to_chat(user, "The package is already labeled for [O.currTag].")
- else
- to_chat(user, "You need to set a destination first!")
-
- else if(istype(W, /obj/item/pen))
- switch(alert("What would you like to alter?",,"Title","Description", "Cancel"))
- if("Title")
- var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN)
- if(!str || !length(str))
- to_chat(user, " Invalid text.")
- return
- user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
- "You title \the [src]: \"[str]\"",\
- "You hear someone scribbling a note.")
- name = "[name] ([str])"
- if(!examtext && !nameset)
- nameset = 1
- update_icon()
- else
- nameset = 1
- if("Description")
- var/str = sanitize(input(usr,"Label text?","Set label",""))
- if(!str || !length(str))
- to_chat(user, "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]\"",\
- "You label \the [src]: \"[examtext]\"",\
- "You hear someone scribbling a note.")
- 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(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, dist)
- . = ..()
- if(sortTag)
- . += "It is labeled \"[sortTag]\""
- if(examtext)
- . += "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.dmi'
- 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)
- . = ..()
- if(.)
- return
- 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, "You have labeled the destination as [O.currTag].")
- if(!src.sortTag)
- src.sortTag = O.currTag
- update_icon()
- else
- src.sortTag = O.currTag
- playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1)
- else
- to_chat(user, "The package is already labeled for [O.currTag].")
- else
- to_chat(user, "You need to set a destination first!")
-
- else if(istype(W, /obj/item/pen))
- switch(alert("What would you like to alter?",,"Title","Description", "Cancel"))
- if("Title")
- var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN)
- if(!str || !length(str))
- to_chat(user, " Invalid text.")
- return
- user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\
- "You title \the [src]: \"[str]\"",\
- "You hear someone scribbling a note.")
- name = "[name] ([str])"
- if(!examtext && !nameset)
- nameset = 1
- update_icon()
- else
- nameset = 1
-
- if("Description")
- var/str = sanitize(input(usr,"Label text?","Set label",""))
- if(!str || !length(str))
- to_chat(user, "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]\"",\
- "You label \the [src]: \"[examtext]\"",\
- "You hear someone scribbling a note.")
- 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(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, dist)
- . = ..()
- if(sortTag)
- . += "It is labeled \"[sortTag]\""
- if(examtext)
- . += "It has a note attached which reads, \"[examtext]\""
-
-/obj/item/packageWrap
- name = "package wrapper"
- icon = 'icons/obj/items.dmi'
- icon_state = "deliveryPaper"
- w_class = WEIGHT_CLASS_NORMAL
- var/amount = 25.0
-
-
-/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params)
- if(!(clickchain_flags & CLICKCHAIN_HAS_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(target in user)
- return
- if(user in target) //no wrapping closets that you are inside - it's not physically possible
- return
-
- user.attack_log += "\[[time_stamp()]\] Has used [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.set_weight_class(O.w_class)
- var/i = round(O.get_weight_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(usr)
- O.add_fingerprint(usr)
- src.add_fingerprint(usr)
- src.amount -= 1
- user.visible_message("\The [user] wraps \a [target] with \a [src].",\
- "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
- "You hear someone taping paper around a small object.")
- 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].",\
- "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
- "You hear someone taping paper around a large object.")
- else if(src.amount < 3)
- to_chat(user, "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].",\
- "You wrap \the [target], leaving [amount] units of paper on \the [src].",\
- "You hear someone taping paper around a large object.")
- else if(src.amount < 3)
- to_chat(user, "You need more paper.")
- else
- to_chat(user, "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, dist)
- . = ..()
- . += "There are [amount] units of package wrap left!"
-
-/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 = WEIGHT_CLASS_SMALL
- item_state = "electronic"
- slot_flags = SLOT_BELT
-
-/obj/item/destTagger/ui_state()
- return GLOB.inventory_state
-
-/obj/item/destTagger/ui_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/ui_data(mob/user, datum/tgui/ui)
- var/list/data = ..()
-
- data["currTag"] = currTag
- data["taggerLocs"] = GLOB.tagger_locations
-
- return data
-
-/obj/item/destTagger/attack_self(mob/user)
- . = ..()
- if(.)
- return
- ui_interact(user)
-
-/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui)
- if(..())
- return TRUE
- add_fingerprint(usr)
- 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 = 1
- icon_state = "intake"
-
- var/c_mode = 0
-
-/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir)
- . = ..()
- spawn(5)
- trunk = locate() in src.loc
- if(trunk)
- trunk.linked = src // link the pipe trunk to self
-
-/obj/machinery/disposal/deliveryChute/interact()
- return
-
-/obj/machinery/disposal/deliveryChute/update()
- return
-
-/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute
- if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return
- switch(dir)
- if(NORTH)
- if(AM.loc.y != src.loc.y+1) return
- if(EAST)
- if(AM.loc.x != src.loc.x+1) return
- if(SOUTH)
- if(AM.loc.y != src.loc.y-1) return
- if(WEST)
- if(AM.loc.x != src.loc.x-1) return
-
-// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36
-
- 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()
-
-/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
-
-/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user)
- if(!I || !user)
- return
-
- if(I.is_screwdriver())
- if(c_mode==0)
- c_mode=1
- playsound(src.loc, I.tool_sound, 50, 1)
- to_chat(user, "You remove the screws around the power connection.")
- return
- else if(c_mode==1)
- c_mode=0
- playsound(src.loc, I.tool_sound, 50, 1)
- to_chat(user, "You attach the screws around the power connection.")
- return
- else if(istype(I, /obj/item/weldingtool) && c_mode==1)
- var/obj/item/weldingtool/W = I
- if(W.remove_fuel(0,user))
- playsound(src.loc, W.tool_sound, 50, 1)
- to_chat(user, "You start slicing the floorweld off the delivery chute.")
- if(do_after(user,20 * W.tool_speed))
- if(!src || !W.isOn()) return
- to_chat(user, "You sliced the floorweld off the delivery chute.")
- var/obj/structure/disposalconstruct/C = new (src.loc)
- C.ptype = 8 // 8 = Delivery chute
- C.update()
- C.anchored = 1
- C.density = 1
- qdel(src)
- return
- else
- to_chat(user, "You need more welding fuel to complete this task.")
- return
-
-/obj/machinery/disposal/deliveryChute/Destroy()
- if(trunk)
- trunk.linked = null
- ..()