Disposal pipes and pipe dispensers refactor

This commit is contained in:
ACCount
2017-11-12 00:31:00 -06:00
committed by CitadelStationBot
parent 1f7deabbfb
commit 500307b034
16 changed files with 1196 additions and 1196 deletions
+495
View File
@@ -0,0 +1,495 @@
// Disposal bin and Delivery chute.
#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE
/obj/machinery/disposal
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
anchored = TRUE
density = TRUE
on_blueprints = 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
interact_open = TRUE
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
// 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.loc = null
stored = make_from
pressure_charging = FALSE // newly built disposal bins start with pump off
else
stored = new /obj/structure/disposalconstruct(null, make_from = 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/ComponentInitialize()
. = ..()
AddComponent(/datum/component/rad_insulation, RAD_NO_INSULATION)
/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(istype(I, /obj/item/screwdriver))
panel_open = !panel_open
playsound(get_turf(src), I.usesound, 50, 1)
to_chat(user, "<span class='notice'>You [panel_open ? "remove":"attach"] the screws around the power connection.</span>")
return
else if(istype(I, /obj/item/weldingtool) && panel_open)
var/obj/item/weldingtool/W = I
if(W.remove_fuel(0,user))
playsound(src.loc, 'sound/items/welder2.ogg', 100, 1)
to_chat(user, "<span class='notice'>You start slicing the floorweld off \the [src]...</span>")
if(do_after(user,20*I.toolspeed, target = src) && panel_open)
if(!W.isOn())
return
to_chat(user, "<span class='notice'>You slice the floorweld off \the [src].</span>")
deconstruct()
return
if(user.a_intent != INTENT_HARM)
if((I.flags_1 & ABSTRACT_1) || !user.temporarilyRemoveItemFromInventory(I))
return
place_item_in_disposal(I, user)
update_icon()
return 1 //no afterattack
else
return ..()
/obj/machinery/disposal/proc/place_item_in_disposal(obj/item/I, mob/user)
I.forceMove(src)
user.visible_message("[user.name] places \the [I] into \the [src].", "<span class='notice'>You place \the [I] into \the [src].</span>")
//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(!iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves.
return
if(!isturf(user.loc)) //No magically doing it from inside closets
return
if(target.buckled || target.has_buckled_mobs())
return
if(target.mob_size > MOB_SIZE_HUMAN)
to_chat(user, "<span class='warning'>[target] doesn't fit inside [src]!</span>")
return
add_fingerprint(user)
if(user == target)
user.visible_message("[user] starts climbing into [src].", "<span class='notice'>You start climbing into [src]...</span>")
else
target.visible_message("<span class='danger'>[user] starts putting [target] into [src].</span>", "<span class='userdanger'>[user] starts putting you into [src]!</span>")
if(do_mob(user, target, 20))
if (!loc)
return
target.forceMove(src)
if(user == target)
user.visible_message("[user] climbs into [src].", "<span class='notice'>You climb into [src].</span>")
else
target.visible_message("<span class='danger'>[user] has placed [target] in [src].</span>", "<span class='userdanger'>[user] has placed [target] in [src].</span>")
add_logs(user, target, "stuffed", addition="into [src]")
target.LAssailant = user
update_icon()
/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()
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)
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(obj/item/storage/src_object, mob/user)
for(var/obj/item/I in src_object)
if(user.s_active != src_object)
if(I.on_found(user))
return
src_object.remove_from_storage(I, src)
return 1
// 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"
// 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))
var/obj/item/storage/bag/trash/T = I
to_chat(user, "<span class='warning'>You empty the bag.</span>")
for(var/obj/item/O in T.contents)
T.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, 300, 200, 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
var/per = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100)
data["per"] = round(per, 1)
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/hitby(atom/movable/AM)
if(isitem(AM) && AM.CanEnterDisposals())
if(prob(75))
AM.forceMove(src)
visible_message("<span class='notice'>[AM] lands in [src].</span>")
update_icon()
else
visible_message("<span class='notice'>[AM] bounces off of [src]'s rim!</span>")
return ..()
else
return ..()
/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
SSblackbox.inc("disposal_auto_flush")
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
updateDialog()
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/CollidedWith(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.loc = src
else if(ismob(AM))
var/mob/M = AM
if(prob(2)) // to prevent mobs being stuck in infinite loops
to_chat(M, "<span class='warning'>You hit the edge of the chute.</span>")
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
@@ -0,0 +1,231 @@
// Disposal pipe construction
// This is the pipe that you drag around, not the attached ones.
/obj/structure/disposalconstruct
name = "disposal pipe segment"
desc = "A huge pipe segment used for constructing disposal systems."
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
icon_state = "conpipe"
anchored = FALSE
density = FALSE
pressure_resistance = 5*ONE_ATMOSPHERE
level = 2
max_integrity = 200
var/obj/pipe_type = /obj/structure/disposalpipe/segment
var/pipename
/obj/structure/disposalconstruct/examine(mob/user)
..()
to_chat(user, "<span class='notice'>Alt-click to rotate it clockwise.</span>")
/obj/structure/disposalconstruct/New(loc, _pipe_type, _dir = SOUTH, flip = FALSE, obj/make_from)
..()
if(make_from)
pipe_type = make_from.type
setDir(make_from.dir)
anchored = TRUE
if(istype(make_from, /obj/structure/disposalpipe))
var/obj/structure/disposalpipe/D = make_from
if(D.construct_type)
pipe_type = D.construct_type
else
if(_pipe_type)
pipe_type = _pipe_type
setDir(_dir)
pipename = initial(pipe_type.name)
if(flip)
flip()
update_icon()
/obj/structure/disposalconstruct/Move()
var/old_dir = dir
..()
setDir(old_dir) //pipes changing direction when moved is just annoying and buggy
// update iconstate and dpdir due to dir and type
/obj/structure/disposalconstruct/update_icon()
icon_state = initial(pipe_type.icon_state)
if(is_pipe())
icon_state = "con[icon_state]"
if(anchored)
level = initial(pipe_type.level)
layer = initial(pipe_type.layer)
else
level = initial(level)
layer = initial(layer)
else if(ispath(pipe_type, /obj/machinery/disposal/bin))
// Disposal bins recieve special icon treating
if(anchored)
icon_state = "disposal"
else
icon_state = "condisposal"
// hide called by levelupdate if turf intact status changes
// change visibility status and force update of icon
/obj/structure/disposalconstruct/hide(var/intact)
invisibility = (intact && level==1) ? INVISIBILITY_MAXIMUM: 0 // hide if floor is intact
update_icon()
/obj/structure/disposalconstruct/proc/get_disposal_dir()
if(!is_pipe())
return NONE
var/obj/structure/disposalpipe/temp = pipe_type
var/initialize_dirs = initial(temp.initialize_dirs)
var/dpdir = NONE
if(dir in GLOB.diagonals) // Bent pipes
return dir
if(initialize_dirs != DISP_DIR_NONE)
dpdir = dir
if(initialize_dirs & DISP_DIR_LEFT)
dpdir |= turn(dir, 90)
if(initialize_dirs & DISP_DIR_RIGHT)
dpdir |= turn(dir, -90)
if(initialize_dirs & DISP_DIR_FLIP)
dpdir |= turn(dir, 180)
return dpdir
// flip and rotate verbs
/obj/structure/disposalconstruct/verb/rotate()
set name = "Rotate Pipe"
set category = "Object"
set src in view(1)
if(usr.incapacitated())
return
if(anchored)
to_chat(usr, "<span class='warning'>You must unfasten the pipe before rotating it!</span>")
return
setDir(turn(dir, -90))
update_icon()
/obj/structure/disposalconstruct/AltClick(mob/user)
..()
if(!in_range(src, user))
return
else
rotate()
/obj/structure/disposalconstruct/verb/flip()
set name = "Flip Pipe"
set category = "Object"
set src in view(1)
if(usr.incapacitated())
return
if(anchored)
to_chat(usr, "<span class='warning'>You must unfasten the pipe before flipping it!</span>")
return
setDir(turn(dir, 180))
var/obj/structure/disposalpipe/temp = pipe_type
if(initial(temp.flip_type))
if(dir in GLOB.diagonals) // Fix RPD-induced diagonal turning
setDir(turn(dir, 45))
pipe_type = initial(temp.flip_type)
update_icon()
// attackby item
// wrench: (un)anchor
// weldingtool: convert to real pipe
/obj/structure/disposalconstruct/attackby(obj/item/I, mob/user, params)
var/ispipe = is_pipe() // Indicates if we should change the level of this pipe
add_fingerprint(user)
var/turf/T = get_turf(src)
if(T.intact && isfloorturf(T))
to_chat(user, "<span class='warning'>You can only attach the [pipename] if the floor plating is removed!</span>")
return
if(!ispipe && iswallturf(T))
to_chat(user, "<span class='warning'>You can't build [pipename]s on walls, only disposal pipes!</span>")
return
if(istype(I, /obj/item/wrench))
if(anchored)
anchored = FALSE
density = FALSE
to_chat(user, "<span class='notice'>You detach the [pipename] from the underfloor.</span>")
else
if(ispipe)
var/dpdir = get_disposal_dir()
for(var/obj/structure/disposalpipe/CP in T)
var/pdir = CP.dpdir
if(istype(CP, /obj/structure/disposalpipe/broken))
pdir = CP.dir
if(pdir & dpdir)
to_chat(user, "<span class='warning'>There is already a disposal pipe at that location!</span>")
return
level = 1 // Pipes only, don't want disposal bins to disappear under the floors
else // Disposal or outlet
var/found_trunk = FALSE
for(var/obj/structure/disposalpipe/CP in T)
if(istype(CP, /obj/structure/disposalpipe/trunk))
found_trunk = TRUE
break
if(!found_trunk)
to_chat(user, "<span class='warning'>The [pipename] requires a trunk underneath it in order to work!</span>")
return
anchored = TRUE
density = initial(pipe_type.density)
to_chat(user, "<span class='notice'>You attach the [pipename] to the underfloor.</span>")
playsound(src, I.usesound, 100, 1)
update_icon()
else if(istype(I, /obj/item/weldingtool))
if(anchored)
var/obj/item/weldingtool/W = I
if(W.remove_fuel(0,user))
playsound(src, I.usesound, 50, 1)
to_chat(user, "<span class='notice'>You start welding the [pipename] in place...</span>")
if(do_after(user, 8*I.toolspeed, target = src))
if(!loc || !W.isOn())
return
to_chat(user, "<span class='notice'>The [pipename] has been welded in place.</span>")
var/obj/O = new pipe_type(loc, src)
transfer_fingerprints_to(O)
return
else
to_chat(user, "<span class='warning'>You need to attach it to the plating first!</span>")
return
/obj/structure/disposalconstruct/proc/is_pipe()
return ispath(pipe_type, /obj/structure/disposalpipe)
//helper proc that makes sure you can place the construct (i.e no dense objects stacking)
/obj/structure/disposalconstruct/proc/can_place()
if(is_pipe())
return TRUE
for(var/obj/structure/disposalconstruct/DC in get_turf(src))
if(DC == src)
continue
if(!DC.is_pipe()) //there's already a chute/outlet/bin there
return FALSE
return TRUE
+23
View File
@@ -0,0 +1,23 @@
// called when movable is expelled from a disposal pipe or outlet
// by default does nothing, override for special behaviour
/atom/movable/proc/pipe_eject(direction)
return
/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)
/obj/effect/decal/cleanable/robot_debris/gib/pipe_eject(direction)
var/list/dirs
if(direction)
dirs = list(direction, turn(direction, -45), turn(direction, 45))
else
dirs = GLOB.alldirs.Copy()
streak(dirs)
+128
View File
@@ -0,0 +1,128 @@
// 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_MAXIMUM
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
dir = NONE
var/datum/gas_mixture/gas // gas used to flush, will appear at exit point
var/active = FALSE // true if the holder is moving, otherwise inactive
var/count = 1000 // can travel 1000 steps before going inactive (in case of loops)
var/destinationTag = NONE // changes if contains a delivery container
var/tomail = FALSE // contains wrapped package
var/hasmob = FALSE // contains a mob
/obj/structure/disposalholder/ComponentInitialize()
. = ..()
AddComponent(/datum/component/rad_insulation, RAD_NO_INSULATION)
/obj/structure/disposalholder/Destroy()
QDEL_NULL(gas)
active = FALSE
return ..()
// initialize a holder from the contents of a disposal unit
/obj/structure/disposalholder/proc/init(obj/machinery/disposal/D)
gas = D.air_contents// transfer gas resv. into holder object
//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.client)
M.reset_perspective(src)
hasmob = TRUE
//Checks 1 contents level deep. This means that players can be sent through disposals mail...
//...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(locate(/mob/living) in O)
hasmob = TRUE
break
// now everything inside the disposal gets put into the holder
// note AM since can contain mobs or objs
for(var/A in D)
var/atom/movable/AM = A
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
// start the movement process
// argument is the disposal unit the holder started in
/obj/structure/disposalholder/proc/start(obj/machinery/disposal/D)
if(!D.trunk)
D.expel(src) // no trunk connected, so expel immediately
return
forceMove(D.trunk)
active = TRUE
setDir(DOWN)
move()
// movement process, persists while holder is moving through pipes
/obj/structure/disposalholder/proc/move()
set waitfor = FALSE
var/obj/structure/disposalpipe/last
while(active)
var/obj/structure/disposalpipe/curr = loc
last = curr
curr = curr.transfer(src)
if(!curr && active)
last.expel(src, loc, dir)
stoplag()
if(!(count--))
active = FALSE
// find the turf which should contain the next pipe
/obj/structure/disposalholder/proc/nextloc()
return get_step(src, 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 holder meets a stuck holder
/obj/structure/disposalholder/proc/merge(obj/structure/disposalholder/other)
for(var/A in other)
var/atom/movable/AM = A
AM.forceMove(src) // move everything in other holder to this one
if(ismob(AM))
var/mob/M = AM
M.reset_perspective(src) // if a client mob, update eye to follow this holder
qdel(other)
// called when player tries to move while in a pipe
/obj/structure/disposalholder/relaymove(mob/user)
if(user.incapacitated())
return
for(var/mob/M in range(5, get_turf(src)))
M.show_message("<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>", 2)
playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0)
// called to vent all gas in holder to a location
/obj/structure/disposalholder/proc/vent_gas(turf/T)
T.assume_air(gas)
T.air_update_turf()
/obj/structure/disposalholder/AllowDrop()
return TRUE
/obj/structure/disposalholder/ex_act(severity, target)
return
+88
View File
@@ -0,0 +1,88 @@
// the disposal outlet machine
/obj/structure/disposaloutlet
name = "disposal outlet"
desc = "An outlet for the pneumatic disposal system."
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
icon_state = "outlet"
density = TRUE
anchored = TRUE
var/active = FALSE
var/turf/target // this will be where the output objects are 'thrown' to.
var/obj/structure/disposalpipe/trunk/trunk // the attached pipe trunk
var/obj/structure/disposalconstruct/stored
var/start_eject = 0
var/eject_range = 3
/obj/structure/disposaloutlet/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
if(make_from)
setDir(make_from.dir)
make_from.forceMove(src)
stored = make_from
else
stored = new /obj/structure/disposalconstruct(src, make_from = src)
target = get_ranged_target_turf(src, dir, 10)
trunk = locate() in loc
if(trunk)
trunk.linked = src // link the pipe trunk to self
/obj/structure/disposaloutlet/ComponentInitialize()
. = ..()
AddComponent(/datum/component/rad_insulation, RAD_NO_INSULATION)
/obj/structure/disposaloutlet/Destroy()
if(trunk)
trunk.linked = null
trunk = null
QDEL_NULL(stored)
return ..()
// expel the contents of the holder object, then delete it
// called when the holder exits the outlet
/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H)
flick("outlet-open", src)
if((start_eject + 30) < world.time)
start_eject = world.time
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0)
addtimer(CALLBACK(src, .proc/expel_holder, H, TRUE), 20)
else
addtimer(CALLBACK(src, .proc/expel_holder, H), 20)
/obj/structure/disposaloutlet/proc/expel_holder(obj/structure/disposalholder/H, playsound=FALSE)
if(playsound)
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(!H)
return
var/turf/T = get_turf(src)
for(var/A in H)
var/atom/movable/AM = A
AM.forceMove(T)
AM.pipe_eject(dir)
AM.throw_at(target, eject_range, 1)
H.vent_gas(T)
qdel(H)
/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user, params)
add_fingerprint(user)
if(istype(I, /obj/item/weldingtool))
var/obj/item/weldingtool/W = I
if(W.remove_fuel(0,user))
playsound(src, 'sound/items/welder2.ogg', 100, 1)
to_chat(user, "<span class='notice'>You start slicing the floorweld off [src]...</span>")
if(do_after(user, 20*I.toolspeed, target = src))
if(!W.isOn())
return
to_chat(user, "<span class='notice'>You slice the floorweld off [src].</span>")
stored.forceMove(loc)
transfer_fingerprints_to(stored)
stored = null
qdel(src)
else
return ..()
+345
View File
@@ -0,0 +1,345 @@
// Disposal pipes
/obj/structure/disposalpipe
name = "disposal pipe"
desc = "An underfloor disposal pipe."
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
anchored = TRUE
density = FALSE
on_blueprints = TRUE
level = 1 // underfloor only
dir = NONE // dir will contain dominant direction for junction pipes
max_integrity = 200
armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes
var/dpdir = NONE // bitmask of pipe directions
var/initialize_dirs = NONE // bitflags of pipe directions added on init, see \code\_DEFINES\pipe_construction.dm
var/construct_type // If set, used as type for pipe constructs. If not set, src.type is used.
var/flip_type // If set, the pipe is flippable and becomes this type when flipped
var/obj/structure/disposalconstruct/stored
/obj/structure/disposalpipe/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
if(!QDELETED(make_from))
setDir(make_from.dir)
make_from.forceMove(src)
stored = make_from
else
stored = new /obj/structure/disposalconstruct(src, make_from=src)
// Hack for old map pipes to work, remove after all maps are updated
if(flip_type)
var/obj/structure/disposalpipe/flip = flip_type
if(icon_state == initial(flip.icon_state))
initialize_dirs = initial(flip.initialize_dirs)
construct_type = flip_type
if(dir in GLOB.diagonals) // Bent pipes already have all the dirs set
initialize_dirs = NONE
if(initialize_dirs != DISP_DIR_NONE)
dpdir = dir
if(initialize_dirs & DISP_DIR_LEFT)
dpdir |= turn(dir, 90)
if(initialize_dirs & DISP_DIR_RIGHT)
dpdir |= turn(dir, -90)
if(initialize_dirs & DISP_DIR_FLIP)
dpdir |= turn(dir, 180)
update()
/obj/structure/disposalpipe/ComponentInitialize()
. = ..()
AddComponent(/datum/component/rad_insulation, RAD_NO_INSULATION)
// pipe is deleted
// ensure if holder is present, it is expelled
/obj/structure/disposalpipe/Destroy()
var/obj/structure/disposalholder/H = locate() in src
if(H)
H.active = FALSE
expel(H, get_turf(src), 0)
QDEL_NULL(stored)
return ..()
// returns the direction of the next pipe object, given the entrance dir
// by default, returns the bitmask of remaining directions
/obj/structure/disposalpipe/proc/nextdir(obj/structure/disposalholder/H)
return dpdir & (~turn(H.dir, 180))
// transfer the holder through this pipe segment
// overriden for special behaviour
/obj/structure/disposalpipe/proc/transfer(obj/structure/disposalholder/H)
return transfer_to_dir(H, nextdir(H))
/obj/structure/disposalpipe/proc/transfer_to_dir(obj/structure/disposalholder/H, nextdir)
H.setDir(nextdir)
var/turf/T = H.nextloc()
var/obj/structure/disposalpipe/P = H.findpipe(T)
if(P)
// find other holder in next loc, if inactive merge it with current
var/obj/structure/disposalholder/H2 = locate() in P
if(H2 && !H2.active)
H.merge(H2)
H.forceMove(P)
return P
else // if wasn't a pipe, then they're now in our turf
H.forceMove(get_turf(src))
return null
// update the icon_state to reflect hidden status
/obj/structure/disposalpipe/proc/update()
var/turf/T = get_turf(src)
hide(T.intact && !isspaceturf(T)) // space never hides pipes
// hide called by levelupdate if turf intact status changes
// change visibility status and force update of icon
/obj/structure/disposalpipe/hide(var/intact)
invisibility = intact ? INVISIBILITY_MAXIMUM: 0 // hide if floor is intact
// expel the held objects into a turf
// called when there is a break in the pipe
/obj/structure/disposalpipe/proc/expel(obj/structure/disposalholder/H, turf/T, direction)
var/turf/target
var/eject_range = 5
var/turf/open/floor/floorturf
if(isfloorturf(T)) //intact floor, pop the tile
floorturf = T
if(floorturf.floor_tile)
new floorturf.floor_tile(T)
floorturf.make_plating()
if(direction) // direction is specified
if(isspaceturf(T)) // if ended in space, then range is unlimited
target = get_edge_target_turf(T, direction)
else // otherwise limit to 10 tiles
target = get_ranged_target_turf(T, direction, 10)
eject_range = 10
else if(floorturf)
target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5))
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
for(var/A in H)
var/atom/movable/AM = A
AM.forceMove(get_turf(src))
AM.pipe_eject(direction)
if(target)
AM.throw_at(target, eject_range, 1)
H.vent_gas(T)
qdel(H)
// pipe affected by explosion
/obj/structure/disposalpipe/contents_explosion(severity, target)
var/obj/structure/disposalholder/H = locate() in src
if(H)
H.contents_explosion(severity, target)
/obj/structure/disposalpipe/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
if(damage_flag == "melee" && damage_amount < 10)
return 0
return ..()
//attack by item
//weldingtool: unfasten and convert to obj/disposalconstruct
/obj/structure/disposalpipe/attackby(obj/item/I, mob/user, params)
add_fingerprint(user)
if(istype(I, /obj/item/weldingtool))
if(!can_be_deconstructed(user))
return
var/obj/item/weldingtool/W = I
if(W.remove_fuel(0, user))
playsound(src, I.usesound, 50, 1)
to_chat(user, "<span class='notice'>You start slicing [src]...</span>")
// check if anything changed over 2 seconds
if(do_after(user, 30*I.toolspeed, target = src))
if(!W.isOn())
return
deconstruct()
to_chat(user, "<span class='notice'>You slice [src].</span>")
else
return ..()
//checks if something is blocking the deconstruction (e.g. trunk with a bin still linked to it)
/obj/structure/disposalpipe/proc/can_be_deconstructed()
return TRUE
// called when pipe is cut with welder
/obj/structure/disposalpipe/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(disassembled)
if(stored)
stored.forceMove(loc)
transfer_fingerprints_to(stored)
stored.setDir(dir)
stored = null
else
var/turf/T = get_turf(src)
for(var/D in GLOB.cardinals)
if(D & dpdir)
var/obj/structure/disposalpipe/broken/P = new(T)
P.setDir(D)
qdel(src)
/obj/structure/disposalpipe/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FIVE)
deconstruct()
// Straight pipe segment
/obj/structure/disposalpipe/segment
icon_state = "pipe"
initialize_dirs = DISP_DIR_FLIP
/obj/structure/disposalpipe/segment/Initialize()
// Hacks for old map pipes to work, remove after all maps are updated
if(icon_state == "pipe-c")
switch(dir)
if(NORTH)
dir = NORTHEAST
if(SOUTH)
dir = SOUTHWEST
if(EAST)
dir = SOUTHEAST
if(WEST)
dir = NORTHWEST
if(icon_state != "pipe")
icon_state = "pipe"
. = ..()
// A three-way junction with dir being the dominant direction
/obj/structure/disposalpipe/junction
icon_state = "pipe-j1"
initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP
flip_type = /obj/structure/disposalpipe/junction/flip
/obj/structure/disposalpipe/junction/Initialize()
if(icon_state == "pipe-y") // Hack for old map pipes to work, remove after all maps are updated
initialize_dirs = DISP_DIR_LEFT | DISP_DIR_RIGHT
flip_type = null
construct_type = /obj/structure/disposalpipe/junction/yjunction
. = ..()
// 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(obj/structure/disposalholder/H)
var/flipdir = turn(H.dir, 180)
if(flipdir != dir) // came from secondary dir, so exit through primary
return dir
else // came from primary, so need to choose a secondary exit
var/mask = dpdir & (~dir) // get a mask of secondary dirs
// find one secondary dir in mask
var/secdir = NONE
for(var/D in GLOB.cardinals)
if(D & mask)
secdir = D
break
if(prob(50)) // 50% chance to choose the found secondary dir
return secdir
else // or the other one
return mask & (~secdir)
/obj/structure/disposalpipe/junction/flip
icon_state = "pipe-j2"
initialize_dirs = DISP_DIR_LEFT | DISP_DIR_FLIP
flip_type = /obj/structure/disposalpipe/junction
/obj/structure/disposalpipe/junction/yjunction
icon_state = "pipe-y"
initialize_dirs = DISP_DIR_LEFT | DISP_DIR_RIGHT
flip_type = null
//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()
. = ..()
getlinked()
/obj/structure/disposalpipe/trunk/Destroy()
if(linked)
if(istype(linked, /obj/structure/disposaloutlet))
var/obj/structure/disposaloutlet/D = linked
D.trunk = null
else if(istype(linked, /obj/machinery/disposal))
var/obj/machinery/disposal/D = linked
D.trunk = null
return ..()
/obj/structure/disposalpipe/trunk/proc/getlinked()
linked = null
var/turf/T = get_turf(src)
var/obj/machinery/disposal/D = locate() in T
if(D)
linked = D
if (!D.trunk)
D.trunk = src
var/obj/structure/disposaloutlet/O = locate() in T
if(O)
linked = O
/obj/structure/disposalpipe/trunk/can_be_deconstructed(mob/user)
if(linked)
to_chat(user, "<span class='warning'>You need to deconstruct disposal machinery above this pipe!</span>")
return FALSE
return TRUE
// 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
// otherwise, go to the linked object
if(linked)
var/obj/structure/disposaloutlet/O = linked
if(istype(O))
O.expel(H) // expel at outlet
else
var/obj/machinery/disposal/D = linked
D.expel(H) // expel at disposal
else
expel(H, get_turf(src), 0) // expel at turf
return null
/obj/structure/disposalpipe/trunk/nextdir(obj/structure/disposalholder/H)
if(H.dir == DOWN)
return dir
else
return NONE
// a broken pipe
/obj/structure/disposalpipe/broken
desc = "A broken piece of disposal pipe."
icon_state = "pipe-b"
initialize_dirs = DISP_DIR_NONE
// broken pipes always have dpdir=0 so they're not found as 'real' pipes
// i.e. will be treated as an empty turf
/obj/structure/disposalpipe/broken/deconstruct()
qdel(src)
@@ -0,0 +1,102 @@
// A three-way junction that sorts objects based on check_sorting(H) proc
// This is a base type, use subtypes on the map.
/obj/structure/disposalpipe/sorting
name = "sorting disposal pipe"
desc = "An underfloor disposal pipe with a sorting mechanism."
icon_state = "pipe-j1s"
initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP
/obj/structure/disposalpipe/sorting/nextdir(obj/structure/disposalholder/H)
var/sortdir = dpdir & ~(dir | turn(dir, 180))
if(H.dir != sortdir) // probably came from the negdir
if(check_sorting(H)) // if destination matches filtered type...
return sortdir // exit through sortdirection
// go with the flow to positive direction
return dir
// Sorting check, to be overridden in subtypes
/obj/structure/disposalpipe/sorting/proc/check_sorting(obj/structure/disposalholder/H)
return FALSE
// Mail sorting junction, uses package tags to sort objects.
/obj/structure/disposalpipe/sorting/mail
flip_type = /obj/structure/disposalpipe/sorting/mail/flip
var/sortType = 0
// sortType is to be set in map editor.
// Supports both singular numbers and strings of numbers similar to access level strings.
// Look at the list called TAGGERLOCATIONS in /_globalvars/lists/flavor_misc.dm
var/list/sortTypes = list()
/obj/structure/disposalpipe/sorting/mail/flip
flip_type = /obj/structure/disposalpipe/sorting/mail
icon_state = "pipe-j2s"
initialize_dirs = DISP_DIR_LEFT | DISP_DIR_FLIP
/obj/structure/disposalpipe/sorting/mail/Initialize()
. = ..()
// Generate a list of soring tags.
if(sortType)
if(isnum(sortType))
sortTypes |= sortType
else if(istext(sortType))
var/list/sorts = splittext(sortType,";")
for(var/x in sorts)
var/n = text2num(x)
if(n)
sortTypes |= n
/obj/structure/disposalpipe/sorting/mail/examine(mob/user)
..()
if(sortTypes.len)
to_chat(user, "It is tagged with the following tags:")
for(var/t in sortTypes)
to_chat(user, "\t[GLOB.TAGGERLOCATIONS[t]].")
else
to_chat(user, "It has no sorting tags set.")
/obj/structure/disposalpipe/sorting/mail/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/destTagger))
var/obj/item/device/destTagger/O = I
if(O.currTag)// Tagger has a tag set
if(O.currTag in sortTypes)
sortTypes -= O.currTag
to_chat(user, "<span class='notice'>Removed \"[GLOB.TAGGERLOCATIONS[O.currTag]]\" filter.</span>")
else
sortTypes |= O.currTag
to_chat(user, "<span class='notice'>Added \"[GLOB.TAGGERLOCATIONS[O.currTag]]\" filter.</span>")
playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
else
return ..()
/obj/structure/disposalpipe/sorting/mail/check_sorting(obj/structure/disposalholder/H)
return (H.destinationTag in sortTypes)
// Wrap sorting junction, sorts objects destined for the mail office mail table (tomail = 1)
/obj/structure/disposalpipe/sorting/wrap
desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects."
flip_type = /obj/structure/disposalpipe/sorting/wrap/flip
initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP
/obj/structure/disposalpipe/sorting/wrap/check_sorting(obj/structure/disposalholder/H)
return H.tomail
/obj/structure/disposalpipe/sorting/wrap/flip
icon_state = "pipe-j2s"
flip_type = /obj/structure/disposalpipe/sorting/wrap
initialize_dirs = DISP_DIR_LEFT | DISP_DIR_FLIP
// Hacks for old map pipes to work, remove after all maps are updated
/obj/structure/disposalpipe/wrapsortjunction
parent_type = /obj/structure/disposalpipe/sorting/wrap
/obj/structure/disposalpipe/sortjunction
parent_type = /obj/structure/disposalpipe/sorting/mail