Files
Ghom 0b0c5ea91e Unit test material checks are now performed on all crafting recipes by default. All stack recipes now transfer mats to the results (#92620)
## About The Pull Request
Extends the part of the crafting unit test that ensures consistency
between the total mats of the components of a recipe (or rather, the
result of said recipe) and a generic instance of the same type as its
result, previously only implemented on food recipes.

## Why It's Good For The Game
This ensures a degree of consistency with the material composition of
various objects in the game. I couldn't do it in the original PR as that
one was too big already and it took months to get it merged, and have
the relative bugs fixed.

Currently a WIP as I slowly deal with the unit test reports.

## Changelog

🆑
refactor: Follow-up to the crafting/material refactor from months ago.
All objects crafted with stacks now inherit their mat composition (not
necessarily the effects and color) by default, while previously only a
few things like chair, sinks and toilets did. Report any object looking
or behaving weirdly as a result.
fix: The material composition of ammo boxes is no longer a 1/10 of what
it's supposed to be. It was a shitty hack to make it harder to recycle
empty ammo boxes. Instead, they lose materials as they're emptied now.
/🆑
2025-12-02 18:29:01 -05:00

201 lines
6.4 KiB
Plaintext

/obj/item/wallframe/secure_safe
name = "secure safe frame"
desc = "A locked safe. It being unpowered prevents any access until placed back onto a wall."
icon = 'icons/obj/storage/storage.dmi'
icon_state = "wall_safe"
base_icon_state = "wall_safe"
result_path = /obj/structure/secure_safe
pixel_shift = 32
w_class = WEIGHT_CLASS_GIGANTIC
obj_flags = CONDUCTS_ELECTRICITY
resistance_flags = FIRE_PROOF
custom_materials = list(
/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 8,
/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 4,
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2,
)
material_flags = MATERIAL_EFFECTS
/// The lock code transferred from the structure
var/stored_lock_code
/obj/item/wallframe/secure_safe/Initialize(mapload)
. = ..()
create_storage(
max_specific_storage = WEIGHT_CLASS_GIGANTIC,
max_total_storage = 20,
)
if(stored_lock_code && !(obj_flags & EMAGGED))
atom_storage.set_locked(STORAGE_FULLY_LOCKED)
update_appearance()
/obj/item/wallframe/secure_safe/update_icon_state()
. = ..()
if(obj_flags & EMAGGED)
icon_state = "[base_icon_state]_broken"
else
icon_state = "[base_icon_state][stored_lock_code ? "_locked" : null]"
/obj/item/wallframe/secure_safe/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
if(obj_flags & EMAGGED)
return FALSE
obj_flags |= EMAGGED
visible_message(span_warning("Sparks fly from [src]!"), blind_message = span_hear("You hear a faint electrical spark."))
balloon_alert(user, "lock destroyed")
playsound(src, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
stored_lock_code = null
atom_storage.locked = STORAGE_NOT_LOCKED
update_appearance()
return TRUE
/obj/item/wallframe/secure_safe/after_attach(obj/structure/secure_safe/safe)
if(!istype(safe))
return ..()
for(var/obj/item in contents)
item.forceMove(safe)
var/datum/component/lockable_storage/storage_component = safe.GetComponent(/datum/component/lockable_storage)
if(stored_lock_code)
storage_component?.set_lock_code(stored_lock_code)
if(obj_flags & EMAGGED)
storage_component?.break_lock()
return ..()
/datum/armor/secure_safe
melee = 30
bullet = 30
laser = 20
energy = 20
bomb = 30
fire = 95
acid = 30
/**
* Wall safes
* Holds items and uses the lockable storage component
* to allow people to lock items up.
*/
/obj/structure/secure_safe
name = "secure safe"
desc = "Excellent for securing things away from grubby hands."
icon = 'icons/obj/storage/storage.dmi'
icon_state = "wall_safe"
base_icon_state = "wall_safe"
anchored = TRUE
density = FALSE
resistance_flags = FIRE_PROOF
obj_flags = CONDUCTS_ELECTRICITY
armor_type = /datum/armor/secure_safe
max_integrity = 300
damage_deflection = 21
custom_materials = list(
/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT*5,
/datum/material/titanium = SHEET_MATERIAL_AMOUNT*3,
)
material_flags = MATERIAL_EFFECTS
/// The lock code used to open the safe
var/stored_lock_code
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
/obj/structure/secure_safe/Initialize(mapload)
. = ..()
//this will create the storage for us.
AddComponent(/datum/component/lockable_storage, stored_lock_code)
if(mapload)
PopulateContents()
find_and_mount_on_atom()
RegisterSignal(src, COMSIG_LOCKABLE_STORAGE_SET_CODE, PROC_REF(update_lock_code))
/obj/structure/secure_safe/find_and_mount_on_atom(mark_for_late_init, late_init)
if(!density)
return ..()
/obj/structure/secure_safe/atom_deconstruct(disassembled)
if(!density) //if we're a wall item, we'll drop a wall frame.
var/obj/item/wallframe/secure_safe/new_safe = new(get_turf(src))
// Transfer lock code to the wallframe
var/datum/component/lockable_storage/storage_component = GetComponent(/datum/component/lockable_storage)
if(storage_component)
new_safe.stored_lock_code = storage_component.lock_code
if(obj_flags & EMAGGED)
new_safe.obj_flags |= EMAGGED
new_safe.update_appearance()
for(var/obj/item in contents)
item.forceMove(new_safe)
/obj/structure/secure_safe/proc/PopulateContents()
new /obj/item/paper(src)
new /obj/item/pen(src)
/obj/structure/secure_safe/proc/update_lock_code(obj/structure/secure_safe/safe, new_code)
SIGNAL_HANDLER
stored_lock_code = new_code
/obj/structure/secure_safe/ex_act(severity, target)
if(severity <= EXPLODE_LIGHT)
return FALSE
return ..()
/obj/structure/secure_safe/hos
name = "head of security's safe"
/**
* This safe is meant to be damn robust. To break in, you're supposed to get creative, or use acid or an explosion.
*
* This makes the safe still possible to break in for someone who is prepared and capable enough, either through
* chemistry, botany or whatever else.
*
* The safe is also weak to explosions, so spending some early TC could allow an antag to blow it upen if they can
* get access to it.
*/
/obj/structure/secure_safe/caps_spare
name = "captain's spare ID safe"
desc = "In case of emergency, do not break glass. All Captains and Acting Captains are provided with codes to access this safe. \
It is made out of the same material as the station's Black Box and is designed to resist all conventional weaponry. \
There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion.\
Due to the expensive material, it was made incredibly small to cut corners, leaving only enough room to fit something as slim as an ID card."
icon_state = "spare_safe"
base_icon_state = "spare_safe"
armor_type = /datum/armor/safe_caps_spare
damage_deflection = 30 // prevents stealing the captain's spare using null rods/lavaland monsters/AP projectiles
density = TRUE
anchored_tabletop_offset = 6
custom_materials = list(
/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT*5,
/datum/material/gold = SHEET_MATERIAL_AMOUNT*3,
)
material_flags = MATERIAL_EFFECTS
/datum/armor/safe_caps_spare
melee = 100
bullet = 100
laser = 100
energy = 100
bomb = 70
fire = 80
acid = 70
/obj/structure/secure_safe/caps_spare/Initialize(mapload)
. = ..()
atom_storage.set_holdable(/obj/item/card/id)
AddComponent(/datum/component/lockable_storage, \
lock_code = SSid_access.spare_id_safe_code, \
can_hack_open = FALSE, \
)
/obj/structure/secure_safe/caps_spare/PopulateContents()
new /obj/item/card/id/advanced/gold/captains_spare(src)
/obj/structure/secure_safe/caps_spare/rust_heretic_act()
take_damage(damage_amount = 100, damage_type = BRUTE, damage_flag = MELEE, armour_penetration = 100)