Implements fuel fires

Implements liquid fuel fires using the liquid fuel cleanable decal objects.
Rewrites zburn to be more sane, replaces convoluted calculations with straightforward reaction limit.
Fixes issue with liquid fuel spreading caused by thee object being deleted.
Prevents zones from being repeatedly added to the active fire zones list, and processed repeatedly.
This commit is contained in:
mwerezak
2015-02-07 16:43:17 -05:00
committed by mwerezak
parent 3ba295dadb
commit 49b3db964f
8 changed files with 155 additions and 1198 deletions

View File

@@ -20,7 +20,7 @@ atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed
turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) /turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
if(fire_protection > world.time-300) if(fire_protection > world.time-300)
return 0 return 0
if(locate(/obj/fire) in src) if(locate(/obj/fire) in src)
@@ -35,37 +35,35 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
if(air_contents.check_combustability(liquid)) if(air_contents.check_combustability(liquid))
igniting = 1 igniting = 1
create_fire(1000) create_fire(vsc.fire_firelevel_multiplier)
return igniting return igniting
/zone/proc/process_fire() /zone/proc/process_fire()
if(!air.check_combustability()) var/datum/gas_mixture/burn_gas = air.remove_ratio(vsc.fire_consuption_rate, fire_tiles.len)
var/firelevel = burn_gas.zburn(src, fire_tiles, force_burn = 1, no_check = 1)
//world << "[src]: firelevel [firelevel]"
air.merge(burn_gas)
if(firelevel)
for(var/turf/T in fire_tiles)
if(T.fire)
T.fire.firelevel = firelevel
else
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T
fire_tiles -= T
fuel_objs -= fuel
else
for(var/turf/simulated/T in fire_tiles) for(var/turf/simulated/T in fire_tiles)
if(istype(T.fire)) if(istype(T.fire))
T.fire.RemoveFire() T.fire.RemoveFire()
T.fire = null T.fire = null
fire_tiles.Cut() fire_tiles.Cut()
fuel_objs.Cut()
if(!fire_tiles.len) if(!fire_tiles.len)
air_master.active_fire_zones.Remove(src) air_master.active_fire_zones.Remove(src)
return
var/datum/gas_mixture/burn_gas = air.remove_ratio(vsc.fire_consuption_rate, fire_tiles.len)
var/gm = burn_gas.group_multiplier
burn_gas.group_multiplier = 1
burn_gas.zburn(force_burn = 1, no_check = 1)
burn_gas.group_multiplier = gm
air.merge(burn_gas)
var/firelevel = air.calculate_firelevel()
for(var/turf/T in fire_tiles)
if(T.fire)
T.fire.firelevel = firelevel
else
fire_tiles -= T
/turf/proc/create_fire(fl) /turf/proc/create_fire(fl)
return 0 return 0
@@ -79,8 +77,12 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
return 1 return 1
fire = new(src, fl) fire = new(src, fl)
zone.fire_tiles |= src
air_master.active_fire_zones |= zone air_master.active_fire_zones |= zone
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in src
zone.fire_tiles |= src
if(fuel) zone.fuel_objs += fuel
return 0 return 0
/obj/fire /obj/fire
@@ -121,8 +123,8 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
SetLuminosity(3) SetLuminosity(3)
//im not sure how to implement a version that works for every creature so for now monkeys are firesafe //im not sure how to implement a version that works for every creature so for now monkeys are firesafe
for(var/mob/living/carbon/human/M in loc) for(var/mob/living/L in loc)
M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the humans! L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs!
loc.fire_act(air_contents, air_contents.temperature, air_contents.volume) loc.fire_act(air_contents, air_contents.temperature, air_contents.volume)
for(var/atom/A in loc) for(var/atom/A in loc)
@@ -137,10 +139,11 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
if(!enemy_tile.zone || enemy_tile.fire) if(!enemy_tile.zone || enemy_tile.fire)
continue continue
if(!enemy_tile.zone.fire_tiles.len) //if(!enemy_tile.zone.fire_tiles.len) TODO - optimize
var/datum/gas_mixture/acs = enemy_tile.return_air() var/datum/gas_mixture/acs = enemy_tile.return_air()
if(!acs || !acs.check_combustability()) var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in enemy_tile
continue if(!acs || !acs.check_combustability(liquid))
continue
//If extinguisher mist passed over the turf it's trying to spread to, don't spread and //If extinguisher mist passed over the turf it's trying to spread to, don't spread and
//reduce firelevel. //reduce firelevel.
@@ -169,85 +172,117 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
/obj/fire/Del() /obj/fire/Del()
if (istype(loc, /turf/simulated)) if (istype(loc, /turf/simulated))
SetLuminosity(0) RemoveFire()
loc = null
air_master.active_hotspots.Remove(src)
..() ..()
/obj/fire/proc/RemoveFire() /obj/fire/proc/RemoveFire()
if (istype(loc, /turf)) if (istype(loc, /turf))
SetLuminosity(0) SetLuminosity(0)
loc = null loc = null
air_master.active_hotspots.Remove(src) air_master.active_hotspots.Remove(src)
turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again. /turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again.
turf/proc/apply_fire_protection() /turf/proc/apply_fire_protection()
turf/simulated/apply_fire_protection() /turf/simulated/apply_fire_protection()
fire_protection = world.time fire_protection = world.time
//Returns the firelevel
datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, force_burn, no_check = 0) /datum/gas_mixture/proc/zburn(zone/zone, force_burn, no_check = 0)
. = 0 . = 0
if((temperature > PHORON_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(liquid))) if((temperature > PHORON_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(zone? zone.fuel_objs : null)))
var/gas_fuel = 0 //in the case of mixed gas/liquid fires, the gas burns first.
var/liquid_fuel = 0
var/total_fuel = 0 var/total_fuel = 0
var/total_oxidizers = 0 var/total_oxidizers = 0
//*** Get the fuel and oxidizer amounts
for(var/g in gas) for(var/g in gas)
if(gas_data.flags[g] & XGM_GAS_FUEL) if(gas_data.flags[g] & XGM_GAS_FUEL)
total_fuel += gas[g] gas_fuel += gas[g]
if(gas_data.flags[g] & XGM_GAS_OXIDIZER) if(gas_data.flags[g] & XGM_GAS_OXIDIZER)
total_oxidizers += gas[g] total_oxidizers += gas[g]
gas_fuel *= group_multiplier
total_oxidizers *= group_multiplier
if(liquid)
//Liquid Fuel //Liquid Fuel
if(liquid.amount <= 0.1) if(zone)
del liquid for(var/obj/effect/decal/cleanable/liquid_fuel/fuel in zone.fuel_objs)
else liquid_fuel += fuel.amount*LIQUIDFUEL_AMOUNT_TO_MOL
total_fuel += liquid.amount
if(total_fuel == 0) total_fuel = gas_fuel + liquid_fuel
if(total_fuel <= 0.005)
return 0 return 0
//Calculate the firelevel. //*** Determine how fast the fire burns
var/firelevel = calculate_firelevel(liquid, total_fuel, total_oxidizers, force = 1)
//get the current inner energy of the gas mix //calculate the firelevel.
var/firelevel = calculate_firelevel(zone? zone.fuel_objs : null, total_fuel, total_oxidizers, force = 1)
//get the current thermal energy of the gas mix
//this must be taken here to prevent the addition or deletion of energy by a changing heat capacity //this must be taken here to prevent the addition or deletion of energy by a changing heat capacity
var/starting_energy = temperature * heat_capacity() var/starting_energy = temperature * heat_capacity()
//determine the amount of oxygen used //determine how far the reaction can progress
var/used_oxidizers = min(total_oxidizers, total_fuel / 2) var/reaction_limit = min(total_oxidizers*(FIRE_REACTION_FUEL_AMOUNT/FIRE_REACTION_OXIDIZER_AMOUNT), total_fuel) //stoichiometric limit
//determine the amount of fuel actually used //determine the actual rate of reaction, as measured by the amount of fuel reacting
var/used_fuel_ratio = min(2 * total_oxidizers , total_fuel) / total_fuel
total_fuel = total_fuel * used_fuel_ratio
var/total_reactants = total_fuel + used_oxidizers //vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).
var/gas_reaction_progress = max(0.2*group_multiplier, (firelevel/vsc.fire_firelevel_multiplier)*gas_fuel)*FIRE_GAS_BURNRATE_MULT
//liquid fuels are not as volatile, and the reaction progress depends on the size of the area that is burning (which is sort of accounted for by firelevel). Having more fuel means a longer burn.
var/liquid_reaction_progress = (firelevel/vsc.fire_firelevel_multiplier)*FIRE_LIQUID_BURNRATE_MULT
//determine the amount of reactants actually reacting //world << "liquid_reaction_progress = [liquid_reaction_progress]"
var/used_reactants_ratio = min(max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants //world << "gas_reaction_progress = [gas_reaction_progress]"
var/total_reaction_progress = gas_reaction_progress + liquid_reaction_progress
if(total_reaction_progress <= 0)
return 0
var/used_fuel = min(total_reaction_progress, reaction_limit)
var/used_oxidizers = used_fuel*(FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT)
//world << "used_fuel = [used_fuel]; used_oxidizers = [used_oxidizers]; reaction_limit=[reaction_limit]"
//*** Remove fuel and oxidizer, add carbon dioxide and heat
//remove and add gasses as calculated //remove and add gasses as calculated
remove_by_flag(XGM_GAS_OXIDIZER, used_oxidizers * used_reactants_ratio) var/used_gas_fuel = min(used_fuel*(gas_reaction_progress/total_reaction_progress), gas_fuel) //remove in proportion to the relative reaction progress
remove_by_flag(XGM_GAS_FUEL, total_fuel * used_reactants_ratio) var/used_liquid_fuel = between(0, used_fuel-used_gas_fuel, liquid_fuel)
adjust_gas("carbon_dioxide", max(total_fuel*used_reactants_ratio, 0)) //remove_by_flag() and adjust_gas() handle the group_multiplier for us.
remove_by_flag(XGM_GAS_OXIDIZER, used_oxidizers)
remove_by_flag(XGM_GAS_FUEL, used_gas_fuel)
adjust_gas("carbon_dioxide", used_oxidizers)
if(liquid) //As a simplification, we remove fuel equally from all fuel sources. It might be that some fuel sources have more fuel, some have less, but whatever.
liquid.amount -= (liquid.amount * used_fuel_ratio * used_reactants_ratio) * 5 // liquid fuel burns 5 times as quick if(zone && zone.fuel_objs.len)
var/fuel_to_remove = used_liquid_fuel/(zone.fuel_objs.len*LIQUIDFUEL_AMOUNT_TO_MOL) //convert back to liquid volume units
//world << "used gas fuel = [used_gas_fuel]; used other fuel = [used_fuel-used_gas_fuel]; fuel_to_remove = [fuel_to_remove]"
var/liquidonly = !check_combustability()
for(var/O in zone.fuel_objs)
var/obj/effect/decal/cleanable/liquid_fuel/fuel = O
if(!istype(fuel))
zone.fuel_objs -= fuel
continue
if(liquid.amount <= 0) del liquid fuel.amount -= fuel_to_remove
if(fuel.amount <= 0)
zone.fuel_objs -= fuel
if(liquidonly)
var/turf/T = fuel.loc
if(istype(T) && T.fire) del(T.fire)
del(fuel)
//calculate the energy produced by the reaction and then set the new temperature of the mix //calculate the energy produced by the reaction and then set the new temperature of the mix
temperature = (starting_energy + vsc.fire_fuel_energy_release * total_fuel) / heat_capacity() temperature = (starting_energy + vsc.fire_fuel_energy_release * used_fuel) / heat_capacity()
update_values() update_values()
. = total_reactants * used_reactants_ratio return firelevel
datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_fuel/liquid) datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
. = 0 . = 0
for(var/g in gas) for(var/g in gas)
if(gas_data.flags[g] & XGM_GAS_OXIDIZER && gas[g] >= 0.1) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && gas[g] >= 0.1)
@@ -257,7 +292,7 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_
if(!.) if(!.)
return 0 return 0
if(liquid) if(fuel_objs && fuel_objs.len)
return 1 return 1
. = 0 . = 0
@@ -266,7 +301,7 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_
. = 1 . = 1
break break
datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid) /datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid=null)
. = 0 . = 0
for(var/g in gas) for(var/g in gas)
if(gas_data.flags[g] & XGM_GAS_OXIDIZER && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1)
@@ -285,20 +320,12 @@ datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fu
. = 1 . = 1
break break
datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid, total_fuel = null, total_oxidizers = null, force = 0) //Returns a value between 0 and vsc.fire_firelevel_multiplier
/datum/gas_mixture/proc/calculate_firelevel(list/fuel_objs, total_fuel, total_oxidizers, force = 0)
//Calculates the firelevel based on one equation instead of having to do this multiple times in different areas. //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
var/firelevel = 0 var/firelevel = 0
if(force || check_recombustability(liquid)) if(force || check_recombustability(fuel_objs))
if(isnull(total_fuel))
for(var/g in gas)
if(gas_data.flags[g] & XGM_GAS_FUEL)
total_fuel += gas[g]
if(gas_data.flags[g] & XGM_GAS_OXIDIZER)
total_oxidizers += gas[g]
if(liquid)
total_fuel += liquid.amount
var/total_combustables = (total_fuel + total_oxidizers) var/total_combustables = (total_fuel + total_oxidizers)
if(total_combustables > 0) if(total_combustables > 0)

