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

cball.cpp

// ------------------------------------------------------------------
// cball.cpp
// ------------------------------------------------------------------
// This class is the ball of the player
// By Kronoman - In loving memory of my father
// Copyright (c) 2003,2004, Kronoman
// ------------------------------------------------------------------

#include "cball.h"
#include "gerror.h"
#include "sphermap.h"
#include "sound.h"

// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------
CBall::CBall()
{
      x = y = z = dx = dy = dz = 0.0;
      spr = spr_shadow = spr_cache = NULL;
      ctmap = NULL;
      particle_manager = NULL;

      lives = BALL_LIVES;
      anglex = angley = 0.0;
      score = 0;

      // DEBUG - we use all available controllers by default
      control.set_use_mouse(true);
      control.set_use_joystick(true);
      control.set_use_keyboard(true);

}

// ------------------------------------------------------------------
// Destructor
// ------------------------------------------------------------------
CBall::~CBall()
{
      // release RAM
      if (spr_cache != NULL)
      {
            destroy_bitmap(spr_cache);
            spr_cache = NULL;
      }
}

// ------------------------------------------------------------------
// Draw the ball, in bmp, displaced by sx,sy pixels
// This uses 3D Allegro polygon routines
// A *scene* must be initialized!!
// ------------------------------------------------------------------
void CBall::draw(BITMAP *bmp, int sx, int sy)
{
      float s = 0; // scale (is added to the size, not multiplied!)
      if (spr == NULL)
            return ; // can't draw, set the sprite first (texture of sphere)
      if (z < BALL_MIN_Z*0.95)
            return ; // we are dead, the ball is gone, don't draw

      // create sprite cache for rendering sphere texture (this is the size of texture, not the radius of the ball)
      if (spr_cache == NULL)
            spr_cache = create_bitmap(spr->w, spr->h);

      if (spr_cache == NULL)
            raise_error("ERROR: CBall:draw() -> can't create sprite texture render cache (spr_cache) (out of memory?)\n");

      // if the ball is jumping or falling, we scale it acording to Z
      if (z < -1.0)
      {
            s = - (z * BALL_RADIUS / BALL_MIN_Z);
      }

      if (z > 1.0)
      {
            s = (z * BALL_RADIUS * 2 / BALL_MAX_Z);
      }


      // render sphere
      MATRIX m;
      clear_to_color(spr_cache, makecol(255, 0, 255)); // magic pink fill

      get_planet_rotation_matrix(&m, ftofix(anglex), ftofix(angley), 0); // rotate texture
      mapped_sphere_ex(spr_cache, spr_cache->w / 2, spr_cache->h / 2, spr_cache->w / 2, spr, &m); // render texture

      // add shadow enviroment map
      set_trans_blender(200, 200, 200, 200); // transparent blender
      draw_trans_sprite(spr_cache, spr_shadow, 0, 0); // blit the shiny shadow


      // do a quad for render of the ball
      V3D_f *v_xyz_p[4]; // this is needed to pass the array pointer to software renderer
      V3D_f pc_v[4]; // the 4 vertex of the quad are precalculated and stored here

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

      pc_v[0].x = x - sx - s;
      pc_v[0].y = y - sy - s;

      pc_v[1].x = x - sx + BALL_RADIUS * 2 + s;
      pc_v[1].y = y - sy - s;

      pc_v[2].x = x - sx + BALL_RADIUS * 2 + s;
      pc_v[2].y = y - sy + BALL_RADIUS * 2 + s;

      pc_v[3].x = x - sx - s;
      pc_v[3].y = y - sy + BALL_RADIUS * 2 + s;

      pc_v[0].z = pc_v[1].z = pc_v[2].z = pc_v[3].z = (z < 0) ? 2 : 0.25;


      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];

      // texture map coordinates
      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;

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

}


// ------------------------------------------------------------------
// Update ball logic, returns true if ball is 'dead'
// Also, does sounds, pick up prizes, etc... :)
// ------------------------------------------------------------------

