Jump to content
IGNORED

New Tunnels of Doom engine?


Recommended Posts

Hey all,

 

Recently I got an urge to play a 10-level Tunnels of Doom game again, so I fired one up on Classic99. (For the record, successful game! My rogue ended up with triple digit hit points.)

 

As I played, I noticed flaws that always irked me. A mistaken keystroke causes you to lose your turn, or even canceling a ranged weapon. The weird artifacts, like monsters attempting and failing to use special attacks. The SLOW generation of the 3D view.

 

Then I found the truly excellent TOD guide by Michael Vepraukas, which is a gold mine on how the game file is structured. And it got me thinking, what if I could write a new engine in 100% TMS9900 assembly that could replicate the original game engine but with refinements and corrections, AND could also drive ANY of the TOD games?

 

The big challenge, really, is that there's no source available for the game. GPL is an eclectic thing, and when I tried using the GPL disassembler on a cartridge binary, it's 2nd line was a CALL to an area in the 32k expansion. Clearly not working. :P MOST of the game engine could be replicated without source, there's just two major hurdles:

 

  1. The basic attack/defense mechanics. These can probably be gleaned through observation, but it may take a lot of work to tune it to match the original engine's style. (I notice, for example, that the miss rate is inordinately high.)
  2. Map corridors. Due to lack of space, the corridors for floors are algorithmically generated each time to descend or ascend. Replicating this exactly is crucial. So far based on observation, the one thing I noticed is that rooms that are aligned vertically are always connected.

 

Any interest in this? Any insights or suggestions?

  • Like 13
Link to comment
Share on other sites

I think a new engine with new features and refinements would be very beneficial.  Of course, that being said, if there are new features then it would also need to be backwards compatible to run existing TOD games then of course any new TOD games may need a whole new TOD editor.  

 

 

  • Like 2
Link to comment
Share on other sites

1 hour ago, Gary from OPA said:

Yes, a new TOD would be great idea. I might be able to help produce a proper commented GPL disassembly. I will also search my archives and see if it's been partly done already.

That would be awesome!

 

 

Link to comment
Share on other sites

You'll probably get more efficient code from not seeing the original source anyway. The programming styles we see today are pretty different from what we did in '81, not to mention TOD would have strictly followed the TI rules that we blatantly ignore today. ;)

 

It would be really cool to see a new driver for the games... but it won't be me. :)

 

  • Like 3
Link to comment
Share on other sites

52 minutes ago, Tursi said:

You'll probably get more efficient code from not seeing the original source anyway. The programming styles we see today are pretty different from what we did in '81, not to mention TOD would have strictly followed the TI rules that we blatantly ignore today. ;)

 

It would be really cool to see a new driver for the games... but it won't be me. :)

 

You have done far MORE than your fair share for the TI community, Mike. I bow to you.

  • Like 2
Link to comment
Share on other sites

Oh yes, I found the first part of his article awhile back (I even posted on it), but I forgot he does talk about the algorithm a bit. Confirmed what I thought about verticals always being drawn!

 

I just noticed yesterday that corridors only come as straights, T's, or a cross. There's no corner pieces because there wasn't quite enough graphic room to store them. It's definitely a feature-centric algorithm.

 

I'll do some prototyping and see if I can get something going. I mainly would want the maps to match what the original engine would generate, but if it at least makes playable maps that could be good enough.

  • Like 2
Link to comment
Share on other sites

So I wrote up a prototype map generation in Extended BASIC to test out making corridors. My initial one definitely connects all the rooms but honestly makes WAY too many corridors:

 

image.thumb.png.4e7567ac62b6c3fc6ab137282d53b26c.png

 

Here's the code for the interested. Next step is to make it a bit less thirsty.

 

100 RANDOMIZE 1982 :: OPTION BASE 1 :: DIM RTB(26),RLR(26),RY(26),RX(26),RD(26),RYR(4),RYA(4),RXR(4),RXA(4)
110 CALL CLEAR :: CALL SCREEN(4):: CALL COLOR(9,6,15,10,6,15):: FOR I=96 TO 108 STEP 4 :: READ C$ :: CALL CHAR(I,C$):: NEXT I
120 FOR I=1 TO 4 :: READ RYR(I),RYA(I),RXR(I),RXA(I):: NEXT I :: FOR I=1 TO 19 :: CALL HCHAR(I,3,107,28):: NEXT I
130 R=1 :: GOSUB 1000 :: CALL VCHAR(Y,X,104):: RTB(R)=TB :: RLR(R)=LR :: RY(R)=Y :: RX(R)=X
140 FOR R=3 TO 4 :: GOSUB 1000 :: CALL VCHAR(Y,X,105):: RTB(R)=TB :: RLR(R)=LR :: RY(R)=Y :: RX(R)=X :: NEXT R
150 FOR R=5 TO 6 :: GOSUB 1000 :: CALL VCHAR(Y,X,106):: RTB(R)=TB :: RLR(R)=LR :: RY(R)=Y :: RX(R)=X :: NEXT R
160 FOR R=7 TO 26 :: GOSUB 1000 :: CALL VCHAR(Y,X,103):: RTB(R)=TB :: RLR(R)=LR :: RY(R)=Y :: RX(R)=X :: NEXT R

