← Optimizing Buggy Boy (2)Keyboard handling (1) →
  Dungeon Master intro
Wed 20th May 2020   
In 1996 I released a small prototype of what could be Dungeon Master on the Oric.

People being people, they kept bugging me about when I would finish it, so 24 years later I decided to take a look at it.

The 1996 prototypes

Dungeon layout tests
Dungeon layout tests



Back in the days, I was just starting to learn again the 6502, I only had primitive tools, and had no idea on how to exploit the floppy disk drive, so obviously the results were kind of primitive...

All that was done way before all these modern discussions about AIC, that was before the advanced dithering algorithms provided by LibPippi or PictOric, so everything is drawn using large flat surfaces in single colors, because it's all I knew how to do!

I did draw some basic items and character portraits, using a maximum of two colors per scanline (which explains the 6 pixels spaces between the characters to allow for the attributes placements).

A few dungeon objects

You can see here one of the first few releases I made back then:


This very first prototype1 was just using lines to draw the walls, drawn directly on the video memory, which explains the black transition when moving, because it was slow to draw on the screen.

For the next version, I added some "proper" bitmap blocks for the walls, doors and traps, assembled together to create the final dungeon view.

Old walls

The result is still not awesome, but at least it does not flicker:


And just to finish on the topic of these old previews, here is a mashup of a bunch of graphics I did, with some tests for character creation, stairs, view from when you fell in a trap, etc...

Mashup of old graphics
Mashup of old graphics

That sums up where the project ended.

HALT

There was a few reasons explaining why the project did not get farther at the time.

The main one is really that I realized that trying to make an actual port of the Dungeon Master game as it was designed on the Atari ST or Amiga would be detrimental to the playability because the Oric does not have a mouse.

Most of the Dungeon Master gameplay revolves around the ability to move with the arrow block 2 while clicking around the UI or the screen to pick-up objects from the floor, perform attack actions, activate buttons on walls and doors, etc... in a very intuitive and efficient way.

There are some console ports of the game that use the pad to move a virtual mouse cursor, but it feels very wrong and awkward, so that's definitely not the direction I wanted to take.

The other issue was that I did not know how to use the floppy drive, did not know about to access the overlay memory, and struggled to see how to efficiently draw animated monsters on the top of the maze without having to revert to a pure monochrome or very attribute-clashy display.

Fast forward a few years decades, and we have solutions for most of the technical problems:
  • I now have the FloppyBuilder which can be used to access data on the disk efficiently
  • The associated loader leaves more than 15kb free in overlay memory
  • Thanks to Twilighte creativity we can use AIC to get an acceptable way to display colorful screens
Here is an example of what the graphics could look like using AIC, with a few of the Atari ST 16 colors monsters for comparison.

AIC graphic test
AIC graphic test

It's still far from perfect, but at least it shows that we could have a game with a recognizable look, which is good enough for me.

Still, I did not really come up with any good ideas3 regarding the in-game interactions without a mouse.

If you have ideas or suggestion, please share!

The FTL intro

One thing I always liked in Dungeon Master, was the nice and clean introduction sequence, with the FTL4 swooshing animation and the zooming Dungeon logo.

If you've never seen it, here is an emulated capture of the animation (minus the disk drive loading and animation preparation delays):


On the real Atari ST there are significant delays because the game starts loading some data, and the Dungeon zoom is done procedurally to avoid wasting disk space.

I was wondering if I could do something similar on the Oric, so I spent a couple of hours thinking about it and doing some calculations on paper, a couple days extracting the original graphics and converting them to something the Oric could display, and one day doing the code to display it.

Here is what I came up with, I hope you like it (watch it in fullscreen, the embedded version shows some scanline artefacts):


So, now this is out of the way (I did not want to put the result at the end of the page), let see how I did it.

Conceptualization

Contrary to some things I've heard, most of good things achieved in software is not the result of endless nights of hacking, but more of a preliminary phase of thinking and designing to eliminate the bad ideas and select a few that could work, then some experimentation, and finally some proper development and multiple phases of revisions and polishing5.

In this particular case, we know that we have the following elements:
  • a white FTL logo that appears with a swoosh animation and sound and a small yellow star at the end
  • a fading in and out "Presents" text
  • a relatively large embossed golden zooming "Dungeon" word
  • a red "Master" word with a black shadow under
