My partner is in hospital at the moment. She is pretty unwell but she is at least in the best place now and things are being addressed. It has been a very worrying couple of weeks and hopefully we will get some answers now.
----------------------------------------------
Weirdly, given the lack of updates, I've actually been programming more obsessively than usual recently. I've been working on a far more sophisticated version of the Squishy level editor for a few weeks now and have a pretty funky result so far.
I'll have to get round to posting some stuff with screenshots soon but basically the main cool features are:
Some of those features are prepared for rather than implemented but it is all going to work. I'm a way off being able to export levels for Squishy to consume but when I do I hope that levels will become a pleasure to create.
My logic is that I lose interest in projects when the basic game framework is written and the challenge becomes asset creation. Since I chose a project (actually a frighteningly long time ago given my normal attention span) that is quite asset heavy, I figure two things:
So rather than a bodge editor that requires memorising keyboard shortcuts and generally just being aimed at me, I decided that a month or so spent on a top class editor was time well spent.
I still occasionally fire up Squishy and remind myself that it is fun to play, but I've really enjoyed working on Squed 2.
Undo and redo. Jesus. How to swiss cheese an already struggling brain in one easy step. But it works.
I've drawn a bit of inspiration from the patterns book and the Command pattern nonsense but like all programmers with a few years under my belt, I've avoided trying to actually base real code on their theory (is that harsh?).
This is how it works.
I have a LevelContainer class that has as members a LevelData instance and an EditorData instance. The LevelData instance is capable of cloning itself via all of its constituents supporting covariant return Clone() methods.
The only external access to this data is const so that any actual changes to the level data have to be actioned via the LevelContainer instance, which gets passed around liberally to the rest of the application.
The EditorData instance contains a stack of stack of UndoActions (using my own ptr_stack which uses my ptr_vector which would probably be better replaced with boost::ptr_vector but those that know me know my shame of not yet having embraced boost - I have no excuses, I'm under a lot of stress).
I can call BeginUndoSequence() to push a new stack onto the stack. When actions take place, I create a new UndoAction inheritor that gets pushed on the stack on the top of the stack. When I call EndUndoSequence, I pop the stack on the stack if it is empty and the next undo action starts its own stack.
When Undo is invoked, I take the current top stack and pop its actions, implementing their Perform() method after first locking the Undo mechanism. The UndoAction inheritors use the same methods as actually doing the stuff to reverse themselves.
But...each UndoAction also implements a virtual UndoAction *Redo(Resources &Res) method that returns a new-ed instance of the opposite UndoAction and via some complex shennanigans updates a Redo stack of stacks with the information needed to undo the undo.
The Redo stack gets invalidated when a new Undo action is pushed onto the Undo stack and so far this all appears to work okay.
Wibble wibble. Brain not working great at moment. But it seems to work.
It is sort of like the Command pattern except I don't actually use a Command to do the work in the first place. Perhaps it would be easier if I did but as I'm just making this stuff up as I go along that turned out not to be the case.
So far there has only been one action that has required me to Clone() the entire level data and that has been the Undo of removing a module. Since re-loading a module and re-creating all the entities that were removed due to its deletion buggers up all the indices, this proved to be a simpler solution. Every other action the editor can perform can be undone by only storing the minimal information required to do just that.
When things do go tits up, Squed 2 is pretty stable and preserves all of the data it can, providing a nice list of warnings that explain exactly what measures it has had to take to keep the application running and without affecting anything actually on file.
I wrap tinfoil round my head so the CIA can't read my thoughts. Unlikely cookie for anyone still reading at this point.
It's been a headfuck, but a very satisfying one and now things are cool because at the same time that I implement something, I also implement its undo/redo mechanism. This is one of those things that needs to be designed in from day one and not crowbarred in later.
Screenshots to follow. Can't be arsed at the moment.