mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 00:29:02 +01:00
[MIRROR] Auto-docs + code improvements + splits up status_effect.dm [MDB IGNORE] (#12983)
* Auto-docs + code improvements + splits up status_effect.dm (#66362) * Code improvements for status effects in general * Does this for now * Throws in a qdeleted check * A return * comment tweak * Missed some ref()s * Wrong var * Comment clarifications * Some more comment clarifications * Auto-docs + code improvements + splits up status_effect.dm Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
This commit is contained in:
@@ -23,10 +23,9 @@
|
||||
/mob/living/changeNext_move(num)
|
||||
var/mod = next_move_modifier
|
||||
var/adj = next_move_adjust
|
||||
for(var/i in status_effects)
|
||||
var/datum/status_effect/S = i
|
||||
mod *= S.nextmove_modifier()
|
||||
adj += S.nextmove_adjust()
|
||||
for(var/datum/status_effect/effect as anything in status_effects)
|
||||
mod *= effect.nextmove_modifier()
|
||||
adj += effect.nextmove_adjust()
|
||||
next_move = world.time + ((num + adj)*mod)
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
add_memory_in_range(target, 7, MEMORY_GUNPOINT, list(DETAIL_PROTAGONIST = target, DETAIL_DEUTERAGONIST = shooter, DETAIL_WHAT_BY = weapon), story_value = STORY_VALUE_OKAY, memory_flags = MEMORY_CHECK_BLINDNESS)
|
||||
|
||||
shooter.apply_status_effect(/datum/status_effect/holdup, shooter)
|
||||
target.apply_status_effect(/datum/status_effect/grouped/heldup, shooter)
|
||||
target.apply_status_effect(/datum/status_effect/grouped/heldup, REF(shooter))
|
||||
|
||||
if(istype(weapon, /obj/item/gun/ballistic/rocketlauncher) && weapon.chambered)
|
||||
if(target.stat == CONSCIOUS && IS_NUKE_OP(shooter) && !IS_NUKE_OP(target) && (locate(/obj/item/disk/nuclear) in target.get_contents()) && shooter.client)
|
||||
@@ -58,7 +58,7 @@
|
||||
/datum/component/gunpoint/Destroy(force, silent)
|
||||
var/mob/living/shooter = parent
|
||||
shooter.remove_status_effect(/datum/status_effect/holdup)
|
||||
target.remove_status_effect(/datum/status_effect/grouped/heldup, shooter)
|
||||
target.remove_status_effect(/datum/status_effect/grouped/heldup, REF(shooter))
|
||||
SEND_SIGNAL(target, COMSIG_CLEAR_MOOD_EVENT, "gunpoint")
|
||||
return ..()
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
/datum/component/gunpoint/proc/async_trigger_reaction()
|
||||
var/mob/living/shooter = parent
|
||||
shooter.remove_status_effect(/datum/status_effect/holdup) // try doing these before the trigger gets pulled since the target (or shooter even) may not exist after pulling the trigger, dig?
|
||||
target.remove_status_effect(/datum/status_effect/grouped/heldup, shooter)
|
||||
target.remove_status_effect(/datum/status_effect/grouped/heldup, REF(shooter))
|
||||
SEND_SIGNAL(target, COMSIG_CLEAR_MOOD_EVENT, "gunpoint")
|
||||
|
||||
if(point_of_no_return)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/// Status effects are used to apply temporary or permanent effects to mobs.
|
||||
/// This file contains their code, plus code for applying and removing them.
|
||||
/datum/status_effect
|
||||
/// The ID of the effect. ID is used in adding and removing effects to check for duplicates, among other things.
|
||||
var/id = "effect"
|
||||
/// When set initially / in on_creation, this is how long the status effect lasts in deciseconds.
|
||||
/// While processing, this becomes the world.time when the status effect will expire.
|
||||
/// -1 = infinite duration.
|
||||
var/duration = -1
|
||||
/// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds.
|
||||
/// While processing, this becomes the world.time when the next tick will occur.
|
||||
/// -1 = will stop processing, if duration is also unlimited (-1).
|
||||
var/tick_interval = 1 SECONDS
|
||||
/// The mob affected by the status effect.
|
||||
var/mob/living/owner
|
||||
/// How many of the effect can be on one mob, and/or what happens when you try to add a duplicate.
|
||||
var/status_type = STATUS_EFFECT_UNIQUE
|
||||
/// If TRUE, we call [proc/on_remove] when owner is deleted. Otherwise, we call [proc/be_replaced].
|
||||
var/on_remove_on_mob_delete = FALSE
|
||||
/// If defined, this text will appear when the mob is examined
|
||||
/// To use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
|
||||
var/examine_text
|
||||
/// The typepath to the alert thrown by the status effect when created.
|
||||
/// Status effect "name"s and "description"s are shown to the owner here.
|
||||
var/alert_type = /atom/movable/screen/alert/status_effect
|
||||
/// The alert itself, created in [proc/on_creation] (if alert_type is specified).
|
||||
var/atom/movable/screen/alert/status_effect/linked_alert
|
||||
/// Used to define if the status effect should be using SSfastprocess or SSprocessing
|
||||
var/processing_speed = STATUS_EFFECT_FAST_PROCESS
|
||||
|
||||
/datum/status_effect/New(list/arguments)
|
||||
on_creation(arglist(arguments))
|
||||
|
||||
/// Called from New() with any supplied status effect arguments.
|
||||
/// Not guaranteed to exist by the end.
|
||||
/// Returning FALSE from on_apply will stop on_creation and self-delete the effect.
|
||||
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
|
||||
if(new_owner)
|
||||
owner = new_owner
|
||||
if(QDELETED(owner) || !on_apply())
|
||||
qdel(src)
|
||||
return
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
|
||||
if(duration != -1)
|
||||
duration = world.time + duration
|
||||
tick_interval = world.time + tick_interval
|
||||
|
||||
if(alert_type)
|
||||
var/atom/movable/screen/alert/status_effect/new_alert = owner.throw_alert(id, alert_type)
|
||||
new_alert.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = new_alert //so we can reference the alert, if we need to
|
||||
|
||||
if(duration > 0 || initial(tick_interval) > 0) //don't process if we don't care
|
||||
switch(processing_speed)
|
||||
if(STATUS_EFFECT_FAST_PROCESS)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
if (STATUS_EFFECT_NORMAL_PROCESS)
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/Destroy()
|
||||
switch(processing_speed)
|
||||
if(STATUS_EFFECT_FAST_PROCESS)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
if (STATUS_EFFECT_NORMAL_PROCESS)
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
if(owner)
|
||||
linked_alert = null
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
on_remove()
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
// Status effect process. Handles adjusting it's duration and ticks.
|
||||
// If you're adding processed effects, put them in [proc/tick]
|
||||
// instead of extending / overriding ththe process() proc.
|
||||
/datum/status_effect/process(delta_time)
|
||||
if(QDELETED(owner))
|
||||
qdel(src)
|
||||
return
|
||||
if(tick_interval < world.time)
|
||||
tick()
|
||||
tick_interval = world.time + initial(tick_interval)
|
||||
if(duration != -1 && duration < world.time)
|
||||
qdel(src)
|
||||
|
||||
/// Called whenever the effect is applied in on_created
|
||||
/// Returning FALSE will cause it to delete itself during creation instead.
|
||||
/datum/status_effect/proc/on_apply()
|
||||
return TRUE
|
||||
|
||||
/// Called every tick from process().
|
||||
/datum/status_effect/proc/tick()
|
||||
return
|
||||
|
||||
/// Called whenever the buff expires or is removed (qdeleted)
|
||||
/// Note that at the point this is called, it is out of the
|
||||
/// owner's status_effects list, but owner is not yet null
|
||||
/datum/status_effect/proc/on_remove()
|
||||
return
|
||||
|
||||
/// Called instead of on_remove when a status effect
|
||||
/// of status_type STATUS_EFFECT_REPLACE is replaced by itself,
|
||||
/// or when a status effect with on_remove_on_mob_delete
|
||||
/// set to FALSE has its mob deleted
|
||||
/datum/status_effect/proc/be_replaced()
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
owner = null
|
||||
qdel(src)
|
||||
|
||||
/// Called before being fully removed (before on_remove)
|
||||
/// Returning FALSE will cancel removal
|
||||
/datum/status_effect/proc/before_remove()
|
||||
return TRUE
|
||||
|
||||
/// Called when a status effect of status_type STATUS_EFFECT_REFRESH
|
||||
/// has its duration refreshed in apply_status_effect - is passed New() args
|
||||
/datum/status_effect/proc/refresh(effect, ...)
|
||||
var/original_duration = initial(duration)
|
||||
if(original_duration == -1)
|
||||
return
|
||||
duration = world.time + original_duration
|
||||
|
||||
/// Adds nextmove modifier multiplicatively to the owner while applied
|
||||
/datum/status_effect/proc/nextmove_modifier()
|
||||
return 1
|
||||
|
||||
/// Adds nextmove adjustment additiviely to the owner while applied
|
||||
/datum/status_effect/proc/nextmove_adjust()
|
||||
return 0
|
||||
|
||||
/// Alert base type for status effect alerts
|
||||
/atom/movable/screen/alert/status_effect
|
||||
name = "Curse of Mundanity"
|
||||
desc = "You don't feel any different..."
|
||||
/// The status effect we're linked to
|
||||
var/datum/status_effect/attached_effect
|
||||
|
||||
/atom/movable/screen/alert/status_effect/Destroy()
|
||||
attached_effect = null //Don't keep a ref now
|
||||
return ..()
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
// Status effect helpers for living mobs
|
||||
|
||||
/**
|
||||
* Applies a given status effect to this mob.
|
||||
*
|
||||
* new_effect - TYPEPATH of a status effect to apply.
|
||||
* Additional status effect arguments can be passed.
|
||||
*
|
||||
* Returns the instance of the created effected, if successful.
|
||||
* Returns 'null' if unsuccessful.
|
||||
*/
|
||||
/mob/living/proc/apply_status_effect(datum/status_effect/new_effect, ...)
|
||||
RETURN_TYPE(/datum/status_effect)
|
||||
|
||||
// The arguments we pass to the start effect. The 1st argument is this mob.
|
||||
var/list/arguments = args.Copy()
|
||||
arguments[1] = src
|
||||
|
||||
// If the status effect we're applying doesn't allow multiple effects, we need to handle it
|
||||
if(initial(new_effect.status_type) != STATUS_EFFECT_MULTIPLE)
|
||||
for(var/datum/status_effect/existing_effect as anything in status_effects)
|
||||
if(existing_effect.id != initial(new_effect.id))
|
||||
continue
|
||||
|
||||
switch(existing_effect.status_type)
|
||||
// Multiple are allowed, continue as normal. (Not normally reachable)
|
||||
if(STATUS_EFFECT_MULTIPLE)
|
||||
break
|
||||
// Only one is allowed of this type - early return
|
||||
if(STATUS_EFFECT_UNIQUE)
|
||||
return
|
||||
// Replace the existing instance (deletes it).
|
||||
if(STATUS_EFFECT_REPLACE)
|
||||
existing_effect.be_replaced()
|
||||
// Refresh the existing type, then early return
|
||||
if(STATUS_EFFECT_REFRESH)
|
||||
existing_effect.refresh(arglist(arguments))
|
||||
return
|
||||
|
||||
// Create the status effect with our mob + our arguments
|
||||
var/datum/status_effect/new_instance = new new_effect(arguments)
|
||||
return new_instance
|
||||
|
||||
/**
|
||||
* Removes all instances of a given status effect from this mob
|
||||
*
|
||||
* removed_effect - TYPEPATH of a status effect to remove.
|
||||
* Additional status effect arguments can be passed - these are passed into before_remove.
|
||||
*
|
||||
* Returns TRUE if at least one was removed.
|
||||
*/
|
||||
/mob/living/proc/remove_status_effect(datum/status_effect/removed_effect, ...)
|
||||
var/list/arguments = args.Copy(2)
|
||||
|
||||
. = FALSE
|
||||
for(var/datum/status_effect/existing_effect as anything in status_effects)
|
||||
if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arguments))
|
||||
qdel(existing_effect)
|
||||
. = TRUE
|
||||
|
||||
return .
|
||||
|
||||
/**
|
||||
* Checks if this mob has a status effect that shares the passed effect's ID
|
||||
*
|
||||
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
|
||||
*
|
||||
* Returns an instance of a status effect, or NULL if none were found.
|
||||
*/
|
||||
/mob/living/proc/has_status_effect(datum/status_effect/checked_effect)
|
||||
RETURN_TYPE(/datum/status_effect)
|
||||
|
||||
for(var/datum/status_effect/present_effect as anything in status_effects)
|
||||
if(present_effect.id == initial(checked_effect.id))
|
||||
return present_effect
|
||||
|
||||
return null
|
||||
|
||||
/**
|
||||
* Reteurns a list of all status effects that share the passed effect type's ID
|
||||
*
|
||||
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
|
||||
*
|
||||
* Returns a list
|
||||
*/
|
||||
/mob/living/proc/has_status_effect_list(datum/status_effect/checked_effect)
|
||||
RETURN_TYPE(/list)
|
||||
|
||||
var/list/effects_found = list()
|
||||
for(var/datum/status_effect/present_effect as anything in status_effects)
|
||||
if(present_effect.id == initial(checked_effect.id))
|
||||
effects_found += present_effect
|
||||
|
||||
return effects_found
|
||||
@@ -212,7 +212,6 @@
|
||||
/datum/status_effect/grouped/stasis
|
||||
id = "stasis"
|
||||
duration = -1
|
||||
tick_interval = 10
|
||||
alert_type = /atom/movable/screen/alert/status_effect/stasis
|
||||
var/last_dead_time
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// Status effect from multiple sources, when all sources are removed, so is the effect
|
||||
/datum/status_effect/grouped
|
||||
// Grouped effects adds itself to [var/sources] and destroys itself if one exists already, there are never actually multiple
|
||||
status_type = STATUS_EFFECT_MULTIPLE
|
||||
/// A list of all sources applying this status effect. Sources are a list of keys
|
||||
var/list/sources = list()
|
||||
|
||||
/datum/status_effect/grouped/on_creation(mob/living/new_owner, source)
|
||||
var/datum/status_effect/grouped/existing = new_owner.has_status_effect(type)
|
||||
if(existing)
|
||||
existing.sources |= source
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
sources |= source
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/grouped/before_remove(source)
|
||||
sources -= source
|
||||
return !length(sources)
|
||||
@@ -0,0 +1,20 @@
|
||||
/// These effects reapply their on_apply() effect when refreshed while stacks < max_stacks.
|
||||
/datum/status_effect/limited_buff
|
||||
id = "limited_buff"
|
||||
duration = -1
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
///How many stacks we currently have
|
||||
var/stacks = 1
|
||||
///How many stacks we can have maximum
|
||||
var/max_stacks = 3
|
||||
|
||||
/datum/status_effect/limited_buff/refresh(effect)
|
||||
if(stacks < max_stacks)
|
||||
on_apply()
|
||||
stacks++
|
||||
else
|
||||
maxed_out()
|
||||
|
||||
/// Called whenever the buff is refreshed when there are more stacks than max_stacks.
|
||||
/datum/status_effect/limited_buff/proc/maxed_out()
|
||||
return
|
||||
@@ -130,11 +130,11 @@
|
||||
icon_state = "aimed"
|
||||
|
||||
/datum/status_effect/grouped/heldup/on_apply()
|
||||
owner.apply_status_effect(/datum/status_effect/grouped/surrender, src)
|
||||
owner.apply_status_effect(/datum/status_effect/grouped/surrender, REF(src))
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/grouped/heldup/on_remove()
|
||||
owner.remove_status_effect(/datum/status_effect/grouped/surrender, src)
|
||||
owner.remove_status_effect(/datum/status_effect/grouped/surrender, REF(src))
|
||||
return ..()
|
||||
|
||||
// holdup is for the person aiming
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
/// Status effects that can stack.
|
||||
/datum/status_effect/stacking
|
||||
id = "stacking_base"
|
||||
duration = -1 // Only removed under specific conditions.
|
||||
tick_interval = 1 SECONDS // Deciseconds between decays, once decay starts
|
||||
alert_type = null
|
||||
/// How many stacks are currently accumulated.
|
||||
/// Also, the default stacks number given on application.
|
||||
var/stacks = 0
|
||||
// Deciseconds until ticks start occuring, which removes stacks
|
||||
/// (first stack will be removed at this time plus tick_interval)
|
||||
var/delay_before_decay
|
||||
/// How many stacks are lost per tick (decay trigger)
|
||||
var/stack_decay = 1
|
||||
/// The threshold for having special effects occur when a certain stack number is reached
|
||||
var/stack_threshold
|
||||
/// The maximum number of stacks that can be applied
|
||||
var/max_stacks
|
||||
/// If TRUE, the status effect is consumed / removed when stack_threshold is met
|
||||
var/consumed_on_threshold = TRUE
|
||||
/// Set to true once the stack_threshold is crossed, and false once it falls back below
|
||||
var/threshold_crossed = FALSE
|
||||
|
||||
/// Icon file for overlays applied when the status effect is applied
|
||||
var/overlay_file
|
||||
/// Icon file for underlays applied when the status effect is applied
|
||||
var/underlay_file
|
||||
/// Icon state for overlays applied when the status effect is applied
|
||||
/// States in the file must be given a name, followed by a number which corresponds to a number of stacks.
|
||||
/// Put the state name without the number in these state vars
|
||||
var/overlay_state
|
||||
/// Icon state for underlays applied when the status effect is applied
|
||||
/// The number is concatonated onto the string based on the number of stacks to get the correct state name.
|
||||
var/underlay_state
|
||||
/// A reference to our overlay appearance
|
||||
var/mutable_appearance/status_overlay
|
||||
/// A referenceto our underlay appearance
|
||||
var/mutable_appearance/status_underlay
|
||||
|
||||
/// Effects that occur when the stack count crosses stack_threshold
|
||||
/datum/status_effect/stacking/proc/threshold_cross_effect()
|
||||
return
|
||||
|
||||
/// Effects that occur if the status effect is removed due to the stack_threshold being crossed
|
||||
/datum/status_effect/stacking/proc/stacks_consumed_effect()
|
||||
return
|
||||
|
||||
/// Effects that occur if the status is removed due to being under 1 remaining stack
|
||||
/datum/status_effect/stacking/proc/fadeout_effect()
|
||||
return
|
||||
|
||||
/// Runs every time tick(), causes stacks to decay over time
|
||||
/datum/status_effect/stacking/proc/stack_decay_effect()
|
||||
return
|
||||
|
||||
/// Called when the stack_threshold is crossed (stacks go over the threshold)
|
||||
/datum/status_effect/stacking/proc/on_threshold_cross()
|
||||
threshold_cross_effect()
|
||||
if(consumed_on_threshold)
|
||||
stacks_consumed_effect()
|
||||
qdel(src)
|
||||
|
||||
/// Called when the stack_threshold is uncrossed / dropped (stacks go under the threshold after being over it)
|
||||
/datum/status_effect/stacking/proc/on_threshold_drop()
|
||||
return
|
||||
|
||||
/// Whether the owner can have the status effect.
|
||||
/// Return FALSE if the owner is not in a valid state (self-deletes the effect), or TRUE otherwise
|
||||
/datum/status_effect/stacking/proc/can_have_status()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/// Whether the owner can currently gain stacks or not
|
||||
/// Return FALSE if the owner is not in a valid state, or TRUE otherwise
|
||||
/datum/status_effect/stacking/proc/can_gain_stacks()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/datum/status_effect/stacking/tick()
|
||||
if(!can_have_status())
|
||||
qdel(src)
|
||||
else
|
||||
add_stacks(-stack_decay)
|
||||
stack_decay_effect()
|
||||
|
||||
/// Add (or remove) [stacks_added] stacks to our current stack count.
|
||||
/datum/status_effect/stacking/proc/add_stacks(stacks_added)
|
||||
if(stacks_added > 0 && !can_gain_stacks())
|
||||
return FALSE
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
stacks += stacks_added
|
||||
if(stacks > 0)
|
||||
if(stacks >= stack_threshold && !threshold_crossed) //threshold_crossed check prevents threshold effect from occuring if changing from above threshold to still above threshold
|
||||
threshold_crossed = TRUE
|
||||
on_threshold_cross()
|
||||
if(consumed_on_threshold)
|
||||
return
|
||||
else if(stacks < stack_threshold && threshold_crossed)
|
||||
threshold_crossed = FALSE //resets threshold effect if we fall below threshold so threshold effect can trigger again
|
||||
on_threshold_drop()
|
||||
if(stacks_added > 0)
|
||||
tick_interval += delay_before_decay //refreshes time until decay
|
||||
stacks = min(stacks, max_stacks)
|
||||
status_overlay.icon_state = "[overlay_state][stacks]"
|
||||
status_underlay.icon_state = "[underlay_state][stacks]"
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
else
|
||||
fadeout_effect()
|
||||
qdel(src) //deletes status if stacks fall under one
|
||||
|
||||
/datum/status_effect/stacking/on_creation(mob/living/new_owner, stacks_to_apply)
|
||||
. = ..()
|
||||
if(.)
|
||||
add_stacks(stacks_to_apply)
|
||||
|
||||
/datum/status_effect/stacking/on_apply()
|
||||
if(!can_have_status())
|
||||
return FALSE
|
||||
status_overlay = mutable_appearance(overlay_file, "[overlay_state][stacks]")
|
||||
status_underlay = mutable_appearance(underlay_file, "[underlay_state][stacks]")
|
||||
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
|
||||
var/icon_height = I.Height()
|
||||
status_overlay.pixel_x = -owner.pixel_x
|
||||
status_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
|
||||
status_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the status's overlay size based on the target's icon size
|
||||
status_underlay.pixel_x = -owner.pixel_x
|
||||
status_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
|
||||
status_underlay.alpha = 40
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stacking/Destroy()
|
||||
if(owner)
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
QDEL_NULL(status_overlay)
|
||||
return ..()
|
||||
@@ -1,299 +0,0 @@
|
||||
//Status effects are used to apply temporary or permanent effects to mobs. Mobs are aware of their status effects at all times.
|
||||
//This file contains their code, plus code for applying and removing them.
|
||||
//When making a new status effect, add a define to status_effects.dm in __DEFINES for ease of use!
|
||||
|
||||
/datum/status_effect
|
||||
var/id = "effect" //Used for screen alerts.
|
||||
var/duration = -1 //How long the status effect lasts in DECISECONDS. Enter -1 for an effect that never ends unless removed through some means.
|
||||
var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second. Setting this to -1 will stop processing if duration is also unlimited.
|
||||
var/mob/living/owner //The mob affected by the status effect.
|
||||
var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another
|
||||
var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
|
||||
var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
|
||||
var/alert_type = /atom/movable/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
|
||||
var/atom/movable/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
|
||||
///Processing speed - used to define if the status effect should be using SSfastprocess or SSprocessing
|
||||
var/processing_speed = STATUS_EFFECT_FAST_PROCESS
|
||||
|
||||
/datum/status_effect/New(list/arguments)
|
||||
on_creation(arglist(arguments))
|
||||
|
||||
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
|
||||
if(new_owner)
|
||||
owner = new_owner
|
||||
if(QDELETED(owner) || !on_apply())
|
||||
qdel(src)
|
||||
return
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
if(duration != -1)
|
||||
duration = world.time + duration
|
||||
tick_interval = world.time + tick_interval
|
||||
if(alert_type)
|
||||
var/atom/movable/screen/alert/status_effect/A = owner.throw_alert(id, alert_type)
|
||||
A.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = A //so we can reference the alert, if we need to
|
||||
if(duration > 0 || initial(tick_interval) > 0) //don't process if we don't care
|
||||
switch(processing_speed)
|
||||
if(STATUS_EFFECT_FAST_PROCESS)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
if (STATUS_EFFECT_NORMAL_PROCESS)
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/Destroy()
|
||||
switch(processing_speed)
|
||||
if(STATUS_EFFECT_FAST_PROCESS)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
if (STATUS_EFFECT_NORMAL_PROCESS)
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
if(owner)
|
||||
linked_alert = null
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
on_remove()
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/process()
|
||||
if(!owner)
|
||||
qdel(src)
|
||||
return
|
||||
if(tick_interval < world.time)
|
||||
tick()
|
||||
tick_interval = world.time + initial(tick_interval)
|
||||
if(duration != -1 && duration < world.time)
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself.
|
||||
return TRUE
|
||||
/datum/status_effect/proc/tick() //Called every tick.
|
||||
/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
|
||||
/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
owner = null
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/proc/before_remove() //! Called before being removed; returning FALSE will cancel removal
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/proc/refresh(effect, ...)
|
||||
var/original_duration = initial(duration)
|
||||
if(original_duration == -1)
|
||||
return
|
||||
duration = world.time + original_duration
|
||||
|
||||
//clickdelay/nextmove modifiers!
|
||||
/datum/status_effect/proc/nextmove_modifier()
|
||||
return 1
|
||||
|
||||
/datum/status_effect/proc/nextmove_adjust()
|
||||
return 0
|
||||
|
||||
////////////////
|
||||
// ALERT HOOK //
|
||||
////////////////
|
||||
|
||||
/atom/movable/screen/alert/status_effect
|
||||
name = "Curse of Mundanity"
|
||||
desc = "You don't feel any different..."
|
||||
var/datum/status_effect/attached_effect
|
||||
|
||||
/atom/movable/screen/alert/status_effect/Destroy()
|
||||
attached_effect = null //Don't keep a ref now
|
||||
return ..()
|
||||
|
||||
//////////////////
|
||||
// HELPER PROCS //
|
||||
//////////////////
|
||||
|
||||
/mob/living/proc/apply_status_effect(effect, ...) //applies a given status effect to this mob, returning the effect if it was successful
|
||||
. = FALSE
|
||||
var/datum/status_effect/S1 = effect
|
||||
LAZYINITLIST(status_effects)
|
||||
var/list/arguments = args.Copy()
|
||||
arguments[1] = src
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(S.id == initial(S1.id) && S.status_type)
|
||||
if(S.status_type == STATUS_EFFECT_REPLACE)
|
||||
S.be_replaced()
|
||||
else if(S.status_type == STATUS_EFFECT_REFRESH)
|
||||
S.refresh(arglist(arguments))
|
||||
return
|
||||
else
|
||||
return
|
||||
S1 = new effect(arguments)
|
||||
. = S1
|
||||
|
||||
/mob/living/proc/remove_status_effect(effect, ...) //removes all of a given status effect from this mob, returning TRUE if at least one was removed
|
||||
. = FALSE
|
||||
var/list/arguments = args.Copy(2)
|
||||
if(status_effects)
|
||||
var/datum/status_effect/S1 = effect
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(initial(S1.id) == S.id && S.before_remove(arguments))
|
||||
qdel(S)
|
||||
. = TRUE
|
||||
|
||||
/mob/living/proc/has_status_effect(effect) //returns the effect if the mob calling the proc owns the given status effect
|
||||
. = FALSE
|
||||
if(status_effects)
|
||||
var/datum/status_effect/S1 = effect
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(initial(S1.id) == S.id)
|
||||
return S
|
||||
|
||||
/mob/living/proc/has_status_effect_list(effect) //returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of
|
||||
. = list()
|
||||
if(status_effects)
|
||||
var/datum/status_effect/S1 = effect
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(initial(S1.id) == S.id)
|
||||
. += S
|
||||
|
||||
//////////////////////
|
||||
// STACKING EFFECTS //
|
||||
//////////////////////
|
||||
|
||||
/datum/status_effect/stacking
|
||||
id = "stacking_base"
|
||||
duration = -1 //removed under specific conditions
|
||||
alert_type = null
|
||||
var/stacks = 0 //how many stacks are accumulated, also is # of stacks that target will have when first applied
|
||||
var/delay_before_decay //deciseconds until ticks start occuring, which removes stacks (first stack will be removed at this time plus tick_interval)
|
||||
tick_interval = 10 //deciseconds between decays once decay starts
|
||||
var/stack_decay = 1 //how many stacks are lost per tick (decay trigger)
|
||||
var/stack_threshold //special effects trigger when stacks reach this amount
|
||||
var/max_stacks //stacks cannot exceed this amount
|
||||
var/consumed_on_threshold = TRUE //if status should be removed once threshold is crossed
|
||||
var/threshold_crossed = FALSE //set to true once the threshold is crossed, false once it falls back below
|
||||
var/overlay_file
|
||||
var/underlay_file
|
||||
var/overlay_state // states in .dmi must be given a name followed by a number which corresponds to a number of stacks. put the state name without the number in these state vars
|
||||
var/underlay_state // the number is concatonated onto the string based on the number of stacks to get the correct state name
|
||||
var/mutable_appearance/status_overlay
|
||||
var/mutable_appearance/status_underlay
|
||||
|
||||
/datum/status_effect/stacking/proc/threshold_cross_effect() //what happens when threshold is crossed
|
||||
|
||||
/datum/status_effect/stacking/proc/stacks_consumed_effect() //runs if status is deleted due to threshold being crossed
|
||||
|
||||
/datum/status_effect/stacking/proc/fadeout_effect() //runs if status is deleted due to being under one stack
|
||||
|
||||
/datum/status_effect/stacking/proc/stack_decay_effect() //runs every time tick() causes stacks to decay
|
||||
|
||||
/datum/status_effect/stacking/proc/on_threshold_cross()
|
||||
threshold_cross_effect()
|
||||
if(consumed_on_threshold)
|
||||
stacks_consumed_effect()
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/stacking/proc/on_threshold_drop()
|
||||
|
||||
/datum/status_effect/stacking/proc/can_have_status()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/datum/status_effect/stacking/proc/can_gain_stacks()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/datum/status_effect/stacking/tick()
|
||||
if(!can_have_status())
|
||||
qdel(src)
|
||||
else
|
||||
add_stacks(-stack_decay)
|
||||
stack_decay_effect()
|
||||
|
||||
/datum/status_effect/stacking/proc/add_stacks(stacks_added)
|
||||
if(stacks_added > 0 && !can_gain_stacks())
|
||||
return FALSE
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
stacks += stacks_added
|
||||
if(stacks > 0)
|
||||
if(stacks >= stack_threshold && !threshold_crossed) //threshold_crossed check prevents threshold effect from occuring if changing from above threshold to still above threshold
|
||||
threshold_crossed = TRUE
|
||||
on_threshold_cross()
|
||||
if(consumed_on_threshold)
|
||||
return
|
||||
else if(stacks < stack_threshold && threshold_crossed)
|
||||
threshold_crossed = FALSE //resets threshold effect if we fall below threshold so threshold effect can trigger again
|
||||
on_threshold_drop()
|
||||
if(stacks_added > 0)
|
||||
tick_interval += delay_before_decay //refreshes time until decay
|
||||
stacks = min(stacks, max_stacks)
|
||||
status_overlay.icon_state = "[overlay_state][stacks]"
|
||||
status_underlay.icon_state = "[underlay_state][stacks]"
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
else
|
||||
fadeout_effect()
|
||||
qdel(src) //deletes status if stacks fall under one
|
||||
|
||||
/datum/status_effect/stacking/on_creation(mob/living/new_owner, stacks_to_apply)
|
||||
. = ..()
|
||||
if(.)
|
||||
add_stacks(stacks_to_apply)
|
||||
|
||||
/datum/status_effect/stacking/on_apply()
|
||||
if(!can_have_status())
|
||||
return FALSE
|
||||
status_overlay = mutable_appearance(overlay_file, "[overlay_state][stacks]")
|
||||
status_underlay = mutable_appearance(underlay_file, "[underlay_state][stacks]")
|
||||
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
|
||||
var/icon_height = I.Height()
|
||||
status_overlay.pixel_x = -owner.pixel_x
|
||||
status_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
|
||||
status_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the status's overlay size based on the target's icon size
|
||||
status_underlay.pixel_x = -owner.pixel_x
|
||||
status_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
|
||||
status_underlay.alpha = 40
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stacking/Destroy()
|
||||
if(owner)
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
QDEL_NULL(status_overlay)
|
||||
return ..()
|
||||
|
||||
/// Status effect from multiple sources, when all sources are removed, so is the effect
|
||||
/datum/status_effect/grouped
|
||||
status_type = STATUS_EFFECT_MULTIPLE //! Adds itself to sources and destroys itself if one exists already, there are never multiple
|
||||
var/list/sources = list()
|
||||
|
||||
/datum/status_effect/grouped/on_creation(mob/living/new_owner, source)
|
||||
var/datum/status_effect/grouped/existing = new_owner.has_status_effect(type)
|
||||
if(existing)
|
||||
existing.sources |= source
|
||||
qdel(src)
|
||||
return FALSE
|
||||
else
|
||||
sources |= source
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/grouped/before_remove(source)
|
||||
sources -= source
|
||||
return !length(sources)
|
||||
|
||||
//These effects reapplies their on_apply() effect when refreshed while stacks < max_stacks
|
||||
/datum/status_effect/limited_buff
|
||||
id = "limited_buff"
|
||||
duration = -1
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
///How many stacks we currently have
|
||||
var/stacks = 1
|
||||
///How many stacks we can have maximum
|
||||
var/max_stacks = 3
|
||||
|
||||
/datum/status_effect/limited_buff/refresh(effect)
|
||||
if(stacks < max_stacks)
|
||||
on_apply()
|
||||
stacks++
|
||||
else
|
||||
maxed_out()
|
||||
|
||||
/datum/status_effect/limited_buff/proc/maxed_out()
|
||||
return
|
||||
@@ -25,13 +25,14 @@
|
||||
med_hud_set_status()
|
||||
|
||||
/mob/living/Destroy()
|
||||
if(LAZYLEN(status_effects))
|
||||
for(var/s in status_effects)
|
||||
var/datum/status_effect/S = s
|
||||
if(S.on_remove_on_mob_delete) //the status effect calls on_remove when its mob is deleted
|
||||
qdel(S)
|
||||
else
|
||||
S.be_replaced()
|
||||
for(var/datum/status_effect/effect as anything in status_effects)
|
||||
// The status effect calls on_remove when its mob is deleted
|
||||
if(effect.on_remove_on_mob_delete)
|
||||
qdel(effect)
|
||||
|
||||
else
|
||||
effect.be_replaced()
|
||||
|
||||
if(ranged_ability)
|
||||
ranged_ability.remove_ranged_ability(src)
|
||||
if(buckled)
|
||||
|
||||
+5
-1
@@ -1164,12 +1164,16 @@
|
||||
#include "code\datums\station_traits\negative_traits.dm"
|
||||
#include "code\datums\station_traits\neutral_traits.dm"
|
||||
#include "code\datums\station_traits\positive_traits.dm"
|
||||
#include "code\datums\status_effects\_status_effect.dm"
|
||||
#include "code\datums\status_effects\_status_effect_helpers.dm"
|
||||
#include "code\datums\status_effects\agent_pinpointer.dm"
|
||||
#include "code\datums\status_effects\buffs.dm"
|
||||
#include "code\datums\status_effects\drug_effects.dm"
|
||||
#include "code\datums\status_effects\gas.dm"
|
||||
#include "code\datums\status_effects\grouped_effect.dm"
|
||||
#include "code\datums\status_effects\limited_effect.dm"
|
||||
#include "code\datums\status_effects\neutral.dm"
|
||||
#include "code\datums\status_effects\status_effect.dm"
|
||||
#include "code\datums\status_effects\stacking_effect.dm"
|
||||
#include "code\datums\status_effects\wound_effects.dm"
|
||||
#include "code\datums\status_effects\debuffs\debuffs.dm"
|
||||
#include "code\datums\status_effects\debuffs\speech_debuffs.dm"
|
||||
|
||||
Reference in New Issue
Block a user