/obj/structure/closet name = "closet" desc = "It's a basic storage unit." icon = 'icons/obj/containers/closet.dmi' icon_state = "generic" density = TRUE build_amt = 2 slowdown = 5 pass_flags_self = PASSSTRUCTURE | LETPASSCLICKS | PASSTRACE var/icon_door = null /// Override to have open overlay use icon different to its base's var/icon_door_override = FALSE /// Handles secure locker overlays like the locking lights var/icon_door_overlay = "" /// Secure locker or not. typically it shouldn't need lights if it's insecure var/secure = FALSE /// Whether to display secure lights when open. var/secure_lights = FALSE var/opened = FALSE var/welded = FALSE var/locked = FALSE var/broken = FALSE /// If you can shove people in it. var/large = TRUE /// If it can be moved by people using the right tools. basically means if you can change the anchored var. var/canbemoved = FALSE /// If its screwed in place var/screwed = TRUE /// If its wrenched down var/wrenched = TRUE /// Never solid (You can always pass over it) var/wall_mounted = FALSE var/health = 100 /// If someone is currently breaking out. mutex var/breakout = 0 /// 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/storage_capacity = 45 //Tying this to mob sizes was dumb 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 /// The overlay for the closet's door var/obj/effect/overlay/closet_door/door_obj var/obj/effect/overlay/closet_door/door_obj_alt var/is_animating_door = FALSE /// Used if you want to have an overlay below the door. used for guncabinets. var/door_underlay = FALSE /// 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_squish = 0.12 // DON'T TOUCH!!! /// The maximum angle the door will be drawn at var/door_anim_angle = 136 // DON'T TOUCH!!! /// X position of the closet door hinge, relative to the center of the sprite var/door_hinge_x = -6.5 /// For closets with two doors. why a seperate var? because some closets may be weirdly shaped or something. var/door_hinge_x_alt = 6.5 /// Set to 0 to make the door not animate at all var/door_anim_time = 2.5 /// Used by body bags and air bubbles. var/contains_body = FALSE /obj/structure/closet/mechanics_hints(mob/user, distance, is_adjacent) . += ..() . += "When closed, a welder could be used to weld the closet shut." /obj/structure/closet/disassembly_hints(mob/user, distance, is_adjacent) . += ..() . += "When opened, a welder could be used to cut the closet back into steel sheets." /obj/structure/closet/antagonist_hints(mob/user, distance, is_adjacent) . += ..() . += "Using a Closet Teleporter (Stealth & Camouflage uplink item) on this can turn it into a quick transportation method- just don't get caught!" /obj/structure/closet/feedback_hints(mob/user, distance, is_adjacent) . += ..() if(!src.opened && isghost(user)) . += "It contains: [counting_english_list(contents)]" if(distance <= 1 && !src.opened) var/content_size = 0 for(var/obj/item/I in contents) if(!I.anchored) content_size += Ceiling(I.w_class/2) if(!content_size) . += "\The [src] is empty." else if(storage_capacity > content_size*4) . += "\The [src] is barely filled." else if(storage_capacity > content_size*2) . += "\The [src] is less than half full." else if(storage_capacity > content_size) . += "\The [src] still has some free space." else . += "\The [src] is full." if(src.opened && linked_teleporter && is_adjacent) . += SPAN_WARNING("There appears to be a device screwed onto the interior backplate of \the [src]...") /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 /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/Destroy() QDEL_NULL(linked_teleporter) QDEL_NULL(door_obj) QDEL_NULL(door_obj_alt) . = ..() /// Fill lockers with this. /obj/structure/closet/proc/fill() return /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 TRUE return ..() /obj/structure/closet/proc/can_open() if(welded || locked) return FALSE if(istype(loc, /obj/structure/crate_shelf)) return FALSE return TRUE /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 M.set_fullscreen(FALSE, "closet_impaired", /atom/movable/screen/fullscreen/closet_impaired) /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 && linked_teleporter.last_use + 600 < world.time) 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/mob_limit) 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.set_fullscreen(TRUE, "closet_impaired", /atom/movable/screen/fullscreen/closet_impaired) M.forceMove(src) added_units += M.mob_size if(mob_limit) //We only want to store one valid mob break 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(obj/projectile/hitting_projectile, def_zone, piercing_hit) . = ..() if(. != BULLET_ACT_HIT) return . var/proj_damage = hitting_projectile.get_structure_damage() if(!proj_damage) return BULLET_ACT_BLOCK if(hitting_projectile.penetrating || istype(hitting_projectile, /obj/projectile/bullet)) for(var/mob/living/L in contents) hitting_projectile.Impact(L) damage(proj_damage) /obj/structure/closet/attackby(obj/item/attacking_item, mob/user) if(istype(attacking_item, /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 = attacking_item 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, src, DO_REPAIR_CONSTRUCT)) 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(attacking_item, /obj/item/grab)) var/obj/item/grab/G = attacking_item mouse_drop_receive(G.affecting, user) //act like they were dragged onto the closet return 0 if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER) // 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, src, DO_REPAIR_CONSTRUCT)) 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(attacking_item.tool_behaviour == TOOL_WELDER) var/obj/item/weldingtool/WT = attacking_item 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, src, DO_REPAIR_CONSTRUCT)) 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(attacking_item, /obj/item/storage/laundry_basket) && attacking_item.contents.len) var/obj/item/storage/laundry_basket/LB = attacking_item 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(!attacking_item.dropsafety()) return if(attacking_item) user.drop_from_inventory(attacking_item,loc) else user.drop_item() else if(istype(attacking_item, /obj/item/cratescanner)) var/obj/item/cratescanner/Cscanner = attacking_item if(locked) to_chat(user, SPAN_WARNING("[attacking_item] refuses to scan \the [src]. Unlock it first!")) return if(welded) to_chat(user, SPAN_WARNING("[attacking_item] detects that \the [src] is welded shut, and refuses to scan.")) return if(istype(loc, /obj/structure/crate_shelf)) to_chat(user, SPAN_WARNING("[attacking_item] can't scan \the [src] while it is on \the [loc].")) return Cscanner.print_contents(name, contents, src.loc) else if(istype(attacking_item, /obj/item/stack/packageWrap)) return else if(istype(attacking_item, /obj/item/ducttape)) return else if(attacking_item.tool_behaviour == TOOL_WELDER) var/obj/item/weldingtool/WT = attacking_item 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(!attacking_item.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(attacking_item.tool_behaviour == TOOL_SCREWDRIVER && canbemoved) if(screwed) to_chat(user, SPAN_NOTICE("You start to unscrew \the [src] from the floor...")) attacking_item.play_tool_sound(get_turf(src), 50) if (do_after(user, 10/attacking_item.toolspeed SECONDS, src, DO_REPAIR_CONSTRUCT)) to_chat(user, SPAN_NOTICE("You unscrew the locker!")) attacking_item.play_tool_sound(get_turf(src), 50) 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/attacking_item.toolspeed SECONDS, src, DO_REPAIR_CONSTRUCT)) to_chat(user, SPAN_NOTICE("You screw \the [src]!")) attacking_item.play_tool_sound(get_turf(src), 50) screwed = TRUE else if(attacking_item.tool_behaviour == TOOL_WRENCH && canbemoved) if(wrenched && !screwed) to_chat(user, SPAN_NOTICE("You start to unfasten the bolts holding \the [src] in place...")) attacking_item.play_tool_sound(get_turf(src), 50) if (do_after(user, 15/attacking_item.toolspeed SECONDS, src, DO_REPAIR_CONSTRUCT)) to_chat(user, SPAN_NOTICE("You unfasten \the [src]'s bolts!")) attacking_item.play_tool_sound(get_turf(src), 50) wrenched = FALSE anchored = FALSE else if(!wrenched) to_chat(user, SPAN_NOTICE("You start to fasten the bolts holding the locker in place...")) attacking_item.play_tool_sound(get_turf(src), 50) if (do_after(user, 15/attacking_item.toolspeed SECONDS, src, DO_REPAIR_CONSTRUCT)) to_chat(user, SPAN_NOTICE("You fasten the \the [src]'s bolts!")) attacking_item.play_tool_sound(get_turf(src), 50) wrenched = TRUE anchored = TRUE else if(istype(attacking_item, /obj/item/hand_labeler)) var/obj/item/hand_labeler/HL = attacking_item if(HL.mode == 1) return else attack_hand(user) else if(istype(attacking_item,/obj/item/card/id) && secure) togglelock(user) // Secure locker cutting open stuff. else if(!opened && secure) if(!broken && istype(attacking_item,/obj/item/material/twohanded/chainsaw)) var/obj/item/material/twohanded/chainsaw/ChainSawVar = attacking_item ChainSawVar.cutting = 1 user.visible_message(\ SPAN_DANGER("[user.name] starts cutting \the [src] with the [attacking_item]!"),\ 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, user, DO_REPAIR_CONSTRUCT)) user.visible_message(\ SPAN_WARNING("[user.name] finishes cutting open \the [src] with the [attacking_item]."),\ 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 [attacking_item]!"), SPAN_DANGER("You hear metal being sliced and sparks flying.")) ChainSawVar.cutting = 0 else if(istype(attacking_item, /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 [attacking_item]!"), 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() SHOULD_NOT_SLEEP(TRUE) SHOULD_BE_PURE(TRUE) . = !opened /obj/structure/closet/proc/is_open() SHOULD_NOT_SLEEP(TRUE) SHOULD_BE_PURE(TRUE) . = opened /obj/structure/closet/mouse_drop_receive(atom/dropped, mob/user, params) var/atom/movable/screen/O = dropped if(istype(O, /atom/movable/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("[user] 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/living/user, direction) . = ..() if(user.stat || !isturf(loc) || user.loc != src || open()) return 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) || (issilicon(usr) && Adjacent(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) ClearOverlays() if(!opened) layer = OBJ_LAYER if(welded) AddOverlays("[icon_door_overlay]welded") if(!is_animating_door) if(icon_door) AddOverlays("[icon_door]_door") if(double_doors) AddOverlays("[icon_door]_door_alt") if(!icon_door) AddOverlays("[icon_state]_door") if(double_doors) AddOverlays("[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) AddOverlays("[icon_door_override ? icon_door : icon_state]_open") if(secure && secure_lights) update_secure_overlays() /obj/structure/closet/proc/update_secure_overlays() AddOverlays("[icon_door_overlay]securitypanel") if(broken) AddOverlays("[icon_door_overlay]emag") else if(locked) AddOverlays("[icon_door_overlay]locked") else AddOverlays("[icon_door_overlay]unlocked") /obj/structure/closet/proc/animate_door(var/closing = FALSE) if(!door_anim_time) return if(!door_obj) door_obj = new var/default_door_icon = "[icon_door || icon_state]_door" vis_contents |= door_obj door_obj.icon = icon door_obj.icon_state = default_door_icon is_animating_door = TRUE var/num_steps = door_anim_time / world.tick_lag for(var/step in 0 to num_steps) var/angle = door_anim_angle * (closing ? 1 - (step/num_steps) : (step/num_steps)) var/matrix/door_transform = get_door_transform(angle) var/door_state var/door_layer if (angle >= 90) door_state = "[icon_door_override ? icon_door : icon_state]_back" door_layer = FLOAT_LAYER else door_state = default_door_icon door_layer = ABOVE_HUMAN_LAYER if(step == 0) door_obj.transform = door_transform door_obj.icon_state = door_state door_obj.layer = door_layer else if(step == 1) animate(door_obj, transform = door_transform, icon_state = door_state, layer = door_layer, time = world.tick_lag, flags = ANIMATION_END_NOW) else animate(transform = door_transform, 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|TIMER_CLIENT_TIME) /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 remove_vis_contents(door_obj) update_icon() UpdateOverlays(src) /obj/structure/closet/proc/animate_door_alt(var/closing = FALSE) if(!door_anim_time) return if(!door_obj_alt) door_obj_alt = new var/default_door_icon = "[icon_door || icon_state]_door_alt" vis_contents |= door_obj_alt door_obj_alt.icon = icon door_obj_alt.icon_state = default_door_icon is_animating_door = TRUE var/num_steps = door_anim_time / world.tick_lag for(var/step in 0 to num_steps) var/angle = door_anim_angle * (closing ? 1 - (step/num_steps) : (step/num_steps)) var/matrix/door_transform = get_door_transform(angle, TRUE) var/door_state var/door_layer if (angle >= 90) door_state = "[icon_door_override ? icon_door : icon_state]_back_alt" door_layer = FLOAT_LAYER else door_state = default_door_icon door_layer = ABOVE_HUMAN_LAYER if(step == 0) door_obj_alt.transform = door_transform door_obj_alt.icon_state = door_state door_obj_alt.layer = door_layer else if(step == 1) animate(door_obj_alt, transform = door_transform, icon_state = door_state, layer = door_layer, time = world.tick_lag, flags = ANIMATION_END_NOW) else animate(transform = door_transform, 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|TIMER_CLIENT_TIME) /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 remove_vis_contents(door_obj_alt) update_icon() UpdateOverlays(src) /obj/structure/closet/proc/get_door_transform(angle, var/inverse_hinge = FALSE) var/matrix/door_matrix = matrix() var/matrix_door_hinge = inverse_hinge ? door_hinge_x_alt : door_hinge_x door_matrix.Translate(-matrix_door_hinge, 0) door_matrix.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 door_matrix.Translate(matrix_door_hinge, 0) return door_matrix /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(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type) if(!damage || !environment_smash) return user.do_attack_animation(src) visible_message(SPAN_DANGER("[user] [attack_message] \the [src]!")) damage(damage) return TRUE /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) if(istype(loc, /obj/structure/crate_shelf)) var/obj/structure/crate_shelf/shelf = loc shelf.relay_container_resist_act(escapee, src) return //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, do_flags = DO_DEFAULT & ~DO_SHOW_PROGRESS)) //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) /* ========================== Contents Scanner ========================== */ /obj/item/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 = 'icons/obj/item/scanner.dmi' icon_state = "crate_scanner" item_state = "crate_scanner" matter = list(DEFAULT_WALL_MATERIAL = 250, MATERIAL_GLASS = 140) w_class = WEIGHT_CLASS_SMALL obj_flags = OBJ_FLAG_CONDUCTABLE slot_flags = SLOT_BELT /obj/item/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
" var/obj/item/paper/printout = new /obj/item/paper(targetloc) printout.info = "contents of [targetname]
" printout.info += "[outputstring]"