diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 21c239c59b9..7a20d3ea4c9 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1377,6 +1377,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait given to mobs that can dig #define TRAIT_MOB_CAN_DIG "mob_can_dig" +/// This atom has a tether attached to it +#define TRAIT_TETHER_ATTACHED "tether_attached" + /** * * This trait is used in some interactions very high in the interaction chain to allow diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index f5d79447f56..fb53fc6cad0 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -34,6 +34,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_STICKERED" = TRAIT_STICKERED, "TRAIT_UNHITTABLE_BY_PROJECTILES" = TRAIT_UNHITTABLE_BY_PROJECTILES, "TRAIT_UNLINKABLE_FISHING_SPOT" = TRAIT_UNLINKABLE_FISHING_SPOT, + "TRAIT_TETHER_ATTACHED" = TRAIT_TETHER_ATTACHED, ), /atom/movable = list( "TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE, diff --git a/code/datums/components/tether.dm b/code/datums/components/tether.dm index b9eb40647ac..ba11306cd89 100644 --- a/code/datums/components/tether.dm +++ b/code/datums/components/tether.dm @@ -15,8 +15,13 @@ var/datum/beam/tether_beam /// Tether module if we were created by one var/obj/item/mod/module/tether/parent_module + /// Source, if any, for TRAIT_TETHER_ATTACHED we add + var/tether_trait_source + /// If TRUE, only add TRAIT_TETHER_ATTACHED to our parent + var/no_target_trait -/datum/component/tether/Initialize(atom/tether_target, max_dist = 7, tether_name, atom/embed_target = null, start_distance = null, parent_module = null) +/datum/component/tether/Initialize(atom/tether_target, max_dist = 7, tether_name, atom/embed_target = null, start_distance = null, \ + parent_module = null, tether_trait_source = null, no_target_trait = FALSE) if(!ismovable(parent) || !istype(tether_target) || !tether_target.loc) return COMPONENT_INCOMPATIBLE @@ -24,6 +29,8 @@ src.embed_target = embed_target src.max_dist = max_dist src.parent_module = parent_module + src.tether_trait_source = tether_trait_source + src.no_target_trait = no_target_trait cur_dist = max_dist if (start_distance != null) cur_dist = start_distance @@ -34,6 +41,10 @@ src.tether_name = initial(tmp.name) else src.tether_name = tether_name + if (!isnull(tether_trait_source)) + ADD_TRAIT(parent, TRAIT_TETHER_ATTACHED, tether_trait_source) + if (!no_target_trait) + ADD_TRAIT(tether_target, TRAIT_TETHER_ATTACHED, tether_trait_source) /datum/component/tether/RegisterWithParent() RegisterSignal(parent, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(check_tether)) @@ -54,8 +65,12 @@ /datum/component/tether/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED)) + if (!isnull(tether_trait_source)) + REMOVE_TRAIT(parent, TRAIT_TETHER_ATTACHED, tether_trait_source) if (!QDELETED(tether_target)) UnregisterSignal(tether_target, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + if (!isnull(tether_trait_source) && !no_target_trait) + REMOVE_TRAIT(tether_target, TRAIT_TETHER_ATTACHED, tether_trait_source) if (!QDELETED(tether_beam)) UnregisterSignal(tether_beam.visuals, list(COMSIG_CLICK, COMSIG_QDELETING)) qdel(tether_beam) @@ -143,12 +158,23 @@ INVOKE_ASYNC(src, PROC_REF(process_beam_click), source, location, params, user) /datum/component/tether/proc/process_beam_click(atom/source, atom/location, params, mob/user) - if (!location.can_interact(user)) + var/turf/nearest_turf + for (var/turf/line_turf in get_line(get_turf(parent), get_turf(tether_target))) + if (user.CanReach(line_turf)) + nearest_turf = line_turf + break + + if (isnull(nearest_turf)) return + + if (!user.can_perform_action(nearest_turf)) + nearest_turf.balloon_alert(user, "cannot reach!") + return + var/list/modifiers = params2list(params) if(LAZYACCESS(modifiers, CTRL_CLICK)) location.balloon_alert(user, "cutting the tether...") - if (!do_after(user, 1 SECONDS, user)) + if (!do_after(user, 2 SECONDS, user)) return qdel(src) diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index abe09594500..ea12a61e5b4 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -91,6 +91,13 @@ cooldown_time = 1.5 SECONDS required_slots = list(ITEM_SLOT_GLOVES) +/obj/item/mod/module/tether/used() + if(HAS_TRAIT_FROM(mod.wearer, TRAIT_TETHER_ATTACHED, REF(src))) + balloon_alert(mod.wearer, "already tethered!") + playsound(src, 'sound/items/weapons/gun/general/dry_fire.ogg', 25, TRUE) + return FALSE + return ..() + /obj/item/mod/module/tether/on_select_use(atom/target) . = ..() if(!.) @@ -139,7 +146,10 @@ /obj/projectile/tether/proc/on_embedded(datum/source, obj/item/payload, atom/hit) SIGNAL_HANDLER - firer.AddComponent(/datum/component/tether, hit, 7, "MODtether", payload, parent_module = parent_module) + if (HAS_TRAIT_FROM(hit, TRAIT_TETHER_ATTACHED, REF(parent_module))) + return + + firer.AddComponent(/datum/component/tether, hit, 7, "MODtether", payload, parent_module = parent_module, tether_trait_source = REF(parent_module)) /obj/projectile/tether/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) . = ..() @@ -161,7 +171,12 @@ return if (istype(target, /obj/item/tether_anchor) || isstructure(target) || ismachinery(target)) - firer.AddComponent(/datum/component/tether, target, 7, "MODtether", parent_module = parent_module) + if(HAS_TRAIT_FROM(target, TRAIT_TETHER_ATTACHED, REF(parent_module))) + return + var/avoid_target_trait = FALSE + if (istype(target, /obj/item/tether_anchor)) + avoid_target_trait = TRUE + firer.AddComponent(/datum/component/tether, target, 7, "MODtether", parent_module = parent_module, tether_trait_source = REF(parent_module), no_target_trait = avoid_target_trait) return var/hitx = impact_x @@ -182,7 +197,7 @@ anchor.pixel_x = hitx anchor.pixel_y = hity anchor.anchored = TRUE - firer.AddComponent(/datum/component/tether, anchor, 7, "MODtether", parent_module = parent_module) + firer.AddComponent(/datum/component/tether, anchor, 7, "MODtether", parent_module = parent_module, tether_trait_source = REF(parent_module)) /obj/projectile/tether/Destroy() QDEL_NULL(line) @@ -210,8 +225,12 @@ if (!can_interact(user) || !user.CanReach(src) || !isturf(loc)) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(HAS_TRAIT_FROM(user, TRAIT_TETHER_ATTACHED, REF(src))) + balloon_alert(user, "already tethered!") + return + balloon_alert(user, "attached tether") - user.AddComponent(/datum/component/tether, src, 7, "tether") + user.AddComponent(/datum/component/tether, src, 7, "tether", tether_trait_source = REF(src)) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/tether_anchor/mouse_drop_receive(atom/target, mob/user, params) @@ -221,9 +240,13 @@ if (!isliving(target) || !target.CanReach(src)) return + if(HAS_TRAIT_FROM(target, TRAIT_TETHER_ATTACHED, REF(src))) + balloon_alert(user, "already tethered!") + return + if (target == user) balloon_alert(user, "attached tether") - user.AddComponent(/datum/component/tether, src, 7, "tether") + user.AddComponent(/datum/component/tether, src, 7, "tether", tether_trait_source = REF(src), no_target_trait = TRUE) return balloon_alert(user, "attaching tether...") @@ -231,9 +254,13 @@ if (!do_after(user, 5 SECONDS, target)) return + if(HAS_TRAIT_FROM(target, TRAIT_TETHER_ATTACHED, REF(src))) + balloon_alert(user, "already tethered!") + return + balloon_alert(user, "attached tether") to_chat(target, span_userdanger("[user] attaches a tether to you!")) - target.AddComponent(/datum/component/tether, src, 7, "tether") + target.AddComponent(/datum/component/tether, src, 7, "tether", tether_trait_source = REF(src), no_target_trait = TRUE) /datum/embed_data/tether_projectile embed_chance = 65 //spiky diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi index 9811af0b548..f91f9f0b9dc 100644 Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