From 86b06e3d3f6ecb141377911ff9dcc0e2600982a0 Mon Sep 17 00:00:00 2001 From: Iajret Creature <122297233+Steals-The-PRs@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:05:04 +0300 Subject: [PATCH] [MIRROR] Buffs the SC/FISHER Saboteur Handgun. (#1432) (#2396) * Buffs the SC/FISHER Saboteur Handgun. (#81553) ## About The Pull Request The saboteur gun will now silence pAIs, toggle off radio broadcasting (won't auto-relay nearby speech), disable turrets, chill out secbots a little, and turn off APCs like power outages do. The disrupt duration has also been buffed from 10/20 to 15/25 for ranged and point-blank respectively. Removed a conspicious chat message from an otherwise inconspicious gun. Brought the code up to date. ## Why It's Good For The Game The concept is cool, alas it's also undermined by how much of a joke it's right now, and the game has plenty already. The amount of interactions it has with things is underwhelming, so you could barely consider it a stealth tool. The duration is also quite scarce, I pointed that out in the original PR too. Basically, I want to make the item cooler. ## Changelog :cl: balance: Buffed the duration of the SC/FISHER Saboteur Handgun's disruption effects. It's also stealthier and it won't conspiciously alert living mobs hit by it. add: Added saboteur interactions with radios, pAIs, turrets, secbots and APCs. /:cl: --------- * Buffs the SC/FISHER Saboteur Handgun. --------- Co-authored-by: NovaBot <154629622+NovaBot13@users.noreply.github.com> Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> Co-authored-by: Jacquerel --- code/__DEFINES/dcs/signals/signals_object.dm | 4 ++ .../__DEFINES/dcs/signals/signals_saboteur.dm | 5 -- code/__DEFINES/robots.dm | 5 ++ code/game/machinery/camera/camera.dm | 3 +- .../machinery/porta_turret/portable_turret.dm | 63 +++++++++++-------- .../game/objects/items/devices/radio/radio.dm | 8 +++ code/game/objects/items/melee/baton.dm | 9 +++ .../cargo/markets/market_items/weapons.dm | 2 +- code/modules/clothing/head/hardhat.dm | 13 +++- code/modules/clothing/spacesuits/plasmamen.dm | 19 +++++- code/modules/mob/living/carbon/human/human.dm | 2 +- .../modules/mob/living/silicon/robot/robot.dm | 2 +- .../mob/living/simple_animal/bot/secbot.dm | 13 ++++ code/modules/mod/modules/modules_general.dm | 12 ++++ code/modules/pai/card.dm | 18 ++++-- code/modules/pai/pai.dm | 7 +++ code/modules/power/apc/apc_main.dm | 6 ++ .../projectile/special/lightbreaker.dm | 35 ----------- .../projectile/special/saboteur.dm | 30 +++++++++ code/modules/vehicles/mecha/_mecha.dm | 7 +++ tgstation.dme | 3 +- 21 files changed, 181 insertions(+), 85 deletions(-) delete mode 100644 code/__DEFINES/dcs/signals/signals_saboteur.dm delete mode 100644 code/modules/projectiles/projectile/special/lightbreaker.dm create mode 100644 code/modules/projectiles/projectile/special/saboteur.dm diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 91dbba15ff4..da4fc0fcb2f 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -406,6 +406,10 @@ ///sent to the projectile when spawning the item (shrapnel) that may be embedded: (new_item) #define COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED "projectile_on_spawn_embedded" +/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target +#define COMSIG_HIT_BY_SABOTEUR "hit_by_saboteur" + #define COMSIG_SABOTEUR_SUCCESS (1<<0) + // /obj/vehicle/sealed/car/vim signals ///from /datum/action/vehicle/sealed/noise/chime/Trigger(): () diff --git a/code/__DEFINES/dcs/signals/signals_saboteur.dm b/code/__DEFINES/dcs/signals/signals_saboteur.dm deleted file mode 100644 index 5b0fef52aee..00000000000 --- a/code/__DEFINES/dcs/signals/signals_saboteur.dm +++ /dev/null @@ -1,5 +0,0 @@ -// Light disruptor. Not to be confused with the light eater, which permanently disables lights. - -/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target -#define COMSIG_HIT_BY_SABOTEUR "HIT_BY_SABOTEUR" - #define COMSIG_SABOTEUR_SUCCESS (1<<0) diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 573373347eb..71e2042fdc6 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -198,6 +198,8 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define JUDGE_IDCHECK (1<<1) #define JUDGE_WEAPONCHECK (1<<2) #define JUDGE_RECORDCHECK (1<<3) +///lowered threat level +#define JUDGE_CHILLOUT (1<<4) /// Above this level of assessed threat, Beepsky will attack you #define THREAT_ASSESS_DANGEROUS 4 @@ -215,6 +217,8 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define SECBOT_CHECK_RECORDS (1<<3) ///Whether we will stun & cuff or endlessly stun #define SECBOT_HANDCUFF_TARGET (1<<4) +///if it's currently affected by a saboteur bolt (lowered perp threat level) +#define SECBOT_SABOTEUR_AFFECTED (1<<5) DEFINE_BITFIELD(security_mode_flags, list( "SECBOT_DECLARE_ARRESTS" = SECBOT_DECLARE_ARRESTS, @@ -222,6 +226,7 @@ DEFINE_BITFIELD(security_mode_flags, list( "SECBOT_CHECK_WEAPONS" = SECBOT_CHECK_WEAPONS, "SECBOT_CHECK_RECORDS" = SECBOT_CHECK_RECORDS, "SECBOT_HANDCUFF_TARGET" = SECBOT_HANDCUFF_TARGET, + "SECBOT_SABOTEUR_AFFECTED" = SECBOT_SABOTEUR_AFFECTED, )) //MedBOT defines diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 69c01a22f62..9dc50ff9ce7 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -185,7 +185,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) /obj/machinery/camera/proc/on_saboteur(datum/source, disrupt_duration) SIGNAL_HANDLER - emp_act(EMP_LIGHT, reset_time = disrupt_duration) + //lasts twice as much so we don't have to constantly shoot cameras just to be S T E A L T H Y + emp_act(EMP_LIGHT, reset_time = disrupt_duration * 2) return COMSIG_SABOTEUR_SUCCESS /obj/machinery/camera/proc/post_emp_reset(thisemp, previous_network) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 809171af1ef..f5f8327607a 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -103,6 +103,8 @@ DEFINE_BITFIELD(turret_flags, list( var/datum/action/turret_toggle/toggle_action /// Mob that is remotely controlling the turret var/mob/remote_controller + /// While the cooldown is still going on, it cannot be re-enabled. + COOLDOWN_DECLARE(disabled_time) /datum/armor/machinery_porta_turret melee = 50 @@ -133,18 +135,32 @@ DEFINE_BITFIELD(turret_flags, list( if(!has_cover) INVOKE_ASYNC(src, PROC_REF(popUp)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + AddElement(/datum/element/hostile_machine) -/obj/machinery/porta_turret/proc/toggle_on(set_to) - var/current = on - if (!isnull(set_to)) - on = set_to - else - on = !on - if (current != on) - check_should_process() - if (!on) - popDown() +///Toggles the turret on or off depending on the value of the turn_on arg. +/obj/machinery/porta_turret/proc/toggle_on(turn_on = TRUE) + if(on == turn_on) + return + if(on && !COOLDOWN_FINISHED(src, disabled_time)) + return + on = turn_on + check_should_process() + if (!on) + popDown() + +///Prevents turned from being turned on for a duration, then restarts them after that if the second ard is true. +/obj/machinery/porta_turret/proc/set_disabled(duration = 6 SECONDS, will_restart = on) + COOLDOWN_START(src, disabled_time, duration) + if(will_restart) + addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), duration + 1) //the cooldown isn't over until the tick after its end. + toggle_on(FALSE) + +/obj/machinery/porta_turret/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(set_disabled), disrupt_duration) + return COMSIG_SABOTEUR_SUCCESS /obj/machinery/porta_turret/proc/check_should_process() if (datum_flags & DF_ISPROCESSING) @@ -256,7 +272,7 @@ DEFINE_BITFIELD(turret_flags, list( switch(action) if("power") if(anchored) - toggle_on() + toggle_on(!on) return TRUE else to_chat(usr, span_warning("It has to be secured first!")) @@ -364,10 +380,8 @@ DEFINE_BITFIELD(turret_flags, list( audible_message(span_hear("[src] hums oddly...")) obj_flags |= EMAGGED controllock = TRUE - toggle_on(FALSE) //turns off the turret temporarily + set_disabled(6 SECONDS) update_appearance() - //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 6 SECONDS) //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here return TRUE @@ -385,11 +399,9 @@ DEFINE_BITFIELD(turret_flags, list( if(prob(20)) turret_flags |= TURRET_FLAG_SHOOT_ALL // Shooting everyone is a pretty big deal, so it's least likely to get turned on - toggle_on(FALSE) + set_disabled(rand(6 SECONDS, 20 SECONDS)) remove_control() - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), rand(60,600)) - /obj/machinery/porta_turret/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) . = ..() if(. && atom_integrity > 0) //damage received @@ -1186,17 +1198,14 @@ DEFINE_BITFIELD(turret_flags, list( installation = /obj/item/gun/energy/laser/bluetag team_color = "blue" -/obj/machinery/porta_turret/lasertag/bullet_act(obj/projectile/P) +/obj/machinery/porta_turret/lasertag/bullet_act(obj/projectile/projectile) . = ..() - if(on) - if(team_color == "blue") - if(istype(P, /obj/projectile/beam/lasertag/redtag)) - toggle_on(FALSE) - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 10 SECONDS) - else if(team_color == "red") - if(istype(P, /obj/projectile/beam/lasertag/bluetag)) - toggle_on(FALSE) - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 10 SECONDS) + if(!on) + return + if(team_color == "blue" && istype(projectile, /obj/projectile/beam/lasertag/redtag)) + set_disabled(10 SECONDS) + else if(team_color == "red" && istype(projectile, /obj/projectile/beam/lasertag/bluetag)) + set_disabled(10 SECONDS) #undef TURRET_STUN #undef TURRET_LETHAL diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index a3861199178..d9e76042dcd 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -120,6 +120,8 @@ slapcraft_recipes = list(/datum/crafting_recipe/improv_explosive)\ ) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + /obj/item/radio/Destroy() remove_radio_all(src) //Just to be sure QDEL_NULL(wires) @@ -127,6 +129,12 @@ QDEL_NULL(keyslot) return ..() +/obj/item/radio/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(broadcasting) //no broadcasting but it can still be used to send radio messages. + set_broadcasting(FALSE) + return COMSIG_SABOTEUR_SUCCESS + /obj/item/radio/proc/set_frequency(new_frequency) SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args) remove_radio(src, frequency) diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index f57d9af87f0..a9e28e2fe03 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -454,6 +454,7 @@ else cell = new preload_cell_type(src) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(convert)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) update_appearance() /obj/item/melee/baton/security/get_cell() @@ -488,6 +489,14 @@ qdel(item) qdel(src) +/obj/item/melee/baton/security/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(!active) + return + toggle_light() + active = FALSE + update_appearance() + return COMSIG_SABOTEUR_SUCCESS /obj/item/melee/baton/security/Exited(atom/movable/mov_content) . = ..() if(mov_content == cell) diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm index 052074a30b3..11f242d57c8 100644 --- a/code/modules/cargo/markets/market_items/weapons.dm +++ b/code/modules/cargo/markets/market_items/weapons.dm @@ -66,7 +66,7 @@ /datum/market_item/weapon/fisher name = "SC/FISHER Saboteur Handgun" - desc = "A self-recharging, compact pistol that disrupts flashlights and security cameras, if only temporarily. Also usable in melee." + desc = "A self-recharging, compact pistol that disrupts lights, cameras, APCs, turrets and more, if only temporarily. Also usable in melee." item = /obj/item/gun/energy/recharge/fisher price_min = CARGO_CRATE_VALUE * 2 diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 57dfa130824..7d87a1d8297 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -40,9 +40,7 @@ /obj/item/clothing/head/utility/hardhat/Initialize(mapload) . = ..() AddElement(/datum/element/update_icon_updates_onmob) - -/obj/item/clothing/head/utility/hardhat/attack_self(mob/living/user) - toggle_helmet_light(user) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /obj/item/clothing/head/utility/hardhat/proc/toggle_helmet_light(mob/living/user) on = !on @@ -62,6 +60,15 @@ /obj/item/clothing/head/utility/hardhat/proc/turn_off(mob/user) set_light_on(FALSE) +/obj/item/clothing/head/utility/hardhat/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(on) + toggle_helmet_light() + return COMSIG_SABOTEUR_SUCCESS + +/obj/item/clothing/head/utility/hardhat/attack_self(mob/living/user) + toggle_helmet_light(user) + /obj/item/clothing/head/utility/hardhat/orange icon_state = "hardhat0_orange" inhand_icon_state = null diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 64b83c07650..860ac12d202 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -78,6 +78,7 @@ . = ..() visor_toggling() update_appearance() + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /obj/item/clothing/head/helmet/space/plasmaman/examine() . = ..() @@ -108,6 +109,11 @@ playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing update_appearance() +/obj/item/clothing/head/helmet/space/plasmaman/update_icon_state() + . = ..() + icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" + inhand_icon_state = icon_state + /obj/item/clothing/head/helmet/space/plasmaman/update_overlays() . = ..() . += visor_icon @@ -139,6 +145,7 @@ hitting_clothing.forceMove(src) update_appearance() +///By the by, helmets have the update_icon_updates_onmob element, so we don't have to call mob.update_worn_head() /obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(mutable_appearance/standing, isinhands) . = ..() if(!isinhands && smile) @@ -161,9 +168,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/attack_self(mob/user) helmet_on = !helmet_on - icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" - inhand_icon_state = icon_state - user.update_worn_head() //So the mob overlay updates + update_appearance() if(helmet_on) if(!up) @@ -176,6 +181,14 @@ update_item_action_buttons() +/obj/item/clothing/head/helmet/space/plasmaman/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(!helmet_on) + return + helmet_on = FALSE + update_appearance() + return COMSIG_SABOTEUR_SUCCESS + /obj/item/clothing/head/helmet/space/plasmaman/attack_hand_secondary(mob/user) ..() . = SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index a02caac1fa6..f21ddd54519 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -396,7 +396,7 @@ if(judgement_criteria & JUDGE_EMAGGED || HAS_TRAIT(src, TRAIT_ALWAYS_WANTED)) return 10 //Everyone is a criminal! - var/threatcount = 0 + var/threatcount = judgement_criteria & JUDGE_CHILLOUT ? -THREAT_ASSESS_DANGEROUS : 0 //Lasertag bullshit if(lasercolor) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index f00249137f4..adc0310d189 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -444,7 +444,7 @@ SIGNAL_HANDLER if(lamp_enabled) toggle_headlamp(TRUE) - to_chat(src, span_warning("Your headlamp was forcibly turned off. Restarting it should fix it, though.")) + balloon_alert(src, "headlamp off!") return COMSIG_SABOTEUR_SUCCESS /** diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 51a18523646..5ab70f0f57d 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -141,6 +141,7 @@ ) AddElement(/datum/element/connect_loc, loc_connections) AddComponent(/datum/component/security_vision, judgement_criteria = NONE, update_judgement_criteria = CALLBACK(src, PROC_REF(judgement_criteria))) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /mob/living/simple_animal/bot/secbot/Destroy() QDEL_NULL(weapon) @@ -164,6 +165,16 @@ SSmove_manager.stop_looping(src) last_found = world.time +/mob/living/simple_animal/bot/secbot/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(!(security_mode_flags & SECBOT_SABOTEUR_AFFECTED)) + security_mode_flags |= SECBOT_SABOTEUR_AFFECTED + addtimer(CALLBACK(src, PROC_REF(remove_saboteur_effect)), disrupt_duration) + return COMSIG_SABOTEUR_SUCCESS + +/mob/living/simple_animal/bot/secbot/proc/remove_saboteur_effect() + security_mode_flags &= ~SECBOT_SABOTEUR_AFFECTED + /mob/living/simple_animal/bot/secbot/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)//shocks only make him angry if(base_speed < initial(base_speed) + 3) base_speed += 3 @@ -232,6 +243,8 @@ final |= JUDGE_RECORDCHECK if(security_mode_flags & SECBOT_CHECK_WEAPONS) final |= JUDGE_WEAPONCHECK + if(security_mode_flags & SECBOT_SABOTEUR_AFFECTED) + final |= JUDGE_CHILLOUT return final /mob/living/simple_animal/bot/secbot/proc/special_retaliate_after_attack(mob/user) //allows special actions to take place after being attacked. diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 5cc542703d2..f03b0ac44c6 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -377,6 +377,12 @@ /// Maximum range we can set. var/max_range = 5 +/obj/item/mod/module/flashlight/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + +/obj/item/mod/module/flashlight/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_HIT_BY_SABOTEUR) + /obj/item/mod/module/flashlight/on_activation() . = ..() if(!.) @@ -392,6 +398,12 @@ set_light_flags(light_flags & ~LIGHT_ATTACHED) set_light_on(active) +/obj/item/mod/module/flashlight/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(active) + on_deactivation() + return COMSIG_SABOTEUR_SUCCESS + /obj/item/mod/module/flashlight/on_process(seconds_per_tick) active_power_cost = base_power * light_range return ..() diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm index c1a9c5a88ba..77ca42aeebc 100644 --- a/code/modules/pai/card.dm +++ b/code/modules/pai/card.dm @@ -21,6 +21,14 @@ /// Prevents a crew member from hitting "request pAI" repeatedly var/request_spam = FALSE +/obj/item/pai_card/Initialize(mapload) + . = ..() + + update_appearance() + SSpai.pai_card_list += src + ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + /obj/item/pai_card/attackby(obj/item/used, mob/user, params) if(pai && istype(used, /obj/item/encryptionkey)) if(!pai.encrypt_mod) @@ -63,12 +71,10 @@ emotion_icon = initial(emotion_icon) update_appearance() -/obj/item/pai_card/Initialize(mapload) - . = ..() - - update_appearance() - SSpai.pai_card_list += src - ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) +/obj/item/pai_card/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(pai) + return pai.on_saboteur(source, disrupt_duration) /obj/item/pai_card/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is staring sadly at [src]! [user.p_They()] can't keep living without real human intimacy!")) diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm index 0ead2e9fd2c..cd128499750 100644 --- a/code/modules/pai/pai.dm +++ b/code/modules/pai/pai.dm @@ -230,6 +230,7 @@ RegisterSignal(src, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed)) RegisterSignals(src, list(COMSIG_LIVING_ADJUST_BRUTE_DAMAGE, COMSIG_LIVING_ADJUST_BURN_DAMAGE), PROC_REF(on_shell_damaged)) RegisterSignal(src, COMSIG_LIVING_ADJUST_STAMINA_DAMAGE, PROC_REF(on_shell_weakened)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /mob/living/silicon/pai/make_laws() laws = new /datum/ai_laws/pai() @@ -343,6 +344,12 @@ to_chat(src, span_danger("WARN: Holochasis range restrictions disabled.")) return TRUE +/mob/living/silicon/pai/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + set_silence_if_lower(disrupt_duration) + balloon_alert(src, "muted!") + return COMSIG_SABOTEUR_SUCCESS + /** * Resets the pAI and any emagged status. * diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm index 95086b9949b..e910a325575 100644 --- a/code/modules/power/apc/apc_main.dm +++ b/code/modules/power/apc/apc_main.dm @@ -205,6 +205,7 @@ register_context() addtimer(CALLBACK(src, PROC_REF(update)), 5) RegisterSignal(SSdcs, COMSIG_GLOB_GREY_TIDE, PROC_REF(grey_tide)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) update_appearance() var/static/list/hovering_mob_typechecks = list( @@ -235,6 +236,11 @@ disconnect_terminal() return ..() +/obj/machinery/power/apc/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + energy_fail(disrupt_duration) + return COMSIG_SABOTEUR_SUCCESS + /obj/machinery/power/apc/proc/assign_to_area(area/target_area = get_area(src)) if(area == target_area) return diff --git a/code/modules/projectiles/projectile/special/lightbreaker.dm b/code/modules/projectiles/projectile/special/lightbreaker.dm deleted file mode 100644 index 2be6d9e4470..00000000000 --- a/code/modules/projectiles/projectile/special/lightbreaker.dm +++ /dev/null @@ -1,35 +0,0 @@ -/obj/projectile/energy/fisher - name = "attenuated kinetic force" - alpha = 0 - damage = 0 - damage_type = BRUTE - armor_flag = BOMB - range = 21 - projectile_phasing = PASSTABLE | PASSMOB | PASSMACHINE | PASSSTRUCTURE - hitscan = TRUE - var/disrupt_duration = 10 SECONDS - -/obj/projectile/energy/fisher/on_hit(atom/target, blocked, pierce_hit) - . = ..() - var/lights_flickered = 0 - if(SEND_SIGNAL(target, COMSIG_HIT_BY_SABOTEUR, disrupt_duration) & COMSIG_SABOTEUR_SUCCESS) - lights_flickered++ - if(!isliving(target)) - return - var/list/things_to_disrupt = list() - if(ishuman(target)) - var/mob/living/carbon/human/human_target = target - things_to_disrupt = human_target.get_all_gear() - else - var/mob/living/living_target = target // i guess this covers borgs too? - things_to_disrupt = living_target.get_equipped_items(include_pockets = TRUE, include_accessories = TRUE) - for(var/obj/item/thingy as anything in things_to_disrupt) - if(SEND_SIGNAL(thingy, COMSIG_HIT_BY_SABOTEUR, disrupt_duration) & COMSIG_SABOTEUR_SUCCESS) - lights_flickered++ - if(lights_flickered) - to_chat(target, span_warning("Your light [lights_flickered > 1 ? "sources flick" : "source flicks"] off.")) - -/obj/projectile/energy/fisher/melee - range = 1 - suppressed = SUPPRESSED_VERY - disrupt_duration = 20 SECONDS diff --git a/code/modules/projectiles/projectile/special/saboteur.dm b/code/modules/projectiles/projectile/special/saboteur.dm new file mode 100644 index 00000000000..4ef6b9ffcbe --- /dev/null +++ b/code/modules/projectiles/projectile/special/saboteur.dm @@ -0,0 +1,30 @@ +/obj/projectile/energy/fisher + name = "attenuated kinetic force" + alpha = 0 + damage = 0 + damage_type = BRUTE + armor_flag = BOMB + range = 21 + projectile_phasing = PASSTABLE | PASSMOB | PASSMACHINE | PASSSTRUCTURE + hitscan = TRUE + var/disrupt_duration = 15 SECONDS + +/obj/projectile/energy/fisher/on_hit(atom/target, blocked, pierce_hit) + . = ..() + var/list/things_to_disrupt = list(target) + if(isliving(target)) + var/mob/living/live_target = target + things_to_disrupt += live_target.get_all_gear() + + var/success = FALSE + for(var/atom/disrupted as anything in things_to_disrupt) + if(SEND_SIGNAL(disrupted, COMSIG_HIT_BY_SABOTEUR, disrupt_duration) & COMSIG_SABOTEUR_SUCCESS) + success = TRUE + + if(success && ismob(firer)) + target.balloon_alert(firer, "disrupted") + +/obj/projectile/energy/fisher/melee + range = 1 + suppressed = SUPPRESSED_VERY + disrupt_duration = 25 SECONDS diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index 85d408f5348..f718bd5c695 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -221,6 +221,7 @@ ui_view.generate_view("mech_view_[REF(src)]") RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) spark_system = new spark_system.set_up(2, 0, src) @@ -799,6 +800,12 @@ remove_action_type_from_mob(/datum/action/vehicle/sealed/mecha/mech_toggle_lights, occupant) return COMPONENT_BLOCK_LIGHT_EATER +/obj/vehicle/sealed/mecha/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(mecha_flags &= HAS_LIGHTS && light_on) + set_light_on(FALSE) + return COMSIG_SABOTEUR_SUCCESS + /// Apply corresponding accesses /obj/vehicle/sealed/mecha/proc/update_access() req_access = one_access ? list() : accesses diff --git a/tgstation.dme b/tgstation.dme index 878c108bc2d..9acc080acf0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -337,7 +337,6 @@ #include "code\__DEFINES\dcs\signals\signals_radiation.dm" #include "code\__DEFINES\dcs\signals\signals_reagent.dm" #include "code\__DEFINES\dcs\signals\signals_restaurant.dm" -#include "code\__DEFINES\dcs\signals\signals_saboteur.dm" #include "code\__DEFINES\dcs\signals\signals_scangate.dm" #include "code\__DEFINES\dcs\signals\signals_screentips.dm" #include "code\__DEFINES\dcs\signals\signals_spatial_grid.dm" @@ -5490,12 +5489,12 @@ #include "code\modules\projectiles\projectile\special\floral.dm" #include "code\modules\projectiles\projectile\special\gravity.dm" #include "code\modules\projectiles\projectile\special\ion.dm" -#include "code\modules\projectiles\projectile\special\lightbreaker.dm" #include "code\modules\projectiles\projectile\special\meteor.dm" #include "code\modules\projectiles\projectile\special\mindflayer.dm" #include "code\modules\projectiles\projectile\special\neurotoxin.dm" #include "code\modules\projectiles\projectile\special\plasma.dm" #include "code\modules\projectiles\projectile\special\rocket.dm" +#include "code\modules\projectiles\projectile\special\saboteur.dm" #include "code\modules\projectiles\projectile\special\temperature.dm" #include "code\modules\projectiles\projectile\special\wormhole.dm" #include "code\modules\reagents\chem_splash.dm"