Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
103 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

The code for the mouse maps

In my game you can zoom in and out of the map. Actually there are two zoom levels, large and small, so in the following code segments m_Size is the current tile size (0 = Large, 1 = Small).

First define the global mouse map array:

char g_MouseMap[2][20][64][32];

[2] is for the two mouse maps
[20] is for the 20 tiles that I use
[64] is for the height of a large tile (64 x 64)
[32] is for the width of a large tile (64 x 64). I'm using 7 colours so I'm compressing the data down, and saving approx 80K of memory.

Next the mouse map lookup constants:

#define MM_NONE 0
#define MM_NORTHWEST 1
#define MM_NORTH 2
#define MM_NORTHEAST 3
#define MM_SOUTHWEST 4
#define MM_SOUTH 5
#define MM_SOUTHEAST 6

These are the same as the ones Ernest uses, but I've added North and South movement.

A helper function to load the mouse maps in to the array from 256 colour RAW files (produced from PSP without a header):

bool LoadMouseMaps()
{
  FILE *fin;
  int x, y, o;
  char c;

  fin = fopen( "smtiles.raw", "rb" );

  if( fin == NULL )
  {
    Log( __FILE__, __LINE__, "Unable to open smtiles.raw" );
    return false;
  }
  else
  {
    for( y = 0; y < 32; y++ )
    {
      for( o = 0; o < 20; o ++ )
      {
        for( x = 0; x < 16; x ++ )
        {
          c = ((fgetc(fin) & 0x0F) << 4) + fgetc(fin) & 0x0F;

          g_MouseMap[1][o][y][x] = c;
        }
      }
    }

    fclose( fin );

    fin = fopen( "lgtiles.raw", "rb" );

    if( fin == NULL )
    {
      Log( __FILE__, __LINE__, "Unable to open lgtiles.raw" );
      return false;
    }
    else
    {
      for( y = 0; y < 64; y++ )
      {
        for( o = 0; o < 20; o ++ )
        {
          for( x = 0; x < 32; x ++ )
          {
            c = ((fgetc(fin) & 0x0F) << 4) + fgetc(fin) & 0x0F;

            g_MouseMap[0][o][y][x] = c;
          }
        }
      }

      fclose( fin );
    }
  }

  return true;
}

The Log( __FILE__, __LINE__, "***" ) function is my error logging code, but the rest of the code is standard C / C++.  Please note that when exporting a raw file from PSP, reduce the colours down to 16 and then export, taking note of how the system has re-arranged your colours in the palette as this caused a problem in my code at first.

The following is a little helper to get the mouse map movement number:

int MM_LookUp( int x, int y, int t )
{
  int v;

  x %= ( m_Size == 1 ? 32 : 64 );
  y %= ( m_Size == 1 ? 32 : 64 );

  v = g_MouseMap[m_Size][t][y][x >> 1];

  if( ( x & 1 ) == 0 )
    v = v & 0x0F;
  else
    v = ( v >> 4 ) & 0x0F;

  return v;
}

And finally the code for the main mouse map function, please note this code still has the infinite loop in it:

void MouseMap( int xp, int yp, int &mx, int &my )
{
  int m;
  int cx, cy;
  int fx, fy;
  int md, mc;

  // Adjustment for lower left corner offset.
  yp += ( m_Size == 1 ? 32 : 64 );

  // Step 1 : Screen -> World Co-Ords
  xp += m_OffX;
  yp += m_OffY;

  // Step 2 : Offset from world co-ords of tile (0,0)

  xp -= ( ( 0 - 0 ) << ( 5 - m_Size ) );
  yp -= ( ( 0 + 0 ) << ( 4 - m_Size ) );

  // Step 3 : Determine MouseMap Co-ords

  // Course co-ords
  cx = xp / ( m_Size == 1 ? 32 : 64 );
  cy = yp / ( m_Size == 1 ? 16 : 32 );

  // Fine co-ords
  fx = xp % ( m_Size == 1 ? 32 : 64 );
  fy = yp % ( m_Size == 1 ? 16 : 32 );

  // Adjust for my Unusual mouse maps...
  fy += ( m_Size == 1 ?  8 : 16 );

  // Adjust for negative co-ords
  if( fx < 0 )
  {
    fx += ( m_Size == 1 ) ? 32 : 64;
    cx --;
  }
  if( fy < 0 )
  {
    fy += ( m_Size == 1 ) ? 32 : 64;
    cy --;
  }

  // Step 4 : Perform Corse Tile walk
  mx = 0;
  my = 0;

  while( cy < 0 )
  {
    TileWalk( mx, my, ISO_NORTH );
    cy ++;
  }

  while( cy > 0 )
  {
    TileWalk( mx, my, ISO_SOUTH );
    cy --;
  }

  while( cx < 0 )
  {
    TileWalk( mx, my, ISO_WEST );
    cx ++;
  }

  while( cx > 0 )
  {
    TileWalk( mx, my, ISO_EAST );
    cx --;
  }

  // Step 5 : Use the lookup table for final tile walk...
  m = -1;
  mc = 4;

  while( ( m != MM_NONE ) && ( mc > 0 ) )
  {
    m = MM_LookUp( fx,
      fy + (Map[( my >= 0) ? my : 0][(mx >= 0) ? mx : 0].Height << (3 - m_Size)),
      Map[(my >= 0) ? my : 0][(mx >= 0) ? mx : 0].Floor);

    switch( m )
    {
    case MM_NORTHWEST:
      {
        if( md == MM_SOUTHEAST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTHWEST );
        fx += ( m_Size == 1 ) ? 16 : 32; // Half x
        fy += ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_NORTH:
      {
        if( md == MM_SOUTH )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTH );
        fy += ( m_Size == 1 ) ? 16 : 32; // Half y
        break;
      }
    case MM_NORTHEAST:
      {
        if( md == MM_SOUTHWEST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTHEAST );
        fx -= ( m_Size == 1 ) ? 16 : 32; // Half x
        fy += ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_SOUTHWEST:
      {
        if( md == MM_NORTHEAST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTHWEST );
        fx += ( m_Size == 1 ) ? 16 : 32; // Half x
        fy -= ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_SOUTH:
      {
        if( md == MM_NORTH )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTH );
        fy -= ( m_Size == 1 ) ? 16 : 32; // Half y
        break;
      }
    case MM_SOUTHEAST:
      {
        if( md == MM_NORTHWEST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTHEAST );
        fx -= ( m_Size == 1 ) ? 16 : 32; // Half x
        fy -= ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    }
    md = m;
  }
}

A slight problem with the above techniques

Ok so the above would work, as long as you do not go above a height of 2.  To go above a height of 2, a small change to step 5 of the mouse map function is required. Add a new variable declaration (int tfy) and you're back in business:

// Step 5 : Use the lookup table for final tile walk...
m = -1;
mc = 4;

while( ( m != MM_NONE ) && ( mc > 0 ) )
{
  m = MM_LookUp( fx,
    fy + (Map[(my >= 0) ? my : 0][(mx >= 0) ? mx : 0].Height << (3 - m_Size)),
    Map[(my >= 0) ? my : 0][(mx >= 0) ? mx : 0].Floor);

  tfy = (fy +
        (Map[(my >= 0) ? my : 0][(mx >= 0) ? mx : 0].Height << (3 - m_Size)));

  if( ( fx >= 0 ) && ( tfy >= 0 ) && ( fx < ( m_Size == 1 ? 32 : 64 ) ) &&
      ( tfy < ( m_Size == 1 ? 32 : 64 ) ) )
  {
    switch( m )
    {
    case MM_NORTHWEST:
      {
        if( md == MM_SOUTHEAST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTHWEST );
        fx += ( m_Size == 1 ) ? 16 : 32; // Half x
        fy += ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_NORTH:
      {
        if( md == MM_SOUTH )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTH );
        fy += ( m_Size == 1 ) ? 16 : 32; // Half y
        break;
      }
    case MM_NORTHEAST:
      {
        if( md == MM_SOUTHWEST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_NORTHEAST );
        fx -= ( m_Size == 1 ) ? 16 : 32; // Half x
        fy += ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_SOUTHWEST:
      {
        if( md == MM_NORTHEAST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTHWEST );
        fx += ( m_Size == 1 ) ? 16 : 32; // Half x
        fy -= ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    case MM_SOUTH:
      {
        if( md == MM_NORTH )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTH );
        fy -= ( m_Size == 1 ) ? 16 : 32; // Half y
        break;
      }
    case MM_SOUTHEAST:
      {
        if( md == MM_NORTHWEST )
          mc --;
        else
          mc = 4;
        TileWalk( mx, my, ISO_SOUTHEAST );
        fx -= ( m_Size == 1 ) ? 16 : 32; // Half x
        fy -= ( m_Size == 1 ) ?  8 : 16; // Quarter y
        break;
      }
    }
  }
  else
  {
    if( tfy >= ( m_Size == 1 ? 32 : 64 ) )
    {
      m = MM_SOUTH;
      TileWalk( mx, my, ISO_SOUTH );
      fy -= ( m_Size == 1 ) ? 16 : 32;
    }
  }
  md = m;
}

I've included a copy of my test application that uses the above techniques.




Contents
  Introduction
  The code

  Source code
  Printable version
  Discuss this article