DON'T REPEAT YOURSELF

Simplifying scripting with the DRY principle

I've been playing Drakonais's excellent RM 2003 project The Tiamat Sacrament lately. It's got a good story and well-thought-out mechanics, and it's just generlaly a lot of fun.

It's also got scripting. It's got a lot of scripting, in fact, especially for the Garrison minigames. And scripting has always been something I've found interesting. It was the limitations in RPG Maker's scripting system that prompted me to start working on an engine of my own, the TURBU project. And when I ran The Tiamat Sacrament through my project importer, which usually chews through several maps per second, it stalled for several seconds each on the Garrison minigame maps while generating some absolutely massive script files from them. So I looked at the scripts, and I saw some clear problems.

I've talked with Drakonais, and he said it was all right to discuss what I've shared with him about his scripts and how they could be improved. I don't want anyone to think I'm trashing his project, because I'm not. But a lot of RPG Maker games suffer from the same problems, and could benefit from the same techniques.

When you need the same thing multiple times in the same script, the natural thing to do is to copy it and paste it so you'll have the same code available. But generally speaking, this is a very bad idea. There are some exceptions, for example if you really need to reduce lag it can be useful in RPG Maker to copy repetitive code to multiple places, (this is due to a quirk in the way RM's scripting engine works, and will not apply to projects imported in the TURBU engine once I've got it working,) but in general, you don't want to repeat code blocks.

Instead, remember the DRY principle: Don't Repeat Yourself. This is a very simple bit of coding philosophy, but unfortunately a lot of people never learn it. It doesn't generally get taught in high school or even college programming classes; it's something you end up learning out in the real world if you become a professional programmer. If not, you might stumble across it somewhere, or you might not. I'm writing this up so hopefully a few people might stumble across it here.

Don't Repeat Yourself. If you ever find yourself copying and pasting a block of script code to a new place, there's probably a better way to do it. You could put it in a common event, or just another event on the same map, and use the Call Event command in multiple places to call it. This has a few different benefits:

First off, it simplifies the script and makes it easier to read. If you give the routine a meaningful name, you can tell what the script is supposed to be doing without getting lost in the details of how it's doing it. Second, it centralizes your script code and makes it easier to change.

For example, in The Tiamat Sacrament, in one of the minigame maps, the following sequence is repeated approximately 1400 times in the map:

Play Sound: Heal8
Variable operation: V(100) + 20
Message: Got 20 CP!

This plays when an enemy is killed. Let's say that (completely hypothetically) Drakonais does some testing and decides that that's not enough, that it would work better if killing an enemy rewarded the player with 25 CP. (Or maybe that the reward is too high and needs to be reduced. Same basic idea.) Right now, he'd have a huge headache on his hands, hunting down every single instance in the codebase and changing the variable operation and the text of the message. (And without the benefit of any sort of search-and-replace tool in the scripter!)

But if there was a script called "Kill Reward", and each of those ~1400 instances said "Call Event: Kill Reward", then if he needed to change the size of the reward, he would only have to change the one script. All of the Call Event commands would continue pointing to it, and he's just made a system-wide change by just changing two lines in one place!

This isn't restricted to small routines, either. He's got 4 huge scripts in there, around 1000 lines each. The first line of each script is unique; the entire rest of the script body is identical. If that 1000 line main body was moved to its own routine and called with Call Event, not only would the script code become more centralized, it would also make the map file smaller!

Again, I don't want anyone thinking I'm picking on The Tiamat Sacrament. Massive duplication is a very common issue in RPG Maker projects, because it's the most natural way to write scripts. And if everyone could write 100% perfect scripts the first time, it wouldn't be a problem.

It's only when you change your paradigm a little, and start thinking about having to be able to come back later and fix or otherwise modify them, that you start to think about readability and centralization. When you focus on making scripts that are easy to read instead of simply creating scripts, you start to notice things like this, and your code becomes cleaner, smaller, and, in the long run, less buggy.

