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

game.c

/***************************************************************************
                          game.c  -  description
                             -------------------
    begin                : 03/03/19
    copyright            : (C) 2003 by Michael Speck
    email                : kulkanie@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

/***** INCLUDES ************************************************************/

#include "../client/lbreakout.h"
#include "game.h"
#include "bricks.h"
#include "paddle.h"
#include "balls.h"
#include "shots.h"
#include "extras.h"

/***** EXTERNAL VARIABLES **************************************************/

extern int ball_w, ball_dia;

/***** EXPORTS *************************************************************/

Game *cur_game = 0;

/***** FORWARDED DECLARATIONS **********************************************/

/***** LOCAL TYPE DEFINITIONS **********************************************/

/***** LOCAL VARIABLES *****************************************************/

static GameDiff diffs[DIFF_COUNT] = {
    { 9, 12,  8, 12, 20,  5, 0.10, 0.0016, 0.20, 32000, 0 },
    { 6,  9,  0,  2,  8,  8, 0.24, 0.0016, 0.40, 1800,  1 },
    { 5,  7,  0,  1,  6, 10, 0.27, 0.0016, 0.43, 1800,  1 },
    { 4,  5,  0,  1,  4, 13, 0.30, 0.0015, 0.45, 1800,  1 }
};

/* in network game the ball is slower and the paddle is bigger */
static GameDiff net_diffs[DIFF_COUNT] = {
    { 6, 9, 1, 2, 8, 8,  0.18, 0.0012, 0.30, 1800, 1 },
    { 5, 7, 1, 2, 6, 10, 0.21, 0.0012, 0.33, 1800, 1 },
    { 4, 5, 1, 2, 4, 13, 0.24, 0.0011, 0.35, 1800, 1 }
};

/***** LOCAL FUNCTIONS *****************************************************/

/***** PUBLIC FUNCTIONS ****************************************************/

/* create/delete game context */
Game *game_create( int game_type, int diff, int rel_warp_limit )
{
      Game *game = salloc( 1, sizeof( Game ) );

      /* set diff and game type */
      game->game_type = game_type;
      if ( game_type == GT_LOCAL )
            game->diff = &diffs[diff];
      else
            game->diff = &net_diffs[diff];
      game->rel_warp_limit = rel_warp_limit;
      
      /* create lists */
      game->shots = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
      game->exp_bricks = list_create( LIST_NO_AUTO_DELETE, LIST_NO_CALLBACK );
      game->heal_bricks = list_create( LIST_NO_AUTO_DELETE, LIST_NO_CALLBACK );
      game->extras = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
      game->balls = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );

      /* set ball speed */
      game->ball_v_min = game->diff->v_start;
      game->ball_v_max = game->diff->v_max;
      delay_set( &game->speedup_delay, game->diff->v_delay );
      
      /* create paddles */
      game->paddle_count = (game_type==GT_NETWORK)?2:1;
      /* bottom */
        game->paddles[PADDLE_BOTTOM] = paddle_create( 0, PADDLE_BOTTOM, 
            (MAP_HEIGHT-2)*BRICK_HEIGHT, 
            game->diff->paddle_size, 
            game->diff->paddle_min_size, game->diff->paddle_max_size,
            0 );
      /* top */
      if ( game_type == GT_NETWORK )
            game->paddles[PADDLE_TOP] = paddle_create( 0, PADDLE_TOP,
                  BRICK_HEIGHT+2, 
                  game->diff->paddle_size, 
                  game->diff->paddle_min_size, game->diff->paddle_max_size,
                  0 );
      
      return game;
}
void game_delete( Game **_game )
{
      Game *game = *_game;
      int i;
      
      if ( game == 0 ) return;
      
      /* delete paddles */
      for ( i = 0; i < game->paddle_count; i++ )
            paddle_delete( game->paddles[i] );
      
      /* delete lists */
      if ( game->shots ) list_delete( game->shots );
      if ( game->exp_bricks ) list_delete( game->exp_bricks );
      if ( game->heal_bricks ) list_delete( game->heal_bricks );
      if ( game->extras ) list_delete( game->extras );
      if ( game->balls ) list_delete( game->balls );

      free( game );
      *_game = 0;
}

/* finalize single game level. the level_type is determined by
 * counting the bricks. the data of 'level' is copied and modified
 * while playing. */
