Chapter 14. Loading Native Mappy Files

This chapter builds on the subject covered in the last chapter on creating levels with Mappy, and takes it one step further by providing the information and source code you'll need to load up a native Mappy file directly with Allegro. You will be able to edit a Mappy FMP file and then load that file directly in your game. This will eliminate the need to export the level data from Mappy, and greatly speed up and simplify the development of a tile-based game (such as the games we'll create in the following two chapters). Here's what is covered in this chapter:

Studying the Mappy Allegro Library (MappyAL)

The Mappy file structure is binary and includes not only the data, but also the tiles. A library has been created to support Mappy within Allegro programs and is available for download on the Mappy website at http://www.tilemap.co.uk. The library is called MappyAL, and the current release at the time of this writing is 11D. The MappyAL code is available on the CD-ROM in the softwaremappy folder.

All you need are the mappyal.c and mappyal.h files to use Mappy map files in your own programs. Because I will not be going into the advanced features of Mappy or the MappyAL library, I encourage you to browse the Mappy home page, view the tutorials, and download the many source code examples (including many complete games) to learn about the more advanced features of Allegro. We only really need basic tile editing and the ability to save a simple Mappy file for use in our programs. If you want to do something like multi-layer levels, you can refer to Chapter 16, “Horizontal Scrolling Platform Games,” for more information.

Note

Like Mappy itself, the Mappy Allegro library (MappyAL) was created by Robin Burrows. I am grateful to Robin for sharing his excellent level editing program and library source code.

The MappyAL Library

The MappyAL library is very easy to use. Basically, you call MapLoad to open a Mappy file. MapDrawBG is used to draw a background of tiles, and MapDrawFG draws foreground tiles (specified by layer number). There is one drawback to the MappyAL library—it was written quite a long time ago, back in the days when VGA mode 13h (320 × 200) was popular. Unfortunately, the MappyAL library only renders 8-bit (256 color) maps correctly.

You can convert a true color map to 8-bit color. Simply open the MapTools menu and select Useful Functions, Change Block Size/Depth. This will change the color depth of the map file; you can then import 8-bit tiles and the map will be restored. Paint Shop Pro can easily convert the tiles used in this chapter to 8-bit without too much loss of quality. Ideally, I recommend using the simple text map data due to this drawback. Table 14.1 shows a list of the most useful of the global variables exposed by the MappyAL code. This is not a comprehensive list.

Table 14.1. Mappy Allegro Library Global Variables

Variable

Data Type

Description

maperror

int

Error number after failed load

mapwidth

int

Width of map (in tiles)

mapheight

int

Height of map (in tiles)

mapblockwidth

int

Width of single tiles

mapblockheight

int

Height of single tiles

mapdepth

int

Color depth of tiles

mapnumblockgfx

short int

Total number of blocks in map

Let's take a look at some of the key functions in the Mappy Allegro library as well, since you'll be working with them quite a bit. There are support functions in the library beyond this list, but they are mainly called by these functions. I think the function names are self-descriptive. This is not a comprehensive list (the less important functions are seldom used).

  • void MapFreeMem (void);

  • int MapLoad (char *);

  • int MapLoadVRAM (char *);

  • int MapGetBlockID (int, int);

  • int MapGenerateYLookup (void);

  • int MapChangeLayer (int);

  • int MapGetXOffset (int, int);

  • int MapGetYOffset (int, int);

  • void MapInitAnims (void);

  • void MapUpdateAnims (void);

  • void MapDrawBG (BITMAP *, int, int, int, int, int, int);

  • void MapDrawFG (BITMAP *, int, int, int, int, int, int, int);

  • BITMAP * MapMakeParallaxBitmap (BITMAP *, int);

  • void MapDrawParallax (BITMAP *, BITMAP *, int, int, int, int, int, int);

Loading a Mappy File

Given that you have an FMP file ready to be loaded, you can use the MapLoad function provided by the MappyAL library to load a native Mappy file. Here is an example:

MapLoad("level.fmp");

Tip

Due to a bug in the MapLoad function, it returns a–1 for success and 0 for failure, which causes problems when checking the return value. Therefore, when you check the return value, you want to look for non-zero to indicate success. Since we have the source code, it would be easy to fix this bug, but I have left MappyAL as is.

Once you have loaded a Mappy file, the MappyAL library exposes a lot of global variables and functions that contain the information you need to work with the Mappy file. Before you load another level, or quit the program, you should always free the memory used by the Mappy level by calling the MapFreeMem function:

MapFreeMem();

Retrieving the Tile Number

You can check on a tile number located at a specific x,y location by calling the MapGetBlockID function. This represents the tile number of the block at that position in the level. Here is an example:

tile_num = MapGetBlockID(200, 300);

Drawing a Background Layer

Mappy allows you to configure a tile that is drawn as the background or the foreground for the layer you're working on. In essence, you can draw two different layers from a single tile map. To draw the background layer, you can use the MapDrawBG function. Here is an example that draws the tile map starting at coordinates (map_pos_x, map_pos_y) in a window at the upper-left corner of the screen with a size of 200, 200:

MapDrawBG(screen, map_pos_x, map_pos_y, 1, 1, 200, 200);

Drawing a Foreground Layer

You can specify up to three foreground layers for a single tile map, each with a different tile if you want. Most of the time you'll just use a single layer for a level and tell it which tiles are solid or not (for collision purposes—something we'll get into in Chapter 16). The only time you'll need to draw a foreground is when you've created a parallax level in which there's a background (such as clouds or mountains or something else in the distance) that scrolls independently of the foreground objects (usually ledges and other things to walk on). Most of the time, for simple games, you'll only draw a background. When you do need to draw a foreground layer over the top of a background layer (with transparency), you use the MapDrawFG function. It has the exact same set of parameters as MapDrawBG, and here's an example:

MapDrawFG(screen, map_pos_x, map_pos_y, 1, 1, 200, 200);

Loading a Native Mappy File

Now it's time to write a short test program to see how to load a native Mappy file containing map data and tiles, and then display the map on the screen with the ability to scroll the map. Create a new project, add a reference to the Allegro library, and add the mappyal.c and mappyal.h files to the project. (These source code files provide support for Mappy in your Allegro programs.) Then, type in the following code into the main.c file. You can use the map1.fmp file you saved earlier in this chapter—or you can use any Mappy file you want to test, because this program can render any Mappy file regardless of dimensions (which are stored inside the map file rather than in the source code). Figure 14.1 shows the TestMappy program running.

The TestMappy program demonstrates how to load a native Mappy file.

Figure 14.1. The TestMappy program demonstrates how to load a native Mappy file.

#include "allegro.h"
#include "mappyal.h"
#define MODE GFX_AUTODETECT_WINDOWED
#define WIDTH 640
#define HEIGHT 480
#define WHITE makecol(255,255,255)

//x, y offset in pixels
int xoffset = 0;
int yoffset = 0;

//double buffer
BITMAP *buffer;