View File

@@ -44,6 +44,7 @@ Class Procs:
/zone/var/invalid = 0 /zone/var/invalid = 0
/zone/var/list/contents = list() /zone/var/list/contents = list()
/zone/var/list/fire_tiles = list() /zone/var/list/fire_tiles = list()
/zone/var/list/fuel_objs = list()
/zone/var/needs_update = 0 /zone/var/needs_update = 0
@@ -72,8 +73,10 @@ Class Procs:
T.zone = src T.zone = src
contents.Add(T) contents.Add(T)
if(T.fire) if(T.fire)
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T
fire_tiles.Add(T) fire_tiles.Add(T)
air_master.active_fire_zones.Add(src) air_master.active_fire_zones |= src
if(fuel) fuel_objs += fuel
T.update_graphic(air.graphic) T.update_graphic(air.graphic)
/zone/proc/remove(turf/simulated/T) /zone/proc/remove(turf/simulated/T)
@@ -85,6 +88,9 @@ Class Procs:
#endif #endif
contents.Remove(T) contents.Remove(T)
fire_tiles.Remove(T) fire_tiles.Remove(T)
if(T.fire)
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T
fuel_objs -= fuel
T.zone = null T.zone = null
T.update_graphic(graphic_remove = air.graphic) T.update_graphic(graphic_remove = air.graphic)
if(contents.len) if(contents.len)