Obviously, being on the Oric we have a number of choices to do, regarding the acceptable visual quality degradation (we simply don't have all the possible colors, we have color proximity constraints, and the horizontal resolution is 240 instead of 320), as well as the storage issues - both on disk and in memory.

For obvious reason the biggest issue is to get the zoom done properly!

After doing a quick and dirty rescaling of a screenshot, I found out that the maximum size of the "Dungeon" logo was 216x60 pixels, which gives us 60 by 36 bytes = 2160 bytes for this logo.

Investigating the animation frame by frame shows that there are about 16 different sizes, which means we could store 16 of these big size logo in 34560 bytes, which is way under the 37631 available bytes, so no problem storing the various sizes in memory.

Regarding the "Presents", it's just a tiny 17 pixels tall bitmap, the "Master" is 180x37, and the "FTL" logo is 183x88, so not much more additional size, we should be fine!

Implementation

Now that we have confirmed that there is no problem storing all that in memory, the question is about how to animate things.

Contrary to what I've read about what Chema, Twilighte and myself have done, there is no magic involved, and given the choice between complicated or simple, we generally tend to chose "simple" if that actually meets the requirements.

The Zooming Dungeon

In this particular case the big question is how to perform the zoom.

A proud elite demomaker would quickly get his calculator and list of opcodes and come up with super smart solution to write an elegant piece of optimized 6502 assembler code able to generate an arbitrary number of scaled down versions of the logo.

I'm more like a lazy kind of guy, so instead I decided to make the various sizes of logo in my painting program and save it all in one single big picture with all the bitmap data in 240 pixel wide, this way I can just call memcpy to display any of the elements!

The swooshing FTL

I used the same approach for the FTL logo.

The animation is so fast, that really spending the time to do the proper "curving" apparition is not worth the time, and is typically the kind of thing that could be done later in a second iteration phase.

What I came up with for this version is simply a vertical drawing of the image, scanline by scanline, with an ATTRIBUTE based gradient on three colors to make it appear like a nice fading transition.

And obviously I needed some sound to accompany the effect, so I reused by "YM virtual registers" code from my New Year 2019 CEO Competition, just increasing the volume of one of the registers while changing the pitch of the white noise generator.

Like the animation itself, this could be improved, but as a first attempt that was good enough!

Putting it all together

Here is the actual picture I'm using in the intro:

Intro graphics data
Intro graphics data

I'm not actually loading it: It's a part of the executable and actually compressed with the intro code, which brings some advantages in term of code complexity because you don't have to figure out where you have free memory, the assembler is doing all the work for you.

Here is the script to convert the picture to an assembler source you can just add to your make file.
SET PICTCONV=%OSDK%\Bin\PictConv -u1 -m0
%PICTCONV% -f0 -o4_SwooshData% data\swoosh_data.png code\swoosh_data.s
then you can just refer to the data with a simple "extern unsigned char SwooshData[];"

To easily locate each of the elements in the bitmap, I added an enum and an array with the start lines of each of the bitmaps:
enum IntroGraphics
{
INTRO_PRESENTS,
INTRO_FTL,
INTRO_MASTER,
INTRO_DUNGEON_11,
INTRO_DUNGEON_10,
INTRO_DUNGEON_9,
INTRO_DUNGEON_8,
INTRO_DUNGEON_7,
INTRO_DUNGEON_6,
INTRO_DUNGEON_5,
INTRO_DUNGEON_4,
INTRO_DUNGEON_3,
INTRO_DUNGEON_2,
INTRO_DUNGEON_1,
_INTRO_MAX_
};

int GraphicOffset[_INTRO_MAX_+1]=
{
1,
18,
107,
145,
206,
262,
313,
361,
406,
448,
487,
521,
553,
578,
595
};
I then added a very simple function able to display any of the graphical elements in the center of the screen by just specifying the bitmap number (using the enum above), as well as a vertical offset (only used to get the "Master" displayed under the "Dungeon" logo).
void BlitBlock(int blockId,int yOffset)
{
int yStart,yEnd,height;

yStart=GraphicOffset[blockId];
yEnd =GraphicOffset[blockId+1]-2;
height=1+yEnd-yStart;

memcpy((unsigned char*)0xa000+40*(yOffset+100-height/2),SwooshData+yStart*40,height*40);
}
the usage is quite easy: BlitBlock(INTRO_PRESENTS,0); will display the word "Presents" in the center of the screen.

Here is what the entire Dungeon Master zoom code looks like:
void ShowZoomingDungeonLogo()
{
int zoomFactor;
FillScreen(64);

FancyDitheredFade(15,16+4,3);

for (zoomFactor=INTRO_DUNGEON_1;zoomFactor!=INTRO_MASTER;zoomFactor--)
{
BlitBlock(zoomFactor,0);
WaitVbl();
}
WaitVBL(25);
BlitBlock(INTRO_MASTER,50);
WaitVBL(50);

FancyDitheredFade(10,16,1);
FancyDitheredFade(10,16,0);

FillScreen(64);
}
As you can see, all I do is to go from INTRO_DUNGEON_1 (the smallest logo) to INTRO_DUNGEON_11 (the largest), with just a synchronization with the screen refresh to keep a smooth animation, and the "Master" is displayed half a second later with a 50 pixels offset down the screen.

The "FancyDitheredFade" and "FillScreen" are just some simple functions I wrote to clean the screen or put attributes to perform color changes:
void FillScreen(unsigned char value)
{
memset((unsigned char*)0xa000,value,8000);
}
It does not provide any real benefit other than being nicer on the eyes when reading the code :)

