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

game.c

/***************************************************************************
                          local_game.c  -  description
                             -------------------
    begin                : Thu Sep 6 2001
    copyright            : (C) 2001 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "lbreakout.h"
#include "../game/game.h"
#include "../gui/gui.h"
#include "client_data.h"
#include "event.h"
#include "config.h"
#include "shrapnells.h"
#include "player.h"
#include "display.h"
#include "paddle.h"
#include "chart.h"
#include "shine.h"
#include "credit.h"
#include "bricks.h"
#include "shots.h"
#include "frame.h"
#include "balls.h"
#include "extras.h"
#include "help.h"
#include "game.h"
#include "comm.h"
#include "slot.h"
#include "manager.h"

SDL_Surface *bkgnd = 0; /* current background picture */
extern SDL_Surface *nuke_bkgnd; /* nuke background */
SDL_Surface *offscreen = 0; /* buffer with frame, background and bricks */
extern StkFont *font; /* standard font */
extern Config config; /* lbreakout config struct */
extern int stk_quit_request; /* terminate game */
extern SDL_Surface *stk_display; 
extern SDL_Surface *brick_pic;
extern int player_count;
extern Player players[MAX_PLAYERS]; /* player infos */
#ifdef AUDIO_ENABLED
extern StkSound *wav_click;
extern StkSound *wav_damn, *wav_dammit, *wav_wontgiveup, *wav_excellent, *wav_verygood;
#endif
extern int ball_pic_x_offset;
extern int paddle_cw;
extern List *client_users;
extern ClientUser *client_user;
extern GuiWidget *list_users;
extern char pause_chatter[CHAT_LINE_COUNT][CHAT_LINE_WIDTH];
extern GuiWidget *gui_key_widget;
extern GuiWidget *gui_clicked_widget;
extern GuiWidget *gui_focused_widget;
extern GuiWidget *dlg_pauseroom, *dlg_chatroom;

extern List *levelset_names;
Player *cur_player = 0; /* current player */
Paddle *l_paddle = 0; /* locally controlled paddle */
Paddle *r_paddle = 0; /* remotely controlled paddle */
LevelSet *game_set; /* set that is played */
Game *local_game = 0; /* in a local game the remote side is faked in this
                   game context to have just one main loop for both game
                   types */
Game *game = 0; /* local/network game context */
int game_round; /* id of current round in network game */
int game_stats[2][7]; /* network game stats */
int stats_received = 0;
int game_over = 0; /* network game is over */
int allow_disintegrate = 1; /* wether Plane of Inner Stability may be entered */
char best_name[32];
int  best_score; /* result of first place in chart of local set */
int  showing_best = 0; /* if true best score is shown, thus display should not be updated */
Display *display_score[2]; /* either score or frags */
Display *display_player[2]; /* player name and lifes or wins */
extern int client_state;
extern int warp_blinks, warp_blink;
extern SDL_Surface *paddle_pic, *weapon_pic, *ball_pic, *extra_pic, *shot_pic;
extern StkFont *display_font;
extern int bkgnd_count;
int bkgnd_ids[MAX_LEVELS]; /* random background ids changed everytime a game is started */
int client_comm_delay; /* delay between communications */
int no_comm_since; /* time passed this last comm */
extern char client_name[16]; /* our local username */
int freakout_seed = 0; /* last used seed for freakout game */

extern int current_player;
extern Item *item_resume_0;

extern void select_chart( char *name, int update );

/*
====================================================================
Locals
====================================================================
*/


/* initiate the level of the game context by using the player's
 * snapshot. the snapshot must've been previously set either by 
 * net update or locally. 'l_pos' is either PADDLE_BOTTOM or TOP
 * indicating which paddle client controls in network game.
 */