int main (void)
{
    //initialize program
    allegro_init();
    install_timer();
    install_keyboard();
    set_color_depth(16);
    set_gfx_mode(MODE, WIDTH, HEIGHT, 0, 0);

    //create the double buffer and clear it
    buffer = create_bitmap(SCREEN_W, SCREEN_H);
    clear(buffer);

    //load the Mappy file
    if (MapLoad("map1.fmp") != 0)
    {
       allegro_message ("Can't
       return 1;
    }

    //main loop
    while (!key[KEY_ESC])
    {
       //check for keyboard input
       if (key[KEY_RIGHT]) xoffset+=4;
       if (key[KEY_LEFT])  xoffset-=4;
       if (key[KEY_UP])    yoffset-=4;
       if (key[KEY_DOWN])  yoffset+=4;

       //make sure it doesn't
       if (xoffset < 0) xoffset = 0;
       if (xoffset > mapwidth*mapblockwidth-SCREEN_W)
           xoffset = mapwidth*mapblockwidth-SCREEN_W;
       if (yoffset < 0) yoffset = 0;
       if (yoffset > mapheight*mapblockheight-SCREEN_H)
           yoffset = mapheight*mapblockheight-SCREEN_H;
       //draw map with single layer
       MapDrawBG(buffer, xoffset, yoffset, 0, 0, SCREEN_W-1, SCREEN_H-1);

       //print scroll position
       textprintf_ex(buffer,font,0,0,WHITE,-1,
           "Position = %d,%d", xoffset, yoffset);
       textprintf_ex(buffer,font,200,0,WHITE,-1,
           "Map size = %d,%d, Tiles = %d,%d", mapwidth, mapheight,
           mapblockwidth, mapblockheight);

       //blit the double buffer
       blit (buffer, screen, 0, 0, 0, 0, SCREEN_W-1, SCREEN_H-1);
    }

    //delete double buffer
    destroy_bitmap(buffer);

    //delete the Mappy level
    MapFreeMem();

    allegro_exit();
    return 0;
}
END_OF_MAIN()

Tip

If your compiler produces any warnings when compiling a project using MappyAL, you may safely ignore the warnings as they are due to type conversions, which are harmless.

Enhancing Tank War

Now it's time for an update to Tank War, the seventh revision to the game. Chapter 13 provided some great fixes and new additions to the game, including animated tanks and non-interrupting explosions. As you might have guessed, this chapter brings native Mappy support to Tank War. It should be a lot of fun, so let's get started! This is going to be an easy modification, just a few lines of code, because Tank War was designed from the start to be flexible. However, there is a lot of code that will be removed from Tank War, because MappyAL takes care of all the scrolling for us. So by using a more powerful library, we've significantly cut down on the amount of code in the game itself.

Do you remember the dimensions of the map1.fmp file that was used in Chapter 13? It is 100 tiles across by 100 tiles down. However, the “actual” map only uses 30 tiles across, 32 tiles down. This is a bit of a problem for Tank War, because MappyAL will render the entire map, not just the visible portion. The reason the map was set to 100 × 100 was to make the Mappy tutorial easier to explain, and at the time it did not matter. Now we're dealing with a map that is 3,200 × 3,200 pixels, which won't work in Tank War. (Actually, it will run just fine, but the tanks won't be bounded by the edge of the map.)

To remedy this situation, I have created a new version of the map file used in this chapter and called it map3.fmp. It is located in chapter14 ankwar_r7 along with the project files for this new revision of Tank War. That same map file was also used in the TestMappy program (because I didn't want to confuse you when the program scrolls beyond the edge of the “map”).

What's the great thing about this situation? You can create a gigantic battlefield map for Tank War! There's no reason why you should limit the game to a mere 30 × 32 tiles. Go ahead and create a huge map, with lots of different terrain, so that it isn't so easy to find the other player. Of course, if you do create a truly magnificent level, you'll need to modify the bullet code—it wasn't designed for large maps, so you can't fire again until the bullet reaches the edge of the map. Just put in a timer so that the bullet will expire if it doesn't hit anything after a few seconds.

Proposed Changes to Tank War

The first thing to do is add mappyal.c and mappyal.h to the project to give Tank War support for the MappyAL library. I could show you how to render the tiles directly in Tank War, which is how the game works now, but it's far easier to use the functions in MappyAL to draw the two scrolling game windows. You can open the completed project from chapter14 ankwar_r7, or open the Chapter 13 version of the game and make the upcoming changes.

How about a quick overview? Figure 14.2 shows Tank War using the map file from the TestMappy program! The next shot, Figure 14.3, player two is invading the base of player one!

Tank War now supports the use of Mappy files instead of a hard-coded map.

Figure 14.2. Tank War now supports the use of Mappy files instead of a hard-coded map.

Support for Mappy levels gives Tank War a lot of new potential, as anyone can create a custom battlefield for the game.

Figure 14.3. Support for Mappy levels gives Tank War a lot of new potential, as anyone can create a custom battlefield for the game.

Modifying Tank War

Now let's make the necessary changes to Tank War to replace the hard-coded background with support for Mappy levels.

Modifying tankwar.h

First up is the tankwar.h header file. Add a new #define line to include the mappyal.h file in the project. Note the change in bold.

/////////////////////////////////////////////////////////////////////////
// Game Programming All In One, Third Edition
// Tank War Enhancement 7 - tankwar.h
/////////////////////////////////////////////////////////////////////////

#ifndef _TANKWAR_H
#define _TANKWAR_H

#include "allegro.h"
#include "mappyal.h"

Next, remove the reference to the hard-coded map array (I have commented out the line so you will see what line to remove). This line follows the bitmap definitions.

//the game map
//extern int map[];

Next, delete the definition for the tiles bitmap pointer. Because Mappy levels contain the tiles, your program doesn't need to load the tiles, just the map file (isn't that great?).

//bitmap containing source tiles
//BITMAP *tiles;

Finally, delete the reference to the scroll bitmap, which is also no longer needed:

//virtual background buffer
//BITMAP *scroll;

Well, you've ripped out quite a bit of the game with just this first file! That is one fringe benefit to using MappyAL, in that a lot of source code formerly required for scrolling (the hard way) is now built into MappyAL.

Modifying setup.c

Next up is the setup.c source code file. Scroll down to the setupscreen function and slash the code that loads the tiles and draws them on the virtual background image. You can also delete the section of code that created the virtual background. I'll list the entire function here with the code commented out that you should delete. Note the changes in bold.

void setupscreen()
{
    int ret;

    //set video mode
    set_color_depth(16);
    ret = set_gfx_mode(MODE, WIDTH, HEIGHT, 0, 0);
    if (ret != 0) {
        allegro_message(allegro_error);
        return;
    }


/* REMOVE THIS ENTIRE SECTION OF COMMENTED CODE
   //create the virtual background
     scroll = create_bitmap(MAPW, MAPH);
     if (scroll = = NULL)
     {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Error creating virtual background");
        return;
     }

     //load the tile bitmap
     tiles = load_bitmap("tiles.bmp", NULL);
     if (tiles = = NULL)
     {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Error loading tiles.bmp file");
        return;
     }

     //now draw tiles on virtual background
     for (tiley=0; tiley < scroll->h; tiley+=TILEH)
     {
         for (tilex=0; tilex < scroll->w; tilex+=TILEW)
         {
             //use the result of grabframe directly in blitter
             blit(grabframe(tiles, TILEW+1, TILEH+1, 0, 0, COLS, map[n+ +]),
            scroll, 0, 0, tilex, tiley, TILEW, TILEH);
     }
   }

     //done with tiles
     destroy_bitmap(tiles);

     END OF THE CHOPPING BLOCK
*/

     //load screen background
     back = load_bitmap("background.bmp", NULL);
     if (back == NULL)
     {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Error loading background.bmp file");
        return;
     }

     //create the double buffer
     buffer = create_bitmap(WIDTH, HEIGHT);
     if (buffer == NULL)
     {
         set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
         allegro_message("Error creating double buffer");
         return;
     }

     //position the radar
     radarx = 270;
     radary = 1;

     //position each player
     scrollx[0] = 100;
     scrolly[0] = 100;
     scrollx[1] = MAPW - 400;
     scrolly[1] = MAPH - 500;

     //position the scroll windows
     startx[0] = 5;
     starty[0] = 93;
     startx[1] = 325;
     starty[1] = 93;
}

Modifying tank.c

Now open up the tank.c file and scroll down to the movetank function. Down at the bottom of the function, you'll see the section of code that keeps the tank inside the boundary of the map. This was based on the virtual background bitmap's width and height, but now needs to be based on the Mappy level size instead. The mapwidth, mapblockwidth, mapheight, and mapblockheight variables are global and found inside mappyal.h. Make the changes noted in bold.

void movetank(int num)
{
    int dir = tanks[num]->dir;
    int speed = tanks[num]->xspeed;

    //animate tank when moving
    if (speed > 0)
    {
       tanks[num]->animdir = 1;
       tanks[num]->framedelay = MAXSPEED - speed;
    }
    elseif (speed < 0)
    {
        tanks[num]->animdir = -1;
        tanks[num]->framedelay = MAXSPEED - abs(speed);
    }
   else
      tanks[num]->animdir = 0;


   //update tank position
   switch(dir)
   {
      case 0:
         scrolly[num] -= speed;
         break;
      case 1:
          scrolly[num] -= speed;
          scrollx[num] += speed;
          break;
    case 2:
         scrollx[num] += speed;
         break;
    case 3:
       scrollx[num] += speed;
       scrolly[num] += speed;
       break;
    case 4:
       scrolly[num] += speed;
       break;
    case 5:
       scrolly[num] += speed;
       scrollx[num] -= speed;
       break;
    case 6:
       scrollx[num] -= speed;
       break;
    case 7:
       scrollx[num] -= speed;
       scrolly[num] -= speed;
       break;
}

//keep tank inside bounds
if (scrollx[num] < 0)
{
    scrollx[num] = 0;
    tanks[num]->xspeed = 0;
}

else if (scrollx[num] > mapwidth*mapblockwidth - SCROLLW)
{
    scrollx[num] = mapwidth*mapblockwidth - SCROLLW;
    tanks[num]->xspeed = 0;
}

if (scrolly[num] < 0)
{
   scrolly[num] = 0;
   tanks[num]->xspeed = 0;
}
else if (scrolly[num] > mapheight*mapblockheight - SCROLLH)
 {
   scrolly[num] = mapheight*mapblockheight - SCROLLH;
   tanks[num]->xspeed = 0;
 }
}

Modifying main.c

Now open up the main.c file. The first thing you'll need to do in main.c is remove the huge map[] array definition (with included map tile values). Just delete the whole array, including the #define B 39 line. I won't list the commented-out code here because the map definition was quite large, but here are the first three lines (for the speed-readers out there who tend to miss entire pages at a time):

//#define B 39
//int map[MAPW*MAPH] = {
// B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,

Don't forget to delete the rest of the map array definition that follows these lines.

Next, scroll down to the main function, and add the code that loads the Mappy file as shown in the bold lines that follow.

//main function
int main(void)
{
    int anim;

    //initialize the game
    allegro_init();
    install_keyboard();
    install_timer();
    srand(time(NULL));
    setupscreen();
    setuptanks();
    loadsprites();

    //load the Mappy file
    if (MapLoad("map3.fmp") != 0)
    {
       allegro_message
       return 1;
    }

Next, you'll need to modify the lines that used to draw the scrolling background, replacing those lines with a call to MapDrawBG, which is all you need now to draw the background (note where the blit function call has been commented out). We'll use the same variables as before.

//game loop
while(!gameover)
{
    //move the tanks and bullets
    for (n=0; n<2; n++)
    {
        movetank(n);
        animatetank(n);
        movebullet(n);
    }

//draw background bitmap
blit(back, buffer, 0, 0, 0, 0, back->w, back->h);

//draw scrolling windows (now using Mappy)
for (n=0; n<2; n++)
     //blit(scroll, buffer, scrollx[n], scrolly[n],
     //    startx[n], starty[n], SCROLLW, SCROLLH);
       MapDrawBG(buffer, scrollx[n], scrolly[n],
           startx[n], starty[n], SCROLLW, SCROLLH);

Remove the line of code down near the end of main that destroys the scroll bitmap, which is no longer used.

//destroy bitmaps
destroy_bitmap(explode_bmp);
destroy_bitmap(back);
//destroy_bitmap(scroll);
destroy_bitmap(buffer);

Okay, just one more change to main and you'll be done. Add the following line of code at the bottom of main to free the MappyAL tile map:

     //free the MappyAL memory
     MapFreeMem();

     return 0;
}
END_OF_MAIN()

Summary

This chapter explained how to use the Mappy Allegro library (MappyAL). The latest version of Tank War now incorporates support for native Mappy files via the MappyAL library, giving the game a huge boost in playability. Now anyone can create battlefield maps for Tank War and fight it out with friends. In the next revision we'll learn how to add solid objects to the Mappy level so the tanks will be able to interact with the level to a certain degree.

Chapter Quiz

You can find the answers to this chapter quiz in Appendix A, “Chapter Quiz Answers.”

1.

What is the name of the map editor we've been using in this chapter?

  1. Tile Editor

  2. Map Edit

  3. Mappy

  4. TestMappy

2.

What is the name of the function used to load a level/map file?

  1. MapLoad

  2. LoadLevel

  3. OpenMap

  4. ReadLevel

3.

What is the name of the helper library used in this chapter to load game levels/maps?

  1. LevelLib

  2. MappyAL

  3. SpriteLib

  4. MapFile

4.

What function would you use to draw the foreground layer of a map?

  1. DrawForeground

  2. DrawMapLayer

  3. DrawLevelFG

  4. MapDrawFG

5.

What is the name of the global variable containing the width of a tile map that has been loaded?

  1. tilemapwidth

  2. mapwidth

  3. tileMapSizeX

  4. tilewidth

6.

What function is used to draw the background layer of a map?

  1. DrawBG

  2. MapDrawBG

  3. DrawBackground

  4. DrawMapLayer

7.

What function returns the tile number at a specified x,y position on the map?

  1. gettilenumber

  2. getmaptile

  3. MapGetBlockID

  4. mapGetTile

8.

What is the name of the global variable containing the height value of tiles contained in the map file that has been loaded?

  1. maptilesize

  2. tileheight

  3. mapblockheight

  4. blocksize

9.

What function should you call before ending the program to free the memory used by map data?

  1. MapFreeMem

  2. freemapdata

  3. deleteMap

  4. clearMapData

10.

Which MappyAL library function loads a Mappy file?

  1. MapLoad

  2. LoadMap

  3. MappyLoad

  4. OpenMap

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.212.102