mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## About The Pull Request title removed to_chats because the balloon alerts are more fitting here and you don't need to look back at your chat for any information on these ## Changelog 🆑 refactor: refactored trapdoor remote interaction code /🆑
436 lines
17 KiB
Plaintext
436 lines
17 KiB
Plaintext
|
|
///makes this file more legible
|
|
#define IS_OPEN(parent) isgroundlessturf(parent)
|
|
///distance a trapdoor will accept a link request.
|
|
#define TRAPDOOR_LINKING_SEARCH_RANGE 4
|
|
|
|
/**
|
|
* ## trapdoor component!
|
|
*
|
|
* component attached to floors to turn them into trapdoors, a constructable trap that when signalled drops people to the level below.
|
|
* assembly code at the bottom of this file
|
|
*/
|
|
/datum/component/trapdoor
|
|
///assembly tied to this trapdoor
|
|
var/obj/item/assembly/trapdoor/assembly
|
|
///path of the turf this should change into when the assembly is pulsed. needed for openspace trapdoors knowing what to turn back into
|
|
var/trapdoor_turf_path
|
|
/// is this trapdoor "conspicuous" (ie. it gets examine text and overlay added)
|
|
var/conspicuous
|
|
/// overlay that makes trapdoors more obvious
|
|
var/static/trapdoor_overlay
|
|
/**
|
|
* list of lists that are arguments for readding decals when the linked trapdoor comes back. pain.
|
|
*
|
|
* we are storing this data FOR the trapdoor component we are linked to. kinda like a multitool.
|
|
* format: list(list(element's description, element's cleanable, element's directional, element's pic))
|
|
* the list will be filled with all the data of the deleting elements (when ChangeTurf is called) only when the trapdoor begins to open.
|
|
* so any other case the elements will be changed but not recorded.
|
|
*/
|
|
var/list/stored_decals = list()
|
|
/// Trapdoor shuts close automatically
|
|
var/autoclose = TRUE
|
|
/// Delay before trapdoor shuts close
|
|
var/autoclose_delay = 5 SECONDS
|
|
|
|
/datum/component/trapdoor/Initialize(starts_open, trapdoor_turf_path, assembly, conspicuous = TRUE, list/carried_decals = null, autoclose = TRUE)
|
|
if(!isopenturf(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
src.conspicuous = conspicuous
|
|
src.assembly = assembly
|
|
src.autoclose = autoclose
|
|
if(carried_decals)
|
|
stored_decals = carried_decals.Copy()
|
|
|
|
if(!trapdoor_overlay)
|
|
trapdoor_overlay = mutable_appearance('icons/turf/overlays.dmi', "border_black", ABOVE_NORMAL_TURF_LAYER)
|
|
|
|
if(IS_OPEN(parent))
|
|
openspace_trapdoor_setup(trapdoor_turf_path, assembly)
|
|
if(autoclose)
|
|
addtimer(CALLBACK(src, PROC_REF(try_closing)), autoclose_delay)
|
|
else
|
|
tile_trapdoor_setup(trapdoor_turf_path, assembly)
|
|
|
|
if(starts_open)
|
|
try_opening()
|
|
|
|
///initializing as an opened trapdoor, we need to trust that we were given the data by a closed trapdoor
|
|
/datum/component/trapdoor/proc/openspace_trapdoor_setup(trapdoor_turf_path)
|
|
src.trapdoor_turf_path = trapdoor_turf_path
|
|
|
|
///initializing as a closed trapdoor, we need to take data from the tile we're on to give it to the open state to store
|
|
/datum/component/trapdoor/proc/tile_trapdoor_setup(trapdoor_turf_path)
|
|
src.trapdoor_turf_path = parent.type
|
|
if(stored_decals.len)
|
|
reapply_all_decals()
|
|
if(conspicuous)
|
|
var/turf/parent_turf = parent
|
|
parent_turf.add_overlay(trapdoor_overlay)
|
|
|
|
/datum/component/trapdoor/RegisterWithParent()
|
|
. = ..()
|
|
RegisterSignal(parent, COMSIG_TURF_CHANGE, PROC_REF(turf_changed_pre))
|
|
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
|
if(!src.assembly)
|
|
RegisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK, PROC_REF(on_link_requested))
|
|
else
|
|
RegisterSignal(assembly, COMSIG_ASSEMBLY_PULSED, PROC_REF(toggle_trapdoor))
|
|
// SKYRAT EDIT START - Trapdoors shouldn't be targeted by SSDecay.
|
|
if(isturf(parent))
|
|
var/turf/turf_parent = parent
|
|
turf_parent.turf_flags &= ~CAN_DECAY_BREAK_1
|
|
// SKYRAT EDIT END
|
|
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(try_unlink))
|
|
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_link))
|
|
|
|
/datum/component/trapdoor/UnregisterFromParent()
|
|
. = ..()
|
|
UnregisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK)
|
|
if(assembly)
|
|
UnregisterSignal(assembly, COMSIG_ASSEMBLY_PULSED)
|
|
UnregisterSignal(parent, COMSIG_TURF_CHANGE)
|
|
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
|
|
UnregisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL))
|
|
UnregisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION)
|
|
|
|
/datum/component/trapdoor/proc/try_unlink(turf/source, mob/user, obj/item/tool)
|
|
SIGNAL_HANDLER
|
|
if(!assembly)
|
|
return
|
|
if(IS_OPEN(parent))
|
|
source.balloon_alert(user, "can't unlink trapdoor when its open")
|
|
return
|
|
source.balloon_alert(user, "unlinking trapdoor")
|
|
INVOKE_ASYNC(src, PROC_REF(async_try_unlink), source, user, tool)
|
|
return
|
|
|
|
/datum/component/trapdoor/proc/try_link(turf/source, mob/user, obj/item/tool)
|
|
SIGNAL_HANDLER
|
|
if(!istype(tool, /obj/item/trapdoor_remote))
|
|
return
|
|
var/obj/item/trapdoor_remote/remote = tool
|
|
if(!remote.internals)
|
|
source.balloon_alert(user, "missing internals")
|
|
return
|
|
if(IS_OPEN(parent))
|
|
source.balloon_alert(user, "can't link trapdoor when its open")
|
|
return
|
|
if(assembly)
|
|
source.balloon_alert(user, "already linked")
|
|
return
|
|
source.balloon_alert(user, "linking trapdoor")
|
|
INVOKE_ASYNC(src, PROC_REF(async_try_link), source, user, tool)
|
|
|
|
/datum/component/trapdoor/proc/async_try_link(turf/source, mob/user, obj/item/trapdoor_remote/remote)
|
|
if(!do_after(user, 2 SECONDS, target=source))
|
|
return
|
|
if(IS_OPEN(parent))
|
|
source.balloon_alert(user, "can't link trapdoor when its open")
|
|
return
|
|
src.assembly = remote.internals
|
|
++assembly.linked
|
|
source.balloon_alert(user, "trapdoor linked")
|
|
UnregisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK)
|
|
RegisterSignal(assembly, COMSIG_ASSEMBLY_PULSED, PROC_REF(toggle_trapdoor))
|
|
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(try_unlink))
|
|
|
|
/datum/component/trapdoor/proc/async_try_unlink(turf/source, mob/user, obj/item/tool)
|
|
if(!do_after(user, 5 SECONDS, target=source))
|
|
return
|
|
if(IS_OPEN(parent))
|
|
source.balloon_alert(user, "can't unlink trapdoor when its open")
|
|
return
|
|
assembly.linked = max(assembly.linked - 1, 0)
|
|
stored_decals = list()
|
|
UnregisterSignal(assembly, COMSIG_ASSEMBLY_PULSED)
|
|
UnregisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL))
|
|
RegisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK, PROC_REF(on_link_requested))
|
|
assembly = null
|
|
source.balloon_alert(user, "trapdoor unlinked")
|
|
|
|
/datum/component/trapdoor/proc/decal_detached(datum/source, description, cleanable, directional, pic)
|
|
SIGNAL_HANDLER
|
|
///so it adds the list to the list, not appending it to the end. thank you byond, very cool.
|
|
stored_decals += list(list(description, cleanable, directional, pic))
|
|
|
|
/**
|
|
* ## reapply_all_decals
|
|
*
|
|
* changing turfs does not bring over decals, so we must perform a little bit of element reapplication.
|
|
*/
|
|
/datum/component/trapdoor/proc/reapply_all_decals()
|
|
for(var/list/element_data as anything in stored_decals)
|
|
apply_decal(element_data[1], element_data[2], element_data[3], element_data[4])
|
|
stored_decals = list()
|
|
|
|
/// small proc that takes passed arguments and drops it into a new element
|
|
/datum/component/trapdoor/proc/apply_decal(description, cleanable, directional, pic)
|
|
parent.AddElement(/datum/element/decal, _description = description, _cleanable = cleanable, _dir = directional, _pic = pic)
|
|
|
|
///called by linking remotes to tie an assembly to the trapdoor
|
|
/datum/component/trapdoor/proc/on_link_requested(datum/source, obj/item/assembly/trapdoor/assembly)
|
|
SIGNAL_HANDLER
|
|
if(get_dist(parent, assembly) > TRAPDOOR_LINKING_SEARCH_RANGE)
|
|
return
|
|
. = LINKED_UP
|
|
src.assembly = assembly
|
|
++assembly.linked
|
|
UnregisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK)
|
|
RegisterSignal(assembly, COMSIG_ASSEMBLY_PULSED, PROC_REF(toggle_trapdoor))
|
|
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(try_unlink))
|
|
|
|
///signal called by our assembly being pulsed
|
|
/datum/component/trapdoor/proc/toggle_trapdoor(datum/source)
|
|
SIGNAL_HANDLER
|
|
if(assembly)
|
|
autoclose = assembly.autoclose
|
|
if(!IS_OPEN(parent))
|
|
try_opening()
|
|
else
|
|
try_closing()
|
|
|
|
///signal called by turf changing
|
|
/datum/component/trapdoor/proc/turf_changed_pre(datum/source, path, new_baseturfs, flags, post_change_callbacks)
|
|
SIGNAL_HANDLER
|
|
var/turf/open/dying_trapdoor = parent
|
|
if((flags & CHANGETURF_TRAPDOOR_INDUCED) == 0) //not a process of the trapdoor
|
|
if(!IS_OPEN(parent) && !ispath(path, /turf/closed) && !ispath(path, /turf/open/openspace)) // allow people to place tiles on plating / change tiles without breaking the trapdoor
|
|
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), path, conspicuous, assembly)
|
|
return
|
|
// otherwise, break trapdoor
|
|
dying_trapdoor.visible_message(span_warning("The trapdoor mechanism in [dying_trapdoor] is broken!"))
|
|
if(assembly)
|
|
assembly.linked = max(assembly.linked - 1, 0)
|
|
stored_decals.Cut()
|
|
assembly = null
|
|
return
|
|
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), trapdoor_turf_path, conspicuous, assembly)
|
|
|
|
/**
|
|
* ## carry_over_trapdoor
|
|
*
|
|
* applies the trapdoor to the new turf (created by the last trapdoor)
|
|
* apparently callbacks with arguments on invoke and the callback itself have the callback args go first. interesting!
|
|
*/
|
|
/datum/component/trapdoor/proc/carry_over_trapdoor(trapdoor_turf_path, conspicuous, assembly, turf/new_turf)
|
|
new_turf.AddComponent(/datum/component/trapdoor, FALSE, trapdoor_turf_path, assembly, conspicuous, stored_decals, autoclose)
|
|
|
|
/**
|
|
* ## on_examine
|
|
*
|
|
* examine message for conspicuous trapdoors that makes it obvious
|
|
*/
|
|
/datum/component/trapdoor/proc/on_examine(datum/source, mob/user, list/examine_text)
|
|
SIGNAL_HANDLER
|
|
if(conspicuous)
|
|
examine_text += "There seems to be a tiny gap around this tile with some wires that you might be able to pulse with a <b>multitool</b>."
|
|
|
|
/**
|
|
* ## try_opening
|
|
*
|
|
* small proc for opening the turf into openspace
|
|
* there are no checks for opening a trapdoor, but closed has some
|
|
*/
|
|
/datum/component/trapdoor/proc/try_opening()
|
|
var/turf/open/trapdoor_turf = parent
|
|
///we want to save this turf's decals as they were right before deletion, so this is the point where we begin listening
|
|
if(assembly)
|
|
RegisterSignal(parent, COMSIG_TURF_DECAL_DETACHED, PROC_REF(decal_detached))
|
|
playsound(trapdoor_turf, 'sound/machines/trapdoor/trapdoor_open.ogg', 50)
|
|
trapdoor_turf.visible_message(span_warning("[trapdoor_turf] swings open!"))
|
|
trapdoor_turf.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
|
|
|
|
/**
|
|
* ## try_closing
|
|
*
|
|
* small proc for closing the turf back into what it should be
|
|
* trapdoor can be blocked by building things on the openspace turf
|
|
*/
|
|
/datum/component/trapdoor/proc/try_closing()
|
|
var/turf/open/trapdoor_turf = parent
|
|
var/obj/structure/lattice/blocking = locate() in trapdoor_turf.contents
|
|
if(blocking)
|
|
trapdoor_turf.visible_message(span_warning("The trapdoor mechanism in [trapdoor_turf] tries to shut, but is jammed by [blocking]!"))
|
|
return
|
|
playsound(trapdoor_turf, 'sound/machines/trapdoor/trapdoor_shut.ogg', 50)
|
|
trapdoor_turf.visible_message(span_warning("The trapdoor mechanism in [trapdoor_turf] swings shut!"))
|
|
trapdoor_turf.ChangeTurf(trapdoor_turf_path, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
|
|
|
|
#undef IS_OPEN
|
|
|
|
/obj/item/assembly/trapdoor
|
|
name = "trapdoor controller"
|
|
desc = "A sinister-looking controller for a trapdoor."
|
|
icon_state = "trapdoor"
|
|
///if the trapdoor isn't linked it will try to link on pulse, this shouldn't be spammable
|
|
COOLDOWN_DECLARE(search_cooldown)
|
|
///trapdoor link cooldown time here!
|
|
var/search_cooldown_time = 10 SECONDS
|
|
///if true, a trapdoor in the world has a reference to this assembly and is listening for when it is pulsed.
|
|
var/linked = FALSE
|
|
/// Linked trapdoors will automatically close
|
|
var/autoclose = TRUE
|
|
|
|
/obj/item/assembly/trapdoor/pulsed(mob/pulser)
|
|
. = ..()
|
|
if(linked > 0)
|
|
return
|
|
if(!COOLDOWN_FINISHED(src, search_cooldown))
|
|
if(loc && pulser)
|
|
loc.balloon_alert(pulser, "linking on cooldown!")
|
|
return
|
|
attempt_link_up()
|
|
COOLDOWN_START(src, search_cooldown, search_cooldown_time)
|
|
|
|
/obj/item/assembly/trapdoor/proc/attempt_link_up()
|
|
var/turf/assembly_turf = get_turf(src)
|
|
if(!COOLDOWN_FINISHED(src, search_cooldown))
|
|
var/timeleft = DisplayTimeText(COOLDOWN_TIMELEFT(src, search_cooldown))
|
|
assembly_turf.visible_message(span_warning("[src] is on cooldown! Please wait [timeleft]."), vision_distance = SAMETILE_MESSAGE_RANGE)
|
|
return
|
|
if(SEND_GLOBAL_SIGNAL(COMSIG_GLOB_TRAPDOOR_LINK, src) & LINKED_UP)
|
|
playsound(assembly_turf, 'sound/machines/chime.ogg', 50, TRUE)
|
|
assembly_turf.visible_message(span_notice("[src] has linked up to a nearby trapdoor! \
|
|
You may now use it to check where the trapdoor is... be careful!"), vision_distance = SAMETILE_MESSAGE_RANGE)
|
|
else
|
|
playsound(assembly_turf, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
|
|
assembly_turf.visible_message(span_warning("[src] has failed to find a trapdoor nearby to link to."), vision_distance = SAMETILE_MESSAGE_RANGE)
|
|
|
|
/**
|
|
* ## trapdoor remotes!
|
|
*
|
|
* Item that accepts the assembly for the internals and helps link/activate it.
|
|
* This base type is an empty shell that needs the assembly added to it first to work.
|
|
*/
|
|
/obj/item/trapdoor_remote
|
|
name = "trapdoor remote"
|
|
desc = "A small machine that interfaces with a trapdoor controller for easy use."
|
|
icon = 'icons/obj/devices/remote.dmi'
|
|
icon_state = "trapdoor_remote"
|
|
COOLDOWN_DECLARE(trapdoor_cooldown)
|
|
var/trapdoor_cooldown_time = 2 SECONDS
|
|
var/obj/item/assembly/trapdoor/internals
|
|
|
|
/obj/item/trapdoor_remote/examine(mob/user)
|
|
. = ..()
|
|
if(!internals)
|
|
. += span_warning("[src] has no internals! It needs a trapdoor controller to function.")
|
|
return
|
|
. += span_notice("The internals can be removed with a screwdriver.")
|
|
if(!internals.linked)
|
|
. += span_warning("[src] is not linked to a trapdoor.")
|
|
. += span_notice("[src] will link to nearby trapdoors when used.")
|
|
return
|
|
. += span_notice("[src] is linked to [internals.linked] trapdoor(s).")
|
|
. += span_notice("It can be linked to additional trapdoor(s) by using it on a trapdoor.")
|
|
. += span_notice("Trapdoor can be unlinked with multitool.")
|
|
. += span_notice("Autoclose is [internals.autoclose ? "enabled" : "disabled"], ctrl-click to toggle.")
|
|
if(!COOLDOWN_FINISHED(src, trapdoor_cooldown))
|
|
. += span_warning("It is on a short cooldown.")
|
|
|
|
/obj/item/trapdoor_remote/screwdriver_act(mob/living/user, obj/item/tool)
|
|
. = ..()
|
|
if(!internals)
|
|
to_chat(user, span_warning("[src] has no internals!"))
|
|
return
|
|
to_chat(user, span_notice("You pop [internals] out of [src]."))
|
|
internals.forceMove(get_turf(src))
|
|
internals = null
|
|
|
|
/obj/item/trapdoor_remote/item_interaction(mob/living/user, obj/item/assembly/trapdoor/assembly, list/modifiers)
|
|
if(!istype(assembly))
|
|
return NONE
|
|
if(internals)
|
|
balloon_alert(user, "doesn't fit!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
balloon_alert(user, "added")
|
|
internals = assembly
|
|
user.transferItemToLoc(assembly, src)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/item/trapdoor_remote/attack_self(mob/user, modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return TRUE
|
|
|
|
if(!internals)
|
|
user.balloon_alert(user, "no device!")
|
|
return TRUE
|
|
|
|
if(!internals.linked)
|
|
internals.pulsed(user)
|
|
// The pulse linked successfully
|
|
if(internals.linked)
|
|
user.balloon_alert(user, "linked [internals.linked] trapdoors")
|
|
// The pulse failed to link
|
|
else
|
|
user.balloon_alert(user, "link failed!")
|
|
return TRUE
|
|
|
|
if(!COOLDOWN_FINISHED(src, trapdoor_cooldown))
|
|
user.balloon_alert(user, "on cooldown!")
|
|
return TRUE
|
|
|
|
user.balloon_alert(user, "trapdoor triggered")
|
|
playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
|
|
icon_state = "trapdoor_pressed"
|
|
addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), trapdoor_cooldown_time)
|
|
COOLDOWN_START(src, trapdoor_cooldown, trapdoor_cooldown_time)
|
|
internals.pulsed(user)
|
|
return TRUE
|
|
|
|
/obj/item/trapdoor_remote/item_ctrl_click(mob/user)
|
|
if (!user.is_holding(src))
|
|
return CLICK_ACTION_BLOCKING
|
|
if(!internals)
|
|
user.balloon_alert(user, "no device!")
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
internals.autoclose = !internals.autoclose
|
|
user.balloon_alert(user, "autoclose [internals.autoclose ? "enabled" : "disabled"]")
|
|
return CLICK_ACTION_SUCCESS
|
|
|
|
#undef TRAPDOOR_LINKING_SEARCH_RANGE
|
|
|
|
///subtype with internals already included. If you're giving a department a roundstart trapdoor, this is what you want
|
|
/obj/item/trapdoor_remote/preloaded
|
|
|
|
/obj/item/trapdoor_remote/preloaded/Initialize(mapload)
|
|
. = ..()
|
|
internals = new(src)
|
|
|
|
/// trapdoor parts kit, allows trapdoors to be made by players
|
|
/obj/item/trapdoor_kit
|
|
name = "trapdoor parts kit"
|
|
desc = "A kit containing all the parts needed to build a trapdoor. Can only be used on open space."
|
|
icon = 'icons/obj/weapons/improvised.dmi'
|
|
icon_state = "kitsuitcase"
|
|
var/in_use = FALSE
|
|
|
|
/obj/item/trapdoor_kit/Initialize(mapload)
|
|
. = ..()
|
|
AddElement(/datum/element/openspace_item_click_handler)
|
|
|
|
/obj/item/trapdoor_kit/handle_openspace_click(turf/target, mob/user, list/modifiers)
|
|
interact_with_atom(target, user, modifiers)
|
|
|
|
/obj/item/trapdoor_kit/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
|
var/turf/target_turf = get_turf(interacting_with)
|
|
if(!isopenspaceturf(target_turf))
|
|
return NONE
|
|
in_use = TRUE
|
|
balloon_alert(user, "constructing trapdoor")
|
|
if(!do_after(user, 5 SECONDS, interacting_with))
|
|
in_use = FALSE
|
|
return ITEM_INTERACT_BLOCKING
|
|
in_use = FALSE
|
|
if(!isopenspaceturf(target_turf)) // second check to make sure nothing changed during constructions
|
|
return ITEM_INTERACT_BLOCKING
|
|
var/turf/new_turf = target_turf.place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
|
new_turf.AddComponent(/datum/component/trapdoor, starts_open = FALSE, conspicuous = TRUE)
|
|
balloon_alert(user, "trapdoor constructed")
|
|
qdel(src)
|
|
return ITEM_INTERACT_SUCCESS
|