Files
Bubberstation/code/datums/components/thermite.dm
MrMelbert 9007190a74 Audits a bunch of calls to receive_damage, replacing them with apply_damage (#88205)
## About The Pull Request

Looks through calls to `receive_damage` and replaces them with calls to
`apply_damage`

`receive_damage` is a gross to use internal proc that doesn't take into
account physiology (damage modifiers) or even update the mob's sprite
when taking damage

It should be avoided many uses - `apply_damage`, in fact, can take a
bodypart as a target, and is overall a lot easier and more ergonomic to
use.

"So what are valid uses of it?"
- Apply damage itself, and similar direct-damage procs 
- Ensuring you deal an exact amount of damage to a bodypart
- Damaging a limb with no owner

## Changelog

🆑 Melbert
refactor: A ton of things now use the more correct method of applying
damage to you. Which means they will correctly factor in damage
modifiers and are less likely to break your sprite. Some examples
include embedded objects jostling around, chiropractice, and tackling a
wall. Report any oddities, such as extreme damage or bodyparts being
wrongly affected.
fix: Having acid splashed on your face may now disfigure you and make
you bald, as it once did three years ago.
fix: Itchy heretic trauma now better checks if the bodypart is covered
or not before determining if you should itch.
fix: "Repair Puncture" logs no longer mistakenly report you are
"Incising burned flesh"
/🆑
2024-12-04 09:00:15 +13:00

209 lines
6.7 KiB
Plaintext

/datum/component/thermite
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// Amount of thermite on parent
var/amount
/// Amount of thermite required to burn through parent
var/burn_require
/// The thermite overlay
var/thermite_overlay
/// Default thermite overlay, do not touch
var/static/mutable_appearance/default_thermite_overlay = mutable_appearance('icons/effects/effects.dmi', "thermite")
/// Callback related to burning, stored so the timer can be easily reset without losing the user
var/datum/callback/burn_callback
/// The timer for burning parent, calls burn_callback when done
var/burn_timer
/// The thermite fire overlay
var/obj/effect/overlay/thermite/fakefire
/// Blacklist of turfs that cannot have thermite on it
var/static/list/blacklist = typecacheof(list(
/turf/open/lava,
/turf/open/space,
/turf/open/water,
/turf/open/chasm,
))
/// List of turfs that are immune to thermite
var/static/list/immunelist = typecacheof(list(
/turf/closed/wall/mineral/diamond,
/turf/closed/indestructible,
/turf/open/indestructible,
))
/// List of turfs that take extra thermite to burn through
var/static/list/resistlist = typecacheof(list(
/turf/closed/wall/r_wall,
))
/datum/component/thermite/Initialize(amount = 50, thermite_overlay = default_thermite_overlay)
if(!isturf(parent))
return COMPONENT_INCOMPATIBLE
//not actually incompatible, but not valid
if(blacklist[parent.type])
qdel(src)
return
if(immunelist[parent.type])
src.amount = 0 //Yeah the overlay can still go on it and be cleaned but you arent burning down a diamond wall
else
src.amount = amount
if(resistlist[parent.type])
burn_require = 50
else
burn_require = 30
src.thermite_overlay = thermite_overlay
/datum/component/thermite/Destroy()
thermite_overlay = null
if(burn_timer)
deltimer(burn_timer)
burn_timer = null
if(burn_callback)
burn_callback = null
if(fakefire)
QDEL_NULL(fakefire)
return ..()
/datum/component/thermite/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(attackby_react))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, PROC_REF(on_fire_act))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(parent_qdeleting)) //probably necessary because turfs are wack
var/turf/turf_parent = parent
turf_parent.update_appearance()
/datum/component/thermite/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_ATTACKBY,
COMSIG_ATOM_ATTACK_HAND,
COMSIG_ATOM_EXAMINE,
COMSIG_ATOM_FIRE_ACT,
COMSIG_ATOM_UPDATE_OVERLAYS,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_QDELETING,
))
var/turf/turf_parent = parent
turf_parent.update_appearance()
/datum/component/thermite/InheritComponent(datum/component/thermite/new_comp, i_am_original, amount)
if(!i_am_original)
return
src.amount += amount
if(burn_timer) // prevent people from skipping a longer timer
deltimer(burn_timer)
burn_timer = addtimer(burn_callback, min(amount * 0.35 SECONDS, 20 SECONDS), TIMER_STOPPABLE)
/// Alerts the user that this turf is, in fact, covered with thermite.
/datum/component/thermite/proc/on_examine(turf/source, mob/user, list/examine_list)
SIGNAL_HANDLER
examine_list += span_warning("[source.p_Theyre()] covered in thermite.")
/// Used to maintain the thermite overlay on the parent [/turf].
/datum/component/thermite/proc/on_update_overlays(turf/parent_turf, list/overlays)
SIGNAL_HANDLER
if(thermite_overlay)
overlays += thermite_overlay
/**
* Used to begin the thermite burning process
*
* Arguments:
* * mob/user - The user igniting the thermite
*/
/datum/component/thermite/proc/thermite_melt(mob/user)
var/turf/parent_turf = parent
playsound(parent_turf, 'sound/items/tools/welder.ogg', 100, TRUE)
fakefire = new(parent_turf)
burn_callback = CALLBACK(src, PROC_REF(burn_parent), user)
burn_timer = addtimer(burn_callback, min(amount * 0.35 SECONDS, 20 SECONDS), TIMER_STOPPABLE)
//unregister everything related to burning
UnregisterSignal(parent, list(COMSIG_COMPONENT_CLEAN_ACT, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_FIRE_ACT))
/**
* Used to actually melt parent
*
* Arguments:
* * mob/user - The user that ignited the thermite
*/
/datum/component/thermite/proc/burn_parent(mob/user)
var/turf/parent_turf = parent
if(fakefire)
QDEL_NULL(fakefire)
if(user)
parent_turf.add_hiddenprint(user)
if(amount >= burn_require)
parent_turf = parent_turf.Melt()
parent_turf.burn_tile()
burn_timer = null
qdel(src)
/**
* Wash reaction, used to clean off thermite from parent
*/
/datum/component/thermite/proc/clean_react(datum/source, strength)
SIGNAL_HANDLER
//Thermite is just some loose powder, you could probably clean it with your hands
qdel(src)
return COMPONENT_CLEANED
/**
* fire_act reaction, has to be the correct temperature
*
* Arguments:
* * datum/source - The source of the flame
* * exposed_temperature - The temperature of the flame hitting the thermite
* * exposed_volume - The volume of the flame
*/
/datum/component/thermite/proc/on_fire_act(datum/source, exposed_temperature, exposed_volume)
SIGNAL_HANDLER
// This is roughly the real life requirement to ignite thermite
// (honestly not really sure what the point of this is, considering a god damn lighter can ignite this)
if(exposed_temperature >= 1922)
thermite_melt()
/// Handles searing the hand of anyone who tries to touch parent without protection, while burning
/datum/component/thermite/proc/on_attack_hand(atom/source, mob/living/carbon/user)
SIGNAL_HANDLER
//not burning
if(!fakefire)
return NONE
if(!iscarbon(user) || user.can_touch_burning(source))
return NONE
user.apply_damage(5, BURN, user.get_active_hand())
to_chat(user, span_userdanger("The ignited thermite on \the [source] burns your hand!"))
INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, emote), "scream")
playsound(source, SFX_SEAR, 50, TRUE)
return COMPONENT_CANCEL_ATTACK_CHAIN
/**
* attackby reaction, ignites the thermite if its a flame creating object
*
* Arguments:
* * datum/source - The source of the attack
* * obj/item/thing - Item being attacked by
* * mob/user - The user behind the attack
* * params - params
*/
/datum/component/thermite/proc/attackby_react(datum/source, obj/item/thing, mob/user, params)
SIGNAL_HANDLER
if(thing.get_temperature() >= FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
thermite_melt(user)
/// Signal handler for COMSIG_QDELETING, necessary because turfs can be weird with qdel()
/datum/component/thermite/proc/parent_qdeleting(datum/source)
SIGNAL_HANDLER
if(!QDELING(src))
qdel(src)