Wednesday, July 10, 2013

Refactoring level loading in LEDS


If you want monsters in a level with libgeds, a .map file isn't enough. You need to provide a companion .cmd GameScript file that contain commands parsed by the engine to instanciate dynamic object, set rules that guide them, etc. Most of that content is left unmodified when you edit your map: only some 'GameOBject number U will start with behaviour B at coordinates (X,Y)' is modified. So far, I thus only extracted those coordinates into Monster objects and would use the updated Monster list to patch the .cmd file when you click [save]. Yes, I really mean patching. Scanning the original file, and deciding for each line whether I keep it as is or replace it with something coming from monster[i].writeback().

But the approach couldn't work properly when we start inserting new gobs that mess up the line numbering in the ouptut. Plus, it is excessively limited, while I'd like to be able to include more features in the level editor (such as linking spons with pins, picking the background image, etc.)

The new approach thus keeps a complete copy of the .cmd file in memory, divided in blocks. Each block holds lines that should be edited together, like the full configuration for one monster, the settings for one plane, etc. I'm almost done. All that's left to fix is spacing in generated lines.

I quite liked how the former version of LEDS had some "controller" class (WelcomeWindow) that provided an empty vector to LevelModel so that it'd fill it with the objects the view (MonstersManager) would use. Unfortunately, in the new version, the relationship between code blocks and in-editor structures (like the Monster instances) got more complex, and a flat vector no longer works well. The new Block class offers the ability for any part of the code to crawl through the sequence of Block instances that were generated while parsing the level's cmd file. Unfortunately, this dependency between LevelModel and MonstersManager has turned implicit while it was nicely explicit before.

There's just one design decision I wish I could validate with a magic crystal ball. Any line on a GameScript that starts with a 'g' is now packed into a GobBlock unless that GobBlock refuses it (if so, a new GobBlock is created). That means the gob%.focus had to be changed into focus=gob%, for instance. Not exactly what I'd recommend in any serious project, but the syntax of GameScript is fairly simple and should not receive further significant extensions.

No comments: