Wildfires
On holidays this week, but I managed to squeeze a few updates: fixing a few bugs and adding a new feature. Bugs are boring: some crashes, some GPU buffers not being updated, some timing issues. The feature is a little bit interesting, and for me it was an exercise in reflection on the topic of rabbit holes.
Wildfires
As I was testing fireball effect timings (projectile, ground lighting on fire, creatures lighting on fire, etc), I realised that nearby trees remained unscathed. This is not realistic, but more importantly, it's not as fun as it could be. Since I support dynamic environment anyway, why not actually showcase all that? So, what this means, is that I want:
- Fireball might spawn a "burning fire" object in some empty floor tiles where it hits
- The "burning fire" should last a few rounds before being extinguished
- If there's anything flammable nearby, it might catch fire. This includes trees.
- If a creature steps into the fire, it starts burning
- Burning fire should also be a light source
Alright! To make things simple, I already had some half-baked implementation of a fire object where it behaved like a trap: if you stepped on it, you caught fire.
So, why is this a rabbit hole? Because there are a few things to take into account. Most important was: where do I store and execute the code for the fire's spreading logic? One thought was to have AI like with everything else, but there's certain boilerplate involved with that, so that for simple, repeating actions of non-sentient entities it would be overkill. The solution to that was to introduce another queue in the Time system that stores entities with such simple logic, ordered by when they should execute. And after they execute their logic, they schedule themselves again at the next interval.
Second part is where to store the logic. I expect to have a number of different logic classes, and maybe some objects would use the same logic with different parameters. This is a perfect candidate for my json database, so that was implemented quickly. The logic object itself contains the "tick" function that runs at every simulation step of the object. For the fire, the logic is "am I next to something flammable? Then, maybe I should light it up!". First attempt for that, I ran that logic immediately, and the result was hilarious: I spawned a fire next to a tree, and then immediately a chain reaction started, burning down the entire forest, and the map was filled with fire objects. Ok, that needs fixing. Second iteration: fire has "fuel" that lasts for a few turns, e.g. 5. For the first 3-4 turns we do nothing, and then the fire becomes transmissive and attempts to burn neighbours. After the fuel runs out, the fire gets extinguished. This massively improved things, so a forest can burn down at more reasonable rates, although it still is a bit too fast. Even if I set 20% burning chance for a nearby tree, in a forest situation a tree might end up being surrounded my multiple fire tiles, therefore increasing its chance to light up significantly. This is something that might need tinkering later on.
Next part was to make the fire a light source. That was relatively easy, as entities support some generic-ish properties, like weight, light emission etc. I had to add support for loading these properties via json and fix up a bug or two.
The part where fire object might spawn from the fireball is not implemented yet, but it's trivial.
A final consideration, not mentioned here, is what happens when we change levels. What happens if we leave a level while a forest is burning down? Option 1 is to freeze simulation, but that's unrealistic. "Let me go and find a scroll of rain and douse the flames". That would be nice, but one would expect the fire to move on. Running the simulation in the background sounds also hard, as it won't scale if we need to do that for all simulations and AI. And if we cherry-pick, then we lose on consistency. What to do here? The answer was thankfully already implemented, as I realised: after leaving a level, we run any AI and simulations for the level for a number of turns, before freezing everything. This means that the level changes by the time we're back, but we don't suffer huge performance penalties.
A bonus consideration: rain at the moment has no effect on fire objects. Neither do any water/ice spells. Or spawning fireballs on water. I just realised, as I was writing the previous paragraph. So these also need to be taken into account, for intuitive, immersive and emergent gameplay.
That's it for now - more next week!