File: game.c

package info (click to toggle)
lbreakout2 2.6.5-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster
  • size: 8,888 kB
  • sloc: ansic: 29,983; sh: 4,352; makefile: 958; yacc: 288; sed: 16
file content (494 lines) | stat: -rw-r--r-- 15,270 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
/***************************************************************************
                          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, 4 },
    { 6,  9,  0,  2,  8,  8, 0.24, 0.0016, 0.40, 1800,  1, 1 },
    { 5,  7,  0,  1,  6, 10, 0.27, 0.0016, 0.43, 1800,  1, 1 },
    { 4,  5,  0,  1,  4, 13, 0.30, 0.0015, 0.45, 1800,  1, 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, 1 },
    { 5, 7, 1, 2, 6, 10, 0.21, 0.0012, 0.33, 1800, 1, 1 },
    { 4, 5, 1, 2, 4, 13, 0.24, 0.0011, 0.35, 1800, 1, 1 }
};

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

static void game_setup_title_and_author( Game *game, Level *level )
{
  switch (level->type)
    {
    case LT_NORMAL:
      snprintf( game->title, 32, "%s", level->name );
      snprintf( game->author, 32, "%s", level->author );
      break;
    case LT_JUMPING_JACK:
      snprintf( game->title, 32, _("Jumping Jack") );
      snprintf( game->author, 32, _("Bonus Level") );
      break;
    case LT_OUTBREAK:
        snprintf( game->title, 32, _("Outbreak") );
        snprintf( game->author, 32, _("Bonus Level") );
        break;
    case LT_BARRIER:
        snprintf( game->title, 32, _("Barrier") );
        snprintf( game->author, 32, _("Bonus Level") );
        break;
    case LT_SITTING_DUCKS:
        snprintf( game->title, 32, _("Sitting Ducks") );
        snprintf( game->author, 32, _("Bonus Level") );
        break;
    case LT_HUNTER:
        snprintf( game->title, 32, _("Hunter") );
        snprintf( game->author, 32, _("Bonus Level") );
        break;
    case LT_DEFENDER:
        snprintf( game->title, 32, _("Defender") );
        snprintf( game->author, 32, _("Bonus Level") );
        break;
    default:
      snprintf( game->title, 32, _("Unknown Level Type") );
      snprintf( game->author, 32, "???" );
      break;
    }
}

/***** 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 );

    /* delete bonus level stuff */
    if (game->blDuckPositions) free(game->blDuckPositions);
	if (game->blInvaders) free(game->blInvaders);
    
    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;
  game->isBonusLevel = 0;
  game->totalBonusLevelScore = 0;

  /* set title and author */
  game_setup_title_and_author( game, level );

  /* set level type. is level::type except for normal level with no bricks: pingpong */
  if (level->type==LT_NORMAL && game->game_type == GT_NETWORK && game->brick_count == 0 )
    game->level_type = LT_PINGPONG;
  else
  {
      game->level_type = level->type;
      if (game->level_type!=LT_NORMAL) game->isBonusLevel = 1;
  }
  
  /* clear extras */
  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 );
  }
  
  /* do bricks as last to have influence on balls to keep bonus level stuff in one place */
  /* setup bricks (from level data or from special level type; this includes setting the
     bonus level data if any */
  bricks_init( game, game->game_type, level, game->diff->score_mod, game->rel_warp_limit );
    
}

/* 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 whether to use convex paddle */
void game_set_convex_paddle( int convex )
{
	cur_game->paddle_is_convex = convex;
}

/* set whether 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 whether balls are fired at random angle or whether 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 whether 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_PINGPONG ) {
			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->isBonusLevel )
				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++;
}