mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-22 05:17:38 +01:00
9b5f89a717
Adds turf fires (ported from Baystation, which ported them from Shiptest), which are now used instead of ZAS fires for fuel decals and flamethrowers. They also supplement ZAS fires in especially bad atmos fires. This makes flamethrowers and related objects more suitable for AOE denial, rather than cooking the room (including yourself). They still do the latter, but less so than a gaseous fire. To make flamethrowers actually useful against them, simple mobs now take brute damage when on fire. ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | --- | --- | --- | | sound/weapons/flamethrower/flamethrower1.ogg | [rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 | | sound/weapons/flamethrower/flamethrower2.ogg | [rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 | | sound/weapons/flamethrower/flamethrower3.ogg | [rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 | | sound/weapons/flamethrower/flamethrower_empty.ogg | [rye-rice](https://github.com/rye-rice) (Shiptest) | CC-BY-SA 3.0 | | icons/effects/particles/smoke.dmi | [rye-rice](https://github.com/shiptest-ss13/Shiptest/commits?author=rye-rice) (Shiptest) | CC-BY-SA 3.0 | | icons/effects/turf_fire.dmi | CM Dev (cmss13) | CC-BY-SA 3.0 |
444 lines
14 KiB
Plaintext
444 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 FIRE_LIGHT_1 2 //These defines are the power of the light given off by fire at various stages
|
|
#define FIRE_LIGHT_2 4
|
|
#define FIRE_LIGHT_3 5
|
|
|
|
//The absolute minimum temperature, below which a fire will go out. In Kelvins
|
|
#define MINIMUM_HEAT_THRESHOLD 220
|
|
|
|
/turf
|
|
|
|
//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)
|
|
return
|
|
|
|
/**
|
|
* Create a temporary ignition source at turf. Think sparks/igniters.
|
|
*
|
|
* Checks for flammable materials at its location.
|
|
*
|
|
* * exposed_temperature - ignition source temp (K)
|
|
* * exposed_volume - gas volume (passed to fire_act() for locs)
|
|
*
|
|
* Returns boolean and/or executes create_fire(exposed_temperature).
|
|
*/
|
|
/turf/simulated/hotspot_expose(exposed_temperature, exposed_volume)
|
|
if(fire_protection > world.time-300)
|
|
return FALSE
|
|
if(locate(/obj/hotspot) in src)
|
|
return TRUE
|
|
var/datum/gas_mixture/air_contents = return_air()
|
|
if(!air_contents || exposed_temperature < PHORON_MINIMUM_BURN_TEMPERATURE)
|
|
return FALSE
|
|
|
|
var/igniting = FALSE
|
|
var/obj/effect/decal/cleanable/liquid_fuel/liquid = locate() in src
|
|
if(liquid && air_contents.check_combustibility(liquid))
|
|
IgniteTurf(liquid.amount * 10)
|
|
QDEL_NULL(liquid)
|
|
|
|
if(air_contents.check_combustibility())
|
|
igniting = TRUE
|
|
create_fire(exposed_temperature)
|
|
|
|
return igniting
|
|
|
|
/zone/proc/process_fire()
|
|
var/datum/gas_mixture/burn_gas = air.remove_ratio(GLOB.vsc.fire_consuption_rate, LAZYLEN(fire_tiles))
|
|
|
|
var/firelevel = burn_gas.zburn(src, fire_tiles, force_burn = TRUE, no_check = TRUE)
|
|
|
|
air.merge(burn_gas)
|
|
|
|
if(firelevel)
|
|
for(var/turf/T in fire_tiles)
|
|
if(T.hotspot)
|
|
T.hotspot.firelevel = firelevel
|
|
else
|
|
LAZYREMOVE(fire_tiles, T)
|
|
else
|
|
for(var/turf/simulated/T in fire_tiles)
|
|
if(istype(T.hotspot))
|
|
qdel(T.hotspot)
|
|
LAZYCLEARLIST(fire_tiles)
|
|
UNSETEMPTY(fire_tiles)
|
|
|
|
if(!LAZYLEN(fire_tiles))
|
|
SSair.active_fire_zones.Remove(src)
|
|
|
|
/turf/proc/create_fire(fl)
|
|
return FALSE
|
|
|
|
/turf/simulated/create_fire(fl)
|
|
|
|
if(hotspot)
|
|
hotspot.firelevel = max(fl, hotspot.firelevel)
|
|
return TRUE
|
|
|
|
if(!zone)
|
|
return TRUE
|
|
|
|
hotspot = new(src, fl)
|
|
SSair.active_fire_zones |= zone
|
|
|
|
var/obj/effect/decal/cleanable/foam/extinguisher_foam = locate() in src
|
|
if(extinguisher_foam && extinguisher_foam.reagents)
|
|
hotspot.firelevel *= max(0,1 - (extinguisher_foam.reagents.total_volume*0.04))
|
|
//25 units will eliminate the fire completely
|
|
|
|
LAZYOR(zone.fire_tiles, src)
|
|
|
|
return FALSE
|
|
|
|
// Future low-quality heat effect
|
|
/obj/heat
|
|
icon = 'icons/effects/fire.dmi'
|
|
icon_state = "3"
|
|
appearance_flags = PIXEL_SCALE | NO_CLIENT_COLOR
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
render_target = HEAT_EFFECT_PLATE_RENDER_TARGET
|
|
|
|
/obj/hotspot
|
|
//Icon for fire on turfs.
|
|
//Different from turf fires
|
|
anchored = 1
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
|
|
blend_mode = BLEND_ADD
|
|
|
|
icon = 'icons/effects/fire.dmi'
|
|
icon_state = "wavey_fire"
|
|
light_color = LIGHT_COLOR_FIRE
|
|
layer = FIRE_LAYER
|
|
|
|
var/firelevel = 1 //Calculated by gas_mixture.calculate_firelevel()
|
|
|
|
/obj/hotspot/process()
|
|
. = 1
|
|
|
|
var/turf/simulated/my_tile = loc
|
|
if(!istype(my_tile) || !my_tile.zone)
|
|
if(my_tile && my_tile.hotspot == src)
|
|
my_tile.hotspot = null
|
|
qdel(src)
|
|
return PROCESS_KILL
|
|
|
|
var/datum/gas_mixture/air_contents = my_tile.return_air()
|
|
|
|
if(firelevel > 6)
|
|
set_light(9, FIRE_LIGHT_3) // We set color later in the proc, that should trigger an update.
|
|
else if(firelevel > 2.5)
|
|
set_light(7, FIRE_LIGHT_2)
|
|
else
|
|
set_light(5, FIRE_LIGHT_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.temperature, air_contents.volume)
|
|
for(var/atom/A in loc)
|
|
A.fire_act(air_contents.temperature, air_contents.volume)
|
|
|
|
//spread
|
|
for(var/direction in GLOB.cardinals)
|
|
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.hotspot)
|
|
continue
|
|
|
|
//if(!enemy_tile.zone.fire_tiles.len) TODO - optimize
|
|
var/datum/gas_mixture/acs = enemy_tile.return_air()
|
|
if(!acs || !acs.check_combustibility())
|
|
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/GLOB.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)
|
|
|
|
set_light(l_color = fire_color(air_contents.temperature, TRUE))
|
|
var/list/animate_targets = get_above_oo() + src
|
|
for (var/thing in animate_targets)
|
|
var/atom/movable/AM = thing
|
|
animate(AM, color = fire_color(air_contents.temperature), 5)
|
|
|
|
/obj/hotspot/Initialize(mapload, fl)
|
|
. = ..()
|
|
|
|
if(!isturf(loc))
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
set_dir(pick(GLOB.cardinals))
|
|
|
|
var/datum/gas_mixture/air_contents = loc.return_air()
|
|
color = fire_color(air_contents.temperature)
|
|
set_light(3, 1, color)
|
|
|
|
firelevel = fl
|
|
SSair.active_hotspots.Add(src)
|
|
|
|
//If this is a turf and it can burn maybe it should be ignited too - Lingering fire effect of sorts
|
|
var/turf/simulated/floor/F = loc
|
|
if(istype(F) && F.flooring && F.flooring.flags & TURF_CAN_BURN)
|
|
if(prob(30))
|
|
F.IgniteTurf(rand(8,22))
|
|
|
|
/obj/hotspot/proc/fire_color(var/env_temperature)
|
|
var/temperature = max(4000*sqrt(firelevel/GLOB.vsc.fire_firelevel_multiplier), env_temperature)
|
|
return heat2color(temperature)
|
|
|
|
/obj/hotspot/Destroy()
|
|
var/turf/T = loc
|
|
if (istype(T))
|
|
set_light(0)
|
|
T.hotspot = null
|
|
SSair.active_hotspots.Remove(src)
|
|
. = ..()
|
|
|
|
/turf/simulated
|
|
var/tmp/fire_protection = 0 //Protects newly extinguished tiles from being overrun again.
|
|
|
|
/turf/proc/apply_fire_protection()
|
|
return
|
|
|
|
/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_combustibility()))
|
|
|
|
#ifdef ZASDBG
|
|
log_subsystem_zas_debug("***************** FIREDBG *****************")
|
|
log_subsystem_zas_debug("Burning [zone? zone.name : "zoneless gas_mixture"]!")
|
|
#endif
|
|
|
|
var/gas_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
|
|
|
|
total_fuel = gas_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) / GLOB.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)
|
|
|
|
var/firelevel = (gas_fuel*gas_firelevel)/total_fuel
|
|
|
|
var/total_reaction_progress = gas_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 ZASDBG
|
|
log_subsystem_zas_debug("gas_fuel = [gas_fuel], total_oxidizers = [total_oxidizers]")
|
|
log_subsystem_zas_debug("total_fuel = [total_fuel], reaction_limit = [reaction_limit]")
|
|
log_subsystem_zas_debug("firelevel -> [firelevel] (gas: [gas_firelevel])")
|
|
log_subsystem_zas_debug("gas_reaction_progress = [gas_reaction_progress]")
|
|
log_subsystem_zas_debug("total_reaction_progress = [total_reaction_progress]")
|
|
log_subsystem_zas_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((!gas_fuel || used_fuel <= FIRE_GAS_MIN_BURNRATE*length(zone.contents)))
|
|
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
|
|
|
|
//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(GAS_CO2, used_oxidizers)
|
|
|
|
//calculate the energy produced by the reaction and then set the new temperature of the mix
|
|
temperature = (starting_energy + GLOB.vsc.fire_fuel_energy_release * (used_gas_fuel)) / heat_capacity()
|
|
update_values()
|
|
|
|
#ifdef ZASDBG
|
|
log_subsystem_zas_debug("used_gas_fuel = [used_gas_fuel]; total = [used_fuel]")
|
|
log_subsystem_zas_debug("new temperature = [temperature]; new pressure = [return_pressure()]")
|
|
#endif
|
|
|
|
if (temperature < MINIMUM_HEAT_THRESHOLD)
|
|
firelevel = 0
|
|
|
|
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 && length(fuel_objs))
|
|
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_combustibility(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] * GLOB.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] * GLOB.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
|
|
if(!total_moles || !group_multiplier)
|
|
damping_multiplier = min(1, active_combustables)
|
|
else if(!total_moles)
|
|
damping_multiplier = min(1, active_combustables / group_multiplier)
|
|
else if(!group_multiplier)
|
|
damping_multiplier = min(1, active_combustables / total_moles)
|
|
else
|
|
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 ZASDBG
|
|
ASSERT(damping_multiplier <= 1)
|
|
ASSERT(mix_multiplier <= 1)
|
|
#endif
|
|
|
|
//toss everything together -- should produce a value between 0 and fire_firelevel_multiplier
|
|
firelevel = GLOB.vsc.fire_firelevel_multiplier * mix_multiplier * damping_multiplier
|
|
|
|
return max( 0, firelevel)
|
|
|
|
|
|
/mob/living/proc/FireBurn(firelevel, last_temperature, pressure)
|
|
var/mx = 5 * firelevel/GLOB.vsc.fire_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
|
|
apply_damage(2.5*mx, DAMAGE_BURN)
|
|
|
|
/**
|
|
* Applies direct damage to human mobs to fire.
|
|
* On the chopping block depending on how we feel.
|
|
* We can decide its fate in future reworks.
|
|
*/
|
|
/mob/living/carbon/human/FireBurn(firelevel, last_temperature, 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/GLOB.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, DAMAGE_BURN, BP_HEAD, used_weapon = "Fire")
|
|
apply_damage(2.5*mx*chest_exposure, DAMAGE_BURN, BP_CHEST, used_weapon = "Fire")
|
|
apply_damage(2.0*mx*groin_exposure, DAMAGE_BURN, BP_GROIN, used_weapon = "Fire")
|
|
apply_damage(0.6*mx*legs_exposure, DAMAGE_BURN, BP_L_LEG, used_weapon = "Fire")
|
|
apply_damage(0.6*mx*legs_exposure, DAMAGE_BURN, BP_R_LEG, used_weapon = "Fire")
|
|
apply_damage(0.4*mx*arms_exposure, DAMAGE_BURN, BP_L_ARM, used_weapon = "Fire")
|
|
apply_damage(0.4*mx*arms_exposure, DAMAGE_BURN, BP_R_ARM, used_weapon = "Fire")
|
|
|
|
#undef FIRE_LIGHT_1
|
|
#undef FIRE_LIGHT_2
|
|
#undef FIRE_LIGHT_3
|
|
|
|
#undef MINIMUM_HEAT_THRESHOLD
|