diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index 9e1c25f28bf..793d236b462 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -17,6 +17,8 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin return simulated /turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) + if(fire_protection > world.time-300) + return 0 if(locate(/obj/fire) in src) return 1 @@ -24,11 +26,11 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin if(!air_contents || exposed_temperature < FLAMMABLE_GAS_MINIMUM_BURN_TEMPERATURE) return 0 - //vaporize_fuel(air_contents) - var/igniting = 0 - if(air_contents.check_combustibility()) + var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src + if(air_contents.check_combustibility(liquid)) igniting = 1 + create_fire(exposed_temperature) return igniting @@ -44,17 +46,47 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin 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/T in fire_tiles) + for(var/turf/simulated/T in fire_tiles) if(istype(T.fire)) - qdel(T.fire) + T.fire.RemoveFire() + T.fire = null fire_tiles.Cut() + fuel_objs.Cut() if(!fire_tiles.len) SSair.active_fire_zones.Remove(src) +/zone/proc/remove_liquidfuel(used_liquid_fuel, 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 @@ -65,14 +97,17 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin fire = new(src, fl) SSair.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 = TRUE - mouse_opacity = MOUSE_OPACITY_UNCLICKABLE + anchored = 1 + mouse_opacity = 0 blend_mode = BLEND_ADD @@ -86,12 +121,12 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin /obj/fire/Process() . = 1 - var/turf/my_tile = loc + var/turf/simulated/my_tile = loc if(!istype(my_tile) || !my_tile.zone) if(my_tile && my_tile.fire == src) my_tile.fire = null - qdel(src) - return PROCESS_KILL + RemoveFire() + return 1 var/datum/gas_mixture/air_contents = my_tile.return_air() @@ -112,15 +147,9 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin for(var/atom/A in loc) A.fire_act(air_contents, air_contents.temperature, air_contents.volume) - // prioritize nearby fuel overlays first - for(var/direction in GLOB.cardinal) - var/turf/enemy_tile = get_step(my_tile, direction) - if(istype(enemy_tile) && enemy_tile.reagents) - enemy_tile.hotspot_expose(air_contents.temperature, air_contents.volume) - //spread for(var/direction in GLOB.cardinal) - var/turf/enemy_tile = get_step(my_tile, direction) + 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 @@ -129,11 +158,18 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin //if(!enemy_tile.zone.fire_tiles.len) TODO - optimize var/datum/gas_mixture/acs = enemy_tile.return_air() - if(!acs || !acs.check_combustibility()) + var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in enemy_tile + if(!acs || !acs.check_combustibility(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)) + if(prob(50 + 50 * (firelevel / vsc.fire_firelevel_multiplier)) && my_tile.CanPass(src, enemy_tile) && enemy_tile.CanPass(src, my_tile)) enemy_tile.create_fire(firelevel) else @@ -142,11 +178,12 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin animate(src, color = fire_color(air_contents.temperature), 5) set_light(l_color = color) -/obj/fire/Initialize(mapload, fl) - . = ..() +/obj/fire/New(newLoc,fl) + ..() - if(!isturf(loc)) - return INITIALIZE_HINT_QDEL + if(!istype(loc, /turf)) + qdel(src) + return set_dir(pick(GLOB.cardinal)) @@ -157,40 +194,63 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin firelevel = fl SSair.active_hotspots.Add(src) + if (fl >= 2) + playsound(src.loc, SFX_EXPLOSION_FUEL, 50, FALSE, 15) + /obj/fire/proc/fire_color(env_temperature) var/temperature = max(4000*sqrt(firelevel/vsc.fire_firelevel_multiplier), env_temperature) return heat2color(temperature) /obj/fire/Destroy() + RemoveFire() + return ..() + +/obj/fire/proc/RemoveFire() var/turf/T = loc if (istype(T)) set_light(0) + T.fire = null + forceMove(null) SSair.active_hotspots.Remove(src) - . = ..() + +/// Protects newly extinguished tiles from being overrun again. +/turf/var/fire_protection = 0 +/turf/proc/apply_fire_protection() + fire_protection = world.time //Returns the firelevel /datum/gas_mixture/proc/react(zone/zone, force_burn, no_check = 0) . = 0 - if((temperature > FLAMMABLE_GAS_MINIMUM_BURN_TEMPERATURE || force_burn) && (no_check ||check_recombustibility())) + if((temperature > FLAMMABLE_GAS_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) - total_fuel += gas[g] + gas_fuel += gas[g] if(gas_data.flags[g] & XGM_GAS_OXIDIZER) total_oxidizers += gas[g] - total_fuel *= group_multiplier + 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 @@ -203,36 +263,54 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin //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/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, volume*group_multiplier) / vsc.fire_firelevel_multiplier + //vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old react).) + 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/total_reaction_progress = min(max(min_burn, firelevel*total_fuel)*FIRE_GAS_BURNRATE_MULT, total_fuel) + 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("total_fuel = [total_fuel], total_oxidizers = [total_oxidizers]") + 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]") + 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 && (!total_fuel || used_fuel <= FIRE_GAS_MIN_BURNRATE*zone.contents.len)) - return 0 + 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 - used_fuel = min(used_fuel, total_fuel) + 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) - var/datum/gas_mixture/burned_fuel = remove_by_flag(XGM_GAS_FUEL, used_fuel) + var/datum/gas_mixture/burned_fuel = remove_by_flag(XGM_GAS_FUEL, used_gas_fuel) for(var/g in burned_fuel.gas) adjust_gas(gas_data.burn_product[g], burned_fuel.gas[g]) + if(zone) + zone.remove_liquidfuel(used_liquid_fuel, !check_combustibility()) + //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_fuel) / heat_capacity() + temperature = (starting_energy + vsc.fire_fuel_energy_release * (used_gas_fuel + used_liquid_fuel)) / heat_capacity() update_values() #ifdef FIREDBG @@ -242,7 +320,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin return firelevel -/datum/gas_mixture/proc/check_recombustibility() +/datum/gas_mixture/proc/check_recombustability(list/fuel_objs) . = 0 for(var/g in gas) if(gas[g] >= 0.1) @@ -253,6 +331,9 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin if(!.) return 0 + if(fuel_objs && fuel_objs.len) + return 1 + . = 0 for(var/g in gas) if(gas[g] >= 0.1) @@ -260,7 +341,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin . = 1 break -/datum/gas_mixture/proc/check_combustibility() +/datum/gas_mixture/proc/check_combustibility(obj/effect/decal/cleanable/liquid_fuel/liquid = null) . = 0 for(var/g in gas) if(QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) @@ -271,6 +352,9 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin if(!.) return 0 + if(liquid) + return 1 + . = 0 for(var/g in gas) if(QUANTIZE(gas[g] * vsc.fire_consuption_rate) >= 0.1) diff --git a/code/modules/ZAS/Zone.dm b/code/modules/ZAS/Zone.dm index 6aa58a54501..5cbdbe2fe95 100644 --- a/code/modules/ZAS/Zone.dm +++ b/code/modules/ZAS/Zone.dm @@ -52,6 +52,7 @@ Class Procs: var/list/graphic_remove = list() var/last_air_temperature = TCMB var/condensing = FALSE + var/list/fuel_objs = list() /zone/New() SSair.add_zone(src) @@ -72,6 +73,9 @@ Class Procs: T.zone = src contents.Add(T) if(T.fire) + var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T + if(fuel) + fuel_objs += fuel fire_tiles.Add(T) SSair.active_fire_zones |= src T.update_graphic(air.graphic) @@ -87,6 +91,9 @@ Class Procs: T.c_copy_air() // to avoid losing contents 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(air.graphic) if(contents.len) diff --git a/code/modules/atmospherics/datum_pipeline.dm b/code/modules/atmospherics/datum_pipeline.dm index 13f539aa46c..4b04b04b378 100644 --- a/code/modules/atmospherics/datum_pipeline.dm +++ b/code/modules/atmospherics/datum_pipeline.dm @@ -102,7 +102,7 @@ possible_expansions += item item.getting_pipelined = TRUE members += item - register_signal(item, SIGNAL_QDELETING, nameof(.proc/on_member_qdel)) + register_signal(item, SIGNAL_QDELETING, nameof(.proc/on_member_qdel), TRUE) volume += item.volume @@ -122,7 +122,7 @@ if(edge_check > 0) edges += borderline - register_signal(borderline, SIGNAL_QDELETING, nameof(.proc/on_borderline_qdel)) + register_signal(borderline, SIGNAL_QDELETING, nameof(.proc/on_borderline_qdel), TRUE) possible_expansions -= borderline