static int init_level( Player *player, int l_pos )
{
      int length;
      char str[32];

      /* init level by replacing with the players snapshot. this will
       * screw the total brick count thus stats are useless for local
       * game but they are not used anyway as the remote/local game
       * context contains the real stats */
      if ( game->game_type == GT_LOCAL )
            game_init( local_game, &player->snapshot );
      game_init( game, &player->snapshot );
      
      /* initiate frame for game type */
      frame_init();

      /* create offscreen */
      offscreen = stk_surface_create( SDL_SWSURFACE, stk_display->w, stk_display->h );
      SDL_SetColorKey( offscreen, 0, 0 );

      /* add&create background */
      if ( game->game_type == GT_LOCAL )
            bkgnd_draw( bkgnd, bkgnd_ids[player->level_id] );
      else
            bkgnd_draw( bkgnd, -1 );
      /* add frame */
      frame_draw();
      /* add bricks */
      bricks_draw();
      /* draw lives */
      if ( game->game_type == GT_LOCAL )
            frame_draw_lives( player->lives, game->diff->max_lives );
   
      /* determine what is the local and what is the remote paddle and
       * connect the proper players with each paddle */
      if ( game->game_type == GT_LOCAL ) {
            l_paddle = game->paddles[PADDLE_BOTTOM];
            l_paddle->player = player;
            r_paddle = 0;
      } else {
            if ( l_pos == PADDLE_BOTTOM ) {
                  l_paddle = game->paddles[PADDLE_BOTTOM];
                  r_paddle = game->paddles[PADDLE_TOP];
            } else {
                  l_paddle = game->paddles[PADDLE_TOP];
                  r_paddle = game->paddles[PADDLE_BOTTOM];
            }
            /* player 0 is always the local player */
            l_paddle->player = &players[0];
            r_paddle->player = &players[1];
            
            /* let top paddle use alternative graphics if any */
            if ( paddle_pic->w > 3*paddle_cw )
                  game->paddles[PADDLE_TOP]->pic_x_offset = 3*paddle_cw;
      }
      player->paddle_id = l_pos;

      /* displays */
      if ( game->game_type == GT_LOCAL ) {
            /* we put these displays to the old positions 
               at the top of the frame */
            sprintf( str, "%s", player->name );
            length = strlen(best_name);
            if (strlen(player->name)>length )
              length = strlen(player->name);
            length *= 8;
            display_player[0] = displays_add( 402, 0,
                        length + 4, 16, str, player->lives, 0 );
            display_score[0] = displays_add( stk_display->w - 52 - 76, 0,
                        76, 16, "", player->stats.total_score, 9 );
            display_player[0]->use_alpha = 0;
            display_score[0]->use_alpha = 0;
      } else {
            /* wins */
            sprintf( str, "%s ~x%i", 
                  game->paddles[0]->player->name, 
                  game->paddles[0]->player->stats.wins );
            length = strlen( str ) * 8;
            display_player[0] = displays_add( 
                  BRICK_WIDTH + 20, ( MAP_HEIGHT - 1 ) * BRICK_HEIGHT + 2,
                  length + 4, 16, str, 0, 0 );
            sprintf( str, "%s ~x%i", 
                  game->paddles[1]->player->name, 
                  game->paddles[1]->player->stats.wins );
            length = strlen( str ) * 8;
            display_player[1] = displays_add( 
                  BRICK_WIDTH + 20, 2, length + 4, 16, str, 0, 0 );
            /* scores */
            display_score[0] = displays_add( 
                        stk_display->w - BRICK_WIDTH - 20 - 76,
                        ( MAP_HEIGHT - 1 ) * BRICK_HEIGHT + 2,
                        76, 16, "", game->paddles[0]->score, 9 );
            display_score[1] = displays_add( 
                        stk_display->w - BRICK_WIDTH - 20 - 76, 2,
                        76, 16, "", game->paddles[1]->score, 9 );
      }

      /* initiate credit */
    if ( game->game_type == GT_LOCAL && game_set )
          credit_init( player->snapshot.name, player->snapshot.author, 
            player->level_id, game_set->count );
    else
          credit_init( player->snapshot.name, player->snapshot.author, 
            player->level_id, 0/*don't display info*/ );

      /* show offscreen */
      stk_surface_blit( offscreen, 0,0,-1,-1, stk_display, 0,0 );

      /* no refresh rect as we want to use dim effect */
      return 1;
}

static void finalize_level( void )
{
      /* set alpha keys to OPAQUE */
      SDL_SetAlpha( paddle_pic, 0,0 );
      SDL_SetAlpha( weapon_pic, 0,0 );
      SDL_SetAlpha( extra_pic, 0,0 );
      SDL_SetAlpha( ball_pic, 0,0 );
      SDL_SetAlpha( shot_pic, 0,0 );
      SDL_SetAlpha( display_font->surface, 0,0 );

      /* reset ball graphic */
      ball_pic_x_offset = 0;
      
      /* reset shrapnells */
      shrapnells_reset();
      /* reset shine */
      shine_reset();
      /* reset explosions */
      exps_clear();
      exps_set_dark( 0 );

      /* delete offscreen */
      stk_surface_free( &offscreen );

      /* clear credits */
      credit_clear();
      
      /* clear displays */
      displays_clear();
      
      /* clear game contexts */
      if ( game->game_type == GT_LOCAL ) 
            game_finalize( local_game );
      game_finalize( game );
}

/* display formatted info + score table if multiple players */
static void display_score_table( char *format, ... )
{
      va_list args;
      int i;
      char info[256], buf[32];

      va_start( args, format );
      vsnprintf( info, 64, format, args );
      va_end( args );
      
      if ( player_count > 1 ) {
            strcat( info, "##" );
            for ( i = 0; i < player_count; i++ ) {
                  /* add player and score */
                  sprintf( buf, "#%12s %10i", " ", 
                              players[i].stats.total_score );
                  strcpy( buf + 1, players[i].name );
                  buf[strlen(players[i].name)+1] = 32;
                  strcat( info, buf );
            }
      }

      display_text( font, info );
      stk_display_update( STK_UPDATE_ALL );
}

/* begin frame by hiding all objects */
static void begin_frame( void )
{
      int i;
      
        displays_hide();
        frame_info_hide();
        extras_hide();
        for ( i = 0; i < game->paddle_count; i++ ) {
            paddle_hide( game->paddles[i] );
          paddle_ammo_hide( game->paddles[i] );
      }
        balls_hide();
        shots_hide();
        shrapnells_hide();
        walls_hide();
        frame_warp_icon_hide();
        shine_hide();
        exps_hide();
        credit_hide();
}

/* end frame by drawing all objects and updating the screen */
static void end_frame( void )
{
      int i;
      
      /* show -- some things will be missing if darkness is enabled */
      balls_show_shadow();
      extras_show_shadow();
      for ( i = 0; i < game->paddle_count; i++ )
            paddle_show_shadow( game->paddles[i] );
      shots_show();
      if ( config.debris_level == DEBRIS_BELOW_BALL ) {
            exps_show();
            if ( !game->extra_active[EX_DARKNESS] ) {
                  shrapnells_show();
                  frame_info_show();
            }
      }     
      if ( config.ball_level == BALL_ABOVE_BONUS )
            extras_show();
      balls_show();
      if ( config.ball_level == BALL_BELOW_BONUS )
            extras_show();
      for ( i = 0; i < game->paddle_count; i++ )
            paddle_show( game->paddles[i] );
      if ( !game->extra_active[EX_DARKNESS] ) walls_show();
      shine_show();
      if ( config.debris_level == DEBRIS_ABOVE_BALL ) {
            exps_show();
            if ( !game->extra_active[EX_DARKNESS] ) {
                  shrapnells_show();
                  frame_info_show();
            }
      }     
      frame_warp_icon_show();
      displays_show();
      for ( i = 0; i < game->paddle_count; i++ )
            paddle_ammo_show( game->paddles[i] );
      credit_show();
}

