Monday, 22 June 2020

A Gammaquest II-Like Game: MC-10 Port of VIC-20 Sword of Fargoal

My son Charlie and I have made a port of the Commodore VIC-20 Version of The Sword of Fargoal to TRS-80 MC-10. Because of the more limited nature of the graphic abilities of the MC-10 it renders its dungeons as ASCII text, which provides gameplay more like what Jeff McCord's unpublished forerunner game Gammaquest II on the Commodore PET might have been like.


It was quite a challenge to get the game working on the MC-10. I had to figure out how to use the BASIC's CLEAR command to allocate protected memory at the top of the memory map. The VIC version pokes the map for the current level into memory, and then just uses PEEK to plot that information back onto the text screen area as the player moves. One's surroundings are revealed only immediately around the player, which is what provides the "fog of war" view that has come to define the "rogue-like" genre of which Fargoal is one of the first.

A complexity that I had to deal with is that I also wanted to store a short M/L timer routine created by Darren Atkinson in higher memory.  I needed this routine because the game uses the TI function of the VIC to give you 33 minutes to get back to the top level after you recover the sword somewhere on level 15-20.  So I had to figure out how much memory I needed for both functions to fit.

I also had to work around the limitation of Darren's timer to only go for 15 minutes (or so) before it loops back to zero. So I changed the time keeping variables quite a bit.  Now every time the monster movement routine is initiated, the time since it was last run is added to a time-keeping variable (T). This along with the fact that  the timer must be turned off every time sounds (other than the step sound) occur means some time accuracy is lost.  However, Charlie and I worked in some fudge factors to offset for the time when sounds are being played.  On the whole our testing indicates that the program keeps a close approximation to the actual number of seconds passed.  We erred on the side of generosity (we think), so you should actually get a little more than the standard 33 minutes to get out after collecting the sword.


The VIC version uses a loader program to load some custom fonts, with certain uncommon punctuation characters redefined as small graphic images of the character, monsters, stairs and pits, etc.  Redefining the characters was not available on the MC-10 and since the punctuation characters used were somewhat arbitrarily assigned a fair bit of re-adjusting for aesthetic purposes had to occur.  Instead of square and round brackets for monsters I used some of the unique punctuation characters, such as right arrow, up arrow and hash symbols.  Instead of using the right and left slashes for gold, I assigned these to up and down staircases. Pits and ceiling holes became different round characters: O, 0, and @.  Gold became the dollar sign. Magic/trap squares became the question mark.  Each of these changes required careful searching and replacing of all the places in the code where the original character code was used.  Fiddly, but after much testing, I think it has been done thoroughly.

The most important part of the conversion was changing McCord's ingenious dungeon drawing routine from a 22 X 23 character VIC screen to a 32 X 16 MC6847 screen. Also, all the movements for drawing those maps and moving the characters had to be switched from +22 -22 for up and down movement to +32 -32. The re-dimensioning was something I had done for other conversions so it went pretty smoothly.

PRINT commands also had to be switched from using the VIC's embedded control codes in strings method to using PRINT@ commands. When VIC listings are printed from WinVice, these codes generally get converted to periods. They are most commonly used for clearing the screen and in this game repositioning the cursor to the top left for printing messages on the top line "window", so it was not too hard to figure out. Watching Youtube playthroughs provided information about what was going on, on screen.

Youtube was also very helpful for replacing the sounds from the original. The VIC, of course, as a Commodore machine, uses a ton of PEEKs and POKES for all its sounds. All these routines were neatly lined up (for the most part) in one section of the code.  By looking at which subroutine (death, theft, combat, pit falling, etc) one could guess what sounds were meant to be produced.  Charlie, as usual, used his wonderful musical ear to recreate the main game refrain from listening to playthrough videos. He also went through the VIC code for clues from the various POKE commands to try to decipher musical note information and possible note duration. Through this combination he got the refrain to his satisfaction. With my tin ear, I'll leave it to others to judge the quality of his work.

