mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-11 10:11:09 +00:00
* Cytology Adjacent Update #1: New pet food, made from pets. Buffs health of selected simple mobs. (#61532) * doggy dog world * space begone * suggestions from review * removes frog related oopsie * indent fix * Cytology Adjacent Update #1: New pet food, made from pets. Buffs health of selected simple mobs. Co-authored-by: Krysonism <49783092+Krysonism@users.noreply.github.com>
300 lines
11 KiB
Plaintext
300 lines
11 KiB
Plaintext
//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
|