/* grab/ungrab input of actual game */
static void grab_input( int grab )
{
      if ( grab ) {
            SDL_ShowCursor(0);
            SDL_WM_GrabInput( SDL_GRAB_ON );
            SDL_GetRelativeMouseState(0,0);
      } else {
            SDL_ShowCursor(1);
            SDL_WM_GrabInput( SDL_GRAB_OFF );
      }
}

/* switch client to new state */
void set_state( int newstate )
{
      if ( client_state == newstate ) return;
      
      if ( newstate == CS_PLAY )
            grab_input( 1 );
      if ( client_state == CS_PLAY )
            grab_input( 0 );
      if ( client_state == CS_CONFIRM_WARP ||
           client_state == CS_CONFIRM_RESTART ||
           client_state == CS_CONFIRM_QUIT ||
             client_state == CS_CONFIRM_CONTINUE ||
           client_state == CS_GET_READY ||
           client_state == CS_PAUSE ||
           client_state == CS_FINAL_PLAYER_INFO ||
           client_state == CS_RECV_LEVEL ||
           client_state == CS_ROUND_RESULT ||
           client_state == CS_RECV_STATS ||
           client_state == CS_FATAL_ERROR ) {
            /* show offscreen */
            if ( offscreen ) {
                  stk_surface_blit( offscreen, 0,0,-1,-1, stk_display, 0,0 );
                  end_frame();
            }
            /* do not refresh when coming from RECV_LEVEL as a GET_READY
             * will follow */
            if ( client_state != CS_RECV_LEVEL )
            if ( client_state != CS_ROUND_RESULT )
            if ( client_state != CS_RECV_STATS );
                  stk_display_update( STK_UPDATE_ALL );
      }

      client_state = newstate;
      stk_timer_reset();
}

/* Fade all animations until they disappear */
static void fade_anims()
{
      float alpha = 255.0;
      int ms, i;
      stk_timer_reset();
      if ( game->game_type == GT_LOCAL && game->winner != PADDLE_BOTTOM )
            frame_remove_life();
      while ( alpha > 0 ) {
            displays_hide();
            for ( i = 0; i < game->paddle_count; i++ )
                  paddle_hide( game->paddles[i] );
            balls_hide();
            extras_hide();
            shrapnells_hide();
            shots_hide();
            walls_hide();
            credit_hide();
            ms = stk_timer_get_time();
            alpha -= 0.3 * ms;
            if ( alpha < 0 ) alpha = 0;
            shrapnells_update( ms );
            for ( i = 0; i < game->paddle_count; i++ )
                  paddle_alphashow( game->paddles[i], alpha );
            balls_alphashow( alpha );
            extras_alphashow( alpha );
            shots_alphashow( alpha );
            shrapnells_show();
            walls_alphashow( alpha );
            displays_show();
            credit_alphashow( alpha );
            stk_display_update( STK_UPDATE_RECTS );
      }
}

void open_pause_chat( char *text )
{
      set_state( CS_PAUSE );
      
      /* clear pause_chatter */
      memset( pause_chatter, 0, sizeof( pause_chatter ) );
      /* clear global gui widgets */
      gui_focused_widget = 0;
      gui_clicked_widget = 0;
      gui_key_widget = 0;
      /* use 'text' as initial chatter */
      client_add_pausechatter( text, 1 );
      /* gray screen */
      stk_surface_gray( stk_display, 0,0,-1,-1, 1 );
      /* show pauseroom */
      gui_widget_show( dlg_pauseroom );
      stk_display_update( STK_UPDATE_ALL );

      /* disable event filter */
      SDL_SetEventFilter( 0 );
      /* disable client_recv which is called as time event */
      gui_widget_disable_event( dlg_chatroom, GUI_TIME_PASSED );
}

void close_pause_chat( void )
{
      gui_widget_hide( dlg_pauseroom );
      set_state( CS_PLAY );

      /* enable event filter */
      SDL_SetEventFilter( event_filter );
      gui_widget_enable_event( dlg_chatroom, GUI_TIME_PASSED );
}

/* Pause/unpause a local/network game. */
static void client_set_pause( int pause )
{
  if (game_set==0) return; /* test level */
  if (pause&&client_state==CS_PLAY)
    {
      /* in local game simply darken the screen, in
       * network game enter the pausechatroom */
      if ( game->game_type == GT_LOCAL ) {
      set_state(CS_PAUSE);
      display_text( font, "Pause" );
      }
      else {
      open_pause_chat( "You have paused the game." );
      comm_send_short( MSG_PAUSE );
      }
    }
  else if (!pause&&client_state==CS_PAUSE)
    {
      /* unpause (local game only)*/
      if ( game->game_type == GT_LOCAL )
      set_state(CS_PLAY);
    }
}

