1 /***************************************************************************
2                           game.c  -  description
3                              -------------------
4     begin                : 03/03/19
5     copyright            : (C) 2003 by Michael Speck
6     email                : kulkanie@gmx.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 /***** INCLUDES ************************************************************/
19 
20 #include "../client/lbreakout.h"
21 #include "game.h"
22 #include "bricks.h"
23 #include "paddle.h"
24 #include "balls.h"
25 #include "shots.h"
26 #include "extras.h"
27 
28 /***** EXTERNAL VARIABLES **************************************************/
29 
30 extern int ball_w, ball_dia;
31 
32 /***** EXPORTS *************************************************************/
33 
34 Game *cur_game = 0;
35 
36 /***** FORWARDED DECLARATIONS **********************************************/
37 
38 /***** LOCAL TYPE DEFINITIONS **********************************************/
39 
40 /***** LOCAL VARIABLES *****************************************************/
41 
42 static GameDiff diffs[DIFF_COUNT] = {
43     { 9, 12,  8, 12, 20,  5, 0.10, 0.0016, 0.20, 32000, 0, 4 },
44     { 6,  9,  0,  2,  8,  8, 0.24, 0.0016, 0.40, 1800,  1, 1 },
45     { 5,  7,  0,  1,  6, 10, 0.27, 0.0016, 0.43, 1800,  1, 1 },
46     { 4,  5,  0,  1,  4, 13, 0.30, 0.0015, 0.45, 1800,  1, 1 }
47 };
48 
49 /* in network game the ball is slower and the paddle is bigger */
50 static GameDiff net_diffs[DIFF_COUNT] = {
51     { 6, 9, 1, 2, 8, 8,  0.18, 0.0012, 0.30, 1800, 1, 1 },
52     { 5, 7, 1, 2, 6, 10, 0.21, 0.0012, 0.33, 1800, 1, 1 },
53     { 4, 5, 1, 2, 4, 13, 0.24, 0.0011, 0.35, 1800, 1, 1 }
54 };
55 
56 /***** LOCAL FUNCTIONS *****************************************************/
57 
game_setup_title_and_author(Game * game,Level * level)58 static void game_setup_title_and_author( Game *game, Level *level )
59 {
60   switch (level->type)
61     {
62     case LT_NORMAL:
63       snprintf( game->title, 32, "%s", level->name );
64       snprintf( game->author, 32, "%s", level->author );
65       break;
66     case LT_JUMPING_JACK:
67       snprintf( game->title, 32, _("Jumping Jack") );
68       snprintf( game->author, 32, _("Bonus Level") );
69       break;
70     case LT_OUTBREAK:
71         snprintf( game->title, 32, _("Outbreak") );
72         snprintf( game->author, 32, _("Bonus Level") );
73         break;
74     case LT_BARRIER:
75         snprintf( game->title, 32, _("Barrier") );
76         snprintf( game->author, 32, _("Bonus Level") );
77         break;
78     case LT_SITTING_DUCKS:
79         snprintf( game->title, 32, _("Sitting Ducks") );
80         snprintf( game->author, 32, _("Bonus Level") );
81         break;
82     case LT_HUNTER:
83         snprintf( game->title, 32, _("Hunter") );
84         snprintf( game->author, 32, _("Bonus Level") );
85         break;
86     case LT_DEFENDER:
87         snprintf( game->title, 32, _("Defender") );
88         snprintf( game->author, 32, _("Bonus Level") );
89         break;
90     default:
91       snprintf( game->title, 32, _("Unknown Level Type") );
92       snprintf( game->author, 32, "???" );
93       break;
94     }
95 }
96 
97 /***** PUBLIC FUNCTIONS ****************************************************/
98 
99 /* create/delete game context */
game_create(int game_type,int diff,int rel_warp_limit)100 Game *game_create( int game_type, int diff, int rel_warp_limit )
101 {
102 	Game *game = salloc( 1, sizeof( Game ) );
103 
104 	/* set diff and game type */
105 	game->game_type = game_type;
106 	if ( game_type == GT_LOCAL )
107 		game->diff = &diffs[diff];
108 	else
109 		game->diff = &net_diffs[diff];
110 	game->rel_warp_limit = rel_warp_limit;
111 
112 	/* create lists */
113 	game->shots = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
114 	game->exp_bricks = list_create( LIST_NO_AUTO_DELETE, LIST_NO_CALLBACK );
115 	game->heal_bricks = list_create( LIST_NO_AUTO_DELETE, LIST_NO_CALLBACK );
116 	game->extras = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
117 	game->balls = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
118 
119 	/* set ball speed */
120 	game->ball_v_min = game->diff->v_start;
121 	game->ball_v_max = game->diff->v_max;
122 	delay_set( &game->speedup_delay, game->diff->v_delay );
123 
124 	/* create paddles */
125 	game->paddle_count = (game_type==GT_NETWORK)?2:1;
126 	/* bottom */
127         game->paddles[PADDLE_BOTTOM] = paddle_create( 0, PADDLE_BOTTOM,
128 		(MAP_HEIGHT-2)*BRICK_HEIGHT,
129 		game->diff->paddle_size,
130 		game->diff->paddle_min_size, game->diff->paddle_max_size,
131 		0 );
132 	/* top */
133 	if ( game_type == GT_NETWORK )
134         	game->paddles[PADDLE_TOP] = paddle_create( 0, PADDLE_TOP,
135 			BRICK_HEIGHT+2,
136 			game->diff->paddle_size,
137 			game->diff->paddle_min_size, game->diff->paddle_max_size,
138 			0 );
139 
140 	return game;
141 }
game_delete(Game ** _game)142 void game_delete( Game **_game )
143 {
144 	Game *game = *_game;
145 	int i;
146 
147 	if ( game == 0 ) return;
148 
149 	/* delete paddles */
150 	for ( i = 0; i < game->paddle_count; i++ )
151 		paddle_delete( game->paddles[i] );
152 
153 	/* delete lists */
154 	if ( game->shots ) list_delete( game->shots );
155 	if ( game->exp_bricks ) list_delete( game->exp_bricks );
156 	if ( game->heal_bricks ) list_delete( game->heal_bricks );
157 	if ( game->extras ) list_delete( game->extras );
158 	if ( game->balls ) list_delete( game->balls );
159 
160     /* delete bonus level stuff */
161     if (game->blDuckPositions) free(game->blDuckPositions);
162 	if (game->blInvaders) free(game->blInvaders);
163 
164     free( game );
165 	*_game = 0;
166 }
167 
168 /* finalize single game level. the level_type is determined by
169  * counting the bricks. the data of 'level' is copied and modified
170  * while playing. */
game_init(Game * game,Level * level)171 void game_init( Game *game, Level *level )
172 {
173   int i;
174   Ball *ball;
175 
176   game->level_over = 0;
177   game->isBonusLevel = 0;
178   game->totalBonusLevelScore = 0;
179 
180   /* set title and author */
181   game_setup_title_and_author( game, level );
182 
183   /* set level type. is level::type except for normal level with no bricks: pingpong */
184   if (level->type==LT_NORMAL && game->game_type == GT_NETWORK && game->brick_count == 0 )
185     game->level_type = LT_PINGPONG;
186   else
187   {
188       game->level_type = level->type;
189       if (game->level_type!=LT_NORMAL) game->isBonusLevel = 1;
190   }
191 
192   /* clear extras */
193   memset( game->extra_active, 0, sizeof( game->extra_active ) );
194   memset( game->extra_time, 0, sizeof( game->extra_time ) );
195 
196   /* set ball speed */
197   game->ball_v = game->ball_v_min;
198   game->speedup_level = 0;
199 
200   /* clear maxballspeed_request */
201   if ( game->game_type == GT_LOCAL )
202     {
203       cur_game->paddles[0]->maxballspeed_request = 0;
204       cur_game->paddles[0]->maxballspeed_request_old = 0;
205     }
206 
207   /* attach one ball to each paddle */
208   list_clear( game->balls );
209   for ( i = 0; i < game->paddle_count; i++ ) {
210     if ( game->paddles[i]->type == PADDLE_BOTTOM )
211       ball = ball_create((game->paddles[i]->w - ball_w) / 2, -ball_dia );
212     else
213       ball = ball_create((game->paddles[i]->w - ball_w) / 2, game->paddles[i]->h );
214     ball->attached = 1;
215     ball->paddle = game->paddles[i];
216     ball->paddle->attached_ball_count = 1;
217     ball_set_random_angle( ball, game->ball_v );
218     list_add( game->balls, ball );
219   }
220 
221   /* do bricks as last to have influence on balls to keep bonus level stuff in one place */
222   /* setup bricks (from level data or from special level type; this includes setting the
223      bonus level data if any */
224   bricks_init( game, game->game_type, level, game->diff->score_mod, game->rel_warp_limit );
225 
226 }
227 
228 /* reset level/in_game data */
game_finalize(Game * game)229 void game_finalize( Game *game )
230 {
231 	int i;
232 
233 	/* reset lists */
234 	list_clear( game->balls );
235 	list_clear( game->extras );
236 	list_clear( game->shots );
237 	list_clear( game->heal_bricks );
238 	list_clear( game->exp_bricks );
239 
240 	/* reset paddles (and their statistics which are only for
241 	 * the currently played level) */
242 	for ( i = 0; i < game->paddle_count; i++ )
243 		paddle_reset( game->paddles[i] );
244 
245 	/* reset updates */
246 	game_reset_mods();
247 }
248 
249 /* set the game context the subfunctions will apply their changes to */
game_set_current(Game * game)250 void game_set_current( Game *game )
251 {
252 	cur_game = game;
253 }
254 
255 /* set score of paddle 'id'. 0 is bottom paddle and 1 is top paddle */
game_set_score(int id,int score)256 void game_set_score( int id, int score )
257 {
258 	if ( id < 0 || id >= cur_game->paddle_count ) return;
259 	cur_game->paddles[id]->score = score;
260 }
261 
262 /* set number of additional balls a paddle can fire (all paddles) */
game_set_ball_ammo(int ammo)263 void game_set_ball_ammo( int ammo )
264 {
265 	int i;
266 	for ( i = 0; i < cur_game->paddle_count; i++ ) {
267 		cur_game->paddles[i]->ball_ammo = ammo;
268 		cur_game->paddles[i]->start_ball_ammo = ammo;
269 	}
270 }
271 
272 /* set the number of points required to win a PINGPONG level */
game_set_frag_limit(int limit)273 void game_set_frag_limit( int limit )
274 {
275 	cur_game->frag_limit = limit;
276 }
277 
278 /* set wether to use convex paddle */
game_set_convex_paddle(int convex)279 void game_set_convex_paddle( int convex )
280 {
281 	cur_game->paddle_is_convex = convex;
282 }
283 
284 /* set wether balls are returned to a paddle by pressing fire.
285  * the alternative is that they automatically return. */
game_set_ball_auto_return(int auto_return)286 void game_set_ball_auto_return( int auto_return )
287 {
288 	cur_game->balls_return_by_click = !auto_return;
289 }
290 
291 /* set wether balls are fired at random angle or wether the
292  * left/right fire keys are used */
game_set_ball_random_angle(int random)293 void game_set_ball_random_angle( int random )
294 {
295 	cur_game->balls_use_random_angle = random;
296 }
297 
298 /* set the speed of balls will have in accelerated state */
game_set_ball_accelerated_speed(float speed)299 void game_set_ball_accelerated_speed( float speed )
300 {
301     cur_game->accelerated_ball_speed = speed;
302 }
303 
304 /* update state of a paddle. x or y may be 0 which is not a valid value.
305  * in this case the property is left unchanged */
game_set_paddle_state(int id,int x,int y,int left_fire,int right_fire,int return_key)306 void game_set_paddle_state( int id, int x, int y, int left_fire, int right_fire, int return_key )
307 {
308 	Paddle *paddle = 0;
309 
310 	if ( id < 0 || id >= cur_game->paddle_count ) return;
311 
312 	paddle = cur_game->paddles[id];
313 	if ( x != 0 ) { paddle->x = x; paddle->cur_x = x; }
314 	if ( y != 0 ) paddle->y = y;
315 	paddle->fire_left = left_fire;
316 	paddle->fire_right = right_fire;
317 	paddle->ball_return_key_pressed = return_key;
318 }
319 
320 /* move objects, modify game data, store brick hits and collected extras.
321  * return wether level has been finished and the id of the winning paddle
322  * in network games. -1 is a draw. level_over and winner is saved in the
323  * game struct. */
game_update(int ms)324 void game_update( int ms )
325 {
326 	int i;
327 
328 	extras_update( ms );
329 	walls_update( ms );
330 	shots_update( ms );
331 	bricks_update( ms );
332 	for ( i = 0; i < cur_game->paddle_count; i++ )
333 	  {
334 		paddle_update( cur_game->paddles[i], ms );
335 		/* release all balls from paddle if invisible */
336 		if (!paddle_solid(cur_game->paddles[i]))
337 		    balls_detach_from_paddle( cur_game->paddles[i], ((rand()%2==1)?-1:1) );
338 	  }
339 
340 	balls_update( ms );
341 
342 	/* level finished? */
343 	cur_game->level_over = 0;
344 	if ( cur_game->game_type == GT_LOCAL ) {
345 		/* local game */
346 		if ( cur_game->bricks_left == 0 ) cur_game->level_over = 1;
347 		if ( cur_game->balls->count == 0 ) cur_game->level_over = 1;
348 	} else {
349 		/* network game */
350 		if ( cur_game->level_type != LT_PINGPONG ) {
351 			if ( cur_game->bricks_left == 0 ) cur_game->level_over = 1;
352 		}
353 		else
354 		if ( cur_game->paddles[PADDLE_TOP]->score >= cur_game->frag_limit ||
355 		     cur_game->paddles[PADDLE_BOTTOM]->score >= cur_game->frag_limit )
356 			cur_game->level_over = 1;
357 	}
358 
359 	/* if so, determine winner */
360 	if ( cur_game->level_over ) {
361 		if ( cur_game->game_type == GT_LOCAL ) {
362 			if ( cur_game->bricks_left == 0 || cur_game->isBonusLevel )
363 				cur_game->winner = PADDLE_BOTTOM; /* praise */
364 			else
365 				cur_game->winner = PADDLE_TOP; /* swear */
366 		} else {
367 			cur_game->winner = PADDLE_BOTTOM;
368 			if ( cur_game->game_type == GT_NETWORK ) {
369 				if ( cur_game->paddles[PADDLE_TOP]->score >
370 						cur_game->paddles[PADDLE_BOTTOM]->score )
371 					cur_game->winner = PADDLE_TOP;
372 				else
373 					if ( cur_game->paddles[PADDLE_TOP]->score ==
374 							cur_game->paddles[PADDLE_BOTTOM]->score )
375 						cur_game->winner = -1;
376 			}
377 		}
378 	}
379 }
380 
381 /* get the modifications that occured in game_update() */
382 
383 /* get current score of player. return 0 if player does not exist */
game_get_score(int id,int * score)384 int game_get_score( int id, int *score )
385 {
386 	if ( id < 0 || id >= cur_game->paddle_count ) return 0;
387 	*score = cur_game->paddles[id]->score;
388 	return 1;
389 }
390 
391 /* get number of ball reflections */
game_get_reflected_ball_count(void)392 int game_get_reflected_ball_count( void )
393 {
394 	return cur_game->mod.brick_reflected_ball_count+
395 	       cur_game->mod.paddle_reflected_ball_count;
396 }
397 
398 /* get number of ball reflections on bricks */
game_get_brick_reflected_ball_count(void)399 int game_get_brick_reflected_ball_count( void )
400 {
401 	return cur_game->mod.brick_reflected_ball_count;
402 }
403 
404 /* get number of ball reflections on paddle */
game_get_paddle_reflected_ball_count(void)405 int game_get_paddle_reflected_ball_count( void )
406 {
407 	return cur_game->mod.paddle_reflected_ball_count;
408 }
409 
410 /* get number of newly attached balls */
game_get_attached_ball_count(void)411 int game_get_attached_ball_count( void )
412 {
413 	return cur_game->mod.attached_ball_count;
414 }
415 
416 /* get number of fired shots no matter which paddle */
game_get_fired_shot_count(void)417 int game_get_fired_shot_count( void )
418 {
419 	return cur_game->mod.fired_shot_count;
420 }
421 
422 /* hit bricks since last call to game_update() */
game_get_brick_hits(int * count)423 BrickHit *game_get_brick_hits( int *count )
424 {
425 	*count = cur_game->mod.brick_hit_count;
426 	return cur_game->mod.brick_hits;
427 }
428 
429 /* get a list of extras collected by paddle id */
game_get_collected_extras(int id,int * count)430 int *game_get_collected_extras( int id, int *count )
431 {
432 	*count = 0;
433 	if ( id < 0 || id >= cur_game->paddle_count ) return 0;
434 	*count = cur_game->mod.collected_extra_count[id];
435 	return cur_game->mod.collected_extras[id];
436 }
437 
438 /* get a snapshot of the level data which is the brick states
439  * converted to the original file format. this can be used to
440  * overwrite a levels data when player changes in alternative
441  * game */
game_get_level_snapshot(Level * shot)442 void game_get_level_snapshot( Level *shot )
443 {
444 	int i, j;
445 	int y_off;
446 
447 	if ( cur_game->game_type == GT_NETWORK )
448 		y_off = ( MAP_HEIGHT - EDIT_HEIGHT ) / 2;
449 	else
450 		y_off = 1;
451 
452 	for ( i = 0; i < EDIT_WIDTH; i++ )
453 	for ( j = 0; j < EDIT_HEIGHT; j++ ) {
454 		shot->bricks[i][j] = cur_game->bricks[i+1][j+y_off].brick_c;
455 		shot->extras[i][j] = cur_game->bricks[i+1][j+y_off].extra_c;
456 	}
457 }
458 
459 /* reset the modification of game_update() */
game_reset_mods(void)460 void game_reset_mods( void )
461 {
462 	memset( &cur_game->mod, 0, sizeof( GameMod ) );
463 }
464 
465 /* update a statistics struct by the level stats of a paddle.
466  * updates the win/loss/draw as well. the played_rounds entry
467  * is simply increased everytime this function is called */
game_update_stats(int id,GameStats * stats)468 void game_update_stats( int id, GameStats *stats )
469 {
470 	Paddle *paddle;
471 	if ( id < 0 || id >= cur_game->paddle_count ) return;
472 
473 	/* this should be called before game_finalize() as the
474 	 * stats will be cleared there */
475 	paddle = cur_game->paddles[id];
476 
477 	stats->total_score += paddle->score;
478     if ( stats->total_score < 0 ) stats->total_score = 0;
479 	stats->balls_reflected += paddle->balls_reflected;
480 	stats->balls_lost += paddle->balls_lost;
481 	stats->bricks_cleared += paddle->bricks_cleared;
482 	stats->total_brick_count += cur_game->brick_count;
483 	stats->extras_collected += paddle->extras_collected;
484 	stats->total_extra_count += cur_game->extra_count;
485 
486 	if ( cur_game->winner == -1 )
487 		stats->draws++;
488 	else
489 	if ( cur_game->winner == id )
490 		stats->wins++;
491 	else
492 		stats->losses++;
493 	stats->played_rounds++;
494 }
495