WIP: Wizard of Wor

A place where you can keep others updated about your NES-related projects through screenshots, videos or information in general.

Moderator: Moderators

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Mon Jul 10, 2017 8:25 pm

I will definitely look at that engine. Looks great at first glance.

As for graphics, doing yet another pass. Was able to gain 6 pixels in both X and Y directions by pushing the lines in the line tiles toward the edge.

So I am pushing things back, and shuffling around tiles...

I really don't want to push sprites to 16x16...

I for damned sure won't change the text to 8x8, because it will lose all its character.

I hope to get this stable enough to where I can compress all the tiles (get rid of duplicates etc) so I can gain some much needed tile space, especially on the sprite side.

-Thom

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Tue Jul 11, 2017 1:03 pm

Further refinement, turns out, I needed to completely push the Worriors off the screen to get a 10x6 matrix (which can be encoded as a symmetrical 5x3 array)...

Image

But this leaves no room for the Worriors, at all, and with the gate timer, this makes any sort of lives counter be awkward as hell..so...

I may back away to a 8x6 matrix (4x3 symmetry)... OR

I squeeze the sprites to 2x2 instead of 2x3... *deep-breath* will try... I also need to add additional walk cycles and their 90 degree counterparts, I will run out of chr space if I'm not careful...

I have to get all this squared away before I can start writing the meat of the game as all the mazes are drawn programmatically.

-Thom

User avatar
mikejmoffitt
Posts: 1348
Joined: Sun May 27, 2012 8:43 pm

Re: WIP: Wizard of Wor

Post by mikejmoffitt » Tue Jul 11, 2017 1:38 pm

I think you can do well just cramming the lives count to the sides of the radar text, either with small icons or a number. Better to not compromise the playfield.

User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: WIP: Wizard of Wor

Post by Myask » Tue Jul 11, 2017 7:59 pm

well, 10x6 isn't going to be the same as 11x6…

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Tue Jul 11, 2017 8:51 pm

Myask wrote:well, 10x6 isn't going to be the same as 11x6…
I see no way to get 11x6 without squishing the sprites. Which.. given my background in research... I'll do... I won't promise I won't be griping all the way through it, but hey... :)

-Thom

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Tue Jul 11, 2017 11:38 pm

Currently giving myself a crash course in vram_set_update() and the different update vram functions in neslib..goal is to re-draw the dungeon in code, and work out a reasonably quick and compact data structure for the dungeon...now that I have a reasonably good idea of how the tiles in the nametable are to be laid out.

-Thom

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Wed Jul 12, 2017 12:19 am

Well gosh, that was easy.

Code: Select all

  vram_adr(NAMETABLE_A);
  vram_unrle(wow_dungeon);
  pal_bg(palette);
  pal_spr(palette);
  vram_adr(NTADR_A(0,0));

  for (i=0;i<6;++i)
    {
      for (j=0;j<10;++j)
        {
          vram_adr(NTADR_A((j*3)+1,(i*3)+1));
          vram_put(0x74);
          vram_put(0x63);
          vram_put(0x75);
          vram_adr(NTADR_A((j*3)+1,(i*3)+2));
          vram_put(0x65);
          vram_put(0x00);
          vram_put(0x66);
          vram_adr(NTADR_A((j*3)+1,(i*3)+3));
          vram_put(0x76);
          vram_put(0x64);
          vram_put(0x77);
        }
    }

  ppu_on_all();
  ppu_wait_frame();
Which produces:

Image

Will make this more efficient, shortly..but it's nice to know that I understand the vram well enough.

Pushing code up to github now, calling it a night. it's 2am.

-Thom

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Thu Jul 13, 2017 12:54 am

Took a first crack at the dungeon rendering code, which renders from an array of data in the following format

Code: Select all

ULDR TSWX
-------------
0000 0000
Where ULDR is Up, Down Left, Right which sides of the box are to be open, T indicates a teleport wall, S is an enemy spawn point, and W is a Wizard spawn point. Worluk is special as he always appears at one door, and traverses toward the other.

The first pass of the code is inefficient, and I will refine it as I think it through further, but the meat of it is:

Code: Select all

  // ULDR
  // 1111

  dungeon=(char*)dungeon1;
  
  b=0;  // dungeon array index

  for (i=0;i<6;++i)
    {
      for (j=0;j<10;++j)
  	{
	  /* Tile 1 */
  	  vram_adr(NTADR_A((j*3)+1,(i*3)+1));
	  if (( (dungeon[b] & 1<<7) ) && ( (dungeon[b] & 1<<6) ))            /* UP AND LEFT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* LEFT WITH TELEPORT */
		{
		  vram_put(0x73);                                    
		}
	      else
		{
		  vram_put(0x74);                               
		}
	    }
	  else if ( (dungeon[b] & 1<<7) )                           /* UP */
	    {
	      vram_put(0x63);
	    }
	  else if ( (dungeon[b] & 1<<6) )                           /* LEFT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* LEFT WITH TELEPORT */
		{
		  vram_put(0x73);                                    
		}
	      else
		{
		  vram_put(0x65);                               
		}
	    }
	  else
	    {
	      vram_put(0x00);
	    }

	  // Tile 2
	  if (dungeon[b] & (1<<7))            /* UP */
	    {
	      vram_put(0x63);
	    }
	  else
	    {
	      vram_put(0x00);
	    }
	  
	  // Tile 3
	  if (( (dungeon[b] & 1<<7) ) && ( (dungeon[b] & 1<<4) ))            /* UP AND RIGHT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* RIGHT WITH TELEPORT */
		{
		  vram_put(0x98);
		}
	      else
		{
		  vram_put(0x75);
		}
	    }
	  else if ( (dungeon[b] & 1<<7) )                           /* UP */
	    {
	      vram_put(0x63);
	    }
	  else if ( (dungeon[b] & 1<<4) )                           /* RIGHT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* RIGHT WITH TELEPORT */
		{
		  vram_put(0x98);
		}
	      else
		{
		  vram_put(0x66);
		}
	    }
	  else
	    {
	      vram_put(0x00);
	    }

  	  vram_adr(NTADR_A((j*3)+1,(i*3)+2));
	  // Tile 4
	  if (dungeon[b] & (1<<6))            /* LEFT */
	    {
	      if (dungeon[b] & (1<<3))      /* LEFT WITH TELEPORT */
		{
		  vram_put(0x73);
		}
	      else
		{
		  vram_put(0x65);
		}
	    }
	  else
	    {
	      vram_put(0x00);
	    }

	  // Tile 5 is always empty.
	  vram_put(0x00);

	  // Tile 6
	  if (dungeon[b] & (1<<4))            /* RIGHT */
	    {
	      if (dungeon[b] & (1<<3))      /* RIGHT WITH TELEPORT */
		{
		  vram_put(0x98);
		}
	      else
		{
		  vram_put(0x66);
		}
	    }
	  else
	    {
	      vram_put(0x00);
	    }
	  

	  vram_adr(NTADR_A((j*3)+1,(i*3)+3));
	  // Tile 7
	  if (( (dungeon[b] & 1<<6) ) && ( (dungeon[b] & 1<<5) ))            /* LEFT AND DOWN */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* LEFT WITH TELEPORT */
		{
		  vram_put(0x73);
		}
	      else
		{
		  vram_put(0x76);
		}
	    }
	  else if ( (dungeon[b] & 1<<6) )                           /* LEFT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* LEFT WITH TELEPORT */
		{
		  vram_put(0x73);
		}
	      else
		{
		  vram_put(0x65);
		}
	    }
	  else if ( (dungeon[b] & 1<<5) )                           /* DOWN */
	    {
	      vram_put(0x64);
	    }
	  else
	    {
	      vram_put(0x00);
	    }

	  // Tile 8
	  if (dungeon[b] & (1<<5))            /* DOWN */
	    {
	      vram_put(0x64);
	    }
	  else
	    {
	      vram_put(0x00);
	    }

	  // Tile 9
	  if (( (dungeon[b] & 1<<4) ) && ( (dungeon[b] & 1<<5) ))            /* DOWN AND RIGHT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* RIGHT WITH TELEPORT */
		{
		  vram_put(0x98);
		}
	      else
		{
		  vram_put(0x77);
		}
	    }
	  else if ( (dungeon[b] & 1<<5) )                           /* DOWN */
	    {
	      vram_put(0x64);
	    }
	  else if ( (dungeon[b] & 1<<4) )                           /* RIGHT */
	    {
	      if ( (dungeon[b] & 1<<3) )                          /* RIGHT WITH TELEPORT */
		{
		  vram_put(0x98);
		}
	      else
		{
		  vram_put(0x66);
		}
	    }
	  else
	    {
	      vram_put(0x00);
	    }
	  ++b;	  	  
  	}
    }
And this is the base dungeon template, 60 bytes for now, 30 if I mirror one axis, 15 if I mirror both axes.

Code: Select all


const unsigned char dungeon1[60]={0xC0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x90,
                                  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
                                  0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,
                                  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
                                  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
                                  0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30};
Which produces the following output:

Image

I'll draw the first real dungeon tomorrow (actually, this is the layout for "The Pit" which is the hardest dungeon, hehe.)

-Thom

na_th_an
Posts: 543
Joined: Mon May 27, 2013 9:40 am

Re: WIP: Wizard of Wor

Post by na_th_an » Thu Jul 13, 2017 2:51 am

Looking great.

