diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 1af08fc90a9..220a1067982 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -4,6 +4,7 @@ #define CHANGETURF_SKIP (1<<3) // A flag for PlaceOnTop to just instance the new turf instead of calling ChangeTurf. Used for uninitialized turfs NOTHING ELSE #define CHANGETURF_INHERIT_AIR (1<<4) // Inherit air from previous turf. Implies CHANGETURF_IGNORE_AIR #define CHANGETURF_RECALC_ADJACENT (1<<5) //Immediately recalc adjacent atmos turfs instead of queuing. +#define CHANGETURF_TRAPDOOR_INDUCED (1<<6) // Caused by a trapdoor, for trapdoor to know that this changeturf was caused by itself #define IS_OPAQUE_TURF(turf) (turf.directional_opacity == ALL_CARDINALS) diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm index b3ad127b364..6aad6f5c4ee 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -759,6 +759,29 @@ time = 20 SECONDS category = CAT_MISC +/datum/crafting_recipe/trapdoor_kit + name = "Trapdoor Construction Kit" + result = /obj/item/trapdoor_kit + reqs = list(/obj/item/stack/sheet/iron = 4, + /obj/item/stack/rods = 4, + /obj/item/stack/cable_coil = 10, + /obj/item/stock_parts/manipulator = 2, + /obj/item/assembly/signaler = 1) + tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER) + time = 10 SECONDS + category = CAT_MISC + +/datum/crafting_recipe/trapdoor_remote + name = "Trapdoor Remote" + result = /obj/item/trapdoor_remote/preloaded // since its useless without its assembly just require an assembly to craft it + reqs = list( + /obj/item/compact_remote = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/assembly/trapdoor = 1) + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 5 SECONDS + category = CAT_MISC + /datum/crafting_recipe/mousetrap name = "Mouse Trap" result = /obj/item/assembly/mousetrap diff --git a/code/datums/components/trapdoor.dm b/code/datums/components/trapdoor.dm index 32a562f3ba6..9b6072d2621 100644 --- a/code/datums/components/trapdoor.dm +++ b/code/datums/components/trapdoor.dm @@ -2,7 +2,7 @@ ///makes this file more legible #define IS_OPEN(parent) isgroundlessturf(parent) ///distance a trapdoor will accept a link request. -#define TRAPDOOR_LINKING_SEARCH_RANGE 7 +#define TRAPDOOR_LINKING_SEARCH_RANGE 4 /** * ## trapdoor component! @@ -15,12 +15,21 @@ 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 -/datum/component/trapdoor/Initialize(starts_open, trapdoor_turf_path, assembly) +/datum/component/trapdoor/Initialize(starts_open, trapdoor_turf_path, assembly, conspicuous = TRUE) if(!isopenturf(parent)) return COMPONENT_INCOMPATIBLE + src.conspicuous = conspicuous src.assembly = assembly + + 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) else @@ -38,14 +47,19 @@ src.trapdoor_turf_path = parent.type if(assembly && assembly.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/turf_changed_pre) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine) if(!src.assembly) RegisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK, .proc/on_link_requested) else RegisterSignal(assembly, COMSIG_ASSEMBLY_PULSED, .proc/toggle_trapdoor) + RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), .proc/try_unlink) /datum/component/trapdoor/UnregisterFromParent() . = ..() @@ -53,6 +67,33 @@ if(assembly) UnregisterSignal(assembly, COMSIG_ASSEMBLY_PULSED) UnregisterSignal(parent, COMSIG_TURF_CHANGE) + UnregisterSignal(parent, COMSIG_PARENT_EXAMINE) + UnregisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL)) + +/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/async_try_unlink, source, user, tool) + return + +/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 = FALSE + assembly.stored_decals = list() + UnregisterSignal(assembly, COMSIG_ASSEMBLY_PULSED) + UnregisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL)) + RegisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK, .proc/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 @@ -76,13 +117,14 @@ ///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) + if(get_dist(parent, assembly) > TRAPDOOR_LINKING_SEARCH_RANGE || assembly.linked) return . = LINKED_UP src.assembly = assembly assembly.linked = TRUE UnregisterSignal(SSdcs, COMSIG_GLOB_TRAPDOOR_LINK) RegisterSignal(assembly, COMSIG_ASSEMBLY_PULSED, .proc/toggle_trapdoor) + RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), .proc/try_unlink) ///signal called by our assembly being pulsed /datum/component/trapdoor/proc/toggle_trapdoor(datum/source) @@ -96,14 +138,18 @@ /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((!IS_OPEN(dying_trapdoor) && !IS_OPEN(path)) || path == /turf/open/floor/plating) //not a process of the trapdoor, so this trapdoor has been destroyed + 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, /datum/component/trapdoor.proc/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 = FALSE assembly.stored_decals.Cut() assembly = null return - post_change_callbacks += CALLBACK(assembly, /obj/item/assembly/trapdoor.proc/carry_over_trapdoor, trapdoor_turf_path) + post_change_callbacks += CALLBACK(src, /datum/component/trapdoor.proc/carry_over_trapdoor, trapdoor_turf_path, conspicuous, assembly) /** * ## carry_over_trapdoor @@ -111,8 +157,18 @@ * 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! */ -/obj/item/assembly/trapdoor/proc/carry_over_trapdoor(trapdoor_turf_path, turf/new_turf) - new_turf.AddComponent(/datum/component/trapdoor, FALSE, trapdoor_turf_path, src) +/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) + +/** + * ## 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 multitool." /** * ## try_opening @@ -127,7 +183,7 @@ RegisterSignal(parent, COMSIG_TURF_DECAL_DETACHED, .proc/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) + trapdoor_turf.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED) /** * ## try_closing @@ -143,7 +199,7 @@ 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) + trapdoor_turf.ChangeTurf(trapdoor_turf_path, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED) #undef IS_OPEN @@ -280,3 +336,39 @@ /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/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, proximity_flag, click_parameters) + afterattack(target, user, proximity_flag, click_parameters) + +/obj/item/trapdoor_kit/afterattack(atom/target, mob/user, proximity_flag) + . = ..() + if(!proximity_flag) + return + var/turf/target_turf = get_turf(target) + if(!isopenspaceturf(target_turf)) + return + in_use = TRUE + balloon_alert(user, "constructing trapdoor") + if(!do_after(user, 5 SECONDS, target = target)) + in_use = FALSE + return + in_use = FALSE + if(!isopenspaceturf(target_turf)) // second check to make sure nothing changed during constructions + return + var/turf/new_turf = target_turf.PlaceOnTop(/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 diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 63a0ef170c6..c1deedcdae2 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -674,6 +674,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) * ## trapdoor placer! * * This places an unlinked trapdoor in the tile its on (so someone with a remote needs to link it up first) + * Pre-mapped trapdoors (unlike player-made ones) are not conspicuous by default so nothing stands out with them * Admins may spawn this in the round for additional trapdoors if they so desire * if YOU want to learn more about trapdoors, read about the component at trapdoor.dm * note: this is not a turf subtype because the trapdoor needs the type of the turf to turn back into @@ -685,7 +686,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) /obj/effect/mapping_helpers/trapdoor_placer/LateInitialize() var/turf/component_target = get_turf(src) - component_target.AddComponent(/datum/component/trapdoor, starts_open = FALSE) + component_target.AddComponent(/datum/component/trapdoor, starts_open = FALSE, conspicuous = FALSE) qdel(src) /obj/effect/mapping_helpers/ztrait_injector diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 9305657ac53..11fb02db590 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -223,6 +223,15 @@ category = list(RND_CATEGORY_INITIAL, RND_CATEGORY_ELECTRONICS) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING +/datum/design/trapdoor_electronics + name = "Trapdoor Controller Electronics" + id = "trapdoor_electronics" + build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = 50, /datum/material/glass = 50) + build_path = /obj/item/assembly/trapdoor + category = list("initial", "Electronics") + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + /datum/design/camera name = "Camera" id = "camera" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 5ce3b9392ef..0b2691dbb1a 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -62,6 +62,7 @@ "titaniumglass", "toner", "toner_large", + "trapdoor_electronics", "turbine_part_compressor", "turbine_part_rotor", "turbine_part_stator", diff --git a/icons/turf/overlays.dmi b/icons/turf/overlays.dmi index c1867fb12f0..625537fc173 100644 Binary files a/icons/turf/overlays.dmi and b/icons/turf/overlays.dmi differ