void game_init( Game *game, Level *level )
{
      int i;
      Ball *ball;

      game->level_over = 0;
      
      /* initiate level data */
      snprintf( game->title, 32, level->name );
      snprintf( game->author, 32, level->author );
      bricks_init( game, game->game_type, level, game->diff->score_mod, game->rel_warp_limit );
      if ( game->game_type == GT_LOCAL || game->brick_count > 0 )
            game->level_type = LT_NORMAL;
      else
            game->level_type = LT_PINGPONG;
      memset( game->extra_active, 0, sizeof( game->extra_active ) );
      memset( game->extra_time, 0, sizeof( game->extra_time ) );

      /* set ball speed */
      game->ball_v = game->ball_v_min;
      game->speedup_level = 0;
      
        /* clear maxballspeed_request */
        if ( game->game_type == GT_LOCAL )
        {
            cur_game->paddles[0]->maxballspeed_request = 0;
            cur_game->paddles[0]->maxballspeed_request_old = 0;
        }
        
      /* attach one ball to each paddle */
    list_clear( game->balls );
      for ( i = 0; i < game->paddle_count; i++ ) {
            if ( game->paddles[i]->type == PADDLE_BOTTOM )
                  ball = ball_create((game->paddles[i]->w - ball_w) / 2, -ball_dia );
            else
                  ball = ball_create((game->paddles[i]->w - ball_w) / 2, game->paddles[i]->h );
            ball->attached = 1;
            ball->paddle = game->paddles[i];
            ball->paddle->attached_ball_count = 1;
            ball_set_random_angle( ball, game->ball_v );
            list_add( game->balls, ball );
      }
}

/* reset level/in_game data */
void game_finalize( Game *game )
{
      int i;
      
      /* reset lists */
      list_clear( game->balls );
      list_clear( game->extras );
      list_clear( game->shots );
      list_clear( game->heal_bricks );
      list_clear( game->exp_bricks );

      /* reset paddles (and their statistics which are only for 
       * the currently played level) */
      for ( i = 0; i < game->paddle_count; i++ )
            paddle_reset( game->paddles[i] );

      /* reset updates */
      game_reset_mods();
}

/* set the game context the subfunctions will apply their changes to */
void game_set_current( Game *game )
{
      cur_game = game;
}

/* set score of paddle 'id'. 0 is bottom paddle and 1 is top paddle */
void game_set_score( int id, int score ) 
{
      if ( id < 0 || id >= cur_game->paddle_count ) return;
      cur_game->paddles[id]->score = score;
}

/* set number of additional balls a paddle can fire (all paddles) */
void game_set_ball_ammo( int ammo )
{
      int i;
      for ( i = 0; i < cur_game->paddle_count; i++ ) {
            cur_game->paddles[i]->ball_ammo = ammo;
            cur_game->paddles[i]->start_ball_ammo = ammo;
      }
}

/* set the number of points required to win a PINGPONG level */
void game_set_frag_limit( int limit )
{
      cur_game->frag_limit = limit;
}

/* set wether to use convex paddle */
void game_set_convex_paddle( int convex )
{
      cur_game->paddle_is_convex = convex;
}

/* set wether balls are returned to a paddle by pressing fire.
 * the alternative is that they automatically return. */
void game_set_ball_auto_return( int auto_return )
{
      cur_game->balls_return_by_click = !auto_return;
}

/* set wether balls are fired at random angle or wether the
 * left/right fire keys are used */
void game_set_ball_random_angle( int random )
{
      cur_game->balls_use_random_angle = random;
}

/* set the speed of balls will have in accelerated state */
void game_set_ball_accelerated_speed( float speed )
{
    cur_game->accelerated_ball_speed = speed;
}

/* update state of a paddle. x or y may be 0 which is not a valid value.
 * in this case the property is left unchanged */
void game_set_paddle_state( int id, int x, int y, int left_fire, int right_fire, int return_key )
{
      Paddle *paddle = 0;
      
      if ( id < 0 || id >= cur_game->paddle_count ) return;
      
      paddle = cur_game->paddles[id];
      if ( x != 0 ) { paddle->x = x; paddle->cur_x = x; }
      if ( y != 0 ) paddle->y = y;
      paddle->fire_left = left_fire;
      paddle->fire_right = right_fire;
      paddle->ball_return_key_pressed = return_key;
}

/* move objects, modify game data, store brick hits and collected extras.
 * return wether level has been finished and the id of the winning paddle
 * in network games. -1 is a draw. level_over and winner is saved in the
 * game struct. */
