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 - ..()