Unit tests, refactor & realignment for map loaded wall mounts (#93662)

## About The Pull Request
This 1st has to be PR'd so the integration tests can point out all
wallmounts that could not find a support structure to mount on. I then
will do many map edits to align them onto the closest atom

Yes we no longer use wall mount but atom mounted component. All objects
that are mounted on windows/tables & fences now also fall off when
destroyed

It'll probably be a WHILE before I can fix all wall mounts. Long day.
Expect me to misalign many stuff to fix failing CI so make sure to
provide suggestions when possible

Improved wallmount code overall
- Fixes #93793


## Changelog
🆑
fix: fixes all incorrectly maploded wall mounts that aren't actually
hanging on any support structure
fix: objects mounted on tables, windows & fences also fall off now when
destoryed
qol: lights can be mounted on windows
qol: cameras can be mounted on windows
qol: buttons can be mounted on tables
refactor: improved how wall mounts interact with objects as a whole
report bugs on github
/🆑
This commit is contained in:
SyncIt21
2025-11-18 22:37:12 -07:00
committed by GitHub
parent 0926fed114
commit 81427cd5cf
96 changed files with 28598 additions and 28539 deletions
+9 -22
View File
@@ -6,34 +6,21 @@
result_path = /obj/machinery/power/apc/auto_name
/obj/item/wallframe/apc/try_build(turf/on_wall, user)
if(!..())
return
var/turf/T = get_turf(on_wall) //the user is not where it needs to be.
var/area/A = get_area(user)
if(A.apc)
to_chat(user, span_warning("This area already has an APC!"))
return //only one APC per area
if(!A.requires_power)
return FALSE //only one APC per area
if(!A.requires_power || A.always_unpowered)
to_chat(user, span_warning("You cannot place [src] in this area!"))
return //can't place apcs in areas with no power requirement
return FALSE //can't place apcs in areas with no power requirement
for(var/obj/machinery/power/terminal/E in T)
if(E.master)
to_chat(user, span_warning("There is another network terminal here!"))
return
else
new /obj/item/stack/cable_coil(T, 10)
to_chat(user, span_notice("You cut the cables and disassemble the unused power terminal."))
qdel(E)
return TRUE
return FALSE
return ..()
/obj/item/wallframe/apc/screwdriver_act(mob/living/user, obj/item/tool)
//overriding the wallframe parent screwdriver act with this one which allows applying to existing apc frames.
var/turf/turf = get_step(get_turf(user), user.dir)
if(iswallturf(turf))
if(locate(/obj/machinery/power/apc) in get_turf(user))
var/obj/machinery/power/apc/mounted_apc = locate(/obj/machinery/power/apc) in get_turf(user)
mounted_apc.wallframe_act(user, src)
return ITEM_INTERACT_SUCCESS
turf.item_interaction(user, src)
return ITEM_INTERACT_SUCCESS
/obj/item/wallframe/apc/after_attach(obj/machinery/power/apc/attached_to)
for(var/obj/machinery/power/terminal/E in attached_to.loc)
attached_to.make_terminal()
return
@@ -39,7 +39,7 @@
return
RegisterSignal(current_area, COMSIG_AREA_POWER_CHANGE, PROC_REF(AreaPowerCheck))
if(mapload)
find_and_hang_on_wall()
find_and_hang_on_atom()
GLOB.intercoms_list += src
/obj/item/radio/intercom/Destroy()
@@ -142,7 +142,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("telescreen turbine frame", /obj/item/wallframe/telescreen/turbine, 7, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("telescreen engine frame", /obj/item/wallframe/telescreen/engine, 7, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("telescreen auxbase frame", /obj/item/wallframe/telescreen/auxbase, 7, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("tram controller frame", /obj/item/wallframe/tram/controller, 20, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("tram controller frame", /obj/item/wallframe/tram, 20, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("tram display frame", /obj/item/wallframe/indicator_display, 7, crafting_flags = NONE, category = CAT_STRUCTURE), \
null, \
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_APPLIES_MATS, category = CAT_DOORS), \
+72 -45
View File
@@ -6,66 +6,93 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
///The final object to construct after mount
var/result_path
var/wall_external = FALSE // For frames that are external to the wall they are placed on, like light fixtures and cameras.
var/pixel_shift //The amount of pixels
/// For frames that are external to the wall they are placed on, like light fixtures and cameras.
var/wall_external = FALSE
//The amount of pixels to shift when mounted
var/pixel_shift
/obj/item/wallframe/proc/try_build(turf/on_wall, mob/user)
if(get_dist(on_wall,user) > 1)
/**
* Returns an structure to mount on from the atom passed
* for e.g if its not an closed turf then return an structure on the turf to mount on
* Arguments
* * atom/structure - the atom or something in this atom we are trying to mount on
*/
/obj/item/wallframe/proc/find_support_structure(atom/structure)
SHOULD_BE_PURE(TRUE)
return isclosedturf(structure) ? structure : null
/obj/item/wallframe/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
var/atom/support_structure = find_support_structure(interacting_with)
if(isnull(support_structure))
return NONE
if(!try_build(support_structure, user))
return ITEM_INTERACT_FAILURE
playsound(loc, 'sound/machines/click.ogg', 75, TRUE)
user.visible_message(span_notice("[user.name] attaches [src] to the wall."),
span_notice("You attach [src] to the wall."),
span_hear("You hear clicking."))
var/floor_to_support = get_dir(user, support_structure)
var/obj/hanging_object = new result_path(get_turf(user))
hanging_object.setDir(floor_to_support)
if(pixel_shift)
switch(floor_to_support)
if(NORTH)
hanging_object.pixel_y = pixel_shift
if(SOUTH)
hanging_object.pixel_y = -pixel_shift
if(EAST)
hanging_object.pixel_x = pixel_shift
if(WEST)
hanging_object.pixel_x = -pixel_shift
if(!hanging_object.find_and_hang_on_atom())
to_chat(user, span_warning("[src] Could not find all to mount on!."))
return
after_attach(hanging_object)
qdel(src)
return ITEM_INTERACT_SUCCESS
/**
* Check if we can build on this support structure
*
* Arguments
* * atom/support - the atom we are trying to mount on
* * mob/user - the player attempting to do the mount
*/
/obj/item/wallframe/proc/try_build(atom/support, mob/user)
if(get_dist(support, user) > 1)
balloon_alert(user, "you are too far!")
return
var/floor_to_wall = get_dir(user, on_wall)
if(!(floor_to_wall in GLOB.cardinals))
return FALSE
var/floor_to_support = get_dir(user, support)
if(!(floor_to_support in GLOB.cardinals))
balloon_alert(user, "stand in line with wall!")
return
return FALSE
var/turf/T = get_turf(user)
var/area/A = get_area(T)
if(!isfloorturf(T))
balloon_alert(user, "cannot place here!")
return
if(A.always_unpowered)
balloon_alert(user, "cannot place in this area!")
return
if(check_wall_item(T, floor_to_wall, wall_external))
return FALSE
if(check_wall_item(T, floor_to_support, wall_external))
balloon_alert(user, "already something here!")
return
return FALSE
return TRUE
/obj/item/wallframe/proc/attach(turf/on_wall, mob/user)
if(result_path)
playsound(src.loc, 'sound/machines/click.ogg', 75, TRUE)
user.visible_message(span_notice("[user.name] attaches [src] to the wall."),
span_notice("You attach [src] to the wall."),
span_hear("You hear clicking."))
var/floor_to_wall = get_dir(user, on_wall)
var/obj/hanging_object = new result_path(get_turf(user))
hanging_object.setDir(floor_to_wall)
if(pixel_shift)
switch(floor_to_wall)
if(NORTH)
hanging_object.pixel_y = pixel_shift
if(SOUTH)
hanging_object.pixel_y = -pixel_shift
if(EAST)
hanging_object.pixel_x = pixel_shift
if(WEST)
hanging_object.pixel_x = -pixel_shift
hanging_object.find_and_hang_on_wall()
after_attach(hanging_object)
qdel(src)
/**
* Stuff to do after wallframe attached to support atom
*
* Arguments
* * obj/attached_to - the object that has been created on the atom
*/
/obj/item/wallframe/proc/after_attach(obj/attached_to)
transfer_fingerprints_to(attached_to)
/obj/item/wallframe/screwdriver_act(mob/living/user, obj/item/tool)
// For camera-building borgs
var/turf/wall_turf = get_step(get_turf(user), user.dir)
if(iswallturf(wall_turf))
wall_turf.item_interaction(user, src)
return ITEM_INTERACT_SUCCESS
return interact_with_atom(get_step(get_turf(user), user.dir), user)
/obj/item/wallframe/wrench_act(mob/living/user, obj/item/tool)
var/metal_amt = round(custom_materials[GET_MATERIAL_REF(/datum/material/iron)]/SHEET_MATERIAL_AMOUNT) //Replace this shit later