mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-21 16:46:50 +01:00
247 lines
6.3 KiB
Plaintext
247 lines
6.3 KiB
Plaintext
/**
|
|
* ## Status Effects
|
|
*
|
|
* permanent / temporary tickable effects applied to mobs that are able to track
|
|
* instance data.
|
|
*
|
|
* each effect potentially has its own amount of variable arguments that
|
|
* can be passed into apply_status_effect. they will be detailed per-file.
|
|
*
|
|
* todo: /datum/prototype/status_effect
|
|
* todo: /datum/prototype/status_effect/simple for normal ones.
|
|
*/
|
|
/datum/status_effect
|
|
abstract_type = /datum/status_effect
|
|
/// unique identifier
|
|
var/identifier = "effect"
|
|
/// duration in deciseconds - 0 for permanent.
|
|
var/duration
|
|
/// expiration timerid
|
|
var/decay_timer
|
|
/// start time - when refreshing we just bump this to world.time or something.
|
|
var/started
|
|
/// deciseconds per tick. null for no ticking.
|
|
var/tick_interval
|
|
/// next world.time we should tick.
|
|
var/tick_next
|
|
/// path of screen alert thrown
|
|
var/alert_type
|
|
/// screen alert instance if it exists
|
|
var/atom/movable/screen/alert/status_effect/alert_linked
|
|
/// mob we're affecting
|
|
var/mob/owner
|
|
|
|
/datum/status_effect/New(mob/owner, duration, list/arguments)
|
|
ASSERT(isnull(owner.status_effects[identifier]))
|
|
src.owner = owner
|
|
owner.status_effects[identifier] = src
|
|
if(!isnull(duration))
|
|
src.duration = duration
|
|
started = world.time
|
|
on_apply(arglist(arguments))
|
|
rebuild_decay_timer()
|
|
|
|
/datum/status_effect/Destroy()
|
|
owner?.status_effects?.Remove(identifier)
|
|
on_remove()
|
|
owner = null
|
|
if(decay_timer)
|
|
deltimer(decay_timer)
|
|
decay_timer = null
|
|
return ..()
|
|
|
|
/**
|
|
* Called on tick
|
|
*
|
|
* @params
|
|
* * dt - seconds elapsed
|
|
*/
|
|
/datum/status_effect/proc/tick(dt)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
|
|
/**
|
|
* decays - this *must* either delete ourselves or automatically reapply the decay timer!
|
|
*/
|
|
/datum/status_effect/proc/decay()
|
|
qdel(src)
|
|
|
|
/**
|
|
* called on full removal
|
|
*/
|
|
/datum/status_effect/proc/on_remove()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(!isnull(tick_interval))
|
|
SSstatus_effects.ticking -= src
|
|
|
|
/**
|
|
* called after add
|
|
*
|
|
* @params
|
|
* * ... - rest of parameters from /mob/apply_status_effect()
|
|
*/
|
|
/datum/status_effect/proc/on_apply(...)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(!isnull(tick_interval))
|
|
SSstatus_effects.ticking += src
|
|
|
|
/**
|
|
* called on refresh
|
|
*
|
|
* @params
|
|
* * old_timeleft - old time remaining
|
|
* * ... - rest of parameters from /mob/apply_status_effect() or /datum/status_effect/refresh()
|
|
*/
|
|
/datum/status_effect/proc/on_refreshed(old_timeleft, ...)
|
|
return
|
|
|
|
/**
|
|
* refreshes our duration
|
|
*
|
|
* if duration is supplied and it isn't necessary to refresh, on_refreshed is not called.
|
|
*
|
|
* @params
|
|
* * duration - if supplied, refreshes us to that long from now (if we weren't already at or above). otherwise, refreshes us to what current duration was from now.
|
|
* * ... - rest of parameters from /mob/apply_status_effect(), passed to on_refreshed.
|
|
*
|
|
* @return were we refreshed?
|
|
*/
|
|
/datum/status_effect/proc/refresh(duration, ...)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
var/left = time_left()
|
|
if(isnull(duration))
|
|
duration = src.duration
|
|
else if(duration < left)
|
|
return FALSE
|
|
set_duration_from_now(duration)
|
|
var/list/built = args.Copy()
|
|
built[1] = left
|
|
on_refreshed(arglist(built))
|
|
return TRUE
|
|
|
|
/datum/status_effect/proc/time_left()
|
|
return (started + duration) - world.time
|
|
|
|
/datum/status_effect/proc/set_duration_from_apply(duration)
|
|
src.duration = duration
|
|
rebuild_decay_timer()
|
|
|
|
/datum/status_effect/proc/set_duration_from_now(duration)
|
|
src.duration = duration
|
|
started = world.time
|
|
rebuild_decay_timer()
|
|
|
|
/datum/status_effect/proc/adjust_duration(duration)
|
|
src.duration += duration
|
|
rebuild_decay_timer()
|
|
|
|
/datum/status_effect/proc/set_tick_interval(interval)
|
|
var/was_ticking = !isnull(tick_interval)
|
|
tick_interval = interval
|
|
if(was_ticking && isnull(interval))
|
|
SSstatus_effects.ticking -= src
|
|
else if(!was_ticking && !isnull(interval))
|
|
SSstatus_effects.ticking += src
|
|
|
|
/datum/status_effect/proc/rebuild_decay_timer()
|
|
if(decay_timer)
|
|
deltimer(decay_timer)
|
|
decay_timer = null
|
|
if(isnull(duration))
|
|
return
|
|
var/time_left = time_left()
|
|
if(time_left <= 0)
|
|
decay()
|
|
return
|
|
decay_timer = addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/status_effect, decay)), time_left, TIMER_STOPPABLE)
|
|
|
|
/datum/status_effect/vv_edit_var(var_name, var_value, mass_edit, raw_edit)
|
|
if(raw_edit)
|
|
return ..()
|
|
switch(var_name)
|
|
if(NAMEOF(src, tick_interval))
|
|
set_tick_interval(var_value)
|
|
. = TRUE
|
|
if(.)
|
|
datum_flags |= DF_VAR_EDITED
|
|
return
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
switch(var_name)
|
|
if(NAMEOF(src, duration))
|
|
rebuild_decay_timer()
|
|
|
|
/datum/status_effect/proc/on_examine(list/examine_list)
|
|
return
|
|
|
|
//? Mob procs
|
|
|
|
/**
|
|
* applies a status effect to this mob
|
|
*
|
|
* if the status effect is already there, we will refresh it instead.
|
|
*
|
|
* @params
|
|
* * path - path to effect
|
|
* * duration - (optional) duration of effect; defaults to what the effect specifies.
|
|
* * list/additional - all further args passed to effect.
|
|
*
|
|
* @return effect datum created / refreshed / whatever, null on failure
|
|
*/
|
|
/mob/proc/apply_status_effect(datum/status_effect/path, duration, list/additional)
|
|
if(isnull(status_effects))
|
|
status_effects = list()
|
|
var/id = initial(path.identifier)
|
|
if(status_effects[id])
|
|
var/datum/status_effect/existing = status_effects[id]
|
|
var/list/built = additional?.Copy() || list()
|
|
built.Insert(1, duration)
|
|
existing.refresh(arglist(built))
|
|
return existing
|
|
else
|
|
var/datum/status_effect/making = new path(src, duration, additional)
|
|
return making
|
|
|
|
/**
|
|
* remove a status effect
|
|
*
|
|
* * will remove grouped effects entirely.
|
|
*
|
|
* @params
|
|
* * path - path to effect
|
|
* * stacks - stacks to remove for stacking, default is all.
|
|
*
|
|
* @return stacks **left**. for single effects this is probably 0.
|
|
*/
|
|
/mob/proc/remove_status_effect(datum/status_effect/path, stacks = INFINITY, ...)
|
|
var/id = initial(path.identifier)
|
|
var/datum/status_effect/found = status_effects?[id]
|
|
if(isnull(found))
|
|
return 0
|
|
if(istype(found, /datum/status_effect/stacking))
|
|
var/datum/status_effect/stacking/stacking = found
|
|
. = min(stacking.stacks, stacks)
|
|
stacking.adjust_stacks(-stacks, FALSE)
|
|
else
|
|
. = 1
|
|
qdel(found)
|
|
|
|
/**
|
|
* checks if we have a status effect
|
|
*
|
|
* @params
|
|
* * path - path to effect
|
|
*
|
|
* @return effect datum found or null
|
|
*/
|
|
/mob/proc/has_status_effect(datum/status_effect/path)
|
|
var/id = initial(path.identifier)
|
|
return LAZYACCESS(status_effects, id)
|
|
|
|
//? Alert object
|
|
|
|
/atom/movable/screen/alert/status_effect
|
|
name = "Curse of Mundanity"
|
|
desc = "You don't feel any different..."
|
|
var/datum/status_effect/attached_effect
|