/* modify the client and its state according to the key pressed */
static int handle_default_key( int key, int *abort )
{
      SDL_Surface *buffer;
      
      switch ( key ) {
            case SDLK_F1:
            case SDLK_h:
                  if ( client_state != CS_PLAY ) break;
                  if ( game->game_type == GT_NETWORK ) break; /* only for single player */
                  grab_input(0);
                  help_run();
                  grab_input(1);
                  return 1;
            case SDLK_q:
            case SDLK_ESCAPE:
                  /* recv_stats or final_stats means we already broke up
                   * the game so ESC will directly quit */
                  if ( client_state == CS_RECV_STATS || client_state == CS_FINAL_STATS ) {
                        *abort = 1;
                        break;
                  }

                  if ( client_state == CS_CONFIRM_QUIT ) break;
                  if ( client_state == CS_PAUSE ) break;
                        if ( players_count() == 0 ) break; 
                  set_state(CS_CONFIRM_QUIT); 
                        if ( game->game_type == GT_LOCAL && game_set != 0 /*not testing a level*/ )
                      display_text( font, "Quit Game? y/n#(If yes, this game may be resumed later.#No highscore entry is created yet.)" );
                        else
                      display_text( font, "Quit Game? y/n" );
                  return 1;
            case SDLK_r:
                  if ( client_state != CS_PLAY ) break;
                  if ( game->game_type == GT_NETWORK ) break; /* only for single player */
                  if ( game_set == 0 ) break; /* test level */
                  if ( cur_player->lives < 2 ) break;
                  set_state(CS_CONFIRM_RESTART); 
                  display_text( font, "Restart Level? y/n" );
                  return 1;
            case SDLK_d:
                  if ( client_state != CS_PLAY ) break;
                  if ( game->game_type == GT_NETWORK ) break; /* only for single player */
                  if ( !allow_disintegrate ) break;
                  grab_input(0);
                  game_nuke();
                  grab_input(1);
                  return 1;
            case SDLK_f:
                  buffer = stk_surface_create( SDL_SWSURFACE, 640, 480 );
                  SDL_BlitSurface( stk_display, 0, buffer, 0 );
                  config.fullscreen = !config.fullscreen;
                  stk_display_apply_fullscreen( config.fullscreen );
                  SDL_BlitSurface( buffer, 0, stk_display, 0 );
                  stk_display_update( STK_UPDATE_ALL);
                  SDL_FreeSurface( buffer );
                  return 1;
            case SDLK_s:
#ifdef AUDIO_ENABLED
                  config.sound = !config.sound;
                  stk_audio_enable_sound( config.sound );
#endif
                  return 1;
            case SDLK_a:
                  config.anim++;
                  if ( config.anim >= 4 ) config.anim = 0;
                  return 1;
            case SDLK_TAB:
                  stk_display_take_screenshot();
                  return 1;
            case SDLK_t:
                  return 0;
            case SDLK_p:
                  if ( client_state == CS_PLAY )
                    client_set_pause(1);
                  else if (client_state==CS_PAUSE)
                    client_set_pause(0);
                  return 1;
            default: 
                  if ( client_state != CS_PLAY ) break;
                  if ( game->game_type != GT_LOCAL ) break;
                  if ( game->bricks_left > game->warp_limit ) break;
                  if ( game_set == 0 ) break; /* test level */
                  if ( key == config.k_warp ) {
                        set_state(CS_CONFIRM_WARP);
                        display_text( font, "Warp to next level? y/n" );
                        return 1;
                  }
                  break;
      }
      
      return 0;
}

/* update local objects (shrapnells,extras,explosions...) and communicate
 * every client_comm_delay seconds either with real or fake server */
static void update_game( int ms )
{
      int i;
      
      /* run the fake server game */
      if ( game->game_type == GT_LOCAL ) {
            game_set_current( local_game );
            game_update( ms );
            game_set_current( game );
      }
            
      /* local animations and movements */
      for ( i = 0; i < game->paddle_count; i++ )
            client_paddle_update( game->paddles[i], ms );
      client_shots_update( ms );
      client_balls_update( ms );
      client_extras_update( ms );
      client_walls_update( ms );
      shrapnells_update( ms );
      frame_warp_icon_update( ms );
      shine_update( ms );
      exps_update( ms );
      displays_update( ms );
      credit_update( ms );

      /* communicate */
      if ( (no_comm_since+=ms) >= client_comm_delay ) {
            no_comm_since -= client_comm_delay;

            /* send paddle state */
            comm_send_paddle( l_paddle );
      
            /* receive game data from local or remote server and 
             * apply it to the game context. */
            comm_recv();
            
            /* update score displays */
            if (!showing_best)
              display_set_value( 
                    display_score[0], 
                    game->paddles[0]->player->stats.total_score + 
                    game->paddles[0]->score );
            if ( game->game_type == GT_NETWORK )
                  display_set_value( 
                        display_score[1], 
                        game->paddles[1]->player->stats.total_score + 
                        game->paddles[1]->score );
      }
}

/* give us a damn or excellent depending on the outcome of the level.
 * the result for network game must've been received already so that
 * game::level_over and game::winner are valid entries. */
static void play_speech( void )
{
#ifdef AUDIO_ENABLED
      if ( !config.speech ) return;
      if ( game->winner == -1 ) return; /* draw */
      
      if ( game->paddles[game->winner] == l_paddle ) {
            if ( rand() % 2 )
                  stk_sound_play( wav_excellent );
            else
                  stk_sound_play( wav_verygood );
      } else {
            if ( !game->diff->allow_maluses ) return; /* bad speech is bad somehow */

            if ( rand() % 2 )
                  stk_sound_play( wav_damn );
            else
                  stk_sound_play( wav_dammit );
      }
#endif
}

/* check players of local game wether they entered a highscore */
static void check_highscores( void )
{
      int i;
      
      chart_clear_new_entries();
      for ( i = 0; i < config.player_count; i++ )
            chart_add( 
                  chart_set_query(game_set->name), 
                  players[i].name, 
                  players[i].level_id + 1, 
                  players[i].stats.total_score );
      chart_save();
}

/* init next network game round by displaying a message and switching
 * to GET_READY. */
void init_next_round( void )
{
      game_round++;
      set_state( CS_GET_READY );
      init_level( cur_player, cur_player->paddle_id );
      display_text( font,
            "***** Round %i *****###You control the %s paddle in this level!#"
            "To fire a ball keep the mouse button PRESSED.#Don't just click.###"
            "Press any key when you are ready...###(You can pause the game with 'p' any time.)"
            "###NOTE: Due to latency, bonuses on the server are closer than they "
            "appear! I'll try to work on that.", 
            game_round, cur_player->paddle_id==0?"BOTTOM":"TOP" );
}