Colors and compression

Maybe some of the readers who did not fall asleep are wondering why the picture with all the bitmaps is black and white since ultimately they will be displayed with colors.

There are two main reasons:
  • Better compression ratio
  • Fades and transitions
The advantage of having just one big picture fitting the width of the screen, is that it makes the blitting code super simple since everything is 40 bytes wide.

Obviously this is a waste of memory, but there's no benefit in having free memory other than mental satisfaction, and on disk that has no impact since this is all compressed, and long runs of BLACK take the same room independently of how large they are.

Which is why I chose to not store the colors in the image: Each of the color attributes embeded in the image would break the runs of black pixels, resulting in a larger file on disk!

The second advantage is that by having source data without any attribute I can easily perform color changes, fades, etc... without having to worry about some spurious attribute taking over my changes.

In code I implemented a quite ugly-yet-practical function:
void ApplyAttributes(unsigned char* start,int stride, int lineCounter,char a1,char a2)
{
while (lineCounter--)
{
start[0]=a1;
start[1]=a2;
start+=stride;
}
}
What the function does is quite simple: Given a start address, a stride (number of bytes to jump) and a number of line, the function applies to values in memory (in this particular use: a PAPER and a INK value).

The cool thing is that since my bitmap and the screen have the same size and format, I can use this function to apply attributes on the screen or on the bitmap itself, which allowed me to apply the colors I wanted on the in-memory bitmap at the start of the program, without impacting the compression ratio:
// "Dungeon" is YELLOW over BLUE
ApplyAttributes(SwooshData+40*GraphicOffset[INTRO_DUNGEON_11],40,
GraphicOffset[_INTRO_MAX_]-GraphicOffset[INTRO_DUNGEON_11],16+4,3);

// "Master" is RED over BLUE
ApplyAttributes(SwooshData+40*GraphicOffset[INTRO_MASTER],40,
GraphicOffset[INTRO_DUNGEON_11]-GraphicOffset[INTRO_MASTER],16+4,1);
And the same function is used to perform the "pseudo fades" on the BLUE background and logos:
void FancyDitheredFade(int delay, char a1,char a2)
{
ApplyAttributes((unsigned char*)0xa000,160,50,a1,a2);
WaitVBL(delay);
ApplyAttributes((unsigned char*)0xa000+80,160,50,a1,a2);
WaitVBL(delay);
ApplyAttributes((unsigned char*)0xa000+40,160,50,a1,a2);
WaitVBL(delay);
ApplyAttributes((unsigned char*)0xa000+120,160,50,a1,a2);
WaitVBL(delay);
}

