Retro City Rampage’s simple scripting language has proven very powerful. The game makes heavy use of scripts, using about 90 of them at the moment with a few more to be added before it’s finally tied in a bow (which will be happening very soon).
Although my initial NES effort Grand Theftendo had its missions written directly in 6502, I designed the scripting language for RCR under the mentality that it could feasibly run on the NES if I were to port it backwards. It was lightweight and efficient! However, that being said, this also meant it was simple and somewhat primitive.
But, RCR got ambitious. More ambitious in cinematics. More ambitious in gameplay. These created a number of situations throughout development where the scripting language needed to be extended. New features needed to be added to the language but with time always being the enemy, the simplest approach always needed to be taken. Amazingly though in hindsight, these simple (and often elegant) solutions seem to have been the best anyway.
One example of where I could’ve gone and where I ended up going was for catching events. Given the time and resources, the old me would’ve designed an object oriented language, where I could set up sprite instances each with their own “onAttacked” method, etc. It would’ve been so clean and elegant! But that wasn’t going to happen. What was the core of what I needed? Events of course! So I added an event thread to each script and global event functions such as “on.attacked”. Each event then passes down params which can be used to check which object was attacked. And you know what? Most of the time I’m just checking if it’s the player object, at the most I pretty much check 3-4 different objects, so the single function doesn’t make code too messy. And if it did, I can break the code up into separate function calls (which were another quick hack).
I also made use of the event thread and added callbacks, so for example, when all of the enemies in a group are killed or the player reaches a “go” arrow, the function if passed in will be called. This type of stuff is used for all kinds of things such as waypoints for a car race.
So anyway, the win today was script-to-script messaging. At one point, one of my visions for an ideal scripting language involved implementing a linker so scripts could could share common functions and variables with each other (or at the very least, all have access global functions/variables from a global script). However, this again wasn’t going to happen. Faced with a situation where I really needed to add script-to-script communication to avoid potentially shoddy code, I did the next best thing. I made use of my script opcodes by adding a post.message one, and added an event function which is triggered in the other running scripts. Worked perfectly!

For those curious, the multiple scripts running include one for the region/building/room you’re in and one for the active mission. So if you trigger something that is set up in the room (ie. activating a secret entrance) and the mission needs to know about this, it now makes use of the messaging. Since I didn’t have access to the actual object which was being activated from the other script, I could’ve implemented some sketchy catcher events. Instead of saying “if event.obj == the_obj” I’d need to say “if is type prop and is subtypeand … and …” then stick this into any event would could cause the entrance to activate. Messy code and vulnerable to holes. The simple message is the obvious way to go. It’s all about writing safe, robust, bulletproof code. Cuts down on possible bugs down the line.
After RCR is done I’ll write a whole article on the dozen or so of these scenarios which lead to great progress and robust functionality that didn’t feel like hacks or duct tape.
…and this new functionality took less time to do than I spent writing this article. EFFICIENCY FTW!