mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 05:30:05 +01:00
Refactors the worst list ever, Stun Absorptions, into status effects + makes status flags more accurate (making certain mobs more vulnerable to incapacitations?) (#76000)
## About The Pull Request - Refactors the stun absorption list into a status effect - Does a fair bit of cleanup around stun code Weird thing involved in this. Check out this define. `IS_STUN_IMMUNE(source, ignore_canstun) ((source.status_flags & GODMODE) || (!ignore_canstun && (!(source.status_flags & CANKNOCKDOWN) || HAS_TRAIT(source, TRAIT_STUNIMMUNE))))` Notice anything odd about it? It only checks for `CANKNOCKDOWN`. What does this mean? Well, *every single* one of the stun procs used this macro for checking stun immunity. Which means every method of stun checked the `CANKNOCKDOWN`. This means that, say you have a mob which has `CANSTUN` but not `CANKNOCKDOWN`. Intuitively this means that the mob cannot be knocked down, but can be stunned. But instead, this means the mob can't be stunned either. This doesn't affect humans, they have all the status flags, but it does affect some other mobs. Alien adults (not queens) have `CANUNCONSCIOUS|CANPUSH`. Before, they didn't have `CANKNOCKDOWN`, so they were fully immune to stuns and sleeps. But now, they can be knocked unconscious. However, overall it doesn't change much, as most mobs that flipped off `CANKNOCKDOWN` flipped off the others too. For consistency though it makes sense for these flags to work as they imply. - `incapacitate` didn't have a signal, now it does ## Why It's Good For The Game More consistent, better code? I may use this in the future. ## Changelog 🆑 Melbert refactor: Refactored Stun Absorptions (Bastard Sword, His Grace) refactor: Refactored Stun Immunity. Note this means that some mobs which, prior, were immune to all forms of incapacitation are now vulnerable to some. Notably, adult non-queen xenomorphs are now vulnerable to falling unconscious. /🆑
This commit is contained in:
@@ -22,10 +22,16 @@
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/his_grace/on_apply()
|
||||
owner.log_message("gained His Grace's stun immunity", LOG_ATTACK)
|
||||
owner.add_stun_absorption("hisgrace", INFINITY, 3, null, "His Grace protects you from the stun!")
|
||||
owner.add_stun_absorption(
|
||||
source = id,
|
||||
priority = 3,
|
||||
self_message = span_boldwarning("His Grace protects you from the stun!"),
|
||||
)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/his_grace/on_remove()
|
||||
owner.remove_stun_absorption(id)
|
||||
|
||||
/datum/status_effect/his_grace/tick()
|
||||
bloodlust = 0
|
||||
var/graces = 0
|
||||
@@ -45,11 +51,6 @@
|
||||
owner.adjustOxyLoss(-(grace_heal * 2))
|
||||
owner.adjustCloneLoss(-grace_heal)
|
||||
|
||||
/datum/status_effect/his_grace/on_remove()
|
||||
owner.log_message("lost His Grace's stun immunity", LOG_ATTACK)
|
||||
if(islist(owner.stun_absorption) && owner.stun_absorption["hisgrace"])
|
||||
owner.stun_absorption -= "hisgrace"
|
||||
|
||||
|
||||
/datum/status_effect/wish_granters_gift //Fully revives after ten seconds.
|
||||
id = "wish_granters_gift"
|
||||
@@ -112,34 +113,30 @@
|
||||
icon_state = "blooddrunk"
|
||||
|
||||
/datum/status_effect/blooddrunk/on_apply()
|
||||
. = ..()
|
||||
if(.)
|
||||
ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, BLOODDRUNK_TRAIT)
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.physiology.brute_mod *= 0.1
|
||||
H.physiology.burn_mod *= 0.1
|
||||
H.physiology.tox_mod *= 0.1
|
||||
H.physiology.oxy_mod *= 0.1
|
||||
H.physiology.clone_mod *= 0.1
|
||||
H.physiology.stamina_mod *= 0.1
|
||||
owner.log_message("gained blood-drunk stun immunity", LOG_ATTACK)
|
||||
owner.add_stun_absorption("blooddrunk", INFINITY, 4)
|
||||
owner.playsound_local(get_turf(owner), 'sound/effects/singlebeat.ogg', 40, 1, use_reverb = FALSE)
|
||||
ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, BLOODDRUNK_TRAIT)
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/human_owner = owner
|
||||
human_owner.physiology.brute_mod *= 0.1
|
||||
human_owner.physiology.burn_mod *= 0.1
|
||||
human_owner.physiology.tox_mod *= 0.1
|
||||
human_owner.physiology.oxy_mod *= 0.1
|
||||
human_owner.physiology.clone_mod *= 0.1
|
||||
human_owner.physiology.stamina_mod *= 0.1
|
||||
owner.add_stun_absorption(source = id, priority = 4)
|
||||
owner.playsound_local(get_turf(owner), 'sound/effects/singlebeat.ogg', 40, 1, use_reverb = FALSE)
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/blooddrunk/on_remove()
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.physiology.brute_mod *= 10
|
||||
H.physiology.burn_mod *= 10
|
||||
H.physiology.tox_mod *= 10
|
||||
H.physiology.oxy_mod *= 10
|
||||
H.physiology.clone_mod *= 10
|
||||
H.physiology.stamina_mod *= 10
|
||||
owner.log_message("lost blood-drunk stun immunity", LOG_ATTACK)
|
||||
REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, BLOODDRUNK_TRAIT);
|
||||
if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"])
|
||||
owner.stun_absorption -= "blooddrunk"
|
||||
var/mob/living/carbon/human/human_owner = owner
|
||||
human_owner.physiology.brute_mod *= 10
|
||||
human_owner.physiology.burn_mod *= 10
|
||||
human_owner.physiology.tox_mod *= 10
|
||||
human_owner.physiology.oxy_mod *= 10
|
||||
human_owner.physiology.clone_mod *= 10
|
||||
human_owner.physiology.stamina_mod *= 10
|
||||
REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, BLOODDRUNK_TRAIT)
|
||||
owner.remove_stun_absorption(id)
|
||||
|
||||
//Used by changelings to rapidly heal
|
||||
//Heals 10 brute and oxygen damage every second, and 5 fire
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* # Stun absorption
|
||||
*
|
||||
* A status effect effectively functions as [TRAIT_STUNIMMUNE], but with additional effects tied to it,
|
||||
* such as showing a message on trigger / examine, or only blocking a limited amount of stuns.
|
||||
*
|
||||
* Apply this via [/mob/living/proc/add_stun_absorption]. If you do not supply a duration,
|
||||
* remove this via [/mob/living/proc/remove_stun_absorption].
|
||||
*/
|
||||
/datum/status_effect/stun_absorption
|
||||
id = "absorb_stun"
|
||||
tick_interval = -1
|
||||
alert_type = null
|
||||
status_type = STATUS_EFFECT_MULTIPLE
|
||||
|
||||
/// The string key sourcer of the stun absorption, used for logging
|
||||
var/source
|
||||
/// The priority of the stun absorption. Used so that multiple sources will not trigger at once.
|
||||
/// This number is arbitrary but try to keep in sane / in line with other sources that exist.
|
||||
var/priority = -1
|
||||
/// How many total seconds of stuns that have been blocked.
|
||||
var/seconds_of_stuns_absorbed = 0 SECONDS
|
||||
/// The max number of seconds we can block before self-deleting.
|
||||
var/max_seconds_of_stuns_blocked = INFINITY
|
||||
/// The message shown via visible message to all nearby mobs when the effect triggers.
|
||||
var/shown_message
|
||||
/// The message shown to the owner when the effect triggers.
|
||||
var/self_message
|
||||
/// Message shown on anyone examining the owner.
|
||||
var/examine_message
|
||||
|
||||
/// Static list of all generic "stun received " signals that we will react to and block.
|
||||
/// These all have the same arguments sent, so we can handle them all via the same signal handler.
|
||||
/// Note though that we can register other signals to block effects outside of these if we want.
|
||||
var/static/list/incapacitation_effect_signals = list(
|
||||
COMSIG_LIVING_STATUS_IMMOBILIZE,
|
||||
COMSIG_LIVING_STATUS_INCAPACITATE,
|
||||
COMSIG_LIVING_STATUS_KNOCKDOWN,
|
||||
COMSIG_LIVING_STATUS_PARALYZE,
|
||||
COMSIG_LIVING_STATUS_STUN,
|
||||
)
|
||||
|
||||
/datum/status_effect/stun_absorption/on_creation(
|
||||
mob/living/new_owner,
|
||||
source,
|
||||
duration,
|
||||
priority = -1,
|
||||
shown_message,
|
||||
self_message,
|
||||
examine_message,
|
||||
max_seconds_of_stuns_blocked = INFINITY,
|
||||
)
|
||||
|
||||
if(isnum(duration))
|
||||
src.duration = duration
|
||||
|
||||
src.source = source
|
||||
src.priority = priority
|
||||
src.shown_message = shown_message
|
||||
src.self_message = self_message
|
||||
src.examine_message = examine_message
|
||||
src.max_seconds_of_stuns_blocked = max_seconds_of_stuns_blocked
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stun_absorption/on_apply()
|
||||
if(owner.mind || owner.client)
|
||||
owner.log_message("gained stun absorption (from: [source || "Unknown"])", LOG_ATTACK)
|
||||
|
||||
RegisterSignals(owner, incapacitation_effect_signals, PROC_REF(try_absorb_incapacitating_effect))
|
||||
RegisterSignal(owner, COMSIG_LIVING_GENERIC_STUN_CHECK, PROC_REF(try_absorb_generic_effect))
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/stun_absorption/on_remove()
|
||||
if(owner.mind || owner.client)
|
||||
owner.log_message("lost stun absorption (from: [source || "Unknown"])", LOG_ATTACK)
|
||||
|
||||
UnregisterSignal(owner, incapacitation_effect_signals)
|
||||
UnregisterSignal(owner, COMSIG_LIVING_GENERIC_STUN_CHECK)
|
||||
|
||||
/datum/status_effect/stun_absorption/get_examine_text()
|
||||
return replacetext(examine_message, "%EFFECT_OWNER_THEYRE", owner.p_theyre(TRUE))
|
||||
|
||||
/**
|
||||
* Signal proc for generic stun signals being sent, such as [COMSIG_LIVING_STATUS_STUN] or [COMSIG_LIVING_STATUS_KNOCKDOWN].
|
||||
*
|
||||
* When we get stunned, we will try to absorb a number of seconds from the stun, and return [COMPONENT_NO_STUN] if we succeed.
|
||||
*/
|
||||
/datum/status_effect/stun_absorption/proc/try_absorb_incapacitating_effect(mob/living/source, amount = 0, ignore_canstun = FALSE)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// we blocked a stun this tick that resulting is us qdeling, so stop
|
||||
if(QDELING(src))
|
||||
return NONE
|
||||
|
||||
// Amount less than (or equal to) zero is removing stuns, so we don't want to block that
|
||||
if(amount <= 0 || ignore_canstun)
|
||||
return NONE
|
||||
|
||||
if(!absorb_stun(amount))
|
||||
return NONE
|
||||
|
||||
return COMPONENT_NO_STUN
|
||||
|
||||
/**
|
||||
* Signal proc for [COMSIG_LIVING_GENERIC_STUN_CHECK]. (Note, this includes being stamcrit)
|
||||
*
|
||||
* Whenever a generic stun check is done against us, we'll just try to block it with "0 second" stun.
|
||||
* This prevents spam us from showing feedback messages, and is for the generic "can be stunned" check.
|
||||
*/
|
||||
/datum/status_effect/stun_absorption/proc/try_absorb_generic_effect(mob/living/source, check_flags)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(QDELING(src))
|
||||
return NONE
|
||||
|
||||
// "0 amount" / "0 seconds of stun" is used so no feedback is sent on success
|
||||
if(!absorb_stun(0))
|
||||
return NONE
|
||||
|
||||
return COMPONENT_NO_STUN
|
||||
|
||||
/**
|
||||
* Absorb a number of seconds of stuns.
|
||||
* If we hit the max amount of absorption, we will qdel ourself in this proc.
|
||||
*
|
||||
* * amount - this is the number of deciseconds being absorbed at once.
|
||||
*
|
||||
* Returns TRUE on successful absorption, or FALSE otherwise.
|
||||
*/
|
||||
/datum/status_effect/stun_absorption/proc/absorb_stun(amount)
|
||||
if(owner.stat != CONSCIOUS)
|
||||
return FALSE
|
||||
|
||||
// Now we gotta check that no other stun absorption we have is blocking us
|
||||
for(var/datum/status_effect/stun_absorption/similar_effect in owner.status_effects)
|
||||
if(similar_effect == src)
|
||||
continue
|
||||
// they blocked a stun this tick that resulted in them qdeling, so disregard
|
||||
if(QDELING(similar_effect))
|
||||
continue
|
||||
// if we have another stun absorption with higher priority,
|
||||
// don't do anything, let them handle it instead
|
||||
if(similar_effect.priority > priority)
|
||||
return FALSE
|
||||
|
||||
// At this point, a stun was successfully absorbed
|
||||
|
||||
// Only do effects if the amount was > 0 seconds
|
||||
if(amount > 0 SECONDS)
|
||||
// Show the message
|
||||
if(shown_message)
|
||||
// We do this replacement meme, instead of just setting it up in creation,
|
||||
// so that we respect indentity changes done while active
|
||||
var/really_shown_message = replacetext(shown_message, "%EFFECT_OWNER", "[owner]")
|
||||
owner.visible_message(really_shown_message, ignored_mobs = owner)
|
||||
|
||||
// Send the self message
|
||||
if(self_message)
|
||||
to_chat(owner, self_message)
|
||||
|
||||
// Count seconds absorbed
|
||||
seconds_of_stuns_absorbed += amount
|
||||
if(seconds_of_stuns_absorbed >= max_seconds_of_stuns_blocked)
|
||||
qdel(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* [proc/apply_status_effect] wrapper specifically for [/datum/status_effect/stun_absorption],
|
||||
* specifically so that it's easier to apply stun absorptions with named arguments.
|
||||
*
|
||||
* If the mob already has a stun absorption from the same source, will not re-apply the effect,
|
||||
* unless the new effect's priority is higher than the old effect's priority.
|
||||
*
|
||||
* Arguments
|
||||
* * source - the source of the stun absorption.
|
||||
* * duration - how long does the stun absorption last before it ends? -1 or null = infinite duration
|
||||
* * priority - what is this effect's priority to other stun absorptions? higher = more priority
|
||||
* * message - optional, "other message" arg of visible message, shown on trigger. Use %EFFECT_OWNER if you want the owner's name to be inserted.
|
||||
* * self_message - optional, "self message" arg of visible message, shown on trigger
|
||||
* * examine_message - optional, what is shown on examine of the mob.
|
||||
* * max_seconds_of_stuns_blocked - optional, how many seconds of stuns can it block before deleting? the stun that breaks over this number is still blocked, even if it is much higher.
|
||||
*
|
||||
* Returns an instance of a stun absorption effect, or NULL if failure
|
||||
*/
|
||||
/mob/living/proc/add_stun_absorption(
|
||||
source,
|
||||
duration,
|
||||
priority = -1,
|
||||
message,
|
||||
self_message,
|
||||
examine_message,
|
||||
max_seconds_of_stuns_blocked = INFINITY,
|
||||
)
|
||||
|
||||
// Handle duplicate sources
|
||||
for(var/datum/status_effect/stun_absorption/existing_effect in status_effects)
|
||||
if(existing_effect.source != source)
|
||||
continue
|
||||
|
||||
// If an existing effect's priority is greater or equal to our passed priority...
|
||||
if(existing_effect.priority >= priority)
|
||||
// don't bother re-applying the effect, and return
|
||||
return
|
||||
|
||||
// otherwise, delete existing and replace with new
|
||||
qdel(existing_effect)
|
||||
|
||||
return apply_status_effect(
|
||||
/datum/status_effect/stun_absorption,
|
||||
source,
|
||||
duration,
|
||||
priority,
|
||||
message,
|
||||
self_message,
|
||||
examine_message,
|
||||
max_seconds_of_stuns_blocked,
|
||||
)
|
||||
|
||||
/**
|
||||
* Removes all stub absorptions with the passed source.
|
||||
*
|
||||
* Returns TRUE if an effect was deleted, FALSE otherwise
|
||||
*/
|
||||
/mob/living/proc/remove_stun_absorption(source)
|
||||
. = FALSE
|
||||
for(var/datum/status_effect/stun_absorption/effect in status_effects)
|
||||
if(effect.source != source)
|
||||
continue
|
||||
|
||||
qdel(effect)
|
||||
. = TRUE
|
||||
|
||||
return .
|
||||
Reference in New Issue
Block a user