Logo Search packages:      
Sourcecode: kball version File versions  Download package

tmap.cpp

// ------------------------------------------------------------------
// tmap.cpp
// ------------------------------------------------------------------
// This handles the tile map for the ball game
// ------------------------------------------------------------------
// By Kronoman - In loving memory of my father
// Copyright (c) 2003, Kronoman
// ------------------------------------------------------------------
// ** VERY IMPORTANT NOTICE **
//
// SINCE THE WALLS ARE 3D RENDERED, ALL DRAW ROUTINES *NEED*
// A 3D Allegro's create_scene(int nedge, int npoly); BEFORE CALLING THEM!
// ------------------------------------------------------------------
#include "tmap.h"

// NOTE: instead of printf, strcmp, etc, I use the Allegro's UNICODE equivalents

// ============================================================================
// ============================================================================
// CTileClass class
// ============================================================================
// ============================================================================

// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------
CTileClass::CTileClass()
{
      adx = ady = adz = 0.0;
      mdx = mdy = mdz = 1.0;
      bounce_factor = 0.9;
      exit_level = false;
      solid = false;
      spr = NULL;
      sound = NULL;

      // relative to prize layer
      score = 0;
      indispensable = false;
      is_a_prize = false;

      usprintf(spr_name,"NULL");
      usprintf(sound_name,"NULL");
}

// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------
CTileClass::~CTileClass()
{
      // nothing to be done here yet
}

// ----------------------------------------------------------------------------
// This saves parameters to a previously set config *FILE* (not memory pointer)
// nt is the tile number ID
// ----------------------------------------------------------------------------

void CTileClass::save_to_config(int nt)
{
      char str1[2048];

      usprintf(str1,"TILE_%d", nt); // section to read

      set_config_float(str1, "bounce_factor", bounce_factor);

      set_config_float(str1, "adx", adx);
      set_config_float(str1, "ady", ady);
      set_config_float(str1, "adz", adz);

      set_config_float(str1, "mdx", mdx);
      set_config_float(str1, "mdy", mdy);
      set_config_float(str1, "mdz", mdz);

      set_config_int(str1, "exit_level", (int)exit_level);
      set_config_int(str1, "solid", (int)solid);

      set_config_string(str1, "spr", spr_name);
      set_config_string(str1, "sound", sound_name);

      set_config_int(str1, "score", score);
      set_config_int(str1, "is_a_prize", is_a_prize);
      set_config_int(str1, "indispensable", (int)indispensable);

}

// ----------------------------------------------------------------------------
// This loads the tile 'nt' (1..255) from a *previously* set config
// is mean to be called ONLY by CTMap::load_tile_set_from_file()
// ----------------------------------------------------------------------------
void CTileClass::load_from_config(int nt, CWDatafile *data)
{
      char str1[2048];

      usprintf(str1,"TILE_%d", nt); // section to read

      // parameters
      bounce_factor = get_config_float(str1, "bounce_factor", 0.9);
      adx = get_config_float(str1, "adx", 0.0);
      ady = get_config_float(str1, "ady", 0.0);
      adz = get_config_float(str1, "adz", 0.0);

      mdx = get_config_float(str1, "mdx", 1.0);
      mdy = get_config_float(str1, "mdy", 1.0);
      mdz = get_config_float(str1, "mdz", 1.0);

      exit_level = (bool)get_config_int(str1, "exit_level", 0);
      solid = (bool)get_config_int(str1, "solid", 0);

      indispensable = (bool)get_config_int(str1,"indispensable",0);
      score = get_config_int(str1, "score", 0);
      is_a_prize = (bool)get_config_int(str1, "is_a_prize", 0);

      if (indispensable)
            is_a_prize = true; // if is indispensable, it must has the prize flag set

      // sprite
      spr = NULL; // default value for sprite

      usprintf(spr_name, "%s", get_config_string(str1, "spr", "null")); // I'm interested in keep the sprite name for future saving of tile set to disk

      // if spr_name != "null", then the sprite is defined, so fint it or die trying :P
      if (ustricmp(spr_name, "null") != 0)
      {
            spr = (BITMAP *)data->get_resource_dat(spr_name);
      }

      // sound
      sound = NULL;

      usprintf(sound_name, "%s", get_config_string(str1, "sound", "null")); // I'm interested in keep the sound name for future saving of tile set to disk

      if (ustricmp(sound_name, "null") != 0)
      {
            sound = (SAMPLE *)data->get_resource_dat(sound_name);
      }

      // done
}