int CBall::update_logic()
{
      int ret ; // this ret is NOT used as return value of 'return' in this routine.
      bool over_exit_floor = false; // we are over floor that is level's exit?

      if (ctmap == NULL)
            return CBALL_IS_FINE; // sorry, I don't know the map where I move

      if (z < BALL_MIN_Z)
            return CBALL_IS_DEAD; // we are dead (Z too low), we lose, we can't handle the ball, etc... :P // DEBUG : sound ?

      // rotate the thing
      if ((int)dx != 0 || (int)dy != 0 || (int)dz != 0)
      {
            // DEBUG - check this, the rotation is not 100% correct in certain cases
            if ((angley >= 0 && angley <= 64) || (angley >= 192))
                  anglex -= dx;
            else
                  anglex += dx;

            angley -= dy;

            if (anglex < 0)
                  anglex = 255;
            if (anglex > 255)
                  anglex = 0;
            if (angley < 0)
                  angley = 255;
            if (angley > 255)
                  angley = 0;
      }


      // control input
      ret = control.do_input_poll();

      if (ret & KC_UP)
            dy -= BALL_SPEED_Y;
      if (ret & KC_DOWN)
            dy += BALL_SPEED_Y;
      if (ret & KC_LEFT)
            dx -= BALL_SPEED_X;
      if (ret & KC_RIGHT)
            dx += BALL_SPEED_X;

      // apply current tile floor transformations (current floor on center of the ball)
      ret = ctmap->get_tile_type(((int)x + (BALL_RADIUS)) / TMAPS_W, ((int)y + (BALL_RADIUS)) / TMAPS_H, 0);
      if (0 < z && z < 1.0) // only if 0 < Z < 1.0 we are in the floor
      {
            if (ret > 0) // is over a valid tile
            {
                  
                  if (ctmap->tile_set[ret].sound && (!ctmap->tile_set[ret].exit_level || (ctmap->tile_set[ret].exit_level &&  (ctmap->prize_map_indispensable<1)  ) ) )
                        soundw.play_sample(ctmap->tile_set[ret].sound, rand() % 50 + 180, 128, 1000 + rand() % 250 - 125, 0); // SOUND of the tile, DEBUG
                  
                  dx += ctmap->tile_set[ret].adx;
                  dy += ctmap->tile_set[ret].ady;
                  dz += ctmap->tile_set[ret].adz;

                  dx *= ctmap->tile_set[ret].mdx;
                  dy *= ctmap->tile_set[ret].mdy;
                  dz *= ctmap->tile_set[ret].mdz;

                  // if we are over a floor, we are safe
                  if (dz < 0)
                        dz = 0.0; // we aren't falling anymore =D
                  if (z < 1)
                        z = 1.0; // we are over floor =D

                  // this floor is exit?
                  over_exit_floor = ctmap->tile_set[ret].exit_level;
            }
            else
            {
                  // if we are outside map or in a empty floor, we must fall to death... :^O
                  dz -= BALL_SPEED_Z; // negative dz, means fall!
            }
      }
      else
      {
            z -= N_BALL_SPEED_Z; // apply gravity
      }

      // comprobations of max values

      if (dy < -BALL_MSPEED)
            dy = -BALL_MSPEED;

      if (dy > BALL_MSPEED)
            dy = BALL_MSPEED;

      if (dx < -BALL_MSPEED)
            dx = -BALL_MSPEED;

      if (dx > BALL_MSPEED)
            dx = BALL_MSPEED;

      if (dz < -BALL_MSPEED)
            dz = -BALL_MSPEED;

      if (dz > BALL_MSPEED)
            dz = BALL_MSPEED;

      // friction
      if (dy > 0)
            dy -= N_BALL_SPEED_Y;

      if (dy < 0)
            dy += N_BALL_SPEED_Y;

      if (dx > 0)
            dx -= N_BALL_SPEED_X;

      if (dx < 0)
            dx += N_BALL_SPEED_X;

      // 'dead zone' of the ball
      if (dy < N_BALL_SPEED_Y*1.1 && dy > 0)
            dy = 0.0;

      if (dy > -N_BALL_SPEED_Y*1.1 && dy < 0)
            dy = 0.0;

      if (dx < N_BALL_SPEED_X*1.1 && dx > 0)
            dx = 0.0;

      if (dx > -N_BALL_SPEED_X*1.1 && dx < 0)
            dx = 0.0;

      // gravity (if dz > 1.0, the ball is jumping)
      if (dz > 0)
            dz -= N_BALL_SPEED_Z;

      // Collision with walls -- DEBUG - DEBUG -- THIS NEEDS HEAVY CHECK!!!
      // predict where the ball goes (for check collisions)
      float xp, yp;
      xp = x + dx;
      yp = y + dy;

      /*
      Atento:
      formula para sacar el rebote

      r = u - 2*n*(n dot u)

      donde 
      r = vector que quiero
      u = vector direccion bola
      n = normal de la superficie (pared)

      dot = producto escalar
      */


      if (z > 0) // only if z > 0 we are over the floor; we only collide with walls if we are over the floor. duh!
      {
            float n_dot_u = 0, yn = 0, xn = 0; // n dot u , n(x,y)
            float part_x = 0, part_y = 0; // particle spark position

            for (int i = 0; i < 8; i ++) // check 8 points of ball's sprite
            {
                  // NOTE: the size of the bounding box is smaller than the ball radius

                  // BGAP: size to shrink the bounding box
#define BGAP 6
                  switch (i)
                  {
                        case 0:  // up middle
                        part_x = xp + BALL_RADIUS;
                        part_y = yp + BGAP;

                        xn = 0;
                        yn = 1;
                        break;

                        case 1:  // down middle
                        part_x = xp + BALL_RADIUS;
                        part_y = yp + BALL_RADIUS * 2 - BGAP;
                        xn = 0;
                        yn = -1;
                        break;

                        case 2:  // left middle
                        part_x = xp + BGAP;
                        part_y = yp + BALL_RADIUS;
                        xn = 1;
                        yn = 0;
                        break;

                        case 3:  // right middle
                        part_x = xp + BALL_RADIUS * 2 - BGAP;
                        part_y = yp + BALL_RADIUS;
                        xn = -1;
                        yn = 0;
                        break;

#define normalized_v 0.707106781
                        // this are the four cornes
                        // the weird numbers on normal vectors are because they are normalized!
                        case 4:  // up left
                        part_x = xp + BGAP;
                        part_y = yp + BGAP;
                        xn = normalized_v;
                        yn = normalized_v;
                        break;

                        case 5:  // down left
                        part_x = xp + BGAP;
                        part_y = yp + BALL_RADIUS * 2 - BGAP;
                        xn = normalized_v;
                        yn = -normalized_v;
                        break;

                        case 6:  // up right
                        part_x = xp + BALL_RADIUS * 2 - BGAP;
                        part_y = yp + BGAP;
                        xn = -normalized_v;
                        yn = normalized_v;
                        break;

                        case 7:  // down right
                        part_x = xp + BALL_RADIUS * 2 - BGAP;
                        part_y = yp + BALL_RADIUS * 2 - BGAP;
                        xn = -normalized_v;
                        yn = -normalized_v;
                        break;
#undef normalized_v

                  }
#undef BGAP
                  ret = ctmap->get_tile_type((int)part_x / TMAPS_W, (int)part_y / TMAPS_H, 0); // up

                  if (ret > 0)
                  {
                        if (ctmap->tile_set[ret].solid)
                        {
                              // DEBUG -- hack way to solve bug of corners!!
                              if (i < 4) // hack with corners - it WORKS, and that is what counts :P
                              {
                                    // bounce!
                                    n_dot_u = dx * xn + dy * yn;
                                    // this is the real bounce, proper done with vectors (although most of the time it don't work :P)
                                    dx = dx - 2 * xn * n_dot_u;
                                    dy = dy - 2 * yn * n_dot_u;
                              }
                              else
                              {
                                    dx *= -1; // hacky bounce for conners... I know, is wrong... I dont care...
                                    dy *= -1;
                              } // end of hack

                              // bounce factor of wall
                              dx *= ctmap->tile_set[ret].bounce_factor;
                              dy *= ctmap->tile_set[ret].bounce_factor;


                              // DEBUG: the particle spark NEEDS to be accurate
                              for (int i = 0; i < rand() % 10 + 5; i++) // particle spark
                                    particle_manager->add_particle(new CBaseParticle(part_x + (rand() % 6) - 3, part_y + (rand() % 6) - 3, (float)(rand() % 300) / 100.0*(dx < 0 ? -1 : 1), (float)(rand() % 300) / 100.0*(dy < 0 ? -1 : 1), makecol(255, rand() % 55 + 200, 0), rand() % 15 + 5));

                              // bounce sound
                              if (ctmap->tile_set[ret].sound)
                                    soundw.play_sample(ctmap->tile_set[ret].sound, rand() % 56 + 200, 128 + rand() % 64 - 32, 1000 + rand() % 300 - 150, 0);

                              break; // end for (if not, the code will fail, will bounce many times, most of the time just rendering void the bounce)
                        }
                  }
            }
      }

      // move the ball


      x += dx;
      y += dy;
      z += dz;

      if (x < 0)
            x = 0;

      if (y < 0)
            y = 0;

      if (x > TMAP_SIZE * TMAPS_W)
            x = TMAP_SIZE * TMAPS_W;

      if (y > TMAP_SIZE * TMAPS_H)
            y = TMAP_SIZE * TMAPS_H;

      if (z > BALL_MAX_Z) // top jump, hit the 'roof'
      {
            z = BALL_MAX_Z;

            if (dz > 0)
                  dz = dz * 0.8; // decrease jump by 20 % also
      }

      // finally, check the 'prize' layer (only if we are over the floor)

      ret = ctmap->get_tile_type(((int)x + (BALL_RADIUS)) / TMAPS_W, ((int)y + (BALL_RADIUS)) / TMAPS_H, 1);

      if ((ctmap->tile_set[ret].indispensable || ctmap->tile_set[ret].is_a_prize) && z >= 0 && ret > 0)
      {

            score += ctmap->tile_set[ret].score;

            if (ctmap->tile_set[ret].indispensable)
                  ctmap->prize_map_indispensable--;

            ctmap->set_tile_type(((int)x + (BALL_RADIUS)) / TMAPS_W, ((int)y + (BALL_RADIUS)) / TMAPS_H, 1, 0); // clean the tile

            // SOUND
            if (ctmap->tile_set[ret].sound)
                  soundw.play_sample(ctmap->tile_set[ret].sound, rand() % 26 + 230, 128 + rand() % 64 - 32, 1000 + rand() % 100 - 50, 0);

            // add score message
            if (particle_manager != NULL)
            {
                  char score_txt[256];
                  usprintf(score_txt, "%c%d", (ctmap->tile_set[ret].score > 0 ? '+' : '-'), ctmap->tile_set[ret].score);

                  particle_manager->add_particle(new CExplosiveTextParticle(x + BALL_RADIUS, y, 0.0, (float)(rand() % 100 + 25) / -100.0, makecol(255, rand() % 128 + 128, 0), rand() % 30 + 30, string(score_txt) ) );

                  for (int i = 0; i < rand() % 30 + 30; i++)
                        particle_manager->add_particle(new CSparkParticle(x + BALL_RADIUS, y + BALL_RADIUS, (float)((rand() % 800) - 400) / 100.0, (float)((rand() % 800) - 400) / 100.0, makecol(rand() % 128 + 128, 255, 0), rand() % 15 + 15, 3));
            }
      }


      // if I'm not dead, and I'm over level exit and I'm still, exit level
      if (over_exit_floor)
            return CBALL_EXIT_LEVEL;

      return CBALL_IS_FINE;   // not dead yet ;)
}

Generated by  Doxygen 1.6.0   Back to index