@@ -0,0 +1,543 @@
|
||||
// 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
|
||||
// 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(istype(I, /obj/item/screwdriver))
|
||||
panel_open = !panel_open
|
||||
I.play_tool_sound(src)
|
||||
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)
|
||||
if(!I.tool_start_check(user, amount=0))
|
||||
return
|
||||
|
||||
to_chat(user, "<span class='notice'>You start slicing the floorweld off \the [src]...</span>")
|
||||
if(I.use_tool(src, user, 20, volume=100) && panel_open)
|
||||
to_chat(user, "<span class='notice'>You slice the floorweld off \the [src].</span>")
|
||||
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 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(!can_stuff_mob_in(target, user))
|
||||
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>")
|
||||
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, "<span class='warning'>[target] doesn't fit inside [src]!</span>")
|
||||
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
|
||||
|
||||
/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from)
|
||||
. = ..()
|
||||
ui = new /datum/oracle_ui/themed/nano(src, 330, 190, "disposal_bin")
|
||||
ui.auto_refresh = TRUE
|
||||
ui.can_resize = FALSE
|
||||
|
||||
// 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, "<span class='warning'>You empty the bag.</span>")
|
||||
for(var/obj/item/O in T.contents)
|
||||
STR.remove_from_storage(O,src)
|
||||
T.update_icon()
|
||||
update_icon()
|
||||
ui.soft_update_fields()
|
||||
else
|
||||
ui.soft_update_fields()
|
||||
return ..()
|
||||
|
||||
// handle machine interaction
|
||||
|
||||
/obj/machinery/disposal/bin/ui_interact(mob/user, state)
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
if(user.loc == src)
|
||||
to_chat(user, "<span class='warning'>You cannot reach the controls from inside!</span>")
|
||||
return
|
||||
ui.render(user)
|
||||
|
||||
/obj/machinery/disposal/bin/oui_canview(mob/user)
|
||||
if(user.loc == src)
|
||||
return FALSE
|
||||
if(stat & BROKEN)
|
||||
return FALSE
|
||||
if(Adjacent(user))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/machinery/disposal/bin/oui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["flush"] = flush ? ui.act("Disengage", user, "handle-0", class="active") : ui.act("Engage", user, "handle-1")
|
||||
data["full_pressure"] = full_pressure ? "Ready" : (pressure_charging ? "Pressurizing" : "Off")
|
||||
data["pressure_charging"] = pressure_charging ? ui.act("Turn Off", user, "pump-0", class="active", disabled=full_pressure) : ui.act("Turn On", user, "pump-1", disabled=full_pressure)
|
||||
var/per = full_pressure ? 100 : CLAMP(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 99)
|
||||
data["per"] = "[round(per, 1)]%"
|
||||
data["contents"] = ui.act("Eject Contents", user, "eject", disabled=contents.len < 1)
|
||||
data["isai"] = isAI(user)
|
||||
return data
|
||||
|
||||
/obj/machinery/disposal/bin/oui_act(mob/user, action, list/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
|
||||
ui.soft_update_fields()
|
||||
|
||||
/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)
|
||||
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/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("<span class='danger'>[user.name] shoves [target.name] into \the [src]!</span>",
|
||||
"<span class='danger'>You shove [target.name] into \the [src]!</span>", 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()
|
||||
ui.soft_update_fields()
|
||||
|
||||
/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
|
||||
|
||||
ui.soft_update_fields()
|
||||
|
||||
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, "<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,187 @@
|
||||
// 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/Initialize(loc, _pipe_type, _dir = SOUTH, flip = FALSE, obj/make_from)
|
||||
. = ..()
|
||||
if(make_from)
|
||||
pipe_type = make_from.type
|
||||
setDir(make_from.dir)
|
||||
anchored = TRUE
|
||||
|
||||
else
|
||||
if(_pipe_type)
|
||||
pipe_type = _pipe_type
|
||||
setDir(_dir)
|
||||
|
||||
pipename = initial(pipe_type.name)
|
||||
|
||||
if(flip)
|
||||
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
|
||||
rotcomp.BaseRot(null,ROTATION_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 receive 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
|
||||
|
||||
/obj/structure/disposalconstruct/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_FLIP | ROTATION_VERBS ,null,CALLBACK(src, .proc/can_be_rotated), CALLBACK(src, .proc/after_rot))
|
||||
|
||||
/obj/structure/disposalconstruct/proc/after_rot(mob/user,rotation_type)
|
||||
if(rotation_type == ROTATION_FLIP)
|
||||
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()
|
||||
|
||||
/obj/structure/disposalconstruct/proc/can_be_rotated(mob/user,rotation_type)
|
||||
if(anchored)
|
||||
to_chat(user, "<span class='warning'>You must unfasten the pipe before rotating it!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// construction/deconstruction
|
||||
// wrench: (un)anchor
|
||||
// weldingtool: convert to real pipe
|
||||
/obj/structure/disposalconstruct/wrench_act(mob/living/user, obj/item/I)
|
||||
if(anchored)
|
||||
anchored = FALSE
|
||||
density = FALSE
|
||||
to_chat(user, "<span class='notice'>You detach the [pipename] from the underfloor.</span>")
|
||||
else
|
||||
var/ispipe = is_pipe() // Indicates if we should change the level of this pipe
|
||||
|
||||
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 TRUE
|
||||
|
||||
if(!ispipe && iswallturf(T))
|
||||
to_chat(user, "<span class='warning'>You can't build [pipename]s on walls, only disposal pipes!</span>")
|
||||
return TRUE
|
||||
|
||||
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 TRUE
|
||||
|
||||
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 TRUE
|
||||
|
||||
anchored = TRUE
|
||||
density = initial(pipe_type.density)
|
||||
to_chat(user, "<span class='notice'>You attach the [pipename] to the underfloor.</span>")
|
||||
I.play_tool_sound(src, 100)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/structure/disposalconstruct/welder_act(mob/living/user, obj/item/I)
|
||||
if(anchored)
|
||||
if(!I.tool_start_check(user, amount=0))
|
||||
return TRUE
|
||||
|
||||
to_chat(user, "<span class='notice'>You start welding the [pipename] in place...</span>")
|
||||
if(I.use_tool(src, user, 8, volume=50))
|
||||
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)
|
||||
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need to attach it to the plating first!</span>")
|
||||
return TRUE
|
||||
|
||||
/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
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
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/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
|
||||
if(AM == src)
|
||||
continue
|
||||
SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, 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
|
||||
else if(istype(AM, /mob/living/silicon/robot))
|
||||
var/obj/item/destTagger/borg/tagger = locate() in AM
|
||||
if (tagger)
|
||||
src.destinationTag = tagger.currTag
|
||||
|
||||
|
||||
// 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
|
||||
@@ -0,0 +1,196 @@
|
||||
/obj/structure/bigDelivery
|
||||
name = "large parcel"
|
||||
desc = "A large delivery parcel."
|
||||
icon = 'icons/obj/storage.dmi'
|
||||
icon_state = "deliverycloset"
|
||||
density = TRUE
|
||||
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
|
||||
var/giftwrapped = FALSE
|
||||
var/sortTag = 0
|
||||
|
||||
/obj/structure/bigDelivery/interact(mob/user)
|
||||
playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/bigDelivery/Destroy()
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/atom/movable/AM in contents)
|
||||
AM.forceMove(T)
|
||||
return ..()
|
||||
|
||||
/obj/structure/bigDelivery/contents_explosion(severity, target)
|
||||
for(var/atom/movable/AM in contents)
|
||||
AM.ex_act()
|
||||
|
||||
/obj/structure/bigDelivery/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/destTagger))
|
||||
var/obj/item/destTagger/O = W
|
||||
|
||||
if(sortTag != O.currTag)
|
||||
var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag])
|
||||
to_chat(user, "<span class='notice'>*[tag]*</span>")
|
||||
sortTag = O.currTag
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 100, 1)
|
||||
|
||||
else if(istype(W, /obj/item/pen))
|
||||
if(!user.is_literate())
|
||||
to_chat(user, "<span class='notice'>You scribble illegibly on the side of [src]!</span>")
|
||||
return
|
||||
var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN)
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
if(!str || !length(str))
|
||||
to_chat(user, "<span class='warning'>Invalid text!</span>")
|
||||
return
|
||||
user.visible_message("[user] labels [src] as [str].")
|
||||
name = "[name] ([str])"
|
||||
|
||||
else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped)
|
||||
var/obj/item/stack/wrapping_paper/WP = W
|
||||
if(WP.use(3))
|
||||
user.visible_message("[user] wraps the package in festive paper!")
|
||||
giftwrapped = TRUE
|
||||
icon_state = "gift[icon_state]"
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need more paper!</span>")
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/bigDelivery/relay_container_resist(mob/living/user, obj/O)
|
||||
if(ismovableatom(loc))
|
||||
var/atom/movable/AM = loc //can't unwrap the wrapped container if it's inside something.
|
||||
AM.relay_container_resist(user, O)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You lean on the back of [O] and start pushing to rip the wrapping around it.</span>")
|
||||
if(do_after(user, 50, target = O))
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != O || O.loc != src )
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You successfully removed [O]'s wrapping !</span>")
|
||||
O.forceMove(loc)
|
||||
playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1)
|
||||
qdel(src)
|
||||
else
|
||||
if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded.
|
||||
to_chat(user, "<span class='warning'>You fail to remove [O]'s wrapping!</span>")
|
||||
|
||||
|
||||
/obj/item/smallDelivery
|
||||
name = "parcel"
|
||||
desc = "A brown paper delivery parcel."
|
||||
icon = 'icons/obj/storage.dmi'
|
||||
icon_state = "deliverypackage3"
|
||||
var/giftwrapped = 0
|
||||
var/sortTag = 0
|
||||
|
||||
/obj/item/smallDelivery/contents_explosion(severity, target)
|
||||
for(var/atom/movable/AM in contents)
|
||||
AM.ex_act()
|
||||
|
||||
/obj/item/smallDelivery/attack_self(mob/user)
|
||||
user.temporarilyRemoveItemFromInventory(src, TRUE)
|
||||
for(var/X in contents)
|
||||
var/atom/movable/AM = X
|
||||
user.put_in_hands(AM)
|
||||
playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/smallDelivery/attack_self_tk(mob/user)
|
||||
if(ismob(loc))
|
||||
var/mob/M = loc
|
||||
M.temporarilyRemoveItemFromInventory(src, TRUE)
|
||||
for(var/X in contents)
|
||||
var/atom/movable/AM = X
|
||||
M.put_in_hands(AM)
|
||||
else
|
||||
for(var/X in contents)
|
||||
var/atom/movable/AM = X
|
||||
AM.forceMove(src.loc)
|
||||
playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/smallDelivery/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/destTagger))
|
||||
var/obj/item/destTagger/O = W
|
||||
|
||||
if(sortTag != O.currTag)
|
||||
var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag])
|
||||
to_chat(user, "<span class='notice'>*[tag]*</span>")
|
||||
sortTag = O.currTag
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 100, 1)
|
||||
|
||||
else if(istype(W, /obj/item/pen))
|
||||
if(!user.is_literate())
|
||||
to_chat(user, "<span class='notice'>You scribble illegibly on the side of [src]!</span>")
|
||||
return
|
||||
var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN)
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
if(!str || !length(str))
|
||||
to_chat(user, "<span class='warning'>Invalid text!</span>")
|
||||
return
|
||||
user.visible_message("[user] labels [src] as [str].")
|
||||
name = "[name] ([str])"
|
||||
|
||||
else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped)
|
||||
var/obj/item/stack/wrapping_paper/WP = W
|
||||
if(WP.use(1))
|
||||
icon_state = "gift[icon_state]"
|
||||
giftwrapped = 1
|
||||
user.visible_message("[user] wraps the package in festive paper!")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need more paper!</span>")
|
||||
|
||||
|
||||
/obj/item/destTagger
|
||||
name = "destination tagger"
|
||||
desc = "Used to set the destination of properly wrapped packages."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "cargotagger"
|
||||
var/currTag = 0 //Destinations are stored in code\globalvars\lists\flavor_misc.dm
|
||||
var/locked_destination = FALSE //if true, users can't open the destination tag window to prevent changing the tagger's current destination
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
flags_1 = CONDUCT_1
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
|
||||
/obj/item/destTagger/borg
|
||||
name = "cyborg destination tagger"
|
||||
desc = "Used to fool the disposal mail network into thinking that you're a harmless parcel. Does actually work as a regular destination tagger as well."
|
||||
|
||||
/obj/item/destTagger/suicide_act(mob/living/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins tagging [user.p_their()] final destination! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
if (islizard(user))
|
||||
to_chat(user, "<span class='notice'>*HELL*</span>")//lizard nerf
|
||||
else
|
||||
to_chat(user, "<span class='notice'>*HEAVEN*</span>")
|
||||
playsound(src, 'sound/machines/twobeep.ogg', 100, 1)
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/destTagger/proc/openwindow(mob/user)
|
||||
var/dat = "<tt><center><h1><b>TagMaster 2.2</b></h1></center>"
|
||||
|
||||
dat += "<table style='width:100%; padding:4px;'><tr>"
|
||||
for (var/i = 1, i <= GLOB.TAGGERLOCATIONS.len, i++)
|
||||
dat += "<td><a href='?src=[REF(src)];nextTag=[i]'>[GLOB.TAGGERLOCATIONS[i]]</a></td>"
|
||||
|
||||
if(i%4==0)
|
||||
dat += "</tr><tr>"
|
||||
|
||||
dat += "</tr></table><br>Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]</tt>"
|
||||
|
||||
user << browse(dat, "window=destTagScreen;size=450x350")
|
||||
onclose(user, "destTagScreen")
|
||||
|
||||
/obj/item/destTagger/attack_self(mob/user)
|
||||
if(!locked_destination)
|
||||
openwindow(user)
|
||||
return
|
||||
|
||||
/obj/item/destTagger/Topic(href, href_list)
|
||||
add_fingerprint(usr)
|
||||
if(href_list["nextTag"])
|
||||
var/n = text2num(href_list["nextTag"])
|
||||
currTag = n
|
||||
openwindow(usr)
|
||||
Reference in New Issue
Block a user