Skip to content

Commit

Permalink
Merge pull request #1163 from Absolucy/fix-fire
Browse files Browse the repository at this point in the history
[PORT] Fire improvements + fixups (and acid improvements)
  • Loading branch information
dwasint authored Feb 7, 2024
2 parents 18eeaa6 + 6fdf58f commit 573bfd5
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 115 deletions.
6 changes: 2 additions & 4 deletions code/__DEFINES/acid.dm
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// The acid power required to destroy most closed turfs.
#define ACID_POWER_MELT_TURF 200
/// The maximum amount of damage (per second) acid can deal to an [/obj].
#define OBJ_ACID_DAMAGE_MAX 300
#define MOVABLE_ACID_DAMAGE_MAX 300
/// Maximum acid volume that can be applied to an [/obj].
#define OBJ_ACID_VOLUME_MAX 300
#define MOVABLE_ACID_VOLUME_MAX 300
/// Maximum acid volume that can be applied to a [/mob/living].
#define MOB_ACID_VOLUME_MAX 1000
/// Maximum acid volume that can be applied to a [/turf].
Expand All @@ -15,7 +15,5 @@
/// The scaling factor for the acid decay rate.
#define ACID_DECAY_SCALING 1

/// The default icon state for the acid overlay. Not to be confused with the error icon state.
#define ACID_OVERLAY_DEFAULT "default"
/// The combined acid power and acid volume required to burn hands.
#define ACID_LEVEL_HANDBURN 20
4 changes: 2 additions & 2 deletions code/controllers/subsystem/processing/fire_burning.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// The subsystem used to tick [/datum/component/burning] instances.
PROCESSING_SUBSYSTEM_DEF(fire_burning)
name = "Fire Burning"
PROCESSING_SUBSYSTEM_DEF(burning)
name = "Burning"
priority = FIRE_PRIORITY_BURNING
flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
101 changes: 55 additions & 46 deletions code/datums/components/acid.dm
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/effects/effects.dmi', "acid"))
GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/effects/acid.dmi', "default"))

/** Component representing acid applied to an object.
*
/**
* Component representing acid applied to an object.
* Must be attached to an atom.
* Processes, repeatedly damaging whatever it is attached to.
* If the parent atom is a turf it applies acid to the contents of the turf.
* If not being applied to a mob or turf, the atom must use the integrity system.
*/
/datum/component/acid
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
Expand All @@ -14,6 +15,8 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
var/acid_volume
/// The maximum volume of acid on the parent [/atom].
var/max_volume = INFINITY
/// Acid overlay appearance we apply
var/acid_overlay
/// The ambiant sound of acid eating away at the parent [/atom].
var/datum/looping_sound/acid/sizzle
/// Used exclusively for melting turfs. TODO: Move integrity to the atom level so that this can be dealt with there.
Expand All @@ -23,91 +26,99 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
/// The proc used to handle the parent [/atom] when processing. TODO: Unify damage and resistance flags so that this doesn't need to exist!
var/datum/callback/process_effect

/datum/component/acid/Initialize(_acid_power, _acid_volume, _max_volume=null)
if((_acid_power) <= 0 || (_acid_volume <= 0))
stack_trace("Acid component added with insufficient acid power ([_acid_power]) or acid volume ([_acid_power]).")
return COMPONENT_INCOMPATIBLE // Not enough acid or the acid's too weak, either one.
/datum/component/acid/Initialize(acid_power = ACID_POWER_MELT_TURF, acid_volume = 50, acid_overlay = GLOB.acid_overlay)
if(!isatom(parent))
stack_trace("Acid component added to [parent] ([parent?.type]) which is not a /atom subtype.")
return COMPONENT_INCOMPATIBLE // Incompatible type. TODO: Rework take_damage to the atom level and move this there.

if(isobj(parent))
var/obj/parent_object = parent
if(parent_object.resistance_flags & UNACIDABLE) // The parent object cannot have acid. Should never happen, will happen.
stack_trace("Acid component added to unacidable object [parent].")
return COMPONENT_INCOMPATIBLE

max_volume = OBJ_ACID_VOLUME_MAX
process_effect = CALLBACK(src, PROC_REF(process_obj), parent)
else if(isliving(parent))
return COMPONENT_INCOMPATIBLE
//not incompatible, but pointless
var/atom/atom_parent = parent
if((acid_power) <= 0 || (acid_volume <= 0))
stack_trace("Acid component added to an atom ([atom_parent.type]) with insufficient acid power ([acid_power]) or acid volume ([acid_volume]).")
qdel(src)
return