200 CALL SCREEN(10):: FOR R=1 TO 26 :: IF RY(R)=0 THEN 290
201 FOR R2=1 TO 26 :: IF R=R2 THEN 210 :: IF RY(R2)=0 THEN 210 :: IF RX(R)<>RX(R2) THEN 210
202 X=RX(R):: IF RY(R) < RY(R2) THEN Y=1+RY(R):: YS=Y ELSE Y=1+RY(R2) :: YS=Y
203 CALL GCHAR(Y,X,G):: IF G=107 THEN Y=Y+1 :: GOTO 203 ELSE CALL VCHAR(YS,X,97,Y-YS)
210 NEXT R2
290 NEXT R

300 CALL SCREEN(14):: FOR R=1 TO 26 :: IF RY(R)=0 THEN 390
301 FOR R2=1 TO 26 :: IF R=R2 THEN 310 :: IF RY(R2)=0 THEN 310 :: IF RY(R)<>RY(R2) THEN 310
302 Y=RY(R):: IF RX(R) < RX(R2) THEN X=1+RX(R) :: XS=X ELSE X=1+RX(R2) :: XS=X
303 CALL GCHAR(Y,X,G):: IF G=107 THEN X=X+1 :: GOTO 303 ELSE CALL HCHAR(Y,XS,96,X-XS)
304 IF G=97 THEN CALL VCHAR(Y,X,101)
310 NEXT R2
390 NEXT R

400 CALL SCREEN(11):: GOSUB 500 :: CALL SCREEN(4)
410 GOTO 410

500 FOR R=1 TO 26 :: IF RD(R)>1 OR RY(R)=0 THEN 990
510 CC=0 :: UC=0 :: DC=0 :: LC=0 :: RC=0
515 CALL GCHAR(RY(R)-1,RX(R),GU):: CALL GCHAR(RY(R)+1,RX(R),GD):: CALL GCHAR(RY(R),RX(R)-1,GL):: CALL GCHAR(RY(R),RX(R)+1,GR)
520 IF GR=97 THEN CALL VCHAR(RY(R),RX(R)+1,101):: CC=CC+1 :: GOTO 530
521 IF GR=102 THEN CALL VCHAR(RY(R),RX(R)+1,98):: CC=CC+1 :: GOTO 530
522 IF GR=107 THEN RC=1

530 IF GL=97 THEN CALL VCHAR(RY(R),RX(R)-1,102):: CC=CC+1 :: GOTO 540
531 IF GL=102 THEN CALL VCHAR(RY(R),RX(R)-1,98):: CC=CC+1 :: GOTO 540
532 IF GL=107 THEN LC=1

540 IF GU=96 THEN CALL VCHAR(RY(R)-1,RX(R),100):: CC=CC+1 :: GOTO 550
541 IF GU=99 THEN CALL VCHAR(RY(R)-1,RX(R),98):: CC=CC+1 :: GOTO 550
542 IF GU=107 THEN UC=1

550 IF GD=96 THEN CALL VCHAR(RY(R)+1,RX(R),99):: CC=CC+1 :: GOTO 560
551 IF GD=100 THEN CALL VCHAR(RY(R)+1,RX(R),98):: CC=CC+1 :: GOTO 560
552 IF GD=107 THEN DC=1

560 RD(R)=CC :: IF CC>=2 THEN 990

600 IF RC=0 OR CC>=2 THEN 700 ELSE Y=RY(R):: X=RX(R):: XS=X+1
610 X=X+1 :: CALL GCHAR(Y,X,G):: IF G=107 THEN 610
620 IF G=32 THEN 700
630 IF G>102 THEN CE=G :: GOTO 660
640 IF (G=101 OR G=102) THEN CE=98 :: GOTO 660
650 IF G=97 THEN CE=101 
660 CALL HCHAR(Y,XS,96,X-XS):: CALL HCHAR(Y,X,CE):: CC=CC+1