/* display a message about the winner */
void finalize_round( void )
{
      if ( (char)game->winner == -1 )
            display_text( font, "DRAW" );
      else {
            game->paddles[game->winner]->player->stats.wins++;
            if ( game->winner == cur_player->paddle_id )
                  display_text( font, "You have won this round!" );
            else
                  display_text( font, "You have lost this round." );
      }
      finalize_level();
      set_state( CS_ROUND_RESULT );
}

/* display the final statistics. the first player is always this client
 * and the second is the remote. */
void display_final_stats( void )
{
      int win;

      /* won this match? */
      if ( game_stats[0][0] > game_stats[1][0] )
            win = 1;
      else
      if ( game_stats[0][0] == game_stats[1][0] )
            win = -1;
      else
            win = 0;
      
      /* build stats string */
      display_text( font, 
                  "             Result: %s              ##" \
                  "                  %12s %12s##" \
                  "Wins:             %12i %12i#" \
                  "Losses:           %12i %12i#" \
                  "Draws:            %12i %12i#" \
                  "#" \
                  "Total Score:      %12i %12i#" \
                  "#" \
                  "Balls Kept:       %11i%% %11i%%#" \
                  "Bricks Cleared:   %11i%% %11i%%#" \
                  "Extras Collected: %11i%% %11i%%##" \
                        "(Press SPACE to continue)",
                  win==1?"VICTORY":win==0?" DEFEAT":"   DRAW",
                  players[0].name, players[1].name,
                  game_stats[0][0], game_stats[1][0],
                  game_stats[0][1], game_stats[1][1],
                  game_stats[0][2], game_stats[1][2],
                  game_stats[0][3], game_stats[1][3],
                  game_stats[0][4], game_stats[1][4],
                  game_stats[0][5], game_stats[1][5],
                  game_stats[0][6], game_stats[1][6] );
}

/* save data from local and local_game variables and update
 * the menu hint. */
void save_local_game( int slot_id )
{
    GameSlot gs;
    int i;

    memset( &gs, 0, sizeof(GameSlot) );
    strcpy( gs.setname, game_set->name );
    gs.diff = config.diff;
    gs.player_count = config.player_count;
    gs.cur_player = current_player;
    for ( i = 0; i < MAX_PLAYERS; i++ )
    {
        strcpy( gs.player_names[i], config.player_names[i] );
        gs.player_cur_level_id[i] = players[i].level_id;
        gs.player_lives[i] = players[i].lives;
        gs.player_scores[i] =  players[i].stats.total_score;
    }
    gs.freakout_seed = freakout_seed;
    if ( !slot_save( slot_id, &gs ) )
        fprintf( stderr, "ERROR: couldn't save game!\n" );
    slot_update_hint( slot_id, item_resume_0->hint );
}

/* check whether Shift is pressed to switch between own and highest score */
void handle_display_switch()
{
  int modstate = 0;
  modstate = SDL_GetModState();
  if (!showing_best)
    {
      if (modstate&KMOD_RSHIFT||modstate&KMOD_LSHIFT)
      {
        display_set_text( display_player[0], best_name );
        display_set_value_directly( display_score[0], best_score );
        display_set_highlight( display_player[0], 1 );
        display_set_highlight( display_score[0], 1 );
        showing_best = 1;
      }
    }
  else
    {
      if (!(modstate&KMOD_RSHIFT||modstate&KMOD_LSHIFT))
      {
        display_set_text( display_player[0], cur_player->name );
        display_set_value_directly( display_score[0],
                       game->paddles[0]->player->stats.total_score +
                       game->paddles[0]->score );
        display_set_highlight( display_player[0], 0 );
        display_set_highlight( display_score[0], 0 );
        showing_best = 0;
      }
    }
}

/*
====================================================================
Publics
====================================================================
*/

/* create various resources like shrapnells */
void client_game_create()
{
      frame_create();
      shrapnells_init();
      shine_load();
      init_angles();

      /* background */
      bkgnd = stk_surface_create( SDL_SWSURFACE, 
                  stk_display->w, stk_display->h );
      SDL_SetColorKey( bkgnd, 0, 0 );
      stk_surface_fill( bkgnd, 0,0,-1,-1, 0x0 );

}
void client_game_delete()
{
      stk_surface_free( &bkgnd );
      
      displays_clear();
      frame_delete();
      shrapnells_delete();
      shine_delete();
}

/* create network/local game context and initiate game state:
 * network needs to receive the level data and a local game
 * has to load the next level */
