Files
Aurora.3/code/game/objects/structures/weapons_rack.dm
Fluffy 9acce89e2a Weapons Racks (#19338)
Added weapons racks, can be locked/unlocked with an ID card.
Added weapons racks sprites, from Wezzy.
Refactored some circuitboard code, moved the defines, applied the
defines, DMDocs.
Moved mutable appearance appearance flag set to the mutable appearance
file.
Reorganized the Horizon armory with the weapons racks, moved things
around, put down markers on lockers depending on the danger level of
their content, reorganized content of the remaining lockers.
Made weapons racks constructable with circuitboards.


![image](https://github.com/Aurorastation/Aurora.3/assets/65877598/8b4ec7ae-40c4-4cd7-b338-76120d468b90)

![image](https://github.com/Aurorastation/Aurora.3/assets/65877598/a69d1f85-c4b2-4643-af2b-6832e3ea2c43)

![image](https://github.com/Aurorastation/Aurora.3/assets/65877598/11c1afe4-0fa1-4708-8eb4-64e1f5594913)

sound/items/metal_shutter.ogg -
https://freesound.org/people/bruno.auzet/sounds/524695/ (CC0, sound was
edited)
2024-06-09 17:07:52 +00:00

261 lines
7.7 KiB
Plaintext

/obj/structure/weapons_rack
name = "weapon rack"
desc = "A rack designed to store weapons."
icon = 'icons/obj/machinery/weapons_rack.dmi'
icon_state = "rack_base"
anchored = TRUE
density = TRUE
///The maximum number of weapons that can be stored in the rack
VAR_PRIVATE/max_weapons_loaded = 4
///Boolean, if the rack is locked (no weapon can be added or removed)
VAR_PRIVATE/locked = FALSE
///Boolean, if the rack is forced open (eg. it was cut open, melted open or whatever)
VAR_PRIVATE/forced_open = FALSE
VAR_PRIVATE/obj/effect/visual_holder
/obj/structure/weapons_rack/Initialize(mapload)
..()
visual_holder = new()
visual_holder.vis_flags |= VIS_INHERIT_ID
visual_holder.appearance_flags |= KEEP_TOGETHER|RESET_TRANSFORM
//This is used to hide any stray pixels that the rifles could have, eg. for overboarding the rack sprite
var/mask_for_rifles_filter = filter(type="alpha", icon = icon(src.icon, "rack_mask"))
visual_holder.filters += mask_for_rifles_filter
src.vis_contents += visual_holder
return INITIALIZE_HINT_LATELOAD
/obj/structure/weapons_rack/LateInitialize()
. = ..()
for(var/obj/item/gun in get_turf(src))
gun.forceMove(src)
//This protects us from mapper mistakes
//don't worry, they will find a way to get this to bug out, just not this particular one
if(length(src.contents) > max_weapons_loaded)
stack_trace("Weapon rack at [src.x],[src.y],[src.z] is full and still find weapons to load on init in the turf!")
qdel(gun)
if(length(req_one_access))
locked = TRUE
update_icon()
/obj/structure/weapons_rack/Destroy()
QDEL_NULL(visual_holder)
. = ..()
/obj/structure/weapons_rack/attackby(obj/item/attacking_item, mob/user, params)
. = ..()
//Hit with a gun, to add it to the rack
if(istype(attacking_item, /obj/item/gun))
//No adding or removing weapons from the rack while locked
if(locked)
to_chat(user, SPAN_WARNING("The rack is locked."))
return
if(length(src.contents) >= max_weapons_loaded)
to_chat(user, SPAN_WARNING("The rack is full."))
return
if(!user.unEquip(attacking_item))
return
attacking_item.forceMove(src)
update_icon()
//Hit with ID, to lock/unlock the rack
else if(istype(attacking_item, /obj/item/card/id))
var/obj/item/card/id/identification_card = attacking_item
if(!forced_open && has_access(req_one_access = src.req_one_access, accesses = identification_card.access))
balloon_alert_to_viewers("[user] [locked ? "unlocks" : "locks"] \the [src]!")
playsound(src, 'sound/items/metal_shutter.ogg', 50, TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
toggle_lock(user)
else
to_chat(user, SPAN_WARNING("You lack the required access to operate this rack's lock, or the lock mechanism is broken."))
else if(locked && attacking_item.iswelder())
var/obj/item/weldingtool/WT = attacking_item
balloon_alert_to_viewers("[user] starts to cut through the lock on \the [src]!")
if(WT.use_tool(src, user, 20 SECONDS, extra_checks = CALLBACK(src, PROC_REF(can_continue_opening))))
//Last check because use_tool() is shitcoded
if(forced_open || !can_be_opened())
return
toggle_lock(user)
forced_open = TRUE
else
balloon_alert_to_viewers("[src] was unlocked while you was working on it!")
/obj/structure/weapons_rack/attack_hand(mob/living/user)
. = ..()
if(length(src.contents))
//No adding or removing weapons from the rack while locked
if(locked)
to_chat(user, SPAN_WARNING("The rack is locked."))
return
//Build a list of options for the radial menu, based on our content
var/list/radial_options = list()
for(var/obj/item/gun/gun_in_rack in src.contents)
radial_options[gun_in_rack] = image(gun_in_rack.icon, gun_in_rack.icon_state)
//Show the user the radial menu
var/obj/item/gun/rifle_to_take = show_radial_menu(user, src, radial_options, require_near = TRUE, tooltips = TRUE)
//No choice was made, return
if(isnull(rifle_to_take))
return
//Assuming the rifle is still in the rack when the user chooses it, deliver the rifle
if(rifle_to_take in src.contents)
rifle_to_take.forceMove(get_turf(user))
user.put_in_hands(rifle_to_take)
update_icon()
else
to_chat(user, SPAN_WARNING("\The [rifle_to_take] is not in the rack anymore!"))
#define BASE_OFFSET_RIFLE_SLOT -1
#define INTER_OFFSET_RIFLE_SLOT 7
/obj/structure/weapons_rack/update_icon()
. = ..()
visual_holder.overlays.Cut()
ClearOverlays()
for(var/i in 1 to length(src.contents))
var/obj/item/gun/G = src.contents[i]
var/matrix/transform_matrix = matrix(G.transform)
transform_matrix.Turn(-90)
transform_matrix.Scale(0.8, 0.8)
transform_matrix.Translate((((world.icon_size/2)+BASE_OFFSET_RIFLE_SLOT) - world.icon_size) + (i*INTER_OFFSET_RIFLE_SLOT), 0)
//Why not the rifle itself you might ask? Because there's the safety icon on it
var/mutable_appearance/weapon_appearance = mutable_appearance(G.icon, G.icon_state, plane = src.plane, flags = G.appearance_flags, layer = G.layer)
weapon_appearance.transform = transform_matrix
visual_holder.overlays += weapon_appearance
if(locked)
AddOverlays(image(src.icon, "locked_overlay", layer = ABOVE_OBJ_LAYER))
#undef BASE_OFFSET_RIFLE_SLOT
#undef INTER_OFFSET_RIFLE_SLOT
/obj/structure/weapons_rack/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended)
. = ..()
if(locate(/obj/item/gun) in src)
. += "<br/>It contains:<br/>"
for(var/obj/item/gun/G in src.contents)
. += "\A [G.name]"
if(locked)
. += SPAN_WARNING("It is locked.")
/obj/structure/weapons_rack/emag_act(remaining_charges, mob/user, emag_source)
. = ..()
to_chat(user, SPAN_NOTICE("You start to fiddle with the electronic lock of \the [src]."))
if(do_after(user, 10 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(can_continue_opening))))
to_chat(user, SPAN_NOTICE("You manage to unlock \the [src]."))
toggle_lock(user)
/obj/structure/weapons_rack/ex_act(severity)
if(severity > 1 && can_be_opened())
toggle_lock()
forced_open = TRUE
if(severity > 2)
for(var/obj/item/gun/G in src.contents)
if(prob(10))
//Uh oh, the munitions cooked in the gun!
fragem(get_turf(G), 4, 7, 3, 3, 10, 1, TRUE)
qdel(G)
else
throw_at_random(TRUE, 4, 7)
. = ..()
/obj/structure/weapons_rack/fire_act(exposed_temperature, exposed_volume)
. = ..()
if(exposed_temperature > 1811) //Melting temperature of steel, in kelvin
//You melted the door
if(can_be_opened())
if(prob(20))
toggle_lock()
forced_open = TRUE
else
var/picked = pick_weight(list("burn_weapon" = 2, "explode_ammos" = 10, "nothing" = 80))
//Burn down one of the guns
if(picked == "burn_weapon" && length(src.contents))
var/obj/item/gun/G = pick(src.contents)
G.forceMove(get_turf(src))
qdel(G)
//Just some explosion effect
else if(picked == "explode_ammos")
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(fragem), get_turf(src), 2, 3, 3, 3, 10, 1, TRUE)
/**
* Locks/unlocks the rack
*
* * user - A `/mob` that is unlocking the rack, optional
*/
/obj/structure/weapons_rack/proc/toggle_lock(mob/user, force_lock_state)
SHOULD_NOT_SLEEP(TRUE)
if(user)
add_fibers(user)
add_fingerprint(user)
locked = !locked
update_icon()
/**
* Checks if the locker can be opened (NOT permissions, as in "did someone else already open it" kind of thing)
*
*/
/obj/structure/weapons_rack/proc/can_be_opened()
SHOULD_NOT_SLEEP(TRUE)
SHOULD_BE_PURE(TRUE)
//Can't open it, it's forced open
if(forced_open)
return FALSE
if(locked)
return TRUE
else
return FALSE
/**
* Basically the inverse of `can_be_opened()`, because the `do_* / use_*` callbacks work the other way around
*
* Used only for callbacks, do not call directly
*/
/obj/structure/weapons_rack/proc/can_continue_opening()
SHOULD_NOT_SLEEP(TRUE)
SHOULD_BE_PURE(TRUE)
return !can_be_opened()