From 4d913a19bd86faa867ed2c21ef09f969a4b18fed Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 14 Aug 2022 07:05:53 +0200 Subject: [PATCH] [MIRROR] Player-craftable Trapdoors [MDB IGNORE] (#15618) * Player-craftable Trapdoors * Update trapdoor.dm Co-authored-by: RandomGamer123 <31096837+RandomGamer123@users.noreply.github.com> Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> --- code/__DEFINES/turfs.dm | 1 + code/datums/components/crafting/recipes.dm | 23 ++++ code/datums/components/trapdoor.dm | 111 ++++++++++++++++-- code/modules/mapping/mapping_helpers.dm | 3 +- .../research/designs/autolathe_designs.dm | 9 ++ code/modules/research/techweb/all_nodes.dm | 1 + icons/turf/overlays.dmi | Bin 6131 -> 6165 bytes 7 files changed, 138 insertions(+), 10 deletions(-) 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 ce0a6190a6e..d980928bb70 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -750,6 +750,29 @@ time = 200 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 cd82aecaf5d..689ea3b4696 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,19 +47,25 @@ 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) + // 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/try_unlink) /datum/component/trapdoor/UnregisterFromParent() . = ..() @@ -58,6 +73,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 @@ -81,13 +123,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) @@ -101,14 +144,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 @@ -116,8 +163,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 @@ -132,7 +189,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 @@ -148,7 +205,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 @@ -274,3 +331,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 c1a1e3dee19..2215d3e1189 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 e0383063f49..393b59f83ff 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -223,6 +223,15 @@ category = list("initial", "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 5e9affa7505..367894c4b7d 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 c1867fb12f0e51342290655213d81eb5635f6b4a..625537fc173e5ef86b900ea489a608625e2d3045 100644 GIT binary patch delta 5992 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 delta 5959 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