/* Making Bombs with ZAS: Make burny fire with lots of burning Draw off 5000K gas from burny fire Separate gas into oxygen and phoron components Obtain phoron and oxygen tanks filled up about 50-75% with normal-temp gas Fill rest with super hot gas from separated canisters, they should be about 125C now. Attach to transfer valve and open. BOOM. */ //#define FIREDBG /turf/var/obj/fire/fire = null //Some legacy definitions so fires can be started. atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) return null 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 0 if(locate(/obj/fire) in src) return 1 var/datum/gas_mixture/air_contents = return_air() if(!air_contents || exposed_temperature < PHORON_MINIMUM_BURN_TEMPERATURE) return 0 var/igniting = 0 var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src if(air_contents.check_combustability(liquid)) igniting = 1 create_fire(exposed_temperature) return igniting /zone/proc/process_fire() 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) 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) if(istype(T.fire)) T.fire.RemoveFire() T.fire = null fire_tiles.Cut() fuel_objs.Cut() if(!fire_tiles.len) air_master.active_fire_zones.Remove(src) /zone/proc/remove_liquidfuel(var/used_liquid_fuel, var/remove_fire=0) if(!fuel_objs.len) return //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. It will mean that sometimes we will remove a tiny bit less fuel then we intended to. var/fuel_to_remove = used_liquid_fuel/(fuel_objs.len*LIQUIDFUEL_AMOUNT_TO_MOL) //convert back to liquid volume units for(var/O in fuel_objs) var/obj/effect/decal/cleanable/liquid_fuel/fuel = O if(!istype(fuel)) fuel_objs -= fuel continue fuel.amount -= fuel_to_remove if(fuel.amount <= 0) fuel_objs -= fuel if(remove_fire) var/turf/T = fuel.loc if(istype(T) && T.fire) qdel(T.fire) qdel(fuel) /turf/proc/create_fire(fl) return 0 /turf/simulated/create_fire(fl) if(fire) fire.firelevel = max(fl, fire.firelevel) return 1 if(!zone) return 1 fire = new(src, fl) 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 /obj/fire //Icon for fire on turfs. anchored = 1 mouse_opacity = 0 blend_mode = BLEND_ADD icon = 'icons/effects/fire.dmi' icon_state = "1" light_color = "#ED9200" layer = TURF_LAYER var/firelevel = 1 //Calculated by gas_mixture.calculate_firelevel() /obj/fire/process() . = 1 var/turf/simulated/my_tile = loc if(!istype(my_tile) || !my_tile.zone) if(my_tile.fire == src) my_tile.fire = null RemoveFire() return 1 var/datum/gas_mixture/air_contents = my_tile.return_air() if(firelevel > 6) icon_state = "3" set_light(7, 3) else if(firelevel > 2.5) icon_state = "2" set_light(5, 2) else icon_state = "1" set_light(3, 1) for(var/mob/living/L in loc) L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs! loc.fire_act(air_contents, air_contents.temperature, air_contents.volume) for(var/atom/A in loc) A.fire_act(air_contents, air_contents.temperature, air_contents.volume) //spread for(var/direction in cardinal) var/turf/simulated/enemy_tile = get_step(my_tile, direction) if(istype(enemy_tile)) if(my_tile.open_directions & direction) //Grab all valid bordering tiles if(!enemy_tile.zone || enemy_tile.fire) continue //if(!enemy_tile.zone.fire_tiles.len) TODO - optimize var/datum/gas_mixture/acs = enemy_tile.return_air() var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in enemy_tile if(!acs || !acs.check_combustability(liquid)) continue //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(prob( 50 + 50 * (firelevel/vsc.fire_firelevel_multiplier) ) && my_tile.CanPass(null, enemy_tile, 0,0) && enemy_tile.CanPass(null, my_tile, 0,0)) enemy_tile.create_fire(firelevel) else enemy_tile.adjacent_fire_act(loc, air_contents, air_contents.temperature, air_contents.volume) animate(src, color = fire_color(air_contents.temperature), 5) set_light(l_color = color) /obj/fire/New(newLoc,fl) ..() if(!istype(loc, /turf)) qdel(src) return set_dir(pick(cardinal)) var/datum/gas_mixture/air_contents = loc.return_air() color = fire_color(air_contents.temperature) set_light(3, 1, color) firelevel = fl air_master.active_hotspots.Add(src) /obj/fire/proc/fire_color(var/env_temperature) var/temperature = max(4000*sqrt(firelevel/vsc.fire_firelevel_multiplier), env_temperature) return heat2color(temperature) /obj/fire/Destroy() RemoveFire() ..() /obj/fire/proc/RemoveFire() var/turf/T = loc if (istype(T)) set_light(0) T.fire = null loc = null air_master.active_hotspots.Remove(src) /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 //Returns the firelevel /datum/gas_mixture/proc/zburn(zone/zone, force_burn, no_check = 0) . = 0 if((temperature > PHORON_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(zone? zone.fuel_objs : null))) #ifdef FIREDBG log_debug("***************** FIREDBG *****************") log_debug("Burning [zone? zone.name : "zoneless gas_mixture"]!") #endif var/gas_fuel = 0 var/liquid_fuel = 0 var/total_fuel = 0 var/total_oxidizers = 0 //*** Get the fuel and oxidizer amounts for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_FUEL) gas_fuel += gas[g] if(gas_data.flags[g] & XGM_GAS_OXIDIZER) total_oxidizers += gas[g] gas_fuel *= group_multiplier total_oxidizers *= group_multiplier //Liquid Fuel var/fuel_area = 0 if(zone) for(var/obj/effect/decal/cleanable/liquid_fuel/fuel in zone.fuel_objs) liquid_fuel += fuel.amount*LIQUIDFUEL_AMOUNT_TO_MOL fuel_area++ total_fuel = gas_fuel + liquid_fuel if(total_fuel <= 0.005) return 0 //*** Determine how fast the fire burns //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 var/starting_energy = temperature * heat_capacity() //determine how far the reaction can progress var/reaction_limit = min(total_oxidizers*(FIRE_REACTION_FUEL_AMOUNT/FIRE_REACTION_OXIDIZER_AMOUNT), total_fuel) //stoichiometric limit //calculate the firelevel. var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit) var/firelevel_ratio = firelevel / vsc.fire_firelevel_multiplier //vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).) var/min_burn = 0.30*volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast var/gas_reaction_progress = max(min_burn, firelevel_ratio*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. Limit the burn rate to a certain amount per area. var/liquid_reaction_progress = (firelevel_ratio*0.2 + 0.05)*fuel_area*FIRE_LIQUID_BURNRATE_MULT var/total_reaction_progress = gas_reaction_progress + liquid_reaction_progress var/used_fuel = min(total_reaction_progress, reaction_limit) var/used_oxidizers = used_fuel*(FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT) #ifdef FIREDBG log_debug("gas_fuel = [gas_fuel], liquid_fuel = [liquid_fuel], total_oxidizers = [total_oxidizers]") log_debug("fuel_area = [fuel_area], total_fuel = [total_fuel], reaction_limit = [reaction_limit]") log_debug("firelevel -> [firelevel] / [vsc.fire_firelevel_multiplier]") log_debug("liquid_reaction_progress = [liquid_reaction_progress]") log_debug("gas_reaction_progress = [gas_reaction_progress]") log_debug("total_reaction_progress = [total_reaction_progress]") log_debug("used_fuel = [used_fuel], used_oxidizers = [used_oxidizers]; ") #endif //if the reaction is progressing too slow then it isn't self-sustaining anymore and burns out if(zone) //be less restrictive with canister and tank reactions if((!liquid_fuel || used_fuel <= FIRE_LIQUD_MIN_BURNRATE) && (!gas_fuel || used_fuel <= FIRE_GAS_MIN_BURNRATE*group_multiplier)) return 0 //*** Remove fuel and oxidizer, add carbon dioxide and heat //remove and add gasses as calculated var/used_gas_fuel = min(max(0.25, used_fuel*(gas_reaction_progress/total_reaction_progress)), gas_fuel) //remove in proportion to the relative reaction progress var/used_liquid_fuel = min(max(0.25, used_fuel-used_gas_fuel), liquid_fuel) //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(zone) zone.remove_liquidfuel(used_liquid_fuel, !check_combustability()) //calculate the energy produced by the reaction and then set the new temperature of the mix temperature = (starting_energy + vsc.fire_fuel_energy_release * (used_gas_fuel + used_liquid_fuel)) / heat_capacity() #ifdef FIREDBG log_debug("used_gas_fuel = [used_gas_fuel]; used_liquid_fuel = [used_liquid_fuel]; total = [used_fuel]") log_debug("new temperature = [temperature]") #endif update_values() return firelevel datum/gas_mixture/proc/check_recombustability(list/fuel_objs) . = 0 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && gas[g] >= 0.1) . = 1 break if(!.) return 0 if(fuel_objs && fuel_objs.len) return 1 . = 0 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_FUEL && gas[g] >= 0.1) . = 1 break /datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid=null) . = 0 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) . = 1 break if(!.) return 0 if(liquid) return 1 . = 0 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_FUEL && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.005) . = 1 break //returns a value between 0 and vsc.fire_firelevel_multiplier /datum/gas_mixture/proc/calculate_firelevel(total_fuel, total_oxidizers, reaction_limit) //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas. var/firelevel = 0 var/total_combustables = (total_fuel + total_oxidizers) if(total_combustables > 0) //slows down the burning when the concentration of the reactants is low var/dampening_multiplier = min(1, reaction_limit / (total_moles/group_multiplier)) //calculates how close the mixture of the reactants is to the optimum //fires burn better when there is more oxidizer -- too much fuel will choke them out a bit, reducing firelevel. var/mix_multiplier = 1 / (1 + (5 * ((total_fuel / total_combustables) ** 2))) #ifdef FIREDBG ASSERT(dampening_multiplier <= 1) ASSERT(mix_multiplier <= 1) #endif //toss everything together -- should produce a value between 0 and fire_firelevel_multiplier firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * dampening_multiplier return max( 0, firelevel) /mob/living/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure) var/mx = 5 * firelevel/vsc.fire_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) //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 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(l_hand == C || r_hand == C) continue if( C.max_heat_protection_temperature >= last_temperature ) if(C.body_parts_covered & HEAD) head_exposure = 0 if(C.body_parts_covered & UPPER_TORSO) chest_exposure = 0 if(C.body_parts_covered & LOWER_TORSO) groin_exposure = 0 if(C.body_parts_covered & LEGS) legs_exposure = 0 if(C.body_parts_covered & ARMS) arms_exposure = 0 //minimize this for low-pressure enviroments var/mx = 5 * firelevel/vsc.fire_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. apply_damage(2.5*mx*head_exposure, BURN, "head", 0, 0, "Fire") apply_damage(2.5*mx*chest_exposure, BURN, "chest", 0, 0, "Fire") apply_damage(2.0*mx*groin_exposure, BURN, "groin", 0, 0, "Fire") apply_damage(0.6*mx*legs_exposure, BURN, "l_leg", 0, 0, "Fire") apply_damage(0.6*mx*legs_exposure, BURN, "r_leg", 0, 0, "Fire") 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")