diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index c6bd68c4..cdbbd6cd 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -35,37 +35,34 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) if(air_contents.check_combustability(liquid)) igniting = 1 - create_fire(1000) + create_fire(vsc.fire_firelevel_multiplier) return igniting /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) + + 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) - 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) return 0 @@ -79,8 +76,13 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) return 1 fire = new(src, fl) - zone.fire_tiles |= src 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 @@ -123,8 +125,8 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) 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(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the humans! + 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) @@ -146,10 +148,11 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) if(!enemy_tile.zone || enemy_tile.fire) continue - if(!enemy_tile.zone.fire_tiles.len) - var/datum/gas_mixture/acs = enemy_tile.return_air() - if(!acs || !acs.check_combustability()) - continue + //if(!enemy_tile.zone.fire_tiles.len) + 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. @@ -178,10 +181,7 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) /obj/fire/Del() if (istype(loc, /turf/simulated)) - SetLuminosity(0) - - loc = null - air_master.active_hotspots.Remove(src) + RemoveFire() ..() @@ -198,65 +198,101 @@ turf/simulated/apply_fire_protection() fire_protection = world.time -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 - if((temperature > PLASMA_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(liquid))) + if ((temperature > PLASMA_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustability(zone? zone.fuel_objs : null))) + var/gas_fuel = 0 + var/liquid_fuel = 0 var/total_fuel = 0 var/total_oxidizers = 0 - for(var/g in gas) + for (var/g in gas) if(gas_data.flags[g] & XGM_GAS_FUEL) - total_fuel += gas[g] + gas_fuel += gas[g] if(gas_data.flags[g] & XGM_GAS_OXIDIZER) total_oxidizers += gas[g] + gas_fuel *= group_multiplier + total_oxidizers *= group_multiplier - if(liquid) - //Liquid Fuel - if(liquid.amount <= 0.1) - del liquid - else - total_fuel += liquid.amount + if (zone) + for (var/obj/effect/decal/cleanable/liquid_fuel/fuel in zone.fuel_objs) + liquid_fuel += fuel.amount*LIQUIDFUEL_AMOUNT_TO_MOL - if(total_fuel == 0) + total_fuel = gas_fuel + liquid_fuel + + if (total_fuel <= 0.005) return 0 - //Calculate the firelevel. - var/firelevel = calculate_firelevel(liquid, total_fuel, total_oxidizers, force = 1) + //*** Determine how fast the fire burns - //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 var/starting_energy = temperature * heat_capacity() - //determine the amount of oxygen used - var/used_oxidizers = min(total_oxidizers, total_fuel / 2) + //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 - //determine the amount of fuel actually used - var/used_fuel_ratio = min(2 * total_oxidizers , total_fuel) / total_fuel - total_fuel = total_fuel * used_fuel_ratio + //determine the actual rate of reaction, as measured by the amount of fuel reacting - 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 - var/used_reactants_ratio = min(max(total_reactants * firelevel / vsc.fire_firelevel_multiplier, 0.2), total_reactants) / total_reactants + //world << "liquid_reaction_progress = [liquid_reaction_progress]" + //world << "gas_reaction_progress = [gas_reaction_progress]" + + 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) + //world << "used_fuel = [used_fuel]; used_oxidizers = [used_oxidizers]; reaction_limit=[reaction_limit]" + + if (zone && zone.fuel_objs.len) + if (used_fuel <= FIRE_LIQUD_MIN_BURNRATE) + return 0 + else if (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 - remove_by_flag(XGM_GAS_OXIDIZER, used_oxidizers * used_reactants_ratio) - remove_by_flag(XGM_GAS_FUEL, total_fuel * 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 + 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) - liquid.amount -= (liquid.amount * used_fuel_ratio * used_reactants_ratio) * 5 // liquid fuel burns 5 times as quick + //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. + 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 - 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() - . = 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 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && gas[g] >= 0.1) @@ -266,7 +302,7 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_ if(!.) return 0 - if(liquid) + if(fuel_objs && fuel_objs.len) return 1 . = 0 @@ -275,7 +311,7 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_ . = 1 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 for(var/g in gas) if(gas_data.flags[g] & XGM_GAS_OXIDIZER && QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) @@ -294,20 +330,11 @@ datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fu . = 1 break -datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid, total_fuel = null, total_oxidizers = null, force = 0) +/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. var/firelevel = 0 - if(force || check_recombustability(liquid)) - 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 - + if(force || check_recombustability(fuel_objs)) var/total_combustables = (total_fuel + total_oxidizers) if(total_combustables > 0) diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm index 1f2cbee8..806db550 100644 --- a/code/ZAS/Zone.dm +++ b/code/ZAS/Zone.dm @@ -44,6 +44,7 @@ Class Procs: /zone/var/invalid = 0 /zone/var/list/contents = list() /zone/var/list/fire_tiles = list() +/zone/var/list/fuel_objs = list() /zone/var/needs_update = 0 @@ -72,8 +73,11 @@ Class Procs: T.zone = src contents.Add(T) if(T.fire) + var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in 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) /zone/proc/remove(turf/simulated/T) @@ -85,6 +89,9 @@ Class Procs: #endif contents.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.update_graphic(graphic_remove = air.graphic) if(contents.len) @@ -142,7 +149,7 @@ Class Procs: M << name for(var/g in air.gas) M << "[gas_data.name[g]]: [air.gas[g]]" - M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)" + M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]�K ([air.temperature - T0C]�C)" M << "O2 per N2: [(air.gas["nitrogen"] ? air.gas["oxygen"]/air.gas["nitrogen"] : "N/A")] Moles: [air.total_moles]" M << "Simulated: [contents.len] ([air.group_multiplier])" //M << "Unsimulated: [unsimulated_contents.len]" @@ -162,4 +169,4 @@ Class Procs: M << "Space Edges: [space_edges] ([space_coefficient] connections)" //for(var/turf/T in unsimulated_contents) - // M << "[T] at ([T.x],[T.y])" \ No newline at end of file + // M << "[T] at ([T.x],[T.y])" diff --git a/code/ZAS/_gas_mixture_xgm.dm b/code/ZAS/_gas_mixture_xgm.dm index 8f30ca40..54921b93 100644 --- a/code/ZAS/_gas_mixture_xgm.dm +++ b/code/ZAS/_gas_mixture_xgm.dm @@ -100,7 +100,7 @@ /datum/gas_mixture/proc/add_thermal_energy(var/thermal_energy) if (total_moles == 0) return 0 - + var/heat_capacity = heat_capacity() if (thermal_energy < 0) if (temperature < TCMB) @@ -121,7 +121,7 @@ /datum/gas_mixture/proc/specific_entropy() if (!gas.len || total_moles == 0) return SPECIFIC_ENTROPY_VACUUM - + . = 0 for(var/g in gas) . += gas[g] * specific_entropy_gas(g) @@ -129,24 +129,24 @@ /* It's arguable whether this should even be called entropy anymore. It's more "based on" entropy than actually entropy now. - + Returns the ideal gas specific entropy of a specific gas in the mix. This is the entropy due to that gas per mole of /that/ gas in the mixture, not the entropy due to that gas per mole of gas mixture. - + For the purposes of SS13, the specific entropy is just a number that tells you how hard it is to move gas. You can replace this with whatever you want. Just remember that returning a SMALL number == adding gas to this gas mix is HARD, taking gas away is EASY, and that returning a LARGE number means the opposite (so a vacuum should approach infinity). - So returning a constant/(partial pressure) would probably do what most players expect. Although the version I have implemented below is a bit more nuanced than simply 1/P in that it scales in a way + So returning a constant/(partial pressure) would probably do what most players expect. Although the version I have implemented below is a bit more nuanced than simply 1/P in that it scales in a way which is bit more realistic (natural log), and returns a fairly accurate entropy around room temperatures and pressures. */ /datum/gas_mixture/proc/specific_entropy_gas(var/gasid) if (!(gasid in gas) || gas[gasid] == 0) return SPECIFIC_ENTROPY_VACUUM //that gas isn't here - + //group_multiplier gets divided out in volume/gas[gasid] - also, V/(m*T) = R/(partial pressure) var/molar_mass = gas_data.molar_mass[gasid] var/specific_heat = gas_data.specific_heat[gasid] return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * temperature)) * (molar_mass*specific_heat*temperature)**(2/3) + 1 ) + 15 ) - + //alternative, simpler equation //var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume //return R_IDEAL_GAS_EQUATION * ( log (1 + IDEAL_GAS_ENTROPY_CONSTANT/partial_pressure) + 20 ) @@ -265,7 +265,7 @@ return 1 /datum/gas_mixture/proc/react(atom/dump_location) - zburn(null) + zburn(null, force_burn = 0, no_check = 0) //Rechecks the gas_mixture and adjusts the graphic list if needed. //Two lists can be passed by reference if you need know specifically which graphics were added and removed. diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 969d34e0..8ac7f2f4 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -193,9 +193,7 @@ update_flag else can_label = 0 - if(air_contents.temperature > PLASMA_FLASHPOINT) - air_contents.zburn() - return + air_contents.react() /obj/machinery/portable_atmospherics/canister/return_air() return air_contents diff --git a/code/game/objects/effects/decals/Cleanable/fuel.dm b/code/game/objects/effects/decals/Cleanable/fuel.dm index f3faa7ed..61597be5 100644 --- a/code/game/objects/effects/decals/Cleanable/fuel.dm +++ b/code/game/objects/effects/decals/Cleanable/fuel.dm @@ -9,29 +9,39 @@ obj/effect/decal/cleanable/liquid_fuel New(newLoc,amt=1) src.amount = amt + var/has_spread = 0 //Be absorbed by any other liquid fuel in the tile. for(var/obj/effect/decal/cleanable/liquid_fuel/other in newLoc) if(other != src) other.amount += src.amount - spawn other.Spread() - del src + other.Spread() + has_spread = 1 + break - Spread() . = ..() + if (!has_spread) + Spread() + else + del(src) - proc/Spread() + proc/Spread(exclude = list()) //Allows liquid fuels to sometimes flow into other tiles. - if(amount < 5.0) return + if(amount < 15) return var/turf/simulated/S = loc if(!istype(S)) return for(var/d in cardinal) - if(rand(25)) - var/turf/simulated/target = get_step(src,d) - var/turf/simulated/origin = get_turf(src) - if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0)) - if(!locate(/obj/effect/decal/cleanable/liquid_fuel) in target) - new/obj/effect/decal/cleanable/liquid_fuel(target, amount*0.25) - amount *= 0.75 + var/turf/simulated/target = get_step(src, d) + var/turf/simulated/origin = get_turf(src) + 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 (other_fuel) + other_fuel.amount += amount * 0.25 + 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 icon_state = "mustard" diff --git a/code/setup.dm b/code/setup.dm index da8bfac5..4fc51f38 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -135,6 +135,18 @@ #define PLASMA_MINIMUM_OXYGEN_PLASMA_RATIO 20 #define PLASMA_OXYGEN_FULLBURN 10 +//For other fires +#define FIRE_REACTION_OXIDIZER_AMOUNT 3 +#define FIRE_REACTION_FUEL_AMOUNT 2 + +#define FIRE_GAS_BURNRATE_MULT 1 +#define FIRE_LIQUID_BURNRATE_MULT 0.5 + +#define LIQUIDFUEL_AMOUNT_TO_MOL 1 + +#define FIRE_GAS_MIN_BURNRATE 0.1 +#define FIRE_LIQUD_MIN_BURNRATE 0.05 + #define T0C 273.15 // 0degC #define T20C 293.15 // 20degC #define TCMB 2.7 // -270.3degC