700 IF LC=0 OR CC>=2 THEN 800 ELSE Y=RY(R):: X=RX(R):: XS=X-1
710 X=X-1 :: CALL GCHAR(Y,X,G):: IF G=107 THEN 710
720 IF G=32 THEN 800
730 IF G>102 THEN CE=G :: GOTO 760
740 IF (G=101 OR G=102) THEN CE=98 :: GOTO 760
750 IF G=97 THEN CE=102 
760 CALL HCHAR(Y,X+1,96,XS-X):: CALL HCHAR(Y,X,CE):: CC=CC+1

800 IF UC=0 OR CC>=2 THEN 900 ELSE Y=RY(R):: X=RX(R):: YS=Y-1
810 Y=Y-1 :: IF Y=0 THEN 900 ELSE CALL GCHAR(Y,X,G):: IF G=107 THEN 810
830 IF G>102 THEN CE=G :: GOTO 860
840 IF (G=99 OR G=100) THEN CE=98 :: GOTO 860
850 IF G=96 THEN CE=100 
860 CALL VCHAR(Y,X,97,1+YS-Y):: CALL VCHAR(Y,X,CE):: CC=CC+1

900 IF DC=0 OR CC>=2 THEN 990 ELSE Y=RY(R):: X=RX(R):: YS=Y+1
910 Y=Y+1 :: CALL GCHAR(Y,X,G):: IF G=107 THEN 910
920 IF G=32 THEN 990
930 IF G>102 THEN CE=G :: GOTO 960
940 IF (G=99 OR G=100) THEN CE=98 :: GOTO 960
950 IF G=96 THEN CE=99 
960 CALL VCHAR(YS,X,97,Y-YS):: CALL VCHAR(Y,X,CE):: CC=CC+1

990 NEXT R :: RETURN

1000 TB=INT(RND*2)+1 :: LR=INT(RND*2)+1
1001 IF R>4 THEN Y=INT(RND*RYR(TB))+RYA(TB):: X=INT(RND*RXR(LR))+RXA(LR) ELSE Y=INT(RND*RYR(TB+2))+RYA(TB+2):: X=INT(RND*RXR(LR+2))+RXA(LR+2)
1002 CALL GCHAR(Y,X,G):: IF G<>107 THEN 1001
1003 CALL GCHAR(Y-1,X,G):: IF G<>107 THEN 1001 :: CALL GCHAR(Y+1,X,G):: IF G<>107 THEN 1001
1004 CALL GCHAR(Y,X-1,G):: IF G<>107 THEN 1001 :: CALL GCHAR(Y,X+1,G):: IF G<>107 THEN 1001
1005 RETURN

10000 DATA 000000FFFF0000001818181818181818181818FFFF181818181818FFFF000000
10001 DATA 000000FFFF181818181818F8F81818181818181F1F181818FF81BDA5A5BD81FF
10002 DATA 7EE7FFC3FF81FF7E7EFF81FFC3FFE77E3C66DBBDBDDB663C0000000000000000
10003 DATA 8383838383838383C0C0C0C0C0C0C0C000000000000000000000000000000000
11000 DATA 8,2,16,4
11001 DATA 8,10,10,20
11002 DATA 5,5,13,7
11003 DATA 5,10,7,20

 

  • Like 3
Link to comment
Share on other sites

18 minutes ago, Ksarul said:

You could eliminate a lot of them if you defaulted instances with a T corridor to an L unless it fails to connect the room to the grid. . .

There aren't any L corridors in Tunnels of Doom. Just T's, four-way, up/down and left/right.

Link to comment
Share on other sites

10 hours ago, Elia Spallanzani fdt said:

"corridor digging function links any two rooms, and knows how to follow an existing tunnel to avoid digging extra tunnels; I keep track of which rooms (and clusters of rooms) are connected using the disjoint set data structure"

I get the feeling he may have over-engineered this one. :) I was able to deterministically connect every room to a corridor without having to store much other than "this room has this many connections".

 

I would guess he MUST consider the room's quadrant (top, bottom, left right) as well since the room coordinates are stored to retain that data. Otherwise why wouldn't he have just used the two bytes to store a row and column? I'd probably use those data points to make sure it is biased to "head to center" so a room in the top left quadrant would try and go down and right, for example.

 

Also, his algorithm doesn't work if rooms are too far. A map I created randomly put two rooms along the topmost row and they failed to connect to the rest of the dungeon when the TOD cart rendered it.

 

All in all, this exercise is giving me confidence I can write a corridor algorithm that works well. It may mean a new engine renders a different maze, but that's fine.

  • Like 6
