From e45f17efae33cc7c492e90f0eb96a85f36355fd7 Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Tue, 12 Sep 2023 22:57:44 +0200
Subject: [PATCH] [MIRROR] John Splintercell: a gun that is only good for
shooting out lights [MDB IGNORE] (#23650)
* John Splintercell: a gun that is only good for shooting out lights
* Removes this flashlight nonsense, we don't need copy pasted procs for something this simple
---------
Co-authored-by: Hatterhat <31829017+Hatterhat@users.noreply.github.com>
Co-authored-by: Pinta <68373373+softcerv@users.noreply.github.com>
Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com>
---
.../__DEFINES/dcs/signals/signals_saboteur.dm | 4 ++
code/datums/components/seclight_attachable.dm | 17 ++++---
code/game/machinery/camera/camera.dm | 14 ++++--
code/game/objects/items/devices/flashlight.dm | 44 +++++++++++-------
.../structures/crates_lockers/closets.dm | 2 +-
.../cargo/markets/market_items/weapons.dm | 10 ++++
.../mining/equipment/kinetic_crusher.dm | 5 ++
.../carbon/human/species_types/ethereal.dm | 11 +++++
.../modules/mob/living/silicon/robot/robot.dm | 8 ++++
code/modules/power/floodlight.dm | 9 +++-
code/modules/power/lighting/light.dm | 6 +++
.../projectiles/ammunition/energy/special.dm | 6 +++
code/modules/projectiles/gun.dm | 3 +-
.../projectiles/guns/energy/recharge.dm | 33 +++++++++++++
.../projectile/special/lightbreaker.dm | 34 ++++++++++++++
icons/obj/weapons/guns/energy.dmi | Bin 45742 -> 46351 bytes
.../modules/cell_component/code/flashlight.dm | 40 ++++++----------
tgstation.dme | 2 +
18 files changed, 191 insertions(+), 57 deletions(-)
create mode 100644 code/__DEFINES/dcs/signals/signals_saboteur.dm
create mode 100644 code/modules/projectiles/projectile/special/lightbreaker.dm
diff --git a/code/__DEFINES/dcs/signals/signals_saboteur.dm b/code/__DEFINES/dcs/signals/signals_saboteur.dm
new file mode 100644
index 00000000000..5ef536683fe
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_saboteur.dm
@@ -0,0 +1,4 @@
+// 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"
diff --git a/code/datums/components/seclight_attachable.dm b/code/datums/components/seclight_attachable.dm
index 56761cfd1bf..b3f36fe041a 100644
--- a/code/datums/components/seclight_attachable.dm
+++ b/code/datums/components/seclight_attachable.dm
@@ -98,6 +98,7 @@
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_parent_deleted))
+ RegisterSignal(parent, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/datum/component/seclite_attachable/UnregisterFromParent()
UnregisterSignal(parent, list(
@@ -156,12 +157,10 @@
if(!light)
return FALSE
- light.on = !light.on
- light.update_brightness()
- if(user)
- user.balloon_alert(user, "[light.name] toggled [light.on ? "on":"off"]")
-
- playsound(light, 'sound/weapons/empty.ogg', 100, TRUE)
+ var/successful_toggle = light.toggle_light(user)
+ if(!successful_toggle)
+ return TRUE
+ user.balloon_alert(user, "[light.name] toggled [light.on ? "on":"off"]")
update_light()
return TRUE
@@ -296,3 +295,9 @@
// Yes, this might mess with other icon state alterations,
// but that's the downside of using icon states over overlays.
source.icon_state = base_state
+
+/// Signal proc for [COMSIG_HIT_BY_SABOTEUR] that turns the light off for a few seconds.
+/datum/component/seclite_attachable/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ . = light.on_saboteur(source, disrupt_duration)
+ update_light()
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index a47115a9f60..575464a98af 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -30,7 +30,6 @@
var/busy = FALSE
var/emped = FALSE //Number of consecutive EMP's on this camera
var/in_use_lights = 0
-
// Upgrades bitflag
var/upgrades = 0
@@ -105,6 +104,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
alarm_manager = new(src)
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
+
/obj/machinery/camera/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
for(var/i in network)
network -= i
@@ -155,7 +156,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
if(!status && powered())
. += span_info("It can reactivated with wirecutters.")
-/obj/machinery/camera/emp_act(severity)
+/obj/machinery/camera/emp_act(severity, reset_time = 90 SECONDS)
. = ..()
if(!status)
return
@@ -168,7 +169,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
set_light(0)
emped = emped+1 //Increase the number of consecutive EMP's
update_appearance()
- addtimer(CALLBACK(src, PROC_REF(post_emp_reset), emped, network), 90 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(post_emp_reset), emped, network), reset_time)
for(var/i in GLOB.player_list)
var/mob/M = i
if (M.client?.eye == src)
@@ -176,6 +177,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
M.reset_perspective(null)
to_chat(M, span_warning("The screen bursts into static!"))
+/obj/machinery/camera/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ emp_act(EMP_LIGHT, reset_time = disrupt_duration)
+ return TRUE
+
/obj/machinery/camera/proc/post_emp_reset(thisemp, previous_network)
if(QDELETED(src))
return
@@ -188,7 +194,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
if(can_use())
GLOB.cameranet.addCamera(src)
emped = 0 //Resets the consecutive EMP count
- addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 100)
+ addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 10 SECONDS)
/obj/machinery/camera/ex_act(severity, target)
if(invuln)
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 14fc91ea7d0..ee7a66044fd 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -23,6 +23,8 @@
light_range = 4
light_power = 1
light_on = FALSE
+ /// If we've been forcibly disabled for a temporary amount of time.
+ COOLDOWN_DECLARE(disabled_time)
/// Can we toggle this light on and off (used for contexual screentips only)
var/toggle_context = TRUE
/// The sound the light makes when it's turned on
@@ -32,16 +34,15 @@
/// Is the light turned on or off currently
var/on = FALSE
-// SKYRAT EDIT REMOVAL BEGIN - MOVED TO MODUALR FLASHLIGHT.DM
-/*
/obj/item/flashlight/Initialize(mapload)
. = ..()
if(icon_state == "[initial(icon_state)]-on")
on = TRUE
update_brightness()
register_context()
-*/
-// SKYRAT EDIT REMOVAL END
+
+ if(toggle_context)
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/obj/item/flashlight/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
// single use lights can be toggled on once
@@ -68,19 +69,21 @@
if(light_system == STATIC_LIGHT)
update_light()
-// SKYRAT EDIT REMOVAL BEGIN - MOVED TO MODUALR FLASHLIGHT.DM
-/*
-/obj/item/flashlight/proc/toggle_light()
+/obj/item/flashlight/proc/toggle_light(mob/user)
+ var/disrupted = FALSE
+ //playsound(src, on ? sound_on : sound_off, 40, TRUE) SKYRAT EDIT REMOVAL - SOUND HANDLED IN MODULAR FLASHLIGHT.DM
on = !on
- playsound(src, on ? sound_on : sound_off, 40, TRUE)
+ if(!COOLDOWN_FINISHED(src, disabled_time))
+ if(user)
+ balloon_alert(user, "disrupted!")
+ on = FALSE
+ disrupted = TRUE
update_brightness()
update_item_action_buttons()
- return TRUE
+ return !disrupted
/obj/item/flashlight/attack_self(mob/user)
- toggle_light()
-*/
-// SKYRAT EDIT REMOVAL END
+ toggle_light(user)
/obj/item/flashlight/attack_hand_secondary(mob/user, list/modifiers)
attack_self(user)
@@ -262,6 +265,14 @@
if(istype(user) && dir != user.dir)
setDir(user.dir)
+/// when hit by a light disruptor - turns the light off, forces the light to be disabled for a few seconds
+/obj/item/flashlight/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ if(on)
+ toggle_light()
+ COOLDOWN_START(src, disabled_time, disrupt_duration)
+ return TRUE
+
/obj/item/flashlight/pen
name = "penlight"
desc = "A pen-sized light, used by medical staff. It can also be used to create a hologram to alert people of incoming medical assistance."
@@ -420,16 +431,15 @@
damtype = BURN
. = ..()
-/obj/item/flashlight/flare/turn_off() //SKYRAT EDIT CHANGE
- //on = FALSE SKYRAT EDIT REMOVAL
+/obj/item/flashlight/flare/proc/turn_off()
+ on = FALSE
name = initial(name)
attack_verb_continuous = initial(attack_verb_continuous)
attack_verb_simple = initial(attack_verb_simple)
hitsound = initial(hitsound)
force = initial(force)
damtype = initial(damtype)
- //update_brightness() - SKYRAT EDIT MOVED TO PARENT
- . = ..() //SKYRAT EDIT - MODULAR PARENT PROC
+ update_brightness()
/obj/item/flashlight/flare/extinguish()
. = ..()
@@ -749,11 +759,9 @@
turn_off()
STOP_PROCESSING(SSobj, src)
-/* SKYRAT EDIT REMOVAL
/obj/item/flashlight/glowstick/proc/turn_off()
on = FALSE
update_appearance(UPDATE_ICON)
-*/
/obj/item/flashlight/glowstick/update_appearance(updates=ALL)
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 1e05bae6298..de19ce19058 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -15,7 +15,7 @@
contents_pressure_protection = 0
/// How insulated the thing is, for the purposes of calculating body temperature. Must be between 0 and 1!
contents_thermal_insulation = 0
- pass_flags_self = LETPASSCLICKS
+ pass_flags_self = PASSSTRUCTURE | LETPASSCLICKS
/// The overlay for the closet's door
var/obj/effect/overlay/closet_door/door_obj
diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm
index f40e4fa1447..6115b1e1557 100644
--- a/code/modules/cargo/markets/market_items/weapons.dm
+++ b/code/modules/cargo/markets/market_items/weapons.dm
@@ -63,3 +63,13 @@
price_max = CARGO_CRATE_VALUE * 2
stock_max = 2
availability_prob = 50
+
+/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."
+ item = /obj/item/gun/energy/recharge/fisher
+
+ price_min = CARGO_CRATE_VALUE * 2
+ price_max = CARGO_CRATE_VALUE * 4
+ stock_max = 1
+ availability_prob = 50
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index a1edd69ba3e..8ae842807a8 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -38,6 +38,7 @@
)
//technically it's huge and bulky, but this provides an incentive to use it
AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=20)
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/obj/item/kinetic_crusher/Destroy()
QDEL_LIST(trophies)
@@ -165,6 +166,10 @@
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
update_appearance()
+/obj/item/kinetic_crusher/proc/on_saboteur(datum/source, disrupt_duration)
+ set_light_on(FALSE)
+ playsound(src, 'sound/weapons/empty.ogg', 100, TRUE)
+ return TRUE
/obj/item/kinetic_crusher/update_icon_state()
inhand_icon_state = "crusher[HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 9546a659e41..f087eb6b92c 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -68,6 +68,7 @@
RegisterSignal(ethereal, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag_act))
RegisterSignal(ethereal, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act))
RegisterSignal(ethereal, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
+ RegisterSignal(ethereal, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
ethereal_light = ethereal.mob_light(light_type = /obj/effect/dummy/lighting_obj/moblight/species)
spec_updatehealth(ethereal)
new_ethereal.set_safe_hunger_level()
@@ -84,6 +85,7 @@
UnregisterSignal(former_ethereal, COMSIG_ATOM_EMAG_ACT)
UnregisterSignal(former_ethereal, COMSIG_ATOM_EMP_ACT)
UnregisterSignal(former_ethereal, COMSIG_LIGHT_EATER_ACT)
+ UnregisterSignal(former_ethereal, COMSIG_HIT_BY_SABOTEUR)
QDEL_NULL(ethereal_light)
return ..()
@@ -147,6 +149,15 @@
if(EMP_HEAVY)
addtimer(CALLBACK(src, PROC_REF(stop_emp), H), 20 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //We're out for 20 seconds
+/datum/species/ethereal/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ var/mob/living/carbon/human/our_target = source
+ EMPeffect = TRUE
+ spec_updatehealth(our_target)
+ to_chat(our_target, span_warning("Something inside of you crackles in a bad way."))
+ our_target.take_bodypart_damage(burn = 3, wound_bonus = CANT_WOUND)
+ addtimer(CALLBACK(src, PROC_REF(stop_emp), our_target), disrupt_duration, TIMER_UNIQUE|TIMER_OVERRIDE)
+
/datum/species/ethereal/proc/on_emag_act(mob/living/carbon/human/H, mob/user)
SIGNAL_HANDLER
if(emageffect)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 9732222f0cf..17e59660eb4 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -19,6 +19,7 @@
AddElement(/datum/element/ridable, /datum/component/riding/creature/cyborg)
RegisterSignal(src, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge))
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
robot_modules_background = new()
robot_modules_background.icon_state = "block"
@@ -470,6 +471,13 @@
smash_headlamp()
return COMPONENT_BLOCK_LIGHT_EATER
+/// special handling for getting shot with a light disruptor/saboteur e.g. the fisher
+/mob/living/silicon/robot/proc/on_saboteur(datum/source, disrupt_duration)
+ 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."))
+ return TRUE
/**
* Handles headlamp smashing
diff --git a/code/modules/power/floodlight.dm b/code/modules/power/floodlight.dm
index 3ab8ece250f..0898e31735b 100644
--- a/code/modules/power/floodlight.dm
+++ b/code/modules/power/floodlight.dm
@@ -144,6 +144,7 @@
/obj/machinery/power/floodlight/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_OBJ_PAINTED, TYPE_PROC_REF(/obj/machinery/power/floodlight, on_color_change)) //update light color when color changes
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
register_context()
/obj/machinery/power/floodlight/proc/on_color_change(obj/machinery/power/flood_light, mob/user, obj/item/toy/crayon/spraycan/spraycan, is_dark_color)
@@ -289,6 +290,11 @@
/obj/machinery/power/floodlight/attack_ai(mob/user)
return attack_hand(user)
+/obj/machinery/power/floodlight/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ atom_break(ENERGY) // technically,
+ return TRUE
+
/obj/machinery/power/floodlight/atom_break(damage_flag)
. = ..()
if(!.)
@@ -297,7 +303,8 @@
var/obj/structure/floodlight_frame/floodlight_frame = new(loc)
floodlight_frame.state = FLOODLIGHT_NEEDS_LIGHTS
- new /obj/item/light/tube(loc)
+ var/obj/item/light/tube/our_light = new(loc)
+ our_light.shatter()
qdel(src)
diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm
index 2486a6500c1..ea6a28a8fe4 100644
--- a/code/modules/power/lighting/light.dm
+++ b/code/modules/power/lighting/light.dm
@@ -114,6 +114,7 @@
// Light projects out backwards from the dir of the light
set_light(l_dir = REVERSE_DIR(dir))
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
+ RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
AddElement(/datum/element/atmos_sensitive, mapload)
return INITIALIZE_HINT_LATELOAD
@@ -706,6 +707,11 @@
tube?.burn()
return
+/obj/machinery/light/proc/on_saboteur(datum/source, disrupt_duration)
+ SIGNAL_HANDLER
+ break_light_tube()
+ return TRUE
+
/obj/machinery/light/proc/grey_tide(datum/source, list/grey_tide_areas)
SIGNAL_HANDLER
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index bedfab7b235..24fba4b9ba4 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -74,3 +74,9 @@
select_name = "marksman nanoshot"
e_cost = 0
fire_sound = 'sound/weapons/gun/revolver/shot_alt.ogg'
+
+/obj/item/ammo_casing/energy/fisher
+ projectile_type = /obj/projectile/energy/fisher
+ select_name = "light-buster"
+ e_cost = 250
+ fire_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' // fwip fwip fwip fwip
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index ddacb419bbe..e76af741a18 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -26,6 +26,7 @@
var/vary_fire_sound = TRUE
var/fire_sound_volume = 50
var/dry_fire_sound = 'sound/weapons/gun/general/dry_fire.ogg'
+ var/dry_fire_sound_volume = 30
var/suppressed = null //whether or not a message is displayed when fired
var/can_suppress = FALSE
var/suppressed_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg'
@@ -169,7 +170,7 @@
/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj)
balloon_alert_to_viewers("*click*")
- playsound(src, dry_fire_sound, 30, TRUE)
+ playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE)
/obj/item/gun/proc/fire_sounds()
if(suppressed)
diff --git a/code/modules/projectiles/guns/energy/recharge.dm b/code/modules/projectiles/guns/energy/recharge.dm
index 6644accd8a5..eed27478755 100644
--- a/code/modules/projectiles/guns/energy/recharge.dm
+++ b/code/modules/projectiles/guns/energy/recharge.dm
@@ -135,3 +135,36 @@
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*2)
suppressed = null
ammo_type = list(/obj/item/ammo_casing/energy/bolt/large)
+
+/// A silly gun that does literally zero damage, but disrupts electrical sources of light, like flashlights.
+/obj/item/gun/energy/recharge/fisher
+ name = "\improper SC/FISHER disruptor"
+ desc = "A self-recharging, permanently suppressed, and very haphazardly modified accelerator handgun that does literally nothing to anything except light fixtures and cameras. \
+ Can fire twice before requiring a recharge, with bolts passing through machinery, but demands precision."
+ icon_state = "fisher"
+ base_icon_state = "fisher"
+ dry_fire_sound_volume = 10
+ w_class = WEIGHT_CLASS_SMALL
+ holds_charge = TRUE
+ suppressed = TRUE
+ recharge_time = 1.2 SECONDS
+ ammo_type = list(/obj/item/ammo_casing/energy/fisher)
+
+/obj/item/gun/energy/recharge/fisher/examine_more(mob/user)
+ . = ..()
+ . += span_notice("The SC/FISHER is an illegally-modified kinetic accelerator cut down and refit into a disassembled miniature energy gun chassis, with its pressure chamber \
+ attenuated to launch kinetic bolts that disrupt flashlights and cameras, if only temporarily. This effect also works on cyborg headlamps, and works longer in melee.
\
+ While some would argue that this is a really terrible design choice, others argue that it is very funny to be able to shoot at light sources. Caveat emptor.")
+
+/obj/item/gun/energy/recharge/fisher/afterattack(atom/target, mob/living/user, flag, params)
+ // you should just shoot them, but in case you can't/wont
+ . = ..()
+ if(user.Adjacent(target))
+ var/obj/projectile/energy/fisher/melee/simulated_hit = new
+ simulated_hit.on_hit(target)
+
+/obj/item/gun/energy/recharge/fisher/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ // ...you reeeeeally just shoot them, but in case you can't/won't
+ . = ..()
+ var/obj/projectile/energy/fisher/melee/simulated_hit = new
+ simulated_hit.on_hit(hit_atom)
diff --git a/code/modules/projectiles/projectile/special/lightbreaker.dm b/code/modules/projectiles/projectile/special/lightbreaker.dm
new file mode 100644
index 00000000000..cf273a1d3b2
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/lightbreaker.dm
@@ -0,0 +1,34 @@
+/obj/projectile/energy/fisher
+ name = "attenuated kinetic force"
+ alpha = 0
+ damage = 0
+ damage_type = BRUTE
+ armor_flag = BOMB
+ range = 14
+ projectile_phasing = PASSTABLE | PASSMOB | PASSMACHINE | PASSSTRUCTURE
+ hitscan = TRUE
+ var/disrupt_duration = 10 SECONDS
+
+/obj/projectile/energy/fisher/on_hit(atom/target, blocked, pierce_hit)
+ . = ..()
+ SEND_SIGNAL(target, COMSIG_HIT_BY_SABOTEUR, disrupt_duration)
+ if(!isliving(target))
+ return
+ var/list/things_to_disrupt = list()
+ var/lights_flickered = 0
+ 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))
+ 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/icons/obj/weapons/guns/energy.dmi b/icons/obj/weapons/guns/energy.dmi
index 07ac342a36d76c6099fe9871c5bd0d231a5da553..97b75335b91a0e1a8cdc5b1d6302999111abba7d 100644
GIT binary patch
delta 8484
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
diff --git a/modular_skyrat/modules/cell_component/code/flashlight.dm b/modular_skyrat/modules/cell_component/code/flashlight.dm
index 01de50266e3..6e585781a83 100644
--- a/modular_skyrat/modules/cell_component/code/flashlight.dm
+++ b/modular_skyrat/modules/cell_component/code/flashlight.dm
@@ -18,13 +18,10 @@
/obj/item/flashlight/Initialize(mapload)
. = ..()
- if(icon_state == "[initial(icon_state)]-on")
- turn_on(FALSE)
- update_brightness()
- register_context()
+ update_item_action_buttons()
if(uses_battery)
- AddComponent(/datum/component/cell, cell_override, CALLBACK(src, PROC_REF(turn_off)), _has_cell_overlays = FALSE)
+ AddComponent(/datum/component/cell, cell_override, CALLBACK(src, PROC_REF(quietly_turn_off)), _has_cell_overlays = FALSE)
/obj/item/flashlight/examine(mob/user)
. = ..()
@@ -55,46 +52,37 @@
set_light_power(initial(light_power))
to_chat(user, span_notice("You set [src] to low."))
-/obj/item/flashlight/proc/toggle_light()
+/obj/item/flashlight/toggle_light(noisy = TRUE)
+ . = ..()
if(on)
- turn_off()
- else
+ after_turn_on()
if(uses_battery && !(item_use_power(power_use_amount, TRUE) & COMPONENT_POWER_SUCCESS))
return
- turn_on(makes_noise_when_lit)
- playsound(src, on ? sound_on : sound_off, 40, TRUE)
- return TRUE
-
-/obj/item/flashlight/attack_self(mob/user)
- . = ..()
- toggle_light()
+ if(noisy)
+ playsound(src, on ? sound_on : sound_off, 40, TRUE)
/**
* Handles turning on the flashlight.
* Parameters:
* * noisy - Boolean on whether the flashlight should make an additional noise from being turned on or not. Defaults to TRUE.
*/
-/obj/item/flashlight/proc/turn_on(noisy = TRUE)
- on = TRUE
- if (uses_battery)
+/obj/item/flashlight/proc/after_turn_on(noisy = TRUE)
+ if(uses_battery)
START_PROCESSING(SSobj, src)
- update_brightness()
if(noisy)
playsound(src, 'modular_skyrat/master_files/sound/effects/flashlight.ogg', 40, TRUE) //Credits to ERIS for the sound
- update_item_action_buttons()
-/// Handles turning off the flashlight.
-/obj/item/flashlight/proc/turn_off()
- on = FALSE
- update_brightness()
- update_item_action_buttons()
+/// Quietly turns the flashlight off if it was on (called by the battery running out of charge).
+/obj/item/flashlight/proc/quietly_turn_off()
+ if(on)
+ toggle_light(noisy = FALSE)
/obj/item/flashlight/process(seconds_per_tick)
if(!on)
STOP_PROCESSING(SSobj, src)
return
if(uses_battery && !(item_use_power(power_use_amount) & COMPONENT_POWER_SUCCESS))
- turn_off()
+ quietly_turn_off()
/obj/item/flashlight/update_icon_state()
. = ..()
diff --git a/tgstation.dme b/tgstation.dme
index 194d81879f0..d1c22afce1e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -319,6 +319,7 @@
#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"
@@ -4991,6 +4992,7 @@
#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"