Rewrites plasma rigging code to be much more flexible and dynamic, and cover more cases (#94861)

## About The Pull Request
Instead of being snowflake interactions on objects themselves, rigging
things with plasma or welding fuel is now handled on reagents' side via
``on_spark_act``, allowing each reagent to decide how they want to
handle exposure to electric currents or violent heating.
- Obviously plasma and welding fuel cause explosions, but former gains
additional power from electric current being passed through it (same
math as powercells), while latter doesn't explode unless in an enclosed
space, or a high enough current/temperature has been reached, and merely
creates a hotspot instead.
- Napalm and phlogiston create hotspots, or (try to) damage the holder
if they're enclosed
- Sorium, liquid dark matter, TATP, nitroglycerin and flash/smoke/sonic
powders trigger their usual detonation effects, albeit slightly weaker.
- Teslium creates a ZAP, with additional power for a BIG ZAP if it has
been triggered by electric current.
- RDX becomes spicier with temperature and current, similarly to how it
becomes more dangerous when mixed with LE or teslium.
- Gunpowder produces a delayed explosion effect (unless its hot or the
current is high enough, in which case its instant) and causes sparks as
the message claims it does (normal gunpowder explosions also do that)

Plasma and gunpowder rigged items can now be controlled using
stabilizing agent - in its presence, plasma will not explode unless the
used charge is higher than 0.2% of a standard cell's per unit of agent,
while gunpowder will instead spend 0.1 + (charge / 10% of a standard
cell's charge) units of agent to delay its detonation.

Smoke (powder) now produces a visible message and slightly damages lungs
of whoever it reacted inside of, if the container was a mob.
The numbers on plasma/welding fuel rigs should be ***roughly*** the
same. Also lighters can now be rigged in a fashion similar to welding
tools.

Also it turns out we broke light rigging at some point, so now it works
again.

## Why It's Good For The Game

This makes the system much more flexible and allows players to make more
creative IEDs, expanding upon the sandbox aspect of the game. Also I
have an upcoming project bringing IEDs (kind of, in a new form) back,
which is why I wrote this in the first place (but decided to atomize the
PRs).
As for smoke damage, it feels weird to not have any tells when someone
suddenly starts hacking up volumetric amounts of smoke, it makes sense
that they'd struggle a bit after coughing a few dozen cubic meters of
thick vape clouds.

## Changelog
🆑
add: Multiple new reagents such as napalm, sorium, TATP,
smoke/sonic/flash powders and other explosives can now be used to rig
cells or tools.
add: Plasma and gunpowder rigged items can now have controlled current
requirement/delay by adding some stabilizing agent alongside the main
reagent.
balance: Creating chemical smoke inside of a mob now makes them
violently cough it up, damaging their lungs a bit.
fix: Chemically-rigged lightbulbs now once again explode.
refactor: Refactored how power cells, welding tools, lightbulbs,
cigarettes (and now, lighters) handle being rigged with chemicals.
/🆑
This commit is contained in:
SmArtKar
2026-01-21 19:21:06 +00:00
committed by GitHub
parent 61f6e7d22c
commit c74f104c89
24 changed files with 374 additions and 140 deletions
+9
View File
@@ -231,6 +231,7 @@
if(LIGHT_BROKEN,LIGHT_BURNED,LIGHT_EMPTY)
on = FALSE
low_power_mode = FALSE
if(on)
var/brightness_set = brightness
var/power_set = bulb_power
@@ -239,6 +240,10 @@
color_set = color
if(reagents || !is_full_charge())
START_PROCESSING(SSmachines, src)
if (reagents.spark_act(active_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE)
message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.")
qdel(src)
return
var/area/local_area = get_room_area()
if (flickering)
brightness_set = brightness * bulb_low_power_brightness_mul
@@ -274,6 +279,10 @@
else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE * SSMACHINES_DT) && !turned_off())
use_power = IDLE_POWER_USE
low_power_mode = TRUE
if (reagents?.spark_act(idle_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE)
message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.")
qdel(src)
return
START_PROCESSING(SSmachines, src)
else
use_power = IDLE_POWER_USE
+31 -33
View File
@@ -18,9 +18,7 @@
var/rating_base = STANDARD_CELL_CHARGE
///Maximum charge in cell units
var/maxcharge = STANDARD_CELL_CHARGE
///If the cell has been booby-trapped by injecting it with plasma. Chance on use() to explode.
var/rigged = FALSE
///If the power cell was damaged by an explosion, chance for it to become corrupted and function the same as rigged.
///If the power cell was damaged by an explosion, chance for it to become corrupted and function the same as if it was rigged with plasma.
var/corrupted = FALSE
///How much power is given per second in a recharger.
var/chargerate = STANDARD_CELL_RATE * 0.05
@@ -43,7 +41,7 @@
/obj/item/stock_parts/power_store/get_save_vars()
. = ..()
. += NAMEOF(src, charge)
. += NAMEOF(src, rigged)
. += NAMEOF(src, corrupted)
return .
/obj/item/stock_parts/power_store/Initialize(mapload, override_maxcharge)
@@ -107,10 +105,6 @@
return .
/obj/item/stock_parts/power_store/create_reagents(max_vol, flags)
. = ..()
RegisterSignal(reagents, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(on_reagent_change))
/obj/item/stock_parts/power_store/update_overlays()
. = ..()
if(grown_battery)
@@ -163,8 +157,7 @@
/// Returns: The power used from the cell in joules.
/obj/item/stock_parts/power_store/use(used, force = FALSE)
var/power_used = min(used, charge)
if(rigged && power_used > 0)
explode()
if(power_used > 0 && try_explode())
return 0 // The cell decided to explode so we won't be able to use it.
if(!force && charge < used)
return 0
@@ -180,8 +173,8 @@
/obj/item/stock_parts/power_store/proc/give(amount)
var/power_used = min(maxcharge-charge,amount)
charge += power_used
if(rigged && amount > 0)
explode()
if (amount)
try_explode()
return power_used
/**
@@ -193,46 +186,51 @@
/obj/item/stock_parts/power_store/proc/change(amount)
var/energy_used = clamp(amount, -charge, maxcharge - charge)
charge += energy_used
if(rigged && energy_used)
explode()
if(energy_used)
try_explode()
return energy_used
/obj/item/stock_parts/power_store/examine(mob/user)
. = ..()
if(rigged)
if(corrupted)
. += span_danger("This [name] seems to be faulty!")
else if(!isnull(charge_light_type))
. += "The charge meter reads [CEILING(percent(), 0.1)]%." //so it doesn't say 0% charge when the overlay indicates it still has charge
/obj/item/stock_parts/power_store/proc/on_reagent_change(datum/reagents/holder)
SIGNAL_HANDLER
/obj/item/stock_parts/power_store/proc/try_explode(max_charge = FALSE)
var/check_charge = charge
if (max_charge)
check_charge = maxcharge
rigged = corrupted || !!holder.has_reagent(/datum/reagent/toxin/plasma, 5) //has_reagent returns the reagent datum
if(!check_charge)
return FALSE
/obj/item/stock_parts/power_store/proc/explode()
if(!charge)
return
var/range_devastation = -1
var/range_heavy = round(sqrt(charge / (3.6 * rating_base)))
var/range_light = round(sqrt(charge / (0.9 * rating_base)))
var/range_flash = range_light
if (!corrupted)
if (!(reagents?.spark_act(check_charge, TRUE) & SPARK_ACT_DESTRUCTIVE))
return FALSE
message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a rigged power cell explosion at [AREACOORD(loc)].")
usr?.log_message("triggered a rigged power cell explosion", LOG_GAME)
usr?.log_message("triggered a rigged power cell explosion", LOG_VICTIM, log_globally = FALSE)
qdel(src)
return TRUE
var/range_heavy = round(sqrt(check_charge / (3.6 * rating_base)))
var/range_light = round(sqrt(check_charge / (0.9 * rating_base)))
if(!range_light)
rigged = FALSE
corrupt()
return
return FALSE
message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a rigged/corrupted power cell explosion at [AREACOORD(loc)].")
usr?.log_message("triggered a rigged/corrupted power cell explosion", LOG_GAME)
usr?.log_message("triggered a rigged/corrupted power cell explosion", LOG_VICTIM, log_globally = FALSE)
explosion(src, devastation_range = range_devastation, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_flash)
message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a corrupted power cell explosion at [AREACOORD(loc)].")
usr?.log_message("triggered a corrupted power cell explosion", LOG_GAME)
usr?.log_message("triggered a corrupted power cell explosion", LOG_VICTIM, log_globally = FALSE)
explosion(src, devastation_range = -1, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_light)
qdel(src)
return TRUE
/obj/item/stock_parts/power_store/proc/corrupt(force)
charge /= 2
maxcharge = max(maxcharge/2, chargerate)
if (force || prob(10))
rigged = TRUE //broken batterys are dangerous
corrupted = TRUE
/obj/item/stock_parts/power_store/emp_act(severity)