if(isliving(parent))
max_volume = MOB_ACID_VOLUME_MAX
process_effect = CALLBACK(src, PROC_REF(process_mob), parent)
else if(isturf(parent))
max_volume = TURF_ACID_VOLUME_MAX
process_effect = CALLBACK(src, PROC_REF(process_turf), parent)
//if we failed all other checks, we must be an /atom/movable that uses integrity
else if(atom_parent.uses_integrity)
// The parent object cannot have acid. Not incompatible, but should not really happen.
if(atom_parent.resistance_flags & UNACIDABLE)
qdel(src)
return

max_volume = MOVABLE_ACID_VOLUME_MAX
process_effect = CALLBACK(src, PROC_REF(process_movable), parent)
//or not...
else
stack_trace("Tried to add /datum/component/acid to an atom ([atom_parent.type]) which does not use atom_integrity!")
return COMPONENT_INCOMPATIBLE

src.acid_power = acid_power
set_volume(acid_volume)
src.acid_overlay = acid_overlay

acid_power = _acid_power
set_volume(_acid_volume)

var/atom/parent_atom = parent
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
parent_atom.update_appearance()
sizzle = new(parent, TRUE)
START_PROCESSING(SSacid, src)

/datum/component/acid/Destroy(force, silent)
STOP_PROCESSING(SSacid, src)
QDEL_NULL(sizzle)
if(sizzle)
QDEL_NULL(sizzle)
if(process_effect)
QDEL_NULL(process_effect)
UnregisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS)
if(parent && !QDELING(parent))
var/atom/parent_atom = parent
parent_atom.update_appearance()
return ..()

/datum/component/acid/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
RegisterSignal(parent, COMSIG_ATOM_EXPOSE_REAGENT, PROC_REF(on_expose_reagent))
if(isturf(parent))
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(on_entered))
var/atom/atom_parent = parent
atom_parent.update_appearance()

/datum/component/acid/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_EXAMINE,
COMSIG_ATOM_UPDATE_OVERLAYS,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_ATOM_ATTACK_HAND,
COMSIG_ATOM_EXPOSE_REAGENT))

if(isturf(parent))
UnregisterSignal(parent, COMSIG_ATOM_ENTERED)
var/atom/atom_parent = parent
if(!QDELETED(atom_parent))
atom_parent.update_appearance()

/// Averages corrosive power and sums volume.
/datum/component/acid/InheritComponent(datum/component/C, i_am_original, _acid_power, _acid_volume)
acid_power = ((acid_power * acid_volume) + (_acid_power * _acid_volume)) / (acid_volume + _acid_volume)
set_volume(acid_volume + _acid_volume)
/datum/component/acid/InheritComponent(datum/component/new_comp, i_am_original, acid_power, acid_volume)
acid_power = ((src.acid_power * src.acid_volume) + (acid_power * acid_volume)) / (src.acid_volume + acid_volume)
set_volume(src.acid_volume + acid_volume)

/// Sets the acid volume to a new value. Limits the acid volume by the amount allowed to exist on the parent atom.
/datum/component/acid/proc/set_volume(new_volume)
acid_volume = clamp(new_volume, 0, max_volume)
if(!acid_volume)
qdel(src)


/// Handles the slow corrosion of the parent [/atom].
/datum/component/acid/process(seconds_per_tick)
process_effect?.InvokeAsync(seconds_per_tick)
if(QDELING(src)) //The process effect deals damage, and on turfs diminishes the acid volume, potentially destroying the component. Let's not destroy it twice.
return
set_volume(acid_volume - (ACID_DECAY_BASE + (ACID_DECAY_SCALING*round(sqrt(acid_volume)))) * seconds_per_tick)

/// Handles processing on a [/obj].
/datum/component/acid/proc/process_obj(obj/target, seconds_per_tick)
/// Handles processing on an [/atom/movable] (that uses atom_integrity).
/datum/component/acid/proc/process_movable(atom/movable/target, seconds_per_tick)
if(target.resistance_flags & ACID_PROOF)
return
target.take_damage(min(1 + round(sqrt(acid_power * acid_volume)*0.3), OBJ_ACID_DAMAGE_MAX) * seconds_per_tick, BURN, ACID, 0)
target.take_damage(min(1 + round(sqrt(acid_power * acid_volume)*0.3), MOVABLE_ACID_DAMAGE_MAX) * seconds_per_tick, BURN, ACID, 0)

