Last few weeks were work on lighting. But not just pretty-pixel-art lighting, but lighting for gameplay purposes. So far, light was mostly a visual effect via time-of-day-lighting and by representation of the 3 visibility states: currently seen (100% brightness), unseen but previously-explored (40% brightness), and never-seen (black).
But, this is not enough. Here are some examples where a more well-developed lighting solution could be utilised:
- Stealth. Light is essential for stealth. Low light is advantageous when trying to move undetected, unless enemies see well in the dark
- Light puzzles. Some doors/chests might be opened only with the presence of certain amount of light (e.g very high or very low)
- Status effects. Creatures can be blinded, by bright light.
- Creature traits. Some creatures can see in the dark, and don't get any penalties from low light
- Field of view. Creatures might be able to see further in good (for them) light conditions
- Combat effects. To-hit chances might be lower in undesirable light situations, e.g. a cave dweller in bright light or a regular creature at very low light
- Spells, wall torches, movable objects. To control the level of light in various ways.
To enable the above I need some way of simulating light in a way that I have access to from the gameplay code, rather than it being just a visual effect. To do that, I'm using a simple lighting "model", which seems to be working pretty well. The light model is comprised of the following:
- Light exists at a tile, between 0 (full darkness) and 1 (very bright).
- Each zone (level or sublevel) can specify a "ambient" light level, which is the light level without any other light sources around. For example, outdoors would be 1 and a dungeon/cavern could be 0.5.
- Light sources emit light in all directions, which reduces linearly
- Light is additive: a light source's light gets added to the ambient light.
- Light bleeding: at the borders between zones, light can bleed from the brighter one to the darker one
- Light propagates via Dijkstra maps, using the light source(s) as goals and the weights for intensities. If the brightness of a lightsource is 0.7, the goal is set to a weight of -0.7, and I gather all grid cells whose weight is <= 0, and use the "-weight" as the brightness. The light decay rate (how much brightness is lost from a tile to a neighbouring one) is set to a low value, I'm using 0.07.
So, that's about it. I'm running these calculations in the native plugin and they're pretty fast.So, to best test the system, I added torches and braziers. Torches exist on dungeon walls, braziers are actual objects (and probably movable, although heavy). I've added procedural placement of torches in the dungeon walls so I don't have to worry about placing them, and it works fine.
I've iterated a few times on the look of lighting, fog of war, weather and time-of-day color grading. After some useful feedback here and here, I've opted for a faded sepia for areas that the player can't actively see but has explored, whereas the brightness remains the same. Time-of-day affects brightness, and colour: Ambient light get tinted with time-of-day color grading (e.g. blue-ish at night) whereas light sources provide untinted brightness. This is shown here. Compare with the earlier videos below
Finally, I've been experimenting with a different look for fog-of-war in the overworld (as shown at the first video), although it's still a bit too gray for my taste. Now that the light infrastructure is there, onwards to other things, and possibly some usual maintainance/refactoring which follows feature additions.