Monday, 1 January 2024

"Dungeon" by Brian Sawyer (1979)


This is a port of the classic PET "Rogue-like" game (before Rogue) "Dungeon" to the TRS-80 MC-10. I found an unfinished part in it that would have allowed the player to have a double distance "fog of war" view instead of the single space view immediately around the player's position. The "S" key was assigned to the function and it would have cost -2 HP. In fact, I think the -2 part of the function works when you hit S, but the full expanded view routine doesn't seem to have been implemented. See lines 610 and 1310 in the source that I worked from:

600 K=-40:J=3:M=40:R=3:GN=0
610 IFSM=1THENK=-80:J=5:M=80:R=4:SM=0
620 O=L-32767-R
630 IFO+32811>33768THENM=0

This is the beginning of the "fog of war" reveal routine. The SM variable is a switch that if activated in the key input routine causes an alteration of variables to create different offsets for the reveal box around the player. But not all these variables are actually used in the subsequent nested FOR/NEXTs used to display the spaces around the character.  I think Sawyer realized that an odd number would be needed for the routine to actually be centered on the character, which would require a 5 X 5 character space box around the player character instead of an originally planned 4.  Also, this would require some error checking as the routine moved past the edge of the screen in the bottom right and top left.  A vestige of that can be seen in line 630 where a check is made to see whether the offset takes the view outside of screen memory at the bottom right.  

Here is the code that was meant to switch the mode on:

1310 IFL$="S"THENSM=1:HP=HP-2

My guess is that Sawyer realized what a daunting task getting the routine working would be (a bounds check also for the top left, for example) and that the game was playing just fine without it. So he just left it half implemented and carried on. It is not uncommon to find such unfinished bits and pieces in old BASIC games. I think there were other unused variables that I removed from this game and untold others in other games. They are the vestiges of the unwieldy editors that one had to use back in the day.  It was impossible to simply view the code as a whole and to be able to do quick global searches and replaces. LISTing and then pausing the display as the lines sped past meant that many false starts would simply be left in place if they didn't impede program execution. Today I like to clean these things up, as they can help smooth out and speed up game play.  I recognize that there is a loss of fidelity with the original, which is why I try to document such modifications. But my love of these programs means I like to see them run as best they can.

To that end I also added a feature to continue with another dungeon if you get all the gold on a screen.  The game as it was would simply end in the same way as when you die. It would reveal the map and prompt for a replay. It didn't display a win message.  However, it seemed simple to preserve the player's basic stats of Hit Points, Experience and Gold Pieces and then generate a new map for the player to continue their quest. This will be extremely difficult as the monsters ramp up according to your own HP modified by their own intrinsic level of ferocity.  In fact, it might be a good strategy to simply bleed off HP down to a level that is easier to manage.

I also added a "bloodstain" which shows where you were killed on the map. It's just a red block, but it helps differentiate an end from death versus completing a level. I also added messages "You have found all the gold" and an alternate prompt "Continue to next level?" if you complete the map. Here's a video of a test of an early version of this feature:


I also added some sounds, such as when you find gold or are killed, since the MC-10 is a slightly more advanced machine than the soundless classic "trinity."  And I added some instructions to the title screen about the keys used in the game.

Hit Points Oddity Fixed?

There's an oddity about one of the keys. There seems to be no limit to the player's ability to press R (the 5 key in the original game) to "rest" and gain Hit Points. The only disincentive seems to be the time taken waiting for the screen update.

1290 IFA=ASC("5")THENHP=HP+1+SQR(EX/HP)

The use of the SQR function and a division of Experience by Hit Points scales the function down (I think) as you increase the number of HP, so the speed of increase will decrease as you add HP.  This means you will have to waste more time pressing the R key.  And you also will simply have to fight tougher monsters as you add HP as HP is one of the factors determining the stats of the monster you face.  So there is some mathematical alchemy of hitting the right spot of building strength in relation to experience that might be useful for game play. I will have to examine the code more closely and discuss this issue with my mathematically adept son next time he comes home. If he has any insights, I'll add them here.

