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