File diff suppressed because it is too large Load Diff

View File

@@ -301,7 +301,7 @@
/datum/gas_mixture/proc/react(atom/dump_location) /datum/gas_mixture/proc/react(atom/dump_location)
zburn(null) zburn(null, force_burn=0, no_check=0) //could probably just call zburn() here with no args but I like being explicit.
//Rechecks the gas_mixture and adjusts the graphic list if needed. //Rechecks the gas_mixture and adjusts the graphic list if needed.

View File

@@ -199,9 +199,7 @@ update_flag
else else
can_label = 0 can_label = 0
if(air_contents.temperature > PHORON_FLASHPOINT) air_contents.react() //cooking up air cans - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE
air_contents.zburn()
return
/obj/machinery/portable_atmospherics/canister/return_air() /obj/machinery/portable_atmospherics/canister/return_air()
return air_contents return air_contents

View File

@@ -1,10 +1,10 @@
obj/effect/decal/cleanable/liquid_fuel /obj/effect/decal/cleanable/liquid_fuel
//Liquid fuel is used for things that used to rely on volatile fuels or phoron being contained to a couple tiles. //Liquid fuel is used for things that used to rely on volatile fuels or phoron being contained to a couple tiles.
icon = 'icons/effects/effects.dmi' icon = 'icons/effects/effects.dmi'
icon_state = "fuel" icon_state = "fuel"
layer = TURF_LAYER+0.2 layer = TURF_LAYER+0.2
anchored = 1 anchored = 1
var/amount = 1 //Basically moles. var/amount = 1
New(turf/newLoc,amt=1,nologs=0) New(turf/newLoc,amt=1,nologs=0)
if(!nologs) if(!nologs)
@@ -16,25 +16,34 @@ obj/effect/decal/cleanable/liquid_fuel
for(var/obj/effect/decal/cleanable/liquid_fuel/other in newLoc) for(var/obj/effect/decal/cleanable/liquid_fuel/other in newLoc)
if(other != src) if(other != src)
other.amount += src.amount other.amount += src.amount
var/oldsrc = src
src = null
spawn other.Spread() spawn other.Spread()
del src del(oldsrc)
Spread() Spread()
. = ..() . = ..()
proc/Spread() proc/Spread(exclude=list())
//Allows liquid fuels to sometimes flow into other tiles. //Allows liquid fuels to sometimes flow into other tiles.
if(amount < 5.0) return if(amount < 15) return //lets suppose welder fuel is fairly thick and sticky. For something like water, 5 or less would be more appropriate.
var/turf/simulated/S = loc var/turf/simulated/S = loc
if(!istype(S)) return if(!istype(S)) return
for(var/d in cardinal) for(var/d in cardinal)
if(rand(25)) var/turf/simulated/target = get_step(src,d)
var/turf/simulated/target = get_step(src,d) var/turf/simulated/origin = get_turf(src)
var/turf/simulated/origin = get_turf(src) if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0))
if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0)) var/obj/effect/decal/cleanable/liquid_fuel/other_fuel = locate() in target
if(!locate(/obj/effect/decal/cleanable/liquid_fuel) in target) if(other_fuel)
new/obj/effect/decal/cleanable/liquid_fuel(target, amount*0.25,1) other_fuel.amount += amount*0.25
amount *= 0.75 if(!(other_fuel in exclude))
exclude += src
other_fuel.Spread(exclude)
else
new/obj/effect/decal/cleanable/liquid_fuel(target, amount*0.25,1)
amount *= 0.75
flamethrower_fuel flamethrower_fuel
icon_state = "mustard" icon_state = "mustard"