So remember, Don't Repeat Yourself. Repeat that to yourself until it's second nature, then start applying it in your script work. It might seem like more work at first, but you'll thank yourself when it comes time to fix stuff.

Posts

Pages: 1
I think the cause of this is due to how unintuitive it is to make functions in RPG Maker 2003. It's easy to get a shitty event to spill all over the place and the IDE isn't kind to moving code around. Plus the general averseness of rewriting code doesn't help and that's an even bigger problem for all software developers, amateur and professional. You make some shitty event and don't feel like going back and improving it because 'it works well enough' (and then you debug it and go gray).

The other cause is RM's language has the teeth equivalent of gums. I made an enemy skill that would remove all the items from the player's inventory and replace them after the battle. Replacing the items was easy, I had a common event that just had to be pointed to the start of the stored items array / variable ID and it would iterate though the list replacing items using the key and value to replace the items. Storing them on the other hand was a copy paste nightmare because you can't use variables to look at the inventory, you have to hand select them. That was a lot of copy+pasting because I couldn't iterate though them and I had to use two common events to do it to cut down on loading time in the editor. The store event was like half a screen at worst. Needless to say you actually didn't lose your entire inventory when the skill was used, just the usable items. Or ~80 items out of 500 or so.
Yeah, I know exactly what you mean about the scripting not being anywhere near powerful enough. You will be able to do that right in TURBU. That was my main motivation for starting to work on it.
author=GreatRedSpirit
The other cause is RM's language has the teeth equivalent of gums. I made an enemy skill that would remove all the items from the player's inventory and replace them after the battle. Replacing the items was easy, I had a common event that just had to be pointed to the start of the stored items array / variable ID and it would iterate though the list replacing items using the key and value to replace the items. Storing them on the other hand was a copy paste nightmare because you can't use variables to look at the inventory, you have to hand select them. That was a lot of copy+pasting because I couldn't iterate though them and I had to use two common events to do it to cut down on loading time in the editor. The store event was like half a screen at worst. Needless to say you actually didn't lose your entire inventory when the skill was used, just the usable items. Or ~80 items out of 500 or so.

If you want to use some of RM's default systems along with more complex events, take a look at BanenenJoe's DestinyPatch. It adds a simple scripting system to do things like that with a single loop.
When RMN4 comes out I think we need to sit down and get all of these RM2k(3) patches and improvement projects sorted out and added to it's engine page. This is the first time I've heard of TURBU or BJ's Destiny Patch (but I don't use 2k3 anymore so that's my fallback excuse).
Yeah. Kentona has offered to add TURBU to the Engines section here once I get it far enough along that you can build and play projects with it. It's not finished yet, but I've gotten pretty far.
Sailerius
did someone say angels
3214
It's also got scripting. It's got a lot of scripting.

No it doesn't. It's got eventing.
Max McGee
with sorrow down past the fence
9159
There is a certain point where the difference between those two things is so small that pointing it out is obnoxious. I mean blocks of 'eventing' in RM that I can point to and go 'fuck you, this is code'.
It isn't real programming unless you code the assembly in yourself
Yeah, what Max said. RM2K's script system is Turing complete, (or would be if it could access infinite memory, of course,) and the large blocks of script implementing the turn-based strategy minigame I was discussing are definitely code. I see "events" as more of a paradigm, describing the way the script code gets activated.
dragonheartman
Developer, Starless Umbra / Heroes of Umbra
2966
author=Max McGee
There is a certain point where the difference between those two things is so small that pointing it out is obnoxious. I mean blocks of 'eventing' in RM that I can point to and go 'fuck you, this is code'.
This. Because some of us use RM2k3 for more than making little chests and doors.
author=dragonheartman
author=Max McGee
There is a certain point where the difference between those two things is so small that pointing it out is obnoxious. I mean blocks of 'eventing' in RM that I can point to and go 'fuck you, this is code'.
This. Because some of us use RM2k3 for more than making little chests and doors.


Whenever I hear scripting, I think of lamebrained people stealing codes from the internet, and not learning even basic eventing.
Pages: 1