From a77ac3e4f16d78f288d722bfcca81ea2f0503e7b Mon Sep 17 00:00:00 2001 From: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:17:38 -0400 Subject: [PATCH] Adds a new component so that items that are "attached" to walls will now drop/deconstruct on turf destruction. (#77417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## About The Pull Request Adds a new component, called wall_mounted, which applies on the wallframe objects on construction, as well as a number of wall frame objects and structures to cover mapped in, roundstart objects of the like. I might have forgotten a few, but this covers the vast majority that players will run into in a given round. This will cover wall destruction, turf explosion, the whole nine yards, and call that object/structure/machine's deconstruct proc. We have some special handling for intercoms as well since they're apparently items. So most basic case is this: You have a wall. that wall holds a sign. If you examine the wall, it tells you that the wall is currently supporting the **Example Sign**. It tells you that if the wall is damaged or destroyed, the sign will **fall off the wall.** So, if you were to welder, bomb, or hulk your way through that wall, it would call the deconstruct() proc on that sign, and fall off the wall, leaving an item sign at the foot of the wall. ## To-Do - [x] Stop breaking all wallmounts when operating shuttles (Signal conflict with COMSIG_TURF_CHANGED 😔) - [x] Confirm that the ~~deconstruct~~ designated proc of each wallmount falling is sane for the intended object - [x] Clean up the contents of the wall_mounted component to reduce copy-paste on object init. - [x] Add it to more stuff that may just not have a directional helper? - [x] ~~Change how APC construction is handled to make it easier!~~ - [x] ~~Don't accidently nerf malf AI into the ground I guess~~ ## Why It's Good For The Game Closes #22283. Helps close more of #47526. Closes #54983. Closes https://github.com/wall-nerds/wallening/issues/90. All of these objects are "wall mounts". It stands to reason that they're mounted to the walls they appear to be attached to. This attempts to rectify them by giving them a turf link to the turf they're mounted to, and then upon changes to that turf, dropping or breaking that object. It'll need a little more polish to get to 100%, since I can see a few more issues to iron out first, but I'm dropping this here for now to get some feedback and put some fire under me to get this completed. ## Changelog :cl: add: Wall mounted objects (Things like APCs, Air Alarms, Light switches, Signs, Posters, Newscasters, you name it) will now fall to the ground and break or deconstruct when their attaching wall is changed or broken. /:cl: --- code/datums/components/wall_mounted.dm | 74 +++++++++++++++++++ code/game/machinery/barsigns.dm | 1 + code/game/machinery/buttons.dm | 15 ++++ code/game/machinery/camera/camera.dm | 2 + code/game/machinery/camera/camera_assembly.dm | 1 + code/game/machinery/computer/telescreen.dm | 1 + code/game/machinery/defibrillator_mount.dm | 1 + code/game/machinery/digital_clock.dm | 1 + code/game/machinery/firealarm.dm | 2 +- code/game/machinery/flasher.dm | 1 + code/game/machinery/igniter.dm | 1 + code/game/machinery/lightswitch.dm | 1 + .../newscaster/newscaster_machine.dm | 1 + .../machinery/porta_turret/portable_turret.dm | 1 + code/game/machinery/requests_console.dm | 1 + code/game/machinery/status_display.dm | 1 + .../objects/items/devices/radio/intercom.dm | 13 +++- code/game/objects/items/storage/secure.dm | 1 + code/game/objects/items/wall_mounted.dm | 17 +++-- code/game/objects/structures/extinguisher.dm | 1 + code/game/objects/structures/fireaxe.dm | 1 + code/game/objects/structures/mirror.dm | 4 + code/game/objects/structures/noticeboard.dm | 1 + code/game/objects/structures/signs/_signs.dm | 45 ++++++++--- code/game/objects/structures/watercloset.dm | 1 + .../machinery/air_alarm/_air_alarm.dm | 1 + .../machinery/bluespace_vendor.dm | 1 + code/modules/paperwork/ticketmachine.dm | 1 + code/modules/power/apc/apc_main.dm | 1 + code/modules/power/lighting/light.dm | 15 ++++ .../modules/power/lighting/light_construct.dm | 1 + code/modules/reagents/reagent_dispenser.dm | 18 +++++ tgstation.dme | 1 + 33 files changed, 205 insertions(+), 23 deletions(-) create mode 100644 code/datums/components/wall_mounted.dm diff --git a/code/datums/components/wall_mounted.dm b/code/datums/components/wall_mounted.dm new file mode 100644 index 00000000000..67ba52feeea --- /dev/null +++ b/code/datums/components/wall_mounted.dm @@ -0,0 +1,74 @@ +// This element should be applied to wall-mounted machines/structures, so that if the wall it's "hanging" from is broken or deconstructed, the wall-hung structure will deconstruct. +/datum/component/wall_mounted + dupe_mode = COMPONENT_DUPE_ALLOWED + /// The wall our object is currently linked to. + var/turf/hanging_wall_turf + /// Callback to the parent's proc to call on the linked object when the wall disappear's or changes. + var/datum/callback/on_drop + +/datum/component/wall_mounted/Initialize(target_wall, on_drop_callback) + . = ..() + if(!isobj(parent)) + return COMPONENT_INCOMPATIBLE + if(!isturf(target_wall)) + return COMPONENT_INCOMPATIBLE + if(!on_drop_callback) + on_drop = TYPE_PROC_REF(/obj, deconstruct) + hanging_wall_turf = target_wall + on_drop = on_drop_callback + +/datum/component/wall_mounted/RegisterWithParent() + RegisterSignal(hanging_wall_turf, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(hanging_wall_turf, COMSIG_TURF_CHANGE, PROC_REF(drop_wallmount)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(drop_wallmount)) + RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_linked_destroyed)) + +/datum/component/wall_mounted/UnregisterFromParent() + UnregisterSignal(hanging_wall_turf, list(COMSIG_ATOM_EXAMINE, COMSIG_TURF_CHANGE)) + UnregisterSignal(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED)) + hanging_wall_turf = null + +/** + * Basic reference handling if the hanging/linked object is destroyed first. + */ +/datum/component/wall_mounted/proc/on_linked_destroyed() + SIGNAL_HANDLER + if(!QDELING(src)) + qdel(src) + +/** + * When the wall is examined, explains that it's supporting the linked object. + */ +/datum/component/wall_mounted/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + examine_list += span_notice("\The [hanging_wall_turf] is currently supporting [span_bold("[parent]")]. Deconstruction or excessive damage would cause it to [span_bold("fall to the ground")].") + +/** + * Handles the dropping of the linked object. This is done via deconstruction, as that should be the most sane way to handle it for most objects. + * Except for intercoms, which are handled by creating a new wallframe intercom, as they're apparently items. + */ +/datum/component/wall_mounted/proc/drop_wallmount() + SIGNAL_HANDLER + var/obj/hanging_parent = parent + hanging_parent.visible_message(message = span_warning("\The [hanging_parent] falls off the wall!"), vision_distance = 5) + on_drop?.Invoke(hanging_parent) + if(!QDELING(src)) + qdel(src) //Well, we fell off the wall, so we're done here. +/** + * Checks object direction and then verifies if there's a wall in that direction. Finally, applies a wall_mounted component to the object. + * + * @param directional If TRUE, will use the direction of the object to determine the wall to attach to. If FALSE, will use the object's loc. + * @param custom_drop_callback If set, will use this callback instead of the default deconstruct callback. + */ +/obj/proc/find_and_hang_on_wall(directional = TRUE, custom_drop_callback) + if(istype(get_area(src), /area/shuttle)) + return FALSE //For now, we're going to keep the component off of shuttles to avoid the turf changing issue. We'll hit that later really; + var/turf/attachable_wall + if(directional) + attachable_wall = get_step(src, dir) + else + attachable_wall = loc ///Pull from the curent object loc + if(!iswallturf(attachable_wall)) + return FALSE//Nothing to latch onto, or not the right thing. + src.AddComponent(/datum/component/wall_mounted, attachable_wall, custom_drop_callback) + return TRUE diff --git a/code/game/machinery/barsigns.dm b/code/game/machinery/barsigns.dm index 16686b23c77..b8cee255356 100644 --- a/code/game/machinery/barsigns.dm +++ b/code/game/machinery/barsigns.dm @@ -24,6 +24,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/barsign, 32) /obj/machinery/barsign/Initialize(mapload) . = ..() set_sign(new /datum/barsign/hiddensigns/signoff) + find_and_hang_on_wall() /obj/machinery/barsign/proc/set_sign(datum/barsign/sign) if(!istype(sign)) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 21a718d8705..5824f93dcc7 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -55,6 +55,7 @@ board.accesses = req_one_access setup_device() + find_and_hang_on_wall() /obj/machinery/button/Destroy() QDEL_NULL(device) @@ -233,6 +234,20 @@ device.pulsed(user) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_BUTTON_PRESSED,src) +/** + * Called when the mounted button's wall is knocked down. + */ +/obj/machinery/button/proc/knock_down() + if(device) + device.forceMove(get_turf(src)) + device = null + if(board) + board.forceMove(get_turf(src)) + req_access = list() + req_one_access = list() + board = null + qdel(src) + /obj/machinery/button/door name = "door button" desc = "A door remote control switch." diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index d0792e8c617..da13377b2f5 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -103,6 +103,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) update_appearance() alarm_manager = new(src) + find_and_hang_on_wall(directional = TRUE, \ + custom_drop_callback = CALLBACK(src, PROC_REF(deconstruct), FALSE)) RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index 37907a5ba29..786a2c3a39a 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -67,6 +67,7 @@ . = ..() if(building) setDir(ndir) + find_and_hang_on_wall() /obj/structure/camera_assembly/update_icon_state() icon_state = "[xray_module ? "xray" : null][initial(icon_state)]" diff --git a/code/game/machinery/computer/telescreen.dm b/code/game/machinery/computer/telescreen.dm index b03ed092269..ceb3c2e923e 100644 --- a/code/game/machinery/computer/telescreen.dm +++ b/code/game/machinery/computer/telescreen.dm @@ -56,6 +56,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai /obj/machinery/computer/security/telescreen/entertainment/Initialize(mapload) . = ..() RegisterSignal(src, COMSIG_CLICK, PROC_REF(BigClick)) + find_and_hang_on_wall() // Bypass clickchain to allow humans to use the telescreen from a distance /obj/machinery/computer/security/telescreen/entertainment/proc/BigClick() diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm index d212f2ba45d..cb12a399260 100644 --- a/code/game/machinery/defibrillator_mount.dm +++ b/code/game/machinery/defibrillator_mount.dm @@ -23,6 +23,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount, 28) /obj/machinery/defibrillator_mount/loaded/Initialize(mapload) //loaded subtype for mapping use . = ..() defib = new/obj/item/defibrillator/loaded(src) + find_and_hang_on_wall() MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount, 28) diff --git a/code/game/machinery/digital_clock.dm b/code/game/machinery/digital_clock.dm index 0bfb8aeba72..d0b695428af 100644 --- a/code/game/machinery/digital_clock.dm +++ b/code/game/machinery/digital_clock.dm @@ -83,6 +83,7 @@ /obj/machinery/digital_clock/Initialize(mapload) . = ..() START_PROCESSING(SSdigital_clock, src) + find_and_hang_on_wall() /obj/machinery/digital_clock/Destroy() STOP_PROCESSING(SSdigital_clock, src) diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 680a7a837d5..82c5ce56a16 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -80,7 +80,7 @@ ) ) AddElement(/datum/element/contextual_screentip_mob_typechecks, hovering_mob_typechecks) - + find_and_hang_on_wall() update_appearance() diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 8d3c4febd70..8075b51dcab 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -28,6 +28,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/flasher, 26) . = ..() // ..() is EXTREMELY IMPORTANT, never forget to add it if(!built) bulb = new(src) + find_and_hang_on_wall() /obj/machinery/flasher/vv_edit_var(vname, vval) . = ..() diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 73f527ca7d5..060b29c4d20 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -162,6 +162,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/sparker, 26) spark_system.set_up(2, 1, src) spark_system.attach(src) register_context() + find_and_hang_on_wall() /obj/machinery/sparker/Destroy() QDEL_NULL(spark_system) diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index f9275794853..54c098de9fd 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -31,6 +31,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/light_switch, 26) area = get_area(src) if(!name) name = "light switch ([area.name])" + find_and_hang_on_wall(custom_drop_callback = CALLBACK(src, PROC_REF(deconstruct), TRUE)) update_appearance() diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 98cce390010..1ae08c066f0 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -73,6 +73,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) GLOB.allCasters += src GLOB.allbountyboards += src update_appearance() + find_and_hang_on_wall() /obj/machinery/newscaster/Destroy() GLOB.allCasters -= src diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index b19a8032e7d..9fae5086989 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -919,6 +919,7 @@ DEFINE_BITFIELD(turret_flags, list( if(built) locked = FALSE power_change() //Checks power and initial settings + find_and_hang_on_wall() /obj/machinery/turretid/Destroy() turrets.Cut() diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index cb2c541d72b..06837cc961d 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -127,6 +127,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) radio = new /obj/item/radio(src) radio.set_listening(FALSE) + find_and_hang_on_wall() /obj/machinery/requests_console/Destroy() QDEL_NULL(radio) diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 102159bbbb9..cbfb61a4a03 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -319,6 +319,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/evac, 32) AddComponent(/datum/component/usb_port, list( /obj/item/circuit_component/status_display, )) + find_and_hang_on_wall() /obj/machinery/status_display/evac/Destroy() SSradio.remove_object(src,frequency) diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 3e261b9eabb..7cba8e9bbac 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -34,6 +34,9 @@ return RegisterSignal(current_area, COMSIG_AREA_POWER_CHANGE, PROC_REF(AreaPowerCheck)) GLOB.intercoms_list += src + if(!unscrewed) + find_and_hang_on_wall(directional = TRUE, \ + custom_drop_callback = CALLBACK(src, PROC_REF(knock_down))) /obj/item/radio/intercom/Destroy() . = ..() @@ -79,8 +82,7 @@ if(tool.use_tool(src, user, 80)) user.visible_message(span_notice("[user] unsecures [src]!"), span_notice("You detach [src] from the wall.")) playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - new/obj/item/wallframe/intercom(get_turf(src)) - qdel(src) + knock_down() /** * Override attack_tk_grab instead of attack_tk because we actually want attack_tk's @@ -174,6 +176,13 @@ set_on(current_area.powered(AREA_USAGE_EQUIP)) // set "on" to the equipment power status of our area. update_appearance() +/** + * Called by the wall mount component and reused during the tool deconstruction proc. + */ +/obj/item/radio/intercom/proc/knock_down() + new/obj/item/wallframe/intercom(get_turf(src)) + qdel(src) + //Created through the autolathe or through deconstructing intercoms. Can be applied to wall to make a new intercom on it! /obj/item/wallframe/intercom name = "intercom frame" diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index 46eb8356c79..ad4be8d6e40 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -181,6 +181,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/secure/safe, 32) . = ..() atom_storage.set_holdable(cant_hold_list = list(/obj/item/storage/secure/briefcase)) atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC + find_and_hang_on_wall() /obj/item/storage/secure/safe/PopulateContents() new /obj/item/paper(src) diff --git a/code/game/objects/items/wall_mounted.dm b/code/game/objects/items/wall_mounted.dm index 3cfafe74f97..1467c8eb62a 100644 --- a/code/game/objects/items/wall_mounted.dm +++ b/code/game/objects/items/wall_mounted.dm @@ -40,21 +40,22 @@ span_hear("You hear clicking.")) var/floor_to_wall = get_dir(user, on_wall) - var/obj/O = new result_path(get_turf(user), floor_to_wall, TRUE) - O.setDir(floor_to_wall) + var/obj/hanging_object = new result_path(get_turf(user), floor_to_wall, TRUE) + hanging_object.setDir(floor_to_wall) + on_wall.AddComponent(/datum/component/wall_mounted, hanging_object) if(pixel_shift) switch(floor_to_wall) if(NORTH) - O.pixel_y = pixel_shift + hanging_object.pixel_y = pixel_shift if(SOUTH) - O.pixel_y = -pixel_shift + hanging_object.pixel_y = -pixel_shift if(EAST) - O.pixel_x = pixel_shift + hanging_object.pixel_x = pixel_shift if(WEST) - O.pixel_x = -pixel_shift - after_attach(O) - + hanging_object.pixel_x = -pixel_shift + after_attach(hanging_object) + hanging_object.find_and_hang_on_wall() qdel(src) /obj/item/wallframe/proc/after_attach(obj/attached_to) diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index 5eca5026776..91b63314eb3 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -20,6 +20,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29) stored_extinguisher = new /obj/item/extinguisher(src) 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) . = ..() diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm index 75294549ebe..59a00618b0e 100644 --- a/code/game/objects/structures/fireaxe.dm +++ b/code/game/objects/structures/fireaxe.dm @@ -36,6 +36,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet, 32) if(populate_contents) held_item = new item_path(src) update_appearance() + find_and_hang_on_wall() /obj/structure/fireaxecabinet/Destroy() if(held_item) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 2fcaefd3575..2a4f44cc0a8 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -27,6 +27,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28) +/obj/structure/mirror/Initialize(mapload) + . = ..() + find_and_hang_on_wall() + /obj/structure/mirror/broken icon_state = "mirror_broke" diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index 9d09a747018..534622619d1 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -26,6 +26,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32) I.forceMove(src) notices++ update_appearance(UPDATE_ICON) + find_and_hang_on_wall() //attaching papers!! /obj/structure/noticeboard/attackby(obj/item/O, mob/user, params) diff --git a/code/game/objects/structures/signs/_signs.dm b/code/game/objects/structures/signs/_signs.dm index cdb7ba0811f..b8709334c37 100644 --- a/code/game/objects/structures/signs/_signs.dm +++ b/code/game/objects/structures/signs/_signs.dm @@ -14,6 +14,8 @@ var/is_editable = FALSE ///sign_change_name is used to make nice looking, alphebetized 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 @@ -23,6 +25,12 @@ /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 /obj/structure/sign/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() @@ -55,18 +63,7 @@ playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) user.visible_message(span_notice("[user] unfastens [src]."), \ span_notice("You unfasten [src].")) - var/obj/item/sign/unwrenched_sign = new (get_turf(user)) - 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. - unwrenched_sign.desc = "[desc] It can be placed on a wall." - unwrenched_sign.icon = icon - unwrenched_sign.icon_state = icon_state - unwrenched_sign.sign_path = type - unwrenched_sign.set_custom_materials(custom_materials) //This is here so picture frames and wooden things don't get messed up. - 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. + knock_down(user) return TRUE /obj/structure/sign/welder_act(mob/living/user, obj/item/I) @@ -115,6 +112,29 @@ 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() + 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. + unwrenched_sign.desc = "[desc] It can be placed on a wall." + unwrenched_sign.icon = icon + unwrenched_sign.icon_state = icon_state + unwrenched_sign.sign_path = type + unwrenched_sign.set_custom_materials(custom_materials) //This is here so picture frames and wooden things don't get messed up. + 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) inheret from the parent type. icon_state = "backing" name = "sign backing" @@ -211,6 +231,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) qdel(src) /obj/item/sign/welder_act(mob/living/user, obj/item/I) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index ec0ae01fbf6..b991bc0c274 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -168,6 +168,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32) /obj/structure/urinal/Initialize(mapload) . = ..() hidden_item = new /obj/item/food/urinalcake + find_and_hang_on_wall() /obj/structure/urinal/attack_hand(mob/living/user, list/modifiers) . = ..() diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm index 3ef5c9a4d33..a11f439ec31 100644 --- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm +++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm @@ -124,6 +124,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm) GLOB.air_alarms += src update_appearance() + find_and_hang_on_wall() /obj/machinery/airalarm/process() if(!COOLDOWN_FINISHED(src, warning_cooldown)) diff --git a/code/modules/atmospherics/machinery/bluespace_vendor.dm b/code/modules/atmospherics/machinery/bluespace_vendor.dm index 7ea5b827d4a..84753354018 100644 --- a/code/modules/atmospherics/machinery/bluespace_vendor.dm +++ b/code/modules/atmospherics/machinery/bluespace_vendor.dm @@ -69,6 +69,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30) /obj/machinery/bluespace_vendor/Initialize(mapload) . = ..() AddComponent(/datum/component/payment, tank_cost, SSeconomy.get_dep_account(ACCOUNT_ENG), PAYMENT_ANGRY) + find_and_hang_on_wall( FALSE) /obj/machinery/bluespace_vendor/LateInitialize() . = ..() diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm index 8d62bf784cb..f41e780e4a9 100644 --- a/code/modules/paperwork/ticketmachine.dm +++ b/code/modules/paperwork/ticketmachine.dm @@ -31,6 +31,7 @@ /obj/machinery/ticket_machine/Initialize(mapload) . = ..() update_appearance() + find_and_hang_on_wall() /obj/machinery/ticket_machine/Destroy() for(var/obj/item/ticket_machine_ticket/ticket in tickets) diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm index e2a1b956590..9bfdaf1e249 100644 --- a/code/modules/power/apc/apc_main.dm +++ b/code/modules/power/apc/apc_main.dm @@ -212,6 +212,7 @@ AddElement(/datum/element/contextual_screentip_bare_hands, rmb_text = "Toggle interface lock") AddElement(/datum/element/contextual_screentip_mob_typechecks, hovering_mob_typechecks) + find_and_hang_on_wall() /obj/machinery/power/apc/Destroy() if(malfai && operating) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index aabaced17d8..747010fc6c0 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -116,6 +116,7 @@ RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) AddElement(/datum/element/atmos_sensitive, mapload) + find_and_hang_on_wall(custom_drop_callback = CALLBACK(src, PROC_REF(knock_down))) return INITIALIZE_HINT_LATELOAD /obj/machinery/light/LateInitialize() @@ -688,6 +689,20 @@ continue INVOKE_ASYNC(src, PROC_REF(flicker)) +/** + * All the effects that occur when a light falls off a wall that it was hung onto. + */ +/obj/machinery/light/proc/knock_down() + new /obj/item/wallframe/light_fixture(drop_location()) + new /obj/item/stack/cable_coil(drop_location(), 1, "red") + if(status != LIGHT_BROKEN) + break_light_tube(FALSE) + if(status != LIGHT_EMPTY) + drop_light_tube() + if(cell) + cell.forceMove(drop_location()) + qdel(src) + /obj/machinery/light/floor name = "floor light" desc = "A lightbulb you can walk on without breaking it, amazing." diff --git a/code/modules/power/lighting/light_construct.dm b/code/modules/power/lighting/light_construct.dm index 5a8f406b86c..05d9533c79e 100644 --- a/code/modules/power/lighting/light_construct.dm +++ b/code/modules/power/lighting/light_construct.dm @@ -33,6 +33,7 @@ . = ..() if(building) setDir(ndir) + find_and_hang_on_wall() /obj/structure/light_construct/Destroy() QDEL_NULL(cell) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 9e3fe883b2e..d1b7afd4b78 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -1,3 +1,5 @@ +#define REAGENT_SPILL_DIVISOR 200 + /obj/structure/reagent_dispensers name = "Dispenser" desc = "..." @@ -203,6 +205,15 @@ return TRUE return FALSE +/obj/structure/reagent_dispensers/proc/knock_down() + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new () + var/range = reagents.total_volume / REAGENT_SPILL_DIVISOR + smoke.attach(drop_location()) + smoke.set_up(round(range), holder = drop_location(), location = drop_location(), carry = reagents, silent = FALSE) + smoke.start(log = TRUE) + reagents.clear_reagents() + qdel(src) + /obj/structure/reagent_dispensers/wrench_act(mob/living/user, obj/item/tool) . = ..() if(!openable) @@ -322,6 +333,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3 . = ..() if(prob(1)) desc = "IT'S PEPPER TIME, BITCH!" + find_and_hang_on_wall() /obj/structure/reagent_dispensers/water_cooler name = "liquid cooler" @@ -373,6 +385,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3 MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/virusfood, 30) +/obj/structure/reagent_dispensers/wall/virusfood/Initialize(mapload) + . = ..() + find_and_hang_on_wall() + /obj/structure/reagent_dispensers/cooking_oil name = "vat of cooking oil" desc = "A huge metal vat with a tap on the front. Filled with cooking oil for use in frying food." @@ -435,3 +451,5 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/virusfood, 30 desc = "A stationary, plumbed, fuel tank." reagent_id = /datum/reagent/fuel accepts_rig = TRUE + +#undef REAGENT_SPILL_DIVISOR diff --git a/tgstation.dme b/tgstation.dme index 49b9b0ec7a9..66ebdccabd0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1106,6 +1106,7 @@ #include "code\datums\components\uplink.dm" #include "code\datums\components\usb_port.dm" #include "code\datums\components\vacuum.dm" +#include "code\datums\components\wall_mounted.dm" #include "code\datums\components\wearertargeting.dm" #include "code\datums\components\weatherannouncer.dm" #include "code\datums\components\wet_floor.dm"