diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index de9f1997a61..e2cb1e01554 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -16,7 +16,7 @@
#define COMSIG_LIVING_DOORCRUSHED "living_doorcrush"
///from base of mob/living/resist() (/mob/living)
#define COMSIG_LIVING_RESIST "living_resist"
-///from base of mob/living/IgniteMob() (/mob/living)
+///from base of mob/living/ignite_mob() (/mob/living)
#define COMSIG_LIVING_IGNITED "living_ignite"
///from base of mob/living/extinguish_mob() (/mob/living)
#define COMSIG_LIVING_EXTINGUISHED "living_extinguished"
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 200e76b80a9..dce92503ba6 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -38,3 +38,4 @@
#define STASIS_CHEMICAL_EFFECT "stasis_chemical"
#define STASIS_ASCENSION_EFFECT "heretic_ascension"
+
diff --git a/code/datums/components/igniter.dm b/code/datums/components/igniter.dm
index 2d9d21022b1..7371942296e 100644
--- a/code/datums/components/igniter.dm
+++ b/code/datums/components/igniter.dm
@@ -1,11 +1,13 @@
/datum/component/igniter
var/fire_stacks
+ var/fire_type
-/datum/component/igniter/Initialize(fire_stacks=1)
+/datum/component/igniter/Initialize(fire_stacks = 1, fire_type = /datum/status_effect/fire_handler/fire_stacks)
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
return COMPONENT_INCOMPATIBLE
src.fire_stacks = fire_stacks
+ src.fire_type = fire_type
/datum/component/igniter/RegisterWithParent()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
@@ -40,5 +42,5 @@
/datum/component/igniter/proc/do_igniter(atom/target)
if(isliving(target))
var/mob/living/L = target
- L.adjust_fire_stacks(fire_stacks)
- L.IgniteMob()
+ L.adjust_fire_stacks(fire_stacks, fire_type)
+ L.ignite_mob()
diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm
index f9e0555deb4..dbbe76abcf0 100644
--- a/code/datums/diseases/advance/symptoms/fire.dm
+++ b/code/datums/diseases/advance/symptoms/fire.dm
@@ -51,12 +51,12 @@
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()
to_chat(M, span_userdanger("Your skin erupts into an inferno!"))
M.emote("scream")
@@ -134,7 +134,7 @@ Bonus
M.visible_message(span_warning("[M]'s sweat sizzles and pops on contact with water!"))
explosion(M, devastation_range = -1, heavy_impact_range = (-1 + explosion_power), light_impact_range = (2 * explosion_power), explosion_cause = src)
Alkali_fire_stage_4(M, A)
- M.IgniteMob()
+ M.ignite_mob()
to_chat(M, span_userdanger("Your sweat bursts into flames!"))
M.emote("scream")
if(5)
@@ -142,7 +142,7 @@ Bonus
M.visible_message(span_warning("[M]'s sweat sizzles and pops on contact with water!"))
explosion(M, devastation_range = -1, heavy_impact_range = (-1 + explosion_power), light_impact_range = (2 * explosion_power), explosion_cause = src)
Alkali_fire_stage_5(M, A)
- M.IgniteMob()
+ M.ignite_mob()
to_chat(M, span_userdanger("Your skin erupts into an inferno!"))
M.emote("scream")
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index da8cf315d82..f92cd30a927 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -455,7 +455,7 @@
. = 0
var/mob/living/M = A.affected_mob
if(M.fire_stacks < 0)
- M.set_fire_stacks(min(M.fire_stacks + 1 * absorption_coeff, 0))
+ M.adjust_fire_stacks(min(absorption_coeff, -M.fire_stacks))
. += power
if(M.reagents.has_reagent(/datum/reagent/water/holywater, needs_metabolizing = FALSE))
M.reagents.remove_reagent(/datum/reagent/water/holywater, 0.5 * absorption_coeff)
diff --git a/code/datums/diseases/rhumba_beat.dm b/code/datums/diseases/rhumba_beat.dm
index f6ffe59ecc0..47138170764 100644
--- a/code/datums/diseases/rhumba_beat.dm
+++ b/code/datums/diseases/rhumba_beat.dm
@@ -32,7 +32,7 @@
if(DT_PROB(10, delta_time))
if(prob(50))
affected_mob.adjust_fire_stacks(2)
- affected_mob.IgniteMob()
+ affected_mob.ignite_mob()
else
affected_mob.emote("gasp")
to_chat(affected_mob, span_danger("You feel a burning beat inside..."))
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
index 8096870697e..d1119a4fa44 100644
--- a/code/datums/mutations/actions.dm
+++ b/code/datums/mutations/actions.dm
@@ -146,7 +146,7 @@
return
our_lizard.adjust_fire_stacks(cone_levels)
- our_lizard.IgniteMob()
+ our_lizard.ignite_mob()
to_chat(our_lizard, span_warning("Something in front of your mouth catches fire!"))
/obj/effect/proc_holder/spell/cone/staggered/firebreath/cast(list/targets, mob/user)
@@ -171,7 +171,7 @@
// The actual burn damage application is not blocked by fireproofing, like space dragons.
target_mob.apply_damage(max(10, 40 - (5 * level)), BURN, spread_damage = TRUE)
target_mob.adjust_fire_stacks(max(2, 5 - level))
- target_mob.IgniteMob()
+ target_mob.ignite_mob()
/obj/effect/proc_holder/spell/cone/staggered/firebreath/do_obj_cone_effect(obj/target_obj, level)
// Further out objects experience less exposed_temperature and exposed_volume
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index 466c6b328b8..3fb98af58fd 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -293,7 +293,7 @@
/datum/mutation/human/fire/on_life(delta_time, times_fired)
if(DT_PROB((0.05+(100-dna.stability)/19.5) * GET_MUTATION_SYNCHRONIZER(src), delta_time))
owner.adjust_fire_stacks(2 * GET_MUTATION_POWER(src))
- owner.IgniteMob()
+ owner.ignite_mob()
/datum/mutation/human/fire/on_acquiring(mob/living/carbon/human/owner)
if(..())
diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm
index 46b62f6e34a..32ea2eb8153 100644
--- a/code/datums/status_effects/_status_effect.dm
+++ b/code/datums/status_effects/_status_effect.dm
@@ -75,12 +75,12 @@
// Status effect process. Handles adjusting it's duration and ticks.
// If you're adding processed effects, put them in [proc/tick]
// instead of extending / overriding ththe process() proc.
-/datum/status_effect/process(delta_time)
+/datum/status_effect/process(delta_time, times_fired)
if(QDELETED(owner))
qdel(src)
return
if(tick_interval < world.time)
- tick()
+ tick(delta_time, times_fired)
tick_interval = world.time + initial(tick_interval)
if(duration != -1 && duration < world.time)
qdel(src)
@@ -96,7 +96,7 @@
return null
/// Called every tick from process().
-/datum/status_effect/proc/tick()
+/datum/status_effect/proc/tick(delta_time, times_fired)
return
/// Called whenever the buff expires or is removed (qdeleted)
diff --git a/code/datums/status_effects/_status_effect_helpers.dm b/code/datums/status_effects/_status_effect_helpers.dm
index 3f21920f516..9cd5f4f3c86 100644
--- a/code/datums/status_effects/_status_effect_helpers.dm
+++ b/code/datums/status_effects/_status_effect_helpers.dm
@@ -40,7 +40,8 @@
// Create the status effect with our mob + our arguments
var/datum/status_effect/new_instance = new new_effect(arguments)
- return new_instance
+ if(!QDELETED(new_instance))
+ return new_instance
/**
* Removes all instances of a given status effect from this mob
diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm
new file mode 100644
index 00000000000..98d88182218
--- /dev/null
+++ b/code/datums/status_effects/debuffs/fire_stacks.dm
@@ -0,0 +1,260 @@
+/datum/status_effect/fire_handler
+ duration = -1
+ alert_type = null
+ status_type = STATUS_EFFECT_REFRESH //Custom code
+ on_remove_on_mob_delete = TRUE
+ tick_interval = 2 SECONDS
+ /// Current amount of stacks we have
+ var/stacks
+ /// Maximum of stacks that we could possibly get
+ var/stack_limit = 20
+ /// 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
+
+ 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(-enemy_effect.stacks * enemy_effect.stack_modifier / stack_modifier)
+ enemy_effect.adjust_stacks(-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()
+
+/**
+ * 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, /atom/movable/screen/alert/fire)
+
+/**
+ * Used to update owner's effect overlay
+ */
+
+/datum/status_effect/fire_handler/proc/update_overlay()
+
+/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
+
+ enemy_types = list(/datum/status_effect/fire_handler/wet_stacks)
+ stack_modifier = 1
+
+ /// If we're on fire
+ var/on_fire = FALSE
+ /// A weakref to the mob light emitter
+ var/datum/weakref/firelight_ref
+ /// Type of mob light emitter we use when on fire
+ var/firelight_type = /obj/effect/dummy/lighting_obj/moblight/fire
+ /// Stores current fire overlay icon state, for optimisation purposes
+ var/last_icon_state
+
+/datum/status_effect/fire_handler/fire_stacks/tick(delta_time, times_fired)
+ if(stacks <= 0)
+ qdel(src)
+ return TRUE
+
+ if(!on_fire || isanimal(owner))
+ return TRUE
+
+ if(iscyborg(owner))
+ adjust_stacks(-0.55 * delta_time)
+ else
+ adjust_stacks(-0.05 * delta_time)
+
+ if(stacks <= 0)
+ qdel(src)
+ return TRUE
+
+ var/datum/gas_mixture/air = owner.loc.return_air()
+ if(!air.gases[/datum/gas/oxygen] || air.gases[/datum/gas/oxygen][MOLES] < 1)
+ qdel(src)
+ return TRUE
+
+ deal_damage(delta_time, times_fired)
+ update_overlay()
+
+/**
+ * Proc that handles damage dealing and all special effects
+ *
+ * Arguments:
+ * - delta_time
+ * - times_fired
+ *
+ */
+
+/datum/status_effect/fire_handler/fire_stacks/proc/deal_damage(delta_time, times_fired)
+ owner.on_fire_stack(delta_time, times_fired, src)
+
+ var/turf/location = get_turf(owner)
+ location.hotspot_expose(700, 25 * delta_time, TRUE)
+
+/**
+ * Used to deal damage to humans and count their protection.
+ *
+ * Arguments:
+ * - delta_time
+ * - times_fired
+ * - no_protection: When set to TRUE, fire will ignore any possible fire protection
+ *
+ */
+
+/datum/status_effect/fire_handler/fire_stacks/proc/harm_human(delta_time, times_fired, no_protection = FALSE)
+ var/mob/living/carbon/human/victim = owner
+ var/thermal_protection = victim.get_thermal_protection()
+
+ if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT && !no_protection)
+ return
+
+ if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT && !no_protection)
+ victim.adjust_bodytemperature(5.5 * delta_time)
+ return
+
+ victim.adjust_bodytemperature((BODYTEMP_HEATING_MAX + (stacks * 12)) * 0.5 * delta_time)
+ SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "on_fire", /datum/mood_event/on_fire)
+ victim.mind?.add_memory(MEMORY_FIRE, list(DETAIL_PROTAGONIST = victim), story_value = STORY_VALUE_OKAY)
+
+/**
+ * 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(firelight_type)
+ firelight_ref = WEAKREF(new firelight_type(owner))
+
+ SEND_SIGNAL(owner, COMSIG_LIVING_IGNITED, owner)
+ cache_stacks()
+ update_overlay()
+
+/**
+ * Handles mob extinguishing, should be the only way to set on_fire to FALSE
+ */
+
+/datum/status_effect/fire_handler/fire_stacks/proc/extinguish()
+ if(firelight_ref)
+ qdel(firelight_ref)
+
+ on_fire = FALSE
+ SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "on_fire")
+ SEND_SIGNAL(owner, COMSIG_LIVING_EXTINGUISHED, owner)
+ cache_stacks()
+ update_overlay()
+
+/datum/status_effect/fire_handler/fire_stacks/on_remove()
+ if(on_fire)
+ extinguish()
+ set_stacks(0)
+ update_overlay()
+
+/datum/status_effect/fire_handler/fire_stacks/update_overlay()
+ last_icon_state = owner.update_fire_overlay(stacks, on_fire, last_icon_state)
+
+/datum/status_effect/fire_handler/fire_stacks/on_apply()
+ . = ..()
+ update_overlay()
+
+/datum/status_effect/fire_handler/wet_stacks
+ id = "wet_stacks"
+
+ enemy_types = list(/datum/status_effect/fire_handler/fire_stacks)
+ stack_modifier = -1
+
+/datum/status_effect/fire_handler/wet_stacks/tick(delta_time)
+ adjust_stacks(-0.5 * delta_time)
+ if(stacks <= 0)
+ qdel(src)
diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm
index bf3117234a1..39113080f62 100644
--- a/code/datums/voice_of_god_command.dm
+++ b/code/datums/voice_of_god_command.dm
@@ -213,7 +213,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
/datum/voice_of_god_command/burn/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.adjust_fire_stacks(1 * power_multiplier)
- target.IgniteMob()
+ target.ignite_mob()
/// This command heats the listeners up like boiling water.
/datum/voice_of_god_command/hot
diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm
index 1ba229388ee..587f8ee0c25 100644
--- a/code/game/machinery/computer/arcade/orion.dm
+++ b/code/game/machinery/computer/arcade/orion.dm
@@ -368,7 +368,7 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
reason = "You ran out of fuel, and drift, slowly, into a star."
if(obj_flags & EMAGGED)
gamer.adjust_fire_stacks(5)
- gamer.IgniteMob() //flew into a star, so you're on fire
+ gamer.ignite_mob() //flew into a star, so you're on fire
to_chat(gamer, span_userdanger("You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames."))
if(obj_flags & EMAGGED)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 0a4e2bffa94..0fa657a7d8f 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -131,7 +131,7 @@
if(.)
if(resistance_flags & ON_FIRE) //Sets the mob on fire if you buckle them to a burning atom/movableect
M.adjust_fire_stacks(1)
- M.IgniteMob()
+ M.ignite_mob()
/**
* Set a mob as unbuckled from src
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index 06ff319eb64..59ef1d376c2 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -69,7 +69,7 @@
/obj/effect/particle_effect/foam/firefighting/foam_mob(mob/living/L)
if(!istype(L))
return
- L.adjust_fire_stacks(-2)
+ L.adjust_wet_stacks(2)
/obj/effect/particle_effect/foam/metal
name = "aluminium foam"
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 0e769f963c6..39ab7fdd5ff 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -713,7 +713,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(get_temperature() && isliving(hit_atom))
var/mob/living/L = hit_atom
- L.IgniteMob()
+ L.ignite_mob()
var/itempush = 1
if(w_class < 4)
itempush = 0 //too light to push anything
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index 08f8356925a..10b19f187ca 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -85,7 +85,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
if(!isliving(M))
return
- if(lit && M.IgniteMob())
+ if(lit && M.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]")
@@ -312,7 +312,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/cigarette/process(delta_time)
var/mob/living/user = isliving(loc) ? loc : null
- user?.IgniteMob()
+ user?.ignite_mob()
if(!reagents.has_reagent(/datum/reagent/oxygen)) //cigarettes need oxygen
var/datum/gas_mixture/air = return_air()
if(!air || !air.has_gas(/datum/gas/oxygen, 1)) //or oxygen on a tile to burn
@@ -802,7 +802,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/lighter/attack(mob/living/carbon/M, mob/living/carbon/user)
- if(lit && M.IgniteMob())
+ if(lit && M.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]")
var/obj/item/clothing/mask/cigarette/cig = help_light_cig(M)
@@ -1025,7 +1025,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
if(reagents.get_reagent_amount(/datum/reagent/fuel))
//HOT STUFF
vaper.adjust_fire_stacks(2)
- vaper.IgniteMob()
+ vaper.ignite_mob()
if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire
var/datum/effect_system/reagents_explosion/e = new()
@@ -1040,7 +1040,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/mob/living/M = loc
if(isliving(loc))
- M.IgniteMob()
+ M.ignite_mob()
if(!reagents.total_volume)
if(ismob(loc))
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index e09ef51ebe4..4ff77c773a1 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -145,7 +145,7 @@
if(isOn() && !QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist
handle_fuel_and_temps(1, user)
var/mob/living/attacked_mob = attacked_atom
- if(attacked_mob.IgniteMob())
+ if(attacked_mob.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(attacked_mob)] on fire with [src] at [AREACOORD(user)]")
@@ -164,7 +164,7 @@
if(!QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist
var/mob/living/attacked_mob = attacked_atom
- if(attacked_mob.IgniteMob())
+ if(attacked_mob.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(attacked_mob)] on fire with [src] at [AREACOORD(user)]")
diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm
index 6bfb760817f..5de5b9693dc 100644
--- a/code/game/objects/structures/bonfire.dm
+++ b/code/game/objects/structures/bonfire.dm
@@ -145,7 +145,7 @@
else if(isliving(burn_target))
var/mob/living/burn_victim = burn_target
burn_victim.adjust_fire_stacks(BONFIRE_FIRE_STACK_STRENGTH * 0.5 * delta_time)
- burn_victim.IgniteMob()
+ burn_victim.ignite_mob()
else if(isobj(burn_target))
var/obj/burned_object = burn_target
if(grill && isitem(burned_object))
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 5c82669420f..4d9f56a4e31 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -220,7 +220,7 @@
burn_living.adjustFireLoss(lava_damage * delta_time)
if(!QDELETED(burn_living)) //mobs turning into object corpses could get deleted here.
burn_living.adjust_fire_stacks(lava_firestacks * delta_time)
- burn_living.IgniteMob()
+ burn_living.ignite_mob()
/turf/open/lava/smooth
name = "lava"
diff --git a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm
index 3761f33faba..ded3be1458e 100644
--- a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm
+++ b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm
@@ -36,7 +36,7 @@
. = ..()
reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind)
exposed_mob.adjust_fire_stacks(round(reac_volume/10))
- exposed_mob.IgniteMob()
+ exposed_mob.ignite_mob()
if(exposed_mob)
exposed_mob.apply_damage(0.8*reac_volume, BURN, wound_bonus=CANT_WOUND)
if(iscarbon(exposed_mob))
diff --git a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm
index 3398d080600..d035319219d 100644
--- a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm
+++ b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm
@@ -31,7 +31,7 @@
for(var/obj/O in T)
O.extinguish()
for(var/mob/living/L in T)
- L.adjust_fire_stacks(-2.5)
+ L.adjust_wet_stacks(2.5)
L.extinguish_mob()
/datum/reagent/blob/pressurized_slime
@@ -45,7 +45,7 @@
var/turf/open/location_turf = get_turf(exposed_mob)
if(istype(location_turf) && prob(reac_volume))
location_turf.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
- exposed_mob.adjust_fire_stacks(-(reac_volume / 10))
+ exposed_mob.adjust_wet_stacks(reac_volume / 10)
exposed_mob.apply_damage(0.4*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(exposed_mob)
exposed_mob.adjustStaminaLoss(reac_volume, FALSE)
diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
index ea56f3228c3..f9ec6a2df1e 100644
--- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
@@ -146,7 +146,7 @@
return
target.adjust_fire_stacks(1)
- target.IgniteMob()
+ target.ignite_mob()
/datum/heretic_knowledge/spell/flame_birth
name = "Nightwater's Rebirth"
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
index f5dc22ac560..4b4ffd8d712 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
@@ -61,7 +61,7 @@
*/
/datum/status_effect/unholy_determination/proc/adjust_all_damages(amount)
- owner.set_fire_stacks(max(owner.fire_stacks - 1, 0))
+ owner.adjust_fire_stacks(-1)
owner.losebreath = max(owner.losebreath - 0.5, 0)
owner.adjustToxLoss(-amount, FALSE, TRUE)
diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm
index 2f4148ae3c4..44f2e71acb8 100644
--- a/code/modules/assembly/igniter.dm
+++ b/code/modules/assembly/igniter.dm
@@ -15,7 +15,7 @@
/obj/item/assembly/igniter/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is trying to ignite [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
- user.IgniteMob()
+ user.ignite_mob()
return FIRELOSS
/obj/item/assembly/igniter/Initialize(mapload)
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index a7d07884a71..a64e53ae979 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -6,7 +6,7 @@
requires_power = FALSE
static_lighting = FALSE
base_lighting_alpha = 255
-
+
/area/awaymission/snowdin/outside
name = "Snowdin Tundra Plains"
icon_state = "awaycontent25"
@@ -219,7 +219,7 @@
burn_human.visible_message(span_warning("[burn_human]'s [burn_limb.name] melts down to the bone!"), \
span_userdanger("You scream out in pain as your [burn_limb.name] melts down to the bone, leaving an eerie plasma-like glow where flesh used to be!"))
if(!plasma_parts.len && !robo_parts.len) //a person with no potential organic limbs left AND no robotic limbs, time to turn them into a plasmaman
- burn_human.IgniteMob()
+ burn_human.ignite_mob()
burn_human.set_species(/datum/species/plasmaman)
burn_human.visible_message(span_warning("[burn_human] bursts into a brilliant purple flame as [burn_human.p_their()] entire body is that of a skeleton!"), \
span_userdanger("Your senses numb as all of your remaining flesh is turned into a purple slurry, sloshing off your body and leaving only your bones to show in a vibrant purple!"))
diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm
index 008dd0a1f59..7f8706bf95d 100644
--- a/code/modules/clothing/spacesuits/plasmamen.dm
+++ b/code/modules/clothing/spacesuits/plasmamen.dm
@@ -22,7 +22,7 @@
if(!istype(H))
return
- if(H.fire_stacks)
+ if(H.fire_stacks > 0)
if(extinguishes_left)
if(next_extinguish > world.time)
return
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index df3bc20bfcb..9c4c3b43308 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -152,8 +152,8 @@
for(var/mob/living/carbon/carbon_victim in range(6, owner))
if(carbon_victim != owner)
carbon_victim.adjust_fire_stacks(8)
- carbon_victim.IgniteMob()
- owner.set_fire_stacks(-20)
+ carbon_victim.ignite_mob()
+ owner.set_wet_stacks(20)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
@@ -161,7 +161,7 @@
owner.visible_message(span_danger("[src] just makes [attack_text] worse by spewing molten death on [owner]!"))
playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, TRUE)
owner.adjust_fire_stacks(12)
- owner.IgniteMob()
+ owner.ignite_mob()
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 204ed0792d2..d9ce824703c 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -1475,7 +1475,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
set waitfor = FALSE
..()
target.set_fire_stacks(max(target.fire_stacks, 0.1)) //Placebo flammability
- fire_overlay = image('icons/mob/OnFire.dmi', target, "Standing", ABOVE_MOB_LAYER)
+ fire_overlay = image('icons/mob/onfire.dmi', target, "human_burning", ABOVE_MOB_LAYER)
if(target.client)
target.client.images += fire_overlay
to_chat(target, span_userdanger("You're set on fire!"))
diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm
index a53924cf2f3..989739563fc 100644
--- a/code/modules/hydroponics/unique_plant_genes.dm
+++ b/code/modules/hydroponics/unique_plant_genes.dm
@@ -81,7 +81,7 @@
var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
to_chat(target, "You are lit on fire from the intense heat of [our_plant]!")
target.adjust_fire_stacks(our_seed.potency / 20)
- if(target.IgniteMob())
+ if(target.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [ADMIN_LOOKUPFLW(target)] on fire with [our_plant] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(target)] on fire with [our_plant] at [AREACOORD(user)]")
our_plant.investigate_log("was used by [key_name(user)] to burn [key_name(target)] at [AREACOORD(user)]", INVESTIGATE_BOTANY)
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 427ac0f8980..336d348fc17 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -131,3 +131,6 @@ In all, this is a lot like the monkey code. /N
/mob/living/carbon/alien/acid_act(acidpwr, acid_volume)
return FALSE//aliens are immune to acid.
+
+/mob/living/carbon/alien/on_fire_stack(delta_time, times_fired, datum/status_effect/fire_handler/fire_stacks/fire_handler)
+ adjust_bodytemperature(BODYTEMP_HEATING_MAX * 0.5 * delta_time)
diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm
index 19e6a17ac30..ca33aa826e7 100644
--- a/code/modules/mob/living/carbon/alien/life.dm
+++ b/code/modules/mob/living/carbon/alien/life.dm
@@ -46,9 +46,3 @@
//natural reduction of movement delay due to stun.
if(move_delay_add > 0)
move_delay_add = max(0, move_delay_add - (0.5 * rand(1, 2) * delta_time))
-
-/mob/living/carbon/alien/handle_fire(delta_time, times_fired)//Aliens on fire code
- . = ..()
- if(.) //if the mob isn't on fire anymore
- return
- adjust_bodytemperature(BODYTEMP_HEATING_MAX * 0.5 * delta_time) //If you're on fire, you heat up!
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index bc59cf4aefe..d43972150ce 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -260,7 +260,6 @@
if(fire_stacks <= 0 && !QDELETED(src))
visible_message(span_danger("[src] successfully extinguishes [p_them()]self!"), \
span_notice("You extinguish yourself."))
- extinguish_mob()
return
/mob/living/carbon/resist_restraints()
@@ -965,15 +964,6 @@
I.extinguish() //extinguishes our clothes
..()
-/mob/living/carbon/fakefire(fire_icon = "Generic_mob_burning")
- var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER)
- new_fire_overlay.appearance_flags = RESET_COLOR
- overlays_standing[FIRE_LAYER] = new_fire_overlay
- apply_overlay(FIRE_LAYER)
-
-/mob/living/carbon/fakefireextinguish()
- remove_overlay(FIRE_LAYER)
-
/mob/living/carbon/proc/create_bodyparts()
var/l_arm_index_next = -1
@@ -1424,3 +1414,27 @@
our_splatter.blood_dna_info = get_blood_dna_list()
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
our_splatter.fly_towards(targ, splatter_strength)
+
+/mob/living/carbon/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
+ var/fire_icon = "generic_burning[suffix]"
+
+ if(!GLOB.fire_appearances[fire_icon])
+ var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/onfire.dmi', fire_icon, -FIRE_LAYER)
+ new_fire_overlay.appearance_flags = RESET_COLOR
+ GLOB.fire_appearances[fire_icon] = new_fire_overlay
+
+ if((stacks > 0 && on_fire) || HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE))
+ if(fire_icon == last_icon_state)
+ return last_icon_state
+
+ remove_overlay(FIRE_LAYER)
+ overlays_standing[FIRE_LAYER] = GLOB.fire_appearances[fire_icon]
+ apply_overlay(FIRE_LAYER)
+ return fire_icon
+
+ if(!last_icon_state)
+ return last_icon_state
+
+ remove_overlay(FIRE_LAYER)
+ apply_overlay(FIRE_LAYER)
+ return null
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 483582ed7af..bcedf7a42ca 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -47,7 +47,6 @@
update_fire()
update_body_parts()
-
/mob/living/carbon/update_inv_hands()
remove_overlay(HANDS_LAYER)
if (handcuffed)
@@ -78,16 +77,6 @@
overlays_standing[HANDS_LAYER] = hands
apply_overlay(HANDS_LAYER)
-
-/mob/living/carbon/update_fire(fire_icon = "Generic_mob_burning")
- remove_overlay(FIRE_LAYER)
- if(on_fire || HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE))
- var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER)
- new_fire_overlay.appearance_flags = RESET_COLOR
- overlays_standing[FIRE_LAYER] = new_fire_overlay
-
- apply_overlay(FIRE_LAYER)
-
/mob/living/carbon/update_damage_overlays()
remove_overlay(DAMAGE_LAYER)
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index c12be3b336f..f5d81b956f2 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -96,9 +96,9 @@
if(HAS_TRAIT(src, TRAIT_DUMB))
msg += "[t_He] seem[p_s()] to be clumsy and unable to think.\n"
- if(fire_stacks > 0)
+ if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks))
msg += "[t_He] [t_is] covered in something flammable.\n"
- if(fire_stacks < 0)
+ if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks))
msg += "[t_He] look[p_s()] a little soaked.\n"
if(pulledby?.grab_state)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 740c420cb0b..431981a40f3 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -214,9 +214,9 @@
msg += "[t_He] [t_has] severe cellular damage!\n"
- if(fire_stacks > 0)
+ if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks))
msg += "[t_He] [t_is] covered in something flammable.\n"
- if(fire_stacks < 0)
+ if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks))
msg += "[t_He] look[p_s()] a little soaked.\n"
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 723f5b5f2aa..d43b174e493 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -966,3 +966,97 @@
for(var/obj/item/I in torn_items)
I.take_damage(damage_amount, damage_type, damage_flag, 0)
+
+/**
+ * Used by fire code to damage worn items.
+ *
+ * Arguments:
+ * - delta_time
+ * - times_fired
+ * - stacks: Current amount of firestacks
+ *
+ */
+
+/mob/living/carbon/human/proc/burn_clothing(delta_time, times_fired, stacks)
+ var/list/burning_items = list()
+ var/obscured = check_obscured_slots(TRUE)
+ //HEAD//
+
+ if(glasses && !(obscured & ITEM_SLOT_EYES))
+ burning_items += glasses
+ if(wear_mask && !(obscured & ITEM_SLOT_MASK))
+ burning_items += wear_mask
+ if(wear_neck && !(obscured & ITEM_SLOT_NECK))
+ burning_items += wear_neck
+ if(ears && !(obscured & ITEM_SLOT_EARS))
+ burning_items += ears
+ if(head)
+ burning_items += head
+
+ //CHEST//
+ if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING))
+ burning_items += w_uniform
+ if(wear_suit)
+ burning_items += wear_suit
+
+ //ARMS & HANDS//
+ var/obj/item/clothing/arm_clothes = null
+ if(gloves && !(obscured & ITEM_SLOT_GLOVES))
+ arm_clothes = gloves
+ else if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS)))
+ arm_clothes = wear_suit
+ else if(w_uniform && ((w_uniform.body_parts_covered & HANDS) || (w_uniform.body_parts_covered & ARMS)))
+ arm_clothes = w_uniform
+ if(arm_clothes)
+ burning_items |= arm_clothes
+
+ //LEGS & FEET//
+ var/obj/item/clothing/leg_clothes = null
+ if(shoes && !(obscured & ITEM_SLOT_FEET))
+ leg_clothes = shoes
+ else if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (wear_suit.body_parts_covered & LEGS)))
+ leg_clothes = wear_suit
+ else if(w_uniform && ((w_uniform.body_parts_covered & FEET) || (w_uniform.body_parts_covered & LEGS)))
+ leg_clothes = w_uniform
+ if(leg_clothes)
+ burning_items |= leg_clothes
+
+ for(var/obj/item/burning in burning_items)
+ burning.fire_act((stacks * 25 * delta_time)) //damage taken is reduced to 2% of this value by fire_act()
+
+/mob/living/carbon/human/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
+ var/fire_icon = "generic_burning[suffix]"
+ if(stacks > HUMAN_FIRE_STACK_ICON_NUM)
+ if(dna && dna.species)
+ fire_icon = "[dna.species.fire_overlay][suffix]"
+ else
+ fire_icon = "human_burning[suffix]"
+
+ if(!GLOB.fire_appearances[fire_icon])
+ var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/onfire.dmi', fire_icon, -FIRE_LAYER)
+ new_fire_overlay.appearance_flags = RESET_COLOR
+ GLOB.fire_appearances[fire_icon] = new_fire_overlay
+
+ if((stacks > 0 && on_fire) || HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE))
+ if(fire_icon == last_icon_state)
+ return last_icon_state
+
+ remove_overlay(FIRE_LAYER)
+ overlays_standing[FIRE_LAYER] = GLOB.fire_appearances[fire_icon]
+ apply_overlay(FIRE_LAYER)
+ return fire_icon
+
+ if(!last_icon_state)
+ return last_icon_state
+
+ remove_overlay(FIRE_LAYER)
+ apply_overlay(FIRE_LAYER)
+ return null
+
+/mob/living/carbon/human/on_fire_stack(delta_time, times_fired, datum/status_effect/fire_handler/fire_stacks/fire_handler)
+ SEND_SIGNAL(src, COMSIG_HUMAN_BURNING)
+ burn_clothing(delta_time, times_fired, fire_handler.stacks)
+ var/no_protection = FALSE
+ if(dna && dna.species)
+ no_protection = dna.species.handle_fire(src, delta_time, times_fired, no_protection)
+ fire_handler.harm_human(delta_time, times_fired, no_protection)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 102d3475e36..19ac4a85f51 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -64,7 +64,6 @@
/// What types of mobs are allowed to ride/buckle to this mob
var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot))
var/lastpuke = 0
- var/last_fire_update
var/account_id
var/hardcore_survival_score = 0
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 039d1215d0f..9e7f4fe0693 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -69,10 +69,6 @@ There are several things that need to be remembered:
dna.species.handle_body(src)
..()
-/mob/living/carbon/human/update_fire()
- ..((fire_stacks > HUMAN_FIRE_STACK_ICON_NUM) ? "Standing" : "Generic_mob_burning")
-
-
/* --------------------------------------- */
//For legacy support.
/mob/living/carbon/human/regenerate_icons()
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 6f2e1281e8a..1fea46d37c4 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -154,22 +154,6 @@
/mob/living/carbon/human/get_body_temp_cold_damage_limit()
return dna.species.bodytemp_cold_damage_limit
-///FIRE CODE
-/mob/living/carbon/human/handle_fire(delta_time, times_fired)
- . = ..()
- if(.) //if the mob isn't on fire anymore
- return
-
- if(dna)
- . = dna.species.handle_fire(src, delta_time, times_fired) //do special handling based on the mob's species. TRUE = they are immune to the effects of the fire.
-
- if(!last_fire_update)
- last_fire_update = fire_stacks
- if((fire_stacks > HUMAN_FIRE_STACK_ICON_NUM && last_fire_update <= HUMAN_FIRE_STACK_ICON_NUM) || (fire_stacks <= HUMAN_FIRE_STACK_ICON_NUM && last_fire_update > HUMAN_FIRE_STACK_ICON_NUM))
- last_fire_update = fire_stacks
- update_fire()
-
-
/mob/living/carbon/human/proc/get_thermal_protection()
var/thermal_protection = 0 //Simple check to estimate how protected we are against multiple temperatures
if(wear_suit)
@@ -181,20 +165,8 @@
thermal_protection = round(thermal_protection)
return thermal_protection
-/mob/living/carbon/human/IgniteMob()
- //If have no DNA or can be Ignited, call parent handling to light user
- //If firestacks are high enough
- if(!dna || dna.species.CanIgniteMob(src))
- return ..()
- . = FALSE //No ignition
-
-/mob/living/carbon/human/extinguish_mob()
- if(!dna || !dna.species.extinguish_mob(src))
- last_fire_update = null
- ..()
//END FIRE CODE
-
//This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, CHEST, GROIN, etc. See setup.dm for the full list)
/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to.
var/thermal_protection_flags = 0
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 849920fbcc9..18f381515f2 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -165,7 +165,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/bodytemp_cold_damage_limit = BODYTEMP_COLD_DAMAGE_LIMIT
/// The icon_state of the fire overlay added when sufficently ablaze and standing. see onfire.dmi
- var/fire_overlay = "Standing"
+ var/fire_overlay = "human_burning"
///the species that body parts are surgically compatible with (found in _DEFINES/mobs.dm)
///current acceptable bitfields are HUMAN_BODY, ALIEN_BODY, LARVA_BODY, MONKEY_BODY, or NONE
@@ -1721,77 +1721,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
//////////
/datum/species/proc/handle_fire(mob/living/carbon/human/H, delta_time, times_fired, no_protection = FALSE)
- if(!CanIgniteMob(H))
- return TRUE
- if(H.on_fire)
- SEND_SIGNAL(H, COMSIG_HUMAN_BURNING)
- //the fire tries to damage the exposed clothes and items
- var/list/burning_items = list()
- var/obscured = H.check_obscured_slots(TRUE)
- //HEAD//
-
- if(H.glasses && !(obscured & ITEM_SLOT_EYES))
- burning_items += H.glasses
- if(H.wear_mask && !(obscured & ITEM_SLOT_MASK))
- burning_items += H.wear_mask
- if(H.wear_neck && !(obscured & ITEM_SLOT_NECK))
- burning_items += H.wear_neck
- if(H.ears && !(obscured & ITEM_SLOT_EARS))
- burning_items += H.ears
- if(H.head)
- burning_items += H.head
-
- //CHEST//
- if(H.w_uniform && !(obscured & ITEM_SLOT_ICLOTHING))
- burning_items += H.w_uniform
- if(H.wear_suit)
- burning_items += H.wear_suit
-
- //ARMS & HANDS//
- var/obj/item/clothing/arm_clothes = null
- if(H.gloves && !(obscured & ITEM_SLOT_GLOVES))
- arm_clothes = H.gloves
- else if(H.wear_suit && ((H.wear_suit.body_parts_covered & HANDS) || (H.wear_suit.body_parts_covered & ARMS)))
- arm_clothes = H.wear_suit
- else if(H.w_uniform && ((H.w_uniform.body_parts_covered & HANDS) || (H.w_uniform.body_parts_covered & ARMS)))
- arm_clothes = H.w_uniform
- if(arm_clothes)
- burning_items |= arm_clothes
-
- //LEGS & FEET//
- var/obj/item/clothing/leg_clothes = null
- if(H.shoes && !(obscured & ITEM_SLOT_FEET))
- leg_clothes = H.shoes
- else if(H.wear_suit && ((H.wear_suit.body_parts_covered & FEET) || (H.wear_suit.body_parts_covered & LEGS)))
- leg_clothes = H.wear_suit
- else if(H.w_uniform && ((H.w_uniform.body_parts_covered & FEET) || (H.w_uniform.body_parts_covered & LEGS)))
- leg_clothes = H.w_uniform
- if(leg_clothes)
- burning_items |= leg_clothes
-
- for(var/X in burning_items)
- var/obj/item/I = X
- I.fire_act((H.fire_stacks * 50)) //damage taken is reduced to 2% of this value by fire_act()
-
- var/thermal_protection = H.get_thermal_protection()
-
- if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT && !no_protection)
- return
- if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT && !no_protection)
- H.adjust_bodytemperature(5.5 * delta_time)
- else
- H.adjust_bodytemperature((BODYTEMP_HEATING_MAX + (H.fire_stacks * 12)) * 0.5 * delta_time)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "on_fire", /datum/mood_event/on_fire)
- H.mind?.add_memory(MEMORY_FIRE, list(DETAIL_PROTAGONIST = H), story_value = STORY_VALUE_OKAY)
-
-/datum/species/proc/CanIgniteMob(mob/living/carbon/human/H)
- if(HAS_TRAIT(H, TRAIT_NOFIRE))
- return FALSE
- return TRUE
-
-/datum/species/proc/extinguish_mob(mob/living/carbon/human/H)
- return
-
+ return no_protection
////////////
// Stun //
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index dbf39d46237..2745ff9da7e 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -173,7 +173,7 @@
to_chat(owner, span_notice("You ignite yourself!"))
else
to_chat(owner, span_warning("You try to ignite yourself, but fail!"))
- H.IgniteMob() //firestacks are already there passively
+ H.ignite_mob() //firestacks are already there passively
//Harder to hurt
/datum/species/golem/diamond
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index 40063c834ce..a10d9c76dac 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -54,7 +54,7 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/r_leg/monkey,
BODY_ZONE_CHEST = /obj/item/bodypart/chest/monkey,
)
- fire_overlay = "Monkey_burning"
+ fire_overlay = "monkey_burning"
dust_anim = "dust-m"
gib_anim = "gibbed-m"
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 6afae82ca1d..e79c5bb8178 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -64,7 +64,7 @@
/datum/species/plasmaman/spec_life(mob/living/carbon/human/H, delta_time, times_fired)
var/atmos_sealed = TRUE
- if(CanIgniteMob(H))
+ if(!HAS_TRAIT(H, TRAIT_NOFIRE))
atmos_sealed = FALSE
else if(!isclothing(H.wear_suit) || !(H.wear_suit.clothing_flags & STOPSPRESSUREDAMAGE))
atmos_sealed = FALSE
@@ -99,7 +99,7 @@
H.adjust_fire_stacks(0.25 * delta_time)
if(!H.on_fire && H.fire_stacks > 0)
H.visible_message(span_danger("[H]'s body reacts with the atmosphere and bursts into flames!"),span_userdanger("Your body reacts with the atmosphere and bursts into flame!"))
- H.IgniteMob()
+ H.ignite_mob()
internal_fire = TRUE
else if(H.fire_stacks)
@@ -117,7 +117,6 @@
no_protection = TRUE
. = ..()
-
/datum/species/plasmaman/pre_equip_species_outfit(datum/job/job, mob/living/carbon/human/equipping, visuals_only = FALSE)
if(job.plasmaman_outfit)
equipping.equipOutfit(job.plasmaman_outfit, visuals_only)
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 3dace5221ff..bc2658f6672 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -68,7 +68,7 @@
to_chat(vampire, span_warning("You don't belong here!"))
vampire.adjustFireLoss(10 * delta_time)
vampire.adjust_fire_stacks(3 * delta_time)
- vampire.IgniteMob()
+ vampire.ignite_mob()
/datum/species/vampire/check_species_weakness(obj/item/weapon, mob/living/attacker)
if(istype(weapon, /obj/item/nullrod/whip))
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index fcfd8585513..c40fa5b8b56 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -71,8 +71,6 @@
handle_traits(delta_time, times_fired) // eye, ear, brain damages
handle_status_effects(delta_time, times_fired) //all special effects, stun, knockdown, jitteryness, hallucination, sleeping, etc
- handle_fire(delta_time, times_fired)
-
if(machine)
machine.check_eye(src)
@@ -110,23 +108,6 @@
else // this is a hot place
adjust_bodytemperature(min(min(temp_delta / BODYTEMP_DIVISOR, BODYTEMP_HEATING_MAX) * delta_time, temp_delta))
-/mob/living/proc/handle_fire(delta_time, times_fired)
- if(fire_stacks < 0) //If we've doused ourselves in water to avoid fire, dry off slowly
- set_fire_stacks(min(0, fire_stacks + (0.5 * delta_time))) //So we dry ourselves back to default, nonflammable.
- if(!on_fire)
- return TRUE //the mob is no longer on fire, no need to do the rest.
- if(fire_stacks > 0)
- adjust_fire_stacks(-0.05 * delta_time) //the fire is slowly consumed
- else
- extinguish_mob()
- return TRUE //mob was put out, on_fire = FALSE via extinguish_mob(), no need to update everything down the chain.
- var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment
- if(!G.gases[/datum/gas/oxygen] || G.gases[/datum/gas/oxygen][MOLES] < 1)
- extinguish_mob() //If there's no oxygen in the tile we're on, put out the fire
- return TRUE
- var/turf/location = get_turf(src)
- location.hotspot_expose(700, 25 * delta_time, TRUE)
-
/**
* Get the fullness of the mob
*
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index ec5a6deb475..ffe76c768fa 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -839,7 +839,6 @@
hallucination = 0
heal_overall_damage(INFINITY, INFINITY, INFINITY, null, TRUE) //heal brute and burn dmg on both organic and robotic limbs, and update health right away.
extinguish_mob()
- fire_stacks = 0
set_confusion(0)
set_drowsyness(0)
jitteriness = 0
@@ -1385,27 +1384,28 @@
G.Recall()
to_chat(G, span_holoparasite("Your summoner has changed form!"))
-/mob/living/proc/fakefireextinguish()
- return
-
-/mob/living/proc/fakefire()
- return
-
/mob/living/proc/unfry_mob() //Callback proc to tone down spam from multiple sizzling frying oil dipping.
REMOVE_TRAIT(src, TRAIT_OIL_FRIED, "cooking_oil_react")
//Mobs on Fire
-/mob/living/proc/IgniteMob()
- if(fire_stacks <= 0 || on_fire)
+
+/// Global list that containes cached fire overlays for mobs
+GLOBAL_LIST_EMPTY(fire_appearances)
+
+/mob/living/proc/ignite_mob()
+ if(fire_stacks <= 0)
return FALSE
- on_fire = TRUE
- src.visible_message(span_warning("[src] catches fire!"), \
- span_userdanger("You're set on fire!"))
- firelight_ref = WEAKREF(new /obj/effect/dummy/lighting_obj/moblight/fire(src))
- throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire)
- update_fire()
- SEND_SIGNAL(src, COMSIG_LIVING_IGNITED,src)
- return TRUE
+
+ var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks)
+ if(!fire_status || fire_status.on_fire)
+ return FALSE
+
+ return fire_status.ignite()
+
+/mob/living/proc/update_fire()
+ var/datum/status_effect/fire_handler/fire_handler = has_status_effect(/datum/status_effect/fire_handler)
+ if(fire_handler)
+ fire_handler.update_overlay()
/**
* Extinguish all fire on the mob
@@ -1414,15 +1414,10 @@
* Signals the extinguishing.
*/
/mob/living/proc/extinguish_mob()
- if(!on_fire)
+ var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks)
+ if(!fire_status || !fire_status.on_fire)
return
- on_fire = FALSE
- fire_stacks = min(0, fire_stacks) //Makes sure we don't get rid of negative firestacks.
- QDEL_NULL(firelight_ref)
- clear_alert(ALERT_FIRE)
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "on_fire")
- SEND_SIGNAL(src, COMSIG_LIVING_EXTINGUISHED, src)
- update_fire()
+ remove_status_effect(/datum/status_effect/fire_handler/fire_stacks)
/**
* Adjust the amount of fire stacks on a mob
@@ -1430,10 +1425,19 @@
* This modifies the fire stacks on a mob.
*
* Vars:
- * * add_fire_stacks: int The amount to modify the fire stacks
+ * * stacks: int The amount to modify the fire stacks
+ * * fire_type: type Type of fire status effect that we apply, should be subtype of /datum/status_effect/fire_handler/fire_stacks
*/
-/mob/living/proc/adjust_fire_stacks(add_fire_stacks)
- set_fire_stacks(fire_stacks + add_fire_stacks)
+
+/mob/living/proc/adjust_fire_stacks(stacks, fire_type = /datum/status_effect/fire_handler/fire_stacks)
+ if(stacks < 0)
+ stacks = max(-fire_stacks, stacks)
+ apply_status_effect(fire_type, stacks)
+
+/mob/living/proc/adjust_wet_stacks(stacks, wet_type = /datum/status_effect/fire_handler/wet_stacks)
+ if(stacks < 0)
+ stacks = max(fire_stacks, stacks)
+ apply_status_effect(wet_type, stacks)
/**
* Set the fire stacks on a mob
@@ -1443,38 +1447,95 @@
*
* Vars:
* * stacks: int The amount to set fire_stacks to
+ * * fire_type: type Type of fire status effect that we apply, should be subtype of /datum/status_effect/fire_handler/fire_stacks
+ * * remove_wet_stacks: bool If we remove all wet stacks upon doing this
*/
-/mob/living/proc/set_fire_stacks(stacks)
- fire_stacks = clamp(stacks, -20, 20)
- if(fire_stacks <= 0)
- extinguish_mob()
+/mob/living/proc/set_fire_stacks(stacks, fire_type = /datum/status_effect/fire_handler/fire_stacks, remove_wet_stacks = TRUE)
+ if(stacks < 0) //Shouldn't happen, ever
+ CRASH("set_fire_stacks recieved negative [stacks] fire stacks")
+
+ if(remove_wet_stacks)
+ remove_status_effect(/datum/status_effect/fire_handler/wet_stacks)
+
+ if(stacks == 0)
+ remove_status_effect(fire_type)
+ return
+
+ apply_status_effect(fire_type, stacks, TRUE)
+
+/mob/living/proc/set_wet_stacks(stacks, wet_type = /datum/status_effect/fire_handler/wet_stacks, remove_fire_stacks = TRUE)
+ if(stacks < 0)
+ CRASH("set_wet_stacks recieved negative [stacks] wet stacks")
+
+ if(remove_fire_stacks)
+ remove_status_effect(/datum/status_effect/fire_handler/fire_stacks)
+
+ if(stacks == 0)
+ remove_status_effect(wet_type)
+ return
+
+ apply_status_effect(wet_type, stacks, TRUE)
//Share fire evenly between the two mobs
//Called in MobBump() and Crossed()
-/mob/living/proc/spreadFire(mob/living/L)
- if(!istype(L))
+/mob/living/proc/spreadFire(mob/living/spread_to)
+ if(!istype(spread_to))
return
// can't spread fire to mobs that don't catch on fire
- if(HAS_TRAIT(L, TRAIT_NOFIRE_SPREAD) || HAS_TRAIT(src, TRAIT_NOFIRE_SPREAD))
+ if(HAS_TRAIT(spread_to, TRAIT_NOFIRE_SPREAD) || HAS_TRAIT(src, TRAIT_NOFIRE_SPREAD))
return
- if(on_fire)
- if(L.on_fire) // If they were also on fire
- var/firesplit = (fire_stacks + L.fire_stacks)/2
- set_fire_stacks(firesplit)
- L.set_fire_stacks(firesplit)
- else // If they were not
- set_fire_stacks(fire_stacks / 2)
- L.adjust_fire_stacks(fire_stacks)
- if(L.IgniteMob()) // Ignite them
- log_game("[key_name(src)] bumped into [key_name(L)] and set them on fire")
+ var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks)
+ var/datum/status_effect/fire_handler/fire_stacks/their_fire_status = spread_to.has_status_effect(/datum/status_effect/fire_handler/fire_stacks)
+ if(fire_status && fire_status.on_fire)
+ if(their_fire_status && their_fire_status.on_fire)
+ var/firesplit = (fire_stacks + spread_to.fire_stacks) / 2
+ var/fire_type = (spread_to.fire_stacks > fire_stacks) ? their_fire_status.type : fire_status.type
+ set_fire_stacks(firesplit, fire_type)
+ spread_to.set_fire_stacks(firesplit, fire_type)
+ return
- else if(L.on_fire) // If they were on fire and we were not
- L.set_fire_stacks(L.fire_stacks / 2)
- adjust_fire_stacks(L.fire_stacks)
- IgniteMob() // Ignite us
+ adjust_fire_stacks(-fire_stacks / 2, fire_status.type)
+ spread_to.adjust_fire_stacks(fire_stacks, fire_status.type)
+ if(spread_to.ignite_mob())
+ log_game("[key_name(src)] bumped into [key_name(spread_to)] and set them on fire")
+ return
+
+ if(!their_fire_status || !their_fire_status.on_fire)
+ return
+
+ spread_to.adjust_fire_stacks(-spread_to.fire_stacks / 2, their_fire_status.type)
+ adjust_fire_stacks(spread_to.fire_stacks, their_fire_status.type)
+ ignite_mob()
+
+/**
+ * Sets fire overlay of the mob.
+ *
+ * Vars:
+ * * stacks: Current amount of fire_stacks
+ * * on_fire: If we're lit on fire
+ * * last_icon_state: Holds last fire overlay icon state, used for optimization
+ * * suffix: Suffix for the fire icon state for special fire types
+ *
+ * This should return last_icon_state for the fire status efect
+ */
+
+/mob/living/proc/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
+ return last_icon_state
+
+/**
+ * Handles effects happening when mob is on normal fire
+ *
+ * Vars:
+ * * delta_time
+ * * times_fired
+ * * fire_handler: Current fire status effect that called the proc
+ */
+
+/mob/living/proc/on_fire_stack(delta_time, times_fired, datum/status_effect/fire_handler/fire_stacks/fire_handler)
+ return
//Mobs on Fire end
@@ -1636,9 +1697,6 @@
if(NAMEOF(src, resting))
set_resting(var_value)
. = TRUE
- if(NAMEOF(src, fire_stacks))
- set_fire_stacks(var_value)
- . = TRUE
if(NAMEOF(src, lying_angle))
set_lying_angle(var_value)
. = TRUE
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 48b9fdf8ca0..6cdfddfd067 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -129,7 +129,7 @@
/mob/living/fire_act()
adjust_fire_stacks(3)
- IgniteMob()
+ ignite_mob()
/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE)
if(user == src || anchored || !isturf(user.loc))
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 93c5c3ba72b..2c2895c0097 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -54,6 +54,10 @@
var/last_special = 0 ///Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out.
var/timeofdeath = 0
+ /// Helper vars for quick access to firestacks, these should be updated every time firestacks are adjusted
+ var/on_fire = FALSE
+ var/fire_stacks = 0
+
/**
* Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects.
*
@@ -75,11 +79,6 @@
/// Time of death
var/tod = null
- var/on_fire = FALSE ///The "Are we on fire?" var
- /// Weak reference to the light our fire is causing, if there is one
- var/datum/weakref/firelight_ref
- var/fire_stacks = 0 ///Tracks how many stacks of fire we have on, max is usually 20
-
var/limb_destroyer = 0 //1 Sets AI behavior that allows mobs to target and dismember limbs with their basic attack.
var/mob_size = MOB_SIZE_HUMAN
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index e94cd69739a..80b12659a01 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -241,9 +241,8 @@
if(istype(A, initial(AM.power_type)))
qdel(A)
-/mob/living/silicon/ai/IgniteMob()
- fire_stacks = 0
- . = ..()
+/mob/living/silicon/ai/ignite_mob()
+ return FALSE
/mob/living/silicon/ai/proc/set_core_display_icon(input, client/C)
if(client && !C)
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 066cd3497f5..886703c9b8c 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -61,9 +61,8 @@
src.visible_message(span_warning("The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!"))
. = ..(Proj)
-/mob/living/silicon/pai/IgniteMob()
- fire_stacks = 0
- . = ..()
+/mob/living/silicon/pai/ignite_mob()
+ return FALSE
/mob/living/silicon/pai/proc/take_holo_damage(amount)
emitterhealth = clamp((emitterhealth - amount), -50, emittermaxhealth)
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index 86c75b7ae8e..f881365350f 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -70,23 +70,3 @@
throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/emptycell)
else
throw_alert(ALERT_CHARGE, /atom/movable/screen/alert/nocell)
-
-//Robots on fire
-/mob/living/silicon/robot/handle_fire(delta_time, times_fired)
- . = ..()
- if(.) //if the mob isn't on fire anymore
- return
- if(fire_stacks > 0)
- adjust_fire_stacks(-0.5 * delta_time)
- else
- extinguish_mob()
- return TRUE
-
- //adjustFireLoss(3)
-
-/mob/living/silicon/robot/update_fire()
- var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', "Generic_mob_burning")
- if(on_fire)
- add_overlay(fire_overlay)
- else
- cut_overlay(fire_overlay)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index e0cd26a7607..1b201c068fb 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -983,3 +983,23 @@
/mob/living/silicon/robot/proc/untip_roleplay()
to_chat(src, span_notice("Your frustration has empowered you! You can now right yourself faster!"))
+
+/mob/living/silicon/robot/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
+ var/fire_icon = "generic_burning[suffix]"
+
+ if(!GLOB.fire_appearances[fire_icon])
+ var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/onfire.dmi', fire_icon, -FIRE_LAYER)
+ new_fire_overlay.appearance_flags = RESET_COLOR
+ GLOB.fire_appearances[fire_icon] = new_fire_overlay
+
+ if(stacks && on_fire)
+ if(last_icon_state == fire_icon)
+ return last_icon_state
+ add_overlay(GLOB.fire_appearances[fire_icon])
+ return fire_icon
+
+ if(!last_icon_state)
+ return last_icon_state
+
+ cut_overlay(GLOB.fire_appearances[fire_icon])
+ return null
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 911aeaeeec9..3b3c4d1b997 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -312,7 +312,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
/mob/living/silicon/robot/fire_act()
if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them
- IgniteMob()
+ ignite_mob()
/mob/living/silicon/robot/emp_act(severity)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
index 68828e60abf..fa53d7208ea 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
@@ -49,4 +49,4 @@
var/mob/living/M = AM
if(!hasmatchingsummoner(M) && M != summoner && M.fire_stacks < 7)
M.set_fire_stacks(7)
- M.IgniteMob()
+ M.ignite_mob()
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
index 8725c28a6c1..09a3d31bd18 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
@@ -163,7 +163,7 @@
K.transform = final
living_target.adjustFireLoss(30)
living_target.adjust_fire_stacks(0.2)//Just here for the showmanship
- living_target.IgniteMob()
+ living_target.ignite_mob()
playsound(living_target,'sound/weapons/sear.ogg', 50, TRUE)
addtimer(CALLBACK(src, .proc/AttackRecovery), 5)
return
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
index 57821a0f0c4..4c7c1a583b9 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm
@@ -203,7 +203,7 @@
var/mob/living/L = target
if (istype(L))
L.adjust_fire_stacks(0.1)
- L.IgniteMob()
+ L.ignite_mob()
/obj/projectile/temp/basilisk/icewing
damage = 5
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
index c050407b86f..04c704eb74b 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm
@@ -294,7 +294,7 @@
if(isliving(mover))
var/mob/living/fire_walker = mover
fire_walker.adjust_fire_stacks(5)
- fire_walker.IgniteMob()
+ fire_walker.ignite_mob()
/obj/structure/legionnaire_bonfire/Destroy()
if(myowner != null)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 3c419ae671c..939b00c8454 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -490,10 +490,7 @@
return FALSE
return TRUE
-/mob/living/simple_animal/handle_fire(delta_time, times_fired)
- return TRUE
-
-/mob/living/simple_animal/IgniteMob()
+/mob/living/simple_animal/ignite_mob()
return FALSE
/mob/living/simple_animal/extinguish_mob()
diff --git a/code/modules/mob/mob_update_icons.dm b/code/modules/mob/mob_update_icons.dm
index 91db373f663..69c3d09c834 100644
--- a/code/modules/mob/mob_update_icons.dm
+++ b/code/modules/mob/mob_update_icons.dm
@@ -49,9 +49,6 @@
/mob/proc/update_hair()
return
-/mob/proc/update_fire()
- return
-
/mob/proc/update_inv_gloves()
return
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index acf0cb81730..3faee2819e9 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -211,7 +211,7 @@
if(user.is_holding(I)) //checking if they're holding it in case TK is involved
user.dropItemToGround(I)
user.adjust_fire_stacks(1)
- user.IgniteMob()
+ user.ignite_mob()
return
if(user.is_holding(src)) //no TK shit here.
diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm
index 36dec11944e..ddd667c325e 100644
--- a/code/modules/paperwork/ticketmachine.dm
+++ b/code/modules/paperwork/ticketmachine.dm
@@ -206,7 +206,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
theirticket.fire_act()
user.dropItemToGround(theirticket)
user.adjust_fire_stacks(1)
- user.IgniteMob()
+ user.ignite_mob()
return
/obj/item/ticket_machine_ticket
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 5636cac5203..6247979ae8b 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -396,7 +396,7 @@
span_userdanger("You feel an inner fire as your skin bursts into flames!")
)
burned_mob.adjust_fire_stacks(5)
- burned_mob.IgniteMob()
+ burned_mob.ignite_mob()
return
/obj/singularity/proc/mezzer()
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index f14ab16058e..663d9319804 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -139,7 +139,7 @@
var/mob/living/user = loc
user.visible_message(span_danger("Concentrated plasma discharges from [src] onto [user], burning them!"), span_userdanger("[src] malfunctions, spewing concentrated plasma onto you! It burns!"))
user.adjust_fire_stacks(4)
- user.IgniteMob()
+ user.ignite_mob()
// Can we weld? Plasma cutter does not use charge continuously.
// Amount cannot be defaulted to 1: most of the code specifies 0 in the call.
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index faf66cf394a..bd578b0545b 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -50,7 +50,7 @@
. = ..()
if(iscarbon(target))
var/mob/living/carbon/M = target
- M.IgniteMob()
+ M.ignite_mob()
else if(isturf(target))
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser/wall
diff --git a/code/modules/projectiles/projectile/bullets/_incendiary.dm b/code/modules/projectiles/projectile/bullets/_incendiary.dm
index a28b5f45a95..508cf3d6587 100644
--- a/code/modules/projectiles/projectile/bullets/_incendiary.dm
+++ b/code/modules/projectiles/projectile/bullets/_incendiary.dm
@@ -10,7 +10,7 @@
if(iscarbon(target))
var/mob/living/carbon/M = target
M.adjust_fire_stacks(fire_stacks)
- M.IgniteMob()
+ M.ignite_mob()
/obj/projectile/bullet/incendiary/Move()
. = ..()
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index 8a0b0ca51a5..8a9ce8c7649 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -88,7 +88,7 @@
if(iscarbon(target))
var/mob/living/carbon/M = target
M.adjust_fire_stacks(6)
- M.IgniteMob()
+ M.ignite_mob()
/obj/projectile/bullet/c38/iceblox //see /obj/projectile/temp for the original code
name = ".38 Iceblox bullet"
diff --git a/code/modules/reagents/chemistry/items.dm b/code/modules/reagents/chemistry/items.dm
index 2ef48dbe7e8..8d84e0edae6 100644
--- a/code/modules/reagents/chemistry/items.dm
+++ b/code/modules/reagents/chemistry/items.dm
@@ -242,7 +242,7 @@
user.visible_message(span_notice("[user] snuffs out [src]'s flame."))
/obj/item/burner/attack(mob/living/carbon/M, mob/living/carbon/user)
- if(lit && M.IgniteMob())
+ if(lit && M.ignite_mob())
message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]")
log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]")
return ..()
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 2b483db33f3..503dd19a0ff 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -2441,7 +2441,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
drinker.adjust_bodytemperature(25 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * delta_time)
if (DT_PROB(2.5, delta_time))
drinker.adjust_fire_stacks(1)
- drinker.IgniteMob()
+ drinker.ignite_mob()
..()
/datum/reagent/consumable/ethanol/painkiller
diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
index fc96c9a079e..c32f59995e0 100644
--- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
@@ -221,7 +221,7 @@
return
exposed_mob.adjust_bodytemperature(-reac_volume * TEMPERATURE_DAMAGE_COEFFICIENT, 50)
- exposed_mob.adjust_fire_stacks(-reac_volume / 2)
+ exposed_mob.adjust_fire_stacks(reac_volume / -2)
if(reac_volume >= metabolization_rate)
exposed_mob.extinguish_mob()
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index f80a33da2c2..399bf14fae7 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -395,7 +395,7 @@
/datum/reagent/hellwater/on_mob_life(mob/living/carbon/M, delta_time, times_fired)
M.set_fire_stacks(min(M.fire_stacks + (1.5 * delta_time), 5))
- M.IgniteMob() //Only problem with igniting people is currently the commonly available fire suits make you immune to being on fire
+ M.ignite_mob() //Only problem with igniting people is currently the commonly available fire suits make you immune to being on fire
M.adjustToxLoss(0.5*delta_time, 0)
M.adjustFireLoss(0.5*delta_time, 0) //Hence the other damages... ain't I a bastard?
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2.5*delta_time, 150)
diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
index bc6120804c9..e745a777c4f 100644
--- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
@@ -73,7 +73,7 @@
/datum/reagent/clf3/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)
. = ..()
exposed_mob.adjust_fire_stacks(min(reac_volume/5, 10))
- exposed_mob.IgniteMob()
+ exposed_mob.ignite_mob()
if(!locate(/obj/effect/hotspot) in exposed_mob.loc)
new /obj/effect/hotspot(exposed_mob.loc)
@@ -185,7 +185,7 @@
exposed_mob.adjust_fire_stacks(1)
var/burndmg = max(0.3*exposed_mob.fire_stacks, 0.3)
exposed_mob.adjustFireLoss(burndmg, 0)
- exposed_mob.IgniteMob()
+ exposed_mob.ignite_mob()
/datum/reagent/phlogiston/on_mob_life(mob/living/carbon/metabolizer, delta_time, times_fired)
metabolizer.adjust_fire_stacks(1 * REM * delta_time)
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 21230dc4bdc..c1c93d011c9 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -424,7 +424,7 @@
/datum/reagent/toxin/spore_burning/on_mob_life(mob/living/carbon/M, delta_time, times_fired)
M.adjust_fire_stacks(2 * REM * delta_time)
- M.IgniteMob()
+ M.ignite_mob()
return ..()
/datum/reagent/toxin/chloralhydrate
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index 08385f6e134..bc47036a636 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -140,7 +140,7 @@
to_chat(C, span_userdanger("The divine explosion sears you!"))
C.Paralyze(40)
C.adjust_fire_stacks(5)
- C.IgniteMob()
+ C.ignite_mob()
..()
/datum/chemical_reaction/gunpowder
diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
index 56331313e09..efc557c93c5 100644
--- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
@@ -632,7 +632,7 @@
/datum/status_effect/stabilized/darkblue/tick()
if(owner.fire_stacks > 0 && prob(80))
- owner.adjust_fire_stacks(-1)
+ owner.adjust_wet_stacks(1)
if(owner.fire_stacks <= 0)
to_chat(owner, span_notice("[linked_extract] coats you in a watery goo, extinguishing the flames."))
var/obj/O = owner.get_active_held_item()
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 158a84c807a..f34e1dba11c 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -331,7 +331,7 @@
if(SLIME_ACTIVATE_MINOR)
to_chat(user, span_notice("You activate [src]. You start feeling colder!"))
user.extinguish_mob()
- user.adjust_fire_stacks(-20)
+ user.adjust_wet_stacks(20)
user.reagents.add_reagent(/datum/reagent/consumable/frostoil,6)
user.reagents.add_reagent(/datum/reagent/medicine/regen_jelly,7)
return 100
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index ff6decd92ab..e803bf74d74 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -291,9 +291,9 @@
if(isliving(user))
var/mob/living/caster = user
if(caster.can_cast_magic(antimagic_flags))
- caster.IgniteMob()
+ caster.ignite_mob()
else
- return
+ return
for(var/mob/living/target in targets)
if(target.can_block_magic(antimagic_flags))
to_chat(user, span_warning("The spell can't seem to affect [target]!"))
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index 516c61913a2..308d72f1022 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -201,7 +201,7 @@
playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, TRUE)
to_chat(owner, span_userdanger("You feel an explosion erupt inside your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!"))
owner.adjust_fire_stacks(20)
- owner.IgniteMob()
+ owner.ignite_mob()
owner.adjustFireLoss(25)
organ_flags |= ORGAN_FAILING
diff --git a/code/modules/unit_tests/resist.dm b/code/modules/unit_tests/resist.dm
index 208c9d0cad3..28f6ea88011 100644
--- a/code/modules/unit_tests/resist.dm
+++ b/code/modules/unit_tests/resist.dm
@@ -5,7 +5,7 @@
TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human does not have 0 fire stacks pre-ignition")
human.adjust_fire_stacks(5)
- human.IgniteMob()
+ human.ignite_mob()
TEST_ASSERT_EQUAL(human.fire_stacks, 5, "Human does not have 5 fire stacks pre-resist")
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 07027957214..b1c3cbbd6b3 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -577,7 +577,7 @@
for(var/mob/living/cookedalive as anything in occupants)
if(cookedalive.fire_stacks < 5)
cookedalive.adjust_fire_stacks(1)
- cookedalive.IgniteMob()
+ cookedalive.ignite_mob()
///Displays a special speech bubble when someone inside the mecha speaks
/obj/vehicle/sealed/mecha/proc/display_speech_bubble(datum/source, list/speech_args)
diff --git a/icons/mob/onfire.dmi b/icons/mob/onfire.dmi
index 7768bf0269f..4fa6ec88acd 100644
Binary files a/icons/mob/onfire.dmi and b/icons/mob/onfire.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 4d7da11fe26..2d098f298b9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1121,6 +1121,7 @@
#include "code\datums\status_effects\debuffs\dizziness.dm"
#include "code\datums\status_effects\debuffs\drugginess.dm"
#include "code\datums\status_effects\debuffs\drunk.dm"
+#include "code\datums\status_effects\debuffs\fire_stacks.dm"
#include "code\datums\status_effects\debuffs\speech_debuffs.dm"
#include "code\datums\weather\weather.dm"
#include "code\datums\weather\weather_types\ash_storm.dm"