Vectrex Aklabeth (3)

Dungeon (continued)

Moving around a dungeon.

This really irritated me at first and I thought the program had a bug. I double checked with Paul Robsons C version (running on my Mac).

After each door (or secret door) I got heavily disoriented. This was because I expected doors to be features BETWEEN two dungeon locations, this is NOT the case. A door occupies one complete dungeon square. So within the game you can reside “inside” a doorway and look in different diections. Once you know that and get used to it it feels ok (-ish), but expecting otherwise and experiencing this is really weird (it gets even creepier with secret doors).

Anyways – displaying monsters using the above type of lists is now also a straight forward thing:
(already included some optimization regarding positioning and scaling)

void DRAWMonster(int x,int y,int Monster,int distance)
{
	switch(Monster)							// Call appropriate function 
	{
		case MN_SKELETON:   draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)SkeletonSyncList);break;
        	case MN_THIEF:      draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)ThiefSyncList);break;
		case MN_RAT:        draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)RatSyncList);break;
		case MN_ORC:        draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)OrcSyncList);break;
		case MN_VIPER:      draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)ViperSyncList);break;
		case MN_CARRION:    draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)CarrionSyncList);break;
		case MN_GREMLIN:    draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)GremlinSyncList);break;
		case MN_MIMIC:      draw_synced_list( Scale[distance-2], Scale[distance-2], 0x9c00, (void *)MimicSyncList);break;
		case MN_DAEMON:     draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)DemonSyncList);break;
		case MN_BALROG:     draw_synced_list( Scale[distance-2], Scale[distance-2], 0xce00, (void *)BalrogSyncList);break;
	}
}

Attack!

Next on the “todo” list was enabling us to attack monsters. While theoretically all easy going… it was not as easy as expected.

  • this is sort of a “game interruption” – meaning we enter attack mode, and after the attack finished we return to the main “loop”. For that to work we must build a new “display loop” for the attacks, since we always need to refresh the screen
  • input… the original uses a key press to select weapons – the first letter of the weapon is used. Naturally on the vectrex we do not have “letter” buttons. But more importantly we only have four buttons and in Aklabeth you can attack with 6 different “weapons”. We had to devise a playable way to do this…
  • unexpected… but not surprising… I had to “debounce” buttons… I hate this, IMHO debounce code always looks bad/stupid…

The ways I do it right now is:

  • button 4 to enter attack mode
  • use a mixture of button presses of Button 1, 2 and 3 to select the weapon
    (keep 1 or more buttons pressed, shows the selected wepon on screen)
  • button 4 to execute the attack

This sounds complicated, but after fighting a few battles you get used to it… and it is quicker than a selection menu.

Weapon usage is surprisingly “complex”:

  • there are weapons that are only usable by a specific class
  • there are melee weapons, distance weapons and both (you can either throw an axe or swing it)
  • the amulet is usable by both classes, but behaves differently
  • stats of your character influence usage:
    – amulet damage depends on dungeon depth
    – weapon damage depends on strength
    – whether you hit depends on your dexterity
    – …

As an “example” – a part of the attack function:
(Chose the weapon – and build a display loop around it. DDAWDraw() – displays the current dungeon.)

	...
	// chose weapon
	while (1)
	{
		CLS;
		Wait_Recal();
		Do_Sound();
		VIA_t1_cnt_lo = 0x80; // scale
		check_buttons();
		
		Intensity_a(0x5f); // set intensity of vector beam...
		DDRAWDraw(p,d);
		print_timed("With what ? ");                // Which weapon
		VIA_t1_cnt_lo = 0x80; // scale
		n = -1;
		if ((Vec_Btn_State &7) == 0b00000001) {n = 1;print_timed("Attack with Rapier?");} //Rapier
		if ((Vec_Btn_State &7) == 0b00000010) {n = 2;print_timed("Attack with Axe?");} //Axe
		if ((Vec_Btn_State &7) == 0b00000011) {n = 3;print_timed("Attack with Shield?");} //Shield
		if ((Vec_Btn_State &7) == 0b00000100) {n = 4;print_timed("Attack with Bow?");} //Bow+Arrow
		if ((Vec_Btn_State &7) == 0b00000101) {n = 5;print_timed("Attack with Amulet?");} //Amulet
		if ((Vec_Btn_State & 8) == 8) break;
		
		if (messageTime>0)
		{
			//            messageTime--;
			Print_Str_d(-128+5+6, -128, (void* const) messageBuffer);
		}
	}
	...