// ----------------------------------------------------------------------------
// Draws the tile on bmp at x,y (pixel coordinates)
// draw_grid is of use for the map editor, will signal bad sprites
// layer is used to determine highness of the thing.
// * Note: prizes aren't scaled *
// ----------------------------------------------------------------------------

void CTileClass::draw(BITMAP *bmp, int x, int y, bool draw_grid, int layer)
{
      V3D_f *v_xyz_p[4]; // this is needed to pass the array pointer to software renderer
      V3D_f pc_v[8]; // the 8 vertex of the quad are precalculated and stored here

      int poltype = POLYTYPE_ATEX_MASK; // type of polygon to render, usually POLYTYPE_ATEX_MASK

      float dx, dy; // displacement to give '3D look'
      float scale_top = 5; // how much to add to the size of top block (pixels) (this is the height of the walls also)

      if (is_a_prize)
            scale_top = 0; // is a prize? don't scale


      if (spr == NULL)
      {
            if (draw_grid)
            {
                  line(bmp,x,y,x+TMAPS_W, y+TMAPS_H, makecol(255,0,0));
                  line(bmp,x+TMAPS_W, y, x, y+TMAPS_H, makecol(255,0,0));
            }
            return;
      }

      // maximun displacement of top of wall's box
#define max_displacement 32.0
      dx = ((x+spr->w/2) - (bmp->w/2)) * (max_displacement) / (float)(bmp->w/2.0);
      dy = ((y+spr->h/2) - (bmp->h/2)) * (max_displacement) / (float)(bmp->h/2.0);

#undef max_displacement


      // 3D rendered (by software render, no OpenGL)

      // precalculate the 8 vertex, 4 first are 'on ground'
      // REMEMBER to set 'u' and 'v' parameters for texture for each face!!!
      // 0-->1
      // 3<--2
      //
      // 4 last are 'on roof'
      // 4-->5
      // 7<--6
      pc_v[0].x=x;
      pc_v[0].y=y;

      pc_v[1].x=x+spr->w;
      pc_v[1].y=y;

      pc_v[2].x=x+spr->w;
      pc_v[2].y=y+spr->h;

      pc_v[3].x=x;
      pc_v[3].y=y+spr->h;

      pc_v[0].z=pc_v[1].z=pc_v[2].z=pc_v[3].z=1;

      pc_v[4].x=x + dx - scale_top;
      pc_v[4].y=y + dy - scale_top;

      pc_v[5].x=x + dx + spr->w + scale_top;
      pc_v[5].y=y + dy - scale_top;

      pc_v[6].x=x + spr->w + dx + scale_top;
      pc_v[6].y=y + spr->h + dy + scale_top;

      pc_v[7].x=x + dx - scale_top ;
      pc_v[7].y=y + dy + spr->h + scale_top;

      pc_v[4].z=pc_v[5].z=pc_v[6].z=pc_v[7].z=0.1;

      // is a floor, or wall?
      if (solid)
      {

            // wall box

            // top face (roof)
            v_xyz_p[0] = &pc_v[4];
            v_xyz_p[1] = &pc_v[5];
            v_xyz_p[2] = &pc_v[6];
            v_xyz_p[3] = &pc_v[7];

            pc_v[4].u = 0;
            pc_v[4].v = 0;
            pc_v[5].u = spr->w;
            pc_v[5].v = 0;
            pc_v[6].u = spr->w;
            pc_v[6].v = spr->h;
            pc_v[7].u = 0;
            pc_v[7].v = spr->h;

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p); // top polygon

            // upper face
            v_xyz_p[0] = &pc_v[4];
            v_xyz_p[1] = &pc_v[5];
            v_xyz_p[2] = &pc_v[1];
            v_xyz_p[3] = &pc_v[0];

            pc_v[4].u = 0;
            pc_v[4].v = 0;
            pc_v[5].u = spr->w;
            pc_v[5].v = 0;
            pc_v[1].u = spr->w;
            pc_v[1].v = spr->h;
            pc_v[0].u = 0;
            pc_v[0].v = spr->h;

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p);

            // bottom face
            v_xyz_p[0] = &pc_v[3];
            v_xyz_p[1] = &pc_v[2];
            v_xyz_p[2] = &pc_v[6];
            v_xyz_p[3] = &pc_v[7];

            pc_v[3].u = 0;
            pc_v[3].v = 0;
            pc_v[2].u = spr->w;
            pc_v[2].v = 0;
            pc_v[6].u = spr->w;
            pc_v[6].v = spr->h;
            pc_v[7].u = 0;
            pc_v[7].v = spr->h;

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p);

            // left face
            v_xyz_p[0] = &pc_v[0];
            v_xyz_p[1] = &pc_v[4];
            v_xyz_p[2] = &pc_v[7];
            v_xyz_p[3] = &pc_v[3];

            pc_v[0].u = 0;
            pc_v[0].v = 0;
            pc_v[4].u = spr->w;
            pc_v[4].v = 0;
            pc_v[7].u = spr->w;
            pc_v[7].v = spr->h;
            pc_v[3].u = 0;
            pc_v[3].v = spr->h;

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p);

            // right face
            v_xyz_p[0] = &pc_v[1];
            v_xyz_p[1] = &pc_v[5];
            v_xyz_p[2] = &pc_v[6];
            v_xyz_p[3] = &pc_v[2];

            pc_v[1].u = 0;
            pc_v[1].v = 0;
            pc_v[5].u = spr->w;
            pc_v[5].v = 0;
            pc_v[6].u = spr->w;
            pc_v[6].v = spr->h;
            pc_v[2].u = 0;
            pc_v[2].v = spr->h;

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p);


      }
      else
      {

            // flat surface
            if (layer == 0 || is_a_prize)
            {
                  v_xyz_p[0] = &pc_v[0];
                  v_xyz_p[1] = &pc_v[1];
                  v_xyz_p[2] = &pc_v[2];
                  v_xyz_p[3] = &pc_v[3];

                  pc_v[0].u = 0;
                  pc_v[0].v = 0;
                  pc_v[1].u = spr->w;
                  pc_v[1].v = 0;
                  pc_v[2].u = spr->w;
                  pc_v[2].v = spr->h;
                  pc_v[3].u = 0;
                  pc_v[3].v = spr->h;

                  pc_v[0].z=pc_v[1].z=pc_v[2].z=pc_v[3].z= (is_a_prize) ? 0.15 : 1;
            }
            else
            {
                  v_xyz_p[0] = &pc_v[4];
                  v_xyz_p[1] = &pc_v[5];
                  v_xyz_p[2] = &pc_v[6];
                  v_xyz_p[3] = &pc_v[7];

                  pc_v[4].u = 0;
                  pc_v[4].v = 0;
                  pc_v[5].u = spr->w;
                  pc_v[5].v = 0;
                  pc_v[6].u = spr->w;
                  pc_v[6].v = spr->h;
                  pc_v[7].u = 0;
                  pc_v[7].v = spr->h;

                  pc_v[4].z=pc_v[5].z=pc_v[6].z=pc_v[7].z=0.1;
            }

            scene_polygon3d_f(poltype, spr, 4, v_xyz_p); // render polygon to scene
      }
}

