[Ready] Refactors stuns and status effects. (#17579)

* Fixes reviver runtime

* Confusion status effect

* Dizzy status effect

* Drowsiness status effect

* decaying -> transient

* Drunkenness status effect

* why use timer when SSfastprocessing work good

* stuns (mostly)

* weaken and immobalise

* stun/weaken times

* update_flags redundancies.

* Slowed()

* Silence + fixes transient decay

* Jittery

* sleeping

* Paralyze -> weaken

* Cult sluring

* paralyse

* Stammer

* slurring + projectile cleanups

* losebreath

* Hallucination

* forgor this

* eyeblurry

* eye blind

* Druggy

* affected didn't like my spacing

* review pass

* second review pass

* some cleanups

* documentation and signal framework

* confusion fix

* Fixes spec_stun

* rejuv fix

* removes a TODO

* conflicted myself

* fixes

* self review

* review

* removes TODOs

* adminfreeze

* TM fixes

* hallucination fix + others

* tones down alchol and runtime fixes

* confusion overlay suggestion

* more fixes

* runtime fix

* losebreath fix

* clamp => directional bounded sum

* steel review

* oops

Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>

* reduces the dizziness cycle rate

* borg hotfix

* sanctified decursening

Co-authored-by: mochi <1496804+dearmochi@users.noreply.github.com>
Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
This commit is contained in:
Charlie
2022-05-24 16:35:26 +01:00
committed by GitHub
parent b32fb92770
commit 0f7a8707ef
311 changed files with 2268 additions and 2155 deletions
+3 -3
View File
@@ -336,7 +336,7 @@
/datum/status_effect/speedlegs/tick()
if(owner.stat || owner.staminaloss >= 90 || owner.mind.changeling.chem_charges <= (stacks + 1) * 3)
to_chat(owner, "<span class='danger'>Our muscles relax without the energy to strengthen them.</span>")
owner.Weaken(3)
owner.Weaken(6 SECONDS)
owner.remove_status_effect(STATUS_EFFECT_SPEEDLEGS)
else
stacks++
@@ -352,10 +352,10 @@
/datum/status_effect/speedlegs/on_remove()
REMOVE_TRAIT(owner, TRAIT_GOTTAGOFAST, "changeling")
if(!owner.weakened)
if(!owner.IsWeakened())
to_chat(owner, "<span class='notice'>Our muscles relax.</span>")
if(stacks >= 7)
to_chat(owner, "<span class='danger'>We collapse in exhaustion.</span>")
owner.Weaken(3)
owner.Weaken(6 SECONDS)
owner.emote("gasp")
owner.mind.changeling.geneticdamage += stacks
+455
View File
@@ -148,3 +148,458 @@
/datum/status_effect/pacifism/on_remove()
REMOVE_TRAIT(owner, TRAIT_PACIFISM, id)
// start of `living` level status procs.
/**
* # Confusion
*
* Prevents moving straight, sometimes changing movement direction at random.
* Decays at a rate of 1 per second.
*/
/datum/status_effect/transient/confusion
id = "confusion"
var/image/overlay
/datum/status_effect/transient/confusion/tick()
. = ..()
if(!.)
return
if(!owner.stat) //add or remove the overlay if they are alive or unconscious/dead
add_overlay()
else if(overlay)
owner.cut_overlay(overlay)
overlay = null
/datum/status_effect/transient/confusion/proc/add_overlay()
if(overlay)
return
var/matrix/M = matrix()
M.Scale(0.6)
overlay = image('icons/effects/effects.dmi', "confusion", pixel_y = 20)
overlay.transform = M
owner.add_overlay(overlay)
/datum/status_effect/transient/confusion/on_remove()
owner.cut_overlay(overlay)
overlay = null
return ..()
/**
* # Dizziness
*
* Slightly offsets the client's screen randomly every tick.
* Decays at a rate of 1 per second, or 5 when resting.
*/
/datum/status_effect/transient/dizziness
id = "dizziness"
var/px_diff = 0
var/py_diff = 0
/datum/status_effect/transient/dizziness/on_remove()
if(owner.client)
// smoothly back to normal
animate(owner.client, 0.2 SECONDS, pixel_x = -px_diff, pixel_y = -py_diff, flags = ANIMATION_PARALLEL)
return ..()
/datum/status_effect/transient/dizziness/tick()
. = ..()
if(!.)
return
var/dir = sin(world.time)
px_diff = cos(world.time * 3) * min(strength * 0.2, 32) * dir
py_diff = sin(world.time * 3) * min(strength * 0.2, 32) * dir
owner.client?.pixel_x = px_diff
owner.client?.pixel_y = py_diff
/datum/status_effect/transient/dizziness/calc_decay()
return (-0.2 + (owner.resting ? -0.8 : 0)) SECONDS
/**
* # Drowsiness
*
* Slows down and causes eye blur, with a 5% chance of falling asleep for a short time.
* Decays at a rate of 1 per second, or 5 when resting.
*/
/datum/status_effect/transient/drowsiness
id = "drowsiness"
/datum/status_effect/transient/drowsiness/tick()
. = ..()
if(!.)
return
owner.EyeBlurry(4 SECONDS)
if(prob(5))
owner.AdjustSleeping(2 SECONDS)
owner.Paralyse(10 SECONDS)
/datum/status_effect/transient/drowsiness/calc_decay()
return (-0.2 + (owner.resting ? -0.8 : 0)) SECONDS
/**
* # Drukenness
*
* Causes a myriad of status effects and other afflictions the stronger it is.
* Decays at a rate of 1 per second if no alcohol remains inside.
*/
/datum/status_effect/transient/drunkenness
id = "drunkenness"
var/alert_thrown = FALSE
// the number of seconds of the status effect required for each effect to kick in.
#define THRESHOLD_SLUR 60 SECONDS
#define THRESHOLD_BRAWLING 60 SECONDS
#define THRESHOLD_CONFUSION 80 SECONDS
#define THRESHOLD_SPARK 100 SECONDS
#define THRESHOLD_VOMIT 120 SECONDS
#define THRESHOLD_BLUR 150 SECONDS
#define THRESHOLD_COLLAPSE 150 SECONDS
#define THRESHOLD_FAINT 180 SECONDS
#define THRESHOLD_BRAIN_DAMAGE 240 SECONDS
#define DRUNK_BRAWLING /datum/martial_art/drunk_brawling
/datum/status_effect/transient/drunkenness/on_remove()
if(alert_thrown)
alert_thrown = FALSE
owner.clear_alert("drunk")
owner.sound_environment_override = SOUND_ENVIRONMENT_NONE
return ..()
/datum/status_effect/transient/drunkenness/tick()
. = ..()
if(!.)
return
// Adjust actual drunkenness based on trait and organ presence
var/alcohol_resistance = 1
var/actual_strength = strength
var/datum/mind/M = owner.mind
var/is_ipc = ismachineperson(owner)
if(HAS_TRAIT(owner, TRAIT_ALCOHOL_TOLERANCE))
alcohol_resistance = 2
actual_strength /= alcohol_resistance
var/obj/item/organ/internal/liver/L
if(!is_ipc)
L = owner.get_int_organ(/obj/item/organ/internal/liver)
var/liver_multiplier = 5 // no liver? get shitfaced
if(L)
liver_multiplier = L.alcohol_intensity
actual_strength *= liver_multiplier
// THRESHOLD_SLUR (60 SECONDS)
if(actual_strength >= THRESHOLD_SLUR)
owner.Slur(actual_strength)
if(!alert_thrown)
alert_thrown = TRUE
owner.throw_alert("drunk", /obj/screen/alert/drunk)
owner.sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
// THRESHOLD_BRAWLING (60 SECONDS)
if(M)
if(actual_strength >= THRESHOLD_BRAWLING)
if(!istype(M.martial_art, DRUNK_BRAWLING))
var/datum/martial_art/MA = new
MA.teach(owner, TRUE)
else if(istype(M.martial_art, DRUNK_BRAWLING))
M.martial_art.remove(src)
// THRESHOLD_CONFUSION (80 SECONDS)
if(actual_strength >= THRESHOLD_CONFUSION && prob(3.3))
owner.AdjustConfused(6 SECONDS / alcohol_resistance, bound_lower = 2 SECONDS, bound_upper = 1 MINUTES)
owner.AdjustDizzy(6 SECONDS / alcohol_resistance, bound_lower = 2 SECONDS, bound_upper = 2 MINUTES)
// THRESHOLD_SPARK (100 SECONDS)
if(is_ipc && actual_strength >= THRESHOLD_SPARK && prob(2.5))
do_sparks(3, 1, owner)
// THRESHOLD_VOMIT (120 SECONDS)
if(!is_ipc && actual_strength >= THRESHOLD_VOMIT && prob(0.8))
owner.fakevomit()
// THRESHOLD_BLUR (150 SECONDS)
if(actual_strength >= THRESHOLD_BLUR)
owner.EyeBlurry(20 SECONDS / alcohol_resistance)
// THRESHOLD_COLLAPSE (150 SECONDS)
if(actual_strength >= THRESHOLD_COLLAPSE && prob(1))
owner.emote("collapse")
do_sparks(3, 1, src)
// THRESHOLD_FAINT (180 SECONDS)
if(actual_strength >= THRESHOLD_FAINT && prob(1))
owner.Paralyse(10 SECONDS / alcohol_resistance)
owner.Drowsy(60 SECONDS / alcohol_resistance)
if(L)
L.receive_damage(1, TRUE)
if(!is_ipc)
owner.adjustToxLoss(1)
// THRESHOLD_BRAIN_DAMAGE (240 SECONDS)
if(actual_strength >= THRESHOLD_BRAIN_DAMAGE && prob(1))
owner.adjustBrainLoss(1)
#undef THRESHOLD_SLUR
#undef THRESHOLD_BRAWLING
#undef THRESHOLD_CONFUSION
#undef THRESHOLD_SPARK
#undef THRESHOLD_VOMIT
#undef THRESHOLD_BLUR
#undef THRESHOLD_COLLAPSE
#undef THRESHOLD_FAINT
#undef THRESHOLD_BRAIN_DAMAGE
#undef DRUNK_BRAWLING
/datum/status_effect/transient/drunkenness/calc_decay()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
if(H.has_booze())
return 0
return -0.2 SECONDS
/datum/status_effect/transient/cult_slurring
id = "cult_slurring"
/datum/status_effect/incapacitating
tick_interval = 0
status_type = STATUS_EFFECT_REPLACE
alert_type = null
var/needs_update_stat = FALSE
/datum/status_effect/incapacitating/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
if(ishuman(new_owner))
var/mob/living/carbon/human/H = new_owner
set_duration = H.dna.species.spec_stun(H, set_duration)
duration = set_duration
if(!duration)
return FALSE
. = ..()
if(. && (needs_update_stat || issilicon(owner)))
owner.update_stat()
owner.update_canmove()
/datum/status_effect/incapacitating/on_remove()
if(needs_update_stat || issilicon(owner)) //silicons need stat updates in addition to normal canmove updates
owner.update_stat()
owner.update_canmove()
return ..()
//STUN - prevents movement and actions, victim stays standing
/datum/status_effect/incapacitating/stun
id = "stun"
//IMMOBILIZED - prevents movement, victim can still stand and act
/datum/status_effect/incapacitating/immobilized
id = "immobilized"
//WEAKENED - prevents movement and action, victim falls over
/datum/status_effect/incapacitating/weakened
id = "weakened"
//PARALYZED - prevents movement and action, victim falls over, victim cannot hear or see.
/datum/status_effect/incapacitating/paralyzed
id = "paralyzed"
needs_update_stat = TRUE
//SLEEPING - victim falls over, cannot act, cannot see or hear, heals under certain conditions.
/datum/status_effect/incapacitating/sleeping
id = "sleeping"
tick_interval = 2 SECONDS
needs_update_stat = TRUE
/datum/status_effect/incapacitating/sleeping/tick()
if(!iscarbon(owner))
return
var/mob/living/carbon/dreamer = owner
if(dreamer.mind?.has_antag_datum(/datum/antagonist/vampire))
if(istype(dreamer.loc, /obj/structure/closet/coffin))
dreamer.adjustBruteLoss(-1, FALSE)
dreamer.adjustFireLoss(-1, FALSE)
dreamer.adjustToxLoss(-1)
dreamer.handle_dreams()
dreamer.adjustStaminaLoss(-10)
var/comfort = 1
if(istype(dreamer.buckled, /obj/structure/bed))
var/obj/structure/bed/bed = dreamer.buckled
comfort += bed.comfort
for(var/obj/item/bedsheet/bedsheet in range(dreamer.loc,0))
if(bedsheet.loc != dreamer.loc) //bedsheets in your backpack/neck don't give you comfort
continue
comfort += bedsheet.comfort
break //Only count the first bedsheet
if(dreamer.get_drunkenness() > 0)
comfort += 1 //Aren't naps SO much better when drunk?
dreamer.AdjustDrunk(-0.4 SECONDS * comfort) //reduce drunkenness while sleeping.
if(comfort > 1 && prob(3))//You don't heal if you're just sleeping on the floor without a blanket.
dreamer.adjustBruteLoss(-1 * comfort, FALSE)
dreamer.adjustFireLoss(-1 * comfort)
if(prob(10) && dreamer.health && dreamer.health_hud_override != HEALTH_HUD_OVERRIDE_CRIT)
dreamer.emote("snore")
//SLOWED - slows down the victim for a duration and a given slowdown value.
/datum/status_effect/incapacitating/slowed
id = "slowed"
var/slowdown_value = 10 // defaults to this value if none is specified
/datum/status_effect/incapacitating/slowed/on_creation(mob/living/new_owner, set_duration, _slowdown_value)
. = ..()
if(isnum(_slowdown_value))
slowdown_value = _slowdown_value
/datum/status_effect/transient/silence
id = "silenced"
/datum/status_effect/transient/silence/on_apply()
. = ..()
ADD_TRAIT(owner, TRAIT_MUTE, id)
/datum/status_effect/transient/silence/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_MUTE, id)
/datum/status_effect/transient/jittery
id = "jittering"
/datum/status_effect/transient/jittery/on_apply()
. = ..()
owner.do_jitter_animation(strength / 20, 1)
/datum/status_effect/transient/jittery/tick()
. = ..()
if(!.)
return
owner.do_jitter_animation(strength / 20, 1)
/datum/status_effect/transient/jittery/calc_decay()
return (-0.2 + (owner.resting ? -0.8 : 0)) SECONDS
/datum/status_effect/transient/stammering
id = "stammer"
/datum/status_effect/transient/slurring
id = "slurring"
/datum/status_effect/transient/lose_breath
id = "lose_breath"
#define HALLUCINATE_COOLDOWN_MIN 20 SECONDS
#define HALLUCINATE_COOLDOWN_MAX 50 SECONDS
/// This is multiplied with [/mob/var/hallucination] to determine the final cooldown. A higher hallucination value means shorter cooldown.
#define HALLUCINATE_COOLDOWN_FACTOR 0.003
/// Percentage defining the chance at which an hallucination may spawn past the cooldown.
#define HALLUCINATE_CHANCE 8
// Severity weights, should sum up to 100!
#define HALLUCINATE_MINOR_WEIGHT 60
#define HALLUCINATE_MODERATE_WEIGHT 30
#define HALLUCINATE_MAJOR_WEIGHT 10
/datum/status_effect/transient/hallucination
id = "hallucination"
var/next_hallucination = 0
/datum/status_effect/transient/hallucination/tick()
. = ..()
if(!.)
return
if(next_hallucination > world.time)
return
next_hallucination = world.time + rand(HALLUCINATE_COOLDOWN_MIN, HALLUCINATE_COOLDOWN_MAX) / (strength * HALLUCINATE_COOLDOWN_FACTOR)
if(!prob(HALLUCINATE_CHANCE))
return
// Pick a severity
var/severity = HALLUCINATE_MINOR
switch(rand(100))
if(0 to HALLUCINATE_MINOR_WEIGHT)
severity = HALLUCINATE_MINOR
if((HALLUCINATE_MINOR_WEIGHT + 1) to (HALLUCINATE_MINOR_WEIGHT + HALLUCINATE_MODERATE_WEIGHT))
severity = HALLUCINATE_MODERATE
if((HALLUCINATE_MINOR_WEIGHT + HALLUCINATE_MODERATE_WEIGHT + 1) to 100)
severity = HALLUCINATE_MAJOR
hallucinate(pickweight(GLOB.hallucinations[severity]))
/**
* Spawns an hallucination for the mob.
*
* Arguments:
* * H - The type path of the hallucination to spawn.
*/
/datum/status_effect/transient/hallucination/proc/hallucinate(hallucination_type)
ASSERT(ispath(hallucination_type))
if(owner.ckey)
add_attack_logs(null, owner, "Received hallucination [hallucination_type]", ATKLOG_ALL)
return new hallucination_type(get_turf(owner), owner)
#undef HALLUCINATE_COOLDOWN_MIN
#undef HALLUCINATE_COOLDOWN_MAX
#undef HALLUCINATE_COOLDOWN_FACTOR
#undef HALLUCINATE_CHANCE
#undef HALLUCINATE_MINOR_WEIGHT
#undef HALLUCINATE_MODERATE_WEIGHT
#undef HALLUCINATE_MAJOR_WEIGHT
/datum/status_effect/transient/eye_blurry
id = "eye_blurry"
/datum/status_effect/transient/eye_blurry/on_apply()
owner.update_blurry_effects()
. = ..()
/datum/status_effect/transient/eye_blurry/on_remove()
owner.update_blurry_effects()
/datum/status_effect/transient/eye_blurry/calc_decay()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
if(isnull(H.dna.species.vision_organ)) //species has no eyes
return ..()
var/obj/item/organ/vision = H.get_int_organ(H.dna.species.vision_organ)
if(!vision || vision.is_bruised() || vision.is_broken()) // doesn't decay if you have damaged eyesight.
return 0
if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) // decays faster if you rest your eyes with a blindfold.
return -1 SECONDS
return ..() //default decay rate
/datum/status_effect/transient/blindness
id = "blindness"
/datum/status_effect/transient/blindness/on_apply()
. = ..()
owner.update_blind_effects()
/datum/status_effect/transient/blindness/on_remove()
owner.update_blind_effects()
/datum/status_effect/transient/blindness/calc_decay()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
if(HAS_TRAIT(owner, TRAIT_BLIND))
return 0
if(isnull(H.dna.species.vision_organ)) // species that have no eyes
return ..()
var/obj/item/organ/vision = H.get_int_organ(H.dna.species.vision_organ)
if(!vision || vision.is_broken() || vision.is_bruised()) //got no eyes or broken eyes
return 0
return ..() //default decay rate
/datum/status_effect/transient/drugged
id = "drugged"
/datum/status_effect/transient/drugged/on_apply()
. = ..()
owner.update_druggy_effects()
/datum/status_effect/transient/drugged/on_remove()
owner.update_druggy_effects()
+33 -1
View File
@@ -51,7 +51,7 @@
if(!owner)
qdel(src)
return
if(tick_interval < world.time)
if(tick_interval <= world.time)
tick()
tick_interval = world.time + initial(tick_interval)
if(duration != -1 && duration < world.time)
@@ -272,3 +272,35 @@
/datum/status_effect/grouped/before_remove(source)
sources -= source
return !length(sources)
/**
* # Transient Status Effect (basetype)
*
* A status effect that works off a (possibly decimal) counter before expiring, rather than a specified world.time.
* This allows for a more precise tweaking of status durations at runtime (e.g. paralysis).
*/
/datum/status_effect/transient
tick_interval = 0.2 SECONDS // SSfastprocess interval
alert_type = null
/// How much strength left before expiring? time in deciseconds.
var/strength = 0
/datum/status_effect/transient/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
strength = set_duration
. = ..()
/datum/status_effect/transient/tick()
if(QDELETED(src) || QDELETED(owner))
return FALSE
. = TRUE
strength += calc_decay()
if(strength <= 0)
qdel(src)
return FALSE
/**
* Returns how much strength should be adjusted per tick.
*/
/datum/status_effect/transient/proc/calc_decay()
return -0.2 SECONDS // 1 per second by default