Skip to content

FireSystem

FireSystem

Phase · priority Environment · 40
Reads OnFire, Combustible; temperature, ignition_point, fuel, water_level, controlled_fire
Writes OnFire (add/remove); temperature via PropagationIntent on channel heat; fuel; events ignition, fire_spread, extinguished
Settings HeatOutputPerFuel, FuelBurnPerHour, ExtinguishWaterThreshold, DeltaFactor 0.8, MinThreshold 0.5
Status designed · Phase 1 (#34)
PLAN §11.1, §11.3, §11.5, §18.5, §10.2 (ownership rule)
Player Fire and heat · The campfire

Combustion and heat generation. For every OnFire node: radiate heat as a propagation, burn fuel, check whether neighbours have crossed their kindling point, and check whether water has put the fire out. Fire spreads by heat only; there is no "fire jumps" rule.

foreach (var node in view.NodesWithComponent("OnFire"))
{
    var heat = view.GetOrDefault(node, "fuel", 0f) > 0 ? s.HeatOutputPerFuel * …  : 0f;
    yield return new PropagationIntent(node, channel: "heat", attribute: "temperature",
                                       initialIntensity: heat, deltaFactor: 0.8f, minThreshold: 0.5f);
    yield return new DeltaAttributeIntent(node, "fuel", -ctx.PerTick(s.FuelBurnPerDay));

    foreach (var n in view.GetAdjacentLocations(node))                       // ignition — the trace needs the numbers
    {
        var t = view.GetOrDefault(n, "temperature", 0f); var ig = view.GetOrDefault(n, "ignition_point", float.MaxValue);
        if (t >= ig && view.HasComponent(n, "Combustible") && !view.HasComponent(n, "OnFire"))
            yield return new AddComponentIntent(n, "OnFire") { Inputs = new() { [$"{n}.temperature"] = t, [$"{n}.ignition_point"] = ig } };
    }
    if (view.GetOrDefault(node, "water_level", 0f) >= s.ExtinguishWaterThreshold || fuel <= 0)
        yield return new RemoveComponentIntent(node, "OnFire");               // baseline physics lives here, not in a rule
}

Reach is bounded by permeability × threshold, never a hop cap. The village's edges (0.3 open air, 0.8 between adjacent buildings, 0.4 fire pit → inn) are the firebreaks; a fire that reaches too far has the wrong fuel or intensity, not a missing radius.

Water extinguishes fire here (PLAN §18.5, invariant 10), not in data/rules/: two places that can disagree about whether a fire goes out is the bug.

Campfire: controlled_fire: true, ignition_point: 9999 — it burns, radiates, consumes fuel, never spreads to itself, and can heat the inn's wall.

Hot-toggle: off, burning nodes stay OnFire and stop radiating; spreading halts; on, it resumes from the current state. Trace: ignition intents carry Inputs so Explain can say because the smithy's heat crossed 250°; propagation records keep per-hop intensities.

History

Phase Change
1 Introduced; fuel burn per hour; ignition intents carry Inputs; extinguish-on-water in the system.

See also: HeatSystem · Intents and the resolver (propagation)