// ============================================================================
// ============================================================================
// CTMap class
// ============================================================================
// ============================================================================

// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------

CTMap::CTMap()
{
      empty_the_map(); // reset the map

      timer_tick_rate = 30; // set this to measure timer tick rate, otherwise, the time will run wild (ex: if update logic is called 30 times by second, then set this to 30)
}

// ----------------------------------------------------------------------------
// Destructor - NUNCA DEBE SER LLAMADO AUTOMATICAMENTE POR C++
// PORQUE GENERA SEG FAULT, YA QUE ALLEGRO MUERE *ANTES* QUE ESTO
// Y LA LIBERACION DE MEMORIA *NO* ANDA
// ----------------------------------------------------------------------------

CTMap::~CTMap()
{
      free_memory();
}

// ----------------------------------------------------------------------------
// This free the memory used by the map and the tile set
// ----------------------------------------------------------------------------
void CTMap::free_memory()
{
      tile_set_data.nuke_datafile();
}

// ----------------------------------------------------------------------------
// Resets all map to 0s
// ----------------------------------------------------------------------------
void CTMap::empty_the_map()
{
      for (int l =0; l < MAP_LAYERS; l++)
            for (int x = 0; x < TMAP_SIZE; x++)
                  for (int y = 0; y < TMAP_SIZE; y++)
                        tile_map[x][y][l] = 0; // empty the map

      // reset start position too (centered)
      sxp = TMAP_SIZE / 2;
      syp = TMAP_SIZE / 2;

      // reset other stuff
      prize_map_indispensable = 0;
      time_m = 60;
      time_s = 0;
      background_index = 0;
      music_index = 0;

      curr_tick = -1; // special flag, means 'start recounting'
}