After I was finished the main part of the conversion, the biggest thing was trying to speed up the program execution. The VIC has the ability to declare certain variables as Integer variables, which helps speed execution. You just have to put a % symbol after the variable name. The MC-10 only uses floats, so it was important to make sure that variable names used for graphic intensive actions, like moving monsters around on screen and tracking the character should be switched to single letter variable names (e.g. T for treasure was switched to GP and E for experience was switched to EX freeing up T and E for other uses). Single letter variables are interpreted by Basic more quickly. Also, removing redundant variables and using scratch variables can help keep the variable lookup list shorter and speed up execution. Finally, I declared all the graphic intensive variables in the initial DIM statements to speedup their execution. Variables declared earlier are placed earlier in the variable lookup table and therefore can be found and operated on faster.

After I had a working copy I thought it would be good to shoot the project by the original author to see how he felt about us sharing it more widely. The wikipedia page for Fargoal indicated that McCord was still vitally involved in modern variations of his classic program, and had collaborated with some programmers who had created a modern iPhone version. So I looked up the page for the new app and left a message about my project. A few days later McCord got back to me. He asked some questions about the project and when he was sure that it was just a retrocomputing homage he generously gave his blessing to share the game with others. All he requested was that his copyright be updated (1982-2020) and a note be made of his permission.  So I replaced the "Epyx presents" part of the old title screen with a reference to McCord's permission to create the game as a retrocomputing "pet project."


He told me that he appreciated my enthusiasm for his original game and desire to bring it to the TRS-80. He also mentioned that he actually used a TRS-80 (Model 1 or 3 as it turned out) at school as one of his first computers to learn BASIC coding as a teenager in Lexington, KY.  But his dad bought a PET, which he ended up using to create the precursor to Sword of Fargoal,  Gammaquest II, while he was in high school.

That program was never published. It was my hope in pursuing this project that not only would I get another working program for my favourite orphan 8-bit system from the early 80s, but also a program that is somewhat evocative of that now missing classic precursor program to Fargoal. So if you have a hankering to relive what playing Gammaquest II might have been like you might find that our version fits the bill. It can be played online here:


Here is the link for the iPhone version of the game:



Addendum:
Charlie and I think we have found a bug in the VIC code, or at least an anomaly.

220 IFD=1ANDT>0ORSF=1THENIFX1<.5ORX1>1ORC=42THEN225

This line is the one that initiates the sword (and gold) stealing routine. The first part of the IF seems to check for whether you are being attacked by a non-humanoid monster (D=0) or a humanoid monster (D=1). Then it checks whether you have gold (T>0) or you have found the sword (SF=1). But by not having brackets around the (T>0ORSF=1) it means that no matter whether a humanoid or non-humanoid monster attacks you, the sword can be stolen. We found that after collecting the sword (see last video below) it was very hard to make it to the top level without sometimes being attacked. Then if the monster is less than half as weak or is stronger than you, the sword will immediately be stolen. At the deepest levels, just after you have stolen the sword there will be many chances for strong monsters to jump you--They move fast and you will be struggling to make it to safety. As you go higher, you will start to encounter weaker monsters but you will also be feeling increasingly rushed as time runs out, so it is easy to rush to find stairs and end up being jumped by a weak beastie. Since the sword immediately goes all the way back to its original level, it's really game over, which seems unfair. So we changed it to:

220 IFD=1AND(T>0ORSF=1)THENIFX1<.5ORX1>1ORC=42THEN225

McCord seemed to intend that at higher levels, even weaker monsters could try to make a grab for the sword. This way those levels wouldn't just be a cake walk, but it seems excessive that if you stumble across *any* monster (especially unseen assassins or dimension spiders), it's game over. It makes more sense to us that you should certainly be wary of being jumped, but it shouldn't be an exercise in perfection. And there should be a real possibility to recover, so we changed it so that the sword is relocated on the current level. You will have to leave the level and return to it to make the sword reappear, so it represents a frustrating, possibly fatal, delay, but not instant failure. I'm not sure if we have disrupted some critical element of the game dynamic, but we will continue our testing and try to determine if it results in a slightly more enjoyable game.

