From 9cc31cdd21e9367508997016e23ecb3c6fde42e5 Mon Sep 17 00:00:00 2001 From: LordBraindead Date: Fri, 5 Jul 2013 09:03:32 +0200 Subject: [PATCH] changed how fire works -fire switched to release energy by burning fuel and generating heat from that - reorganzied the fire process in a saner manner --- code/ZAS/Fire.dm | 333 ++++++++++++++++++++++++++--------------------- 1 file changed, 182 insertions(+), 151 deletions(-) diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index 3cfccaeb841..023d60acf08 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -11,8 +11,6 @@ Attach to transfer valve and open. BOOM. */ - - //Some legacy definitions so fires can be started. atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) return null @@ -21,35 +19,24 @@ atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) - turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) - if(fire_protection > world.time-300) return - var/datum/gas_mixture/air_contents = return_air(1) - if(!air_contents) + if(fire_protection > world.time-300) return 0 - - /*if(active_hotspot) - if(soh) - if(air_contents.toxins > 0.5 && air_contents.oxygen > 0.5) - if(active_hotspot.temperature < exposed_temperature) - active_hotspot.temperature = exposed_temperature - if(active_hotspot.volume < exposed_volume) - active_hotspot.volume = exposed_volume - return 1*/ - var/igniting = 0 if(locate(/obj/fire) in src) return 1 - var/datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases + var/datum/gas_mixture/air_contents = return_air() + if(!air_contents || exposed_temperature < PLASMA_MINIMUM_BURN_TEMPERATURE) + return 0 + + var/igniting = 0 var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src - if(air_contents.calculate_firelevel(liquid) > vsc.IgnitionLevel && (fuel || liquid || air_contents.toxins > 0.5)) + + if(air_contents.check_combustability(liquid)) igniting = 1 - if(air_contents.oxygen < 0.5) - return 0 if(! (locate(/obj/fire) in src)) - var/obj/fire/F = new(src,1000) - F.temperature = exposed_temperature - F.volume = CELL_VOLUME + + new /obj/fire(src,1000) //active_hotspot.just_spawned = (current_cycle < air_master.current_cycle) //remove just_spawned protection if no longer processing this cell @@ -71,93 +58,97 @@ obj layer = TURF_LAYER var - volume = CELL_VOLUME - temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST firelevel = 10000 //Calculated by gas_mixture.calculate_firelevel() - archived_firelevel = 0 process() . = 1 - - if(firelevel > vsc.IgnitionLevel) - - var/turf/simulated/floor/S = loc - if(!S.zone) del src //Cannot exist where zones are broken. - - if(istype(S)) - var - datum/gas_mixture/air_contents = S.return_air() - //Get whatever trace fuels are in the area - datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases - //Also get liquid fuels on the ground. - obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in S - - var/datum/gas_mixture/flow = air_contents.remove_ratio(0.25) - //The reason we're taking a part of the air instead of all of it is so that it doesn't jump to - //the fire's max temperature instantaneously. - - firelevel = air_contents.calculate_firelevel(liquid) - - //Ensure that there is an appropriate amount of fuel and O2 here. - if(firelevel > 0.25 && flow.oxygen > 0.3 && (air_contents.toxins || fuel || liquid)) - - for(var/direction in cardinal) - if(S.air_check_directions&direction) //Grab all valid bordering tiles - - var/turf/simulated/enemy_tile = get_step(S, direction) - - if(istype(enemy_tile)) - //If extinguisher mist passed over the turf it's trying to spread to, don't spread and - //reduce firelevel. - if(enemy_tile.fire_protection > world.time-30) - firelevel -= 1.5 - continue - - //Spread the fire. - if(!(locate(/obj/fire) in enemy_tile)) - if( prob( firelevel*10 ) && S.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, S, 0,0)) - new/obj/fire(enemy_tile,firelevel) - - if(flow) - - //Ensure adequate oxygen and fuel. - if(flow.oxygen > 0.3 && (flow.toxins || fuel || liquid)) - - //Change icon depending on the fuel, and thus temperature. - if(firelevel > 6) - icon_state = "3" - else if(firelevel > 2.5) - icon_state = "2" - else - icon_state = "1" - - //Ensure flow temperature is higher than minimum fire temperatures. - flow.temperature = max(PLASMA_MINIMUM_BURN_TEMPERATURE+0.1,flow.temperature) - - //Burn the gas mixture. - flow.zburn(liquid) - if(fuel && fuel.moles <= 0.00001) - del fuel - - else - - del src - - - S.assume_air(flow) //Then put it back where you found it. - - else - del src - else - del src - else + + //get location and check if it is in a proper ZAS zone + var/turf/simulated/floor/S = loc + if(!S.zone) del src - - + + if(!istype(S)) + del src + + var/datum/gas_mixture/air_contents = S.return_air() + //get liquid fuels on the ground. + var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in S + //and the volatile stuff from the air + var/datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases + + //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 + if(air_contents.oxygen < 0.001) + air_contents.oxygen = 0 + if(air_contents.toxins < 0.001) + air_contents.toxins = 0 + if(fuel) + if(fuel.moles < 0.001) + air_contents.trace_gases.Remove(fuel) + + //check if there is something to combust + if(!air_contents.check_combustability(liquid)) + del src + + //get a firelevel and set the icon + firelevel = air_contents.calculate_firelevel(liquid) + + if(firelevel > 6) + icon_state = "3" + SetLuminosity(7) + else if(firelevel > 2.5) + icon_state = "2" + SetLuminosity(5) + else + icon_state = "1" + SetLuminosity(3) + + //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) - M.FireBurn(temperature, min(max(0.1,firelevel / 20),10)) //Burn the humans! + M.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure() ) //Burn the humans! + + //spread! + for(var/direction in cardinal) + if(S.air_check_directions&direction) //Grab all valid bordering tiles + var/turf/simulated/enemy_tile = get_step(S, direction) + + if(istype(enemy_tile)) + //If extinguisher mist passed over the turf it's trying to spread to, don't spread and + //reduce firelevel. + if(enemy_tile.fire_protection > world.time-30) + firelevel -= 1.5 + continue + + //Spread the fire. + if(!(locate(/obj/fire) in enemy_tile)) + if( prob( 50 + 50 * (firelevel/vsc.fire_firelevel_multiplier) ) && S.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, S, 0,0)) + new/obj/fire(enemy_tile,firelevel) + + //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_ratio(vsc.fire_consuption_rate) + +///////////////////////////////// FLOW HAS BEEN CREATED /// DONT DELETE THE FIRE UNTIL IT IS MERGED BACK OR YOU WILL DELETE AIR /////////////////////////////////////////////// + + if(flow) + if(flow.check_combustability(liquid)) + //Ensure flow temperature is higher than minimum fire temperatures. + //this creates some energy ex nihilo but is necessary to get a fire started + //lets just pretend this energy comes from the ignition source and dont mention this again + //flow.temperature = max(PLASMA_MINIMUM_BURN_TEMPERATURE+0.1,flow.temperature) + + //burn baby burn! + flow.zburn(liquid,1) + + //merge the air back + S.assume_air(flow) + +///////////////////////////////// FLOW HAS BEEN REMERGED /// feel free to delete the fire again from here on ////////////////////////////////////////////////////////////////// + + New(newLoc,fl) ..() @@ -165,13 +156,14 @@ obj del src dir = pick(cardinal) - //sd_SetLuminosity(3,2,0) + SetLuminosity(3) firelevel = fl air_master.active_hotspots.Add(src) + Del() if (istype(loc, /turf/simulated)) - //sd_SetLuminosity(0) + SetLuminosity(0) loc = null air_master.active_hotspots.Remove(src) @@ -179,23 +171,25 @@ obj ..() + turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again. turf/proc/apply_fire_protection() turf/simulated/apply_fire_protection() fire_protection = world.time -datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid) - //This proc is similar to fire(), but uses a simple logarithm to calculate temp, and is thus more stable with ZAS. - if(temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) - var - total_fuel = toxins - fuel_sources = 0 //We'll divide by this later so that fuel is consumed evenly. - datum/gas/volatile_fuel/fuel = locate() in trace_gases +datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid,force_burn) + var/value = 0 + + if((temperature > PLASMA_MINIMUM_BURN_TEMPERATURE || force_burn) && check_combustability(liquid) ) + var/total_fuel = 0 + var/datum/gas/volatile_fuel/fuel = locate() in trace_gases + + total_fuel += toxins + if(fuel) //Volatile Fuel total_fuel += fuel.moles - fuel_sources++ if(liquid) //Liquid Fuel @@ -203,61 +197,94 @@ datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid) del liquid else total_fuel += liquid.amount - fuel_sources++ - //Toxins - if(toxins > 0.3) fuel_sources++ + //Calculate the firelevel. + var/firelevel = calculate_firelevel(liquid) - if(!fuel_sources) return 0 //If there's no fuel, there's no burn. Can't divide by zero anyway. + //get the current inner energy of the gas mix + //this must be taken here to prevent the addition or deletion of energy by a changing heat capacity + var/starting_energy = temperature * heat_capacity() - if(oxygen > 0.3) + //determine the amount of oxygen used + var/total_oxygen = min(oxygen, 2 * total_fuel) + + //determine the amount of fuel actually used + var/used_fuel_ratio = min(oxygen / 2 , total_fuel) / total_fuel + total_fuel = total_fuel * used_fuel_ratio - //Calculate the firelevel. - var/firelevel = calculate_firelevel(liquid) + var/total_reactants = total_fuel + total_oxygen - //Reaches a maximum practical temperature of around 4500. + //determine the amount of reactants actually reacting + var/used_reactants_ratio = min( max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants - //Increase temperature. - temperature = max( 1700*log(0.4*firelevel + 1.23) , temperature ) + //remove and add gasses as calculated + oxygen -= min(oxygen, total_oxygen * used_reactants_ratio ) - //Consume some gas. - var/consumed_gas = min(oxygen,0.05*firelevel,total_fuel) / fuel_sources + toxins -= min(toxins, toxins * used_fuel_ratio * used_reactants_ratio ) - oxygen = max(0,oxygen-consumed_gas) + carbon_dioxide += max(2 * total_fuel, 0) - toxins = max(0,toxins-consumed_gas) + if(fuel) + fuel.moles -= fuel.moles * used_fuel_ratio * used_reactants_ratio + if(fuel.moles <= 0) del fuel - carbon_dioxide += consumed_gas*2 + if(liquid) + liquid.amount -= liquid.amount * used_fuel_ratio * used_reactants_ratio + if(liquid.amount <= 0) del liquid - if(fuel) - fuel.moles -= consumed_gas - if(fuel.moles <= 0) del fuel - - if(liquid) - liquid.amount -= consumed_gas - if(liquid.amount <= 0) del liquid - - update_values() - return consumed_gas*fuel_sources - return 0 + //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() + + update_values() + value = total_reactants * used_reactants_ratio + return value +datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid) + //this check comes up very often and is thus centralized here to ease adding stuff + + var/datum/gas/volatile_fuel/fuel = locate() in trace_gases + var/value = 0 + + if(oxygen && (toxins || fuel || liquid)) + value = 1 + + return value datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid) - //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas. - var - datum/gas/volatile_fuel/fuel = locate() in trace_gases - liquid_concentration = 0 + //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas. + + var/datum/gas/volatile_fuel/fuel = locate() in trace_gases + var/total_fuel = 0 + var/firelevel = 0 + + if(check_combustability(liquid)) + + total_fuel += toxins - oxy_concentration = oxygen / volume - tox_concentration = toxins / volume - fuel_concentration = 0 + if(liquid) + total_fuel += liquid.amount - if(fuel) fuel_concentration = (fuel.moles) / volume - if(liquid) liquid_concentration = (liquid.amount*15) / volume - return (oxy_concentration + tox_concentration + liquid_concentration + fuel_concentration)*100 + if(fuel) + total_fuel += fuel.moles -/mob/living/carbon/human/proc/FireBurn(last_temperature, mx as num) + var/total_combustables = (total_fuel + oxygen) + + if(total_fuel > 0 && oxygen > 0) + + //slows down the burning when the concentration of the reactants is low + var/dampening_multiplier = total_combustables / (total_combustables + nitrogen + carbon_dioxide) + //calculates how close the mixture of the reactants is to the optimum + var/mix_multiplier = 1 / (1 + (5 * ((oxygen / total_combustables) ^2))) + //toss everything together + firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * dampening_multiplier + + return max( 0, firelevel) + + +/mob/living/carbon/human/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure) +// mostly using the old proc from Sky until I can think of something better //Burns mobs due to fire. Respects heat transfer coefficients on various body parts. + //Due to TG reworking how fireprotection works, this is kinda less meaningful. var head_exposure = 1 @@ -266,8 +293,12 @@ datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fue legs_exposure = 1 arms_exposure = 1 - //Get heat transfer coefficients for clothing. + //determine the multiplier + //minimize this for low-pressure enviroments + var/mx = 5 * firelevel/vsc.fire_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1) + //Get heat transfer coefficients for clothing. + //skytodo: kill anyone who breaks things then orders me to fix them for(var/obj/item/clothing/C in src) if(l_hand == C || r_hand == C) continue @@ -294,4 +325,4 @@ datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fue apply_damage(0.4*mx*arms_exposure, BURN, "l_arm", 0, 0, "Fire") apply_damage(0.4*mx*arms_exposure, BURN, "r_arm", 0, 0, "Fire") - //flash_pain() \ No newline at end of file + //flash_pain()