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