Given the field of vision implementation from last time, I decided it was time to test it and make the game a bit interactive, by allowing the user control of a character. This has been really important, as it has forced me to focus on making level transitions, ensuring movement cost maps and visibility maps work ok, ensuring that save/load works correctly, and a myriad of small little things. Below are a few videos and a list of things done since the last blog post:

Animated and fuzzy fog of war

  • Better fog of war this time, implemented as a simple pixel shader. It's simple, it's fast, looks better and does the job for now. The video also shows from about here that now sprites flip horizontally when moving towards the other direction, while when moving vertically they preserve whatever direction they were facing. This costs 1 bit in the 128-bit data per moving sprite, not a big loss or cost :)

  • Added functionality for level movement costs (slightly different than overworld moving costs), consisting of background (walls/floor/liquids) and static objects.

  • A* and all other path calculators take into account that diagonal movement is only allowed when the related cardinal directions are passable.

  • Creatures have light sensitivity, and overworld and dungeons have light levels, that affect line of sight radius.

  • I wanted to refactor a bit of the territory system regarding propagation of influence by replacing the data per tile from class (reference type) to struct (value type), so that led to an exciting journey of more changes, fixes, bug discoveries and further bug fixes, and now it seems to be back on track, better, with less code and fewer bugs.

  • Field of vision optimisation that, when the player/sensor moves to an adjacent tile, we only clear the visible data from the surrounding circle with Los+1 radius, instead of clearing the entire map. This of course is not uncommon, but it also had to be done, as now the related performance cost went from 50ms to 0.2ms when the player moves in the overworld, because the refreshed tiles went from 512x512 to 20x20.

  • So far I wanted to have entity "configurations" which are objects that store the exact information to generate an entity, but decided against that due to the cases when entities have to reference other entities during the generation before the entities are created. So now, for example, when procedurally generating a lock and key, I have to create both entities, configure them, put the reference of the door needing the particular key entity to be unlocked, and then call the magic function "EntityBeginPlay" which makes the entity visible to the game, listeners, other entities etc.

  • Level objects (fountains, chests, etc) can now affect movement and visibility. Can now push level objects and update movement/visibility maps appropriately. Also, added doors, and can open and close them at will, blocking movement/visibility. Also, as a fun sanity check, when pushing a fountain in an open door, door can't close.
  • Explored map disintegrates a bit when revisiting a level. Should later do it based on last visit time. Static objects disappear in explored areas when revisiting a map
  • Save/Load works from overworld and levels
  • Sparse 2D multimaps to store level objects and creatures
  • Ctrl+click moves character towards highlighted path, right click cancels path (this is for fast debugging, should later change with the introduction of turn system)
  • Slightly more flexible sprite rendering, with a list of animations and indices per animation type. So I have a "default", "moving", "death" etc animation types, and I can have for example "door closed" "door open" "door locked" as different animations per type.
  • Simplex noise precomputed inverse distribution function and cumulative distribution function. I wanted mainly the IDF, for cavern generation, as I wanted a scalar variable "density" to control how open or claustrophobic a cavern map is. I wanted density to vary in a linear way. My caverns are generated using thresholded Simplex noise. But the Simplex noise distribution is not uniform therefore the threshold value does not exhibit linear behaviour. Therefore IDF can be pre-calculated and used instead as density, as we feed it a probability value (that can be linear) and we get as output the threshold value to use. So, I did a test with this new variation, and for 10% density to 90% density the final map (after connectivity, etc) looks as follows:

White is open space. The maps get progressively more constrained in a linear way. The last map is very constrained, therefore a lot of parts have been discarded post-connectivity

Here's a video showcasing:

  • Level transitions and overworld-level transitions
  • Fast-path traversal
  • Pushing objects
  • Opening-closing doors
  • Degradation of explored map after leaving level