I'd get rid of the *3's. Using "*" includes extra runtime code plus is slow. You can do the X*3 as (X+(X<<1)) for example. You can also precalculate the offsets. offsX, offsY are unsigned char. addr is unsigned int.

Code: Select all

    offsY = 1;
    for (i=0;i<6;++i) {
        offsX = 1;
        for (j=0;j<10;++j) {
            addr = NTADR_A (offsX, offsY);

            vram_adr( addr );
            // Here: 'vram_put's for the first row.

            // Down a row in the nametable
            addr+=32;
            vram_adr (addr);
            // Here: 'vram_put's for the second row.

            // Down a row in the nametable
            addr+=32;
            vram_adr (addr);
            // Here: 'vram_put's for the third row.

            // Next tile:
            offsX += 3;
        }
        // Next row of tiles
        offsY += 3;
    }
That way you calculate the base address, in the most simple way (just a shift and an addition, which is what NTADR_A does), just once per tile, simplifying your code, thus saving both bytes and cycles.

tepples
Posts: 21755
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: WIP: Wizard of Wor

Post by tepples » Thu Jul 13, 2017 6:41 am

na_th_an wrote:I'd get rid of the *3's. Using "*" includes extra runtime code plus is slow. You can do the X*3 as (X+(X<<1)) for example.
The compiler should be doing this shift conversion for you. If you can prove that it isn't, then either A. something in your code is of the wrong type or B. you need to file an issue.
You can also precalculate the offsets. offsX, offsY are unsigned char. addr is unsigned int.

Code: Select all

// ...
            // Next tile:
            offsX += 3;
This appears to be related to strength reduction, where you can update addresses with a simpler operation based on the recursion that xtimes3[x + 1] = xtimes3[x] + 3. So instead of multiplying by 3 for each cell, add 3 to the destination VRAM address after drawing each cell.

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Thu Jul 13, 2017 9:22 am

All valid points, indeed. I will go through and refine the algorithm as I can.

A note, I am not familiar with CC65 internals. To be blunt, the common wisdom BITD was that the 6502 COULDN'T do a decent C without EMULATING ANOTHER PROCESSOR (p-system, and most 6502 C's that I used literally modelled an 8080, so CC65 to me seems like something of a minor miracle.), so I am not sure WHAT compiler optimizations are actually taking place yet (I need to sit down and look at the outputted source code.

-Thom

User avatar
rainwarrior
Posts: 7680
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: WIP: Wizard of Wor

Post by rainwarrior » Thu Jul 13, 2017 9:59 am

I don't think CC65 has a generic capability to generate shift/add combinations instead of multiplication, but it does seem to have special case library functions for multiplying by a literal in the range 2-10? Here's the one for multiplying by 3:
https://github.com/cc65/cc65/blob/maste ... e/mulax3.s

So... doing (x<<1)+x is slightly faster, I think, but there's a lot of encumbering inefficiencies anyway (stack temporaries, promotion to integer), so the difference is kind of pointless unless you want to write the multiply directly in assembly.

I think you're actually better off with x*3 just because it will produce smaller code (and code size tends to be a serious problem with CC65).

na_th_an
Posts: 543
Joined: Mon May 27, 2013 9:40 am

Re: WIP: Wizard of Wor

Post by na_th_an » Thu Jul 13, 2017 2:45 pm

I've been working for years with a fairly old version of z88dk, so I have the (bad?) habit of not trusting the compiler at all. I also have the bad habit of not checking, which I should start doing, as I was avoiding structs in cc65 'cause I mistakenly thought that they generated worse code when accessing the members than using plain variables. My bad.

In this case, the *3 would indeed be better 'cause this is not a time critical task, so saving bytes is a priority.

I stand, thus, corrected :)
tepples wrote:This appears to be related to strength reduction, where you can update addresses with a simpler operation based on the recursion that xtimes3[x + 1] = xtimes3[x] + 3. So instead of multiplying by 3 for each cell, add 3 to the destination VRAM address after drawing each cell.
I do this all the time, when appliable. It saves me tons of headaches, at least for my own mindset.

tschak909
Posts: 139
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: WIP: Wizard of Wor

Post by tschak909 » Thu Jul 13, 2017 4:09 pm

Okay, several hours of laying out a dungeon, and I'm ready to write a clicky editor.

Anyone got a suggestion on what to use to whip one up for Windows? ;)

-Thom

na_th_an
Posts: 543
Joined: Mon May 27, 2013 9:40 am

Re: WIP: Wizard of Wor

Post by na_th_an » Thu Jul 13, 2017 4:54 pm

I use the good ol' Mappy (mapwin). It can be tuned up to output the current map layer as a plain, headerless set of bytes, one per tile, which you can then process in a batch script to fit your needs.

http://www.mojontwins.com/warehouse/Mappy-mojono.rar

Post Reply