Files
Cody Brittain 9b5f89a717 Ports Turf Fires (#21659)
Adds turf fires (ported from Baystation, which ported them from
Shiptest), which are now used instead of ZAS fires for fuel decals and
flamethrowers. They also supplement ZAS fires in especially bad atmos
fires.

This makes flamethrowers and related objects more suitable for AOE
denial, rather than cooking the room (including yourself). They still do
the latter, but less so than a gaseous fire.

To make flamethrowers actually useful against them, simple mobs now take
brute damage when on fire.

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| sound/weapons/flamethrower/flamethrower1.ogg |
[rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 |
| sound/weapons/flamethrower/flamethrower2.ogg |
[rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 |
| sound/weapons/flamethrower/flamethrower3.ogg |
[rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 |
| sound/weapons/flamethrower/flamethrower_empty.ogg |
[rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 |
| icons/effects/particles/smoke.dmi |
[rye-rice](https://github.com/shiptest-ss13/Shiptest/commits?author=rye-rice)
(Shiptest) | CC-BY-SA 3.0 |
| icons/effects/turf_fire.dmi | CM Dev (cmss13) | CC-BY-SA 3.0 |
2026-01-31 01:41:11 +00:00

237 lines
7.1 KiB
Plaintext

#define TURF_FIRE_REQUIRED_TEMP (T0C+10)
#define TURF_FIRE_TEMP_BASE (T0C+100)
#define TURF_FIRE_POWER_LOSS_ON_LOW_TEMP 3.5
#define TURF_FIRE_TEMP_INCREMENT_PER_POWER 3
#define TURF_FIRE_POWER_LOSS_BASE 0.5
#define TURF_FIRE_VOLUME 150
#define TURF_FIRE_MAX_POWER 50
#define TURF_FIRE_ENERGY_PER_BURNED_OXY_MOL 12000
#define TURF_FIRE_BURN_RATE_BASE 0.06
#define TURF_FIRE_BURN_RATE_PER_POWER 0.02
#define TURF_FIRE_BURN_MINIMUM_OXYGEN_REQUIRED 0.5
#define TURF_FIRE_BURN_PLAY_SOUND_EFFECT_CHANCE 6
#define TURF_FIRE_STATE_SMALL 1
#define TURF_FIRE_STATE_MEDIUM 2
#define TURF_FIRE_STATE_LARGE 3
#define TURF_FIRE_STATE_MAX 4
/obj/turf_fire
icon = 'icons/effects/turf_fire.dmi'
layer = BELOW_DOOR_LAYER
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/// How much power have we got. This is treated like fuel, be it flamethrower liquid or any random thing you could come up with
var/fire_power = 20
/// If false, it does not interact with atmos.
var/interact_with_atmos = TRUE
/// If false, it will not lose power by itself. Mainly for adminbus events or mapping.
var/passive_loss = TRUE
/// Visual state of the fire. Kept track to not do too many updates.
var/current_fire_state
light_color = COLOR_ORANGE
///All the subtypes are for adminbussery and or mapping
/obj/turf_fire/magical
interact_with_atmos = FALSE
passive_loss = FALSE
color = COLOR_LIGHT_CYAN
/obj/turf_fire/small
fire_power = 10
/obj/turf_fire/small/magical
interact_with_atmos = FALSE
passive_loss = FALSE
color = COLOR_SPRING_GREEN
/obj/turf_fire/inferno
fire_power = 30
/obj/turf_fire/inferno/magical
interact_with_atmos = FALSE
passive_loss = FALSE
color = COLOR_VIOLET
/obj/turf_fire/Initialize(mapload, power, fire_color)
. = ..()
var/turf/open_turf = loc
if(open_turf.turf_fire)
return INITIALIZE_HINT_QDEL
if(fire_color && ((color != fire_color) && isnull(color))) //Take colour from proc unless base colour was already custom
color = fire_color
if(color != null)
set_color(color)
particles = new /particles/smoke/turf_fire
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
open_turf.turf_fire = src
SSturf_fire.fires += src
if(power)
fire_power = min(TURF_FIRE_MAX_POWER, power)
UpdateFireState()
/obj/turf_fire/Destroy()
var/turf/T = loc
T.turf_fire = null
SSturf_fire.fires -= src
return ..()
//Approximates colour. It will not change value or saturation, just hue.
/obj/turf_fire/proc/set_color(_color)
light_color = _color
var/base = rgb2num(COLOR_ORANGE, COLORSPACE_HSV) //Base sprite is red fire, but maybe other fires want to be different
var/target = rgb2num(_color, COLORSPACE_HSV)
var/baseAngle = base[1]
var/targetAngle= target[1]
var/angle = (targetAngle - baseAngle)
filters = filter(type = "color", color = list(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
angle/360,0,0,0), space = FILTER_COLOR_HSV)
color = null //The actual colour should remain white so transform makes sense
/obj/turf_fire/proc/process_waste(seconds_per_tick)
var/total_oxidizer = 0
var/turf/T = loc
var/datum/gas_mixture/env = T.return_air()
for (var/g in env.gas)
if(gas_data.flags[g] & XGM_GAS_OXIDIZER)
total_oxidizer += env.gas[g]
if (total_oxidizer < TURF_FIRE_BURN_MINIMUM_OXYGEN_REQUIRED)
return FALSE
var/burn_rate = (TURF_FIRE_BURN_RATE_BASE * seconds_per_tick) + fire_power * TURF_FIRE_BURN_RATE_PER_POWER
if(burn_rate > total_oxidizer)
burn_rate = total_oxidizer
env.remove_by_flag(XGM_GAS_OXIDIZER, burn_rate)
env.adjust_gas(GAS_CO2, burn_rate)
var/energy_released = burn_rate * TURF_FIRE_ENERGY_PER_BURNED_OXY_MOL
env.add_thermal_energy(energy_released)
SSair.mark_for_update(T)
return TRUE
/obj/turf_fire/process(seconds_per_tick)
var/turf/simulated/T = get_turf(src)
if(!T || T.density)
qdel(src)
return
if(prob(TURF_FIRE_BURN_PLAY_SOUND_EFFECT_CHANCE))
playsound(T, SFX_FIRE, 40, TRUE)
if(T.hotspot) //If we have an active hotspot, let it do the damage instead and lets not loose power
return
if(interact_with_atmos && !process_waste(seconds_per_tick))
qdel(src)
return
if(passive_loss)
if(T.air?.temperature < TURF_FIRE_REQUIRED_TEMP)
fire_power -= TURF_FIRE_POWER_LOSS_ON_LOW_TEMP * seconds_per_tick
fire_power -= TURF_FIRE_POWER_LOSS_BASE * seconds_per_tick
if(fire_power <= 0)
qdel(src)
return
var/effective_temperature = TURF_FIRE_TEMP_BASE + (TURF_FIRE_TEMP_INCREMENT_PER_POWER * fire_power)
T.hotspot_expose(effective_temperature)
//Nearby turfs may also trigger a fire (will only start fires if there's fuel, currently)
//Guaranteed fire spread in the last tick
if(prob(50 + fire_power) || fire_power == 1)
for(var/direction in GLOB.cardinals)
var/turf/simulated/other_tile = get_step(T, direction)
// Exit the loop early if the checked tile is not a valid direction for the fire to spread.
if(!istype(other_tile) || (T.open_directions & direction) || other_tile.hotspot || other_tile.turf_fire) //Grab all valid bordering tiles
continue
other_tile.hotspot_expose(effective_temperature)
for(var/atom/movable/burning_atom as anything in T)
burning_atom.fire_act(exposed_temperature = effective_temperature, exposed_volume = TURF_FIRE_VOLUME)
UpdateFireState()
if(!interact_with_atmos || prob(fire_power) || !istype(T, /turf/simulated/floor))
return
var/turf/simulated/floor/F = T
F.burn_tile(effective_temperature)
/obj/turf_fire/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(!istype(arrived))
return
var/turf/T = loc
if(T.hotspot) //If we have an active hotspot, let it do the damage instead
return
arrived.fire_act(exposed_temperature = TURF_FIRE_TEMP_BASE + (TURF_FIRE_TEMP_INCREMENT_PER_POWER*fire_power), exposed_volume = TURF_FIRE_VOLUME)
return
/obj/turf_fire/proc/AddPower(power)
fire_power = min(TURF_FIRE_MAX_POWER, fire_power + power)
UpdateFireState()
/obj/turf_fire/proc/UpdateFireState()
var/new_state
switch(fire_power)
if(0 to 10)
new_state = TURF_FIRE_STATE_SMALL
if(11 to 24)
new_state = TURF_FIRE_STATE_MEDIUM
if(25 to 39)
new_state = TURF_FIRE_STATE_LARGE
if(40 to INFINITY)
new_state = TURF_FIRE_STATE_MAX
if(new_state == current_fire_state)
return
current_fire_state = new_state
switch(current_fire_state)
if(TURF_FIRE_STATE_SMALL)
icon_state = "small"
set_light(1.5, 0.5)
if(TURF_FIRE_STATE_MEDIUM)
icon_state = "medium"
set_light(2, 0.5)
if(TURF_FIRE_STATE_LARGE)
icon_state = "big"
set_light(2, 0.6)
if(TURF_FIRE_STATE_MAX)
icon_state = "max"
set_light(3, 0.7)
#undef TURF_FIRE_REQUIRED_TEMP
#undef TURF_FIRE_TEMP_BASE
#undef TURF_FIRE_POWER_LOSS_ON_LOW_TEMP
#undef TURF_FIRE_TEMP_INCREMENT_PER_POWER
#undef TURF_FIRE_VOLUME
#undef TURF_FIRE_MAX_POWER
#undef TURF_FIRE_ENERGY_PER_BURNED_OXY_MOL
#undef TURF_FIRE_BURN_RATE_BASE
#undef TURF_FIRE_BURN_RATE_PER_POWER
#undef TURF_FIRE_BURN_MINIMUM_OXYGEN_REQUIRED
#undef TURF_FIRE_BURN_PLAY_SOUND_EFFECT_CHANCE
#undef TURF_FIRE_STATE_SMALL
#undef TURF_FIRE_STATE_MEDIUM
#undef TURF_FIRE_STATE_LARGE
#undef TURF_FIRE_STATE_MAX