1 /***************************************************************************
2                           extras.c  -  description
3                              -------------------
4     begin                : Sun Sep 9 2001
5     copyright            : (C) 2001 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 #include "../client/lbreakout.h"
19 #include "levels.h"
20 #include "paddle.h"
21 #include "bricks.h"
22 #include "balls.h"
23 #include "mathfuncs.h"
24 #include "extras.h"
25 
26 extern int ball_w, ball_dia;
27 extern Game *cur_game;
28 
29 /*
30 ====================================================================
31 Locals
32 ====================================================================
33 */
34 
35 /*
36 ====================================================================
37 Public
38 ====================================================================
39 */
40 
41 /*
42 ====================================================================
43 Create new extra at position
44 ====================================================================
45 */
extra_create(int type,int x,int y,int dir)46 Extra *extra_create( int type, int x, int y, int dir )
47 {
48 	Extra *e = salloc( 1, sizeof( Extra ) );
49 	e->type = type;
50 	e->offset = type * BRICK_WIDTH;
51 	e->x = x; e->y = y;
52 	e->dir = dir;
53 	e->alpha = 0;
54 	return e;
55 }
56 
57 /*
58 ====================================================================
59 Use extra when paddle collected it
60 ====================================================================
61 */
extra_use(Paddle * paddle,int extra_type)62 void extra_use( Paddle *paddle, int extra_type )
63 {
64 	Ball 	*b;
65 	int 	i, j;
66 	int 	tm = cur_game->diff->time_mod; /* time modifier */
67 
68 	printf("%d\n",tm);
69 
70 	if ( cur_game->diff->allow_maluses ) {
71 		while( extra_type == EX_RANDOM )
72 			extra_type = rand() % (EX_NUMBER);
73 	} else {
74 		while ( extra_type == EX_RANDOM || extra_is_malus( extra_type ) )
75 			extra_type = rand() % (EX_NUMBER);
76 	}
77 
78 	/* store modification */
79 	i = cur_game->paddles[0]==paddle?0:1;
80 	if ( cur_game->mod.collected_extra_count[i] < MAX_MODS )
81 		cur_game->mod.collected_extras[i][cur_game->mod.collected_extra_count[i]++] =
82 			extra_type;
83 	/* statistics */
84 	paddle->extras_collected++;
85 
86 	switch (extra_type) {
87 		case EX_SCORE200:
88 			paddle->score += cur_game->diff->score_mod * 200 / 10;
89 			break;
90 		case EX_SCORE500:
91 			paddle->score += cur_game->diff->score_mod * 500 / 10;
92 			break;
93 		case EX_SCORE1000:
94 			paddle->score += cur_game->diff->score_mod * 1000 / 10;
95 			break;
96 		case EX_SCORE2000:
97 			paddle->score += cur_game->diff->score_mod * 2000 / 10;
98 			break;
99 		case EX_SCORE5000:
100 			paddle->score += cur_game->diff->score_mod * 5000 / 10;
101 			break;
102 		case EX_SCORE10000:
103 			paddle->score += cur_game->diff->score_mod * 10000 / 10;
104 			break;
105 		case EX_GOLDSHOWER:
106 			paddle->extra_time[EX_GOLDSHOWER] += TIME_GOLDSHOWER * tm;
107 			paddle->extra_active[EX_GOLDSHOWER] = 1;
108 			break;
109 		case EX_LIFE:
110 			/* adding life is handled by client */
111 			break;
112 		case EX_SHORTEN:
113 			paddle_init_resize( paddle, -1);
114 			break;
115 		case EX_LENGTHEN:
116 			paddle_init_resize( paddle, 1);
117 			break;
118 		case EX_BALL:
119 			b = ball_create(
120 				paddle->x + (paddle->w - ball_w) / 2,
121 				paddle->y + ((paddle->type == PADDLE_TOP)?paddle->h:-ball_dia) );
122 			b->paddle = paddle;
123 			ball_set_random_angle( b, cur_game->ball_v );
124 			b->get_target = 1;
125 			list_add( cur_game->balls, b );
126 			break;
127 		case EX_WALL:
128 			paddle->extra_time[EX_WALL] += TIME_WALL * tm;
129 			if ( paddle->extra_active[EX_WALL] ) break;
130 			paddle->extra_active[extra_type] = 1;
131 			if ( paddle->wall_y == 0 ) {
132 				for (i = 1; i < MAP_WIDTH - 1; i++) {
133 					cur_game->bricks[i][0].type = MAP_WALL;
134 					cur_game->bricks[i][0].id = 0;
135 				}
136 			}
137 			else
138 				for (i = 1; i < MAP_WIDTH - 1; i++) {
139 					cur_game->bricks[i][MAP_HEIGHT - 1].type = MAP_WALL;
140 					cur_game->bricks[i][MAP_HEIGHT - 1].id = 0;
141 				}
142 			paddle->wall_alpha = 0;
143 			balls_check_targets( -1, 0 );
144 			break;
145 		case EX_METAL:
146 			cur_game->extra_time[EX_METAL] += TIME_METAL * tm;
147 			cur_game->extra_active[extra_type] = 1;
148 			balls_set_type( BALL_METAL );
149 			/* other ball extras are disabled */
150 			if ( cur_game->extra_active[EX_EXPL_BALL] ) {
151 				cur_game->extra_active[EX_EXPL_BALL] = 0;
152 				cur_game->extra_time[EX_EXPL_BALL] = 0;
153 			}
154 			if ( cur_game->extra_active[EX_WEAK_BALL] ) {
155 				cur_game->extra_active[EX_WEAK_BALL] = 0;
156 				cur_game->extra_time[EX_WEAK_BALL] = 0;
157 			}
158 			break;
159 		case EX_FROZEN:
160 			paddle->extra_time[EX_FROZEN] = TIME_FROZEN * tm;
161 			paddle->extra_active[extra_type] = 1;
162 			paddle_freeze( paddle, 1 );
163 			break;
164 		case EX_WEAPON:
165 			paddle->extra_time[EX_WEAPON] += TIME_WEAPON * tm;
166 			paddle->extra_active[extra_type] = 1;
167 			weapon_install( paddle, 1 );
168 			break;
169 		case EX_SLIME:
170 			paddle->extra_time[EX_SLIME] += TIME_SLIME * tm;
171 			paddle->extra_active[extra_type] = 1;
172 			paddle_set_slime( paddle, 1 );
173 			break;
174 		case EX_FAST:
175 			if ( cur_game->extra_active[EX_SLOW] ) {
176 				cur_game->extra_time[EX_SLOW] = 0;
177 				cur_game->extra_active[EX_SLOW] = 0;
178 			}
179 			cur_game->extra_time[EX_FAST] += TIME_FAST * tm;
180 			cur_game->extra_active[extra_type] = 1;
181                         cur_game->ball_v = cur_game->ball_v_max;
182                         balls_set_velocity( cur_game->balls, cur_game->ball_v );
183 			break;
184 		case EX_SLOW:
185 			if ( cur_game->extra_active[EX_FAST] ) {
186 				cur_game->extra_time[EX_FAST] = 0;
187 				cur_game->extra_active[EX_FAST] = 0;
188 			}
189 			cur_game->extra_time[EX_SLOW] += TIME_SLOW * tm;
190 			cur_game->extra_active[extra_type] = 1;
191 			cur_game->ball_v = cur_game->ball_v_min;
192                         balls_set_velocity( cur_game->balls, cur_game->ball_v );
193 			break;
194 		case EX_CHAOS:
195 			cur_game->extra_time[EX_CHAOS] += TIME_CHAOS * tm;
196 			cur_game->extra_active[extra_type] = 1;
197 			balls_set_chaos( 1 );
198 			break;
199 		case EX_DARKNESS:
200 			cur_game->extra_time[EX_DARKNESS] += TIME_DARKNESS * tm;
201 			cur_game->extra_active[extra_type] = 1;
202 			break;
203 		case EX_GHOST_PADDLE:
204 			paddle->extra_time[EX_GHOST_PADDLE] += TIME_GHOST_PADDLE * tm;
205 			paddle->extra_active[extra_type] = 1;
206 			paddle_set_invis( paddle, 1 );
207 			break;
208 		case EX_TIME_ADD:
209 			for ( i = 0; i < EX_NUMBER; i++ )
210 				if ( cur_game->extra_time[i] )
211 					cur_game->extra_time[i] += 7000 * tm;
212 			for ( i = 0; i < EX_NUMBER; i++ ) {
213 				for ( j = 0; j < cur_game->paddle_count; j++ )
214 					if ( cur_game->paddles[j]->extra_time[i] )
215 						cur_game->paddles[j]->extra_time[i] += 7000 * tm;
216 			}
217 			break;
218 		case EX_EXPL_BALL:
219 			balls_set_type( BALL_EXPL );
220 			cur_game->extra_time[EX_EXPL_BALL] += TIME_EXPL_BALL * tm;
221 			cur_game->extra_active[extra_type] = 1;
222 			/* other ball extras are disabled */
223 			if ( cur_game->extra_active[EX_METAL] ) {
224 				cur_game->extra_active[EX_METAL] = 0;
225 				cur_game->extra_time[EX_METAL] = 0;
226 			}
227 			if ( cur_game->extra_active[EX_WEAK_BALL] ) {
228 				cur_game->extra_active[EX_WEAK_BALL] = 0;
229 				cur_game->extra_time[EX_WEAK_BALL] = 0;
230 			}
231 			break;
232 		case EX_WEAK_BALL:
233 			balls_set_type( BALL_WEAK );
234 			cur_game->extra_time[EX_WEAK_BALL] += TIME_WEAK_BALL * tm;
235 			cur_game->extra_active[extra_type] = 1;
236 			/* other ball extras are disabled */
237 			if ( cur_game->extra_active[EX_METAL] ) {
238 				cur_game->extra_active[EX_METAL] = 0;
239 				cur_game->extra_time[EX_METAL] = 0;
240 			}
241 			if ( cur_game->extra_active[EX_EXPL_BALL] ) {
242 				cur_game->extra_active[EX_EXPL_BALL] = 0;
243 				cur_game->extra_time[EX_EXPL_BALL] = 0;
244 			}
245 			break;
246 		case EX_BONUS_MAGNET:
247 			paddle_set_attract( paddle, ATTRACT_BONUS );
248 			paddle->extra_time[EX_BONUS_MAGNET] += TIME_BONUS_MAGNET * tm;
249 			paddle->extra_active[extra_type] = 1;
250 			if ( paddle->extra_active[EX_MALUS_MAGNET] ) {
251 				paddle->extra_active[EX_MALUS_MAGNET] = 0;
252 				paddle->extra_time[EX_MALUS_MAGNET] = 0;
253 			}
254 			break;
255 		case EX_MALUS_MAGNET:
256 			paddle_set_attract( paddle, ATTRACT_MALUS );
257 			paddle->extra_time[EX_MALUS_MAGNET] += TIME_MALUS_MAGNET * tm;
258 			paddle->extra_active[extra_type] = 1;
259 			if ( paddle->extra_active[EX_BONUS_MAGNET] ) {
260 				paddle->extra_active[EX_BONUS_MAGNET] = 0;
261 				paddle->extra_time[EX_BONUS_MAGNET] = 0;
262 			}
263 			break;
264 		case EX_DISABLE:
265 			/* set all active extra times to 1 so they will expire next
266 			   prog cycle */
267 			for ( i = 0; i < EX_NUMBER; i++ )
268 				if ( cur_game->extra_time[i] )
269 					cur_game->extra_time[i] = 1;
270 			for ( i = 0; i < EX_NUMBER; i++ ) {
271 				for ( j = 0; j < cur_game->paddle_count; j++ )
272 					if ( cur_game->paddles[j]->extra_time[i] )
273 						cur_game->paddles[j]->extra_time[i] = 1;
274 			}
275 			break;
276 		default:
277 			/* it wasn't used so delete mod */
278 			i = cur_game->paddles[0]==paddle?0:1;
279 			cur_game->mod.collected_extra_count[i]--;
280 			break;
281 
282 	}
283 }
284 /*
285 ====================================================================
286 Update extras
287 ====================================================================
288 */
extras_update(int ms)289 void extras_update( int ms )
290 {
291 	Extra       *ex;
292 	int i, j;
293 	int magnets;
294 	Paddle *magnet;
295 
296 	/* check extra_time of limited extras */
297 
298 	/* general extras */
299 	for ( i = 0; i < EX_NUMBER; i++ )
300 		if ( cur_game->extra_time[i] )
301 		if ( (cur_game->extra_time[i] -= ms) <= 0 ) {
302 			cur_game->extra_time[i] = 0;
303 			/* expired */
304 			switch ( i ) {
305 				case EX_EXPL_BALL:
306 				case EX_WEAK_BALL:
307 				case EX_METAL:
308 					balls_set_type( BALL_NORMAL );
309 					break;
310 				case EX_SLOW:
311 				case EX_FAST:
312 					cur_game->ball_v = cur_game->diff->v_start +
313 						cur_game->diff->v_add * cur_game->speedup_level;
314                                         balls_set_velocity( cur_game->balls, cur_game->ball_v );
315 					break;
316 				case EX_CHAOS:
317 					balls_set_chaos( 0 );
318 					break;
319 			}
320 			/* set deactivated */
321 			cur_game->extra_active[i] = 0;
322 		}
323 
324 	/* paddlized extras */
325 	for ( j = 0; j < cur_game->paddle_count; j++ )
326 	for ( i = 0; i < EX_NUMBER; i++ )
327 		/* extra_time of wall is updated in wall_update() */
328 		if ( cur_game->paddles[j]->extra_time[i] && i != EX_WALL )
329 		if ( (cur_game->paddles[j]->extra_time[i] -= ms) <= 0 ) {
330 			cur_game->paddles[j]->extra_time[i] = 0;
331 			/* expired */
332 			switch ( i ) {
333 				case EX_SLIME:
334 				        paddle_set_slime( cur_game->paddles[j], 0 );
335 					/* release all balls from paddle */
336 					balls_detach_from_paddle( cur_game->paddles[j],
337 								  ((rand()%2==1)?-1:1) );
338 					break;
339 				case EX_WEAPON: weapon_install( cur_game->paddles[j], 0 ); break;
340 				case EX_FROZEN:
341 					paddle_freeze( cur_game->paddles[j], 0 );
342 					break;
343 				case EX_GHOST_PADDLE:
344 					paddle_set_invis( cur_game->paddles[j], 0 );
345 					break;
346 				case EX_BONUS_MAGNET:
347 				case EX_MALUS_MAGNET:
348 					paddle_set_attract( cur_game->paddles[j], ATTRACT_NONE );
349 					break;
350 			}
351 			/* set deactivated */
352 			cur_game->paddles[j]->extra_active[i] = 0; /* wall is handled in wall_...() */
353 		}
354 
355 	/* move extras and check if paddle was hit */
356 	list_reset( cur_game->extras );
357 	while ( ( ex = list_next( cur_game->extras ) ) ) {
358 		/* if only one paddle has a magnet active all extras will
359 		 * be attracted by this paddle else the extras 'dir' is used
360 		 */
361 		magnets = 0; magnet = 0;
362 		for ( i = 0; i < cur_game->paddle_count; i++ )
363 			if ( paddle_check_attract( cur_game->paddles[i], ex->type ) ) {
364 				magnets++;
365 				magnet = cur_game->paddles[i]; /* last magnet */
366 			}
367 		if ( magnets != 1 ) {
368 			/* either no or more than one magnet so use default */
369 			if ( ex->dir > 0 )
370 				ex->y += 0.05 * ms;
371 			else
372 				ex->y -= 0.05 * ms;
373 		}
374 		else {
375 			/* 'magnet' is the paddle that will attract this extra */
376 			if ( magnet->type == PADDLE_TOP )
377 				ex->y -= 0.05 * ms;
378 			else
379 				ex->y += 0.05 * ms;
380 			if ( ex->x + ( BRICK_WIDTH >> 1 ) < magnet->x + ( magnet->w >> 1 ) ) {
381 				ex->x += 0.05 * ms;
382 				if ( ex->x + ( BRICK_WIDTH >> 1 ) > magnet->x + ( magnet->w >> 1 ) )
383 					ex->x = magnet->x + ( magnet->w >> 1 ) - ( BRICK_WIDTH >> 1 );
384 			}
385 			else {
386 				ex->x -= 0.05 * ms;
387 				if ( ex->x + ( BRICK_WIDTH >> 1 ) < magnet->x + ( magnet->w >> 1 ) )
388 					ex->x = magnet->x + ( magnet->w >> 1 ) - ( BRICK_WIDTH >> 1 );
389 			}
390 		}
391 		/* if out of screen, kill this extra */
392 		if ( ex->y >= 480 || ex->y + BRICK_HEIGHT < 0 ) {
393 			list_delete_current( cur_game->extras );
394 			continue;
395 		}
396 		for ( j = 0; j < cur_game->paddle_count; j++ ) {
397 			/* contact with paddle core ? */
398 			if ( paddle_solid( cur_game->paddles[j] ) )
399 			if ( ex->x + BRICK_WIDTH > cur_game->paddles[j]->x )
400 			if ( ex->x < cur_game->paddles[j]->x + cur_game->paddles[j]->w - 1 )
401 			if ( ex->y + BRICK_HEIGHT > cur_game->paddles[j]->y )
402 			if ( ex->y < cur_game->paddles[j]->y + cur_game->paddles[j]->h ) {
403 				/* any extra except EX_JOKER is simply used */
404 				if ( ex->type != EX_JOKER ) {
405 					extra_use( cur_game->paddles[j], ex->type );
406 					list_delete_current( cur_game->extras );
407 					break;
408 				}
409 				/* use EX_JOKER and work through all active extras */
410 				/* the mod is only stored to play the sound */
411 				if ( cur_game->mod.collected_extra_count[j] < MAX_MODS )
412 					cur_game->mod.collected_extras[j][cur_game->mod.collected_extra_count[j]++] = EX_JOKER;
413 				list_reset( cur_game->extras );
414 				while ( ( ex = list_next( cur_game->extras ) ) ) {
415 					if ( ex->type != EX_JOKER )
416 					if ( ex->type != EX_SHORTEN )
417 					if ( ex->type != EX_FROZEN )
418 					if ( ex->type != EX_FAST )
419 					if ( ex->type != EX_RANDOM )
420 					if ( ex->type != EX_DARKNESS )
421 					if ( ex->type != EX_GHOST_PADDLE )
422 					if ( ex->type != EX_CHAOS )
423 					if ( ex->type != EX_DISABLE )
424 					if ( ex->type != EX_MALUS_MAGNET )
425 					if ( ex->type != EX_WEAK_BALL ) {
426 						extra_use( cur_game->paddles[j], ex->type );
427 						extra_use( cur_game->paddles[j], ex->type );
428 					}
429 					list_delete_current( cur_game->extras );
430 				}
431 				break;
432 			}
433 		}
434 	}
435 }
436 
437 /* wall */
walls_update(int ms)438 void walls_update( int ms )
439 {
440 	int i, j;
441 
442 	for ( j = 0; j < cur_game->paddle_count; j++ )
443 		if ( cur_game->paddles[j]->extra_active[EX_WALL] ) {
444 			if ( cur_game->paddles[j]->extra_time[EX_WALL] > 0 ) {
445 				if ( (cur_game->paddles[j]->extra_time[EX_WALL] -= ms) < 0 )
446 					cur_game->paddles[j]->extra_time[EX_WALL] = 0;
447 				/* still appearing? */
448 				if (cur_game->paddles[j]->wall_alpha < 255)
449 				if ( (cur_game->paddles[j]->wall_alpha += 0.25 * ms) > 255 )
450 					cur_game->paddles[j]->wall_alpha = 255;
451 			}
452 			else
453 			if ( (cur_game->paddles[j]->wall_alpha -= 0.25 * ms) < 0 ) {
454 				cur_game->paddles[j]->wall_alpha = 0;
455 				cur_game->paddles[j]->extra_active[EX_WALL] = 0;
456 				if ( cur_game->paddles[j]->wall_y == 0 )
457 					for (i = 1; i < MAP_WIDTH - 1; i++)
458 						cur_game->bricks[i][0].type = MAP_EMPTY;
459 				else
460 					for (i = 1; i < MAP_WIDTH - 1; i++)
461 						cur_game->bricks[i][MAP_HEIGHT - 1].type = MAP_EMPTY;
462 				balls_check_targets( -1, 0 );
463 			}
464 		}
465 }
466 
extra_is_malus(int type)467 int extra_is_malus( int type )
468 {
469 	if ( type == EX_SHORTEN ) return 1;
470 	if ( type == EX_FROZEN ) return 1;
471 	if ( type == EX_FAST ) return 1;
472 	if ( type == EX_DARKNESS ) return 1;
473 	if ( type == EX_GHOST_PADDLE ) return 1;
474 	if ( type == EX_CHAOS ) return 1;
475 	if ( type == EX_DISABLE ) return 1;
476 	if ( type == EX_MALUS_MAGNET ) return 1;
477 	if ( type == EX_WEAK_BALL ) return 1;
478 	return 0;
479 }
480 
481