/** * ## 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