Arrows break sometimes

Spring cleaning time! After the playtest, the eternal cycle of refactoring and features continues.

On the feature side, I'm working on spatial ambience, even though that's heavily WIP. Basically, if there's water in the level, I want it to be heard, at an appropriate position. But this is almost on hold because of other things that came into play.

Something that bugged me was that with about 100 moving creatures in the level, performance started being slightly funky. And since I'm way too proud to have performance issues, I thought it's time to fix any longstanding silliness in code. And, as per any big codebase, the more you look, the more you find ... opportunities for things to be better. Some duplicate functionality here, some wasteful behaviour there, and plenty of "I don't know WTF I was thinking" too.

One such example is how I handle various actions that shouldn't happen immediately, e.g. spawn a particle system in 200 msec or make a sprite disappear in 50 msec. Previously, I had different queues with bespoke crufty code, so I saw the opportunity to start ripping things out and replace them with a single queue that exhibits uniform behaviour. Of course, the cruft was there for a reason, and I still need to fix some edge cases, but the code has become simpler so far, which is a good thing, and probably faster, which is also nice. I say probably, because it's less pressure on the garbage collector, which does not have a directly measurable effect on performance, as far as I'm aware.

Another long-in-the-works should-have-fixed was visibility deltas. So far, when the field of view changed, I just sent an event that ... it changed. What and where, was anybody's guess. Reason is that I hadn't thought of a simple non-wasteful way to store deltas of what tiles changed visibility status: tiles that came into view and tiles not in view anymore. Well, that's done as well. Currently I overlay 8x8 tiles on the map, each tile represented by a 64-bit integer. That integer stores a bitmask of which tile changed visibility status. It's super fast to populate it, it has very low memory requirements (a 128x128 map just needs 256 64-bit integers), and we can do quick tests if anything in a tile changed. So effectively we have a little multi-resolution structure made up of 2 levels, ultra cheaply. That, combined with tracking of the union of the previous view AABB with the current view AABB (also tracked), and we don't even have to process all 8x8 tiles - just the ones that intersect that AABB union. TLDR: cheap way to iterate over the tiles that changed their visibility status, that works well for both large and small fov radius.

Another one was queries for entities in a level: e.g. get all objects, get all creatures, get creature at a tile, get all objects at a tile, etc. Previous code held arrays per entity type, current code changed to be able to query with an entity type mask, and even provide priorities (get creature at a tile, if does not exist, get object). Plus the innards changed for performance reasons again. After the dust settles I'll redo the measurements, but there's plenty of dust still and a couple of bugs.

Finally, a visual update. Previously, when you fired arrows and they ended up on the floor, there was a chance they'd be destroyed, otherwise they'd fall as items that you can pick up. When they were destroyed, they just disappeared. Well, not anymore. Now, the arrow breaks using the awesome Sprite Breaking Particle System (TM).

Some other various bugs and fixes, especially introduced bugs by above changes. That's all for now, have a nice weekend!