void game_update( int ms )
{
      int i;

      extras_update( ms );
      walls_update( ms );
      shots_update( ms );
      bricks_update( ms );
      for ( i = 0; i < cur_game->paddle_count; i++ )
        {
            paddle_update( cur_game->paddles[i], ms );
            /* release all balls from paddle if invisible */
            if (!paddle_solid(cur_game->paddles[i]))
                balls_detach_from_paddle( cur_game->paddles[i], ((rand()%2==1)?-1:1) );
        }
              
      balls_update( ms );

      /* level finished? */
      cur_game->level_over = 0;
      if ( cur_game->game_type == GT_LOCAL ) {
            /* local game */
            if ( cur_game->bricks_left == 0 ) cur_game->level_over = 1;
            if ( cur_game->balls->count == 0 ) cur_game->level_over = 1;
      } else {
            /* network game */
            if ( cur_game->level_type == LT_NORMAL ) {
                  if ( cur_game->bricks_left == 0 ) cur_game->level_over = 1;
            } 
            else
            if ( cur_game->paddles[PADDLE_TOP]->score >= cur_game->frag_limit ||
                 cur_game->paddles[PADDLE_BOTTOM]->score >= cur_game->frag_limit )
                  cur_game->level_over = 1;
      }

      /* if so, determine winner */
      if ( cur_game->level_over ) {
            if ( cur_game->game_type == GT_LOCAL ) {
                  if ( cur_game->bricks_left == 0 )
                        cur_game->winner = PADDLE_BOTTOM; /* praise */
                  else
                        cur_game->winner = PADDLE_TOP; /* swear */
            } else {
                  cur_game->winner = PADDLE_BOTTOM;
                  if ( cur_game->game_type == GT_NETWORK ) {
                        if ( cur_game->paddles[PADDLE_TOP]->score >
                                    cur_game->paddles[PADDLE_BOTTOM]->score )
                              cur_game->winner = PADDLE_TOP;
                        else
                              if ( cur_game->paddles[PADDLE_TOP]->score == 
                                          cur_game->paddles[PADDLE_BOTTOM]->score )
                                    cur_game->winner = -1;
                  }
            }
      }
}

/* get the modifications that occured in game_update() */

/* get current score of player. return 0 if player does not exist */
int game_get_score( int id, int *score )
{
      if ( id < 0 || id >= cur_game->paddle_count ) return 0;
      *score = cur_game->paddles[id]->score;
      return 1;
}

/* get number of ball reflections */
int game_get_reflected_ball_count( void )
{
      return cur_game->mod.brick_reflected_ball_count+
             cur_game->mod.paddle_reflected_ball_count;
}

/* get number of ball reflections on bricks */
int game_get_brick_reflected_ball_count( void )
{
      return cur_game->mod.brick_reflected_ball_count;
}

/* get number of ball reflections on paddle */
int game_get_paddle_reflected_ball_count( void )
{
      return cur_game->mod.paddle_reflected_ball_count;
}

/* get number of newly attached balls */
int game_get_attached_ball_count( void )
{
      return cur_game->mod.attached_ball_count;
}

/* get number of fired shots no matter which paddle */
int game_get_fired_shot_count( void )
{
      return cur_game->mod.fired_shot_count;
}

/* hit bricks since last call to game_update() */
BrickHit *game_get_brick_hits( int *count )
{
      *count = cur_game->mod.brick_hit_count;
      return cur_game->mod.brick_hits;
}

/* get a list of extras collected by paddle id */
int *game_get_collected_extras( int id, int *count )
{
      *count = 0;
      if ( id < 0 || id >= cur_game->paddle_count ) return 0;
      *count = cur_game->mod.collected_extra_count[id];
      return cur_game->mod.collected_extras[id];
}

/* get a snapshot of the level data which is the brick states
 * converted to the original file format. this can be used to
 * overwrite a levels data when player changes in alternative
 * game */
void game_get_level_snapshot( Level *shot )
{
      int i, j;
      int y_off;
      
      if ( cur_game->game_type == GT_NETWORK ) 
            y_off = ( MAP_HEIGHT - EDIT_HEIGHT ) / 2;
      else
            y_off = 1;

      for ( i = 0; i < EDIT_WIDTH; i++ )
      for ( j = 0; j < EDIT_HEIGHT; j++ ) {
            shot->bricks[i][j] = cur_game->bricks[i+1][j+y_off].brick_c;
            shot->extras[i][j] = cur_game->bricks[i+1][j+y_off].extra_c;
      }
}

/* reset the modification of game_update() */
void game_reset_mods( void )
{
      memset( &cur_game->mod, 0, sizeof( GameMod ) );
}

/* update a statistics struct by the level stats of a paddle.
 * updates the win/loss/draw as well. the played_rounds entry
 * is simply increased everytime this function is called */
void game_update_stats( int id, GameStats *stats )
{
      Paddle *paddle;
      if ( id < 0 || id >= cur_game->paddle_count ) return;
      
      /* this should be called before game_finalize() as the
       * stats will be cleared there */
      paddle = cur_game->paddles[id];
      
      stats->total_score += paddle->score;
    if ( stats->total_score < 0 ) stats->total_score = 0;
      stats->balls_reflected += paddle->balls_reflected;
      stats->balls_lost += paddle->balls_lost;
      stats->bricks_cleared += paddle->bricks_cleared;
      stats->total_brick_count += cur_game->brick_count;
      stats->extras_collected += paddle->extras_collected;
      stats->total_extra_count += cur_game->extra_count;

      if ( cur_game->winner == -1 )
            stats->draws++;
      else
      if ( cur_game->winner == id )
            stats->wins++;
      else
            stats->losses++;
      stats->played_rounds++;
}


Generated by  Doxygen 1.6.0   Back to index