Airlock construction refactor & new squeeze-through mechanic (#22194)

# Summary

This PR fixes airlock assembly rotations during airlock deconstruction,
adds sprited construction steps and adds a squeeze-through mechanic.

## Changes

- Fixed door assembly not getting correct rotation when deconstruction
an airlock.
- Added squeeze-through mechanic to door assemblies.
- Added construction sprite steps to door assemblies. (These are very
basic, as the construction sprites are also rather basic. Refactoring of
these is welcome.)

## Preview squeeze through mechanic


![dreamseeker_YDwuacD5De](https://github.com/user-attachments/assets/8afae93d-87ad-453e-9dde-999592c20654)
This commit is contained in:
FabianK3
2026-04-24 02:19:34 +00:00
committed by GitHub
parent ab81581dbd
commit 44c9644589
10 changed files with 85 additions and 4 deletions
+76 -2
View File
@@ -6,7 +6,7 @@
name = "airlock assembly"
desc = "An airlock assembly."
icon = 'icons/obj/doors/basic/single/generic/door.dmi'
icon_state = "construction"
icon_state = "construction_new"
anchored = FALSE
density = TRUE
w_class = WEIGHT_CLASS_HUGE
@@ -62,6 +62,7 @@
. = list()
. = ..()
. += "It is currently facing [dir2text(dir)]."
. += "It is possible to squeeze through it as long it is anchored to the floor. Drag and drop a mob onto the assembly to do so."
/obj/structure/door_assembly/Initialize(mapload)
. = ..()
@@ -83,6 +84,7 @@
/obj/structure/door_assembly/door_assembly_ext
base_name = "external airlock"
icon = 'icons/obj/doors/basic/single/external/door.dmi'
airlock_type = /obj/machinery/door/airlock/external
/obj/structure/door_assembly/door_assembly_hatch
@@ -92,7 +94,7 @@
/obj/structure/door_assembly/door_assembly_mhatch
base_name = "maintenance hatch"
icon = 'icons/obj/doors/basic/single/external/door.dmi'
icon = 'icons/obj/doors/basic/single/hatch/door.dmi'
airlock_type = /obj/machinery/door/airlock/maintenance_hatch
/obj/structure/door_assembly/door_assembly_highsecurity
@@ -320,6 +322,75 @@
..()
update_state()
/obj/structure/door_assembly/mouse_drop_receive(atom/dropped, mob/user, params)
// Logic allowing people to squeeze through airlock assemblies.
if(use_check(user) || !Adjacent(user))
return ..()
to_chat(user, SPAN_NOTICE("You try to squeeze through \the [src]..."))
var/speed_multiplier = 1
switch(user.mob_size)
if(MOB_LARGE)
to_chat(user, SPAN_WARNING("However, you are too large to fit through it!"))
return ..()
if(MOB_MEDIUM)
speed_multiplier = 1.0
if(MOB_SMALL)
speed_multiplier = 0.66
if(MOB_TINY)
speed_multiplier = 0.33
if(MOB_MINISCULE)
speed_multiplier = 0.25
else
speed_multiplier = 1.0
if(!src.anchored)
to_chat(user, SPAN_WARNING("\The [src] isn't secured to the floor yet, you can't squeeze through it."))
return ..()
// Check if user is standing directly infront of the airlock facing the open assembly, not diagonal or besides the assembly.
if((src.dir == NORTH || src.dir == SOUTH) && !((src.loc.y == user.loc.y + 1 || src.loc.y == user.loc.y -1) && user.loc.x == src.loc.x))
return ..()
else if((src.dir == EAST || src.dir == WEST) && !((src.loc.x == user.loc.x + 1 || src.loc.x == user.loc.x - 1) && user.loc.y == src.loc.y))
return ..()
// Get position on the opposite side of the airlock from the user.
var/dx = src.loc.x - user.loc.x
var/dy = src.loc.y - user.loc.y
// normalize to a single-tile step
dx = dx > 0 ? 1 : (dx < 0 ? -1 : 0)
dy = dy > 0 ? 1 : (dy < 0 ? -1 : 0)
var/turf/T = locate(src.loc.x + dx, src.loc.y + dy, src.z)
// Check if the location is empty to step upon.
if(!T || !turf_clear(T))
to_chat(user, SPAN_WARNING("However, there is no room to step out on the other side!"))
return ..()
var/turf/old_loc = locate(user.loc.x, user.loc.y, user.loc.z)
src.add_fingerprint(user)
user.resting = TRUE
if(do_after(user, (3 * speed_multiplier) SECONDS, src, DO_UNIQUE, (INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY))) // Squeeze in
user.forceMove(src.loc) // Ignore density check
sleep((2 * speed_multiplier) SECOND) // Crawl delay
if(src.loc != user.loc) // Check if user was moved out of the assembly during the process, if so, abort
user.resting = FALSE
return ..()
if(prob(10 * speed_multiplier)) // Climb out
visible_message(SPAN_WARNING("[user] gets tangled in the \the [src] for a moment while squeezing through it..."))
if(!do_after(user, (5 * speed_multiplier) SECONDS, src, DO_UNIQUE, (INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY)))
user.forceMove(old_loc)
user.resting = FALSE
return ..()
user.Move(T) // Get out behind assembly
visible_message(SPAN_NOTICE("[user] squeezes through the \the [src]!"))
user.resting = FALSE
..()
/obj/structure/door_assembly/proc/CanChainsaw(var/obj/item/material/twohanded/chainsaw/ChainSawVar)
return (ChainSawVar.powered)
@@ -328,10 +399,13 @@
switch (state)
if(STATE_UNWIRED)
name = anchored ? "secured " : "unsecured "
icon_state = anchored ? "construction_anchored" : "construction_new"
if(STATE_WIRED)
name = "wired "
icon_state = "construction_wired"
if(STATE_ELECTRONICS_INSTALLED)
name = "near-finished "
icon_state = "construction_electronics"
name += "[glass == TRUE ? "window " : ""][glass ? "glass airlock" : base_name] assembly"
if(created_name)
name += " ([created_name])"