int client_game_init_local( char *setname )
{
        Set_Chart *chart;
      int i, warp_limit;

      warp_limit = config.rel_warp_limit;
      allow_disintegrate = 1;

      /* the original levelsets do not need these workarounds */
      if ( STRCMP( setname, "LBreakout2" ) || STRCMP( setname, "LBreakout1" ) ) {
            warp_limit = 100;
            allow_disintegrate = 0;
      }
      
      /* the approach for a local game is to use the same
       * settings as a network game. the receiving of packets
       * is simply faked by a local_game context that
       * runs the game locally. but to use only one game loop
       * we do not use it directly but apply its modificiations
       * to game which is visualized */
      local_game = game_create( GT_LOCAL, config.diff, warp_limit );
      game_set_current( local_game );
      game_set_convex_paddle( config.convex );
      game_set_ball_auto_return( !config.return_on_click );
      game_set_ball_random_angle( config.random_angle );
        game_set_ball_accelerated_speed( config.maxballspeed_float );
      
      /* load levels:
       * only required for local games. in network both players
       * just require a single level that can store the incoming
       * data that is send by the server via the net.
       */
      if ( !strcmp( setname, TOURNAMENT ) )
          game_set = levelset_load_all( levelset_names, freakout_seed );
      else
            game_set = levelset_load( setname );
      if ( game_set == 0 ) return 0;

      /* load highest score so far if any */
      chart = chart_set_query(setname);
      strcpy(best_name,"nobody"); best_score = 0;
      if (chart)
        {
          strcpy(best_name,chart->entries[0].name);
            best_score = chart->entries[0].score;
        }
      
      /* create client game context */
      game = game_create( GT_LOCAL, config.diff, warp_limit );
      game_set_current( game );
      
      /* a local game is not limited in its communication */
      client_comm_delay = 0;
      no_comm_since = 0;
      
      /* prepare warp icon at frame */
      warp_blinks = 4; warp_blink = 1;
      
      /* set list of level background ids */
      for ( i = 0; i < MAX_LEVELS; i++ )
            bkgnd_ids[i] = rand() % bkgnd_count;
      
      /* initiate players */
      players_clear();
      for ( i = 0; i < config.player_count; i++ )
            player_add( config.player_names[i], 
                      game->diff->lives, 
                      levelset_get_first( game_set ) );
      cur_player = players_get_first();

      /* init first level */
      init_level( cur_player, PADDLE_BOTTOM );
      
      /* if only one player don't show score table */
      client_state = CS_NONE;
      if ( player_count > 1 )
            set_state( CS_SCORE_TABLE );
      else
            set_state( CS_PLAY ); /* one player starts immediately */
      return 1;
}
int client_game_init_network( char *opponent_name, int diff )
{
      /* create an empty one level levelset. the server will send
       * the data into the level everytime we play. */
      game_set = levelset_create_empty( 1, "empty", "empty" );
      
      /* create client game context */
      game = game_create( GT_NETWORK, diff, 100 );
      game_set_current( game );
      game_round = 0; /* will be increased by init_next_round() */
      game_over = 0;
      
      /* a network game communicates every 25 ms by default */
      client_comm_delay = 25;
      no_comm_since = 0;
      
      /* initiate players */
      players_clear();
      player_add( client_name, game->diff->lives, levelset_get_first( game_set ) );
      player_add( opponent_name, game->diff->lives, levelset_get_first( game_set ) );
      cur_player = players_get_first();

      display_text( font, "Receiving level data..." );
      set_state( CS_RECV_LEVEL );
      return 1;
}

/* create local game context and initiate game state
 * as given from slot 'slot_id'. */
int client_game_resume_local( int slot_id )
{
    int i;
    GameSlot gs;
   
    /* load saved game */
    if ( !slot_load( slot_id, &gs ) ) return 0;
    
    /* FIXME: config settings are overwritten for this */
    config.diff = gs.diff;
    config.player_count = gs.player_count;
    for ( i = 0; i < config.player_count; i++ )
        strcpy( config.player_names[i], gs.player_names[i] );
    freakout_seed = gs.freakout_seed;

    /* create local game where all players have full lives */
    if ( !client_game_init_local( gs.setname ) ) return 0;

    /* re-initiate players */
    players_clear();
    for ( i = 0; i < config.player_count; i++ )
    {
        /* name + lives */
        player_add( config.player_names[i], 
                gs.player_lives[i], 
                levelset_get_first( game_set ) );
        /* level */
        player_init_level( &players[i], 
                           game_set->levels[gs.player_cur_level_id[i]],
                           gs.player_cur_level_id[i] );
        /* score */
        players[i].stats.total_score = gs.player_scores[i];
    }
    cur_player = players_set_current( gs.cur_player );
    
    /* init first level */
    init_level( cur_player, PADDLE_BOTTOM );
      
    return 1;
}

/* create a one level game context for testing a level */
int client_game_init_testing( Level *level )
{
      local_game = game_create( GT_LOCAL, config.diff, 100 );
      game_set_current( local_game );
      game_set_convex_paddle( config.convex );
      game_set_ball_auto_return( !config.return_on_click );
      game_set_ball_random_angle( config.random_angle );
        game_set_ball_accelerated_speed( config.maxballspeed_float );
      
      game = game_create( GT_LOCAL, config.diff, 100 );
      game_set_current( game );

      players_clear();
      player_add( config.player_names[0], game->diff->lives, level );
      cur_player = players_get_first();

      bkgnd_ids[0] = 0;

      init_level( cur_player, PADDLE_BOTTOM );
      
      client_state = CS_NONE;
      set_state( CS_PLAY ); 

      return 1;
}

/* finalize a game and free anything allocated by init process */
void client_game_finalize()
{
      players_clear();

      if ( game && game->game_type == GT_LOCAL ) {
            game_delete( &local_game );
            levelset_delete( &game_set );
      }
      game_delete( &game );
}