The rest of the attack code are mainly these small display loops, which ask for some input. After the attack is calculated and the monster stats are updated, the attack function ends and returns to main loop.

Beware they live!

At the moment the monsters are a dumb bunch, which just stand in the way. Now we have to add Monster AI!

Ok, yes. AI is a big word. But again for a small BASIC program from 1979 – this is cool!

Each time the player moves – all monsters in the current dungeon get a chance to do something. A monster can (depending on circumstances and type):

  • attack the player
  • move towards the player
  • run away from the player
  • recover hit points
  • steal food (half of the food!)
  • steal objects you carry

Implementation wise, this was more of the same. basically “normal” C code – which was mainly used as it was… so there is nothing much to talk about.

Lord British

This also was no major hassle, basically much text output, done in intertwined display rounds. First time you enter you are supposed to enter your name. For this functionality I have not done a counterpart yet… so you are unnamed :-(.

First version

This was the first version I did. Just all parts transcoded to Vectrex – and got everything running.

You can download the “first version”:
(this version, though feature complete, runs at a terrible frame rate, comments are rare and sometimes missleading – the code is not cleaned up.

To use this you need VIDE installed.

Copy the contents to the “projects” directory and open the contained project file.)

to be continued

(next is optimizing the above)

Tagged on: , ,

6 thoughts on “Vectrex Aklabeth (3)

  1. Peer

    Again thanks for the write-up. I played some more, but I still suck at the fighting part. So far, I have refrained from inspecting the code to see if I can deduce some better fighting tactics from it 😉

    I suppose that draw_synced_list() is a C macro? Otherwise parameters x and y would be unused in DRAWMonster(). If so, why are x and y not proper macro parameters? Or, in general, why is draw_synced_list() not a proper C function?

    Cheers,
    Peer

    1. Malban Post author

      Because this function is already a slightly more optimized version. draw_synced_list() is actually an assembler function – not a macro.
      Indeed the x,y parameters are not used! As I discovered that given the correct “synced” list – all monsters can be drawn from the same position (more or less, which actually is the “0xce00” parameter of the drw_sync function).

      Good spotting!

  2. Phillip Eaton

    Oh, I just read Lord British and had a flashback. I checked and, yes, I do now I have a reference to Akalabeth, which, until now, had been just some old Apple II game. I read about Lord British several years back in Steven Levy’s Hackers book (a great read), I went back and checked, he’s mentioned several times from page 397 of the (legally available) online PDF version, and he’s the guy who did all the Ultima titles. Now I have some context – and a binary – I’ll have a look at this and piece together some more computer history. Thanks for releasing it as a WIP!

    1. Peer

      Hey, great, thanks for the pointer! I knew I had also read about this before, but couldn’t remember where. The Hackers book was right behind me on the shelf, and now I looked it up. I will be rereading that chapter tonight 🙂

  3. Phillip Eaton

    I guess this is a theme for conversions to the Vectrex. I compiled a simple Forth command line version of 2048 from Rosetta Code to the Vectrex, and it took a few hours to have it fully working on the command line on the Vectrex using a VecFever serial port terminal. Then I converted it to the vector display using standard Forth print string formatting for each number and the print-str-D BIOS function, resulting in a frame rate of about 10Hz! I will eventually return to optimise it, the challenge will be the get to 50Hz using 100% Forth, I think it’s doable.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.