void FadeOut()
{
ApplyAttributes((unsigned char*)0xa000,40,200,16,7);
WaitVBL(5);
ApplyAttributes((unsigned char*)0xa000,40,200,16,6);
WaitVBL(5);
ApplyAttributes((unsigned char*)0xa000,40,200,16,3);
WaitVBL(5);
ApplyAttributes((unsigned char*)0xa000,40,200,16,4);
WaitVBL(5);
ApplyAttributes((unsigned char*)0xa000,40,200,16,0);
}
It's quite primitive, basically the idea is to use larger values of stride (normally 40 bytes per line) to skip over some of the lines, so instead of changing 200 lines, I change only 100 lines every second line, or 50 lines every 4 lines, which gives this transition effect with the horizontal scanlines.

And that's about it for the fancy visual effects, let's talk about sound!

The AY swoosh sound

As mentionned earlier, I reused a virtual soundchip system which makes it easy to do sound manipulations in C or assembler without having to deal with the VIA or perform function calls.

Basically I'm exposing the 13 soundchip registers as normal in-memory variables:
extern unsigned int  PsgfreqA;         //  0 1
extern unsigned int PsgfreqB; // 2 3
extern unsigned int PsgfreqC; // 4 5
extern unsigned char PsgfreqNoise; // 6
extern unsigned char Psgmixer; // 7
extern unsigned char PsgvolumeA; // 8
extern unsigned char PsgvolumeB; // 9
extern unsigned char PsgvolumeC; // 10
extern unsigned int PsgfreqShape; // 11 12
extern unsigned char PsgenvShape; // 13

extern unsigned char PsgNeedUpdate;
All the user needs to do is to put the correct values in the virtual registers, and set PsgNeedUpdate to a non null value.

On the next IRQ the handler will check the PsgNeedUpdate variable, and if set will update the real AY registers with the content of the virtual registers.

I'm using that in the code that displays the FTL logo:
Psgmixer=1+2+4+8+16+32+64+128 & ~8;  // NOISE on CANAL A active
for (y=0;y<88;y++)
{
if (PsgvolumeA<15)
{
// Increases volume over time
PsgvolumeA++;
}

if (y<32)
{
// Increases the noise frequency over time
PsgfreqNoise++;
}
else
if ( (PsgfreqNoise>0) && (y&1))
{
// And then decreases slowly to give the pseudo doppler effect
PsgfreqNoise--;
}
PsgNeedUpdate=1;

memcpy(screenAddress,sourceAddress,40);
screenAddress[1+40*0]=1;
screenAddress[1+40*1]=3;
screenAddress[1+40*2]=7;

sourceAddress-=40;
screenAddress-=40;

WaitVbl();
}
So basically I'm using the Y variable used to draw each of the horizontal lines of the logo from bottom to top, to also be used as a way to control the sound.

The advantage of this method is that if I wanted to change the amount of time spent to Wait between lines, the sound would also be impacted so it would not finish earlier or later than the graphics.

And that's it for this article, you can watch the associated video on youtube if you want :)


The source code, as usual, will be available on the Defence-Force SVN server, but here is also a direct link to an archive containing exactly all the files and scripts necessary to rebuild this version: DungeonMasterIntro.ART15.zip

The Credits

While I was at it, I did a quick test on making the "Scroll" work on the Oric.

The original was much more complex, with some actual wood handles, and nice shadows and anti-aliasing but trying to replicate that just did not look good enough, so I simplified it to a single sheet of parchment, and instead of the red initials, I used invert video to get the White letters of the Blue background.

Dungeon Master credits
Dungeon Master credits

So there it is, that was me re-imagining the Dungeon Master intro sequence on the Oric.

If you want to watch the complete 50 minutes long capture of me playing Dungeon Master on the real Atari STe (the entire first level of the game, including character creation and floppy formatting!) you can watch this other video:



1. Thanks to Hirudo for the video, it appears that I have lost this version!
2. Which on the ST consists of the usual 4 arrows but also of INSERT and CLR HOME on the left of right of the up key, used to rotate left and right
3. Right now my only idea is to reinvent the game from the perspective of a single character, or have the three other characters in the party play by themselves when youy are not controlling them.
4. Faster Than Light
5. Some people call that Agile, I personally call that "Iterative Waterfall", because for some reason people claiming to do Agile are skipping the design and refactoring phase.
comments powered by Disqus

Coverity Scan Build Status