We also considered nerfing the movement for monsters at levels below 15. At level 20 the monsters  have 1 to 1 movement turns with you, whereas at level 1 they move only every 19 turns.

2 GD=0:P1=0:L1=0:R1=50:S=20-L:IFS<1THENS=1

We thought it might be nice to change it to:

2 GD=0:P1=0:L1=0:R1=50:S=20-L:IFS<5THENS=RND(3)+1

It seemed to us that if you got stuck with the sword being on a lower level than 15, that should be enough of an increased challenge in itself. It didn't seem fair that the monsters should also become ludicrously difficult to avoid (and your movement excessively slow). But we tested playing it with one to one movement, and it wasn't impossibly slow, and there were some techniques for avoidance, so we decided to leave it. McCord seemed to want to allow players to also have other goals than simply getting the sword, such as testing themselves at going deeper.

We also ran across a bug in line 262 noticed by some of the early reviewers of the VIC version of the game.

260 FORJ=.TONN:IFM=N(J)THENV=J:J=NN
261 NEXT
262 SZ=T(V):IFSZ=.THENGOSUB233:K=Q(V):POKEM,K:N(V)=.:GOTO37

Line 262 would sometimes report an array out of bounds error for T(V), so we added a bounds check in line 261.

260 V=-1:FORJ=.TONN:IFM=N(J)THENV=J:J=NN
261 NEXT:IFV<.ORV>3THENV=3
262 SZ=T(V):IFSZ=.THENGOSUB233:K=Q(V):POKEM,K:N(V)=.:GOTO37

This error occurs if a currently searched monster (variable M) is not found among the list of monsters stored in N(J). This check occurs after combat has been initiated by you. For some reason no monster location M has been properly assigned yet if you jump on a monster immediately after level startup so we also initiate M to a monster location at level startup.

We added some instructions outlining the basic operations of the game that you can access at program startup by hitting the H key. We also highlighted the key letters used for the various spells on the Magic report screen. So now, anyone should have the basics in hand for playing our version of the game (such as online via the link above) without having to consult the manual. For example, the 8-way motion of the joystick is recreated using the keys:
QWE
 ASD
 ZXC
SPACE is the "panic button." The S key operates as down key so you can also use the WASD combination for basic motion. So we relocated the "S" for shield spell to the ENTER key.

Here's Shot97 having trouble trying to win the original Fargoal. Hopefully my edits might resolve some such frustrations:


Addendum to the Addendum

We think we found another bug in the following routine (this is the original VIC source):
282 d=1:m2=1:forj=0tonm:m%=m%(j):ifm%=0thennext:goto286
283 p=p%(j):c=a(j):fl=0:gosub293:iffl=1then52
285 p%(j)=p:m%(j)=m%:next
286 m2=0:forj=0tonn:m%=n%(j):ifm%=0thennext:goto37
287 d=d%(j):p=q%(j):c=b(j):ifc=40andl1=1thenc=41
288 gosub293:iffl=1then52
290 d%(j)=d:q%(j)=p:n%(j)=m%:next:goto37
The fl variable is initialized to zero, but only at line 283 inside the loop processing the movement of the non-humanoid monsters on a level. A similar loop begins at line 286 for the humanoid monsters. However, it doesn't initialize the fl variable to zero. This variable is the trigger for the teleport routine being jumped to if player hits the panic button while being attacked by a monster. This jump to the teleport occurs in the IF commands at lines 283 and 288 for the non-human and humanoid monsters respectively. Everything is fine if there are at least some non-humanoid monsters to process on a map. That means the fl variable gets reset to 0. But the "goto286" in line 282 can lead to the entire non-humanoid monster processing routine being skipped. In that case, the fl variable never gets reset. So in the rare occurrence where you initiate a panic teleport when attacked, and there happens to be no non-human monsters left on a level, you can end up automatically teleporting again and again every time the monster movement routine is processed. And don't try to get out of the problem by trying to take a stairway and go to another level, because that will lead to a NF (next without for) error in line 12. The reason for that is that the "if fl=1 then 52" command sequence is a jump back up to line 52 (the main loop) from within a FOR/NEXT loop for processing each of the monster types. In other words, the FOR/NEXT loop is never formally completed/properly exited, which can lead to all sorts of confusion in the interpreter about how to process subsequent NEXTs, as one can see here in this test worked out by Charlie on the Vic emulator:

