mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
Fixes calculate_firelevel() not counting oxidizer when determining the ratio of reactants to other gases. As well firelevel calculation is now done separately for burning gas and burning liquid. Adjusted FIRE_LIQUID_BURNRATE_MULT so that liquid burn rate is unaffected by this fix, which will result in generally higher firelevels and thus faster burn rates.
435 lines
14 KiB
Plaintext
435 lines
14 KiB
Plaintext
/*
|
|
|
|
Making Bombs with ZAS:
|
|
Get gas to react in an air tank so that it gains pressure. If it gains enough pressure, it goes boom.
|
|
The more pressure, the more boom.
|
|
If it gains pressure too slowly, it may leak or just rupture instead of exploding.
|
|
*/
|
|
|
|
//#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
|
|
|
|
//vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).)
|
|
var/gas_firelevel = calculate_firelevel(gas_fuel, total_oxidizers, reaction_limit, volume*group_multiplier) / vsc.fire_firelevel_multiplier
|
|
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 = min(max(min_burn, gas_firelevel*gas_fuel)*FIRE_GAS_BURNRATE_MULT, gas_fuel)
|
|
|
|
//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_firelevel = calculate_firelevel(liquid_fuel, total_oxidizers, reaction_limit, 0) / vsc.fire_firelevel_multiplier
|
|
var/liquid_reaction_progress = min((liquid_firelevel*0.2 + 0.05)*fuel_area*FIRE_LIQUID_BURNRATE_MULT, liquid_fuel)
|
|
|
|
var/firelevel = (gas_fuel*gas_firelevel + liquid_fuel*liquid_firelevel)/total_fuel
|
|
|
|
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] (gas: [gas_firelevel], liquid: [liquid_firelevel])")
|
|
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*zone.contents.len))
|
|
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()
|
|
update_values()
|
|
|
|
#ifdef FIREDBG
|
|
log_debug("used_gas_fuel = [used_gas_fuel]; used_liquid_fuel = [used_liquid_fuel]; total = [used_fuel]")
|
|
log_debug("new temperature = [temperature]; new pressure = [return_pressure()]")
|
|
#endif
|
|
|
|
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, gas_volume)
|
|
//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)
|
|
var/active_combustables = (FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT + 1)*reaction_limit
|
|
|
|
if(total_combustables > 0)
|
|
//slows down the burning when the concentration of the reactants is low
|
|
var/damping_multiplier = min(1, active_combustables / (total_moles/group_multiplier))
|
|
|
|
//weight the damping mult so that it only really brings down the firelevel when the ratio is closer to 0
|
|
damping_multiplier = 2*damping_multiplier - (damping_multiplier*damping_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 the fire out a bit, reducing firelevel.
|
|
var/mix_multiplier = 1 / (1 + (5 * ((total_fuel / total_combustables) ** 2)))
|
|
|
|
#ifdef FIREDBG
|
|
ASSERT(damping_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 * damping_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")
|