nesdev.com
http://forums.nesdev.com/

WIP: Wizard of Wor
http://forums.nesdev.com/viewtopic.php?f=22&t=16172
Page 3 of 9

Author:  tschak909 [ Mon Jul 10, 2017 8:25 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  tschak909 [ Tue Jul 11, 2017 1:03 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  mikejmoffitt [ Tue Jul 11, 2017 1:38 pm ]
Post subject:  Re: WIP: Wizard of Wor

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.

Author:  Myask [ Tue Jul 11, 2017 7:59 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  tschak909 [ Tue Jul 11, 2017 8:51 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  tschak909 [ Tue Jul 11, 2017 11:38 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  tschak909 [ Wed Jul 12, 2017 12:19 am ]
Post subject:  Re: WIP: Wizard of Wor

Well gosh, that was easy.

Code:
  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

Author:  tschak909 [ Thu Jul 13, 2017 12:54 am ]
Post subject:  Re: WIP: Wizard of Wor

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

Code:
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:
  // 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:

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

Author:  na_th_an [ Thu Jul 13, 2017 2:51 am ]
Post subject:  Re: WIP: Wizard of Wor

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:
    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.

Author:  tepples [ Thu Jul 13, 2017 6:41 am ]
Post subject:  Re: WIP: Wizard of Wor

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.

Quote:
You can also precalculate the offsets. offsX, offsY are unsigned char. addr is unsigned int.

Code:
// ...
            // 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.

Author:  tschak909 [ Thu Jul 13, 2017 9:22 am ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  rainwarrior [ Thu Jul 13, 2017 9:59 am ]
Post subject:  Re: WIP: Wizard of Wor

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/master/libsrc/runtime/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).

Author:  na_th_an [ Thu Jul 13, 2017 2:45 pm ]
Post subject:  Re: WIP: Wizard of Wor

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.

Author:  tschak909 [ Thu Jul 13, 2017 4:09 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Author:  na_th_an [ Thu Jul 13, 2017 4:54 pm ]
Post subject:  Re: WIP: Wizard of Wor

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

Page 3 of 9 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/