Never reinit a FOR loop inside other FOR loops!
This is likely a very rare occurrence and it is possible that something in the original code that we somehow messed up prevented it from occurring. But we fixed it in our version by properly shutting down the FOR/NEXT loop before jumping back to the main loop. We also initialize the fl variable at the top of the monster movement routine (line 282) before either the monster-type loops are entered. This is okay since any panic button aborts the entire routine (282-290), so it really only needs to be initialized once. This will also mean a speedup of the first loop as fl will not be reinitialized each time that loop is run.

We also fixed a minor error with the win messages which can flash by too quickly. As the CRPG addict notes in his post on the game:
If you make it, you get a message that says "Your quest is complete!" and then a screen of your overall statistics. If the clock runs out, next time you change levels, you get a message that says "out of time!" but then it takes you to the same statistics screen. Except for that brief flash of "Out of time!" or "Your quest is complete!" you wouldn't really be able to tell if someone won the game or not.
In our version if time runs out that message will stay at the top of the statistics screen. If you win a little refrain will play before you are taken to the stats screen. You can see this in the video of my son Charlie actually winning the game. Thanks to Charlie and his patience and dexterity, I think I can know call this project complete. The Sword has been recovered! Peace is returned to the Great Forest lands.

Charlie for the Win!

Addendum to the Addendum to the Addendum (2023/12/04)

Looks like some other folks have tried to apply some of our bug reports back to the VIC source.  I hope they work out: https://www.reddit.com/r/vic20/comments/ju8vst/sword_of_fargoal_bug_fixes/


5 comments:

  1. Wonderful blog and great that you got to interact with the original author and got his blessing AND you got to actually debug some of the 'bugs' in the VIC-20 version. And you used my video in it :) Kind regards, Mark

    ReplyDelete
    Replies
    1. Mark. Sorry for the delay. I'm not much of a blogger. Thanks for the video. I needed all the help I could get on observing how the game was supposed to run to make my port. I think I did find some bugs that would make it hard to win, but I can't be 100% sure, since I was porting to another system and there might be something I missed that could possibly be hardware based. But I didn't find much out there that indicated that anyone ever actually won, even on official versions...

      Delete
  2. Appreciate the mention and all of this work you and your son have gone through in the porting, figuring out all the quirks, adjusting things, and how you got the authors blessings. Really cool project, and it might just well be my favorite VIC-20 game, so it's great to see it being ported quite nicely to another classic machine.

    ReplyDelete
    Replies
    1. Sorry for the delay. Thanks for you kind comments. The MC-10 is not really a classic machine. More an orphan system needing a little love. But some day I'll port my code to the TRS-80 Color Computer-- the MC-10's big brother and truly a classic machine. Thanks again for the comment.

      Delete
  3. Hello there, thank you for your article.. brought back distant memories of my early days fiddling with BASIC on the VIC20. I also had frustration with attaining the sword and finding it impossible to escape with it. I can’t remember how, but I fiddled the variable that operates the hit damage by putting in a minus sign so each time I was attacked my hit points went up instead of down. Thanks again for your article.

    ReplyDelete