Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-23-10-2025

This commit is contained in:
Roxy
2025-10-23 17:38:23 -04:00
453 changed files with 20635 additions and 15897 deletions
@@ -19,8 +19,6 @@
///How much sitting on this chair influences fishing difficulty
var/fishing_modifier = -5
var/has_armrest = FALSE
// The mutable appearance used for the overlay over buckled mobs.
var/mutable_appearance/armrest
/obj/structure/chair/Initialize(mapload)
. = ..()
@@ -28,8 +26,6 @@
name = "tactical [name]"
fishing_modifier -= 8
MakeRotate()
if (has_armrest)
gen_armrest()
if(can_buckle && fishing_modifier)
AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
@@ -67,14 +63,6 @@
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
/obj/structure/chair/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
if(same_z_layer || !has_armrest)
return ..()
cut_overlay(armrest)
QDEL_NULL(armrest)
gen_armrest()
return ..()
/obj/structure/chair/examine(mob/user)
. = ..()
. += span_notice("It's held together by a couple of <b>bolts</b>.")
@@ -87,7 +75,6 @@
/obj/structure/chair/Destroy()
SSjob.latejoin_trackers -= src //These may be here due to the arrivals shuttle
QDEL_NULL(armrest)
return ..()
/obj/structure/chair/atom_deconstruct(disassembled)
@@ -111,29 +98,16 @@
return
. = ..()
/obj/structure/chair/update_atom_colour()
. = ..()
if (armrest)
color_atom_overlay(armrest)
/obj/structure/chair/proc/gen_armrest()
armrest = GetArmrest()
armrest.layer = ABOVE_MOB_LAYER
armrest.appearance_flags |= KEEP_APART
update_armrest()
/obj/structure/chair/proc/GetArmrest()
return mutable_appearance(icon, "[icon_state]_armrest")
/obj/structure/chair/proc/update_armrest()
if (cached_color_filter)
armrest = filter_appearance_recursive(armrest, cached_color_filter)
update_appearance()
/obj/structure/chair/update_overlays()
. = ..()
if(has_buckled_mobs())
. += armrest
if (!has_buckled_mobs())
return
var/mutable_appearance/armrest = mutable_appearance(icon, "[icon_state]_armrest", ABOVE_MOB_LAYER, src, appearance_flags = KEEP_APART)
var/mutable_appearance/armrest_blocker = emissive_blocker(icon, "[icon_state]_armrest", src, ABOVE_MOB_LAYER)
if (cached_color_filter)
armrest = filter_appearance_recursive(armrest, cached_color_filter)
. += armrest
. += armrest_blocker
///allows each chair to request the electrified_buckle component with overlays that dont look ridiculous
/obj/structure/chair/proc/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
@@ -189,13 +163,13 @@
deconstruct()
//SKYRAT EDIT END
if (has_armrest)
update_armrest()
update_appearance()
/obj/structure/chair/post_unbuckle_mob()
. = ..()
handle_layer()
if (has_armrest)
update_armrest()
update_appearance()
/obj/structure/chair/setDir(newdir)
..()
@@ -20,7 +20,6 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
/// How insulated the thing is, for the purposes of calculating body temperature. Must be between 0 and 1!
contents_thermal_insulation = 0
pass_flags_self = PASSSTRUCTURE | LETPASSCLICKS
/// The overlay for the closet's door
var/obj/effect/overlay/closet_door/door_obj
/// Whether or not this door is being animated
@@ -154,6 +153,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
add_to_roundstart_list()
closet_see_inside = new(src)
obj_flags |= UNIQUE_RENAME
// if closed, any item at the crate's loc is put in the contents
if (mapload)
@@ -289,12 +289,27 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
if(vname in list(NAMEOF(src, locked), NAMEOF(src, welded), NAMEOF(src, secure), NAMEOF(src, icon_welded), NAMEOF(src, delivery_icon)))
update_appearance()
// Clone of closet items
/obj/effect/appearance_clone/closet_item
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/// 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
if(closing && length(contents))
var/icon/mask_icon = icon(icon, has_closed_overlay ? "[icon_door || base_icon_state || initial(icon_state)]_door" : icon_state)
// When closing, all of our contents are already in src - they've already disappeared from the world
// So to make the animation less jarring, we create a clone of everything in the closet to show in the animation
for(var/content in src)
var/obj/effect/appearance_clone/closet_item/clone = new(loc, content)
// Mask keeps in in bounds of the inside of the closet
clone.add_filter("closet_mask", 1, alpha_mask_filter(x = -1 * (clone.pixel_x + clone.pixel_w), y = -1 * (clone.pixel_y + clone.pixel_z), icon = mask_icon))
clone.layer = FLOAT_LAYER - 1
vis_contents += clone
var/default_door_icon = "[icon_door || icon_state]_door"
vis_contents += door_obj
door_obj.icon = icon
@@ -330,6 +345,9 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
/obj/structure/closet/proc/end_door_animation()
is_animating_door = FALSE
vis_contents -= door_obj
for(var/obj/effect/appearance_clone/closet_item/clone in vis_contents)
vis_contents -= clone
qdel(clone)
update_icon()
/// Calculates the matrix to be applied to the animated door overlay
@@ -836,39 +854,6 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
else
balloon_alert(user, "set to [choice]")
else if(!opened && IS_WRITING_UTENSIL(weapon))
if(locked)
balloon_alert(user, "unlock first!")
return
if(isnull(id_card) && secure)
balloon_alert(user, "not yours to rename!")
return
var/name_set = FALSE
var/desc_set = FALSE
var/input_name = tgui_input_text(user, "Locker Name", "Locker Name", max_length = MAX_NAME_LEN)
if(!isnull(input_name))
name = input_name
name_set = TRUE
var/input_desc = tgui_input_text(user, "Locker Description", "Locker Description", max_length = MAX_DESC_LEN)
if(!isnull(input_desc))
desc = input_desc
desc_set = TRUE
var/bit_flag = NONE
if(name_set)
bit_flag |= UPDATE_NAME
if(desc_set)
bit_flag |= UPDATE_DESC
if(bit_flag)
update_appearance(bit_flag)
else if(opened)
if(istype(weapon, cutting_tool))
if(weapon.tool_behaviour == TOOL_WELDER)
@@ -1260,6 +1245,16 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
/obj/structure/closet/proc/add_to_roundstart_list()
GLOB.roundstart_station_closets += src
/obj/structure/closet/rename_checks(mob/living/user)
. = TRUE
if(locked)
src.balloon_alert(user, "unlock first!")
return FALSE
if(isnull(id_card) && secure)
src.balloon_alert(user, "not yours to rename!")
return FALSE
///Spears deal bonus damages to lockers
/obj/structure/closet/secure_closet/attacked_by(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
if(istype(attacking_item, /obj/item/spear))
@@ -39,6 +39,7 @@
rmb_text = "Fold up", \
)
AddElement(/datum/element/contextual_screentip_sharpness, lmb_text = "Remove Tag")
obj_flags |= UNIQUE_RENAME | RENAME_NO_DESC
/obj/structure/closet/body_bag/Destroy()
// If we have a stored bag, and it's in nullspace (not in someone's hand), delete it.
@@ -47,28 +48,22 @@
return ..()
/obj/structure/closet/body_bag/attackby(obj/item/interact_tool, mob/user, list/modifiers, list/attack_modifiers)
if (IS_WRITING_UTENSIL(interact_tool))
if(!user.can_write(interact_tool))
return
var/t = tgui_input_text(user, "What would you like the label to be?", name, max_length = 53)
if(user.get_active_held_item() != interact_tool)
return
if(!user.can_perform_action(src))
return
handle_tag("[t ? t : initial(name)]")
return
if(!tag_name)
return
if(interact_tool.tool_behaviour == TOOL_WIRECUTTER || interact_tool.get_sharpness())
to_chat(user, span_notice("You cut the tag off [src]."))
handle_tag()
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
tag_name = null
update_appearance()
qdel(src.GetComponent(/datum/component/rename))
///Handles renaming of the bodybag's examine tag.
/obj/structure/closet/body_bag/proc/handle_tag(new_name)
/obj/structure/closet/body_bag/nameformat(input, user)
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
tag_name = new_name
name = tag_name ? "[initial(name)] - [tag_name]" : initial(name)
update_appearance()
tag_name = input
update_icon()
return tag_name ? "[initial(name)] - [tag_name]" : initial(name)
/obj/structure/closet/body_bag/rename_reset()
tag_name = null
update_icon()
/obj/structure/closet/body_bag/update_overlays()
. = ..()
@@ -143,3 +143,23 @@
new /obj/item/storage/box/syringes/variety(src)
new /obj/item/storage/box/beakers/variety(src)
new /obj/item/clothing/glasses/science(src)
/obj/structure/closet/secure_closet/paramedic
name = "emergency medical team locker"
req_access = list(ACCESS_PARAMEDIC)
icon_state = "paramed_secure"
/obj/structure/closet/secure_closet/paramedic/PopulateContents()
..()
var/static/items_inside = list(
/obj/item/storage/medkit/emergency = 1,
/obj/item/storage/box/bandages = 1,
/obj/item/pinpointer/crew = 1,
/obj/item/storage/belt/medical/paramedic = 1,
/obj/item/radio/headset/headset_med = 2,
/obj/item/emergency_bed = 2,
/obj/item/storage/bag/garment/paramedic = 2,
)
generate_items_inside(items_inside,src)
@@ -34,9 +34,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/detectiveboard, 32)
if(istype(item, /obj/item/paper) || istype(item, /obj/item/photo))
item.forceMove(src)
cases[current_case].notices++
find_and_hang_on_wall()
register_context()
find_and_hang_on_wall()
/// Attaching evidences: photo and papers
+32 -32
View File
@@ -48,6 +48,7 @@
/obj/structure/door_assembly/Initialize(mapload)
. = ..()
obj_flags |= UNIQUE_RENAME | RENAME_NO_DESC
update_appearance()
update_name()
@@ -81,23 +82,15 @@
if(created_name)
. += span_notice("There is a small <i>paper</i> placard on the assembly, written on it is '[created_name]'.")
/obj/structure/door_assembly/attackby(obj/item/W, mob/living/user, list/modifiers, list/attack_modifiers)
if(IS_WRITING_UTENSIL(W) && !user.combat_mode)
var/t = tgui_input_text(user, "Enter the name for the door", "Airlock Renaming", created_name, max_length = MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
else if((W.tool_behaviour == TOOL_WELDER) && (mineral || glass || !anchored ))
if(!W.tool_start_check(user, amount=1))
/obj/structure/door_assembly/attackby(obj/item/tool, mob/living/user, list/modifiers, list/attack_modifiers)
if((tool.tool_behaviour == TOOL_WELDER) && (mineral || glass || !anchored ))
if(!tool.tool_start_check(user, amount=1))
return
if(mineral)
var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]")
user.visible_message(span_notice("[user] welds the [mineral] plating off the airlock assembly."), span_notice("You start to weld the [mineral] plating off the airlock assembly..."))
if(W.use_tool(src, user, 40, volume=50))
if(tool.use_tool(src, user, 40, volume=50))
to_chat(user, span_notice("You weld the [mineral] plating off."))
new mineral_path(loc, 2)
var/obj/structure/door_assembly/PA = new previous_assembly(loc)
@@ -105,7 +98,7 @@
else if(glass)
user.visible_message(span_notice("[user] welds the glass panel out of the airlock assembly."), span_notice("You start to weld the glass panel out of the airlock assembly..."))
if(W.use_tool(src, user, 40, volume=50))
if(tool.use_tool(src, user, 40, volume=50))
to_chat(user, span_notice("You weld the glass panel out."))
if(heat_proof_finished)
new /obj/item/stack/sheet/rglass(get_turf(src))
@@ -116,11 +109,11 @@
else if(!anchored)
user.visible_message(span_warning("[user] disassembles the airlock assembly."), \
span_notice("You start to disassemble the airlock assembly..."))
if(W.use_tool(src, user, 40, volume=50))
if(tool.use_tool(src, user, 40, volume=50))
to_chat(user, span_notice("You disassemble the airlock assembly."))
deconstruct(TRUE)
else if(W.tool_behaviour == TOOL_WRENCH)
else if(tool.tool_behaviour == TOOL_WRENCH)
if(!anchored )
var/door_check = 1
for(var/obj/machinery/door/D in loc)
@@ -133,7 +126,7 @@
span_notice("You start to secure the airlock assembly to the floor..."), \
span_hear("You hear wrenching."))
if(W.use_tool(src, user, 40, volume=100))
if(tool.use_tool(src, user, 40, volume=100))
if(anchored)
return
to_chat(user, span_notice("You secure the airlock assembly."))
@@ -146,31 +139,31 @@
user.visible_message(span_notice("[user] unsecures the airlock assembly from the floor."), \
span_notice("You start to unsecure the airlock assembly from the floor..."), \
span_hear("You hear wrenching."))
if(W.use_tool(src, user, 40, volume=100))
if(tool.use_tool(src, user, 40, volume=100))
if(!anchored)
return
to_chat(user, span_notice("You unsecure the airlock assembly."))
name = "airlock assembly"
set_anchored(FALSE)
else if(istype(W, /obj/item/stack/cable_coil) && state == AIRLOCK_ASSEMBLY_NEEDS_WIRES && anchored )
if(!W.tool_start_check(user, amount=1))
else if(istype(tool, /obj/item/stack/cable_coil) && state == AIRLOCK_ASSEMBLY_NEEDS_WIRES && anchored )
if(!tool.tool_start_check(user, amount=1))
return
user.visible_message(span_notice("[user] wires the airlock assembly."), \
span_notice("You start to wire the airlock assembly..."))
if(W.use_tool(src, user, 40, amount=1))
if(tool.use_tool(src, user, 40, amount=1))
if(state != AIRLOCK_ASSEMBLY_NEEDS_WIRES)
return
state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
to_chat(user, span_notice("You wire the airlock assembly."))
name = "wired airlock assembly"
else if((W.tool_behaviour == TOOL_WIRECUTTER) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS )
else if((tool.tool_behaviour == TOOL_WIRECUTTER) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS )
user.visible_message(span_notice("[user] cuts the wires from the airlock assembly."), \
span_notice("You start to cut the wires from the airlock assembly..."))
if(W.use_tool(src, user, 40, volume=100))
if(tool.use_tool(src, user, 40, volume=100))
if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
return
to_chat(user, span_notice("You cut the wires from the airlock assembly."))
@@ -178,27 +171,27 @@
state = AIRLOCK_ASSEMBLY_NEEDS_WIRES
name = "secured airlock assembly"
else if(istype(W, /obj/item/electronics/airlock) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS )
W.play_tool_sound(src, 100)
else if(istype(tool, /obj/item/electronics/airlock) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS )
tool.play_tool_sound(src, 100)
user.visible_message(span_notice("[user] installs the electronics into the airlock assembly."), \
span_notice("You start to install electronics into the airlock assembly..."))
if(do_after(user, 4 SECONDS, target = src))
if( state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS )
return
if(!user.transferItemToLoc(W, src))
if(!user.transferItemToLoc(tool, src))
return
to_chat(user, span_notice("You install the airlock electronics."))
state = AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER
name = "near finished airlock assembly"
electronics = W
electronics = tool
else if((W.tool_behaviour == TOOL_CROWBAR) && state == AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER )
else if((tool.tool_behaviour == TOOL_CROWBAR) && state == AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER )
user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
span_notice("You start to remove electronics from the airlock assembly..."))
if(W.use_tool(src, user, 40, volume=100))
if(tool.use_tool(src, user, 40, volume=100))
if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
return
to_chat(user, span_notice("You remove the airlock electronics."))
@@ -212,8 +205,8 @@
electronics = null
ae.forceMove(src.loc)
else if(istype(W, /obj/item/stack/sheet))
var/obj/item/stack/sheet/sheet = W
else if(istype(tool, /obj/item/stack/sheet))
var/obj/item/stack/sheet/sheet = tool
if(!glass && (istype(sheet, /obj/item/stack/sheet/rglass) || istype(sheet, /obj/item/stack/sheet/glass)))
if(noglass)
to_chat(user, span_warning("You cannot add [sheet] to [src]!"))
@@ -270,11 +263,11 @@
transfer_assembly_vars(src, MA, TRUE)
else if((W.tool_behaviour == TOOL_SCREWDRIVER) && state == AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER )
else if((tool.tool_behaviour == TOOL_SCREWDRIVER) && state == AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER )
user.visible_message(span_notice("[user] finishes the airlock."), \
span_notice("You start finishing the airlock..."))
if(W.use_tool(src, user, 40, volume=100))
if(tool.use_tool(src, user, 40, volume=100))
if(loc && state == AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
to_chat(user, span_notice("You finish the airlock."))
finish_door()
@@ -388,3 +381,10 @@
qdel(src)
return TRUE
return FALSE
/obj/structure/door_assembly/nameformat(input, mob/living/user)
created_name = input
return input
/obj/structure/door_assembly/rename_reset()
created_name = null
+3 -3
View File
@@ -12,15 +12,15 @@
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
/obj/structure/extinguisher_cabinet/Initialize(mapload, ndir, building)
/obj/structure/extinguisher_cabinet/Initialize(mapload)
. = ..()
if(building)
if(!mapload)
opened = TRUE
else
stored_extinguisher = new /obj/item/extinguisher(src)
find_and_hang_on_wall()
update_appearance(UPDATE_ICON)
register_context()
find_and_hang_on_wall()
/obj/structure/extinguisher_cabinet/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
+2 -1
View File
@@ -38,7 +38,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet, 32)
if(populate_contents)
held_item = new item_path(src)
update_appearance()
find_and_hang_on_wall()
if(mapload)
find_and_hang_on_wall()
/obj/structure/fireaxecabinet/Destroy()
if(held_item)
@@ -108,6 +108,12 @@
reagent_id = /datum/reagent/water/hollowwater
true_name = "hollow water geyser"
/obj/structure/geyser/chiral_buffer
reagent_id = /datum/reagent/reaction_agent/inversing_buffer
point_value = 250
true_name = "chiral inversing geyser"
discovery_message = "It's a rare chiral inversing geyser! This could be very powerful in the right hands... "
/obj/structure/geyser/random
point_value = 500
+4 -9
View File
@@ -34,10 +34,6 @@
///List of all Races that can be chosen, decided by its Initialize.
var/list/selectable_races = list()
/obj/structure/mirror/Initialize(mapload)
. = ..()
update_choices()
/obj/structure/mirror/Destroy()
mirror_options = null
selectable_races = null
@@ -58,6 +54,10 @@
update_signals = list(COMSIG_ATOM_BREAK), \
check_reflect_signals = list(SIGNAL_ADDTRAIT(TRAIT_NO_MIRROR_REFLECTION), SIGNAL_REMOVETRAIT(TRAIT_NO_MIRROR_REFLECTION)), \
)
if(mapload)
find_and_hang_on_wall()
update_choices()
register_context()
/obj/structure/mirror/proc/can_reflect(atom/movable/target)
///I'm doing it this way too, because the signal is sent before the broken variable is set to TRUE.
@@ -69,11 +69,6 @@
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
/obj/structure/mirror/Initialize(mapload)
. = ..()
find_and_hang_on_wall()
register_context()
/obj/structure/mirror/broken
icon_state = "mirror_broke"
+2 -1
View File
@@ -35,7 +35,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32)
paper.forceMove(src)
notices++
update_appearance(UPDATE_ICON)
find_and_hang_on_wall()
if(mapload)
find_and_hang_on_wall()
//attaching papers!!
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers)
+1 -1
View File
@@ -84,7 +84,7 @@
/obj/structure/plasticflaps/proc/check_melt(turf/source, datum/gas_mixture/air, temperature)
SIGNAL_HANDLER
if(temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
if(temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 1.5)
return
if(!COOLDOWN_FINISHED(src, burn_damage_cd))
return
+64 -2
View File
@@ -18,7 +18,14 @@ FLOOR SAFES
anchored = TRUE
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
obj_flags = CONDUCTS_ELECTRICITY
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
custom_materials = list(
/datum/material/metalhydrogen = SHEET_MATERIAL_AMOUNT*10,
/datum/material/alloy/plastitanium = SHEET_MATERIAL_AMOUNT*5,
)
material_flags = MATERIAL_EFFECTS
/// The maximum combined w_class of stuff in the safe
var/maxspace = 24
/// The amount of tumblers that will be generated
@@ -41,14 +48,26 @@ FLOOR SAFES
/obj/structure/safe/Initialize(mapload)
. = ..()
update_appearance(UPDATE_ICON)
var/static/list/tool_behaviors = list(
TOOL_WRENCH = list(
SCREENTIP_CONTEXT_LMB = "Reset lock",
),
)
AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
// Combination generation
for(var/iterating in 1 to number_of_tumblers)
tumblers.Add(rand(0, 99))
if(!mapload)
if(density)
AddElement(/datum/element/climbable)
AddElement(/datum/element/elevation, pixel_shift = 22)
if(open && !locked)
return
update_appearance(UPDATE_ICON)
// Put as many items on our turf inside as possible
for(var/obj/item/inserting_item in loc)
if(space >= maxspace)
@@ -57,11 +76,46 @@ FLOOR SAFES
space += inserting_item.w_class
inserting_item.forceMove(src)
/obj/structure/safe/examine(mob/user)
. = ..()
. += span_notice("The locking mechanism gears are <b>wrenched</b> in place.")
/obj/structure/safe/update_icon_state()
//uses the same icon as the captain's spare safe (therefore lockable storage) so keep it in line with that
icon_state = "[initial(icon_state)][open ? null : "_locked"]"
return ..()
/obj/structure/safe/wrench_act(mob/living/user, obj/item/tool)
if(!open)
balloon_alert(user, "must be open!")
return ITEM_INTERACT_BLOCKING
balloon_alert(user, "resetting lock...")
to_chat(user, span_notice("You begin resetting the lock for [src]. You'll need to set [number_of_tumblers] numbers."))
var/list/new_tumblers = list()
for(var/tumbler_index in 1 to number_of_tumblers)
var/input_value = tgui_input_number(user, "Set tumbler #[tumbler_index] (0-99):", "Set Lock", 0, 99, 0)
if(isnull(input_value))
balloon_alert(user, "reset cancelled!")
return ITEM_INTERACT_BLOCKING
if(!user.can_perform_action(src))
balloon_alert(user, "reset interrupted!")
return ITEM_INTERACT_BLOCKING
new_tumblers.Add(input_value)
tool.play_tool_sound(src)
if(!do_after(user, 10 SECONDS, target = src))
return ITEM_INTERACT_BLOCKING
tumblers = new_tumblers
current_tumbler_index = 1
dial = 0
tool.play_tool_sound(src)
to_chat(user, span_notice("You successfully reset the lock for [src]. The new combination is: [tumblers.Join("-")]."))
balloon_alert(user, "lock set!")
return ITEM_INTERACT_SUCCESS
/obj/structure/safe/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(open)
. = TRUE //no afterattack
@@ -238,6 +292,10 @@ FLOOR SAFES
if(total_ticks == 1 || prob(SOUND_CHANCE))
balloon_alert(user, pick(sounds))
/obj/structure/safe/open
open = TRUE
locked = FALSE
//FLOOR SAFES
/obj/structure/safe/floor
name = "floor safe"
@@ -249,6 +307,10 @@ FLOOR SAFES
. = ..()
AddElement(/datum/element/undertile)
/obj/structure/safe/floor/open
open = TRUE
locked = FALSE
///Special safe for the station's vault. Not explicitly required, but the piggy bank inside it is.
/obj/structure/safe/vault
+100 -8
View File
@@ -6,6 +6,16 @@
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*5,
/datum/material/titanium = SHEET_MATERIAL_AMOUNT*3,
)
material_flags = MATERIAL_EFFECTS
/// The lock code transferred from the structure
var/stored_lock_code
/obj/item/wallframe/secure_safe/Initialize(mapload)
. = ..()
@@ -13,12 +23,55 @@
max_specific_storage = WEIGHT_CLASS_GIGANTIC,
max_total_storage = 20,
)
atom_storage.set_locked(STORAGE_FULLY_LOCKED)
if(stored_lock_code && !(obj_flags & EMAGGED))
atom_storage.set_locked(STORAGE_FULLY_LOCKED)
update_appearance()
/obj/item/wallframe/secure_safe/after_attach(obj/attached_to)
/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(attached_to)
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
@@ -33,21 +86,48 @@
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)
if(!density)
find_and_hang_on_wall()
AddComponent(/datum/component/lockable_storage, , stored_lock_code)
if(mapload)
PopulateContents()
find_and_hang_on_wall()
RegisterSignal(src, COMSIG_LOCKABLE_STORAGE_SET_CODE, PROC_REF(update_lock_code))
/obj/structure/secure_safe/find_and_hang_on_wall()
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)
@@ -55,6 +135,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
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"
@@ -77,11 +167,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
icon_state = "spare_safe"
base_icon_state = "spare_safe"
armor_type = /datum/armor/safe_caps_spare
max_integrity = 300
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/gold = SMALL_MATERIAL_AMOUNT)
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
+9 -21
View File
@@ -14,8 +14,6 @@
var/is_editable = FALSE
///sign_change_name is used to make nice looking, alphabetized and categorized names when you use a pen on any sign item or structure which is_editable.
var/sign_change_name
///Callback to the knock down proc for wallmounting behavior.
var/knock_down_callback
/datum/armor/structure_sign
melee = 50
@@ -25,12 +23,8 @@
/obj/structure/sign/Initialize(mapload)
. = ..()
register_context()
knock_down_callback = CALLBACK(src, PROC_REF(knock_down))
find_and_hang_on_wall(custom_drop_callback = knock_down_callback)
/obj/structure/sign/Destroy()
. = ..()
knock_down_callback = null
if(mapload)
find_and_hang_on_wall()
/obj/structure/sign/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
@@ -63,7 +57,7 @@
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
user.visible_message(span_notice("[user] unfastens [src]."), \
span_notice("You unfasten [src]."))
knock_down(user)
deconstruct(TRUE)
return TRUE
/obj/structure/sign/welder_act(mob/living/user, obj/item/I)
@@ -112,16 +106,8 @@
return
return ..()
/**
* This is called when a sign is removed from a wall, either through deconstruction or being knocked down.
* @param mob/living/user The user who removed the sign, if it was knocked down by a mob.
*/
/obj/structure/sign/proc/knock_down(mob/living/user)
var/turf/drop_turf
if(user)
drop_turf = get_turf(user)
else
drop_turf = drop_location()
/obj/structure/sign/atom_deconstruct(disassembled)
var/turf/drop_turf = drop_location()
var/obj/item/sign/unwrenched_sign = new (drop_turf)
if(type != /obj/structure/sign/blank) //If it's still just a basic sign backing, we can (and should) skip some of the below variable transfers.
unwrenched_sign.name = name //Copy over the sign structure variables to the sign item we're creating when we unwrench a sign.
@@ -133,7 +119,6 @@
unwrenched_sign.is_editable = is_editable
unwrenched_sign.update_integrity(get_integrity()) //Transfer how damaged it is.
unwrenched_sign.setDir(dir)
qdel(src) //The sign structure on the wall goes poof and only the sign item from unwrenching remains.
/obj/structure/sign/blank //This subtype is necessary for now because some other things (posters, picture frames, paintings) inherit from the parent type.
icon_state = "backing"
@@ -217,6 +202,9 @@
var/obj/structure/sign/placed_sign = new sign_path(user_turf) //We place the sign on the turf the user is standing, and pixel shift it to the target wall, as below.
//This is to mimic how signs and other wall objects are usually placed by mappers, and so they're only visible from one side of a wall.
var/dir = get_dir(user_turf, target_turf)
if(!(dir in GLOB.cardinals))
balloon_alert(user, "stand in line with wall!")
return
if(dir & NORTH)
placed_sign.pixel_y = 32
else if(dir & SOUTH)
@@ -230,7 +218,7 @@
playsound(target_turf, 'sound/items/deconstruct.ogg', 50, TRUE)
placed_sign.update_integrity(get_integrity())
placed_sign.setDir(dir)
placed_sign.find_and_hang_on_wall(TRUE, placed_sign.knock_down_callback)
placed_sign.find_and_hang_on_wall()
qdel(src)
return ITEM_INTERACT_SUCCESS
@@ -232,9 +232,12 @@
if(!flushing && cover_open)
. += "[base_icon_state]-water"
/obj/structure/toilet/atom_deconstruct(dissambled = TRUE)
for(var/obj/toilet_item in cistern_items)
/obj/structure/toilet/dump_contents()
for(var/obj/toilet_item in (cistern_items + fishes))
toilet_item.forceMove(drop_location())
/obj/structure/toilet/atom_deconstruct(dissambled = TRUE)
dump_contents()
if(buildstacktype)
new buildstacktype(loc,buildstackamount)
else
@@ -242,8 +245,6 @@
new M.sheet_type(loc, FLOOR(custom_materials[M] / SHEET_MATERIAL_AMOUNT, 1))
if(has_water_reclaimer)
new /obj/item/stock_parts/water_recycler(drop_location())
if(stuck_item)
stuck_item.forceMove(drop_location())
/obj/structure/toilet/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(user.combat_mode)
@@ -16,7 +16,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
. = ..()
if(mapload)
hidden_item = new /obj/item/food/urinalcake(src)
find_and_hang_on_wall()
find_and_hang_on_wall()
/obj/structure/urinal/Exited(atom/movable/gone, direction)
. = ..()
@@ -8,7 +8,7 @@
anchored = FALSE
density = FALSE
dir = NORTH
obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR
obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR | UNIQUE_RENAME | RENAME_NO_DESC
set_dir_on_move = FALSE
can_atmos_pass = ATMOS_PASS_PROC
@@ -251,17 +251,6 @@
electronics = null
ae.forceMove(drop_location())
else if(IS_WRITING_UTENSIL(W))
var/t = tgui_input_text(user, "Enter the name for the door", "Windoor Renaming", created_name, max_length = MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
return
//Crowbar to complete the assembly, Step 7 complete.
else if(W.tool_behaviour == TOOL_CROWBAR)
if(!electronics)
@@ -367,3 +356,10 @@
update_appearance()
return
/obj/structure/windoor_assembly/nameformat(input, user)
created_name = input
return input
/obj/structure/windoor_assembly/rename_reset()
created_name = initial(created_name)