Journal
I nearly called this entry "Technical coonsiderations" before realizing that that would put my life just too far over the edge. The last three evenings spent on Hatogate were a great opportunity to start up MMF again and do a quick project, after not touching it for the last while (hopefully it'll lead into me completing the Xbox versions of Special Agent and Running Free). And it was an interesting exercise in doing something I haven't done with it before - I've experimented in writing adventure games, but this one felt like it really needed a scripting language running behind the game to make it easy to program reactions to commands. So naturally, I modelled it after the only language remotely mad enough to contain anything Hatoful-related. I wrote it in ZZT-OOP. That is, I based the syntax heavily on the scripting language that ZZT uses, as it gave me something to aim towards (writing a language from scratch at the same time as writing something in that language is like trying to handwrite two different sentences with each hand simultaneously) - and being familiar with it made it readable to me at the same time as being easily parseable, based on commands denoted by # and then another couple of tokens per line. The language is heavily based on a GOTO philosophy with programs jumping about between labels all over the place, but for small scripts it does its job very well - and this game is greatly simplified by the way that there's only one location you can be doing anything in, and one object that you can interact with on the screen ever, so it would never get too tangled. So I borrowed most of the vocabulary - when the player builds a command with a verb and object (or two objects for USE), the game looks in the script for a label matching that command, then starts there and runs through the program. If it doesn't find one, it tries again with the object name replaced with ANYTHING, as a fall-through backup for places where you just need the same response for a variety of things (like using anything on the paintbrush). Labels are labelled with :, comments with ', commands with # (and # followed by an unrecognized command is assumed to be a jump to the label of the same name) and everything else is assumed to be text to display onscreen. Like most good ZZT scripts, things tend to start with #END, which in this case stops running a script and hands control back to the player. #GIVE and #TAKE form a pair of commands to add or remove an item from your inventory - #TAKE has an unfortunately counter-intuitive name because it sounds like you're taking and therefore gaining an item, but I chose to live with this rather than think up a synonym. Similarly, #SET and #CLEAR are used to turn flags on and off - these are named on/off switches that just act like inventory items that you can't see or interact with. Unlike in ZZT, of course, there is no hidden flag limit, flag name length limit, or risk of detonating the flag pile by innocently setting a flag twice. Syntax for #IF was a little more involved, mostly due to my own choice - I could have lived with just one variant, but to make scripting slightly easier, I came up with four different varieties of it. #IF followed by two tokens will check the flag list for token 1 and go to the label matching token 2 if it's found. #IF with only one token after it will check the flag list as above, but will then run the next line if it's found, and skip over it if not. This is useful for situations where you just want to display one alternative line of text depending on whether the player's done something before (I could have implemented #ZAP... but consciously chose not to). For situations where you want to check a flag isn't on, I threw in an #IFNOT command rather than force myself to think backwards. Supporting just one command or line on an #IF is still rather limiting, but it works here - and led to things like #IFNOT PUDDINGTHROWN SKIPLIZARDPUDDING, which is among the most surreal lines of code I've ever written. Of course, I also had to make up a couple of commands specific to this type of game - #SHOW changes the screen shown, and #SPRITE followed by a name and show/hide toggles the sprites overlaid on the location on and off. #ACHIEVE turns on an achievement and #ENDING stops the game. The classic #DIE is present, but only as a label that shows the death screen and calls #ENDGAME, which displays the death text and sets the game up to restart on the next click. It turns out that it's quite easy to create a bad language by just making it up as you go along, and I found myself putting things in to work around my own parser quite often - :END was set up as a label as well as a command for convenience at first, because the original three-token #IF lines could only go to labels and not call commands directly - and even now, you can't jump directly to a label with multiple words and have to duplicate them (:PECKMILLET). There are also a couple of special cases handled by the game and not in the code - the counter for turns before Okosan appears and the death achievement are two of them - but overall it turned into quite a neat little experiment, and I'd be interested to try making another adventure up in the same language to see if it holds together. In doing the interface, I also noticed something very interesting about Shadowgate - the screen is arranged so that all the buttons are on an 8x8 pixel grid. Presumably for ease of laying them out? 2013-01-13 15:41:00 12 comments |