mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-31 07:58:22 +01:00
Port /datum/status_effect and convert wetness and fire stacks to it (#18180)
* Port /datum/status_effect system * Port fire stacks to status_effects * Fixes and adjustments to wetness * One last little thing * Fixes these compile errors A few things on the backend got updated...Adjusts them here. --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
co-authored by
Kashargul
Cameron Lennox
parent
bd8893cbdb
commit
a1322afa05
@@ -64,12 +64,12 @@ Bonus
|
||||
to_chat(M, span_warning(pick("You feel hot.", "You hear a crackling noise.", "You smell smoke.")))
|
||||
if(4)
|
||||
Firestacks_stage_4(M, A)
|
||||
M.IgniteMob()
|
||||
M.ignite_mob()
|
||||
to_chat(M, span_userdanger("Your skin bursts into flames!"))
|
||||
M.emote("scream")
|
||||
if(5)
|
||||
Firestacks_stage_5(M, A)
|
||||
M.IgniteMob()
|
||||
M.ignite_mob()
|
||||
if(M.stat != DEAD)
|
||||
to_chat(M, span_userdanger("Your skin erupts into an inferno!"))
|
||||
M.emote("scream")
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/// 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 = STATUS_EFFECT_PERMANENT
|
||||
/// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds.
|
||||
/// Note that this cannot be faster than the processing subsystem you choose to fire the effect on. (See: [var/processing_speed])
|
||||
/// While processing, this becomes the world.time when the next tick will occur.
|
||||
/// -1 = will prevent ticks, and if duration is also unlimited (-1), stop processing wholesale.
|
||||
var/tick_interval = 1 SECONDS
|
||||
///If our tick intervals are set to be a dynamic value within a range, the lowerbound of said range
|
||||
var/tick_interval_lowerbound
|
||||
///If our tick intervals are set to be a dynamic value within a range, the upperbound of said range
|
||||
var/tick_interval_upperbound
|
||||
/// The mob affected by the status effect.
|
||||
VAR_FINAL/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
|
||||
/// 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 = /obj/screen/alert/status_effect
|
||||
/// The alert itself, created in [proc/on_creation] (if alert_type is specified).
|
||||
VAR_FINAL/obj/screen/alert/status_effect/linked_alert
|
||||
/// If TRUE, and we have an alert, we will show a duration on the alert
|
||||
var/show_duration = FALSE
|
||||
/// Used to define if the status effect should be using SSfastprocess or SSprocessing
|
||||
var/processing_speed = STATUS_EFFECT_FAST_PROCESS
|
||||
/// Do we self-terminate when a fullheal is called?
|
||||
var/remove_on_fullheal = FALSE
|
||||
/// If remove_on_fullheal is TRUE, what flag do we need to be removed?
|
||||
// var/heal_flag_necessary = HEAL_STATUS
|
||||
/// A particle effect, for things like embers - Should be set on update_particles()
|
||||
VAR_FINAL/obj/effect/abstract/particle_holder/particle_effect
|
||||
|
||||
/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)
|
||||
RegisterSignal(owner, COMSIG_LIVING_AHEAL, PROC_REF(remove_effect_on_heal))
|
||||
|
||||
if(duration == INFINITY)
|
||||
// we will optionally allow INFINITY, because i imagine it'll be convenient in some places,
|
||||
// but we'll still set it to -1 / STATUS_EFFECT_PERMANENT for proper unified handling
|
||||
duration = STATUS_EFFECT_PERMANENT
|
||||
if(duration != STATUS_EFFECT_PERMANENT)
|
||||
duration = world.time + duration
|
||||
if(tick_interval != STATUS_EFFECT_NO_TICK)
|
||||
tick_interval = world.time + tick_interval
|
||||
|
||||
if(alert_type)
|
||||
var/obj/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
|
||||
update_shown_duration()
|
||||
|
||||
if(duration > world.time || tick_interval > world.time) //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)
|
||||
if(STATUS_EFFECT_PRIORITY)
|
||||
START_PROCESSING(SSpriority_effects, src)
|
||||
|
||||
update_particles()
|
||||
SEND_SIGNAL(owner, COMSIG_LIVING_STATUS_APPLIED, 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(STATUS_EFFECT_PRIORITY)
|
||||
STOP_PROCESSING(SSpriority_effects, src)
|
||||
if(owner)
|
||||
linked_alert = null
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
on_remove()
|
||||
UnregisterSignal(owner, COMSIG_LIVING_AHEAL)
|
||||
SEND_SIGNAL(owner, COMSIG_LIVING_STATUS_REMOVED, src)
|
||||
owner = null
|
||||
if(particle_effect)
|
||||
QDEL_NULL(particle_effect)
|
||||
return ..()
|
||||
|
||||
/// Updates the status effect alert's maptext (if possible)
|
||||
/datum/status_effect/proc/update_shown_duration()
|
||||
PRIVATE_PROC(TRUE)
|
||||
if(!linked_alert || !show_duration)
|
||||
return
|
||||
|
||||
linked_alert.maptext = MAPTEXT("<span style='text-align:center'>[round((duration - world.time)/10, 1)]s</span>")
|
||||
|
||||
// Status effect process. Handles adjusting its duration and ticks.
|
||||
// If you're adding processed effects, put them in [proc/tick]
|
||||
// instead of extending / overriding the process() proc.
|
||||
/datum/status_effect/process(seconds_per_tick)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
|
||||
if(QDELETED(owner))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(tick_interval == STATUS_EFFECT_AUTO_TICK)
|
||||
tick(seconds_per_tick)
|
||||
else if(tick_interval != STATUS_EFFECT_NO_TICK && tick_interval < world.time)
|
||||
var/tick_length = (tick_interval_upperbound && tick_interval_lowerbound) ? rand(tick_interval_lowerbound, tick_interval_upperbound) : initial(tick_interval)
|
||||
tick(tick_length / (1 SECONDS))
|
||||
tick_interval = world.time + tick_length
|
||||
|
||||
if(QDELING(src))
|
||||
// tick deleted us, no need to continue
|
||||
return
|
||||
|
||||
if(duration != STATUS_EFFECT_PERMANENT)
|
||||
if(duration < world.time)
|
||||
qdel(src)
|
||||
return
|
||||
update_shown_duration()
|
||||
|
||||
/// 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
|
||||
|
||||
/// Gets and formats examine text associated with our status effect.
|
||||
/// Return 'null' to have no examine text appear (default behavior).
|
||||
/datum/status_effect/proc/get_examine_text()
|
||||
return null
|
||||
|
||||
/**
|
||||
* Called every tick from process().
|
||||
* This is only called of tick_interval is not -1.
|
||||
*
|
||||
* Note that every tick =/= every processing cycle.
|
||||
*
|
||||
* * seconds_between_ticks = This is how many SECONDS that elapse between ticks.
|
||||
* This is a constant value based upon the initial tick interval set on the status effect.
|
||||
* It is similar to seconds_per_tick, from processing itself, but adjusted to the status effect's tick interval.
|
||||
*/
|
||||
/datum/status_effect/proc/tick(seconds_between_ticks)
|
||||
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()
|
||||
linked_alert = null
|
||||
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 == STATUS_EFFECT_PERMANENT)
|
||||
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
|
||||
|
||||
/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL] to remove us on fullheal
|
||||
/datum/status_effect/proc/remove_effect_on_heal(datum/source) //, heal_flags)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!remove_on_fullheal)
|
||||
return
|
||||
|
||||
// if(!heal_flag_necessary || (heal_flags & heal_flag_necessary))
|
||||
qdel(src)
|
||||
|
||||
/// Remove [seconds] of duration from the status effect, qdeling / ending if we eclipse the current world time.
|
||||
/datum/status_effect/proc/remove_duration(seconds)
|
||||
if(duration == STATUS_EFFECT_PERMANENT) // Infinite duration
|
||||
return FALSE
|
||||
|
||||
duration -= seconds
|
||||
if(duration <= world.time)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
update_shown_duration()
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Updates the particles for the status effects
|
||||
* Should be handled by subtypes!
|
||||
*/
|
||||
/datum/status_effect/proc/update_particles()
|
||||
SHOULD_CALL_PARENT(FALSE)
|
||||
return
|
||||
|
||||
/datum/status_effect/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(var_name == NAMEOF(src, duration))
|
||||
if(var_value == INFINITY)
|
||||
duration = STATUS_EFFECT_PERMANENT
|
||||
update_shown_duration()
|
||||
|
||||
if(var_name == NAMEOF(src, show_duration))
|
||||
update_shown_duration()
|
||||
|
||||
/// Alert base type for status effect alerts
|
||||
/obj/screen/alert/status_effect
|
||||
name = "Curse of Mundanity"
|
||||
desc = "You don't feel any different..."
|
||||
// maptext_y = 2
|
||||
/// The status effect we're linked to
|
||||
var/datum/status_effect/attached_effect
|
||||
|
||||
/obj/screen/alert/status_effect/Destroy()
|
||||
attached_effect = null //Don't keep a ref now
|
||||
return ..()
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
// 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)
|
||||
if(!QDELETED(new_instance))
|
||||
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(arglist(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 its typepath
|
||||
*
|
||||
* Returns an instance of a status effect, or NULL if none were found.
|
||||
*/
|
||||
/mob/proc/has_status_effect(datum/status_effect/checked_effect)
|
||||
// Yes I'm being cringe and putting this on the mob level even though status effects only apply to the living level
|
||||
// There's quite a few places (namely examine and, bleh, cult code) where it's easier to not need to cast to living before checking
|
||||
// for an effect such as blindness
|
||||
return null
|
||||
|
||||
/mob/living/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
|
||||
|
||||
///Gets every status effect of an ID and returns all of them in a list, rather than the individual 'has_status_effect'
|
||||
/mob/living/proc/get_all_status_effect_of_id(datum/status_effect/checked_effect)
|
||||
RETURN_TYPE(/list/datum/status_effect)
|
||||
|
||||
var/list/all_effects_of_type = list()
|
||||
for(var/datum/status_effect/present_effect as anything in status_effects)
|
||||
if(present_effect.id == initial(checked_effect.id))
|
||||
all_effects_of_type += present_effect
|
||||
|
||||
return all_effects_of_type
|
||||
|
||||
/**
|
||||
* Checks if this mob has a status effect that shares the passed effect's ID
|
||||
* and has the passed sources are in its list of sources (ONLY works for grouped efects!)
|
||||
*
|
||||
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not its typepath
|
||||
*
|
||||
* Returns an instance of a status effect, or NULL if none were found.
|
||||
*/
|
||||
/mob/proc/has_status_effect_from_source(datum/status_effect/grouped/checked_effect, sources)
|
||||
// See [/mob/proc/has_status_effect] for reason behind having this on the mob level
|
||||
return null
|
||||
|
||||
/mob/living/has_status_effect_from_source(datum/status_effect/grouped/checked_effect, sources)
|
||||
RETURN_TYPE(/datum/status_effect)
|
||||
|
||||
if(!ispath(checked_effect))
|
||||
CRASH("has_status_effect_from_source passed with an improper status effect path.")
|
||||
|
||||
if(!islist(sources))
|
||||
sources = list(sources)
|
||||
|
||||
for(var/datum/status_effect/grouped/present_effect in status_effects)
|
||||
if(present_effect.id != initial(checked_effect.id))
|
||||
continue
|
||||
var/list/matching_sources = present_effect.sources & sources
|
||||
if(length(matching_sources))
|
||||
return present_effect
|
||||
|
||||
return null
|
||||
|
||||
/**
|
||||
* Returns 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 its typepath
|
||||
*
|
||||
* Returns a list
|
||||
*/
|
||||
/mob/proc/has_status_effect_list(datum/status_effect/checked_effect)
|
||||
// See [/mob/proc/has_status_effect] for reason behind having this on the mob level
|
||||
return null
|
||||
|
||||
/mob/living/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
|
||||
@@ -0,0 +1,388 @@
|
||||
/////////// BUBBER EDIT THIS WILL BE FORCED TO CONFLICT READ THIS
|
||||
/*
|
||||
RESET THIS FILE BACK TO TG ONCE YOU GET FISH INFUSION
|
||||
RESET THIS FILE BACK TO TG ONCE YOU GET FISH INFUSION
|
||||
RESET THIS FILE BACK TO TG ONCE YOU GET FISH INFUSION
|
||||
RESET THIS FILE BACK TO TG ONCE YOU GET FISH INFUSION
|
||||
|
||||
*/
|
||||
/datum/status_effect/fire_handler
|
||||
duration = STATUS_EFFECT_PERMANENT
|
||||
id = STATUS_EFFECT_ID_ABSTRACT
|
||||
alert_type = null
|
||||
status_type = STATUS_EFFECT_REFRESH //Custom code
|
||||
on_remove_on_mob_delete = TRUE
|
||||
tick_interval = 2 SECONDS
|
||||
processing_speed = STATUS_EFFECT_PRIORITY
|
||||
/// Current amount of stacks we have
|
||||
var/stacks
|
||||
/// Maximum of stacks that we could possibly get
|
||||
var/stack_limit = MAX_FIRE_STACKS
|
||||
/// What status effect types do we remove uppon being applied. These are just deleted without any deduction from our or their stacks when forced.
|
||||
var/list/enemy_types
|
||||
/// What status effect types do we merge into if they exist. Ignored when forced.
|
||||
var/list/merge_types
|
||||
/// What status effect types do we override if they exist. These are simply deleted when forced.
|
||||
var/list/override_types
|
||||
/// For how much firestacks does one our stack count
|
||||
var/stack_modifier = 1
|
||||
|
||||
/datum/status_effect/fire_handler/refresh(mob/living/new_owner, new_stacks, forced = FALSE)
|
||||
if(forced)
|
||||
set_stacks(new_stacks)
|
||||
else
|
||||
adjust_stacks(new_stacks)
|
||||
|
||||
/datum/status_effect/fire_handler/on_creation(mob/living/new_owner, new_stacks, forced = FALSE)
|
||||
. = ..()
|
||||
|
||||
if(isanimal(owner))
|
||||
qdel(src)
|
||||
return
|
||||
// if(isbasicmob(owner))
|
||||
// if(!check_basic_mob_immunity(owner))
|
||||
// qdel(src)
|
||||
// return
|
||||
|
||||
owner = new_owner
|
||||
set_stacks(new_stacks)
|
||||
|
||||
for(var/enemy_type in enemy_types)
|
||||
var/datum/status_effect/fire_handler/enemy_effect = owner.has_status_effect(enemy_type)
|
||||
if(enemy_effect)
|
||||
if(forced)
|
||||
qdel(enemy_effect)
|
||||
continue
|
||||
|
||||
var/cur_stacks = stacks
|
||||
adjust_stacks(-abs(enemy_effect.stacks * enemy_effect.stack_modifier / stack_modifier))
|
||||
enemy_effect.adjust_stacks(-abs(cur_stacks * stack_modifier / enemy_effect.stack_modifier))
|
||||
if(enemy_effect.stacks <= 0)
|
||||
qdel(enemy_effect)
|
||||
|
||||
if(stacks <= 0)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(!forced)
|
||||
var/list/merge_effects = list()
|
||||
for(var/merge_type in merge_types)
|
||||
var/datum/status_effect/fire_handler/merge_effect = owner.has_status_effect(merge_type)
|
||||
if(merge_effect)
|
||||
merge_effects += merge_effects
|
||||
|
||||
if(LAZYLEN(merge_effects))
|
||||
for(var/datum/status_effect/fire_handler/merge_effect in merge_effects)
|
||||
merge_effect.adjust_stacks(stacks * stack_modifier / merge_effect.stack_modifier / LAZYLEN(merge_effects))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
for(var/override_type in override_types)
|
||||
var/datum/status_effect/fire_handler/override_effect = owner.has_status_effect(override_type)
|
||||
if(override_effect)
|
||||
if(forced)
|
||||
qdel(override_effect)
|
||||
continue
|
||||
|
||||
adjust_stacks(override_effect.stacks)
|
||||
qdel(override_effect)
|
||||
|
||||
/**
|
||||
* Setter and adjuster procs for firestacks
|
||||
*
|
||||
* Arguments:
|
||||
* - new_stacks
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/proc/set_stacks(new_stacks)
|
||||
stacks = max(0, min(stack_limit, new_stacks))
|
||||
cache_stacks()
|
||||
|
||||
/datum/status_effect/fire_handler/proc/adjust_stacks(new_stacks)
|
||||
stacks = max(0, min(stack_limit, stacks + new_stacks))
|
||||
cache_stacks()
|
||||
|
||||
/// Checks if the applicable basic mob is immune to the status effect we're trying to apply. Returns TRUE if it is, FALSE if it isn't.
|
||||
// /datum/status_effect/fire_handler/proc/check_basic_mob_immunity(mob/living/basic/basic_owner)
|
||||
// return (basic_owner.basic_mob_flags & FLAMMABLE_MOB)
|
||||
|
||||
/**
|
||||
* Refresher for mob's fire_stacks
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/proc/cache_stacks()
|
||||
owner.fire_stacks = 0
|
||||
var/was_on_fire = owner.on_fire
|
||||
owner.on_fire = FALSE
|
||||
for(var/datum/status_effect/fire_handler/possible_fire in owner.status_effects)
|
||||
owner.fire_stacks += possible_fire.stacks * possible_fire.stack_modifier
|
||||
|
||||
if(!istype(possible_fire, /datum/status_effect/fire_handler/fire_stacks))
|
||||
continue
|
||||
|
||||
var/datum/status_effect/fire_handler/fire_stacks/our_fire = possible_fire
|
||||
if(our_fire.on_fire)
|
||||
owner.on_fire = TRUE
|
||||
|
||||
if(was_on_fire && !owner.on_fire)
|
||||
owner.clear_alert(ALERT_FIRE)
|
||||
else if(!was_on_fire && owner.on_fire)
|
||||
owner.throw_alert(ALERT_FIRE, /obj/screen/alert/fire)
|
||||
// owner.update_appearance(UPDATE_OVERLAYS)
|
||||
owner.update_fire()
|
||||
update_particles()
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks
|
||||
id = "fire_stacks" //fire_stacks and wet_stacks should have different IDs or else has_status_effect won't work
|
||||
remove_on_fullheal = TRUE
|
||||
|
||||
enemy_types = list(/datum/status_effect/fire_handler/wet_stacks)
|
||||
stack_modifier = 1
|
||||
|
||||
/// If we're on fire
|
||||
var/on_fire = FALSE
|
||||
/// Reference to the mob light emitter itself
|
||||
var/obj/effect/dummy/lighting_obj/moblight
|
||||
/// Type of mob light emitter we use when on fire
|
||||
var/moblight_type = /obj/effect/dummy/lighting_obj/moblight/fire
|
||||
/// Cached particle type
|
||||
var/cached_state
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/get_examine_text()
|
||||
if(owner.on_fire)
|
||||
return
|
||||
|
||||
var/datum/gender/T = GLOB.gender_datums[owner.get_visible_gender()]
|
||||
return "[T.He] [T.is] covered in something flammable."
|
||||
|
||||
// /datum/status_effect/fire_handler/fire_stacks/proc/owner_touched_sparks()
|
||||
// SIGNAL_HANDLER
|
||||
|
||||
// ignite()
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/on_creation(mob/living/new_owner, new_stacks, forced = FALSE)
|
||||
. = ..()
|
||||
// RegisterSignal(owner, COMSIG_ATOM_TOUCHED_SPARKS, PROC_REF(owner_touched_sparks))
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/on_remove()
|
||||
// UnregisterSignal(owner, COMSIG_ATOM_TOUCHED_SPARKS)
|
||||
if (cached_state)
|
||||
owner.remove_shared_particles(cached_state)
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/tick(seconds_between_ticks)
|
||||
if(stacks <= 0)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
if(!on_fire)
|
||||
return TRUE
|
||||
|
||||
var/decay_multiplier = 1 // HAS_TRAIT(owner, TRAIT_HUSK) ? 2 : 1 // husks decay twice as fast
|
||||
adjust_stacks(owner.fire_stack_decay_rate * decay_multiplier * seconds_between_ticks)
|
||||
|
||||
if(stacks <= 0)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
var/datum/gas_mixture/air = owner.loc.return_air()
|
||||
if(air.gas[GAS_O2] < 1)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
deal_damage(seconds_between_ticks)
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/update_particles()
|
||||
if (!on_fire)
|
||||
if (cached_state)
|
||||
owner.remove_shared_particles(cached_state)
|
||||
cached_state = null
|
||||
return
|
||||
|
||||
var/particle_type = /particles/embers/minor
|
||||
if(stacks > MOB_BIG_FIRE_STACK_THRESHOLD)
|
||||
particle_type = /particles/embers
|
||||
|
||||
if (cached_state == particle_type)
|
||||
return
|
||||
|
||||
if (cached_state)
|
||||
owner.remove_shared_particles(cached_state)
|
||||
owner.add_shared_particles(particle_type)
|
||||
cached_state = particle_type
|
||||
|
||||
/**
|
||||
* Proc that handles damage dealing and all special effects
|
||||
*
|
||||
* Arguments:
|
||||
* - seconds_between_ticks
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/proc/deal_damage(seconds_per_tick)
|
||||
owner.on_fire_stack(seconds_per_tick, src)
|
||||
|
||||
var/turf/location = get_turf(owner)
|
||||
location.hotspot_expose(700, 25 * seconds_per_tick, TRUE)
|
||||
|
||||
/**
|
||||
* Used to deal damage to humans and count their protection.
|
||||
*
|
||||
* Arguments:
|
||||
* - seconds_between_ticks
|
||||
* - no_protection: When set to TRUE, fire will ignore any possible fire protection
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/proc/harm_human(seconds_per_tick, no_protection = FALSE)
|
||||
var/mob/living/carbon/human/victim = owner
|
||||
var/thermal_protection = victim.get_heat_protection(stacks)
|
||||
|
||||
if(!no_protection)
|
||||
if(thermal_protection == 1) // IMMUNE
|
||||
return
|
||||
|
||||
var/fire_temp_add = (BODYTEMP_HEATING_MAX + (stacks + 15)) * (1 - thermal_protection)
|
||||
victim.bodytemperature += fire_temp_add
|
||||
|
||||
// var/mob/living/carbon/human/victim = owner
|
||||
// var/thermal_protection = victim.get_heat_protection(stacks)
|
||||
|
||||
// if(!no_protection)
|
||||
// if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
|
||||
// return
|
||||
// if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT)
|
||||
// victim.adjust_bodytemperature(5.5 * seconds_per_tick)
|
||||
// return
|
||||
|
||||
// var/amount_to_heat = (BODYTEMP_HEATING_MAX + (stacks * 12)) * 0.5 * seconds_per_tick
|
||||
// if(owner.bodytemperature > BODYTEMP_FIRE_TEMP_SOFTCAP)
|
||||
// // Apply dimishing returns upon temp beyond the soft cap
|
||||
// amount_to_heat = amount_to_heat ** (BODYTEMP_FIRE_TEMP_SOFTCAP / owner.bodytemperature)
|
||||
|
||||
// victim.adjust_bodytemperature(amount_to_heat)
|
||||
// if (!(HAS_TRAIT(victim, TRAIT_RESISTHEAT)))
|
||||
// victim.add_mood_event("on_fire", /datum/mood_event/on_fire)
|
||||
// victim.add_mob_memory(/datum/memory/was_burning)
|
||||
|
||||
/**
|
||||
* Handles mob ignition, should be the only way to set on_fire to TRUE
|
||||
*
|
||||
* Arguments:
|
||||
* - silent: When set to TRUE, no message is displayed
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/proc/ignite(silent = FALSE)
|
||||
if(HAS_TRAIT(owner, TRAIT_NOFIRE))
|
||||
return FALSE
|
||||
|
||||
on_fire = TRUE
|
||||
if(!silent)
|
||||
owner.visible_message(span_warning("[owner] catches fire!"), span_userdanger("You're set on fire!"))
|
||||
|
||||
if(moblight_type)
|
||||
if(moblight)
|
||||
qdel(moblight)
|
||||
moblight = new moblight_type(owner)
|
||||
|
||||
cache_stacks()
|
||||
SEND_SIGNAL(owner, COMSIG_LIVING_IGNITED, owner)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Handles mob extinguishing, should be the only way to set on_fire to FALSE
|
||||
*/
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/proc/extinguish()
|
||||
QDEL_NULL(moblight)
|
||||
on_fire = FALSE
|
||||
// owner.clear_mood_event("on_fire")
|
||||
SEND_SIGNAL(owner, COMSIG_LIVING_EXTINGUISHED, owner)
|
||||
cache_stacks()
|
||||
for(var/obj/item/equipped in (owner.get_equipped_items()))
|
||||
equipped.extinguish()
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/on_remove()
|
||||
if(on_fire)
|
||||
extinguish()
|
||||
set_stacks(0)
|
||||
owner.update_fire()
|
||||
// UnregisterSignal(owner, COMSIG_MOB_UPDATE_ICONS)
|
||||
// owner.update_appearance(UPDATE_OVERLAYS)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/on_apply()
|
||||
. = ..()
|
||||
RegisterSignal(owner, COMSIG_ATOM_EXTINGUISH, PROC_REF(extinguish))
|
||||
owner.update_fire()
|
||||
// add_fire_overlay(owner)
|
||||
// owner.update_appearance(UPDATE_OVERLAYS)
|
||||
|
||||
/datum/status_effect/fire_handler/fire_stacks/proc/add_fire_overlay(mob/living/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(stacks <= 0 || !on_fire)
|
||||
return
|
||||
|
||||
var/mutable_appearance/created_overlay = owner.get_fire_overlay(stacks, on_fire)
|
||||
if(isnull(created_overlay))
|
||||
return
|
||||
|
||||
source.overlays |= created_overlay
|
||||
|
||||
|
||||
// WET
|
||||
/datum/status_effect/fire_handler/wet_stacks
|
||||
id = "wet_stacks"
|
||||
|
||||
enemy_types = list(/datum/status_effect/fire_handler/fire_stacks)
|
||||
stack_modifier = -1
|
||||
/// If the mob has the TRAIT_SLIPPERY_WHEN_WET trait, the mob gets this component while it's wet
|
||||
var/datum/component/slippery/slipperiness
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/on_apply()
|
||||
. = ..()
|
||||
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_WET_FOR_LONGER), SIGNAL_REMOVETRAIT(TRAIT_WET_FOR_LONGER)), PROC_REF(update_wet_stack_modifier))
|
||||
update_wet_stack_modifier()
|
||||
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLIPPERY_WHEN_WET), PROC_REF(become_slippery))
|
||||
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_SLIPPERY_WHEN_WET), PROC_REF(no_longer_slippery))
|
||||
if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET))
|
||||
become_slippery()
|
||||
ADD_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id))
|
||||
owner.add_shared_particles(/particles/droplets)
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/on_remove()
|
||||
. = ..()
|
||||
REMOVE_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id))
|
||||
if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET))
|
||||
no_longer_slippery()
|
||||
owner.remove_shared_particles(/particles/droplets)
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/proc/update_wet_stack_modifier()
|
||||
SIGNAL_HANDLER
|
||||
stack_modifier = HAS_TRAIT(owner, TRAIT_WET_FOR_LONGER) ? -3.5 : -1
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/proc/become_slippery()
|
||||
SIGNAL_HANDLER
|
||||
// slipperiness = owner.AddComponent(/datum/component/slippery, 5 SECONDS, lube_flags = SLIPPERY_WHEN_LYING_DOWN|NO_SLIP_WHEN_WALKING|WEAK_SLIDE)
|
||||
ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/proc/no_longer_slippery()
|
||||
SIGNAL_HANDLER
|
||||
// QDEL_NULL(slipperiness)
|
||||
REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/get_examine_text()
|
||||
var/datum/gender/T = GLOB.gender_datums[owner.get_visible_gender()]
|
||||
return "[T.He] look[T.s] a little soaked."
|
||||
|
||||
/datum/status_effect/fire_handler/wet_stacks/tick(seconds_between_ticks)
|
||||
var/decay = HAS_TRAIT(owner, TRAIT_WET_FOR_LONGER) ? -0.035 : -0.5
|
||||
adjust_stacks(decay * seconds_between_ticks)
|
||||
if(stacks <= 0)
|
||||
qdel(src)
|
||||
|
||||
// /datum/status_effect/fire_handler/wet_stacks/check_basic_mob_immunity(mob/living/basic/basic_owner)
|
||||
// return !(basic_owner.basic_mob_flags & IMMUNE_TO_GETTING_WET)
|
||||
/// BUBBER EDIT END
|
||||
@@ -0,0 +1,47 @@
|
||||
/// Status effect from multiple sources, when all sources are removed, so is the effect
|
||||
/datum/status_effect/grouped
|
||||
id = STATUS_EFFECT_ID_ABSTRACT
|
||||
alert_type = null
|
||||
// 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, ...)
|
||||
//Get our supplied arguments, without new_owner
|
||||
var/list/new_source_args = args.Copy(2)
|
||||
|
||||
var/datum/status_effect/grouped/existing = new_owner.has_status_effect(type)
|
||||
if(existing)
|
||||
existing.sources |= source
|
||||
existing.source_added(arglist(new_source_args))
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
/* We are the original */
|
||||
|
||||
. = ..()
|
||||
if(.)
|
||||
sources |= source
|
||||
source_added(arglist(new_source_args))
|
||||
|
||||
/**
|
||||
* Called after a source is added to the status effect,
|
||||
* this includes the first source added after creation.
|
||||
*/
|
||||
/datum/status_effect/grouped/proc/source_added(source, ...)
|
||||
return
|
||||
|
||||
/**
|
||||
* Called after a source is removed from the status effect. \
|
||||
* `removing` will be TRUE if this is the last source, which means
|
||||
* the effect will be deleted.
|
||||
*/
|
||||
/datum/status_effect/grouped/proc/source_removed(source, removing)
|
||||
return
|
||||
|
||||
/datum/status_effect/grouped/before_remove(source)
|
||||
sources -= source
|
||||
var/was_last_source = !length(sources)
|
||||
source_removed(source, was_last_source)
|
||||
return was_last_source
|
||||
Reference in New Issue
Block a user