mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-24 17:11:40 +00:00
A lot of new defines are now in inventory_sizes.dm, which contains; All the size identifiers (the thing that tells the game if something is bulky, or w/e). Storage costs for all the sizes, which are exponents of two, as previously. A few constants for inventory size. Also changes all storage item's capacity definitions by basing it off of how many 'normal slots' exist for it. This allows one to change the definition for all of the defines in the file, and everything will follow along without needing to change 500 files. In testing, I made all ITEMSIZE_COST_* defines doubled, and nothing had broke. The benefit of doing all of this is that it makes adding new weight classes in the future much simpler, and makes knowing how much space a container has easier, as seeing ITEMSIZE_COST_NORMAL * 7 means it can hold seven normal items.
400 lines
12 KiB
Plaintext
400 lines
12 KiB
Plaintext
/obj/structure/closet
|
|
name = "closet"
|
|
desc = "It's a basic storage unit."
|
|
icon = 'icons/obj/closet.dmi'
|
|
icon_state = "closed"
|
|
density = 1
|
|
w_class = ITEMSIZE_HUGE
|
|
var/icon_closed = "closed"
|
|
var/icon_opened = "open"
|
|
var/opened = 0
|
|
var/welded = 0
|
|
var/wall_mounted = 0 //never solid (You can always pass over it)
|
|
var/health = 100
|
|
var/breakout = 0 //if someone is currently breaking out. mutex
|
|
var/storage_capacity = 2 * MOB_MEDIUM //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/machines/click.ogg'
|
|
var/close_sound = 'sound/machines/click.ogg'
|
|
|
|
var/store_misc = 1
|
|
var/store_items = 1
|
|
var/store_mobs = 1
|
|
|
|
/obj/structure/closet/initialize()
|
|
if(!opened) // if closed, any item at the crate's loc is put in the contents
|
|
var/obj/item/I
|
|
for(I in src.loc)
|
|
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 src.contents)
|
|
content_size += Ceiling(I.w_class/2)
|
|
if(content_size > storage_capacity-5)
|
|
storage_capacity = content_size + 5
|
|
|
|
|
|
/obj/structure/closet/examine(mob/user)
|
|
if(..(user, 1) && !opened)
|
|
var/content_size = 0
|
|
for(var/obj/item/I in src.contents)
|
|
if(!I.anchored)
|
|
content_size += Ceiling(I.w_class/2)
|
|
if(!content_size)
|
|
user << "It is empty."
|
|
else if(storage_capacity > content_size*4)
|
|
user << "It is barely filled."
|
|
else if(storage_capacity > content_size*2)
|
|
user << "It is less than half full."
|
|
else if(storage_capacity > content_size)
|
|
user << "There is still some free space."
|
|
else
|
|
user << "It is full."
|
|
|
|
/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
|
if(air_group || (height==0 || wall_mounted)) return 1
|
|
return (!density)
|
|
|
|
/obj/structure/closet/proc/can_open()
|
|
if(src.welded)
|
|
return 0
|
|
return 1
|
|
|
|
/obj/structure/closet/proc/can_close()
|
|
for(var/obj/structure/closet/closet in get_turf(src))
|
|
if(closet != src)
|
|
return 0
|
|
return 1
|
|
|
|
/obj/structure/closet/proc/dump_contents()
|
|
//Cham Projector Exception
|
|
for(var/obj/effect/dummy/chameleon/AD in src)
|
|
AD.forceMove(src.loc)
|
|
|
|
for(var/obj/I in src)
|
|
I.forceMove(src.loc)
|
|
|
|
for(var/mob/M in src)
|
|
M.forceMove(src.loc)
|
|
if(M.client)
|
|
M.client.eye = M.client.mob
|
|
M.client.perspective = MOB_PERSPECTIVE
|
|
|
|
/obj/structure/closet/proc/open()
|
|
if(src.opened)
|
|
return 0
|
|
|
|
if(!src.can_open())
|
|
return 0
|
|
|
|
src.dump_contents()
|
|
|
|
src.icon_state = src.icon_opened
|
|
src.opened = 1
|
|
playsound(src.loc, open_sound, 15, 1, -3)
|
|
density = 0
|
|
return 1
|
|
|
|
/obj/structure/closet/proc/close()
|
|
if(!src.opened)
|
|
return 0
|
|
if(!src.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)
|
|
|
|
src.icon_state = src.icon_closed
|
|
src.opened = 0
|
|
|
|
playsound(src.loc, close_sound, 15, 1, -3)
|
|
density = 1
|
|
return 1
|
|
|
|
//Cham Projector Exception
|
|
/obj/structure/closet/proc/store_misc(var/stored_units)
|
|
var/added_units = 0
|
|
for(var/obj/effect/dummy/chameleon/AD in src.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 src.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 src.loc)
|
|
if(M.buckled || M.pinned.len)
|
|
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/toggle(mob/user as mob)
|
|
if(!(src.opened ? src.close() : src.open()))
|
|
user << "<span class='notice'>It won't budge!</span>"
|
|
return
|
|
update_icon()
|
|
|
|
// this should probably use dump_contents()
|
|
/obj/structure/closet/ex_act(severity)
|
|
switch(severity)
|
|
if(1)
|
|
for(var/atom/movable/A as mob|obj in src)//pulls everything out of the locker and hits it with an explosion
|
|
A.forceMove(src.loc)
|
|
A.ex_act(severity + 1)
|
|
qdel(src)
|
|
if(2)
|
|
if(prob(50))
|
|
for (var/atom/movable/A as mob|obj in src)
|
|
A.forceMove(src.loc)
|
|
A.ex_act(severity + 1)
|
|
qdel(src)
|
|
if(3)
|
|
if(prob(5))
|
|
for(var/atom/movable/A as mob|obj in src)
|
|
A.forceMove(src.loc)
|
|
qdel(src)
|
|
|
|
/obj/structure/closet/proc/damage(var/damage)
|
|
health -= damage
|
|
if(health <= 0)
|
|
for(var/atom/movable/A in src)
|
|
A.forceMove(src.loc)
|
|
qdel(src)
|
|
|
|
/obj/structure/closet/bullet_act(var/obj/item/projectile/Proj)
|
|
var/proj_damage = Proj.get_structure_damage()
|
|
if(!proj_damage)
|
|
return
|
|
|
|
..()
|
|
damage(proj_damage)
|
|
|
|
return
|
|
|
|
/obj/structure/closet/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
|
if(src.opened)
|
|
if(istype(W, /obj/item/weapon/grab))
|
|
var/obj/item/weapon/grab/G = W
|
|
src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet
|
|
return 0
|
|
if(istype(W,/obj/item/tk_grab))
|
|
return 0
|
|
if(istype(W, /obj/item/weapon/weldingtool))
|
|
var/obj/item/weapon/weldingtool/WT = W
|
|
if(!WT.remove_fuel(0,user))
|
|
if(!WT.isOn())
|
|
return
|
|
else
|
|
user << "<span class='notice'>You need more welding fuel to complete this task.</span>"
|
|
return
|
|
new /obj/item/stack/material/steel(src.loc)
|
|
for(var/mob/M in viewers(src))
|
|
M.show_message("<span class='notice'>\The [src] has been cut apart by [user] with \the [WT].</span>", 3, "You hear welding.", 2)
|
|
qdel(src)
|
|
return
|
|
if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len)
|
|
var/obj/item/weapon/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 class='notice'>[user] empties \the [LB] into \the [src].</span>", \
|
|
"<span class='notice'>You empty \the [LB] into \the [src].</span>", \
|
|
"<span class='notice'>You hear rustling of clothes.</span>")
|
|
return
|
|
if(isrobot(user))
|
|
return
|
|
if(W.loc != user) // This should stop mounted modules ending up outside the module.
|
|
return
|
|
usr.drop_item()
|
|
if(W)
|
|
W.forceMove(src.loc)
|
|
else if(istype(W, /obj/item/weapon/packageWrap))
|
|
return
|
|
else if(istype(W, /obj/item/weapon/weldingtool))
|
|
var/obj/item/weapon/weldingtool/WT = W
|
|
if(!WT.remove_fuel(0,user))
|
|
if(!WT.isOn())
|
|
return
|
|
else
|
|
user << "<span class='notice'>You need more welding fuel to complete this task.</span>"
|
|
return
|
|
src.welded = !src.welded
|
|
src.update_icon()
|
|
for(var/mob/M in viewers(src))
|
|
M.show_message("<span class='warning'>[src] has been [welded?"welded shut":"unwelded"] by [user.name].</span>", 3, "You hear welding.", 2)
|
|
else if(istype(W, /obj/item/weapon/wrench))
|
|
if(welded)
|
|
if(anchored)
|
|
user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.")
|
|
else
|
|
user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.")
|
|
if(do_after(user, 20))
|
|
if(!src) return
|
|
user << "<span class='notice'>You [anchored? "un" : ""]secured \the [src]!</span>"
|
|
anchored = !anchored
|
|
else
|
|
src.attack_hand(user)
|
|
return
|
|
|
|
/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(!src.opened)
|
|
return
|
|
if(istype(O, /obj/structure/closet))
|
|
return
|
|
step_towards(O, src.loc)
|
|
if(user != O)
|
|
user.show_viewers("<span class='danger'>[user] stuffs [O] into [src]!</span>")
|
|
src.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(src.loc))
|
|
return
|
|
|
|
if(!src.open())
|
|
user << "<span class='notice'>It won't budge!</span>"
|
|
|
|
/obj/structure/closet/attack_hand(mob/user as mob)
|
|
src.add_fingerprint(user)
|
|
src.toggle(user)
|
|
|
|
// tk grab then use on self
|
|
/obj/structure/closet/attack_self_tk(mob/user as mob)
|
|
src.add_fingerprint(user)
|
|
if(!src.toggle())
|
|
usr << "<span class='notice'>It won't budge!</span>"
|
|
|
|
/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))
|
|
src.add_fingerprint(usr)
|
|
src.toggle(usr)
|
|
else
|
|
usr << "<span class='warning'>This mob type can't use this verb.</span>"
|
|
|
|
/obj/structure/closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot)
|
|
overlays.Cut()
|
|
if(!opened)
|
|
icon_state = icon_closed
|
|
if(welded)
|
|
overlays += "welded"
|
|
else
|
|
icon_state = icon_opened
|
|
|
|
/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 class='danger'>[user] [attack_message] the [src]!</span>")
|
|
dump_contents()
|
|
spawn(1) qdel(src)
|
|
return 1
|
|
|
|
/obj/structure/closet/proc/req_breakout()
|
|
if(opened)
|
|
return 0 //Door's open... wait, why are you in it's contents then?
|
|
if(!welded)
|
|
return 0 //closed but not welded...
|
|
return 1
|
|
|
|
/obj/structure/closet/proc/mob_breakout(var/mob/living/escapee)
|
|
var/breakout_time = 2 //2 minutes by default
|
|
|
|
if(breakout || !req_breakout())
|
|
return
|
|
|
|
escapee.setClickCooldown(100)
|
|
|
|
//okay, so the closet is either welded or locked... resist!!!
|
|
escapee << "<span class='warning'>You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)</span>"
|
|
|
|
visible_message("<span class='danger'>\The [src] begins to shake violently!</span>")
|
|
|
|
breakout = 1 //can't think of a better way to do this right now.
|
|
for(var/i in 1 to (6*breakout_time * 2)) //minutes * 6 * 5seconds * 2
|
|
if(!do_after(escapee, 50)) //5 seconds
|
|
breakout = 0
|
|
return
|
|
if(!escapee || escapee.incapacitated() || escapee.loc != src)
|
|
breakout = 0
|
|
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
|
|
return
|
|
|
|
playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
|
|
animate_shake()
|
|
add_fingerprint(escapee)
|
|
|
|
//Well then break it!
|
|
breakout = 0
|
|
escapee << "<span class='warning'>You successfully break out!</span>"
|
|
visible_message("<span class='danger'>\The [escapee] successfully broke out of \the [src]!</span>")
|
|
playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
|
|
break_open()
|
|
animate_shake()
|
|
|
|
/obj/structure/closet/proc/break_open()
|
|
welded = 0
|
|
update_icon()
|
|
//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/proc/animate_shake()
|
|
var/init_px = pixel_x
|
|
var/shake_dir = pick(-1, 1)
|
|
animate(src, transform=turn(matrix(), 8*shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
|
|
animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
|
|
|
|
/obj/structure/closet/onDropInto(var/atom/movable/AM)
|
|
return
|