diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index a7d2670747..317066f673 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -56,11 +56,12 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
//Movement Types
-#define GROUND (1<<0)
-#define FLYING (1<<1)
-#define VENTCRAWLING (1<<2)
-#define FLOATING (1<<3)
-#define UNSTOPPABLE (1<<4) //When moving, will Bump()/Cross()/Uncross() everything, but won't be stopped.
+#define GROUND (1<<0)
+#define FLYING (1<<1)
+#define VENTCRAWLING (1<<2)
+#define FLOATING (1<<3)
+#define UNSTOPPABLE (1<<4) //When moving, will Bump()/Cross()/Uncross() everything, but won't be stopped.
+#define CRAWLING (1<<5) //Applied if you're crawling around on the ground/resting.
//Fire and Acid stuff, for resistance_flags
#define LAVA_PROOF (1<<0)
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 6d52f4640c..f42948f4ae 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -46,7 +46,9 @@
#define STATUS_EFFECT_SLEEPING /datum/status_effect/incapacitating/sleeping //the affected is asleep
-#define STATUS_EFFECT_TASED /datum/status_effect/no_combat_mode/electrode/ //the affected has been tased, preventing fine muscle control
+#define STATUS_EFFECT_TASED_WEAK /datum/status_effect/electrode //not as crippling, just slows down
+
+#define STATUS_EFFECT_TASED /datum/status_effect/electrode/no_combat_mode //the affected has been tased, preventing fine muscle control
#define STATUS_EFFECT_PACIFY /datum/status_effect/pacify //the affected is pacified, preventing direct hostile actions
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 2e46046a7e..c694f27d64 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -33,7 +33,6 @@
if(owner.getStaminaLoss())
owner.adjustStaminaLoss(-0.3) //reduce stamina loss by 0.3 per tick, 6 per 2 seconds
-
//UNCONSCIOUS
/datum/status_effect/incapacitating/unconscious
id = "unconscious"
@@ -80,11 +79,11 @@
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
icon_state = "asleep"
-/datum/status_effect/no_combat_mode/
+/datum/status_effect/no_combat_mode
id = "no_combat_mode"
- blocks_combatmode = TRUE
alert_type = null
status_type = STATUS_EFFECT_REPLACE
+ blocks_combatmode = TRUE
/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
@@ -113,31 +112,54 @@
icon = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez"
-/datum/status_effect/no_combat_mode/electrode
+/datum/status_effect/electrode
id = "tased"
+ var/slowdown = 1.5
+ var/slowdown_priority = 50 //to make sure the stronger effect overrides
+ var/affect_crawl = FALSE
+ var/nextmove_modifier = 1
+ var/stamdmg_per_ds = 1 //a 20 duration would do 20 stamdmg, disablers do 24 or something
+ var/last_tick = 0 //fastprocess processing speed is a goddamn sham, don't trust it.
-/datum/status_effect/no_combat_mode/electrode/on_creation(mob/living/new_owner, set_duration)
+/datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration)) //TODO, figure out how to grab from subtype
duration = set_duration
. = ..()
+ last_tick = world.time
+ if(iscarbon(owner))
+ var/mob/living/carbon/C = owner
+ if(C.combatmode)
+ C.toggle_combat_mode(TRUE)
+ C.add_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]", TRUE, priority = slowdown_priority, override = TRUE, multiplicative_slowdown = slowdown, blacklisted_movetypes = affect_crawl? NONE : CRAWLING)
+
+/datum/status_effect/electrode/on_remove()
+ if(iscarbon(owner))
+ var/mob/living/carbon/C = owner
+ C.remove_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]")
+ . = ..()
+
+/datum/status_effect/electrode/tick()
+ var/diff = world.time - last_tick
+ if(owner)
+ owner.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
+ last_tick = world.time
+
+/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
+ return nextmove_modifier
+
+/datum/status_effect/electrode/no_combat_mode
+ id = "tased_strong"
+ slowdown = 8
+ slowdown_priority = 100
+ nextmove_modifier = 2
+ blocks_combatmode = TRUE
+
+/datum/status_effect/electrode/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
+ . = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
- C.add_movespeed_modifier(MOVESPEED_ID_TASED_STATUS, TRUE, override = TRUE, multiplicative_slowdown = 8)
-
-/datum/status_effect/no_combat_mode/electrode/on_remove()
- if(iscarbon(owner))
- var/mob/living/carbon/C = owner
- C.remove_movespeed_modifier(MOVESPEED_ID_TASED_STATUS)
- . = ..()
-
-/datum/status_effect/no_combat_mode/electrode/tick()
- if(owner)
- owner.adjustStaminaLoss(5) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
-
-/datum/status_effect/no_combat_mode/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
- return 2
//OTHER DEBUFFS
/datum/status_effect/his_wrath //does minor damage over time unless holding His Grace
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9c8b2808ce..8652a1ee9e 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1094,6 +1094,10 @@
fall(forced = 1)
canmove = !(ko || recoveringstam || pinned || IsStun() || IsFrozen() || chokehold || buckled || (!has_legs && !ignore_legs && !has_arms)) //Cit change - makes it plausible to move while resting, adds pinning and stamina crit
density = !lying
+ if(resting)
+ ENABLE_BITFIELD(movement_type, CRAWLING)
+ else
+ DISABLE_BITFIELD(movement_type, CRAWLING)
if(lying)
if(layer == initial(layer)) //to avoid special cases like hiding larvas.
layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs
diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm
index 7c2b62c02c..2abc472138 100644
--- a/code/modules/projectiles/ammunition/energy/stun.dm
+++ b/code/modules/projectiles/ammunition/energy/stun.dm
@@ -5,6 +5,10 @@
e_cost = 200
harmful = FALSE
+/obj/item/ammo_casing/energy/electrode/security
+ projectile_type = /obj/item/projectile/energy/electrode/security
+ e_cost = 100
+
/obj/item/ammo_casing/energy/electrode/spec
e_cost = 100
@@ -13,6 +17,7 @@
e_cost = 100
/obj/item/ammo_casing/energy/electrode/hos
+ projectile_type = /obj/item/projectile/energy/electrode/security/hos
e_cost = 200
/obj/item/ammo_casing/energy/electrode/old
@@ -27,4 +32,4 @@
click_cooldown_override = 3.5
/obj/item/ammo_casing/energy/disabler/secborg
- e_cost = 50
\ No newline at end of file
+ e_cost = 50
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index f285248326..37dac55768 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -236,7 +236,7 @@
return
/obj/item/gun/proc/on_cooldown()
- return busy_action || firing || (last_fire + fire_delay > world.time)
+ return busy_action || firing || ((last_fire + fire_delay) > world.time)
/obj/item/gun/proc/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
add_fingerprint(user)
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index 163deae0ed..efe107a3f8 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -97,7 +97,7 @@
pin = null
can_charge = 0
ammo_x_offset = 1
- ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
+ ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
selfcharge = EGUN_SELFCHARGE
var/fail_tick = 0
var/fail_chance = 0
@@ -113,18 +113,21 @@
..()
/obj/item/gun/energy/e_gun/nuclear/proc/failcheck()
- if(prob(fail_chance) && isliving(loc))
- var/mob/living/M = loc
+ if(prob(fail_chance))
switch(fail_tick)
if(0 to 200)
fail_tick += (2*(fail_chance))
- M.rad_act(400)
- to_chat(M, "Your [name] feels warmer.")
+ radiation_pulse(src, 50)
+ var/mob/M = (ismob(loc) && loc) || (ismob(loc.loc) && loc.loc) //thank you short circuiting. if you powergame and nest these guns deeply you get to suffer no-warning radiation death.
+ if(M)
+ to_chat(M, "Your [name] feels warmer.")
if(201 to INFINITY)
SSobj.processing.Remove(src)
- M.rad_act(800)
- crit_fail = 1
- to_chat(M, "Your [name]'s reactor overloads!")
+ radiation_pulse(src, 200)
+ crit_fail = TRUE
+ var/mob/M = (ismob(loc) && loc) || (ismob(loc.loc) && loc.loc)
+ if(M)
+ to_chat(M, "Your [name]'s reactor overloads!")
/obj/item/gun/energy/e_gun/nuclear/emp_act(severity)
. = ..()
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 55d11c52d1..d700db817c 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -18,10 +18,23 @@
/obj/item/gun/energy/e_gun/advtaser
name = "hybrid taser"
- desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams."
+ desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams. Right click in combat mode to fire a taser shot with a cooldown."
icon_state = "advtaser"
- ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/electrode)
+ ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/electrode/security = FALSE)
ammo_x_offset = 2
+ // Not enough guns have altfire systems like this yet for this to be a universal framework.
+ var/last_altfire = 0
+ var/altfire_delay = 15
+
+/obj/item/gun/energy/e_gun/advtaser/altafterattack(atom/target, mob/user, proximity_flag, params)
+ . = TRUE
+ if(last_altfire + altfire_delay > world.time)
+ return
+ var/current_index = current_firemode_index
+ set_firemode_to_type(/obj/item/ammo_casing/energy/electrode)
+ process_afterattack(target, user, proximity_flag, params)
+ set_firemode_index(current_index)
+ last_altfire = world.time
/obj/item/gun/energy/e_gun/advtaser/cyborg
name = "cyborg taser"
diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm
index 3de5d607a1..ec5eee7b6b 100644
--- a/code/modules/projectiles/guns/misc/beam_rifle.dm
+++ b/code/modules/projectiles/guns/misc/beam_rifle.dm
@@ -299,27 +299,17 @@
if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
return
process_aim()
- if(aiming_time_left <= aiming_time_fire_threshold && check_user())
+ if(aiming_time_left <= aiming_time_fire_threshold && check_user() && ((lastfire + delay) <= world.time))
sync_ammo()
- afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
+ do_fire(M.client.mouseObject, M, FALSE, M.client.mouseParams, M.zone_selected)
stop_aiming()
QDEL_LIST(current_tracers)
return ..()
-/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
- if(flag) //It's adjacent, is the user, or is on the user's person
- if(target in user.contents) //can't shoot stuff inside us.
- return
- if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack
- return
- if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
- return
- if(!passthrough && (aiming_time > aiming_time_fire_threshold))
- return
- if(lastfire > world.time + delay)
- return
- lastfire = world.time
+/obj/item/gun/energy/beam_rifle/do_fire(atom/target, mob/living/user, message = TRUE, params, zone_override = "", bonus_spread = 0)
. = ..()
+ if(.)
+ lastfire = world.time
stop_aiming()
/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
index 88e70154d1..3bb1b3794e 100644
--- a/code/modules/projectiles/projectile/energy/stun.dm
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -2,11 +2,11 @@
name = "electrode"
icon_state = "spark"
color = "#FFFF00"
- nodamage = 1
+ nodamage = TRUE
knockdown = 60
knockdown_stamoverride = 36
knockdown_stam_max = 50
- stutter = 5
+ stutter = 10
jitter = 20
hitsound = 'sound/weapons/taserhit.ogg'
range = 7
@@ -14,6 +14,7 @@
muzzle_type = /obj/effect/projectile/muzzle/stun
impact_type = /obj/effect/projectile/impact/stun
var/tase_duration = 50
+ var/strong_tase = TRUE
/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -26,10 +27,24 @@
C.IgniteMob()
if(C.dna && C.dna.check_mutation(HULK))
C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
- else if((C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE))
- C.apply_status_effect(STATUS_EFFECT_TASED, tase_duration)
+ else if(tase_duration && (C.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(C, TRAIT_STUNIMMUNE))
+ C.apply_status_effect(strong_tase? STATUS_EFFECT_TASED : STATUS_EFFECT_TASED_WEAK, tase_duration)
addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet
do_sparks(1, TRUE, src)
..()
+
+/obj/item/projectile/energy/electrode/security
+ tase_duration = 30
+ knockdown = 0
+ stamina = 10
+ knockdown_stamoverride = 0
+ knockdown_stam_max = 0
+ strong_tase = FALSE
+ range = 12
+
+/obj/item/projectile/energy/electrode/security/hos
+ knockdown = 100
+ knockdown_stamoverride = 30
+ knockdown_stam_max = null