mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-05 14:32:52 +00:00
* Routine Cargo Departmental Deliveries (#61992) About The Pull Request Document: https://hackmd.io/@ bazelart/HkY-SO9VF Each department's request console is upgraded. Instead of making requests, they are only able to see crates related to their department that helps the department. They can order one for free (so not out of anyone's wallet) and it will arrive at cargo. Cargo gets the price of the ordered crate for bringing it to the department that ordered it (via an area check, of which the crate will remain locked until satisfied, emitter cracking aside). Ordering a crate puts the console on a cooldown depending on the price of the crate ordered. The time ranges from 10 minutes at the lowest value, to capped at 20 minutes at 4x the default crate's price. the price in time follows a ease in out circular function, where the time increases slowly as the time goes up, but a lot in the middle. towards the higher end it slows down again. Cargo will start with far less money, but this is countered by the new source of income. Finish Mapping it Finish tgui panel for department orders Testmerge it for balance and feedback who cares Maybe give multiple destination areas, just in case one area gets obliterated who cares! Why It's Good For The Game Cargo starts with a budget to spend on themselves, which lets them order their department rewards before doing anything. There is no inherent reward to delivering to other departments other than social expectations of filling the job, which means cargo will simply ignore deliveries and requests if there are more self-important things going on, since those deliveries both cost cargo and do not help cargo in any way Changelog cl add: replaced the request consoles in each department with department order consoles, which order for free on a cooldown. cargo gets these orders and delivers the crates, which are locked until delivery. upon delivery, cargo gets paid the value of the crate, and can then sell the crate back on the shuttle. balance: cargo doesn't start with a budget, other departments get what their budget was split up amongst them /cl * Routine Cargo Departmental Deliveries Co-authored-by: tralezab <40974010+tralezab@users.noreply.github.com> Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
710 lines
24 KiB
Plaintext
710 lines
24 KiB
Plaintext
#define LOCKER_FULL -1
|
|
|
|
/obj/structure/closet
|
|
name = "closet"
|
|
desc = "It's a basic storage unit."
|
|
icon = 'icons/obj/closet.dmi'
|
|
icon_state = "generic"
|
|
density = TRUE
|
|
drag_slowdown = 1.5 // Same as a prone mob
|
|
max_integrity = 200
|
|
integrity_failure = 0.25
|
|
armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 10, BIO = 0, FIRE = 70, ACID = 60)
|
|
blocks_emissive = EMISSIVE_BLOCK_GENERIC
|
|
|
|
/// The overlay for the closet's door
|
|
var/obj/effect/overlay/closet_door/door_obj
|
|
/// Whether or not this door is being animated
|
|
var/is_animating_door = FALSE
|
|
/// Vertical squish of the door
|
|
var/door_anim_squish = 0.12
|
|
/// The maximum angle the door will be drawn at
|
|
var/door_anim_angle = 136
|
|
/// X position of the closet door hinge
|
|
var/door_hinge_x = -6.5
|
|
/// Amount of time it takes for the door animation to play
|
|
var/door_anim_time = 1.5 // set to 0 to make the door not animate at all
|
|
|
|
/// Controls whether a door overlay should be applied using the icon_door value as the icon state
|
|
var/enable_door_overlay = TRUE
|
|
var/has_opened_overlay = TRUE
|
|
var/has_closed_overlay = TRUE
|
|
var/icon_door = null
|
|
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)
|
|
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/closet_open.ogg'
|
|
var/close_sound = 'sound/machines/closet_close.ogg'
|
|
var/open_sound_volume = 35
|
|
var/close_sound_volume = 50
|
|
var/material_drop = /obj/item/stack/sheet/iron
|
|
var/material_drop_amount = 2
|
|
var/delivery_icon = "deliverycloset" //which icon to use when packagewrapped. null to be unwrappable.
|
|
var/anchorable = TRUE
|
|
var/icon_welded = "welded"
|
|
/// How close being inside of the thing provides complete pressure safety. Must be between 0 and 1!
|
|
contents_pressure_protection = 0
|
|
/// How insulated the thing is, for the purposes of calculating body temperature. Must be between 0 and 1!
|
|
contents_thermal_insulation = 0
|
|
/// Whether a skittish person can dive inside this closet. Disable if opening the closet causes "bad things" to happen or that it leads to a logical inconsistency.
|
|
var/divable = TRUE
|
|
/// true whenever someone with the strong pull component is dragging this, preventing opening
|
|
var/strong_grab = FALSE
|
|
///electronics for access
|
|
var/obj/item/electronics/airlock/electronics
|
|
var/can_install_electronics = 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, TRUE), 0)
|
|
. = ..()
|
|
update_appearance()
|
|
PopulateContents()
|
|
if(QDELETED(src)) //It turns out populate contents has a 1 in 100 chance of qdeling src on /obj/structure/closet/emcloset
|
|
return //Why
|
|
var/static/list/loc_connections = list(
|
|
COMSIG_CARBON_DISARM_COLLIDE = .proc/locker_carbon,
|
|
)
|
|
AddElement(/datum/element/connect_loc, loc_connections)
|
|
|
|
//USE THIS TO FILL IT, NOT INITIALIZE OR NEW
|
|
/obj/structure/closet/proc/PopulateContents()
|
|
return
|
|
|
|
/obj/structure/closet/Destroy()
|
|
dump_contents()
|
|
QDEL_NULL(door_obj)
|
|
QDEL_NULL(electronics)
|
|
return ..()
|
|
|
|
/obj/structure/closet/update_appearance(updates=ALL)
|
|
. = ..()
|
|
if(opened || broken || !secure)
|
|
luminosity = 0
|
|
return
|
|
luminosity = 1
|
|
|
|
/obj/structure/closet/update_icon()
|
|
. = ..()
|
|
if(istype(src, /obj/structure/closet/supplypod))
|
|
return
|
|
|
|
layer = opened ? BELOW_OBJ_LAYER : OBJ_LAYER
|
|
|
|
/obj/structure/closet/update_overlays()
|
|
. = ..()
|
|
closet_update_overlays(.)
|
|
|
|
/obj/structure/closet/proc/closet_update_overlays(list/new_overlays)
|
|
. = new_overlays
|
|
if(enable_door_overlay && !is_animating_door)
|
|
if(opened && has_opened_overlay)
|
|
var/mutable_appearance/door_overlay = mutable_appearance(icon, "[icon_state]_open", alpha = src.alpha)
|
|
. += door_overlay
|
|
door_overlay.overlays += emissive_blocker(door_overlay.icon, door_overlay.icon_state, alpha = door_overlay.alpha) // If we don't do this the door doesn't block emissives and it looks weird.
|
|
else if(has_closed_overlay)
|
|
. += "[icon_door || icon_state]_door"
|
|
|
|
if(opened)
|
|
return
|
|
|
|
if(welded)
|
|
. += icon_welded
|
|
|
|
if(broken || !secure)
|
|
return
|
|
//Overlay is similar enough for both that we can use the same mask for both
|
|
. += emissive_appearance(icon, "locked", alpha = src.alpha)
|
|
. += locked ? "locked" : "unlocked"
|
|
|
|
/// Animates the closet door opening and closing
|
|
/obj/structure/closet/proc/animate_door(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_state]_back"
|
|
door_layer = FLOAT_LAYER
|
|
else
|
|
door_state = default_door_icon
|
|
door_layer = ABOVE_MOB_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/end_door_animation), door_anim_time, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_CLIENT_TIME)
|
|
|
|
/// Ends the door animation and removes the animated overlay
|
|
/obj/structure/closet/proc/end_door_animation()
|
|
is_animating_door = FALSE
|
|
vis_contents -= door_obj
|
|
update_icon()
|
|
|
|
/// Calculates the matrix to be applied to the animated door overlay
|
|
/obj/structure/closet/proc/get_door_transform(angle)
|
|
var/matrix/door_matrix = matrix()
|
|
door_matrix.Translate(-door_hinge_x, 0)
|
|
door_matrix.Multiply(matrix(cos(angle), 0, 0, -sin(angle) * door_anim_squish, 1, 0))
|
|
door_matrix.Translate(door_hinge_x, 0)
|
|
return door_matrix
|
|
|
|
/obj/structure/closet/examine(mob/user)
|
|
. = ..()
|
|
if(welded)
|
|
. += span_notice("It's welded shut.")
|
|
if(anchored)
|
|
. += span_notice("It is <b>bolted</b> to the ground.")
|
|
if(opened)
|
|
. += span_notice("The parts are <b>welded</b> together.")
|
|
else if(secure && !opened)
|
|
. += span_notice("Right-click to [locked ? "unlock" : "lock"].")
|
|
|
|
if(HAS_TRAIT(user, TRAIT_SKITTISH) && divable)
|
|
. += span_notice("If you bump into [p_them()] while running, you will jump inside.")
|
|
|
|
/obj/structure/closet/CanAllowThrough(atom/movable/mover, border_dir)
|
|
. = ..()
|
|
if(wall_mounted)
|
|
return TRUE
|
|
|
|
/obj/structure/closet/proc/can_open(mob/living/user, force = FALSE)
|
|
if(force)
|
|
return TRUE
|
|
if(welded || locked)
|
|
return FALSE
|
|
if(strong_grab)
|
|
to_chat(user, span_danger("[pulledby] has an incredibly strong grip on [src], preventing it from opening."))
|
|
return FALSE
|
|
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, span_danger("There's something large on top of [src], preventing it from opening."))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/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 FALSE
|
|
for(var/mob/living/L in T)
|
|
if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
|
|
if(user)
|
|
to_chat(user, span_danger("There's something too large in [src], preventing it from closing."))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/structure/closet/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(mapload = FALSE)
|
|
var/atom/location = drop_location()
|
|
if(!location)
|
|
return
|
|
for(var/atom/movable/AM in location)
|
|
if(AM != src && insert(AM, mapload) == LOCKER_FULL) // limit reached
|
|
if(mapload) // Yea, it's a mapping issue. Blame mappers.
|
|
WARNING("Closet storage capacity of [type] exceeded on mapload at [AREACOORD(src)]")
|
|
break
|
|
for(var/i in reverse_range(location.get_all_contents()))
|
|
var/atom/movable/thing = i
|
|
SEND_SIGNAL(thing, COMSIG_TRY_STORAGE_HIDE_ALL)
|
|
|
|
/obj/structure/closet/proc/open(mob/living/user, force = FALSE)
|
|
if(!can_open(user, force))
|
|
return
|
|
if(opened)
|
|
return
|
|
if(SEND_SIGNAL(src, COMSIG_CLOSET_PRE_OPEN, user, force) & BLOCK_OPEN)
|
|
return
|
|
welded = FALSE
|
|
locked = FALSE
|
|
playsound(loc, open_sound, open_sound_volume, TRUE, -3)
|
|
opened = TRUE
|
|
if(!dense_when_open)
|
|
set_density(FALSE)
|
|
dump_contents()
|
|
animate_door(FALSE)
|
|
update_appearance()
|
|
|
|
after_open(user, force)
|
|
SEND_SIGNAL(src, COMSIG_CLOSET_POST_OPEN, force)
|
|
return TRUE
|
|
|
|
///Proc to override for effects after opening a door
|
|
/obj/structure/closet/proc/after_open(mob/living/user, force = FALSE)
|
|
return
|
|
|
|
/obj/structure/closet/proc/insert(atom/movable/inserted, mapload = FALSE)
|
|
if(length(contents) >= storage_capacity)
|
|
if(!mapload)
|
|
return LOCKER_FULL
|
|
//For maploading, we only return LOCKER_FULL if the movable was otherwise insertable. This way we can avoid logging false flags.
|
|
return insertion_allowed(inserted) ? LOCKER_FULL : FALSE
|
|
if(!insertion_allowed(inserted))
|
|
return FALSE
|
|
if(SEND_SIGNAL(src, COMSIG_CLOSET_INSERT, inserted) & COMPONENT_CLOSET_INSERT_INTERRUPT)
|
|
return TRUE
|
|
inserted.forceMove(src)
|
|
return TRUE
|
|
|
|
/obj/structure/closet/proc/insertion_allowed(atom/movable/AM)
|
|
if(ismob(AM))
|
|
if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets...
|
|
return FALSE
|
|
var/mob/living/L = AM
|
|
if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
|
|
return FALSE
|
|
if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
|
|
if(horizontal && L.density)
|
|
return FALSE
|
|
if(L.mob_size > max_mob_size)
|
|
return FALSE
|
|
var/mobs_stored = 0
|
|
for(var/mob/living/M in contents)
|
|
if(++mobs_stored >= mob_storage_capacity)
|
|
return FALSE
|
|
L.stop_pulling()
|
|
|
|
else if(istype(AM, /obj/structure/closet))
|
|
return FALSE
|
|
else if(isobj(AM))
|
|
if((!allow_dense && AM.density) || AM.anchored || AM.has_buckled_mobs())
|
|
return FALSE
|
|
else if(isitem(AM) && !HAS_TRAIT(AM, TRAIT_NODROP))
|
|
return TRUE
|
|
else if(!allow_objects && !istype(AM, /obj/effect/dummy/chameleon))
|
|
return FALSE
|
|
else
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/structure/closet/proc/close(mob/living/user)
|
|
if(!opened || !can_close(user))
|
|
return FALSE
|
|
take_contents()
|
|
playsound(loc, close_sound, close_sound_volume, TRUE, -3)
|
|
opened = FALSE
|
|
set_density(TRUE)
|
|
animate_door(TRUE)
|
|
update_appearance()
|
|
after_close(user)
|
|
return TRUE
|
|
|
|
///Proc to override for effects after closing a door
|
|
/obj/structure/closet/proc/after_close(mob/living/user)
|
|
return
|
|
|
|
/**
|
|
* Toggles a closet open or closed, to the opposite state. Does not respect locked or welded states, however.
|
|
*/
|
|
/obj/structure/closet/proc/toggle(mob/living/user)
|
|
if(opened)
|
|
return close(user)
|
|
else
|
|
return open(user)
|
|
|
|
/obj/structure/closet/deconstruct(disassembled = TRUE)
|
|
if (!(flags_1 & NODECONSTRUCT_1))
|
|
if(ispath(material_drop) && material_drop_amount)
|
|
new material_drop(loc, material_drop_amount)
|
|
if (electronics)
|
|
var/obj/item/electronics/airlock/electronics_ref = electronics
|
|
electronics = null
|
|
electronics_ref.forceMove(drop_location())
|
|
qdel(src)
|
|
|
|
/obj/structure/closet/atom_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(src.tool_interact(W,user))
|
|
return 1 // No afterattack
|
|
else
|
|
return ..()
|
|
|
|
/obj/structure/closet/proc/tool_interact(obj/item/W, mob/living/user)//returns TRUE if attackBy call shouldn't be continued (because tool was used/closet was of wrong type), FALSE if otherwise
|
|
. = TRUE
|
|
if(opened)
|
|
if(istype(W, cutting_tool))
|
|
if(W.tool_behaviour == TOOL_WELDER)
|
|
if(!W.tool_start_check(user, amount=0))
|
|
return
|
|
|
|
to_chat(user, span_notice("You begin cutting \the [src] apart..."))
|
|
if(W.use_tool(src, user, 40, volume=50))
|
|
if(!opened)
|
|
return
|
|
user.visible_message(span_notice("[user] slices apart \the [src]."),
|
|
span_notice("You cut \the [src] apart with \the [W]."),
|
|
span_hear("You hear welding."))
|
|
deconstruct(TRUE)
|
|
return
|
|
else // for example cardboard box is cut with wirecutters
|
|
user.visible_message(span_notice("[user] cut apart \the [src]."), \
|
|
span_notice("You cut \the [src] apart with \the [W]."))
|
|
deconstruct(TRUE)
|
|
return
|
|
if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too
|
|
return
|
|
else if(W.tool_behaviour == TOOL_WELDER && can_weld_shut)
|
|
if(!W.tool_start_check(user, amount=0))
|
|
return
|
|
|
|
to_chat(user, span_notice("You begin [welded ? "unwelding":"welding"] \the [src]..."))
|
|
if(W.use_tool(src, user, 40, volume=50))
|
|
if(opened)
|
|
return
|
|
welded = !welded
|
|
after_weld(welded)
|
|
user.visible_message(span_notice("[user] [welded ? "welds shut" : "unwelded"] \the [src]."),
|
|
span_notice("You [welded ? "weld" : "unwelded"] \the [src] with \the [W]."),
|
|
span_hear("You hear welding."))
|
|
log_game("[key_name(user)] [welded ? "welded":"unwelded"] closet [src] with [W] at [AREACOORD(src)]")
|
|
update_appearance()
|
|
else if(W.tool_behaviour == TOOL_WRENCH && anchorable)
|
|
if(isinspace() && !anchored)
|
|
return
|
|
set_anchored(!anchored)
|
|
W.play_tool_sound(src, 75)
|
|
user.visible_message(span_notice("[user] [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground."), \
|
|
span_notice("You [anchored ? "anchored" : "unanchored"] \the [src] [anchored ? "to" : "from"] the ground."), \
|
|
span_hear("You hear a ratchet."))
|
|
else if (can_install_electronics && istype(W, /obj/item/electronics/airlock)\
|
|
&& !secure && !electronics && !locked && (welded || !can_weld_shut) && !broken)
|
|
user.visible_message(span_notice("[user] installs the electronics into the [src]."),\
|
|
span_notice("You start to install electronics into the [src]..."))
|
|
if (!do_after(user, 4 SECONDS, target = src))
|
|
return FALSE
|
|
if (electronics || secure)
|
|
return FALSE
|
|
if (!user.transferItemToLoc(W, src))
|
|
return FALSE
|
|
W.moveToNullspace()
|
|
to_chat(user, span_notice("You install the electronics."))
|
|
electronics = W
|
|
if (electronics.one_access)
|
|
req_one_access = electronics.accesses
|
|
else
|
|
req_access = electronics.accesses
|
|
secure = TRUE
|
|
update_appearance()
|
|
else if (can_install_electronics && W.tool_behaviour == TOOL_SCREWDRIVER\
|
|
&& (secure || electronics) && !locked && (welded || !can_weld_shut))
|
|
user.visible_message(span_notice("[user] begins to remove the electronics from the [src]."),\
|
|
span_notice("You begin to remove the electronics from the [src]..."))
|
|
var/had_electronics = !!electronics
|
|
var/was_secure = secure
|
|
if (!do_after(user, 4 SECONDS, target = src))
|
|
return FALSE
|
|
if ((had_electronics && !electronics) || (was_secure && !secure))
|
|
return FALSE
|
|
var/obj/item/electronics/airlock/electronics_ref
|
|
if (!electronics)
|
|
electronics_ref = new /obj/item/electronics/airlock(loc)
|
|
gen_access()
|
|
if (req_one_access.len)
|
|
electronics_ref.one_access = 1
|
|
electronics_ref.accesses = req_one_access
|
|
else
|
|
electronics_ref.accesses = req_access
|
|
else
|
|
electronics_ref = electronics
|
|
electronics = null
|
|
electronics_ref.forceMove(drop_location())
|
|
secure = FALSE
|
|
update_appearance()
|
|
else if(!user.combat_mode)
|
|
var/item_is_id = W.GetID()
|
|
if(!item_is_id)
|
|
return FALSE
|
|
if(item_is_id || !toggle(user))
|
|
togglelock(user)
|
|
else
|
|
return FALSE
|
|
|
|
/obj/structure/closet/proc/after_weld(weld_state)
|
|
return
|
|
|
|
/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/living/user)
|
|
if(!istype(O) || O.anchored || istype(O, /atom/movable/screen))
|
|
return
|
|
if(!istype(user) || user.incapacitated() || user.body_position == LYING_DOWN)
|
|
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(span_warning("[user] [actuallyismob ? "tries to ":""]stuff [O] into [src]."), \
|
|
span_warning("You [actuallyismob ? "try to ":""]stuff [O] into [src]."), \
|
|
span_hear("You hear clanging."))
|
|
if(actuallyismob)
|
|
if(do_after_mob(user, targets, 40))
|
|
user.visible_message(span_notice("[user] stuffs [O] into [src]."), \
|
|
span_notice("You stuff [O] into [src]."), \
|
|
span_hear("You hear a loud metal bang."))
|
|
var/mob/living/L = O
|
|
if(!issilicon(L))
|
|
L.Paralyze(40)
|
|
if(istype(src, /obj/structure/closet/supplypod/extractionpod))
|
|
O.forceMove(src)
|
|
else
|
|
O.forceMove(T)
|
|
close()
|
|
else
|
|
O.forceMove(T)
|
|
return 1
|
|
|
|
/obj/structure/closet/relaymove(mob/living/user, direction)
|
|
if(user.stat || !isturf(loc))
|
|
return
|
|
if(locked)
|
|
if(message_cooldown <= world.time)
|
|
message_cooldown = world.time + 50
|
|
to_chat(user, span_warning("[src]'s door won't budge!"))
|
|
return
|
|
container_resist_act(user)
|
|
|
|
|
|
/obj/structure/closet/attack_hand(mob/living/user, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(user.body_position == LYING_DOWN && get_dist(src, user) > 0)
|
|
return
|
|
|
|
if(!toggle(user))
|
|
togglelock(user)
|
|
|
|
|
|
/obj/structure/closet/attack_paw(mob/user, list/modifiers)
|
|
return attack_hand(user, modifiers)
|
|
|
|
/obj/structure/closet/attack_robot(mob/user)
|
|
if(user.Adjacent(src))
|
|
return attack_hand(user)
|
|
|
|
/obj/structure/closet/attack_robot_secondary(mob/user, list/modifiers)
|
|
if(!user.Adjacent(src))
|
|
return SECONDARY_ATTACK_CONTINUE_CHAIN
|
|
togglelock(user)
|
|
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
|
|
|
// tk grab then use on self
|
|
/obj/structure/closet/attack_self_tk(mob/user)
|
|
if(attack_hand(user))
|
|
return COMPONENT_CANCEL_ATTACK_CHAIN
|
|
|
|
/obj/structure/closet/verb/verb_toggleopen()
|
|
set src in view(1)
|
|
set category = "Object"
|
|
set name = "Toggle Open"
|
|
|
|
if(!usr.canUseTopic(src, BE_CLOSE) || !isturf(loc))
|
|
return
|
|
|
|
if(iscarbon(usr) || issilicon(usr) || isdrone(usr))
|
|
return toggle(usr)
|
|
else
|
|
to_chat(usr, span_warning("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/leaving, direction)
|
|
open()
|
|
if(leaving.loc == src)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/structure/closet/container_resist_act(mob/living/user)
|
|
if(opened)
|
|
return
|
|
if(ismovable(loc))
|
|
user.changeNext_move(CLICK_CD_BREAKOUT)
|
|
user.last_special = world.time + CLICK_CD_BREAKOUT
|
|
var/atom/movable/AM = loc
|
|
AM.relay_container_resist_act(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(span_warning("[src] begins to shake violently!"), \
|
|
span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \
|
|
span_hear("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(span_danger("[user] successfully broke out of [src]!"),
|
|
span_notice("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, span_warning("You fail to break out of [src]!"))
|
|
|
|
/obj/structure/closet/proc/bust_open()
|
|
SIGNAL_HANDLER
|
|
welded = FALSE //applies to all lockers
|
|
locked = FALSE //applies to critter crates and secure lockers only
|
|
broken = TRUE //applies to secure lockers only
|
|
open()
|
|
|
|
/obj/structure/closet/attack_hand_secondary(mob/user, modifiers)
|
|
. = ..()
|
|
|
|
if(!user.canUseTopic(src, BE_CLOSE) || !isturf(loc))
|
|
return
|
|
|
|
if(!opened && secure)
|
|
togglelock(user)
|
|
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
|
|
|
/obj/structure/closet/proc/togglelock(mob/living/user, silent)
|
|
if(secure && !broken)
|
|
if(allowed(user))
|
|
if(iscarbon(user))
|
|
add_fingerprint(user)
|
|
locked = !locked
|
|
user.visible_message(span_notice("[user] [locked ? null : "un"]locks [src]."),
|
|
span_notice("You [locked ? null : "un"]lock [src]."))
|
|
update_appearance()
|
|
else if(!silent)
|
|
to_chat(user, span_alert("Access Denied."))
|
|
else if(secure && broken)
|
|
to_chat(user, span_warning("\The [src] is broken!"))
|
|
|
|
/obj/structure/closet/emag_act(mob/user)
|
|
if(secure && !broken)
|
|
if(user)
|
|
user.visible_message(span_warning("Sparks fly from [src]!"),
|
|
span_warning("You scramble [src]'s lock, breaking it open!"),
|
|
span_hear("You hear a faint electrical spark."))
|
|
playsound(src, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
broken = TRUE
|
|
locked = FALSE
|
|
update_appearance()
|
|
|
|
/obj/structure/closet/get_remote_view_fullscreens(mob/user)
|
|
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
|
|
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1)
|
|
|
|
/obj/structure/closet/emp_act(severity)
|
|
. = ..()
|
|
if(. & EMP_PROTECT_SELF)
|
|
return
|
|
if (!(. & EMP_PROTECT_CONTENTS))
|
|
for(var/obj/O in src)
|
|
O.emp_act(severity)
|
|
if(secure && !broken && !(. & EMP_PROTECT_SELF))
|
|
if(prob(50 / severity))
|
|
locked = !locked
|
|
update_appearance()
|
|
if(prob(20 / severity) && !opened)
|
|
if(!locked)
|
|
open()
|
|
else
|
|
req_access = list()
|
|
req_access += pick(SSid_access.get_region_access_list(list(REGION_ALL_STATION)))
|
|
|
|
/obj/structure/closet/contents_explosion(severity, target)
|
|
switch(severity)
|
|
if(EXPLODE_DEVASTATE)
|
|
SSexplosions.high_mov_atom += contents
|
|
if(EXPLODE_HEAVY)
|
|
SSexplosions.med_mov_atom += contents
|
|
if(EXPLODE_LIGHT)
|
|
SSexplosions.low_mov_atom += contents
|
|
|
|
/obj/structure/closet/singularity_act()
|
|
dump_contents()
|
|
..()
|
|
|
|
/obj/structure/closet/AllowDrop()
|
|
return TRUE
|
|
|
|
/obj/structure/closet/return_temperature()
|
|
return
|
|
|
|
/obj/structure/closet/proc/locker_carbon(datum/source, mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
|
|
SIGNAL_HANDLER
|
|
if(!opened && (locked || welded)) //Yes this could be less code, no I don't care
|
|
return
|
|
if(!opened && !shove_blocked)
|
|
return
|
|
if(opened)
|
|
target.forceMove(src)
|
|
else
|
|
target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
|
|
toggle()
|
|
update_icon()
|
|
target.visible_message(span_danger("[shover.name] shoves [target.name] into \the [src]!"),
|
|
span_userdanger("You're shoved into \the [src] by [target.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
|
|
to_chat(src, span_danger("You shove [target.name] into \the [src]!"))
|
|
log_combat(src, target, "shoved", "into [src] (locker/crate)")
|
|
return COMSIG_CARBON_SHOVE_HANDLED
|
|
|
|
#undef LOCKER_FULL
|