// ----------------------------------------------------------------------------
// This returns if a tile is walkable only
// Is useful to see if the ball can move or not in some particular direction
// Is not useful for check if the ball must fall (for that purpose,
// use get_tile_type and check against < 1 (empty))
//
// This serves for validation purposes (for the ball), will return true
// if the tile is walkable, false otherwise (outside map counts as non walkable)
// ----------------------------------------------------------------------------

bool CTMap::get_tile_walkable(int x, int y, int layer)
{
      if (x < 0 || y < 0 || x > TMAP_SIZE-1 || y > TMAP_SIZE-1 || layer < 0 || layer > MAP_LAYERS-1)
            return false; // outside map!

      if (tile_set[tile_map[x][y][layer]].solid)
            return false; // the place is solid

      // if the tile is empty, or solid = false; then he can walk
      return true;
}

// ----------------------------------------------------------------------------
// This returns the tile type (1..255), or -1 if outside map, or 0 if empty
// if empty, or outside map, ball should fall free to death
// ----------------------------------------------------------------------------
int CTMap::get_tile_type(int x, int y, int layer)
{
      if (x < 0 || y < 0 || x > TMAP_SIZE-1 || y > TMAP_SIZE-1 || layer < 0 || layer > MAP_LAYERS-1)
            return -1; // outside map!

      return tile_map[x][y][layer];
}

// ----------------------------------------------------------------------------
// this sets the tile type at x,y (0..255) x,y are coordinates of the matrix
// ----------------------------------------------------------------------------
void CTMap::set_tile_type(int x, int y, int layer, int value)
{
      if (x < 0 || y < 0 || x > TMAP_SIZE-1 || y > TMAP_SIZE-1 || layer < 0 || layer > MAP_LAYERS-1)
            return; // outside map!
      tile_map[x][y][layer] = value;
}