/// Handles processing on a [/mob/living].
/datum/component/acid/proc/process_mob(mob/living/target, seconds_per_tick)
Expand All @@ -117,8 +128,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
/datum/component/acid/proc/process_turf(turf/target_turf, seconds_per_tick)
var/acid_used = min(acid_volume * 0.05, 20) * seconds_per_tick
var/applied_targets = 0
for(var/am in target_turf)
var/atom/movable/target_movable = am
for(var/atom/movable/target_movable as anything in target_turf)
if(target_movable.acid_act(acid_power, acid_used))
applied_targets++

Expand Down Expand Up @@ -150,16 +160,17 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
/datum/component/acid/proc/on_update_overlays(atom/parent_atom, list/overlays)
SIGNAL_HANDLER

overlays += mutable_appearance('icons/effects/acid.dmi', parent_atom.custom_acid_overlay || ACID_OVERLAY_DEFAULT)
if(acid_overlay)
overlays += acid_overlay

/// Alerts any examiners to the acid on the parent atom.
/datum/component/acid/proc/on_examine(atom/A, mob/user, list/examine_list)
/datum/component/acid/proc/on_examine(atom/source, mob/user, list/examine_list)
SIGNAL_HANDLER

examine_list += span_danger("[A.p_theyre()] covered in corrosive liquid!")
examine_list += span_danger("[source.p_theyre(TRUE)] covered in a corrosive liquid!")

/// Makes it possible to clean acid off of objects.
/datum/component/acid/proc/on_clean(atom/A, clean_types)
/datum/component/acid/proc/on_clean(atom/source, clean_types)
SIGNAL_HANDLER

if(!(clean_types & CLEAN_TYPE_ACID))
Expand All @@ -178,7 +189,6 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
set_volume(acid_volume + reac_volume)
return NONE


/// Handles searing the hand of anyone who tries to touch this without protection.
/datum/component/acid/proc/on_attack_hand(atom/parent_atom, mob/living/carbon/user)
SIGNAL_HANDLER
Expand All @@ -201,7 +211,6 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
user.update_damage_overlays()
return COMPONENT_CANCEL_ATTACK_CHAIN


/// Handles searing the feet of whoever walks over this without protection. Only active if the parent is a turf.
/datum/component/acid/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
Expand Down
47 changes: 29 additions & 18 deletions code/datums/components/burning.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,52 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e

/**
* Component representing an atom being on fire.
* Should not be used on mobs, they use the fire stacks system.
* Should not be used on mobs, they use the fire stacks status effects.
* Can only be used on atoms that use the integrity system.
*/
/datum/component/burning
/// Fire overlay appearance we apply
var/fire_overlay
/// Particle holder for fire particles, if any
var/obj/effect/abstract/particle_holder/particle_effect

/datum/component/burning/Initialize(fire_overlay, fire_particles)
/datum/component/burning/Initialize(fire_overlay = GLOB.fire_overlay, fire_particles = /particles/smoke/burning)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
var/atom/atom_parent = parent
if(!atom_parent.uses_integrity)
stack_trace("Tried to add /datum/component/burning to an atom ([atom_parent]) that does not use atom_integrity!")
stack_trace("Tried to add /datum/component/burning to an atom ([atom_parent.type]) that does not use atom_integrity!")
return COMPONENT_INCOMPATIBLE
// only flammable atoms should have this component, but it's not really an error if we try to apply this to a non flammable one
if(!(atom_parent.resistance_flags & FLAMMABLE) || (atom_parent.resistance_flags & FIRE_PROOF))
qdel(src)
return
src.fire_overlay = fire_overlay
if(fire_particles)
particle_effect = new(atom_parent, fire_particles)
atom_parent.resistance_flags |= ON_FIRE
START_PROCESSING(SSfire_burning, src)
// burning particles look pretty bad when they stack on mobs, so that behavior is not wanted for items
particle_effect = new(atom_parent, fire_particles, isitem(atom_parent) ? NONE : PARTICLE_ATTACH_MOB)
START_PROCESSING(SSburning, src)

