Files
Leland KembleandGitHub 06608081d2 Moves a greater manyer morer things from attackby() to item_interaction() (#96739)
## About The Pull Request

50 files, plus additonal file that was necessary because I slightly
refactored windoor construction. This ended up like three and a half
times larger than most of the other ones, I really wasn't meaning for
that to happen. Maybe its because I more vigorously enforced the empty
line after every return thing. The only way to find out is to read 1700
lines of changes, and I don't feel like doing that again. I pinky
promise it all works, and I've tested every item changed individually.

Changes beyond conversion:
You can't welder down reinforced falsewalls anymore, pretty sure it was
an oversight because they're supposed to be taken down by wirecutters
You can right click to close gun lockers with an empty hand, because
otherwise you can't close the lockers at all if there's a gun in them
Meatspikes no longer runtime on construction due to the old trick of
balloon alert + qdel
Windoor assembly no longer SUCKS DICK!!, it's no longer storing
essentially a boolean in an undocumented number in a string, it no
longer requires a VERB to flip its direction.
on a related note, the creator of the windoor assembly construction
steps has been sent to live on a farm far out in the country

Also, a quick note about how these prs are gonna fuck everything up
until they're all together. There's a lot more places where `attackby()`
is directly called than I would have assumed, and given as those are
being changed to call `item_interaction()` potentially(usually) in a
different pr than the pr in which that atom's `attackby()` is actually
converted, that function's not going to work at all until both are
merged. The good news is that almost every instance this is happening,
it's pure convenience, where the user would have access to both items
anyway and could just manually call the attack chain by clicking them
together. Instances where this is not the case are being skipped over
until they can be packaged together to avoid the issue, but I'm not
bothering to do this on ones where the desync won't make an interaction
actually unusable, because that would require me to turn a 1700 line pr
into a 3500 line pr and I really was aiming for like 500

## Why It's Good For The Game

Ignore the paragraph about how I expect this to create a hostage
situation where the only fix to the bugs I make is to merge my other
prs, and instead think about how cool swing combat would be and how much
you want it

## Changelog
🆑
fix: you now can only use a wirecutter to take down a reinforced
falsewall
fix: meatspikes no longer runtime on construction
fix: you no longer have to use a verb that you can't use to flip windoor
assemblies

qol: you can right click a gun locker to close it

code: 50 files have been moved from attackby() to item_interaction()
/🆑
2026-07-16 15:07:43 +02:00

456 lines
17 KiB
Plaintext

/obj/structure/door_assembly
name = "airlock assembly"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "construction"
var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
anchored = FALSE
density = TRUE
max_integrity = 200
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 4)
/// Airlock's current construction state
var/state = AIRLOCK_ASSEMBLY_NEEDS_WIRES
var/base_name = "Airlock"
var/created_name = null
var/mineral = null
var/obj/item/electronics/airlock/electronics = null
/// Do we perform the extra checks required for multi-tile (large) airlocks
var/multi_tile = FALSE
/// The type path of the airlock once completed (solid version)
var/airlock_type = /obj/machinery/door/airlock
/// The type path of the airlock once completed (glass version)
var/glass_type = /obj/machinery/door/airlock/glass
/// FALSE = glass can be installed. TRUE = glass is already installed.
var/glass = FALSE
/// Whether to heat-proof the finished airlock
var/heat_proof_finished = FALSE
/// If you're changing the airlock material, what is the previous type
var/previous_assembly = /obj/structure/door_assembly
/// Airlocks with no glass version, also cannot be modified with sheets
var/noglass = FALSE
/// Airlock with glass version, but cannot be modified with sheets
var/nomineral = FALSE
/// What type of material the airlock drops when deconstructed
var/material_type = /obj/item/stack/sheet/iron
/// Amount of material the airlock drops when deconstructed
var/material_amt = 4
/obj/structure/door_assembly/multi_tile
name = "large airlock assembly"
icon = 'icons/obj/doors/airlocks/multi_tile/public/glass.dmi'
overlays_file = 'icons/obj/doors/airlocks/multi_tile/public/overlays.dmi'
base_name = "large airlock"
glass_type = /obj/machinery/door/airlock/multi_tile/public/glass
airlock_type = /obj/machinery/door/airlock/multi_tile/public/glass
dir = EAST
multi_tile = TRUE
glass = TRUE
nomineral = TRUE
material_amt = 8
/obj/structure/door_assembly/Initialize(mapload)
. = ..()
obj_flags |= UNIQUE_RENAME | RENAME_NO_DESC
update_appearance()
update_name()
/obj/structure/door_assembly/multi_tile/Initialize(mapload)
. = ..()
set_bounds()
update_appearance(UPDATE_OVERLAYS)
/obj/structure/door_assembly/multi_tile/Move()
. = ..()
set_bounds()
/obj/structure/door_assembly/examine(mob/user)
. = ..()
switch(state)
if(AIRLOCK_ASSEMBLY_NEEDS_WIRES)
if(anchored)
. += span_notice("The anchoring bolts are <b>wrenched</b> in place, but the maintenance panel lacks <i>wiring</i>.")
else
. += span_notice("The assembly is <b>welded together</b>, but the anchoring bolts are <i>unwrenched</i>.")
if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
. += span_notice("The maintenance panel is <b>wired</b>, but the circuit slot is <i>empty</i>.")
if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
. += span_notice("The circuit is <b>connected loosely</b> to its slot, but the maintenance panel is <i>unscrewed and open</i>.")
if(!mineral && !nomineral && !glass && !noglass)
. += span_notice("There are <i>empty</i> slots for glass windows and mineral covers.")
else if(!mineral && !nomineral && glass && !noglass)
. += span_notice("There are <i>empty</i> slots for mineral covers.")
else if(!glass && !noglass)
. += span_notice("There are <i>empty</i> slots for glass windows.")
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/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(anchored && state == AIRLOCK_ASSEMBLY_NEEDS_WIRES && istype(tool, /obj/item/stack/cable_coil))
if(!tool.tool_start_check(user, amount=1))
return ITEM_INTERACT_BLOCKING
user.visible_message(span_notice("[user] wires the airlock assembly."), \
span_notice("You start to wire the airlock assembly..."))
if(!tool.use_tool(src, user, 40, amount=1))
return ITEM_INTERACT_BLOCKING
if(state != AIRLOCK_ASSEMBLY_NEEDS_WIRES)
return ITEM_INTERACT_BLOCKING
state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
to_chat(user, span_notice("You wire the airlock assembly."))
name = "wired airlock assembly"
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
if(state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS && istype(tool, /obj/item/electronics/airlock))
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))
return ITEM_INTERACT_BLOCKING
if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You install the airlock electronics."))
state = AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER
name = "near finished airlock assembly"
electronics = tool
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
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]!"))
return ITEM_INTERACT_BLOCKING
playsound(src, 'sound/items/tools/crowbar.ogg', 100, TRUE)
user.visible_message(span_notice("[user] adds [sheet.name] to the airlock assembly."), \
span_notice("You start to install [sheet.name] into the airlock assembly..."))
if(!do_after(user, 4 SECONDS, target = src))
return ITEM_INTERACT_BLOCKING
if(sheet.get_amount() < 1 || glass)
return ITEM_INTERACT_BLOCKING
if(sheet.type == /obj/item/stack/sheet/rglass)
to_chat(user, span_notice("You install [sheet.name] windows into the airlock assembly."))
heat_proof_finished = TRUE //reinforced glass makes the airlock heat-proof
name = "near finished heat-proofed window airlock assembly"
else
to_chat(user, span_notice("You install regular glass windows into the airlock assembly."))
name = "near finished window airlock assembly"
sheet.use(1)
glass = TRUE
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
if(istype(sheet, /obj/item/stack/sheet/mineral) && sheet.construction_path_type)
if(nomineral || mineral)
to_chat(user, span_warning("You cannot add [sheet] to [src]!"))
return ITEM_INTERACT_BLOCKING
var/type_path_extension = sheet.construction_path_type
var/mineralassembly = text2path("/obj/structure/door_assembly/door_assembly_[type_path_extension]")
if(!ispath(mineralassembly))
to_chat(user, span_warning("You cannot add [sheet] to [src]!"))
return ITEM_INTERACT_BLOCKING
if(sheet.get_amount() < 2)
to_chat(user, span_warning("You need at least two sheets add a mineral cover!"))
return ITEM_INTERACT_BLOCKING
playsound(src, 'sound/items/tools/crowbar.ogg', 100, TRUE)
user.visible_message(span_notice("[user] adds [sheet.name] to the airlock assembly."), \
span_notice("You start to install [sheet.name] into the airlock assembly..."))
if(!do_after(user, 4 SECONDS, target = src) || sheet.get_amount() < 2 || mineral)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You install [type_path_extension] plating into the airlock assembly."))
sheet.use(2)
var/obj/structure/door_assembly/replacement_assembly = new mineralassembly(loc)
if(replacement_assembly.noglass && glass) //in case the new door doesn't support glass. prevents the new one from reverting to a normal airlock after being constructed.
var/obj/item/stack/sheet/dropped_glass
if(heat_proof_finished)
dropped_glass = new /obj/item/stack/sheet/rglass(drop_location())
heat_proof_finished = FALSE
else
dropped_glass = new /obj/item/stack/sheet/glass(drop_location())
glass = FALSE
to_chat(user, span_notice("As you finish, a [dropped_glass.singular_name] falls out of [replacement_assembly]'s frame."))
transfer_assembly_vars(src, replacement_assembly, TRUE)
return ITEM_INTERACT_SUCCESS
return NONE
/obj/structure/door_assembly/welder_act(mob/living/user, obj/item/tool)
if(!mineral && !glass && anchored)
return ITEM_INTERACT_SKIP_TO_ATTACK
if(!tool.tool_start_check(user, amount=1))
return ITEM_INTERACT_BLOCKING
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(!tool.use_tool(src, user, 40, volume=50))
return ITEM_INTERACT_BLOCKING
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)
transfer_assembly_vars(src, PA)
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
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(!tool.use_tool(src, user, 40, volume=50))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You weld the glass panel out."))
if(heat_proof_finished)
new /obj/item/stack/sheet/rglass(get_turf(src))
heat_proof_finished = FALSE
else
new /obj/item/stack/sheet/glass(get_turf(src))
glass = FALSE
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
if(!anchored)
user.visible_message(span_warning("[user] disassembles the airlock assembly."), \
span_notice("You start to disassemble the airlock assembly..."))
if(!tool.use_tool(src, user, 40, volume=50))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You disassemble the airlock assembly."))
deconstruct(TRUE)
return ITEM_INTERACT_SUCCESS
// no return NONE at end because it's not possible we end up here
/obj/structure/door_assembly/wrench_act(mob/living/user, obj/item/tool)
if(anchored)
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(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_BLOCKING
if(!anchored)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You unsecure the airlock assembly."))
name = "airlock assembly"
set_anchored(FALSE)
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
var/door_check = FALSE
for(var/obj/machinery/door/competitor in loc)
if(!competitor.sub_door)
door_check = TRUE
break
if(door_check)
to_chat(user, "There is another door here!")
return ITEM_INTERACT_BLOCKING
user.visible_message(span_notice("[user] secures the airlock assembly to the floor."), \
span_notice("You start to secure the airlock assembly to the floor..."), \
span_hear("You hear wrenching."))
if(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_BLOCKING
if(anchored)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You secure the airlock assembly."))
name = "secured airlock assembly"
set_anchored(TRUE)
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/door_assembly/wirecutter_act(mob/living/user, obj/item/tool)
if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
return ITEM_INTERACT_SKIP_TO_ATTACK
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(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_BLOCKING
if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You cut the wires from the airlock assembly."))
new/obj/item/stack/cable_coil(get_turf(user), 1)
state = AIRLOCK_ASSEMBLY_NEEDS_WIRES
name = "secured airlock assembly"
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/door_assembly/crowbar_act(mob/living/user, obj/item/tool)
if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
return ITEM_INTERACT_SKIP_TO_ATTACK
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(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_BLOCKING
if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You remove the airlock electronics."))
state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
name = "wired airlock assembly"
var/obj/item/electronics/airlock/ae
if (!electronics)
ae = new/obj/item/electronics/airlock(loc)
else
ae = electronics
electronics = null
ae.forceMove(src.loc)
update_name()
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/door_assembly/screwdriver_act(mob/living/user, obj/item/tool)
if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
return ITEM_INTERACT_SKIP_TO_ATTACK
user.visible_message(span_notice("[user] finishes the airlock."), \
span_notice("You start finishing the airlock..."))
if(!tool.use_tool(src, user, 40, volume=100))
return ITEM_INTERACT_BLOCKING
if(!loc || state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You finish the airlock."))
finish_door()
return ITEM_INTERACT_SUCCESS
/obj/structure/door_assembly/proc/finish_door()
var/obj/machinery/door/airlock/door
if(glass)
door = new glass_type( loc )
else
door = new airlock_type( loc )
door.setDir(dir)
door.unres_sides = electronics.unres_sides
door.electronics = electronics
door.heat_proof = heat_proof_finished
door.security_level = 0
if(electronics.shell)
door.AddComponent( \
/datum/component/shell, \
unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event, new /obj/item/circuit_component/remotecam/airlock), \
capacity = SHELL_CAPACITY_LARGE, \
shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION|SHELL_FLAG_REQUIRE_ANCHOR \
)
if(electronics.one_access)
door.req_one_access = electronics.accesses
else
door.req_access = electronics.accesses
if(created_name)
door.name = created_name
else if(electronics.passed_name)
door.name = sanitize(electronics.passed_name)
else
door.name = base_name
if(electronics.passed_cycle_id)
door.closeOtherId = electronics.passed_cycle_id
door.update_other_id()
if(door.unres_sides)
door.unres_latch = TRUE
door.previous_airlock = previous_assembly
electronics.forceMove(door)
door.autoclose = TRUE
door.close()
door.update_appearance()
qdel(src)
return door
/obj/structure/door_assembly/update_overlays()
. = ..()
if(!glass)
. += get_airlock_overlay("fill_construction", icon, src, TRUE)
else
. += get_airlock_overlay("glass_construction", overlays_file, src, TRUE)
. += get_airlock_overlay("panel_c[state+1]", overlays_file, src, TRUE)
/obj/structure/door_assembly/update_name()
name = ""
switch(state)
if(AIRLOCK_ASSEMBLY_NEEDS_WIRES)
if(anchored)
name = "secured "
if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS)
name = "wired "
if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER)
name = "near finished "
name += "[heat_proof_finished ? "heat-proofed " : ""][glass ? "window " : ""][base_name] assembly"
return ..()
/obj/structure/door_assembly/proc/transfer_assembly_vars(obj/structure/door_assembly/source, obj/structure/door_assembly/target, previous = FALSE)
target.glass = source.glass
target.heat_proof_finished = source.heat_proof_finished
target.created_name = source.created_name
target.state = source.state
target.set_anchored(source.anchored)
if(previous)
target.previous_assembly = source.type
if(electronics)
target.electronics = source.electronics
source.electronics.forceMove(target)
target.update_appearance()
target.update_name()
qdel(source)
/obj/structure/door_assembly/atom_deconstruct(disassembled = TRUE)
var/turf/target_turf = get_turf(src)
if(!disassembled)
material_amt = rand(2,4)
new material_type(target_turf, material_amt)
if(glass)
if(disassembled)
if(heat_proof_finished)
new /obj/item/stack/sheet/rglass(target_turf)
else
new /obj/item/stack/sheet/glass(target_turf)
else
new /obj/item/shard(target_turf)
if(mineral)
var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]")
new mineral_path(target_turf, 2)
/obj/structure/door_assembly/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
if(the_rcd.mode == RCD_DECONSTRUCT)
return list("delay" = 5 SECONDS, "cost" = 16)
return FALSE
/obj/structure/door_assembly/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
if(rcd_data[RCD_DESIGN_MODE] == RCD_DECONSTRUCT)
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