Home


Checking out Lianne in... The Dark Crown

August 12, 2019

I've been an amateur programmer all my life. I started out poking around in BASIC on a second-hand TRS-80 when I was 8 or 9, and then moved on to QBasic and eventually QuickBASIC in the mid-'90s, just as the QB hobbyist scene was picking up steam. Good times were had by all.

QuickBASIC 4.5 QuickBASIC 4.5
QBasic and its big sister QuickBASIC (which was a compiler, not just an interpreter!!) made it easy to create your own programs—especially games. It had a simple IDE and intuitive syntax... and the built-in graphics library didn't hurt, either.

No genre was more popular in the QB scene than RPGs, and arguably the most popular of them was Lianne in... The Dark Crown, created in 1997 by a coder and Kiss fan named DarkDread. The graphics blew us all away (at the time). The gameplay is standard (J)RPG fare: you walk around the map looking for your next destination, while fighting random battles.

The game doesn't take itself too seriously. The result is incredibly silly... and fun.

Recently, I re-discovered Lianne and decided to give it a play-through. I live-tweeted my reactions, but instead of reproducing the Twitter thread tweet-for-tweet like I did with my 386 first-look, I'm going to try to expand those thoughts into actual sentences.

Lianne is a demo, rather than a complete game. Like most of our projects back in the QB "scene," it was never finished. Only a few of us ever finished projects, and those that did were looked upon as gods. (I wasn't one of them.)

Instead of dealing with DOSBox, I got the game running natively under Linux thanks to QB64.

The story is about what you'd expect. Baron Von Cain kills the king and steals the Crown of Kissworld, which is said to have great powers—whoever wears it rules the land. You, Lianne, who helped bring peace to Kissworld through your defeat of the evil Wizard Vincent, are called upon to hunt down Baron Cain on his island and bring back the crown.


RIP King Frehley of Kissworld.


Kiss Army. Heh.
Brilliant.

Battles are reminiscent of Dragon Warrior. You face enemies one at a time, in a first-person portrait view. Battles are done through simple keyboard commands: (f)ight, (a)ttack, (m)agic, (r)un, that sort of thing. Your primary weapons for physical attacks are CDs. The in-game currency is cat food.


Excuse me; "CatFood."

Treasure chests are everywhere. You can tell DarkDread was just trying to throw together a map for the demo.

There's a lot of RNG variance in the game. I found a shop (run by a cat, naturally) and upgraded my weapon to a bronze CD. The damage output was definitely higher than the plastic CD I was carrying, but the damage range was nuts. Now, I was doing anywhere from 1 to 39 damage.

Seriously. Right in a row, I hit an enemy for 1 damage, and then the same enemy in the very next attack, for 39 damage.

At one point, I opened a treasure box, and the game crashed. The game didn't give much explanation as to what "error code 9" was.


"Oh oh" indeed.

I figured it might just be one of QB's stock error codes. A look at the code confirmed it (the error-handler is shown below). It's been years since I worked in Q(uick)BASIC, and I couldn't remember what any of the codes mean, so I went searching, and it turns out I was right.




Error 9 is a subscript-out-of-range error. My initial guess was that I was either out of inventory space (which I doubted, since I hadn't really bought or acquired much up to that point), or that the treasure box I opened didn't point to anything valid.

Reading through the source, I found the code for opening a chest. Something really fascinating: chests aren't pre-laid out. They're generated randomly when you open them (see line 1914). This means you could—if so inclined—save and re-load your game, opening a chest each time, until you get the loot you want.

I eventualy found the source of the error: there's only room to open 30 chests, because they're tracked using a 60-integer array: each int represents an x and a y coordinate for a thus-far-discovered chest. The game doesn't do any bounds checking on that array, and there are more than 30 chests in the game, so once you open number thirty-one... bang, you're dead.

(The array is actually 61 elements long, due to a coding oversight. In QB, supplying only one number for the array size sets that number as the upper bound of the array. With the lower bound being 0, the array comes out to 61 elements. I think what DarkDread actually wanted to do here was DIM SHARED Chest(59) or maybe (1 TO 60).)

I saved my game, since I needed to quit in order to re-compile the game and run some tests. But when I went to load up my save, it was missing from the menu. There should have been an entry called "Clarissa" in that number 1 slot. The save files existed—I checked!

The first file, "Clarissa", obviously contains character data: current position in the game world, which direction you're facing, HP, MP, experience, CatFood, spells... everything. I would have been cool if there were labels, but the data is fairly obvious anyway, and DarkDread was probably trying to save space since the game was for DOS. I'm just glad it's human-readable, instead of some binary blob.

The second file, "Clarissa.DAT", is the state of all the treasure boxes in the world: there are 61 lines in the file, and they're all non-zero except for the last three, indicating that I'm nearing the end of the array.

The save routine is dead simple: hardcoded and not very flexible, but perfectly fine for a QuickBASIC RPG in 1997.

Since the save files are named the same as the description that the player enters (instead of just, say, SAVE0.DAT, SAVE1.DAT, etc.), and there are several non-savegame files in the directory that use the .DAT extension, the game has no way of finding the savegame files except by reading their names from a file. However, the game must not have been saving that file correctly (or at all), because when I open it up... all five strings are empty.

I edited the index file manually, and... bingo bongo! We're off on our way again!

But not for long. Another error, this time just from walking around. Error 5: illegal function call. There was no way I'd be able to figure out where it was coming from on my own (at least not without spending more time on it than I cared to), so I simply commented out the game's error handling, so I got QB64's default error dialog instead:

The PUT statement at line 1901 is a graphics statement. The surrounding code calls a boss encounter! I'm guessing the crash is because of some error with drawing the boss's graphics—either the graphic doesn't exist, or the Boss2Mask or Boss2 functions aren't implemented anywhere, or some other thing. Not worth debugging, so I just commented out the graphics calls, and voila... plot stuff!


Obviously, the reason Lianne is talking to nobody is because of the commented-out graphics calls.


You're on, motherf—


How delightfully evil. I can respect that.

What happened next was pretty interesting. Magnus killed me, and then I got a little bit of story that seemed to indicate that I killed Magnus, and then I got the "thanks for trying the demo!" message.

Only after I hit the confirm key on the demo message did I get the standard "you died, Game Over" message. So it looks like the code just assumes that if the Magnus battle is over, you won, so it show the "thanks for playing!" screen. But once that is dismissed, the regular game code notices you're dead.

I decided to try grinding some more so I could beat Magnus, but I was never able to—he's tough! So I finally gave up and just commented out that encounter entirely. Aaaannnd.... nothing. The "stairs" tile behind the Magnus encounter is non-traversible. It doesn't lead anywhere.


Sorry, Lianne. Baron Cain doesn't want visitors.

Finally, the help screen which, among other tips, advises you that "When pressing keys do not hold them down as this will make the game play akward [sic]. That's because input handling was done with the INKEY$ function, which works on an input buffer.

Revisiting this game was incredibly fun, especially debugging it as I went along. I'm not sure if all the errors were present in the original, or if this is an older version, or if it's all down to differences between the original QuickBASIC and QB64.

Speaking of the bugs, I know I might have come off as a bit sarcastic in some of my commentary, but that wasn't meant to slag off DarkDread or this game—I was just having a little fun. The truth of it is, this was an actual, playable demo in a hobbyist scene where not many of us knew what we were doing. After playing it again, I'm suddenly remembering just why the QB scene went gaga over it back in the day (at least in my memory—I remember it being a Really Big Deal). To quote DarkDread himself from the game's readme file: "I programmed this in Qbasic to prove a point: Good, quality software CAN be written in Qbasic. I hope you agree with me that this game proves that point."

It does, man. It does.


Lianne in... The Dark Crown can be downloaded from The Internet Archive.