mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-26 09:03:37 +00:00
* EVEN MORE HARDDEL FIXES (#60228) Fixes a ton of harddels, sourced from #59996 I think this brings us down to like, ~100 per round from ~200, with only like 20 of those being proper hell failures. I've seen harddel profiles below 1 second of total cost. Feeling good. See you on the other side Makes the cryopod control computer into a weakref, never trust bee code Converts brig door timer internal lists to weakrefs Fixes a harddel caused by qdeling a motion sensitive camera after it had left its source area, jesus christ why didn't we do this already holy shit Converts the radio implant ref held by the antenna mutation to weakrefs because it isn't reliably cleaned up, makes the radio implant actually qdel its fucking radio Removes the target var from the throwing datum, it does literally nothing and just exists to cause harddels, mostly for the singularity Fixes a cable harddel sourced from things that try to enter blueprints after smoothing, but before roundstart. IE, shuttles. Removes shuttles from the blueprints Fixes emmisive blockers being added post qdel Removes some manual ghosting from cryopods, I initially did this for harddel reasons, but I figured out a better fix for that. I'm now doing it because it's got this really strange logic for like "re-entering the game" that doesn't actually link to what the ghostize proc does. We should remove this at some point Fixes robot hud objects harddeling due to hanging refs Fixes buildmode related hanging refs, I'm coming for you admin team Fixes a few instances of trying to add the forensics component post qdel, hhhhhhhhhhh Fixes some split personality harddels/weirdness Replaces a use of disconnect_duct with an init qdel hint, I suspect there's more issues with duct harddels, I've seen some odd logs about ahhh the area_contents list, but we can worry about that later Makes teleporter targets into weakrefs, properly types them as /atom Makes frequency devices into weakrefs Makes cameras remove themselves from camera nets on Destroy Makes tgui ui datums implement destroy, this means if I ever see one hang a ref to user or whatever, I know there's an error with calling close() properly. I've seen this harddel once, but not after this change so I assume there was some error with close(). IDK maybe this is a papering over? Would have to ask @ stylemistake I've seen logs of beartraps being in world post del, putting a return there just in case. The same is true of nerf darts, but I haven't really looked into that yet Makes a shoe's ref to untying alerts a weakref, yes this is needed. Moves clearing client_in_contents to the Login of the new mob. This prevents doing things like ghosting someone before a mob qdel causing harddels Fixes a harddel set sourced from adding a status effect to a qdeleted thing. Is this an error? I'm honestly not sure. Converts bsa code to weakrefs Converts the partner var of heat exchangers to weakrefs Converts camera assemblies to weakrefs Fixes some dumb behavior with ammo casings and assuming you'll be on a turf post Destroy parent call Fixes? merger related harddels, you were never cleared from your own members list, so origin objects would end up making a new list, creating harddels. Potential input from @ ninjanomnom about the logic Chasms store a static list of "falling atoms", which only exists for chasms that go somewhere else. This list wasn't being cleared of qdeleted objects, which is what happens when you fall in most chasms. Fixes this, and converts the list to weakrefs. Fixes some runtimes in both sheet code, and the weather listener element. This is here because runtime spam made testing more of a pain, didn't think it needed its own pr Fixes colorful reagent harddels sourced from reagents that were qdel'd before roundstart. I'm only like 50% sure this actually got it, but the issue may have been solved by #60174, so eh Turns the nuke op antag datum's ref to the war button into a weakref Fixes some holopad code that was not nulling refs all the time Converts camera bugs to weakrefs, this was the result of the bug being "reworked" like 6 years back without taking the existing ref clearing into account. Whole item needs a redo, but this'll do for now. Ensures that the both pulling and pullee refs are cleared on Destroy The crew monitor held all users in a non clearing list, makes that list a weakref because I hate everything Oh and I removed all sources of gas_mixture qdeletion, I'm kinda unsure on this since it's not technically supported, but any harddels from it might? indicate something going wrong with like, gas passing logic. I'd like @ MrStonedOne's thoughts, since I trust him to call me an idiot if I'm wrong. <!-- Please add a short description of why you think these changes would benefit the game. If you can't justify it in words, it might not be worth adding. --> ## Why it's not good for the game I crashed sybil like 10 times to get this data, I'm gonna put it to good use. Don't think you're safe sybilites, I'm coming for you. * EVEN MORE HARDDEL FIXES Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
280 lines
11 KiB
Plaintext
280 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)
|