Not a huge day today. Only got to spend about an hour on it at work. Since I didn't really want to get too involved in anything big, instead I just ported over a couple projectile payload components, including the flame burst component above. Also, I made a Loot button icon, and in the process fiddled a little bit with the materials to render the buttons, making them a little less flat. They're not production buttons, but at least they don't really annoy me too bad anymore.
While doing the payloads, I did run into some issues with certain objects being kept alive when they shouldn't be. I haven't gotten entirely to the bottom of it yet, but I suspect it has to do with garbage collection. I'll probably want to review my policies on object destruction to make sure that things are being handled correctly. While I've fixed the outward issues, I have a nagging fear that somehow I'm leaking memory.
The issue was with nodes being removed, but not actually being removed. That is, you can call Remove() on a node and it will remove itself from the scene. However, if there is something keeping the node alive (a shared pointer somewhere) then it doesn't get deleted, and its components will keep updating. I discovered the issue when porting the flame burst payload. I'm having my payloads fire when they receive the Stop() call, called by the Lua scripting system when a node is deleted. As long as the node is properly deleted, Stop() gets called on all ScriptObjects belonging to it, so if one of those is a Payload then it will fire, spawning whatever objects it needs to spawn to do its thing before the node goes out of existence.
However, I noticed that if I fired a fireball a long ways away, it would properly trigger its payload when the fireball was deleted, but if I fired it nearby, then not only would it fail to trigger the payload (indicating the fireball projectile wasn't being properly deleted) but it would also interfere with the floating combat text indicating the resources spent to cast the fireball. The key was if the projectile hit while that combat text was still alive; if this happened, then neither the combat text nor the projectile would die properly, and the result would be a piece of combat text zipping up the screen until it shot out of sight, and a blazing particle fireball cascading through the ground and plunging down, down, down into infinity rather than exploding into a burst of flame like a good fireball should do. The payload was never being triggered.
This leads me to believe that something, somewhere, is causing references to be stored for these two objects when they occur together, but it only happens when both exist at the same time. You can imagine, it's kind of a baffling bug. I "fixed" it by implementing a Node remover, a system that lives on the scene itself and which handles node removal at a certain point during the update cycle. Any node that wants to be removed is no longer allowed to remove itself; instead, it sends an event that is picked up by the Node Remover, with itself as payload data for the event. The Node Remover queues up all nodes, then iterates the list of nodes to remove, removes all of their components and children, then removes them.
I did it this way, since it is not a good idea to call RemoveComponents() on a Node during Update, since that will remove all components, even the component that is currently executing and calling RemoveComponents. Crash. So instead, I have to clear the components at a safe time, when no component on the node is executing any method. With this implementation, projectiles correctly trigger their payloads, flame bursts correctly spawn, burn for awhile and then die.
However... I get the sneaking suspicion that, while I might be removing components, whatever references were keeping the Nodes alive and preventing them from dying properly are still holding those references. While the components are being removed, I think the nodes themselves might be sticking around somehow, and that means I'm leaking.
So, that'll probably be my project for tomorrow: see if I can track down whoever is guilty of hanging onto node references, and ensure that they are suitably punished.