/* run the state driven loop until game is broken up or finished */
void client_game_run( void )
{
      int ms, frame_delay = config.fps?10:1;
      int button_clicked, key_pressed;
      SDL_Event event;
      int abort = 0, i, j, penalty;
      /* frame rate */
      int frames = 0;
      int frame_time = SDL_GetTicks();

      event_clear_sdl_queue();
      
      stk_display_fade( STK_FADE_IN, STK_FADE_DEFAULT_TIME );
      
      stats_received = 0;
      stk_timer_reset(); ms = 1;
      while ( !abort && !stk_quit_request ) {
            /* check wether an event occured */
            button_clicked = key_pressed = 0;
            if ( SDL_PollEvent( &event ) ) {
                  if ( client_state == CS_PAUSE && game->game_type == GT_NETWORK )
                        gui_dispatch_event( &event, ms );
                  else
                  if ( event.type == SDL_MOUSEBUTTONDOWN )
                        button_clicked = event.button.button;
                  else
                  if ( event.type == SDL_KEYDOWN ) {
                        key_pressed = event.key.keysym.sym;
                        if ( handle_default_key( key_pressed, &abort ) )
                              key_pressed = 0;
                  }
                  else
                  if (event.type == SDL_ACTIVEEVENT)
                      {
                      if (event.active.state == SDL_APPINPUTFOCUS ||
                        event.active.state == SDL_APPACTIVE )
                            if (event.active.gain == 0 )
                        client_set_pause(1);
                    }
            }
            else if ( client_state == CS_PAUSE && game->game_type == GT_NETWORK )
                  gui_dispatch_event( 0, ms );

            /* check whether Shift is pressed to switch between own and highest score */
            if (game->game_type == GT_LOCAL)
              handle_display_switch();

            /* let server know we're still alive except
             * in CS_PLAY as we send paddle updates there */
            if ( game->game_type == GT_NETWORK )
                  comm_send_heartbeat();

            /* handle client */
            switch ( client_state ) {

            case CS_FINAL_STATS:
                  if ( key_pressed==SDLK_SPACE ) abort = 1;
                  break;
                  
            case CS_FATAL_ERROR:
                  /* after game was violently broken up the server
                   * may still send the stats of the game so far */
                  if ( button_clicked || key_pressed ) {
                        SDL_Delay(250); /* give time to release button */
                        set_state( CS_RECV_STATS );
                        display_text( font, "Receiving final stats..." );
                  }
                  break;
                  
            case CS_FINAL_TABLE:
                  if ( button_clicked || key_pressed ) {
                        check_highscores();
                        select_chart( game_set->name, 0 );
                                /* remove saved game */
                                slot_delete( 0 );
                                slot_update_hint( 0, item_resume_0->hint );
                        /* quit local game */
                        abort = 1;
                  }
                  break;

            case CS_SCORE_TABLE:
                  /* show who's next player and scores in local game */
                  display_score_table( "Next Player: %s", cur_player->name );
                  set_state( CS_GET_READY );
                  break;
                  
            case CS_FINAL_PLAYER_INFO:
                  if ( button_clicked || key_pressed ) {
                        SDL_Delay(250); /* give time to release button */
                        set_state( CS_NEXT_PLAYER );
                  }
                  break;

            case CS_RECV_LEVEL:
                  comm_recv();
                  if ( cur_player->next_level_received ) {
                        cur_player->next_level_received = 0;
                        cur_player->paddle_id = cur_player->next_paddle_id;
                        init_next_round();
                  }
                  break;

            case CS_RECV_STATS:
                  comm_recv();
                  if ( stats_received ) {
                        set_state( CS_FINAL_STATS );
                        display_final_stats();
                  }
                  break;
                  
            case CS_ROUND_RESULT:
                  if ( button_clicked || key_pressed ) {
                        SDL_Delay(250); /* give time to release button */
                        if ( game_over ) {
                              set_state( CS_RECV_STATS );
                              display_text( font, "Receiving final stats..." );
                        } else {
                              set_state( CS_RECV_LEVEL );
                              display_text( font, "Receiving level data..." );
                        }
                  }
                  break;
                  
            case CS_GET_READY:
                  if ( button_clicked || key_pressed ) {
                        SDL_Delay(250); /* give time to release button */
                        comm_send_short( MSG_READY );
                        set_state( CS_PLAY );
                  }
                  break;

            case CS_PAUSE:
                  if ( game->game_type == GT_LOCAL ) break;

                  /* check wether pause chatroom has been closed
                   * either by client or remote */
                  comm_recv();
                  break;
                  
            case CS_PLAY:
                  /* hide objects */
                  begin_frame();
                  
                  /* apply events to local paddle */
                  paddle_handle_events( l_paddle, ms );

                  /* update local objects and communicate if
                   * comm_delay ms have passed */
                  update_game( ms );
                  
                  /* show objects */
                  end_frame();

                  /* handle local level over */
                  if ( game->level_over ) {
                        if ( game->game_type == GT_LOCAL ) {
                              if ( game_set == 0 ) {
                                    abort = 1; /* was a test level */
                                    grab_input(0);
                                    break;
                              }
                              if ( game->winner == PADDLE_BOTTOM )
                                    set_state( CS_NEXT_LEVEL );
                              else
                                    set_state( CS_LOOSE_LIFE );
                        } else {
                              finalize_round();
                        }
                  }
                  break;

            case CS_NEXT_LEVEL:
                  /* apply paddle stats to player */
                  game_set_current( local_game );
                  game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
                  game_set_current( game );
                  /* init next level for player in local game */
                  cur_player->level_id++;
                  if ( cur_player->level_id >= game_set->count ) {
                        /* deactivate player */
                        cur_player->lives = 0;
                        display_text( font, 
                              "You've cleared all levels...#Congratulations!!!" );
                        set_state( CS_FINAL_PLAYER_INFO );
                        break;
                  }
                  /* get snapshot for next init */
                  cur_player->snapshot = *game_set->levels[cur_player->level_id];
                  /* cycle players */
                  set_state( CS_NEXT_PLAYER );
                  break;

            case CS_RESTART_LEVEL:
                  /* apply paddle stats to player */
                  game_set_current( local_game );
                  game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
                  game_set_current( game );
                  /* reset level for next turn */
                  cur_player->snapshot = *game_set->levels[cur_player->level_id];
                  /* decrease lives (is checked that this wasn't the last one) */
                  cur_player->lives--;
                  /* cycle players */
                  set_state( CS_NEXT_PLAYER );
                  break;
                  
            case CS_LOOSE_LIFE:
                  /* apply paddle stats to player */
                  game_set_current( local_game );
                  game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
                  game_set_current( game );

                  /* remember level for next turn */
                  game_get_level_snapshot( &cur_player->snapshot );

                  /* decrease lives */
                  cur_player->lives--;
                  if ( cur_player->lives == 0 ) {
                        display_text( font, 
                              "You've lost all lives...#Do you want to buy a continue#for 100%% of your score? y/n" );
                                set_state( CS_CONFIRM_CONTINUE );
                        //set_state( CS_FINAL_PLAYER_INFO );
                        break;
                  }
                  set_state( CS_NEXT_PLAYER );
                  break;

            case CS_NEXT_PLAYER:
                  /* game over? */
                  if ( players_count() == 0 ) {
                        display_score_table( "Game Over!" );
                        set_state( CS_FINAL_TABLE );
                        break;
                  }
                  /* speak and fade */
                  play_speech();
                  fade_anims();
                  /* finalize current game context */
                  finalize_level();
                  /* set next player */
                  cur_player = players_get_next();
                  init_level( cur_player, PADDLE_BOTTOM );
                  if ( player_count > 1 )
                        set_state( CS_SCORE_TABLE );
                  else {
                        set_state( CS_PLAY ); /* one player starts immediately */
                        stk_display_update( STK_UPDATE_ALL );
                  }
                  break;
            
                case CS_CONFIRM_CONTINUE:
            case CS_CONFIRM_QUIT:
            case CS_CONFIRM_WARP:
            case CS_CONFIRM_RESTART:
                  if ( key_pressed == 0 ) break;
                  if ( key_pressed==SDLK_n||key_pressed==SDLK_ESCAPE ) {
                            /* if denying continue... DIE!!! */
                            if ( client_state == CS_CONFIRM_CONTINUE )
                            {
                        SDL_Delay(250); /* give time to release button */
                        set_state( CS_NEXT_PLAYER );
                                //set_state( CS_FINAL_PLAYER_INFO );
                            }
                            else
                        set_state( CS_PLAY );
                      break;
                  }
                  if ( key_pressed != SDLK_y && key_pressed != SDLK_z ) break;
                  /* handle confirmed action */
                  SDL_Delay(250); /* give time to release button */
                  switch( client_state ) {
                                case CS_CONFIRM_CONTINUE:
                                    /* clear score and give full lives again */
                                    cur_player->lives = game->diff->lives;
                                    cur_player->stats.total_score = 0;
                                    set_state( CS_NEXT_PLAYER );
                                    break;
                        case CS_CONFIRM_QUIT:
                              comm_send_short( MSG_QUIT_GAME );
                              if ( game->game_type == GT_LOCAL ) {
                                    /* apply paddle stats to player */
                                    game_set_current( local_game );
                                    game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
                                    game_set_current( game );
                                                /* no higscore check anymore as game is supposed to
                                                 * be resumed until normal game over */
                                    /* testing levels don't got for
                                     * high scores ***
                                    if ( game_set ) {
                                          check_highscores();
                                          select_chart( game_set->name, 0 );
                                    }*/
                                                /* save local game */
                                                if ( game_set != 0 /*not testing a level*/ )
                                                    save_local_game( 0 );
                                    
                                                abort = 1;
                              }
                              else {
                                    /* await game stats */
                                    set_state( CS_RECV_STATS );
                                    display_text( font, "Receiving final stats..." );
                              }
                              break;
                        case CS_CONFIRM_WARP:
                              game->winner = -1; /* no speech */
                              local_game->winner = -1; /* not counted as win */
                                        /* substract doubled score of remaining bricks */
                                        penalty = 0;
                                        for ( i = 0; i < MAP_WIDTH; i++ )
                                            for ( j = 0; j < MAP_HEIGHT; j++ )
                                                if ( local_game->bricks[i][j].dur != -1 )
                                                    penalty += local_game->bricks[i][j].score;
                                        printf( "warp penalty: -%d\n", penalty );
                                        local_game->paddles[0]->score -= penalty;
                              set_state( CS_NEXT_LEVEL );
                              break;
                        case CS_CONFIRM_RESTART:
                              game->winner = -1; /* no speech */
                              local_game->winner = -1; /* not counted as win */
                              local_game->level_over = 1;
                              set_state( CS_RESTART_LEVEL );
                              break;
                  }
                  break;

            }

            /* update anything that was changed */
            stk_display_update( STK_UPDATE_RECTS );

            /* get time since last call and delay if below frame_delay */
            ms = stk_timer_get_time();
            if ( ms < frame_delay ) {
                  SDL_Delay( frame_delay - ms );
                  ms += stk_timer_get_time();
            }
            frames++;
      }
      finalize_level();
      client_state = CLIENT_NONE;

      stk_display_fade( STK_FADE_OUT, STK_FADE_DEFAULT_TIME );
      if ( stk_quit_request )
            comm_send_short( MSG_DISCONNECT );
      else
            comm_send_short( MSG_UNHIDE );

      /* frame rate */
      frame_time = SDL_GetTicks() - frame_time;
      printf( "Time: %.2f, Frames: %i -> FPS: %.2f\n", 
            (double)frame_time / 1000, frames, 1000.0*frames/frame_time );

      event_clear_sdl_queue();

      /* update the selected user and the user list in network as 
       * we received ADD/REMOVE_USER messages */
      gui_list_update( list_users, client_users->count );
      /* re-select current entry */
      if ( client_user ) {
            i = list_check( client_users, client_user );
            if ( i != -1 )
                  gui_list_select( list_users, 0, i, 1 );
      }
}

/* test a level until all balls got lost */
void client_game_test_level( Level *level )
{
      stk_display_fade( STK_FADE_IN, STK_FADE_DEFAULT_TIME );
      client_game_init_testing( level );
      client_game_run();
      client_game_finalize();
      stk_display_fade( STK_FADE_OUT, STK_FADE_DEFAULT_TIME );
}



Generated by  Doxygen 1.6.0   Back to index