Link to comment
Share on other sites

On 3/26/2024 at 12:09 PM, adamantyr said:

Also, his algorithm doesn't work if rooms are too far. A map I created randomly put two rooms along the topmost row and they failed to connect to the rest of the dungeon when the TOD cart rendered it.

 

All in all, this exercise is giving me confidence I can write a corridor algorithm that works well. It may mean a new engine renders a different maze, but that's fine.

An interesting map! Anyone who has used  (I'm still using!) Realms, will have complete confidence in your ability to complete this project.

While I often considered a more versatile TOD, never considered legacy compatibility. Can now see how this could easily work.

Best wishes. MikeV.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

After some iterating, I got a map like this:

image.thumb.png.c06a27ec744ddf9bb74ee2af2f8e2a21.png

Looks great, right? Look closer... two large sections never connect with each other!

 

On the one hand, the map is still usable in the game because you can always "check" for secret doors in one of the rooms to get into the other area. But the original TOD engine tracked sets of rooms to prevent it happening at this large of a scale. (Although not well; I remember finding maps with "barbell" rooms that connected to nothing else.)

 

  • Like 1
Link to comment
Share on other sites

35 minutes ago, adamantyr said:

After some iterating, I got a map like this:

image.thumb.png.c06a27ec744ddf9bb74ee2af2f8e2a21.png

Looks great, right? Look closer... two large sections never connect with each other!

 

On the one hand, the map is still usable in the game because you can always "check" for secret doors in one of the rooms to get into the other area. But the original TOD engine tracked sets of rooms to prevent it happening at this large of a scale. (Although not well; I remember finding maps with "barbell" rooms that connected to nothing else.)

 

There is also one room that has no connections at all (near the rightmost stairs).

  • Like 1
Link to comment
Share on other sites

Yeah, that was due to a miscalculation of room connections. The trouble is, if I set it to a minimum set of connections per room, it still doesn't solve the disconnected large sections problem.

 

So I'm going to try a different approach and start storing the set data and see what I can do with that.

  • Like 1
Link to comment
Share on other sites

I took a look with the debugger to get an idea of what was in the TOD module. I wondered if there was assembly code that is copied to 32K like the LOGO cartridge, but that doesn't seem to be the case. There is nothing in the expansion ram. Looks like there are 5 groms and at 6K each that is 30K of gpl code. In general, gpl code is around 2/3 the size of assembly, so that would be around 45K of assembly code which is quite a bit more than the 32K expansion. I see the ways around this to be:

1 - a banked rom cartridge, which would have plenty of room

2 - a hybrid cartridge, using grom for the parts that do not need speed and assembly for the actual game play. You could copy code into the 32K and run it from there, or use banked rom.

3 - Sams might work, but you would have to store the code in rom (or disc) and copy it to sams. Then the sams would have to be banked as the program runs, so that may be no better than a banked rom cartridge, except that variables and data could be kept in ram instead of vdp ram.

Sounds like an interesting challenge, and a good one too. But it has to be somebody else, not me. (As Bert Williams sang)

  • Like 1
Link to comment
Share on other sites

8 minutes ago, senior_falcon said:

I took a look with the debugger to get an idea of what was in the TOD module. I wondered if there was assembly code that is copied to 32K like the LOGO cartridge, but that doesn't seem to be the case. There is nothing in the expansion ram. Looks like there are 5 groms and at 6K each that is 30K of gpl code. In general, gpl code is around 2/3 the size of assembly, so that would be around 45K of assembly code which is quite a bit more than the 32K expansion. I see the ways around this to be:

1 - a banked rom cartridge, which would have plenty of room

2 - a hybrid cartridge, using grom for the parts that do not need speed and assembly for the actual game play. You could copy code into the 32K and run it from there, or use banked rom.

3 - Sams might work, but you would have to store the code in rom (or disc) and copy it to sams. Then the sams would have to be banked as the program runs, so that may be no better than a banked rom cartridge, except that variables and data could be kept in ram instead of vdp ram.

Sounds like an interesting challenge, and a good one too. But it has to be somebody else, not me. (As Bert Williams sang)

Yeah, GPL is more compact than TMS9900 assembly.

  • #3 would be my favorite option, as I did that with ROA and it works great. But making the engine require a SAMS card may sour the offer for some. :) I may initially do a SAMS version first but mindful that I should design with ROM paging in mind.
  • #1 is the more likely choice, with 32K expansion required because the whole point is to make a fast smooth engine, not one crammed into 256 bytes of RAM. :)
  • #2 is right out. I have ZERO interest in using GPL.
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...