// Disposal bin and Delivery chute. #define SEND_PRESSURE (0.05*ONE_ATMOSPHERE) /obj/machinery/disposal icon = 'icons/obj/atmospherics/pipes/disposal.dmi' density = TRUE armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) max_integrity = 200 resistance_flags = FIRE_PROOF interaction_flags_machine = INTERACT_MACHINE_OPEN | INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON obj_flags = CAN_BE_HIT | USES_TGUI rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE var/datum/gas_mixture/air_contents // internal reservoir var/full_pressure = FALSE var/pressure_charging = TRUE 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 var/obj/structure/disposalconstruct/stored ui_x = 300 ui_y = 180 // create a new disposal // find the attached trunk (if present) and init gas resvr. /obj/machinery/disposal/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() if(make_from) setDir(make_from.dir) make_from.moveToNullspace() stored = make_from pressure_charging = FALSE // newly built disposal bins start with pump off else stored = new /obj/structure/disposalconstruct(null, null , SOUTH , FALSE , src) trunk_check() air_contents = new /datum/gas_mixture() //gas.volume = 1.05 * CELLSTANDARD update_icon() return INITIALIZE_HINT_LATELOAD //we need turfs to have air /obj/machinery/disposal/proc/trunk_check() trunk = locate() in loc if(!trunk) pressure_charging = FALSE flush = FALSE else if(initial(pressure_charging)) pressure_charging = TRUE flush = initial(flush) trunk.linked = src // link the pipe trunk to self /obj/machinery/disposal/Destroy() eject() if(trunk) trunk.linked = null return ..() /obj/machinery/disposal/singularity_pull(S, current_size) ..() if(current_size >= STAGE_FIVE) deconstruct() /obj/machinery/disposal/LateInitialize() //this will get a copy of the air turf and take a SEND PRESSURE amount of air from it var/atom/L = loc var/datum/gas_mixture/env = new env.copy_from(L.return_air()) var/datum/gas_mixture/removed = env.remove(SEND_PRESSURE + 1) air_contents.merge(removed) trunk_check() /obj/machinery/disposal/attackby(obj/item/I, mob/user, params) add_fingerprint(user) if(!pressure_charging && !full_pressure && !flush) if(I.tool_behaviour == TOOL_SCREWDRIVER) panel_open = !panel_open I.play_tool_sound(src) to_chat(user, "You [panel_open ? "remove":"attach"] the screws around the power connection.") return else if(I.tool_behaviour == TOOL_WELDER && panel_open) if(!I.tool_start_check(user, amount=0)) return to_chat(user, "You start slicing the floorweld off \the [src]...") if(I.use_tool(src, user, 20, volume=100) && panel_open) to_chat(user, "You slice the floorweld off \the [src].") deconstruct() return if(user.a_intent != INTENT_HARM) if((I.item_flags & ABSTRACT) || !user.temporarilyRemoveItemFromInventory(I)) return place_item_in_disposal(I, user) update_icon() return TRUE //no afterattack else return ..() /obj/machinery/disposal/proc/place_item_in_disposal(obj/item/I, mob/user) if(istype(I, /obj/item/clothing/head/mob_holder)) var/obj/item/clothing/head/mob_holder/H = I var/mob/living/m = H.held_mob H.release() if(m) user.start_pulling(m, 1) stuff_mob_in(m,user) return//you don't want this going into disposals ever if(user.temporarilyRemoveItemFromInventory(I)) //double-checks never hurt I.forceMove(src) user.visible_message("[user.name] places \the [I] into \the [src].", "You place \the [I] into \the [src].") //mouse drop another mob or self /obj/machinery/disposal/MouseDrop_T(mob/living/target, mob/living/user) if(istype(target)) stuff_mob_in(target, user) /obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user) if(!can_stuff_mob_in(target, user)) return add_fingerprint(user) if(user == target) user.visible_message("[user] starts climbing into [src].", "You start climbing into [src]...") else target.visible_message("[user] starts putting [target] into [src].", "[user] starts putting you into [src]!") if(do_mob(user, target, 20)) if (!loc) return target.forceMove(src) if(user == target) user.visible_message("[user] climbs into [src].", "You climb into [src].") else target.visible_message("[user] has placed [target] in [src].", "[user] has placed [target] in [src].") log_combat(user, target, "stuffed", addition="into [src]") target.LAssailant = user update_icon() /obj/machinery/disposal/proc/can_stuff_mob_in(mob/living/target, mob/living/user, pushing = FALSE) if(!pushing && !iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. if(iscyborg(user)) var/mob/living/silicon/robot/borg = user if (!borg.module || !borg.module.canDispose) return else return FALSE if(!isturf(user.loc)) //No magically doing it from inside closets return FALSE if(target.buckled || target.has_buckled_mobs()) return FALSE if(target.mob_size > MOB_SIZE_HUMAN) if(!pushing) to_chat(user, "[target] doesn't fit inside [src]!") return FALSE return TRUE /obj/machinery/disposal/relaymove(mob/user) attempt_escape(user) // resist to escape the bin /obj/machinery/disposal/container_resist(mob/living/user) attempt_escape(user) /obj/machinery/disposal/proc/attempt_escape(mob/user) if(flushing) return go_out(user) // leave the disposal /obj/machinery/disposal/proc/go_out(mob/user) user.forceMove(loc) update_icon() // monkeys and xenos can only pull the flush lever /obj/machinery/disposal/attack_paw(mob/user) if(stat & BROKEN) return flush = !flush update_icon() // eject the contents of the disposal unit /obj/machinery/disposal/proc/eject() var/turf/T = get_turf(src) for(var/atom/movable/AM in src) AM.forceMove(T) AM.pipe_eject(0) update_icon() // update the icon & overlays to reflect mode & status /obj/machinery/disposal/update_icon() return /obj/machinery/disposal/proc/flush() flushing = TRUE flushAnimation() sleep(10) if(last_sound < world.time + 1) playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) last_sound = world.time sleep(5) if(QDELETED(src)) return var/obj/structure/disposalholder/H = new(src) newHolderDestination(H) H.init(src) air_contents = new() H.start(src) flushing = FALSE flush = FALSE /obj/machinery/disposal/proc/newHolderDestination(obj/structure/disposalholder/H) for(var/obj/item/smallDelivery/O in src) H.tomail = TRUE return /obj/machinery/disposal/proc/flushAnimation() flick("[icon_state]-flush", src) // called when area power changes /obj/machinery/disposal/power_change() ..() // do default setting/reset of stat NOPOWER bit update_icon() // update icon // called when holder is expelled from a disposal /obj/machinery/disposal/proc/expel(obj/structure/disposalholder/H) H.active = FALSE var/turf/T = get_turf(src) var/turf/target playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) for(var/A in H) var/atom/movable/AM = A target = get_offset_target_turf(loc, rand(5)-rand(5), rand(5)-rand(5)) AM.forceMove(T) AM.pipe_eject(0) AM.throw_at(target, 5, 1) H.vent_gas(loc) qdel(H) /obj/machinery/disposal/deconstruct(disassembled = TRUE) var/turf/T = loc if(!(flags_1 & NODECONSTRUCT_1)) if(stored) stored.forceMove(T) src.transfer_fingerprints_to(stored) stored.anchored = FALSE stored.density = TRUE stored.update_icon() for(var/atom/movable/AM in src) //out, out, darned crowbar! AM.forceMove(T) ..() /obj/machinery/disposal/get_dumping_location(obj/item/storage/source,mob/user) return src //How disposal handles getting a storage dump from a storage object /obj/machinery/disposal/storage_contents_dump_act(datum/component/storage/src_object, mob/user) . = ..() if(.) return for(var/obj/item/I in src_object) if(user.active_storage != src_object) if(I.on_found(user)) return src_object.remove_from_storage(I, src) return TRUE // 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 /obj/machinery/disposal/bin name = "disposal unit" desc = "A pneumatic waste disposal unit." icon_state = "disposal" var/datum/oracle_ui/themed/nano/ui obj_flags = CAN_BE_HIT | USES_TGUI | SHOVABLE_ONTO // attack by item places it in to disposal /obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/storage/bag/trash)) //Not doing component overrides because this is a specific type. var/obj/item/storage/bag/trash/T = I var/datum/component/storage/STR = T.GetComponent(/datum/component/storage) to_chat(user, "You empty the bag.") for(var/obj/item/O in T.contents) STR.remove_from_storage(O,src) T.update_icon() update_icon() else return ..() // handle machine interaction /obj/machinery/disposal/bin/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) if(stat & BROKEN) return ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "disposal_unit", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/disposal/bin/ui_data(mob/user) var/list/data = list() data["flush"] = flush data["full_pressure"] = full_pressure data["pressure_charging"] = pressure_charging data["panel_open"] = panel_open data["per"] = CLAMP01(air_contents.return_pressure() / (SEND_PRESSURE)) data["isai"] = isAI(user) return data /obj/machinery/disposal/bin/ui_act(action, params) if(..()) return switch(action) if("handle-0") flush = FALSE update_icon() . = TRUE if("handle-1") if(!panel_open) flush = TRUE update_icon() . = TRUE if("pump-0") if(pressure_charging) pressure_charging = FALSE update_icon() . = TRUE if("pump-1") if(!pressure_charging) pressure_charging = TRUE update_icon() . = TRUE if("eject") eject() . = TRUE /obj/machinery/disposal/bin/alt_attack_hand(mob/user) if(can_interact(usr)) flush = !flush update_icon() return TRUE return FALSE /obj/machinery/disposal/bin/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) if(isitem(AM) && AM.CanEnterDisposals()) if(prob(75)) AM.forceMove(src) visible_message("[AM] lands in [src].") update_icon() else visible_message("[AM] bounces off of [src]'s rim!") return ..() else return ..() /obj/machinery/disposal/bin/shove_act(mob/living/target, mob/living/user) if(!can_stuff_mob_in(target, user, TRUE)) return FALSE target.Knockdown(SHOVE_KNOCKDOWN_SOLID) target.forceMove(src) user.visible_message("[user.name] shoves [target.name] into \the [src]!", "You shove [target.name] into \the [src]!", null, COMBAT_MESSAGE_RANGE) log_combat(user, target, "shoved", "into [src] (disposal bin)") return TRUE /obj/machinery/disposal/bin/flush() ..() full_pressure = FALSE pressure_charging = TRUE update_icon() /obj/machinery/disposal/bin/update_icon() cut_overlays() if(stat & BROKEN) pressure_charging = FALSE flush = FALSE return //flush handle if(flush) add_overlay("dispover-handle") //only handle is shown if no power if(stat & NOPOWER || panel_open) return //check for items in disposal - occupied light if(contents.len > 0) add_overlay("dispover-full") //charging and ready light if(pressure_charging) add_overlay("dispover-charge") else if(full_pressure) add_overlay("dispover-ready") /obj/machinery/disposal/bin/proc/do_flush() set waitfor = FALSE flush() //timed process //charge the gas reservoir and perform flush if ready /obj/machinery/disposal/bin/process() if(stat & BROKEN) //nothing can happen if broken return flush_count++ if(flush_count >= flush_every_ticks) if(contents.len) if(full_pressure) do_flush() flush_count = 0 if(flush && air_contents.return_pressure() >= SEND_PRESSURE) // flush can happen even without power do_flush() if(stat & NOPOWER) // won't charge if no power return use_power(100) // base power usage if(!pressure_charging) // if off or ready, no need to charge return // otherwise charge use_power(500) // charging power usage var/atom/L = loc //recharging from loc turf var/datum/gas_mixture/env = L.return_air() var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() if(env.temperature > 0) var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) //Actually transfer the gas var/datum/gas_mixture/removed = env.remove(transfer_moles) air_contents.merge(removed) air_update_turf() //if full enough, switch to ready mode if(air_contents.return_pressure() >= SEND_PRESSURE) full_pressure = TRUE pressure_charging = FALSE update_icon() return /obj/machinery/disposal/bin/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) //Delivery Chute /obj/machinery/disposal/deliveryChute name = "delivery chute" desc = "A chute for big and small packages alike!" density = TRUE icon_state = "intake" pressure_charging = FALSE // the chute doesn't need charging and always works /obj/machinery/disposal/deliveryChute/Initialize(mapload, obj/structure/disposalconstruct/make_from) . = ..() trunk = locate() in loc if(trunk) trunk.linked = src // link the pipe trunk to self /obj/machinery/disposal/deliveryChute/place_item_in_disposal(obj/item/I, mob/user) if(I.CanEnterDisposals()) ..() flush() /obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute if(!AM.CanEnterDisposals()) return switch(dir) if(NORTH) if(AM.loc.y != loc.y+1) return if(EAST) if(AM.loc.x != loc.x+1) return if(SOUTH) if(AM.loc.y != loc.y-1) return if(WEST) if(AM.loc.x != loc.x-1) return if(isobj(AM)) var/obj/O = AM O.forceMove(src) else if(ismob(AM)) var/mob/M = AM if(prob(2)) // to prevent mobs being stuck in infinite loops to_chat(M, "You hit the edge of the chute.") return M.forceMove(src) flush() /atom/movable/proc/CanEnterDisposals() return TRUE /obj/item/projectile/CanEnterDisposals() return /obj/effect/CanEnterDisposals() return /obj/mecha/CanEnterDisposals() return /obj/machinery/disposal/deliveryChute/newHolderDestination(obj/structure/disposalholder/H) H.destinationTag = 1