But as I tested the game I realized that using the Rest function in combination with the "walk-through-walls" spell gave a pretty ironclad way to defeat the game. You just have to run away from monsters after you get the report of their HP and then build HP using the Rest function until you are equal or better than them. Then I realized I could even do this from the safety of being inside the walls!  So, as soon as I spotted a monster, I would just fade into the nearest wall and start hitting R. Then I would reemerge and kick the monster's butt. This seemed unfair and kind of dull once I'd figured it out.  So I just modified my version to prevent Resting while inside walls. And as further incentive, if you try to do this, you lose the standard amount of HP used for regular movement. I thought this made sense.  After all, you are casting and maintaining a powerful spell while travelling inside walls.  Now, at least you must use HP to travel and then risk emerging into areas to rest that may be inhabited by other denizens of the dungeon. A little more resource management and path planning skills will be needed.

Finally, I had to take all the fancy string declaration used in the original PET version to clear a space in high memory where the dungeon can be safely POKEd before being revealed to the screen by the Fog of War function.  Robin from 8-bit Show and Tell has a nice video documenting a bug that affects this function between early and later versions of PET BASIC.  This video is what made me aware of this classic game, and his information about the game and links to other sources was very helpful.  But I didn't have to replicate this method.  I could just use the CLEAR command to define the end of memory that BASIC is allowed to use.

CLEAR500,26384

Then I just poked the dungeon into the space above 26384. This second argument of CLEAR is usually for defining an area to POKE any machine language subroutines that your BASIC program might call using the USR or EXEC functions.  But its also handy for simply protecting an area for poking data into.  I guess the PET doesn't have this function or the programmer simply used a different method.  I've certainly used blank strings, usually in the form of array strings, to define areas to POKE info into.  It's a very memory efficient method as long as the strings are not altered so they are switched around in string space.  I usually use the VARPTR function to find the beginning of the various indexed array strings, and then POKE using the address returned.  But I didn't use that method here as the program itself uses a method of a single continuous space in memory like the screen memory somehow defined by the string declaration of the early version of PET BASIC, but not in the second version.  Robin was able to patch this code so it worked on the more common updated version of BASIC.

Since the MC-10 screen is smaller than the PET's I also made the dungeon generating routine use all the space in the box defined by the impermeable outside wall around the dungeon. The rooms therefore can butt right up against the outer walls. I also decreased the max room size from 9 to 7.  Hopefully these changes maximize the space for the dungeon.  My efforts at such re-scaling were helped by the fact that the program uses variables to define the horizontal and vertical screen size of the dungeon/screen area.  It makes me wonder whether the dungeon engine had been designed for some generic BASIC system and then utilized by Sawyer.  Either that or he possibly planned to create versions of the program for other systems.

One final oddity. The following code from the monster movement routine has a number 31 in it:

910 IFA=41THENA=39
920 IFA=-39THENA=-41
930 GOTO960
940 IFA=31THENA=41
950 IFA=-41THENA=-39

When moving up or down in a 40 column screen numbers like 40,-40,39-39, 41 and -41 make sense. They allow movement in the up, down and horizontal directions. But 31 doesn't make sense except for a 32 column screen like the MC-10 has. For that kind of screen you want values like 32,-32,31,-31,33 and -33.  It might just be that someone inadvertently modified the original PET source, but it seems strange. I got my source from PYDungeon on Github, who has made a port of the game to the modern language Python. But it might be another indication that the game was originally designed for a different system from the PET with a different screen resolution. Perhaps the KIM?  Who knows.
 
My source can be found here:


How to play

PETDUNG can be played at my GameJolt site under the "More 8-bit BASIC game ports" menu item.


Just select Play Game, "More 8-bit BASIC game ports", and then select PETDUNG from the Cassette Menu, and type RUN and hit Enter in the Main emulator screen.