Files
Aurora.3/code/game/objects/structures/crates_lockers/closets.dm
T
FluffyandGitHub 5e1332d60f OpenDream linting (#16468)
* I wanna set the universe on fire

* dfsaaf

* setup

* dir issue?

* dsf

* Perhaps the script

* saf

* cry

* pain

* sdfas

* Try reintroduction of tag-matcher

* Failed

* Tries to suppress dotnet compile warnings

* Like this maybe?

* woopsie

* Damn pathings

* Hate

* Pragmas

* unlint?

* Maybe?

* GDI

* Redundant ifdef removed and changelog

* Did I forgot the tag matcher, or it was still broken?

* Yea no that script is still broken

* Removed tag-matcher as requested

* *sigh*

* test

* bro what?

* hope

* just fixing the input

* Let's see if we catch it...
2023-06-26 17:05:38 +02:00

793 lines
26 KiB
Plaintext

/obj/structure/closet
name = "closet"
desc = "It's a basic storage unit."
icon = 'icons/obj/closet.dmi'
icon_state = "generic"
density = TRUE
build_amt = 2
slowdown = 5
var/icon_door = null
var/icon_door_override = FALSE //override to have open overlay use icon different to its base's
var/icon_door_overlay = "" //handles secure locker overlays like the locking lights
var/secure = FALSE //secure locker or not. typically it shouldn't need lights if it's insecure
var/secure_lights = FALSE // whether to display secure lights when open.
var/opened = FALSE
var/welded = FALSE
var/locked = FALSE
var/broken = FALSE
var/large = TRUE // if you can shove people in it
var/canbemoved = FALSE // if it can be moved by people using the right tools. basically means if you can change the anchored var.
var/screwed = TRUE // if its screwed in place
var/wrenched = TRUE // if its wrenched down
var/wall_mounted = FALSE //never solid (You can always pass over it)
var/health = 100
var/breakout = 0 //if someone is currently breaking out. mutex
var/storage_capacity = 45 //Tying this to mob sizes was dumb
//This is so that someone can't pack hundreds of items in a locker/crate
//then open it in a populated area to crash clients.
var/open_sound = 'sound/effects/closet_open.ogg'
var/close_sound = 'sound/effects/closet_close.ogg'
var/open_sound_volume = 35
var/close_sound_volume = 50
var/store_misc = TRUE
var/store_items = TRUE
var/store_mobs = TRUE
var/store_structure = FALSE
var/dense_when_open = FALSE
var/maximum_mob_size = 15
var/const/default_mob_size = 15
var/obj/item/closet_teleporter/linked_teleporter
var/double_doors = FALSE
var/obj/effect/overlay/closet_door/door_obj
var/obj/effect/overlay/closet_door/door_obj_alt
var/is_animating_door = FALSE
var/door_underlay = FALSE //used if you want to have an overlay below the door. used for guncabinets.
var/door_anim_squish = 0.12 // Multiplier on proc/get_door_transform. basically, how far you want this to swing out. value of 1 means the length of the door is unchanged (and will swing out of the tile), 0 means it will just slide back and forth.
var/door_anim_angle = 147
var/door_hinge = -6.5 // for closets, x away from the centre of the closet. typically good to add a 0.5 so it's centered on the edge of the closet.
var/door_hinge_alt = 6.5 // for closets with two doors. why a seperate var? because some closets may be weirdly shaped or something.
var/door_anim_time = 2.5 // set to 0 to make the door not animate at all
/obj/structure/closet/LateInitialize()
if(opened) // if closed, any item at the crate's loc is put in the contents
return
var/obj/I
for(I in loc)
if (!istype(I, /obj/item) && !istype(I, /obj/random))
continue
if (I.density || I.anchored || I == src)
continue
I.forceMove(src)
// adjust locker size to hold all items with 5 units of free store room
var/content_size = 0
for(I in contents)
content_size += Ceiling(I.w_class/2)
if(content_size > storage_capacity-5)
storage_capacity = content_size + 5
/obj/structure/closet/Initialize(mapload, var/no_fill)
. = ..()
update_icon()
if(!no_fill)
fill()
if(secure)
verbs += /obj/structure/closet/proc/verb_togglelock
return mapload ? INITIALIZE_HINT_LATELOAD : INITIALIZE_HINT_NORMAL
// Fill lockers with this.
/obj/structure/closet/proc/fill()
/obj/structure/closet/proc/content_info(mob/user, content_size)
if(!content_size)
to_chat(user, "\The [src] is empty.")
else if(storage_capacity > content_size*4)
to_chat(user, "\The [src] is barely filled.")
else if(storage_capacity > content_size*2)
to_chat(user, "\The [src] is less than half full.")
else if(storage_capacity > content_size)
to_chat(user, "\The [src] still has some free space.")
else
to_chat(user, "\The [src] is full.")
/obj/structure/closet/examine(mob/user)
if(!src.opened && (..(user, 1) || isobserver(user)))
var/content_size = 0
for(var/obj/item/I in contents)
if(!I.anchored)
content_size += Ceiling(I.w_class/2)
content_info(user, content_size)
if(!src.opened && isobserver(user))
to_chat(user, "It contains: [counting_english_list(contents)]")
if(src.opened && linked_teleporter && (Adjacent(user) || isobserver(user)))
to_chat(user, FONT_SMALL(SPAN_NOTICE("There appears to be a device attached to the interior backplate of \the [src]...")))
/obj/structure/closet/proc/stored_weight()
var/content_size = 0
for(var/obj/item/I in contents)
if(!I.anchored)
content_size += Ceiling(I.w_class/2)
return content_size
/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0 || wall_mounted)) return 1
if(istype(mover) && mover.checkpass(PASSTRACE))
return 1
return (!density)
/obj/structure/closet/proc/can_open()
if(welded || locked)
return 0
return 1
/obj/structure/closet/proc/can_close()
for(var/obj/structure/closet/closet in get_turf(src))
if(closet != src)
return 1
return 1
/obj/structure/closet/dump_contents()
//Cham Projector Exception
for(var/obj/effect/dummy/chameleon/AD in contents)
AD.forceMove(loc)
for(var/obj/I in contents - linked_teleporter)
I.forceMove(loc)
for(var/mob/M in contents)
M.forceMove(loc)
if(M.client)
M.client.eye = M.client.mob
M.client.perspective = MOB_PERSPECTIVE
/obj/structure/closet/proc/open()
if(opened)
return 0
if(!can_open())
return 0
if(climbable)
structure_shaken()
opened = TRUE
dump_contents()
animate_door(FALSE)
if(double_doors)
animate_door_alt(FALSE)
update_icon()
playsound(loc, open_sound, open_sound_volume, 0, -3)
if(!dense_when_open)
density = FALSE
return 1
/obj/structure/closet/proc/close()
if(!opened)
return 0
if(!can_close())
return 0
var/stored_units = 0
if(store_misc)
stored_units += store_misc(stored_units)
if(store_items)
stored_units += store_items(stored_units)
if(store_mobs)
stored_units += store_mobs(stored_units)
if(store_structure)
stored_units += store_structure(stored_units)
opened = FALSE
animate_door(TRUE)
if(double_doors)
animate_door_alt(TRUE)
update_icon()
if(linked_teleporter)
if(linked_teleporter.last_use + 600 > world.time)
return
var/did_teleport = FALSE
for(var/mob/M in contents)
if(linked_teleporter.do_teleport(M))
did_teleport = TRUE
if(did_teleport)
linked_teleporter.last_use = world.time
playsound(get_turf(src), close_sound, close_sound_volume, 0, -3)
density = initial(density)
return TRUE
//Chem Projector Exception
/obj/structure/closet/proc/store_misc(var/stored_units)
var/added_units = 0
for(var/obj/effect/dummy/chameleon/AD in loc)
if((stored_units + added_units) > storage_capacity)
break
AD.forceMove(src)
added_units++
return added_units
/obj/structure/closet/proc/store_items(var/stored_units)
var/added_units = 0
for(var/obj/item/I in loc)
var/item_size = Ceiling(I.w_class / 2)
if(stored_units + added_units + item_size > storage_capacity)
continue
if(!I.anchored)
I.forceMove(src)
added_units += item_size
return added_units
/obj/structure/closet/proc/store_mobs(var/stored_units)
var/added_units = 0
for(var/mob/living/M in loc)
if(M.buckled_to || M.pinned.len)
continue
if(M.mob_size >= maximum_mob_size)
continue
if(stored_units + added_units + M.mob_size > storage_capacity)
break
if(M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
M.forceMove(src)
added_units += M.mob_size
return added_units
/obj/structure/closet/proc/store_structure(var/stored_units)
var/added_units = 0
for(var/obj/O in loc)
if((stored_units + added_units) > storage_capacity)
break
if(O.density || O.anchored || istype(O,/obj/structure/closet))
continue
if(istype(O, /obj/structure/bed))
var/obj/structure/bed/B = O
if(B.buckled)
continue
O.forceMove(src)
added_units++
return added_units
/obj/structure/closet/proc/toggle(mob/user as mob)
if(!(opened ? close(user) : open(user)))
to_chat(user, SPAN_WARNING("It won't budge!"))
return
return 1
/obj/structure/closet/ex_act(severity)
switch(severity)
if(1)
health -= rand(120, 240)
if(2)
health -= rand(60, 120)
if(3)
health -= rand(30, 60)
if(health <= 0)
for (var/atom/movable/A as mob|obj in src)
A.ex_act(severity + 1)
dump_contents()
new /obj/item/stack/material/steel(get_turf(src))
qdel(src)
/obj/structure/closet/proc/damage(var/damage)
health -= damage
if(health <= 0)
dump_contents()
new /obj/item/stack/material/steel(get_turf(src))
qdel(src)
/obj/structure/closet/bullet_act(var/obj/item/projectile/Proj)
var/proj_damage = Proj.get_structure_damage()
if(!proj_damage)
return
if(Proj.penetrating || istype(Proj, /obj/item/projectile/bullet))
var/distance = get_dist(Proj.starting, get_turf(loc))
for(var/mob/living/L in contents)
Proj.attack_mob(L, distance)
..()
damage(proj_damage)
/obj/structure/closet/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/closet_teleporter))
if(linked_teleporter)
to_chat(user, SPAN_WARNING("\The [src] already has a linked teleporter!"))
return
var/obj/item/closet_teleporter/CT = W
user.visible_message(SPAN_NOTICE("\The [user] starts attaching \the [CT] to \the [src]..."), SPAN_NOTICE("You begin attaching \the [CT] to \the [src]..."), range = 3)
if(do_after(user, 30, TRUE, src))
user.visible_message(SPAN_NOTICE("\The [user] attaches \the [CT] to \the [src]."), SPAN_NOTICE("You attach \the [CT] to \the [src]."), range = 3)
linked_teleporter = CT
CT.attached_closet = src
user.drop_from_inventory(CT, src)
return
if(opened)
if(istype(W, /obj/item/grab))
var/obj/item/grab/G = W
MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet
return 0
if(W.isscrewdriver()) // Moved here so you can only detach linked teleporters when the door is open. So you can like unscrew and bolt the locker normally in most circumstances.
if(linked_teleporter)
user.visible_message(SPAN_NOTICE("\The [user] starts detaching \the [linked_teleporter] from \the [src]..."), SPAN_NOTICE("You begin detaching \the [linked_teleporter] from \the [src]..."), range = 3)
if(do_after(user, 30, TRUE, src))
user.visible_message(SPAN_NOTICE("\The [user] detaches \the [linked_teleporter] from \the [src]."), SPAN_NOTICE("You detach \the [linked_teleporter] from \the [src]."), range = 3)
linked_teleporter.attached_closet = null
user.put_in_hands(linked_teleporter)
linked_teleporter = null
return
if(W.iswelder())
var/obj/item/weldingtool/WT = W
if(WT.isOn())
user.visible_message(
SPAN_WARNING("[user] begins cutting [src] apart."),
SPAN_NOTICE("You begin cutting [src] apart."),
"You hear a welding torch on metal."
)
playsound(loc, 'sound/items/welder_pry.ogg', 50, 1)
if (!do_after(user, 2 SECONDS, act_target = src, extra_checks = CALLBACK(src, PROC_REF(is_open))))
return
if(!WT.use(0,user))
to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task."))
return
else
user.visible_message(
SPAN_NOTICE("[src] has been cut apart by [user] with [WT]."),
SPAN_NOTICE("You cut apart [src] with [WT].")
)
if(linked_teleporter)
linked_teleporter.forceMove(get_turf(src))
linked_teleporter = null
dismantle()
return
if(istype(W, /obj/item/storage/laundry_basket) && W.contents.len)
var/obj/item/storage/laundry_basket/LB = W
var/turf/T = get_turf(src)
for(var/obj/item/I in LB.contents)
LB.remove_from_storage(I, T)
user.visible_message(
SPAN_NOTICE("[user] empties \the [LB] into \the [src]."),
SPAN_NOTICE("You empty \the [LB] into \the [src]."),
SPAN_NOTICE("You hear rustling of clothes.")
)
return
if(!W.dropsafety())
return
if(W)
user.drop_from_inventory(W,loc)
else
user.drop_item()
else if(istype(W, /obj/item/device/cratescanner))
var/obj/item/device/cratescanner/Cscanner = W
if(locked)
to_chat(user, SPAN_WARNING("[W] refuses to scan [src]. Unlock it first!"))
return
if(welded)
to_chat(user, SPAN_WARNING("[W] detects that [src] is welded shut, and refuses to scan."))
return
Cscanner.print_contents(name, contents, src.loc)
else if(istype(W, /obj/item/stack/packageWrap))
return
else if(istype(W, /obj/item/ducttape))
return
else if(W.iswelder())
var/obj/item/weldingtool/WT = W
if(WT.isOn())
user.visible_message(
SPAN_WARNING("[user] begins welding [src] [welded ? "open" : "shut"]."),
SPAN_NOTICE("You begin welding [src] [welded ? "open" : "shut"]."),
"You hear a welding torch on metal."
)
playsound(loc, 'sound/items/welder_pry.ogg', 50, 1)
if(!W.use_tool(src, user, 20, volume = 50, extra_checks = CALLBACK(src, PROC_REF(is_closed))))
return
if(!WT.use(0,user))
to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task."))
return
welded = !welded
update_icon()
user.visible_message(
SPAN_WARNING("[src] has been [welded ? "welded shut" : "unwelded"] by [user]."),
SPAN_NOTICE("You weld [src] [!welded ? "open" : "shut"].")
)
else
attack_hand(user)
else if(W.isscrewdriver() && canbemoved)
if(screwed)
to_chat(user, SPAN_NOTICE("You start to unscrew \the [src] from the floor..."))
playsound(loc, W.usesound, 50, 1)
if (do_after(user, 10/W.toolspeed SECONDS, act_target = src))
to_chat(user, SPAN_NOTICE("You unscrew the locker!"))
playsound(loc, W.usesound, 50, 1)
screwed = FALSE
else if(!screwed && wrenched)
to_chat(user, SPAN_NOTICE("You start to screw the \the [src] to the floor..."))
playsound(src, 'sound/items/Welder.ogg', 80, 1)
if (do_after(user, 15/W.toolspeed SECONDS, act_target = src))
to_chat(user, SPAN_NOTICE("You screw \the [src]!"))
playsound(loc, W.usesound, 50, 1)
screwed = TRUE
else if(W.iswrench() && canbemoved)
if(wrenched && !screwed)
to_chat(user, SPAN_NOTICE("You start to unfasten the bolts holding \the [src] in place..."))
playsound(loc, W.usesound, 50, 1)
if (do_after(user, 15/W.toolspeed SECONDS, act_target = src))
to_chat(user, SPAN_NOTICE("You unfasten \the [src]'s bolts!"))
playsound(loc, W.usesound, 50, 1)
wrenched = FALSE
anchored = FALSE
else if(!wrenched)
to_chat(user, SPAN_NOTICE("You start to fasten the bolts holding the locker in place..."))
playsound(loc, W.usesound, 50, 1)
if (do_after(user, 15/W.toolspeed SECONDS, act_target = src))
to_chat(user, SPAN_NOTICE("You fasten the \the [src]'s bolts!"))
playsound(loc, W.usesound, 50, 1)
wrenched = TRUE
anchored = TRUE
else if(istype(W, /obj/item/device/hand_labeler))
var/obj/item/device/hand_labeler/HL = W
if(HL.mode == 1)
return
else
attack_hand(user)
else if(istype(W,/obj/item/card/id) && secure)
togglelock(user)
// Secure locker cutting open stuff.
else if(!opened && secure)
if(!broken && istype(W,/obj/item/material/twohanded/chainsaw))
var/obj/item/material/twohanded/chainsaw/ChainSawVar = W
ChainSawVar.cutting = 1
user.visible_message(\
SPAN_DANGER("[user.name] starts cutting \the [src] with the [W]!"),\
SPAN_WARNING("You start cutting the [src]..."),\
SPAN_NOTICE("You hear a loud buzzing sound and metal grinding on metal...")\
)
if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, PROC_REF(CanChainsaw), W)))
user.visible_message(\
SPAN_WARNING("[user.name] finishes cutting open \the [src] with the [W]."),\
SPAN_WARNING("You finish cutting open the [src]."),\
SPAN_NOTICE("You hear a metal clank and some sparks.")\
)
emag_act(INFINITY, user, SPAN_DANGER("\The [src] has been sliced open by [user] with \an [W]!"), SPAN_DANGER("You hear metal being sliced and sparks flying."))
ChainSawVar.cutting = 0
else if(istype(W, /obj/item/melee/energy/blade))//Attempt to cut open locker if locked
if(emag_act(INFINITY, user, SPAN_DANGER("\The [src] has been sliced open by [user] with \an [W]!"), SPAN_DANGER("You hear metal being sliced and sparks flying.")))
playsound(loc, 'sound/weapons/blade.ogg', 50, 1)
else
attack_hand(user)
else
attack_hand(user)
return
// helper procs for callbacks
/obj/structure/closet/proc/is_closed()
. = !opened
/obj/structure/closet/proc/is_open()
. = opened
/obj/structure/closet/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
if(istype(O, /obj/screen)) //fix for HUD elements making their way into the world -Pete
return
if(O.loc == user)
return
if(user.restrained() || user.stat || user.weakened || user.stunned || user.paralysis)
return
if((!( istype(O, /atom/movable) ) || O.anchored || !Adjacent(user) || !Adjacent(O) || !user.Adjacent(O) || user.contents.Find(src)))
return
if(!isturf(user.loc)) // are you in a container/closet/pod/etc?
return
if(!opened)
return
if(istype(O, /obj/structure/closet))
return
if(user != O)
var/turf/T = get_turf(src)
if(ismob(O))
if(large)
user.visible_message(SPAN_DANGER("<b>[user]</b> stuffs \the [O] into \the [src]!"), SPAN_NOTICE("You stuff \the [O] into \the [src]."), range = 3)
O.forceMove(T)
close()
else
to_chat(user, SPAN_NOTICE("\The [src] is too small to stuff [O] into!"))
else
O.forceMove(T)
add_fingerprint(user)
return
/obj/structure/closet/attack_ai(mob/user)
if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Robots can open/close it, but not the AI.
attack_hand(user)
/obj/structure/closet/relaymove(mob/user as mob)
if(user.stat || !isturf(loc))
return
if(!open())
to_chat(user, SPAN_NOTICE("It won't budge!"))
/obj/structure/closet/attack_hand(mob/user as mob)
add_fingerprint(user)
if(locked)
togglelock(user)
else
toggle(user)
/obj/structure/closet/verb/verb_toggleopen()
set src in oview(1)
set category = "Object"
set name = "Toggle Open"
if(!usr.canmove || usr.stat || usr.restrained())
return
if(ishuman(usr))
add_fingerprint(usr)
toggle(usr)
else
to_chat(usr, SPAN_WARNING("This mob type can't use this verb."))
/obj/structure/closet/update_icon()
if(!door_underlay)
cut_overlays()
if(!opened)
layer = OBJ_LAYER
if(welded)
add_overlay("[icon_door_overlay]welded")
if(!is_animating_door)
if(icon_door)
add_overlay("[icon_door]_door")
if(double_doors)
add_overlay("[icon_door]_door_alt")
if(!icon_door)
add_overlay("[icon_state]_door")
if(double_doors)
add_overlay("[icon_state]_door_alt")
if(secure)
update_secure_overlays()
if(secure && secure_lights)
update_secure_overlays()
else if(opened)
layer = BELOW_OBJ_LAYER
if(!is_animating_door)
add_overlay("[icon_door_override ? icon_door : icon_state]_open")
if(secure && secure_lights)
update_secure_overlays()
/obj/structure/closet/proc/update_secure_overlays()
if(broken)
add_overlay("[icon_door_overlay]emag")
else
if(locked)
add_overlay("[icon_door_overlay]locked")
else
add_overlay("[icon_door_overlay]unlocked")
/obj/structure/closet/proc/animate_door(var/closing = FALSE)
if(!door_anim_time)
return
if(!door_obj) door_obj = new
vis_contents |= door_obj
door_obj.icon = icon
door_obj.icon_state = "[icon_door || icon_state]_door"
is_animating_door = TRUE
var/num_steps = door_anim_time / world.tick_lag
for(var/I in 0 to num_steps)
var/angle = door_anim_angle * (closing ? 1 - (I/num_steps) : (I/num_steps))
var/matrix/M = get_door_transform(angle)
var/door_state = angle >= 90 ? "[icon_door_override ? icon_door : icon_state]_back" : "[icon_door || icon_state]_door"
var/door_layer = angle >= 90 ? FLOAT_LAYER : ABOVE_MOB_LAYER
if(I == 0)
door_obj.transform = M
door_obj.icon_state = door_state
door_obj.layer = door_layer
else if(I == 1)
animate(door_obj, transform = M, icon_state = door_state, layer = door_layer, time = world.tick_lag, flags = ANIMATION_END_NOW)
else
animate(transform = M, icon_state = door_state, layer = door_layer, time = world.tick_lag)
addtimer(CALLBACK(src, PROC_REF(end_door_animation)),door_anim_time,TIMER_UNIQUE|TIMER_OVERRIDE)
/obj/structure/closet/proc/end_door_animation()
is_animating_door = FALSE // comment this out and the line below to manually tweak the animation end state by fiddling with the door_anim vars to match the open door icon
vis_contents -= door_obj
update_icon()
compile_overlays(src)
/obj/structure/closet/proc/animate_door_alt(var/closing = FALSE)
if(!door_anim_time)
return
if(!door_obj_alt) door_obj_alt = new
vis_contents |= door_obj_alt
door_obj_alt.icon = icon
door_obj_alt.icon_state = "[icon_door || icon_state]_door_alt"
is_animating_door = TRUE
var/num_steps = door_anim_time / world.tick_lag
for(var/I in 0 to num_steps)
var/angle = door_anim_angle * (closing ? 1 - (I/num_steps) : (I/num_steps))
var/matrix/M = get_door_transform(angle, TRUE)
var/door_state = angle >= 90 ? "[icon_door_override ? icon_door : icon_state]_back_alt" : "[icon_door || icon_state]_door_alt"
var/door_layer = angle >= 90 ? FLOAT_LAYER : ABOVE_MOB_LAYER
if(I == 0)
door_obj_alt.transform = M
door_obj_alt.icon_state = door_state
door_obj_alt.layer = door_layer
else if(I == 1)
animate(door_obj_alt, transform = M, icon_state = door_state, layer = door_layer, time = world.tick_lag, flags = ANIMATION_END_NOW)
else
animate(transform = M, icon_state = door_state, layer = door_layer, time = world.tick_lag)
addtimer(CALLBACK(src, PROC_REF(end_door_animation_alt)),door_anim_time,TIMER_UNIQUE|TIMER_OVERRIDE)
/obj/structure/closet/proc/end_door_animation_alt()
is_animating_door = FALSE // comment this out and the line below to manually tweak the animation end state by fiddling with the door_anim vars to match the open door icon
vis_contents -= door_obj_alt
update_icon()
compile_overlays(src)
/obj/structure/closet/proc/get_door_transform(angle, var/inverse_hinge = FALSE)
var/matrix/M = matrix()
var/matrix_door_hinge = inverse_hinge ? door_hinge_alt : door_hinge
M.Translate(-matrix_door_hinge, 0)
M.Multiply(matrix(cos(angle), 0, 0, ((matrix_door_hinge >= 0) ? sin(angle) : -sin(angle)) * door_anim_squish, 1, 0)) // this matrix door hinge >= 0 check is for door hinges on the right, so they swing out instead of upwards
M.Translate(matrix_door_hinge, 0)
return M
/obj/structure/closet/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
for (var/atom/A in src)
if(istype(A,/obj/))
var/obj/O = A
O.hear_talk(M, text, verb, speaking)
/obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys", var/wallbreaker)
if(!damage || !wallbreaker)
return
user.do_attack_animation(src)
visible_message(SPAN_DANGER("[user] [attack_message] the [src]!"))
dump_contents()
QDEL_IN(src, 1)
return 1
/obj/structure/closet/proc/req_breakout()
if(!opened) //Door's open... wait, why are you in it's contents then?
if(locked)
if(welded)
return 2
else
return 1
if(breakout)
return -1 //Already breaking out.
else
return 0
/obj/structure/closet/proc/mob_breakout(var/mob/living/escapee)
//Improved by nanako
//Now it actually works, also locker breakout time stacks with locking and welding
//This means secure lockers are more useful for imprisoning people
var/breakout_time = 1.5 * req_breakout()//1.5 minutes if locked or welded, 3 minutes if both
if(breakout_time <= 0)
return
//okay, so the closet is either welded or locked... resist!!!
escapee.next_move = world.time + 100
escapee.last_special = world.time + 100
to_chat(escapee, SPAN_WARNING("You lean on the back of \the [src] and start pushing the door open. (This will take about [breakout_time] minutes.)"))
visible_message(SPAN_DANGER("\The [src] begins to shake violently!"), SPAN_DANGER("You hear the sound of metal thrashing around nearby."), intent_message = THUNK_SOUND)
var/time = 6 * breakout_time * 2
var/datum/progressbar/bar
if (escapee.client && escapee.client.prefs.toggles_secondary & PROGRESS_BARS)
bar = new(escapee, time, src)
breakout = 1
for(var/i in 1 to time) //minutes * 6 * 5seconds * 2
playsound(loc, 'sound/effects/grillehit.ogg', 100, 1)
shake_animation()
intent_message(THUNK_SOUND)
if (bar)
bar.update(i)
if(!do_after(escapee, 50, display_progress = FALSE)) //5 seconds
breakout = 0
qdel(bar)
return
if(!escapee || escapee.stat || escapee.loc != src)
breakout = 0
qdel(bar)
return //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened
//Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'...
if(!req_breakout())
breakout = 0
qdel(bar)
return
//Well then break it!
breakout = 0
playsound(loc, 'sound/effects/grillehit.ogg', 100, 1)
break_open(escapee)
shake_animation()
qdel(bar)
/obj/structure/closet/proc/break_open(mob/user)
if(secure && !broken)
to_chat(user, SPAN_WARNING("You successfully break out!"))
welded = FALSE
emag_act(INFINITY, user, SPAN_DANGER("\The [user] successfully breaks out of \the [src]!"), SPAN_DANGER("You hear the sound of metal being forced apart!"))
//Do this to prevent contents from being opened into nullspace (read: bluespace)
if(istype(loc, /obj/structure/bigDelivery))
var/obj/structure/bigDelivery/BD = loc
BD.unwrap()
open()
/obj/structure/closet/onDropInto(var/atom/movable/AM)
return
/obj/structure/closet/crush_act()
for (var/atom/movable/A in src)
if(istype(A, /mob/living))
var/mob/living/M = A
M.gib()
else if(A.simulated)
A.ex_act(1)
if(linked_teleporter)
linked_teleporter.forceMove(get_turf(src))
linked_teleporter = null
dump_contents()
new /obj/item/stack/material/steel(get_turf(src))
qdel(src)
/obj/structure/closet/Destroy()
if(linked_teleporter)
QDEL_NULL(linked_teleporter)
return ..()
/*
==========================
Contents Scanner
==========================
*/
/obj/item/device/cratescanner
name = "crate contents scanner"
desc = "A handheld device used to scan and print a manifest of a container's contents. Does not work on locked crates, for privacy reasons."
icon_state = "cratescanner"
matter = list(DEFAULT_WALL_MATERIAL = 250, MATERIAL_GLASS = 140)
w_class = ITEMSIZE_SMALL
item_state = "electronic"
flags = CONDUCT
slot_flags = SLOT_BELT
/obj/item/device/cratescanner/proc/print_contents(targetname, targetcontents, targetloc)
var/output = list()
var/list/outputstring
for(var/atom/item in targetcontents)
if(item.name in output)
output[item.name] = output[item.name] + 1
else
output[item.name] = 1
for(var/entry in output)
outputstring += "- [entry]: [output[entry]]x<br>"
var/obj/item/paper/printout = new /obj/item/paper(targetloc)
printout.info = "contents of [targetname]<br>"
printout.info += "[outputstring]"