// ----------------------------------------------------------------------------
// Loads a tile map from a file, return true if FAILS, false otherwise
// NOTICE: FOR THIS TO COUNT PROPERLY THE PRIZE, THE TILE SET SHOULD BE *ALREADY* LOADED
// NOTICE: the header of the file is '6''6''6''M''A''P' (6 chars = "666MAP"
// ----------------------------------------------------------------------------
bool CTMap::load_map_from_file(const char *filename)
{
      prize_map_indispensable = 0; //reset prize count

      // All file I/O in maps is done using Allegro's packfiles, that way I use compression :D
      PACKFILE *fp = NULL;

      fp = pack_fopen(filename, F_READ);

      if (fp == NULL)
            return true; // error d00d! :(

      // check header
      if (pack_getc(fp) != '6')
            return true; // error
      if (pack_getc(fp) != '6')
            return true; // error
      if (pack_getc(fp) != '6')
            return true; // error
      if (pack_getc(fp) != 'M')
            return true; // error
      if (pack_getc(fp) != 'A')
            return true; // error
      if (pack_getc(fp) != 'P')
            return true; // error

      // start reading data

      // start pos of the player
      sxp = pack_getc(fp);
      syp = pack_getc(fp);

      // time of the map
      time_m = pack_getc(fp);
      time_s = pack_getc(fp);

      // now, get the rest of the map
      for (int l = 0; l < MAP_LAYERS; l ++)
      {
            for (int x = 0; x < TMAP_SIZE; x++)
            {
                  for (int y = 0; y < TMAP_SIZE; y++)
                  {
                        // if we reach eof before ending the loop, a error occurs, I need all the map !
                        if (pack_feof(fp))
                        {
                              pack_fclose(fp);
                              return true; // error!
                        }
                        tile_map[x][y][l] = pack_getc(fp);

                        // check if it is a indispensable prize, count it ;
                        // DEBUG: this only counts the indispensable intems if the layer == 1,
                        // otherwise they are on the floor and can't be pick up
                        if (tile_set[tile_map[x][y][l]].indispensable && l == 1)
                        {
                              prize_map_indispensable++;
                        }
                  }
            }
      }

      // get background ID
      background_index = pack_getc(fp);
      
      // get music index ID
      music_index = pack_getc(fp);

      pack_fclose(fp); // ready

      return false; // all OK
}

// ----------------------------------------------------------------------------
// Save tile map to filename, return true if FAILS, false otherwise
// NOTICE: the header of the file is '6''6''6''M''A''P' (6 chars = "666MAP"
// ----------------------------------------------------------------------------

bool CTMap::save_map_to_file(const char *filename)
{
      PACKFILE *fp = NULL;

      fp = pack_fopen(filename, F_WRITE); // DEBUG - deberia ser PACKED, pero PIERDE 2 bytes (BUG de Allegro?)

      if (fp == NULL)
            return true; // error d00d! :(

      // Put file's header '666MAP'
      pack_putc('6', fp);
      pack_putc('6', fp);
      pack_putc('6', fp);
      pack_putc('M', fp);
      pack_putc('A', fp);
      pack_putc('P', fp);


      // put 2 bytes, start pos of the player
      pack_putc((char)sxp, fp);
      pack_putc((char)syp, fp);

      // time of the map
      pack_putc((char)time_m, fp);
      pack_putc((char)time_s, fp);

      // now, set the rest of the map
      for (int l = 0; l < MAP_LAYERS; l ++)
            for (int x = 0; x < TMAP_SIZE; x++)
                  for (int y = 0; y < TMAP_SIZE; y++)
                        pack_putc((char)tile_map[x][y][l], fp);

      // set background ID
      pack_putc((char)background_index, fp);
      
      // set music ID
      pack_putc((char)music_index, fp);

      // DEBUG:: pad with extra bytes, this was used to fix a _PACK bug, but is no more needed, anyways let it here
      for (int pad=0; pad<916; pad++)
            pack_putc(rand()%128+64, fp);

      pack_fclose(fp); // ready
      return false;
}

// ----------------------------------------------------------------------------
// loads a tile set from a DATAFILE (filename is the name of the datafile),
// return true if FAILS, false otherwise
// ----------------------------------------------------------------------------
bool CTMap::load_tile_set_from_file(char *filename)
{
      this->free_memory(); // free used memory first

      if (tile_set_data.load_datafile(filename))
            return true; // failed :P

      // now, cache all tile set sprites and all the stuff

      // first, I need the tile set configuration, text object named "TILE_SET_CFG_TXT"
      if (tile_set_data.get_resource_dat("TILE_SET_CFG_TXT") == NULL)
            return true; // error, can't load tile sets without the configuration thing :(

      push_config_state(); // to later restore config settings
      // set the config to the data
      set_config_data((char *)tile_set_data.get_resource_dat("TILE_SET_CFG_TXT"), tile_set_data.get_resource("TILE_SET_CFG_TXT")->size);

      // get all the tile set
      for (int i = 1; i < 256; i++)
            tile_set[i].load_from_config(i, &tile_set_data);

      pop_config_state(); // restore config settings

      return false; // done
}

