/obj/structure/closet name = "closet" desc = "It's a basic storage unit." icon = 'icons/obj/closet.dmi' icon_state = "generic" density = TRUE layer = BELOW_OBJ_LAYER var/icon_door = null var/icon_door_override = FALSE //override to have open overlay use icon different to its base's var/secure = FALSE //secure locker or not, also used if overriding a non-secure locker with a secure door overlay to add fancy lights var/opened = FALSE var/welded = FALSE var/locked = FALSE var/large = TRUE var/wall_mounted = 0 //never solid (You can always pass over it) max_integrity = 200 integrity_failure = 50 armor = list(melee = 20, bullet = 10, laser = 10, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 70, acid = 60) var/breakout_time = 1200 var/message_cooldown var/can_weld_shut = TRUE var/horizontal = FALSE var/allow_objects = FALSE var/allow_dense = FALSE var/dense_when_open = FALSE //if it's dense when open or not var/max_mob_size = MOB_SIZE_HUMAN //Biggest mob_size accepted by the container var/mob_storage_capacity = 3 // how many human sized mob/living can fit together inside a closet. var/storage_capacity = 30 //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/cutting_tool = /obj/item/weldingtool var/open_sound = 'sound/machines/click.ogg' var/close_sound = 'sound/machines/click.ogg' var/cutting_sound = 'sound/items/welder.ogg' var/material_drop = /obj/item/stack/sheet/metal var/material_drop_amount = 2 var/delivery_icon = "deliverycloset" //which icon to use when packagewrapped. null to be unwrappable. var/anchorable = TRUE /obj/structure/closet/Initialize(mapload) if(mapload && !opened) // if closed, any item at the crate's loc is put in the contents addtimer(CALLBACK(src, .proc/take_contents), 0) . = ..() update_icon() PopulateContents() //USE THIS TO FILL IT, NOT INITIALIZE OR NEW /obj/structure/closet/proc/PopulateContents() return /obj/structure/closet/Destroy() dump_contents() return ..() /obj/structure/closet/update_icon() cut_overlays() if(!opened) layer = OBJ_LAYER if(icon_door) add_overlay("[icon_door]_door") else add_overlay("[icon_state]_door") if(welded) add_overlay("welded") if(secure) if(!broken) if(locked) add_overlay("locked") else add_overlay("unlocked") else add_overlay("off") else layer = BELOW_OBJ_LAYER if(icon_door_override) add_overlay("[icon_door]_open") else add_overlay("[icon_state]_open") /obj/structure/closet/examine(mob/user) ..() if(welded) to_chat(user, "It's welded shut.") if(anchored) to_chat(user, "It is bolted to the ground.") if(opened) to_chat(user, "The parts are welded together.") else if(secure && !opened) to_chat(user, "Alt-click to [locked ? "unlock" : "lock"].") /obj/structure/closet/CanPass(atom/movable/mover, turf/target) if(wall_mounted) return 1 return !density /obj/structure/closet/proc/can_open(mob/living/user) if(welded || locked) return 0 var/turf/T = get_turf(src) for(var/mob/living/L in T) if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density) if(user) to_chat(user, "There's something large on top of [src], preventing it from opening." ) return 0 return 1 /obj/structure/closet/proc/can_close(mob/living/user) var/turf/T = get_turf(src) for(var/obj/structure/closet/closet in T) if(closet != src && !closet.wall_mounted) return 0 for(var/mob/living/L in T) if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density) if(user) to_chat(user, "There's something too large in [src], preventing it from closing.") return 0 return 1 /obj/structure/closet/proc/dump_contents() var/atom/L = drop_location() for(var/atom/movable/AM in src) AM.forceMove(L) if(throwing) // you keep some momentum when getting out of a thrown closet step(AM, dir) if(throwing) throwing.finalize(FALSE) /obj/structure/closet/proc/take_contents() var/atom/L = drop_location() for(var/atom/movable/AM in L) if(AM != src && insert(AM) == -1) // limit reached break /obj/structure/closet/proc/open(mob/living/user) if(opened || !can_open(user)) return playsound(loc, open_sound, 15, 1, -3) opened = 1 if(!dense_when_open) density = FALSE climb_time *= 0.5 //it's faster to climb onto an open thing dump_contents() update_icon() return 1 /obj/structure/closet/proc/insert(atom/movable/AM) if(contents.len >= storage_capacity) return -1 if(ismob(AM)) if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets... return var/mob/living/L = AM if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs()) return if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items. if(horizontal && L.density) return if(L.mob_size > max_mob_size) return var/mobs_stored = 0 for(var/mob/living/M in contents) if(++mobs_stored >= mob_storage_capacity) return L.stop_pulling() else if(istype(AM, /obj/structure/closet)) return else if(isobj(AM)) if(!allow_objects && !istype(AM, /obj/item) && !istype(AM, /obj/effect/dummy/chameleon)) return if(!allow_dense && AM.density) return if(AM.anchored || AM.has_buckled_mobs() || (AM.flags_1 & NODROP_1)) return else return AM.forceMove(src) if(AM.pulledby) AM.pulledby.stop_pulling() return 1 /obj/structure/closet/proc/close(mob/living/user) if(!opened || !can_close(user)) return 0 take_contents() playsound(loc, close_sound, 15, 1, -3) climb_time = initial(climb_time) opened = 0 density = TRUE update_icon() return 1 /obj/structure/closet/proc/toggle(mob/living/user) if(opened) return close(user) else return open(user) /obj/structure/closet/deconstruct(disassembled = TRUE) if(ispath(material_drop) && material_drop_amount && !(flags_1 & NODECONSTRUCT_1)) new material_drop(loc, material_drop_amount) qdel(src) /obj/structure/closet/obj_break(damage_flag) if(!broken && !(flags_1 & NODECONSTRUCT_1)) bust_open() /obj/structure/closet/attackby(obj/item/W, mob/user, params) if(user in src) return if(opened) if(istype(W, cutting_tool)) if(istype(W, /obj/item/weldingtool)) var/obj/item/weldingtool/WT = W if(WT.remove_fuel(0, user)) to_chat(user, "You begin cutting \the [src] apart...") playsound(loc, cutting_sound, 40, 1) if(do_after(user, 40*WT.toolspeed, 1, target = src)) if(!opened || !WT.isOn()) return playsound(loc, cutting_sound, 50, 1) user.visible_message("[user] slices apart \the [src].", "You cut \the [src] apart with \the [WT].", "You hear welding.") deconstruct(TRUE) return 0 else // for example cardboard box is cut with wirecutters user.visible_message("[user] cut apart \the [src].", \ "You cut \the [src] apart with \the [W].") deconstruct(TRUE) return 0 if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too return 1 else if(istype(W, /obj/item/weldingtool) && can_weld_shut) var/obj/item/weldingtool/WT = W if(!WT.remove_fuel(0, user)) return to_chat(user, "You begin [welded ? "unwelding":"welding"] \the [src]...") playsound(loc, 'sound/items/welder2.ogg', 40, 1) if(do_after(user, 40*WT.toolspeed, 1, target = src)) if(opened || !WT.isOn()) return playsound(loc, WT.usesound, 50, 1) welded = !welded user.visible_message("[user] [welded ? "welds shut" : "unweldeds"] \the [src].", "You [welded ? "weld" : "unwelded"] \the [src] with \the [WT].", "You hear welding.") update_icon() else if(istype(W, /obj/item/wrench) && anchorable) if(isinspace() && !anchored) return anchored = !anchored playsound(src.loc, W.usesound, 75, 1) user.visible_message("[user] [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \ "You [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground.", \ "You hear a ratchet.") else if(user.a_intent != INTENT_HARM && !(W.flags_1 & NOBLUDGEON_1)) if(W.GetID() || !toggle(user)) togglelock(user) return 1 else return ..() /obj/structure/closet/MouseDrop_T(atom/movable/O, mob/living/user) if(!istype(O) || O.anchored || istype(O, /obj/screen)) return if(!istype(user) || user.incapacitated() || user.lying) return if(!Adjacent(user) || !user.Adjacent(O)) return if(user == O) //try to climb onto it return ..() if(!opened) return if(!isturf(O.loc)) return var/actuallyismob = 0 if(isliving(O)) actuallyismob = 1 else if(!isitem(O)) return var/turf/T = get_turf(src) var/list/targets = list(O, src) add_fingerprint(user) user.visible_message("[user] [actuallyismob ? "tries to ":""]stuff [O] into [src].", \ "You [actuallyismob ? "try to ":""]stuff [O] into [src].", \ "You hear clanging.") if(actuallyismob) if(do_after_mob(user, targets, 40)) user.visible_message("[user] stuffs [O] into [src].", \ "You stuff [O] into [src].", \ "You hear a loud metal bang.") var/mob/living/L = O if(!issilicon(L)) L.Knockdown(40) O.forceMove(T) close() else O.forceMove(T) return 1 /obj/structure/closet/relaymove(mob/user) if(user.stat || !isturf(loc) || !isliving(user)) return if(locked) if(message_cooldown <= world.time) message_cooldown = world.time + 50 to_chat(user, "[src]'s door won't budge!") return container_resist() /obj/structure/closet/attack_hand(mob/user) ..() if(user.lying && get_dist(src, user) > 0) return if(!toggle(user)) togglelock(user) return /obj/structure/closet/attack_paw(mob/user) return attack_hand(user) /obj/structure/closet/attack_robot(mob/user) if(user.Adjacent(src)) return attack_hand(user) // tk grab then use on self /obj/structure/closet/attack_self_tk(mob/user) return attack_hand(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(iscarbon(usr) || issilicon(usr) || isdrone(usr)) attack_hand(usr) else to_chat(usr, "This mob type can't use this verb.") // Objects that try to exit a locker by stepping were doing so successfully, // and due to an oversight in turf/Enter() were going through walls. That // should be independently resolved, but this is also an interesting twist. /obj/structure/closet/Exit(atom/movable/AM) open() if(AM.loc == src) return 0 return 1 /obj/structure/closet/container_resist(mob/living/user) if(opened) return if(ismovableatom(loc)) user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT var/atom/movable/AM = loc AM.relay_container_resist(user, src) return if(!welded && !locked) open() return //okay, so the closet is either welded or locked... resist!!! user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT user.visible_message("[src] begins to shake violently!", \ "You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \ "You hear banging from [src].") if(do_after(user,(breakout_time), target = src)) if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) ) return //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting user.visible_message("[user] successfully broke out of [src]!", "You successfully break out of [src]!") bust_open() else if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded. to_chat(user, "You fail to break out of [src]!") /obj/structure/closet/proc/bust_open() welded = FALSE //applies to all lockers locked = FALSE //applies to critter crates and secure lockers only broken = 1 //applies to secure lockers only open() /obj/structure/closet/AltClick(mob/user) ..() if(!user.canUseTopic(src, be_close=TRUE) || !isturf(loc)) to_chat(user, "You can't do that right now!") return if(opened || !secure) return else togglelock(user) /obj/structure/closet/proc/togglelock(mob/living/user) if(secure && !broken) if(allowed(user)) if(iscarbon(user)) add_fingerprint(user) locked = !locked user.visible_message("[user] [locked ? null : "un"]locks [src].", "You [locked ? null : "un"]lock [src].") update_icon() else to_chat(user, "Access Denied") else if(secure && broken) to_chat(user, "\The [src] is broken!") /obj/structure/closet/emag_act(mob/user) if(secure && !broken) user.visible_message("Sparks fly from [src]!", "You scramble [src]'s lock, breaking it open!", "You hear a faint electrical spark.") playsound(src, "sparks", 50, 1) broken = 1 locked = FALSE update_icon() /obj/structure/closet/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) /obj/structure/closet/emp_act(severity) for(var/obj/O in src) O.emp_act(severity) if(secure && !broken) if(prob(50 / severity)) locked = !locked update_icon() if(prob(20 / severity) && !opened) if(!locked) open() else req_access = list() req_access += pick(get_all_accesses()) ..() /obj/structure/closet/contents_explosion(severity, target) for(var/atom/A in contents) A.ex_act(severity, target) CHECK_TICK /obj/structure/closet/singularity_act() dump_contents() ..() /obj/structure/closet/AllowDrop() return TRUE /obj/structure/closet/return_temperature() return