mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
840 lines
28 KiB
Plaintext
840 lines
28 KiB
Plaintext
/*
|
|
~~~~~~~~~~~~~~~~~~ CREATING FLAMMABLE OBJECTS ~~~~~~~~~~~~~~~~~~
|
|
To make your /obj flammable:
|
|
- Set "flammable = TRUE".
|
|
- Ensure "w_class" and "w_type" are defined for the object or its parent.
|
|
- Note: defining "w_type" does NOT automatically make it recyclable, it just adjusts the sorting logic.
|
|
|
|
To make your /turf flammable:
|
|
- Set "flammable = TRUE"
|
|
- Assign a thermal_material as defined in the THERMAL MATERIALS section:
|
|
ie. thermal_material = new/datum/thermal_material/wood()
|
|
- Define a thermal_mass (thermal_mass = 5 is used for wood floors)
|
|
*/
|
|
|
|
///////////////////////////////////////////////
|
|
// ZAS SETTINGS
|
|
///////////////////////////////////////////////
|
|
|
|
///Subjective exponent for the amount of heat generated by objects burning per tick.
|
|
var/ZAS_heat_multiplier = zas_settings.Get(/datum/ZAS_Setting/fire_heat_generation)
|
|
///Subjective multiplier for the amount of mass consumed by objects burning per tick.
|
|
var/ZAS_mass_consumption_multiplier = zas_settings.Get(/datum/ZAS_Setting/fire_mass_consumption)
|
|
///Subjective multiplier for the amount of oxygen consumed by objects burning per tick.
|
|
var/ZAS_oxygen_consumption_multiplier = zas_settings.Get(/datum/ZAS_Setting/fire_oxygen_consumption)
|
|
///Percent chance for fire to spread from tile to tile.
|
|
var/ZAS_fire_spread_chance = zas_settings.Get(/datum/ZAS_Setting/fire_spread_rate)
|
|
///Ratio of air removed and combusted per tick.
|
|
var/ZAS_air_consumption_rate = zas_settings.Get(/datum/ZAS_Setting/fire_consumption_rate)
|
|
///Multiplied by the equation for firelevel, affects mainly the extingiushing of fires.
|
|
var/ZAS_firelevel_multiplier = zas_settings.Get(/datum/ZAS_Setting/fire_firelevel_multiplier)
|
|
///The energy in joule released when burning one mol of a burnable substance.
|
|
var/ZAS_fuel_energy_release_rate = zas_settings.Get(/datum/ZAS_Setting/fire_fuel_energy_release)
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// THERMAL MATERIALS
|
|
///////////////////////////////////////////////
|
|
/datum/thermal_material
|
|
var/autoignition_temperature
|
|
var/heating_value
|
|
var/molecular_weight
|
|
var/fuel_ox_ratio
|
|
var/flame_temp
|
|
|
|
/datum/thermal_material/wood
|
|
autoignition_temperature = AUTOIGNITION_WOOD
|
|
heating_value = HHV_WOOD
|
|
molecular_weight = MOLECULAR_WEIGHT_WOOD
|
|
fuel_ox_ratio = FUEL_OX_RATIO_WOOD
|
|
flame_temp = FLAME_TEMPERATURE_WOOD
|
|
/datum/thermal_material/plastic
|
|
autoignition_temperature = AUTOIGNITION_PLASTIC
|
|
heating_value = HHV_PLASTIC
|
|
molecular_weight = MOLECULAR_WEIGHT_PLASTIC
|
|
fuel_ox_ratio = FUEL_OX_RATIO_PLASTIC
|
|
flame_temp = FLAME_TEMPERATURE_PLASTIC
|
|
/datum/thermal_material/fabric
|
|
autoignition_temperature = AUTOIGNITION_FABRIC
|
|
heating_value = HHV_FABRIC
|
|
molecular_weight = MOLECULAR_WEIGHT_FABRIC
|
|
fuel_ox_ratio = FUEL_OX_RATIO_FABRIC
|
|
flame_temp = FLAME_TEMPERATURE_FABRIC
|
|
/datum/thermal_material/wax
|
|
autoignition_temperature = AUTOIGNITION_WAX
|
|
heating_value = HHV_WAX
|
|
molecular_weight = MOLECULAR_WEIGHT_WAX
|
|
fuel_ox_ratio = FUEL_OX_RATIO_WAX
|
|
flame_temp = FLAME_TEMPERATURE_WAX
|
|
/datum/thermal_material/biological
|
|
autoignition_temperature = AUTOIGNITION_BIOLOGICAL
|
|
heating_value = HHV_BIOLOGICAL
|
|
molecular_weight = MOLECULAR_WEIGHT_BIOLOGICAL
|
|
fuel_ox_ratio = FUEL_OX_RATIO_BIOLOGICAL
|
|
flame_temp = FLAME_TEMPERATURE_BIOLOGICAL
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// COMBUSTION
|
|
///////////////////////////////////////////////
|
|
/atom
|
|
var/on_fire = 0
|
|
var/flammable = FALSE
|
|
var/autoignition_temperature //inherited from thermal_material unless defined otherwise
|
|
var/initial_thermal_mass
|
|
var/thermal_mass = 0 //VERY loose estimate of mass in kg
|
|
var/datum/thermal_material/thermal_material //contains the material properties of the item for burning, if applicable
|
|
var/fire_protection //duration that something stays extinguished
|
|
var/burntime = 0
|
|
|
|
var/melt_temperature = 0 //unused
|
|
var/molten = 0 //unused
|
|
|
|
var/fire_dmi = 'icons/effects/fire.dmi'
|
|
var/fire_sprite = "fire"
|
|
var/fire_overlay = null
|
|
|
|
var/atom/movable/firelightdummy/firelightdummy
|
|
|
|
/atom/movable/New()
|
|
. = ..()
|
|
if(flammable)
|
|
switch(w_type)
|
|
if(RECYK_WOOD, RECYK_CARDBOARD)
|
|
thermal_material = new/datum/thermal_material/wood()
|
|
if(RECYK_PLASTIC, RECYK_ELECTRONIC, RECYK_MISC, NOT_RECYCLABLE)
|
|
thermal_material = new/datum/thermal_material/plastic()
|
|
if(RECYK_FABRIC)
|
|
thermal_material = new/datum/thermal_material/fabric()
|
|
if(RECYK_WAX)
|
|
thermal_material = new/datum/thermal_material/wax()
|
|
if(RECYK_BIOLOGICAL)
|
|
thermal_material = new/datum/thermal_material/biological()
|
|
if(!thermal_material)
|
|
flammable = FALSE
|
|
//uncomment the following line if you want to identify any improperly-configured flammable items (may spam the logs).
|
|
//warning("[src] was defined as flammable but was missing a 'w_type' definition; [src] marked as non-flammable for this round.")
|
|
return
|
|
if(!autoignition_temperature)
|
|
autoignition_temperature = thermal_material.autoignition_temperature
|
|
fire_protection = world.time
|
|
|
|
/atom/movable/firelightdummy
|
|
gender = PLURAL
|
|
name = "fire"
|
|
mouse_opacity = 0
|
|
vis_flags = VIS_INHERIT_ID
|
|
light_color = LIGHT_COLOR_FIRE
|
|
|
|
/atom/movable/firelightdummy/New()
|
|
.=..()
|
|
set_light(2,2)
|
|
|
|
/atom/proc/melt() //unused
|
|
return
|
|
|
|
/atom/proc/solidify() //unused
|
|
return
|
|
|
|
/atom/proc/ashtype()
|
|
return /obj/effect/decal/cleanable/ash
|
|
|
|
/atom/proc/useThermalMass(var/used_mass)
|
|
thermal_mass -= used_mass
|
|
|
|
/atom/proc/genSmoke(var/oxy,var/temp,var/turf/where,var/force_smoke = FALSE)
|
|
if(!force_smoke)
|
|
if(prob(clamp(lerp(temp,T20C,T0C + 1000,96,100),96,100))) //4% chance of smoke at 20C, 0% at 1000C
|
|
return FALSE
|
|
var/smoke_density = clamp(5 * ((MINOXY2BURN/oxy) ** 2),1,5)
|
|
var/datum/effect/system/smoke_spread/bad/smoke = new
|
|
smoke.set_up(smoke_density,0,where)
|
|
smoke.time_to_live = 10 SECONDS
|
|
smoke.start()
|
|
|
|
/atom/proc/check_fire_protection()
|
|
if(fire_protection >= world.time)
|
|
return TRUE
|
|
|
|
/atom/proc/flammable_reagent_check()
|
|
if(reagents?.total_volume)
|
|
return TRUE
|
|
|
|
/**
|
|
* Burns solid objects and produces heat.
|
|
*
|
|
* Called on every obj/effect/fire/process().
|
|
* Energy is taken from burning atoms and delivered to the obj/effect/fire at the atom's location.
|
|
* Outputs heat produced (kJ), Oxygen consumed (mol), CO2 produced (mol), and max temperature (K) of the burning object.
|
|
*/
|
|
/atom/proc/burnSolidFuel()
|
|
//Don't burn the container until all reagents have been depleted via burnLiquidFuel().
|
|
if(flammable_reagent_check())
|
|
return
|
|
|
|
if(!flammable)
|
|
extinguish()
|
|
return
|
|
|
|
//Setup
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
extinguish()
|
|
return
|
|
var/datum/thermal_material/material = src.thermal_material
|
|
var/datum/gas_mixture/air = T.return_air()
|
|
var/oxy_ratio = air.molar_ratio(GAS_OXYGEN)
|
|
var/temperature = air.return_temperature()
|
|
var/delta_t
|
|
var/heat_out = 0 //J
|
|
var/oxy_used = 0 //mols
|
|
var/co2_prod = 0 //mols
|
|
|
|
|
|
//Check if a fire is present at the current location.
|
|
var/in_fire = FALSE
|
|
if(locate(/obj/effect/fire) in T)
|
|
in_fire = TRUE
|
|
|
|
burntime += 1 SECONDS
|
|
|
|
//Rate at which energy is consumed from the burning atom and delivered to the fire.
|
|
//Provides the "heat" and "oxygen" portions of the fire triangle.
|
|
var/burnrate = (oxy_ratio/(MINOXY2BURN + rand(-2,2)*0.01)) * (temperature/T20C) //burnrate ~ 1 for standard air
|
|
if(burnrate < 0.1)
|
|
extinguish()
|
|
return
|
|
|
|
var/delta_m = 0.20 * burnrate * ZAS_mass_consumption_multiplier
|
|
useThermalMass(delta_m)
|
|
genSmoke(oxy_ratio,temperature,T)
|
|
|
|
//Change in internal energy = energy produced by combustion (assuming perfect combustion).
|
|
heat_out = material.heating_value * delta_m
|
|
|
|
//Moles of Oxygen consumed and CO2 produced.
|
|
oxy_used = (delta_m / material.molecular_weight) / material.fuel_ox_ratio
|
|
co2_prod = oxy_used //simplification
|
|
|
|
//Start a fire on the tile if a burning object is present without an underlying fire effect.
|
|
if(!in_fire)
|
|
//Change in internal energy = change in energy due to heat transfer due to isochoric reaction
|
|
delta_t = heat_out/(delta_m * material.heating_value)
|
|
T.hotspot_expose(temperature + delta_t, FULL_FLAME, 1)
|
|
new /obj/effect/fire(T)
|
|
|
|
//Ash the object if all of its mass has been consumed.
|
|
if(thermal_mass <= 0.05)
|
|
if(burntime < MIN_BURN_TIME) //preventing things from burning up in an instant
|
|
thermal_mass = 0.1
|
|
else
|
|
thermal_mass = 0
|
|
ashify()
|
|
|
|
heat_out *= 1000
|
|
|
|
return list("heat_out"=heat_out,"oxy_used"=oxy_used,"co2_prod"=co2_prod,"max_temperature"=material.flame_temp)
|
|
|
|
/**
|
|
* Burns flammable liquid puddles or flammable liquids within containers and produces heat.
|
|
*
|
|
* Called on every obj/effect/fire/process().
|
|
* Energy is taken from burning atoms and delivered to the obj/effect/fire at the atom's location.
|
|
* Outputs heat produced (MJ), Oxygen consumed (mol), CO2 produced (mol), and max temperature (K) of the burning object.
|
|
*/
|
|
/atom/proc/burnLiquidFuel()
|
|
if(!flammable_reagent_check())
|
|
return
|
|
|
|
//Setup
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
extinguish()
|
|
return
|
|
|
|
var/heat_out = 0 //MJ
|
|
var/oxy_used = 0 //mols
|
|
var/co2_prod = 0 //mols (some reagents consume co2 when they burn)
|
|
var/max_temperature = 0 //K
|
|
var/consumption_rate = 1.0 //units per tick
|
|
var/has_fuel = FALSE
|
|
|
|
//Check if a fire is present at the current location.
|
|
var/in_fire = FALSE
|
|
if(locate(/obj/effect/fire) in T)
|
|
in_fire = TRUE
|
|
|
|
for(var/possible_fuel in possible_fuels)
|
|
if(reagents.has_reagent(possible_fuel))
|
|
has_fuel = TRUE
|
|
var/list/fuel_stats = possible_fuels[possible_fuel]
|
|
max_temperature = max(max_temperature,fuel_stats["max_temperature"])
|
|
heat_out += fuel_stats["thermal_energy_transfer"]
|
|
consumption_rate = fuel_stats["consumption_rate"]
|
|
oxy_used += fuel_stats["o2_cons"]
|
|
co2_prod += -fuel_stats["co2_cons"]
|
|
reagents.remove_reagent(possible_fuel, consumption_rate)
|
|
|
|
if(!has_fuel)
|
|
for(var/datum/reagent/liquid in reagents.reagent_list)
|
|
reagents.remove_reagent(liquid.id,1) //evaporate non-flammable reagents
|
|
|
|
//Start a fire on the tile if a burning object is present without an underlying fire effect.
|
|
if(!in_fire)
|
|
T.hotspot_expose(max_temperature, FULL_FLAME, 1)
|
|
new /obj/effect/fire(T)
|
|
|
|
return list("heat_out"=heat_out,"oxy_used"=oxy_used,"co2_prod"=co2_prod,"max_temperature"=max_temperature)
|
|
|
|
/atom/proc/has_liquid_fuel()
|
|
if(!reagents)
|
|
return FALSE
|
|
for(var/possible_fuel in possible_fuels)
|
|
if(reagents.has_reagent(possible_fuel))
|
|
return TRUE
|
|
|
|
/atom/proc/ashify()
|
|
if(!on_fire)
|
|
return
|
|
var/ashtype = ashtype()
|
|
new ashtype(src.loc)
|
|
visible_message("<span class='danger'>\The [src] burns into a pile of dust!</span>")
|
|
extinguish()
|
|
qdel(src)
|
|
|
|
/atom/proc/extinguish(var/duration = 30 SECONDS)
|
|
if(on_fire)
|
|
on_fire=0
|
|
fire_protection = world.time + duration
|
|
if(fire_overlay)
|
|
overlays -= fire_overlay
|
|
QDEL_NULL(firelightdummy)
|
|
|
|
/atom/proc/ignite()
|
|
if(check_fire_protection())
|
|
return 0
|
|
|
|
if(!istype(loc, /turf)) //worn or held items don't ignite (for now >:^) )
|
|
return 0
|
|
|
|
if(!flammable)
|
|
if(!reagents)
|
|
return 0
|
|
if(!reagents.total_volume)
|
|
return 0
|
|
if(!has_liquid_fuel())
|
|
return 0
|
|
|
|
on_fire=1
|
|
|
|
if(fire_dmi && fire_sprite && !fire_overlay)
|
|
fire_overlay = mutable_appearance(fire_dmi,fire_sprite)
|
|
overlays += fire_overlay
|
|
|
|
burnSolidFuel()
|
|
burnLiquidFuel()
|
|
return 1
|
|
|
|
/atom/movable/ignite()
|
|
..()
|
|
firelightdummy = new (src)
|
|
|
|
/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
|
if(flammable && !on_fire)
|
|
ignite()
|
|
return 1
|
|
|
|
/atom/proc/checkburn()
|
|
if(on_fire)
|
|
return
|
|
if(!flammable)
|
|
CRASH("[src] was added to burnableatoms despite not being flammable!")
|
|
//if an object is not on fire, is flammable, and is in an environment with temperature above its autoignition temp & sufficient oxygen, ignite it
|
|
if(thermal_mass <= 0)
|
|
ashify()
|
|
return
|
|
var/datum/gas_mixture/G = return_air()
|
|
if(!G)
|
|
return
|
|
if(!(G.temperature >= autoignition_temperature))
|
|
return
|
|
if(!(G.molar_ratio(GAS_OXYGEN) >= MINOXY2BURN))
|
|
return
|
|
if(prob(50))
|
|
ignite()
|
|
|
|
/area/checkburn()
|
|
burnableatoms -= src
|
|
CRASH("[src] added to burnableatoms!")
|
|
|
|
/mob/checkburn()
|
|
burnableatoms -= src
|
|
CRASH("[src] added to burnableatoms!")
|
|
|
|
/area/fire_act()
|
|
return
|
|
|
|
/mob/fire_act()
|
|
return
|
|
|
|
///////////////////////////////////////////////
|
|
// TURF COMBUSTION
|
|
///////////////////////////////////////////////
|
|
/turf
|
|
var/soot_type = /obj/effect/decal/cleanable/soot
|
|
|
|
/turf/New()
|
|
..()
|
|
if(!thermal_material)
|
|
flammable = FALSE
|
|
return
|
|
if(!autoignition_temperature)
|
|
autoignition_temperature = thermal_material.autoignition_temperature
|
|
if(thermal_mass)
|
|
initial_thermal_mass = thermal_mass
|
|
fire_protection = world.time
|
|
|
|
/turf/ashify()
|
|
if(!on_fire)
|
|
return
|
|
burnableatoms -= src
|
|
extinguish()
|
|
|
|
/**
|
|
* Creates a hotspot on the given turf.
|
|
*
|
|
* Hotspots ignite any atoms (including the turf itself) on or gasses in the turf with autoignition temperatures below the input temperature.
|
|
* Arguments:
|
|
* * exposed_temperature - Temperature of the hotspot (Kelvin).
|
|
* * exposed_volume - Relative volume of the turf exposed to the hotspot (Milliliter).
|
|
* * surfaces - Whether or not the hotspot should ignite any atoms in the turf in addition to gasses (Boolean).
|
|
*/
|
|
/turf/proc/hotspot_expose(var/exposed_temperature, var/exposed_volume = CELL_VOLUME, var/surfaces=0)
|
|
return 0
|
|
|
|
/turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, surfaces)
|
|
var/obj/effect/foam/fire/W = locate() in contents
|
|
if(istype(W))
|
|
return 0
|
|
if(check_fire_protection())
|
|
return 0
|
|
|
|
var/datum/gas_mixture/air_contents = return_air()
|
|
|
|
if(!air_contents)
|
|
return 0
|
|
|
|
var/igniting = 0
|
|
if(air_contents.check_combustability(src))
|
|
if(air_contents.check_combustability(src) == 2)
|
|
if(prob(exposed_volume * 100 / CELL_VOLUME))
|
|
ignite()
|
|
igniting = 1
|
|
if(surfaces)
|
|
if((flammable || flammable_reagent_check()) && !on_fire)
|
|
if(prob(exposed_volume * 100 / CELL_VOLUME))
|
|
ignite()
|
|
igniting = 1
|
|
for(var/obj/O in contents)
|
|
if(prob(exposed_volume * 100 / CELL_VOLUME) && istype(O) && O.flammable && !O.on_fire && exposed_temperature >= O.autoignition_temperature)
|
|
O.ignite()
|
|
igniting = 1
|
|
break
|
|
if(igniting)
|
|
new /obj/effect/fire(src)
|
|
return igniting
|
|
|
|
/turf/simulated/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
|
var/obj/effect/E = null
|
|
if(soot_type)
|
|
E = locate(soot_type) in src
|
|
if(..())
|
|
return 1
|
|
if(on_fire)
|
|
if(istype(E))
|
|
qdel(E)
|
|
return 0
|
|
else
|
|
if(flammable)
|
|
ignite()
|
|
if(!E && soot_type && prob(25))
|
|
new soot_type(src)
|
|
return 0
|
|
|
|
/turf/simulated/ignite()
|
|
if(!flammable || check_fire_protection() || thermal_mass <= 0)
|
|
return FALSE
|
|
|
|
var/in_fire = FALSE
|
|
on_fire=1
|
|
|
|
if(locate(/obj/effect/fire) in src)
|
|
in_fire = TRUE
|
|
if(!in_fire)
|
|
new /obj/effect/fire(src)
|
|
return TRUE
|
|
|
|
/turf/simulated/flammable_reagent_check()
|
|
if(locate(/obj/effect/decal/cleanable/liquid_fuel) in src)
|
|
return TRUE
|
|
|
|
/turf/simulated/burnLiquidFuel()
|
|
return
|
|
|
|
/turf/unsimulated/burnSolidFuel()
|
|
return
|
|
|
|
/turf/unsimulated/burnLiquidFuel()
|
|
return
|
|
|
|
///////////////////////////////////////////////
|
|
// FIRE
|
|
///////////////////////////////////////////////
|
|
/obj/effect/fire
|
|
anchored = 1
|
|
mouse_opacity = 0
|
|
blend_mode = BLEND_ADD
|
|
icon = 'icons/effects/fire.dmi'
|
|
icon_state = "key1"
|
|
layer = TURF_FIRE_LAYER
|
|
plane = ABOVE_TURF_PLANE
|
|
light_color = LIGHT_COLOR_FIRE
|
|
var/last_vis_refresh = 0
|
|
var/burn_duration = 0
|
|
|
|
/obj/effect/fire/New()
|
|
. = ..()
|
|
dir = pick(cardinal)
|
|
var/turf/T = get_turf(loc)
|
|
var/datum/gas_mixture/air_contents=T.return_air()
|
|
if(air_contents)
|
|
setfirelight(air_contents.calculate_firelevel(get_turf(src)), air_contents.temperature)
|
|
SSair.add_hotspot(src)
|
|
|
|
/obj/effect/fire/Destroy()
|
|
SSair.remove_hotspot(src)
|
|
set_light(0)
|
|
..()
|
|
|
|
/obj/effect/fire/proc/Extinguish()
|
|
for(var/atom/A in loc)
|
|
A.extinguish()
|
|
qdel(src)
|
|
|
|
/obj/effect/fire/process()
|
|
if(timestopped)
|
|
return 0
|
|
. = 1
|
|
|
|
//Fires shouldn't spawn in areas or mobs, but it has happened...
|
|
if(istype(loc,/area) || istype(loc,/mob))
|
|
qdel(src)
|
|
CRASH("Fire was created at [loc] instead of a turf.")
|
|
|
|
// Get location and check if it is in a proper ZAS zone.
|
|
var/turf/simulated/S = get_turf(loc)
|
|
if(!istype(S) || isnull(S.zone))
|
|
Extinguish()
|
|
return
|
|
|
|
//since the air is processed in fractions, we need to make sure not to have any minuscle residue or
|
|
//the amount of moles might get to low for some functions to catch them and thus result in wonky behaviour
|
|
var/datum/gas_mixture/air_contents = S.return_air()
|
|
|
|
//Check if there is something to combust.
|
|
if(!air_contents.check_recombustability(S))
|
|
Extinguish()
|
|
else if(air_contents.check_recombustability(S) == 1)
|
|
if((air_contents.molar_ratio(GAS_OXYGEN)) < (MINOXY2BURN + rand(-2,2)*0.01))
|
|
Extinguish()
|
|
return
|
|
|
|
if(air_contents.molar_ratio(GAS_OXYGEN) < 0.1 / CELL_VOLUME)
|
|
air_contents[GAS_OXYGEN] = 0
|
|
if(air_contents.molar_ratio(GAS_PLASMA) < 0.1 / CELL_VOLUME)
|
|
air_contents[GAS_PLASMA] = 0
|
|
if(air_contents.molar_ratio(GAS_VOLATILE) < 0.1 / CELL_VOLUME)
|
|
air_contents[GAS_VOLATILE] = 0
|
|
air_contents.update_values()
|
|
|
|
//Set firelevel and fire light.
|
|
var/firelevel = air_contents.calculate_firelevel(S)
|
|
setfirelight(firelevel, air_contents.temperature)
|
|
|
|
//Burn mobs.
|
|
for(var/mob/living/carbon/human/M in loc)
|
|
if(M.mutations.Find(M_UNBURNABLE))
|
|
continue
|
|
M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure())
|
|
|
|
//Burn items in the turf.
|
|
for(var/atom/A in loc)
|
|
A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
|
|
|
|
//Burn the turf, too.
|
|
S.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
|
|
|
|
//Spread
|
|
for(var/direction in cardinal)
|
|
if(S.open_directions & direction) //Grab all valid bordering tiles
|
|
var/turf/simulated/enemy_tile = get_step(S, direction)
|
|
var/liquidburn = FALSE
|
|
if(istype(enemy_tile))
|
|
var/datum/gas_mixture/acs = enemy_tile.return_air()
|
|
if(!acs)
|
|
continue
|
|
if(!acs.check_combustability(enemy_tile))
|
|
continue
|
|
//If extinguisher mist passed over the turf it's trying to spread to, don't spread and reduce firelevel.
|
|
var/obj/effect/foam/fire/W = locate() in enemy_tile
|
|
if(istype(W))
|
|
firelevel -= 3
|
|
continue
|
|
if(enemy_tile.check_fire_protection())
|
|
firelevel -= 1.5
|
|
continue
|
|
if(enemy_tile.flammable_reagent_check())
|
|
liquidburn = TRUE
|
|
//Spread the fire.
|
|
if(!(locate(/obj/effect/fire) in enemy_tile))
|
|
if(prob(clamp(ZAS_fire_spread_chance*round(burn_duration/5)*((acs.temperature/T20C)**0.5) + 25*firelevel + 50*liquidburn,0,100)) && S.Cross(null, enemy_tile, 0,0) && enemy_tile.Cross(null, S, 0,0))
|
|
new/obj/effect/fire(enemy_tile)
|
|
//seperate part of the present gas
|
|
//this is done to prevent the fire burning all gases in a single pass
|
|
var/datum/gas_mixture/flow = air_contents.remove_volume(ZAS_air_consumption_rate * CELL_VOLUME)
|
|
///////////////////////////////// FLOW HAS BEEN CREATED /// DONT DELETE THE FIRE UNTIL IT IS MERGED BACK OR YOU WILL DELETE AIR ///////////////////////////////////////////////
|
|
if(flow)
|
|
flow.zburn(S, 1)
|
|
//merge the air back
|
|
S.assume_air(flow)
|
|
///////////////////////////////// FLOW HAS BEEN REMERGED /// feel free to delete the fire again from here on //////////////////////////////////////////////////////////////////
|
|
burn_duration++
|
|
|
|
/obj/effect/fire/proc/setfirelight(firelevel, firetemp)
|
|
// Update fire color.
|
|
if(last_vis_refresh + ((10 + rand(-2,2)) SECONDS) > world.time)
|
|
return
|
|
|
|
var/range
|
|
var/power
|
|
|
|
if(firelevel > 6)
|
|
icon_state = "key3"
|
|
range = 7
|
|
power = 3
|
|
else if(firelevel > 2.5)
|
|
icon_state = "key2"
|
|
range = 5
|
|
power = 2
|
|
else
|
|
icon_state = "key1"
|
|
range = 3
|
|
power = 1
|
|
|
|
color = heat2color(firetemp)
|
|
set_light(range, power, color)
|
|
last_vis_refresh = world.time
|
|
|
|
/datum/gas_mixture/proc/zburn(var/turf/T, force_burn)
|
|
//NOTE: zburn is also called from canisters and in tanks/pipes (via react()).
|
|
var/value = 0 //always 0 unless Plasma or Volatile Gas combusts with Oxygen.
|
|
|
|
//Return if there's nothing left to burn.
|
|
if(!check_recombustability(T))
|
|
return value
|
|
|
|
var/firelevel = 0
|
|
var/total_fuel = 0
|
|
var/starting_energy = temperature * heat_capacity()
|
|
var/total_oxygen = 0
|
|
var/used_fuel_ratio = 0
|
|
var/total_reactants = 0
|
|
var/used_reactants_ratio = 0
|
|
if(temperature > PLASMA_MINIMUM_BURN_TEMPERATURE || force_burn)
|
|
total_fuel += src[GAS_PLASMA]
|
|
total_fuel += src[GAS_VOLATILE]
|
|
if(total_fuel)
|
|
//Calculate the firelevel.
|
|
firelevel = calculate_firelevel(T)
|
|
//determine the amount of oxygen used
|
|
total_oxygen = min(src[GAS_OXYGEN], PLASMA_MINIMUM_OXYGEN_NEEDED * total_fuel)
|
|
//determine the amount of fuel actually used
|
|
used_fuel_ratio = min(src[GAS_OXYGEN] / PLASMA_MINIMUM_OXYGEN_NEEDED , total_fuel) / total_fuel
|
|
total_fuel = total_fuel * used_fuel_ratio
|
|
total_reactants = total_fuel + total_oxygen
|
|
//determine the amount of reactants actually reacting
|
|
used_reactants_ratio = clamp(firelevel / ZAS_firelevel_multiplier, clamp(0.2 / total_reactants, 0, 1), 1)
|
|
|
|
//Combustion of solids and liquids
|
|
var/combustion_energy = 0
|
|
var/combustion_oxy_used = 0
|
|
var/combustion_co2_prod = 0
|
|
var/max_temperature = 0
|
|
var/solid_burn_products
|
|
var/liquid_burn_products
|
|
if(T)
|
|
if(T.flammable) //burn the turf
|
|
solid_burn_products = T.burnSolidFuel()
|
|
if(solid_burn_products)
|
|
combustion_energy += solid_burn_products["heat_out"]
|
|
combustion_oxy_used += solid_burn_products["oxy_used"]
|
|
combustion_co2_prod += solid_burn_products["co2_prod"]
|
|
max_temperature = max(max_temperature, solid_burn_products["max_temperature"])
|
|
for(var/atom/A in T)
|
|
if(A.flammable) //burn items on the turf
|
|
solid_burn_products = A.burnSolidFuel()
|
|
if(solid_burn_products)
|
|
combustion_energy += solid_burn_products["heat_out"]
|
|
combustion_oxy_used += solid_burn_products["oxy_used"]
|
|
combustion_co2_prod += solid_burn_products["co2_prod"]
|
|
max_temperature = max(max_temperature, solid_burn_products["max_temperature"])
|
|
if(A.reagents || istype(A,/obj/effect/decal/cleanable/liquid_fuel)) //burn liquids in containers on the turf
|
|
liquid_burn_products = A.burnLiquidFuel()
|
|
if(liquid_burn_products)
|
|
combustion_energy += liquid_burn_products["heat_out"]
|
|
combustion_oxy_used += liquid_burn_products["oxy_used"]
|
|
combustion_co2_prod += liquid_burn_products["co2_prod"]
|
|
max_temperature = max(max_temperature, liquid_burn_products["max_temperature"])
|
|
|
|
//Sanity checks.
|
|
combustion_oxy_used = clamp(combustion_oxy_used, 0, src[GAS_OXYGEN])
|
|
if(!max_temperature)
|
|
max_temperature = FLAME_TEMPERATURE_PLASTIC
|
|
|
|
//Remove and add gasses as calculated.
|
|
adjust_multi(
|
|
GAS_OXYGEN, -min(src[GAS_OXYGEN], total_oxygen * used_reactants_ratio + combustion_oxy_used * ZAS_oxygen_consumption_multiplier),
|
|
GAS_PLASMA, -min(src[GAS_PLASMA], (src[GAS_PLASMA] * used_fuel_ratio * used_reactants_ratio) * 3),
|
|
GAS_CARBON, max(2 * total_fuel * used_reactants_ratio + combustion_co2_prod * ZAS_oxygen_consumption_multiplier, 0),
|
|
GAS_VOLATILE, -min(src[GAS_VOLATILE], (src[GAS_VOLATILE] * used_fuel_ratio * used_reactants_ratio) * 5)) //Fuel burns 5 times as quick
|
|
|
|
//Calculate the energy produced by the reaction and then set the new temperature of the mix.
|
|
var/combustion_efficiency = max((1 - temperature/max_temperature),0)
|
|
temperature = (starting_energy + (combustion_energy ** ZAS_heat_multiplier) * combustion_efficiency + ZAS_fuel_energy_release_rate * total_fuel * used_reactants_ratio) / heat_capacity()
|
|
update_values()
|
|
|
|
value = total_reactants * used_reactants_ratio
|
|
return value
|
|
|
|
//Checks if anything in a given turf can continue burning.
|
|
/datum/gas_mixture/proc/check_recombustability(var/turf/T)
|
|
if(gas[GAS_OXYGEN] && (gas[GAS_PLASMA] || gas[GAS_VOLATILE]))
|
|
if(QUANTIZE(molar_density(GAS_PLASMA) * ZAS_air_consumption_rate) >= MOLES_PLASMA_VISIBLE / CELL_VOLUME)
|
|
return 2
|
|
if(QUANTIZE(molar_density(GAS_VOLATILE) * ZAS_air_consumption_rate) >= BASE_ZAS_FUEL_REQ / CELL_VOLUME)
|
|
return 2
|
|
|
|
//Check if we're actually in a turf or not before trying to check object fires
|
|
if(!T)
|
|
return 0
|
|
if(!istype(T))
|
|
warning("check_recombustability being asked to check a [T.type] instead of /turf.")
|
|
return 0
|
|
|
|
if(T.flammable && !T.check_fire_protection() && T.thermal_mass > 0)
|
|
return 1
|
|
|
|
if(locate(/obj/effect/decal/cleanable/liquid_fuel) in T)
|
|
return 1
|
|
|
|
for(var/atom/A in T)
|
|
if(!A.check_fire_protection())
|
|
if(A.flammable && A.thermal_mass > 0)
|
|
return 1
|
|
if(A.reagents)
|
|
for(var/possible_fuel in possible_fuels)
|
|
if(A.reagents.has_reagent(possible_fuel))
|
|
return 1
|
|
|
|
//Checks if anything in a given turf can burn.
|
|
/datum/gas_mixture/proc/check_combustability(var/turf/T)
|
|
if(T.flammable && T.thermal_mass > 0)
|
|
return 1
|
|
|
|
if(locate(/obj/effect/decal/cleanable/liquid_fuel) in T)
|
|
return 1
|
|
|
|
if(gas[GAS_OXYGEN] && (gas[GAS_PLASMA] || gas[GAS_VOLATILE]))
|
|
if(QUANTIZE(molar_density(GAS_PLASMA) * ZAS_air_consumption_rate) >= MOLES_PLASMA_VISIBLE / CELL_VOLUME)
|
|
return 2 //returns 2 if we're burning plasma or volatiles
|
|
if(QUANTIZE(molar_density(GAS_VOLATILE) * ZAS_air_consumption_rate) >= BASE_ZAS_FUEL_REQ / CELL_VOLUME)
|
|
return 2
|
|
|
|
for(var/atom/A in T)
|
|
if(A.flammable && A.thermal_mass > 0)
|
|
return 1
|
|
if(A.reagents)
|
|
for(var/possible_fuel in possible_fuels)
|
|
if(A.reagents.has_reagent(possible_fuel))
|
|
return 1
|
|
|
|
//firelevel represents the intensity of the fire according to GAS REACTANTS only. Solids and liquids burning use an internal burnrate calculation.
|
|
/datum/gas_mixture/proc/calculate_firelevel(var/turf/T)
|
|
var/total_fuel = 0
|
|
var/firelevel = 0
|
|
|
|
if(check_recombustability(T))
|
|
total_fuel += src[GAS_PLASMA]
|
|
total_fuel += src[GAS_VOLATILE]
|
|
var/total_combustables = (total_fuel + src[GAS_OXYGEN])
|
|
|
|
if(total_fuel > 0 && src[GAS_OXYGEN] > 0)
|
|
//slows down the burning when the concentration of the reactants is low
|
|
var/dampening_multiplier = total_combustables / (total_combustables + src[GAS_NITROGEN] + src[GAS_CARBON])
|
|
//calculates how close the mixture of the reactants is to the optimum
|
|
var/mix_multiplier = 1 / (1 + (5 * ((src[GAS_OXYGEN] / total_combustables) ** 2))) // Thanks, Mloc
|
|
//toss everything together
|
|
firelevel = ZAS_firelevel_multiplier * mix_multiplier * dampening_multiplier
|
|
|
|
return max(0, firelevel)
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// BURNING MOBS
|
|
///////////////////////////////////////////////
|
|
/mob/living/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure)
|
|
var/mx = 5 * firelevel/ZAS_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
|
|
apply_damage(2.5*mx, BURN)
|
|
|
|
/mob/living/carbon/human/FireBurn(var/firelevel, var/last_temperature, var/pressure)
|
|
var/head_exposure = 1
|
|
var/chest_exposure = 1
|
|
var/groin_exposure = 1
|
|
var/legs_exposure = 1
|
|
var/arms_exposure = 1
|
|
|
|
//Get heat transfer coefficients for clothing.
|
|
for(var/obj/item/clothing/C in src)
|
|
if(is_holding_item(C))
|
|
continue
|
|
if(C.max_heat_protection_temperature >= last_temperature)
|
|
if(!is_slot_hidden(C.body_parts_covered,FULL_HEAD))
|
|
head_exposure = 0
|
|
if(!is_slot_hidden(C.body_parts_covered,UPPER_TORSO))
|
|
chest_exposure = 0
|
|
if(!is_slot_hidden(C.body_parts_covered,LOWER_TORSO))
|
|
groin_exposure = 0
|
|
if(!is_slot_hidden(C.body_parts_covered,LEGS))
|
|
legs_exposure = 0
|
|
if(!is_slot_hidden(C.body_parts_covered,ARMS))
|
|
arms_exposure = 0
|
|
|
|
//minimize this for low-pressure enviroments
|
|
var/mx = 5 * firelevel/ZAS_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
|
|
|
|
//Always check these damage procs first if fire damage isn't working. They're probably what's wrong.
|
|
var/fire_tile_modifier = 4 //multiplier for damage received while standing on a fire tile
|
|
apply_damage(fire_tile_modifier*HEAD_FIRE_DAMAGE_MULTIPLIER*mx*head_exposure, BURN, LIMB_HEAD, 0, 0, used_weapon = "Fire")
|
|
apply_damage(fire_tile_modifier*CHEST_FIRE_DAMAGE_MULTIPLIER*mx*chest_exposure, BURN, LIMB_CHEST, 0, 0, used_weapon ="Fire")
|
|
apply_damage(fire_tile_modifier*GROIN_FIRE_DAMAGE_MULTIPLIER*mx*groin_exposure, BURN, LIMB_GROIN, 0, 0, used_weapon ="Fire")
|
|
apply_damage(fire_tile_modifier*LEGS_FIRE_DAMAGE_MULTIPLIER*mx*legs_exposure, BURN, LIMB_LEFT_LEG, 0, 0, used_weapon = "Fire")
|
|
apply_damage(fire_tile_modifier*LEGS_FIRE_DAMAGE_MULTIPLIER*mx*legs_exposure, BURN, LIMB_RIGHT_LEG, 0, 0, used_weapon = "Fire")
|
|
apply_damage(fire_tile_modifier*ARMS_FIRE_DAMAGE_MULTIPLIER*mx*arms_exposure, BURN, LIMB_LEFT_ARM, 0, 0, used_weapon = "Fire")
|
|
apply_damage(fire_tile_modifier*ARMS_FIRE_DAMAGE_MULTIPLIER*mx*arms_exposure, BURN, LIMB_RIGHT_ARM, 0, 0, used_weapon = "Fire")
|
|
|
|
if(head_exposure || chest_exposure || groin_exposure || legs_exposure || arms_exposure)
|
|
dizziness = 5
|
|
confused = 5
|
|
if(prob(25))
|
|
audible_scream()
|