/datum/component/burning/Destroy(force, silent)
STOP_PROCESSING(SSburning, src)
if(particle_effect)
QDEL_NULL(particle_effect)
return ..()

/datum/component/burning/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
RegisterSignal(parent, COMSIG_ATOM_EXTINGUISH, PROC_REF(on_extinguish))
var/atom/atom_parent = parent
atom_parent.update_appearance(UPDATE_ICON)
atom_parent.resistance_flags |= ON_FIRE
atom_parent.update_appearance()

/datum/component/burning/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_ATOM_EXTINGUISH))

/datum/component/burning/Destroy(force, silent)
STOP_PROCESSING(SSfire_burning, src)
if(particle_effect)
QDEL_NULL(particle_effect)
UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_ATOM_EXTINGUISH))
var/atom/atom_parent = parent
if(!QDELING(atom_parent) && (atom_parent.resistance_flags & ON_FIRE))
if(!QDELETED(atom_parent))
atom_parent.resistance_flags &= ~ON_FIRE
atom_parent.update_appearance(UPDATE_ICON)
return ..()
atom_parent.update_appearance()

/datum/component/burning/process(seconds_per_tick)
var/atom/atom_parent = parent
Expand All @@ -56,10 +57,20 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
return
atom_parent.take_damage(10 * seconds_per_tick, BURN, FIRE, FALSE)

/// Alerts any examiners that the parent is on fire (even though it should be rather obvious)
/datum/component/burning/proc/on_examine(atom/source, mob/user, list/examine_list)
SIGNAL_HANDLER

examine_list += span_danger("[source.p_theyre(TRUE)] burning!")

/// Maintains the burning overlay on the parent atom
/datum/component/burning/proc/on_update_overlays(atom/source, list/overlays)
SIGNAL_HANDLER

//most likely means the component is being removed
if(!(source.resistance_flags & ON_FIRE))
return

if(fire_overlay)
overlays += fire_overlay

Expand Down
16 changes: 0 additions & 16 deletions code/game/atom_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,3 @@
if(uses_integrity)
return clamp(PENETRATE_ARMOUR(get_armor_rating(impacting_projectile.armor_flag), impacting_projectile.armour_penetration), 0, 100)
return 0

/**
* Should be called when the atom is destroyed by fire
* This proc is terrible. I do not know why it exists.
* Please remove it at some point.
*/
/atom/proc/burn()
return

/**
* Sends COMSIG_ATOM_EXTINGUISH signal which properly removes burning component.
* Can be hooked onto for extra behavior.
*/
/atom/proc/extinguish()
SHOULD_CALL_PARENT(TRUE)
return SEND_SIGNAL(src, COMSIG_ATOM_EXTINGUISH)
17 changes: 16 additions & 1 deletion code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -949,9 +949,23 @@
return FALSE
return TRUE

/**
* Respond to fire being used on our atom
*
* Default behaviour is to send [COMSIG_ATOM_FIRE_ACT] and return
*/
/atom/proc/fire_act(exposed_temperature, exposed_volume)
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
return
return FALSE

/**
* Sends [COMSIG_ATOM_EXTINGUISH] signal, which properly removes burning component if it is present.
*
* Default behaviour is to send [COMSIG_ATOM_ACID_ACT] and return
*/
/atom/proc/extinguish()
SHOULD_CALL_PARENT(TRUE)
return SEND_SIGNAL(src, COMSIG_ATOM_EXTINGUISH)

/**
* React to being hit by a thrown object
Expand Down Expand Up @@ -1912,6 +1926,7 @@
* Override this if you want custom behaviour in whatever gets hit by the rust
*/
/atom/proc/rust_heretic_act()
return

/**
* Used to set something as 'open' if it's being used as a supplypod
Expand Down
2 changes: 0 additions & 2 deletions code/game/machinery/_machinery.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1114,8 +1114,6 @@
if(machine_stat & BROKEN)
. += span_notice("It looks broken and non-functional.")
if(!(resistance_flags & INDESTRUCTIBLE))
if(resistance_flags & ON_FIRE)
. += span_warning("It's on fire!")
var/healthpercent = (atom_integrity/max_integrity) * 100
switch(healthpercent)
if(50 to 99)
Expand Down
Loading

0 comments on commit 573bfd5

Please sign in to comment.