View File

@@ -246,7 +246,7 @@
/obj/item/weapon/tank/process() /obj/item/weapon/tank/process()
//Allow for reactions //Allow for reactions
air_contents.react() air_contents.react() //cooking up air tanks - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE
check_status() check_status()

View File

@@ -122,12 +122,19 @@
#define CARBON_LIFEFORM_FIRE_DAMAGE 4 #define CARBON_LIFEFORM_FIRE_DAMAGE 4
// Phoron fire properties. // Phoron fire properties.
#define PHORON_MINIMUM_BURN_TEMPERATURE (T0C + 100) #define PHORON_MINIMUM_BURN_TEMPERATURE (T0C + 126) //400 K - autoignite temperature in tanks and canisters - enclosed environments I guess
#define PHORON_FLASHPOINT (T0C + 246) #define PHORON_FLASHPOINT (T0C + 246) //519 K - autoignite temperature in air if that ever gets implemented.
#define PHORON_UPPER_TEMPERATURE (T0C + 1370)
#define PHORON_MINIMUM_OXYGEN_NEEDED 2 //These control the mole ratio of oxidizer and fuel used in the combustion reaction
#define PHORON_MINIMUM_OXYGEN_PHORON_RATIO 20 #define FIRE_REACTION_OXIDIZER_AMOUNT 3
#define PHORON_OXYGEN_FULLBURN 10 #define FIRE_REACTION_FUEL_AMOUNT 2
//These control the speed at which fire burns
#define FIRE_GAS_BURNRATE_MULT 1
#define FIRE_LIQUID_BURNRATE_MULT 0.5
//How many moles of fuel are contained within one solid/liquid fuel volume unit
#define LIQUIDFUEL_AMOUNT_TO_MOL 1 //mol/volume unit
#define T0C 273.15 // 0.0 degrees celcius #define T0C 273.15 // 0.0 degrees celcius
#define T20C 293.15 // 20.0 degrees celcius #define T20C 293.15 // 20.0 degrees celcius