Files
Aurora.3/code/game/objects/structures/crates_lockers/closets.dm
NanakoAC 17850e797e Box Nibbling Rework (#2131)
Currently, a mouse gnawing open a box to get its contents generates about a screen's worth of chatspam. And the same applies to eating a box of raisins or similar large meal. I feel this isn't appropriate for the size of the creature nor the severity of the act. In any case ive come up with a better method.

Main attraction of this PR, is that the box nibbling no longer uses visible messages, it only outputs to chat once, when it finally ruptures. In the meantime, any nibbling which doesn't break the box will instead shake it around and make a nibbling sound. Shaking code stolen from locker resist
Mice nibbling things is a quiet clicking noise, easy to ignore. so its unintrusive
Update: I've now extended this to all animal nibbling, not only boxes, but also on food too. To cut down on chatspam from small animals eating things.

The result of this change is that a creature eating a box or a meal is simultaneously easier than before to notice, if you care about it. And easier to ignore, if you don't care.

Also included, two bugfixes:

Re-adds the reverted console canpass behaviour that was lost a couple patches ago
adds a little robustness fix to animals, to deal with an issue where they were getting their stat set back to 0 without properly waking up. still not sure why it happens but now the icons will properly update when they do
2017-06-15 12:32:40 +03:00

407 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 = 5
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 = 40 //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/machines/click.ogg'
var/close_sound = 'sound/machines/click.ogg'
var/store_misc = 1
var/store_items = 1
var/store_mobs = 1
var/const/default_mob_size = 15
/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 src.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 src.contents)
content_size += Ceiling(I.w_class/2)
if(content_size > storage_capacity-5)
storage_capacity = content_size + 5
/obj/structure/closet/Initialize(mapload)
..()
fill()
return mapload ? INITIALIZE_HINT_LATELOAD : INITIALIZE_HINT_NORMAL
// Fill lockers with this.
/obj/structure/closet/proc/fill()
/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/proc/stored_weight()
var/content_size = 0
for(var/obj/item/I in src.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
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()
return 1
// this should probably use dump_contents()
/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.forceMove(src.loc)
A.ex_act(severity + 1)
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)
/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())
user << "<span class='notice'>You need more welding fuel to complete this task.</span>"
return
else
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(!dropsafety(W))
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
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)
return 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)
cut_overlays()
if(!opened)
icon_state = icon_closed
if(welded)
add_overlay("welded")
else
icon_state = icon_opened
/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 class='danger'>[user] [attack_message] the [src]!</span>")
dump_contents()
QDEL_IN(src, 1)
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 1 //closed but not welded...
if(breakout)
return -1 //Already breaking out.
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
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
for(var/i in 1 to (6*breakout_time * 2)) //minutes * 6 * 5seconds * 2
playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
animate_shake()
if(!do_after(escapee, 50)) //5 seconds
breakout = 0
return
if(!escapee || escapee.stat || 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
//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()