// ----------------------------------------------------------------------------
// saves the tile set configuration to a text file (no saves the bitmaps!)
// return true if FAILS, false otherwise
// ----------------------------------------------------------------------------

bool CTMap::save_tile_set_config_to_file(char *filename)
{
      push_config_state();

      // set file
      set_config_file(filename);

      // save all data
      for (int i = 1; i < 256; i++)
            tile_set[i].save_to_config(i);

      pop_config_state();
      return false; // done OK
}


// ----------------------------------------------------------------------------
// Draws a zone of a layer of the map, coordinates are in pixels
//
// ix, iy = top left coordinate of map area to draw
// iw, ih = width, height of map area to draw
// draw_grid = draws the grid on top, only useful for the mapeditor
//
// NOTE: this has 3D software code embedded from Allegro, it *needs*
// a create_scene(), clear_scene() call somewhere before using this, and a
// ----------------------------------------------------------------------------

void CTMap::draw_map(BITMAP *bmp, int ix, int iy, int iw, int ih, int layer, bool draw_grid)
{
      // I want the integer remainder, so I can scroll
      // this returns the remainder of a/b
#define remainder(a,b)  (a-((a/b)*b))

      int ret,xof,yof;

      if (layer < 0 || layer > MAP_LAYERS-1)
            return;

      xof = remainder(ix, TMAPS_W);
      yof = remainder(iy, TMAPS_H);

      for (int y = iy; y < iy + ih + TMAPS_H; y += TMAPS_H)
      {
            for (int x = ix; x < ix + iw + TMAPS_W; x += TMAPS_W)
            {
                  ret = get_tile_type( (x / TMAPS_W), (y / TMAPS_H), layer );
                  if (ret > 0)
                        tile_set[ret].draw(bmp, x-ix-xof, y-iy-yof, draw_grid,layer);

                  if (x > TMAP_SIZE*TMAPS_W)
                        break; // outside bounds, no need to do it

                  // draw the 'start' of the player, and the grid (only useful for the map editor)
                  if (draw_grid)
                  {
                        rect(bmp, x-ix-xof, y-iy-yof, x-ix-xof+TMAPS_W,y-iy-yof+TMAPS_H, makecol(128,128,128));
                        if (x / TMAPS_W == sxp && y / TMAPS_H == syp)
                        {
                              circle(bmp, x-ix-xof+TMAPS_W/2, y-iy-yof+TMAPS_H/2, TMAPS_W/2, makecol(0,0,255));
                              circle(bmp, x-ix-xof+TMAPS_W/2, y-iy-yof+TMAPS_H/2, TMAPS_W/3, makecol(255,0,0));
                              circle(bmp, x-ix-xof+TMAPS_W/2, y-iy-yof+TMAPS_H/2, TMAPS_W/4, makecol(0,255,0));
                        }
                  }
            }
            if (y > TMAP_SIZE*TMAPS_H)
                  break; // outside bounds, no need to do it
      }


}



// ----------------------------------------------------------------------------
// updates the logic of the map (animations, time remaining, etc)
// ----------------------------------------------------------------------------

void CTMap::update_logic()
{
      curr_tick--;
      if (curr_tick < 0)
      {
            curr_tick = timer_tick_rate;

            time_s--;
            if (time_s < 0)
            {
                  time_m--;
                  if (time_m < 0)
                  {
                        time_m = 0; // your time is OVER d00d! HA HA HA HA die bastard die!
                        time_s = 0;
                  }
                  else
                  {
                        time_s = 59;
                  }
            }

      }
}



Generated by  Doxygen 1.6.0   Back to index