1 /***************************************************************************
2                           balls.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 "shots.h"
22 #include "balls.h"
23 #include "bricks.h"
24 #include "mathfuncs.h"
25 
26 #define TOARC(d) (((float)d/180)*M_PI)
27 #define TODEG(a) (((float)a/M_PI)*180)
28 #define VLEN(x, y) ( sqrt( (x)*(x) + (y)*(y) ) )
29 #define REC_SQRT_2 (0.707106781)
30 
31 float ball_vhmask = 0.363970234; /* twenty degrees */
32 float ball_vvmask = 5.67128182; /* ten degrees */
33 int ball_rad = 6;
34 int ball_dia = 12;
35 int ball_w = 12;
36 int ball_h = 12;
37 extern Game *cur_game;
38 
39 int last_ball_brick_reflect_x = -1; /* HACK: used to play local sound */
40 int last_ball_paddle_reflect_x = -1; /* HACK: used to play local sound */
41 int last_ball_attach_x = -1; /* HACK: used to play local sound */
42 
43 /*
44 ====================================================================
45 Locals
46 ====================================================================
47 */
48 
49 #ifdef WITH_BUG_REPORT
50 /*
51 ====================================================================
52 Display info about a ball's target.
53 ====================================================================
54 */
ball_print_target_info(Ball * ball)55 static void ball_print_target_info( Ball *ball )
56 {
57 	Coord  center = { ball->cur.x + ball_rad, ball->cur.y + ball_rad }; /* ball center */
58 	printf( "Target exists: %i\n", ball->target.exists );
59 	printf("Ball: %4.2f,%4.2f (%i,%i) -> %4.2f,%4.2f (%4.2f)\n",
60 			center.x, center.y, (int)center.x/BRICK_WIDTH, (int)center.y/BRICK_HEIGHT,
61 			ball->vel.x, ball->vel.y, ball->vel.y/ball->vel.x );
62 	printf("Brick %i,%i: Side %i (%4.2f,%4.2f)\n",
63 			ball->target.mx, ball->target.my, ball->target.side,
64 			ball->target.x, ball->target.y );
65 	printf("Perp Vector: %4.2f,%4.2f\n", ball->target.perp_vector.x, ball->target.perp_vector.y);
66 	printf("Takes %i ms\n", ball->target.time);
67 }
68 #endif
69 
70 /*
71 ====================================================================
72 Clear contents of target.
73 ====================================================================
74 */
ball_clear_target(Target * t)75 void ball_clear_target( Target *t )
76 {
77 	memset(t, 0, sizeof(Target));
78 	t->side = SIDE_UNDEFINED;
79 }
80 /*
81 ====================================================================
82 Attach ball to paddle.
83 ====================================================================
84 */
ball_attach(Ball * ball,Paddle * paddle)85 void ball_attach( Ball *ball, Paddle *paddle )
86 {
87 	/* relative position */
88 	ball->attached = 1;
89 	ball->paddle = paddle;
90 	ball->paddle->attached_ball_count++;
91 	ball->idle_time = ball->moving_back = ball->return_allowed = 0;
92 	ball->get_target = 0;
93 	ball->cur.x -= paddle->x;
94 	ball->cur.y -= paddle->y;
95 	ball->x = (int)ball->cur.x;
96 	ball->y = (int)ball->cur.y;
97 	cur_game->mod.attached_ball_count++;
98         last_ball_attach_x = ball->x + paddle->x;
99 }
100 /*
101 ====================================================================
102 Reflect ball at brick assume normed perp_vector.
103 ====================================================================
104 */
ball_check_brick_reflection(Ball * b)105 void ball_check_brick_reflection( Ball *b )
106 {
107 	float old_vx;
108 	Vector n;
109 	int reflect;
110 	int chaos_reflect;
111 	int hit_type;
112     Vector oldBallVel = b->vel;
113 
114 	/* time left? */
115 	if (b->target.cur_tm < b->target.time) return;
116 
117 	/* if the brick is destructible (thus it'll take damage)
118 	 * we must reset the idle time
119 	 */
120 	if ( cur_game->bricks[b->target.mx][b->target.my].dur != -1 ||
121 	    (cur_game->extra_active[EX_METAL] &&
122 	     cur_game->bricks[b->target.mx][b->target.my].type != MAP_WALL ) )
123 		b->idle_time = 0;
124 	/* or if it is within the last four rows and no chaotic penalty is active it will
125 	   hit the paddle soon so reset here too */
126 	if ( b->target.my >= MAP_HEIGHT - 4 && b->vel.y > 0 && !cur_game->extra_active[EX_CHAOS] )
127 		b->idle_time = 0;
128 
129 	/* will reflect? */
130 	reflect = 1;
131 	if ( cur_game->extra_active[EX_METAL] )
132 	if ( cur_game->bricks[b->target.mx][b->target.my].type != MAP_WALL )
133 		reflect = 0;
134 
135 	/* will reflect chaotic? */
136 	chaos_reflect = 0;
137 	if ( cur_game->extra_active[EX_CHAOS] ||
138 	     cur_game->bricks[b->target.mx][b->target.my].type == MAP_BRICK_CHAOS )
139 		chaos_reflect = 1;
140 
141 	/* we have a target and so we have a reset position and even if the ball's
142 	   not reflected the position must be reset */
143 	b->cur.x = b->target.x; b->x = (int)b->cur.x;
144 	b->cur.y = b->target.y; b->y = (int)b->cur.y;
145 
146 	if ( reflect ) {
147 		cur_game->mod.brick_reflected_ball_count++;
148                 last_ball_brick_reflect_x = b->x; /* HACK: used to play local sound */
149 		old_vx = b->vel.x;
150 		if ( !chaos_reflect ) {
151 			/* normal reflection */
152 			n.x = (1-2*b->target.perp_vector.x*b->target.perp_vector.x)*b->vel.x +
153 			      ( -2*b->target.perp_vector.x*b->target.perp_vector.y)*b->vel.y;
154 			n.y = ( -2*b->target.perp_vector.x*b->target.perp_vector.y)*b->vel.x +
155 			      (1-2*b->target.perp_vector.y*b->target.perp_vector.y)*b->vel.y;
156 			b->vel.x = n.x;
157 			b->vel.y = n.y;
158 		}
159 		else {
160 			b->vel.x = ((float)RANDOM( -10000, 10000 )) / 10000;
161 			b->vel.y = (float)(RANDOM( -10000, 10000 )) / 10000;
162 		}
163 		if ( b->target.side >= CORNER_UPPER_LEFT && !chaos_reflect )
164 			ball_mask_vel( b, old_vx, BALL_ADD_ENTROPY );
165 		else
166 			ball_mask_vel( b, old_vx, BALL_NO_ENTROPY );
167 		/* only use 2 degree steps */
168 		b->angle = vec2angle( &b->vel );
169 		angle2vec( b->angle, &b->vel );
170 		vector_set_length( &b->vel, cur_game->ball_v );
171 
172 		/* reset contact time: this ball is working for its paddle so it
173 		 * was cheating if it would be allowed to bring a new ball to
174 		 * game */
175 		b->paddle->last_ball_contact = SDL_GetTicks();
176 	}
177 
178 	/* remove brick -- if weak ball there is a 40% chance that no damage is done to the brick */
179 	if ( !cur_game->extra_active[EX_WEAK_BALL] || rand() % 10 < 6 ) {
180 		/* if explosive ball und brick is destructible by normal means set as explosive */
181 		if ( cur_game->extra_active[EX_EXPL_BALL] )
182 		if ( cur_game->bricks[b->target.mx][b->target.my].dur > 0 ) {
183 			cur_game->bricks[b->target.mx][b->target.my].type = MAP_BRICK_EXP;
184 			cur_game->bricks[b->target.mx][b->target.my].dur = 1;
185 		}
186 		/* hit brick */
187 		hit_type = SHR_BY_NORMAL_BALL;
188 		if ( cur_game->extra_active[EX_METAL] ) hit_type = SHR_BY_ENERGY_BALL;
189 		brick_hit( b->target.mx, b->target.my,
190 			cur_game->extra_active[EX_METAL],
191 			hit_type, oldBallVel, b->paddle );
192 	}
193 
194 	/* mark target as disabled so it won't get stuck at the
195 	   bottom of the screen but keep the target position so
196 	   that we know what needs an update. */
197 	b->target.exists = 0;
198 	/* check targets */
199 	balls_check_targets( b->target.mx, b->target.my );
200 	shots_check_targets( b->target.mx, b->target.my );
201 }
202 /*
203 ====================================================================
204 Handle ball's contact with paddle: reflect at perpendicular (normed)
205 or attach.
206 ====================================================================
207 */
ball_handle_paddle_contact(Ball * ball,Paddle * paddle,Vector perp_vector)208 void ball_handle_paddle_contact( Ball *ball, Paddle *paddle, Vector perp_vector )
209 {
210 	float  old_vx = ball->vel.x;
211 	Vector c; /* A(perp_vector) = c; */
212 
213 	ball->paddle = paddle;
214 
215 	/* valid perpendicular? */
216 	if ( perp_vector.x == 0 && perp_vector.y == 0 )
217 		return;
218 
219 	/* reflect */
220 	/* a simple 2x2 matrix does this for us */
221 	c.x = (1-2*perp_vector.x*perp_vector.x)*ball->vel.x +
222 	      ( -2*perp_vector.x*perp_vector.y)*ball->vel.y;
223 	c.y = ( -2*perp_vector.x*perp_vector.y)*ball->vel.x +
224 	      (1-2*perp_vector.y*perp_vector.y)*ball->vel.y;
225 	/* if this new velocity vector does not bring back the ball to the playing field
226 	   thus the lower hemispherical parts of the paddle were hit we consider this
227 	   to be no reflection at all to prevent balls from getting stuck when 'bonus floor'
228 	   is active */
229 	if ( (paddle->type == PADDLE_TOP && c.y < 0) ||
230   	     (paddle->type == PADDLE_BOTTOM && c.y > 0) )
231 		return;
232 
233 	/* set new speed vector */
234 	ball->vel.x = c.x; ball->vel.y = c.y;
235 #ifdef PADDLE_FRICTION
236 	/* transfer friction to ball's velocity if not convex */
237 	if ( cur_game->paddle_is_convex )
238 		ball->vel.x += paddle->v_x * paddle->friction;
239 #endif
240 	ball_mask_vel( ball, old_vx, BALL_NO_ENTROPY );
241 	/* only use 2 degree steps */
242 	ball->angle = vec2angle( &ball->vel );
243 	angle2vec( ball->angle, &ball->vel );
244 	vector_set_length( &ball->vel, cur_game->ball_v );
245 
246 	/* reset position if in wall */
247 	if ( ball->x < BRICK_WIDTH ) {
248 		ball->cur.x = BRICK_WIDTH;
249 		ball->x = (int)ball->cur.x;
250 	}
251 	else
252 		if ( ball->x + ball_dia >= 640 - BRICK_WIDTH ) {
253 			ball->cur.x = 640 - BRICK_WIDTH - ball_dia;
254 			ball->x = (int)ball->cur.x;
255 		}
256 	if ( paddle->extra_active[EX_WALL] ) {
257 		if ( paddle->type == PADDLE_BOTTOM ) {
258 			if ( ball->cur.y + ball_dia > 480 - BRICK_HEIGHT - 1 ) {
259 				ball->cur.y = 480 - BRICK_HEIGHT - 1 - ball_dia;
260 				ball->y = (int)ball->cur.y;
261 			}
262 		}
263 		else {
264 			if ( ball->cur.y < BRICK_HEIGHT ) {
265 				ball->cur.y = BRICK_HEIGHT;
266 				ball->y = (int)ball->cur.y;
267 			}
268 		}
269 	}
270 
271 	/* attach ball if sticky */
272 	if ( paddle_slimy( paddle ) ) {
273 		ball_attach( ball, paddle );
274 		return;
275 	}
276 
277 	/* count successful paddle contacts */
278 	paddle->balls_reflected++;
279 	cur_game->mod.paddle_reflected_ball_count++;
280         last_ball_paddle_reflect_x = ball->x; /* HACK: used to play local sound */
281 
282 	/* get new target */
283 	ball->get_target = 1;
284 
285 	/* reset contact time */
286 	paddle->last_ball_contact = SDL_GetTicks();
287 }
288 /*
289 ====================================================================
290 Berechnung der Schnittpunkte der Geraden, die orthogonal zur
291 Geraden der Ballgeschwindigkeit durch den Ballmittelpunkt verl�uft,
292 also der tangentialen Geschwindigkeitspunkte.
293 Der Geschwindigkeitsvektor wird auf 1 genormt. Ausgehend vom
294 Mittelpunkt wird der dazu orthogonale Vektor jeweils mit ge�ndertem
295 Vorzeichen addiert und ergibt so die Tangentialpunkte.
296 
297 If you're able and willing to translate this please send me your
298 result. ;-)
299 ====================================================================
300 */
ball_get_tangents(Ball * ball,Coord * left,Coord * right)301 void ball_get_tangents( Ball *ball, Coord *left, Coord *right )
302 {
303 	Vector norm_vel = ball->vel;
304 	float center_x = ball->cur.x + ball_rad, center_y = ball->cur.y + ball_rad;
305 
306 	vector_norm( &norm_vel );
307 	left->x = center_x + norm_vel.y * ball_rad;
308 	left->y = center_y - norm_vel.x * ball_rad;
309 	right->x = center_x - norm_vel.y * ball_rad;
310 	right->y = center_y + norm_vel.x * ball_rad;
311 }
312 /*
313 ====================================================================
314 Check if the ball is on paddle's level and an reflect is
315 possible.
316 ====================================================================
317 */
ball_paddle_contact_possible(Ball * ball,Paddle * paddle,Vector old)318 int ball_paddle_contact_possible( Ball *ball, Paddle *paddle, Vector old )
319 {
320 	if ( ball->attached ) return 0; /* was attached to a previous paddle */
321 	if ( !paddle_solid( paddle ) ) return 0;
322 	if ( paddle->type == PADDLE_TOP ) {
323 		if ( ball->vel.y > 0 ) return 0;
324 		if ( ball->y > paddle->y + paddle->h - 1 ) return 0; /* below paddle */
325 		if ( ball->y + ball_dia <= paddle->y + ( paddle->h >> 1 ) )
326 		if ( old.y + ball_dia <= paddle->y + ( paddle->h >> 1 ) )
327 			return 0; /* already behind paddle */
328 	}
329 	else {
330 		if ( ball->vel.y < 0 ) return 0; /* ball moves up so no contact possible because
331 						    if it was below the paddle it has been
332 						    reflected by the bonus floor and MUST ignore
333 						    the paddle */
334 		if ( ball->y + ball_dia < paddle->y ) return 0; /* above paddle */
335 		if ( ball->y >= paddle->y + ( paddle->h >> 1 ) )
336 		if ( old.y >= paddle->y + ( paddle->h >> 1 ) )
337 			return 0; /* already behind paddle */
338 	}
339 	return 1;
340 }
341 /*
342 ====================================================================
343 Check reflection of ball at paddle. 'old' is the position of
344 the ball before update. Used to compute direction.
345 ====================================================================
346 */
347 enum { CONTACT_LEFT = 1, CONTACT_MIDDLE, CONTACT_RIGHT };
ball_check_paddle_reflection(Ball * ball,Paddle * paddle)348 void ball_check_paddle_reflection( Ball *ball, Paddle *paddle )
349 {
350 	Line    ball_line; /* balls velocity line */
351 	Line    paddle_line; /* paddle line */
352 	Coord   pt, pt2; /* auxiliary point (result of intersection) */
353 	int     contact = 0; /* paddle contact */
354 	Vector  perp_vector; /* perpendicular of ball's direction change */
355 	Coord   center = { ball->cur.x + ball_rad, ball->cur.y + ball_rad }; /* center of ball */
356 	Vector  norm_vel; /* normed ball velocity vector */
357 	/* paddle is constructed as two hemispheres at the side and a cylinder in the middle */
358 	Coord   right_hemi_center = { paddle->x + paddle->w - ( paddle->h >> 1 ), paddle->y + ( paddle->h >> 1 ) };
359 	Coord   left_hemi_center = { paddle->x + ( paddle->h >> 1 ), paddle->y + ( paddle->h >> 1 ) };
360 	/* radius of hemispheres */
361 	int     hemi_r = ( ball_rad ) + ( paddle->h >> 1 );
362 	/* if paddle's treated as convex these are the perpendiculars through the hemisphere centers */
363 	Vector  left_convex_perp = { 1, (paddle->type == PADDLE_TOP)?-1:1 };
364 	/* paddle center */
365 	Coord   paddle_center = { paddle->x + ( paddle->w >> 1 ), paddle->y + ( paddle->h >> 1 ) };
366 	/*  center of the convex behaviour -- computed when reflecting by using
367 	    left/right_convex_perp and paddle_center */
368 	Coord   convex_center;
369 	/* perpendicular line used for convex behaviour */
370 	Line    convex_line;
371 
372 	/* the simple check for the y-position of ball and paddle is done
373 	 * in ball_paddle_contact_possible() so if we got here it's possible
374 	 * by velocity and position of ball that it hits the paddle
375 	 */
376 
377 	/*  basic idea:
378 	    The paddle is constructed of a middle rectangle and two hemispheres.
379 	    We check the center line of the ball with the imaginary paddle that's size
380 	    is paddle_size + ball_rad. The intersection with this paddle is the reset point
381 	    for the ball at the same time (if sticky).
382 	    The perpendicular is computed as convex thing. (overwrites the perpendicular
383 	    set by the reflection)
384 	    */
385 	/* ball line */
386 	line_set( &ball_line, center.x, center.y, ball->vel.y / ball->vel.x );
387 	/* imaginary paddle upper/lower line
388 	 * -- we'll decide at intersection which hemipshere to check
389 	 */
390 	if ( paddle->type == PADDLE_TOP )
391 		line_set_hori( &paddle_line, paddle->y + paddle->h - 1 + ball_rad );
392 	else
393 		line_set_hori( &paddle_line, paddle->y - ball_rad );
394 	line_intersect( &paddle_line, &ball_line, &pt );
395 	if ( pt.x < left_hemi_center.x ) {
396 		/* intersect left */
397 		norm_vel = ball->vel; vector_norm( &norm_vel );
398 		if ( circle_intersect( left_hemi_center, hemi_r,
399 					center, norm_vel,
400 					&pt, &pt2 ) ) {
401 			if ( VEC_DIST( center, left_hemi_center ) <= hemi_r ) {
402 				if ( paddle->type == PADDLE_TOP ) {
403 					/* use lower point as intersection */
404 					if ( pt.y < pt2.y ) pt = pt2;
405 				}
406 				else
407 					/* use the higher point as this is the upper intersection */
408 					if ( pt.y > pt2.y ) pt = pt2;
409 				/* use vector between hemi_sphere center and ball center
410 				 * as reflection perp */
411 				perp_vector = vector_get( center.x - left_hemi_center.x,
412 						center.y - left_hemi_center.y );
413 				vector_norm( &perp_vector );
414 				/* had contact */
415 				contact = CONTACT_LEFT;
416 			}
417 		}
418 	}
419 	else
420 	if ( pt.x > right_hemi_center.x ) {
421 		/* intersect right */
422 		norm_vel = ball->vel; vector_norm( &norm_vel );
423 		if ( circle_intersect( right_hemi_center, hemi_r,
424 					center, norm_vel,
425 					&pt, &pt2 ) ) {
426 			if ( VEC_DIST( center, right_hemi_center ) <= hemi_r ) {
427 				if ( paddle->type == PADDLE_TOP ) {
428 					/* use lower point as intersection */
429 					if ( pt.y < pt2.y ) pt = pt2;
430 				}
431 				else
432 					/* use the higher point as this is the upper intersection */
433 					if ( pt.y > pt2.y ) pt = pt2;
434 				/* use vector between hemi_sphere center and ball center
435 				 * as reflection perp */
436 				perp_vector = vector_get( center.x - right_hemi_center.x,
437 						center.y - right_hemi_center.y );
438 				vector_norm( &perp_vector );
439 				/* had contact */
440 				contact = CONTACT_RIGHT;
441 			}
442 		}
443 	}
444 	else {
445 		contact = CONTACT_MIDDLE; /* contact with middle part */
446 		perp_vector = vector_get( 0, 1 ); /* reflect at horizontal line */
447 	}
448 
449 	/* if we got here 'pt' contains the intersection with the imaginary paddle so reset ball
450 	   to this position */
451 	if ( contact ) {
452 		/* reset idle time */
453 		ball->idle_time = 0;
454 		/* reset position if ball will be attached */
455 		if ( paddle_slimy( paddle ) ) {
456 			ball->cur.x = pt.x - ( ball_rad );
457 			ball->cur.y = pt.y - ( ball_rad );
458 			ball->x = (int)ball->cur.x; ball->y = (int)ball->cur.y;
459 		}
460 		/* convex perpendicular */
461 		if ( cur_game->paddle_is_convex ) {
462 			line_set_vert( &paddle_line, paddle_center.x );
463 			line_set( &convex_line, left_hemi_center.x, left_hemi_center.y,
464 					vector_monotony( left_convex_perp ) );
465 			line_intersect( &paddle_line, &convex_line, &convex_center );
466 			/* get actual perp_vector */
467 			perp_vector.x = convex_center.x - pt.x;
468 			perp_vector.y = convex_center.y - pt.y;
469 			//vector_norm( &perp_vector );
470 			/* this vector is not normed but for whatever reason...
471 			   the reflection behaviour is much nicer this way */
472 		}
473 		/* handle contact: attach, reflect, sound... */
474 		ball_handle_paddle_contact( ball, paddle, perp_vector );
475 	}
476 }
477 /*
478 ====================================================================
479 Intersect ball line with imaginary brick line.
480 Use target's map position and
481 set reset position (centered) and perp_vector of target.
482 ball_rad is substracted later in ball_get_target()
483 ====================================================================
484 */
485 enum { LINE_HORI = 0, LINE_VERT };
check_line(Line * ball_line,int type,int anchor,int range_start,int range_end,Coord * pt)486 int check_line( Line *ball_line, int type, int anchor, int range_start, int range_end, Coord *pt ) {
487 	Line line;
488 	if ( type == LINE_HORI )
489 		line_set_hori( &line, anchor );
490 	else
491 		line_set_vert( &line, anchor );
492 	line_intersect( &line, ball_line, pt );
493 	if ( type == LINE_HORI ) {
494 		if ( pt->x >= range_start && pt->x <= range_end ) return 1;
495 		return 0;
496 	}
497 	else {
498 		if ( pt->y >= range_start && pt->y <= range_end ) return 1;
499 		return 0;
500 	}
501 }
ball_intersect_brick(Ball * ball,Target * target)502 void ball_intersect_brick( Ball *ball, Target *target )
503 {
504 	Line  ball_line;
505 	Coord pt; /* auxiliary point */
506 	int   x = target->mx * BRICK_WIDTH;
507 	int   y = target->my * BRICK_HEIGHT; /* left upper corner of brick */
508 	int   intersect = 0; /* intersected? */
509 
510 	/* ball_line */
511 	line_set( &ball_line,
512 		ball->cur.x + ball_rad,
513 		ball->cur.y + ball_rad,
514 		ball->vel.y / ball->vel.x );
515 
516 	if ( ball->vel.x > 0 ) {
517 		/* left */
518 		if ( check_line( &ball_line,
519 					LINE_VERT,
520 					x - ball_rad,
521 					y - ball_rad, y + BRICK_HEIGHT + ball_rad,
522 					&pt ) ) {
523 			intersect = 1;
524 			target->perp_vector = vector_get( 1, 0 );
525 		}
526 	}
527 	else {
528 		/* right */
529 		if ( check_line( &ball_line,
530 					LINE_VERT,
531 					x + BRICK_WIDTH + ball_rad,
532 					y - ball_rad, y + BRICK_HEIGHT + ball_rad,
533 					&pt ) ) {
534 			intersect = 1;
535 			target->perp_vector = vector_get( 1, 0 );
536 		}
537 	}
538 	if ( !intersect ) {
539 		if ( ball->vel.y > 0 ) {
540 			/* top */
541 			if ( check_line( &ball_line,
542 						LINE_HORI,
543 						y - ball_rad,
544 						x - ball_rad, x + BRICK_WIDTH + ball_rad,
545 						&pt ) ) {
546 				intersect = 1;
547 				target->perp_vector = vector_get( 0, 1 );
548 			}
549 		}
550 		else {
551 			/* bottom */
552 			if ( check_line( &ball_line,
553 						LINE_HORI,
554 						y + BRICK_HEIGHT + ball_rad,
555 						x - ball_rad, x + BRICK_WIDTH + ball_rad,
556 						&pt ) ) {
557 				intersect = 1;
558 				target->perp_vector = vector_get( 0, 1 );
559 			}
560 		}
561 	}
562 	/* intersected */
563 	if ( intersect ) {
564 		target->x = pt.x;
565 		target->y = pt.y;
566 		/* perp_vector is set */
567 	}
568 }
569 /*
570 ====================================================================
571 Reflect ball at target at target->side and set perp_vector
572 and reset position x,y of the target. Does not update the ball.
573 ====================================================================
574 */
ball_reflect_at_side(Ball * ball,Target * target)575 void ball_reflect_at_side( Ball *ball, Target *target )
576 {
577 	float old_vx;
578 	int   compute_vel, start;
579 	Line  ball_line;
580 	Line  brick_line;
581 	Coord pt;
582 	/* ball line */
583 	line_set( &ball_line,
584 		ball->cur.x + ball_rad,
585 		ball->cur.y + ball_rad,
586 		ball->vel.y / ball->vel.x );
587 	/* brick line and perp vector */
588 	switch ( target->side ) {
589 		case SIDE_LEFT:
590 			line_set_vert( &brick_line, target->mx * BRICK_WIDTH - ball_rad );
591 			target->perp_vector = vector_get( 1, 0 );
592 			break;
593 		case SIDE_RIGHT:
594 			line_set_vert( &brick_line,
595 				target->mx * BRICK_WIDTH + BRICK_WIDTH + ball_rad );
596 			target->perp_vector = vector_get( 1, 0 );
597 			break;
598 		case SIDE_TOP:
599 			line_set_hori( &brick_line, target->my * BRICK_HEIGHT - ball_rad );
600 			target->perp_vector = vector_get( 0, 1 );
601 			break;
602 		case SIDE_BOTTOM:
603 			line_set_hori( &brick_line,
604 				target->my * BRICK_HEIGHT + BRICK_HEIGHT + ball_rad );
605 			target->perp_vector = vector_get( 0, 1 );
606 			break;
607 		default:
608 			fprintf( stderr, "Unknown side: %i\n", target->side );
609 			break;
610 	}
611 	/* intersect, it's already assured that we hit this brick so just get the reset position */
612 	line_intersect( &brick_line, &ball_line, &pt );
613 	target->x = pt.x;
614 	target->y = pt.y;
615 	/* check if ball slid into next brick because of high angle when
616 	   reflect at side (not corner) */
617 	compute_vel = 0;
618 	switch ( target->side ) {
619 		case SIDE_BOTTOM:
620 		case SIDE_TOP:
621 			if ( ball->vel.x > 0 )
622 				pt.x = target->x + ball_rad;
623 			else
624 				pt.x = target->x - ball_rad;
625 			start = (int)pt.x / BRICK_WIDTH;
626 			if ( cur_game->bricks[start][(int)target->y/BRICK_HEIGHT].type != MAP_EMPTY ) {
627 				if ( ball->vel.x > 0 )
628 					target->x = start * BRICK_WIDTH - ball_rad - 1;
629 				else
630 					target->x = (start+1) * BRICK_WIDTH + ball_rad;
631 				compute_vel = 1;
632 			}
633 			break;
634 		case SIDE_LEFT:
635 		case SIDE_RIGHT:
636 			if ( ball->vel.y > 0 )
637 				pt.y = target->y + ball_rad;
638 			else
639 				pt.y = target->y - ball_rad;
640 			start = (int)pt.y / BRICK_HEIGHT;
641 			if ( cur_game->bricks[(int)target->x/BRICK_WIDTH][start].type != MAP_EMPTY ) {
642 				if ( ball->vel.y > 0 )
643 					target->y = start * BRICK_HEIGHT - ball_rad - 1;
644 				else
645 					target->y = (start+1) * BRICK_HEIGHT + ball_rad;
646 				compute_vel = 1;
647 			}
648 			break;
649 	}
650 	if ( compute_vel ) {
651 		old_vx = ball->vel.x;
652 		ball->vel.x = target->x - (ball->cur.x + ball_rad);
653 		ball->vel.y = target->y - (ball->cur.y + ball_rad);
654 		ball_mask_vel( ball, old_vx, BALL_NO_ENTROPY );
655 		/* should we mask to the 2deg steps here? yes! */
656 		ball->angle = vec2angle( &ball->vel );
657 		angle2vec( ball->angle, &ball->vel );
658 		vector_set_length( &ball->vel, cur_game->ball_v );
659 	}
660 }
661 /*
662 ====================================================================
663 Reflect ball at target but ignore target::side and reflect at
664 corner instead. Does not update the ball.
665 ====================================================================
666 */
ball_reflect_at_corner(Ball * ball,Target * target,int corner)667 void ball_reflect_at_corner( Ball *ball, Target *target, int corner )
668 {
669 	Coord corner_center; /* center of corner circle */
670 	Coord ball_center = { ball->cur.x + ball_rad, ball->cur.y + ball_rad };
671 	Vector norm_vel = ball->vel;
672 	Coord pt, pt2; /* intersection points */
673 
674 	/* norm velocity */
675 	vector_norm( &norm_vel );
676 
677 	/* set up center of corner */
678 	switch ( corner ) {
679 		case CORNER_UPPER_LEFT:
680 			corner_center = vector_get(
681 				target->mx * BRICK_WIDTH,
682 				target->my * BRICK_HEIGHT );
683 			break;
684 		case CORNER_UPPER_RIGHT:
685 			corner_center = vector_get(
686 				target->mx * BRICK_WIDTH + BRICK_WIDTH - 1,
687 				target->my * BRICK_HEIGHT );
688 			break;
689 		case CORNER_LOWER_LEFT:
690 			corner_center = vector_get(
691 				target->mx * BRICK_WIDTH,
692 				target->my * BRICK_HEIGHT + BRICK_HEIGHT - 1);
693 			break;
694 		case CORNER_LOWER_RIGHT:
695 			corner_center = vector_get(
696 				target->mx * BRICK_WIDTH + BRICK_WIDTH - 1,
697 				target->my * BRICK_HEIGHT + BRICK_HEIGHT - 1);
698 			break;
699 	}
700 	/* intersect */
701 	circle_intersect( corner_center, ball_rad + 2, ball_center, norm_vel, &pt, &pt2 );
702 	/* use nearest point for reset and perp vector */
703 	if ( VEC_DIST( ball_center, pt ) < VEC_DIST( ball_center, pt2 ) ) {
704 		target->x = pt.x;
705 		target->y = pt.y;
706 	}
707 	else {
708 		target->x = pt2.x;
709 		target->y = pt2.y;
710 	}
711 	/* compute the spherical perp vector
712 	   (corner center - intersection point) */
713 	target->perp_vector =
714 		vector_get( corner_center.x - target->x,
715 				corner_center.y - target->y );
716 	vector_norm( &target->perp_vector );
717 	/* this vector must operate within a 90� region depending on the corner.
718 	   if it doesn't we have a side reflection unnoticed by the previous
719 	   checks as we enclosed a corner. this is the only position to check
720 	   this as the reset position is different when a corner is enclosed.
721 	   doing this anywhere else would lead to errors. */
722 	switch ( corner ) {
723 		case CORNER_UPPER_LEFT:
724 			if ( target->perp_vector.x * target->perp_vector.y >= 0 ) {
725 				/* we needed the spherical perp to determine if it is
726 				   really a corner however we might have set the
727 				   config option linear_corner */
728 				/*if ( config.linear_corner )
729 					target->perp_vector = vector_get( REC_SQRT_2, REC_SQRT_2 );*/
730 				break;
731 			}
732 			if ( target->y < corner_center.y || target->x >= corner_center.x )
733 				target->perp_vector = vector_get( 0, 1 ); /* top */
734 			else
735 				target->perp_vector = vector_get( 1, 0 ); /* left */
736 			break;
737 		case CORNER_LOWER_RIGHT:
738 			if ( target->perp_vector.x * target->perp_vector.y >= 0 ) {
739 				/*if ( config.linear_corner )
740 					target->perp_vector = vector_get( REC_SQRT_2, REC_SQRT_2 );*/
741 				break;
742 			}
743 			if ( target->y > corner_center.y || target->x <= corner_center.x )
744 				target->perp_vector = vector_get( 0, 1 ); /* bottom */
745 			else
746 				target->perp_vector = vector_get( 1, 0 ); /* right */
747 			break;
748 		case CORNER_UPPER_RIGHT:
749 			if ( target->perp_vector.x * target->perp_vector.y <= 0 ) {
750 				/*if ( config.linear_corner )
751 					target->perp_vector = vector_get( REC_SQRT_2, -REC_SQRT_2 );*/
752 				break;
753 			}
754 			if ( target->y < corner_center.y || target->x <= corner_center.x )
755 				target->perp_vector = vector_get( 0, 1 ); /* top */
756 			else
757 				target->perp_vector = vector_get( 1, 0 ); /* right */
758 			break;
759 		case CORNER_LOWER_LEFT:
760 			if ( target->perp_vector.x * target->perp_vector.y <= 0 ) {
761 				/*if ( config.linear_corner )
762 					target->perp_vector = vector_get( REC_SQRT_2, -REC_SQRT_2 );*/
763 				break;
764 			}
765 			if ( target->y > corner_center.y || target->x >= corner_center.x )
766 				target->perp_vector = vector_get( 0, 1 ); /* bottom */
767 			else
768 				target->perp_vector = vector_get( 1, 0 ); /* left */
769 			break;
770 	}
771 }
772 /*
773 ====================================================================
774 Reflect ball at target ball:t and decide by ball::t::side wether
775 to use reflect_at_side or reflect_at_corner.
776 ====================================================================
777 */
ball_reflect(Ball * ball)778 void ball_reflect( Ball *ball )
779 {
780 	if ( !ball->target.exists ) return;
781 	if ( ball->target.side <= SIDE_LEFT )
782 		ball_reflect_at_side( ball, &ball->target );
783 	else
784 		ball_reflect_at_corner( ball, &ball->target, ball->target.side );
785 }
786 
787 /*
788 ====================================================================
789 Check if ball's tangents enclose a corner and update target's side.
790 ====================================================================
791 */
ball_corner_check(Ball * ball,Target * target_left_tang,Target * target_right_tang,Target * target)792 void ball_corner_check( Ball *ball,
793 	Target *target_left_tang, Target *target_right_tang, Target *target )
794 {
795 	/* balls moving ... */
796 	if ( ball->vel.y > 0 ) {
797 		if ( ball->vel.x < 0 ) {
798 			/* ... down left */
799 			if ( target == target_right_tang )
800 			if ( target->side == SIDE_TOP   )
801 				target->side = CORNER_UPPER_RIGHT;
802 			if ( target == target_left_tang  )
803 			if ( target->side == SIDE_RIGHT )
804 				target->side = CORNER_UPPER_RIGHT;
805 		}
806 		else {
807 			/* ... down right */
808 			if ( target == target_left_tang  )
809 			if ( target->side == SIDE_TOP   )
810 				target->side = CORNER_UPPER_LEFT;
811 			if ( target == target_right_tang )
812 			if ( target->side == SIDE_LEFT  ) target->side = CORNER_UPPER_LEFT;
813 		}
814 	}
815 	else {
816 		if ( ball->vel.x < 0 ) {
817 			/* ... up left */
818 			if ( target == target_right_tang )
819 			if ( target->side == SIDE_RIGHT  )
820 				target->side = CORNER_LOWER_RIGHT;
821 			if ( target == target_left_tang  )
822 			if ( target->side == SIDE_BOTTOM )
823 				target->side = CORNER_LOWER_RIGHT;
824 		}
825 		else {
826 			/* ... up right */
827 			if ( target == target_left_tang  )
828 			if ( target->side == SIDE_LEFT   )
829 				target->side = CORNER_LOWER_LEFT;
830 			if ( target == target_right_tang )
831 			if ( target->side == SIDE_BOTTOM )
832 				target->side = CORNER_LOWER_LEFT;
833 		}
834 	}
835 }
836 
837 /*
838 ====================================================================
839 Public
840 ====================================================================
841 */
842 
843 /*
844 ====================================================================
845 Create ball at position
846 ====================================================================
847 */
ball_create(int x,int y)848 Ball* ball_create( int x, int y )
849 {
850 	Ball *ball = salloc( 1, sizeof( Ball ) );
851 	ball->cur.x = x;
852 	ball->x = x;
853 	ball->cur.y = y;
854 	ball->y = y;
855 	ball->attached = 0;
856 	ball->idle_time = 0;
857 	ball->moving_back = 0;
858 	ball->return_allowed = 0;
859 	ball_clear_target(&ball->target);
860 	return ball;
861 }
862 /*
863 ====================================================================
864 Set a special ball property like metal ball. Unused now
865 as it is directly checked wether extra_active is 1.
866 ====================================================================
867 */
balls_set_type(int type)868 void balls_set_type( int type )
869 {
870 }
871 /*
872 ====================================================================
873 Set chaotic behaviour (random relfection). Unused as extra_active
874 is checked now.
875 ====================================================================
876 */
balls_set_chaos(int chaos)877 void balls_set_chaos( int chaos )
878 {
879 }
880 /*
881 ====================================================================
882 Update balls and detach attached balls if fire was pressed.
883 ====================================================================
884 */
balls_update(int ms)885 void balls_update( int ms )
886 {
887 	int top = 0, bottom = 0; /* num of lost balls */
888 	int i, x, y;
889 	ListEntry *entry = cur_game->balls->head->next;
890 	Ball *ball;
891 	Vector old; /* old position of ball before update */
892 	int 	fired_attached, fire_dir;
893 
894 	/* detach or fire balls from paddles */
895 	for ( i = 0; i < cur_game->paddle_count; i++ ) {
896 		/* check wether paddles created new balls or released attached ones */
897 		if ( (cur_game->paddles[i]->ball_fire_delay-=ms) <= 0 )
898 		if ( cur_game->paddles[i]->fire_left || cur_game->paddles[i]->fire_right ) {
899 			/* reset delay till next ball may be released */
900 			cur_game->paddles[i]->ball_fire_delay = BALL_FIRE_RATE;
901 			/* get direction */
902 			fire_dir = cur_game->paddles[i]->fire_left?-1:1;
903 			/* try to fire attached balls */
904 			fired_attached = balls_detach_from_paddle( cur_game->paddles[i], fire_dir );
905 			/* if no attached balls were fired but paddle has ammo left
906 			 * it creates a new ball in PINGPONG levels */
907 			if ( !fired_attached )
908 			if ( cur_game->level_type == LT_PINGPONG )
909 			if ( cur_game->paddles[i]->ball_ammo > 0 ) {
910 				x = cur_game->paddles[i]->x + (cur_game->paddles[i]->w - ball_w) / 2;
911 				if ( cur_game->paddles[i]->type == PADDLE_BOTTOM )
912 					y = cur_game->paddles[i]->y - ball_dia;
913 				else
914 					y = cur_game->paddles[i]->y + cur_game->paddles[i]->h;
915 				ball = ball_create( x, y );
916 				ball->paddle = cur_game->paddles[i];
917 				if ( cur_game->balls_use_random_angle )
918 					ball_set_random_angle( ball, cur_game->ball_v );
919 				else {
920 					ball->vel.x = 1.0 * fire_dir;
921 					if ( ball->paddle->type == PADDLE_TOP )
922 						ball->vel.y = 1.2;
923 					else
924 						ball->vel.y = -1.2;
925 					/* only use 2 degree steps */
926 					ball->angle = vec2angle( &ball->vel );
927 					angle2vec( ball->angle, &ball->vel );
928 					vector_set_length( &ball->vel, cur_game->ball_v );
929 				}
930 				ball->get_target = 1;
931 				list_add( cur_game->balls, ball );
932 				cur_game->paddles[i]->ball_ammo--;
933 			}
934 		}
935 
936 		/* check wether no balls are attached and the respawn time is exceeded.
937 		 * then in NMP a ball is created and attached */
938 		if ( cur_game->game_type == GT_NETWORK && cur_game->level_type != LT_PINGPONG )
939 		if ( cur_game->paddles[i]->attached_ball_count == 0 )
940 		if ( SDL_GetTicks() >= cur_game->paddles[i]->last_ball_contact + BALL_RESPAWN_TIME ) {
941 			x = cur_game->paddles[i]->x + (cur_game->paddles[i]->w - ball_w) / 2;
942 			if ( cur_game->paddles[i]->type == PADDLE_BOTTOM )
943 				y = cur_game->paddles[i]->y - ball_dia;
944 			else
945 				y = cur_game->paddles[i]->y + cur_game->paddles[i]->h;
946 			ball = ball_create( x, y );
947 			list_add( cur_game->balls, ball );
948 			ball_attach( ball, cur_game->paddles[i] );
949 			ball_set_random_angle( ball, cur_game->ball_v );
950 		}
951 	}
952 
953         /* speed up/down balls on request */
954         if ( cur_game->game_type == GT_LOCAL )
955         {
956             if ( cur_game->paddles[0]->maxballspeed_request && !cur_game->paddles[0]->maxballspeed_request_old )
957             {
958                 cur_game->ball_v = cur_game->accelerated_ball_speed;
959                 balls_set_velocity( cur_game->balls, cur_game->ball_v );
960             }
961             if ( !cur_game->paddles[0]->maxballspeed_request && cur_game->paddles[0]->maxballspeed_request_old )
962             {
963                 if ( cur_game->extra_active[EX_SLOW] )
964                     cur_game->ball_v = cur_game->ball_v_min;
965                 else
966                 if ( cur_game->extra_active[EX_FAST] )
967                     cur_game->ball_v = cur_game->ball_v_max;
968                 else
969                     cur_game->ball_v = cur_game->diff->v_start +
970                         cur_game->diff->v_add * cur_game->speedup_level;
971                 balls_set_velocity( cur_game->balls, cur_game->ball_v );
972             }
973         }
974 
975 	/* increase speed */
976 	if ( !cur_game->extra_active[EX_SLOW] )
977 	if ( !cur_game->extra_active[EX_FAST] )
978         if ( cur_game->game_type != GT_LOCAL || !cur_game->paddles[0]->maxballspeed_request )
979 		balls_inc_vel( ms );
980 
981 	/* return idle balls if not autoreturn */
982 	for ( i = 0; i < cur_game->paddle_count; i++ )
983 		if ( cur_game->paddles[i]->ball_return_key_pressed )
984 			balls_return( cur_game->paddles[i] );
985 
986 	/* move balls */
987 	while ( entry != cur_game->balls->tail ) {
988 		ball = entry->item;
989 		old.x = ball->cur.x;
990 		old.y = ball->cur.y;
991 
992 		/* update ball when moving back */
993 		if ( ball->moving_back ) {
994 			/* update velocity */
995 			ball->vel.x = ( ball->paddle->x + ( ball->paddle->w >> 1 ) ) -
996 				      ( ball->cur.x + ball_rad );
997 			ball->vel.y = ( ball->paddle->y - ball_rad + 2 ) - ( ball->cur.y + ball_rad );
998 			vector_set_length( &ball->vel, cur_game->ball_v_max );
999 			/* new position */
1000 			ball->cur.x += ball->vel.x * ms;
1001 			ball->cur.y += ball->vel.y * ms;
1002 			ball->x = (int)ball->cur.x;
1003 			ball->y = (int)ball->cur.y;
1004 			/* check if paddle is reached and attach the ball */
1005 			if ( ball->x + ball_rad >= ball->paddle->x )
1006 			if ( ball->x + ball_rad < ball->paddle->x + ball->paddle->w )
1007 			if ( ball->y + ball_dia >= ball->paddle->y )
1008 			if ( ball->y + ball_dia < ball->paddle->y + ball->paddle->h ) {
1009 				ball->cur.x = ball->paddle->x + ( ball->paddle->w >> 1 ) - ball_rad;
1010 				if ( ball->paddle->type == PADDLE_TOP )
1011 					ball->cur.y = ball->paddle->y + ball->paddle->h;
1012 				else
1013 					ball->cur.y = ball->paddle->y - ball_dia;
1014 				ball->x = (int)ball->cur.x;
1015 				ball->y = (int)ball->cur.y;
1016 				ball_attach( ball, ball->paddle );
1017 				ball_set_random_angle( ball, cur_game->ball_v );
1018 			}
1019 		}
1020 
1021 		/* update ball if not attached and not moving back */
1022 		if ( !ball->attached && !ball->moving_back ) {
1023 			/* increase idle time -- paddle and brick_check will reset this value */
1024 			if ( !ball->return_allowed )
1025 				ball->idle_time += ms;
1026 
1027 			/* check if reflected by any paddle */
1028 			for ( i = 0; i < cur_game->paddle_count; i++ )
1029 				if ( ball_paddle_contact_possible( ball, cur_game->paddles[i], old ) )
1030 					ball_check_paddle_reflection( ball, cur_game->paddles[i] );
1031 
1032 			/* update target? */
1033 			if ( ball->get_target ) {
1034 				ball_get_target( ball );
1035 				ball->get_target = 0;
1036 			}
1037 
1038 			/* new position if NOT attached*/
1039 			if ( !ball->attached ) {
1040 				ball->cur.x += ball->vel.x * ms;
1041 				ball->cur.y += ball->vel.y * ms;
1042 				ball->x = (int)ball->cur.x;
1043 				ball->y = (int)ball->cur.y;
1044 			}
1045 
1046 			/* reflection by brick */
1047 			/* quick hack to handle the case when the ball was just attached but
1048 			 * touches the wall and the slimy paddle in the same instant. -
1049 			 * Patrick Hohmeyer 19.12.01 */
1050 			if ( ball->target.exists && !ball->attached ) {
1051 				ball->target.cur_tm += ms;
1052 				ball_check_brick_reflection( ball );
1053 			}
1054 
1055 			/* check if idle time is above limit and the ball has a target because if
1056 			 * there is no target the ball moves out of the window and should not go
1057 			 * back to the paddle as it's moving into this direction by itself
1058 			 */
1059 			if ( ball->idle_time >= BALLS_IDLE_LIMIT )
1060 			if ( !ball->return_allowed )
1061 			if ( ball->target.exists ) {
1062 				/* okay send this ball back home or allow to do so by click */
1063 				if ( !cur_game->balls_return_by_click ) {
1064 					ball->idle_time = 0;
1065 					ball->moving_back = 1;
1066 					ball->target.exists = 0; /* no target */
1067 				}
1068 				else {
1069 					ball->idle_time = 0;
1070 					ball->return_allowed = 1;
1071 				}
1072 			}
1073 		}
1074 
1075 		/* get next entry in list and remove those out of the window */
1076 		entry = entry->next;
1077 		if (!ball->attached )
1078 		if ( ball->x >= 640 ||
1079 		     ball->x + ball_dia < 0 ||
1080 		     ball->y >= 480 ||
1081 		     ball->y + ball_dia < 0 ) {
1082 			if ( ball->y + ball_rad <= 480 >> 1 )
1083 				top++;
1084 			else
1085 				bottom++;
1086 			list_delete_entry( cur_game->balls, entry->prev );
1087 		}
1088 	}
1089 
1090 	/* update stats */
1091 	cur_game->paddles[PADDLE_BOTTOM]->balls_lost += bottom;
1092 	if ( cur_game->paddles[PADDLE_TOP] )
1093 		cur_game->paddles[PADDLE_TOP]->balls_lost += top;
1094 
1095 	/* modify scores in network game. for local games lost balls do not
1096 	 * change score. */
1097 	if ( cur_game->game_type == GT_NETWORK ) {
1098 		/* modify scores when a ball got lost */
1099 		if ( cur_game->level_type == LT_PINGPONG ) {
1100 			/* in pingpong a lost ball gives opponent one point and
1101 			 * the paddle that lost the ball can bring it back to
1102 			 * game */
1103 			cur_game->paddles[PADDLE_BOTTOM]->score += top;
1104 			cur_game->paddles[PADDLE_TOP]->score += bottom;
1105 			cur_game->paddles[PADDLE_BOTTOM]->ball_ammo += bottom;
1106 			cur_game->paddles[PADDLE_TOP]->ball_ammo += top;
1107 		}
1108 		else {
1109 			/* in a normal level 10% of score gets lost and
1110 			 * last contact time is reset so that it will take
1111 			 * ten seconds penalty before a new ball is
1112 			 * generated. */
1113 			if ( top ) {
1114 				while ( top-- > 0 )
1115 					cur_game->paddles[PADDLE_TOP]->score =
1116 						90 * cur_game->paddles[PADDLE_TOP]->score / 100;
1117 				cur_game->paddles[PADDLE_TOP]->last_ball_contact = SDL_GetTicks();
1118 			}
1119 			if ( bottom ) {
1120 				while ( bottom-- > 0 )
1121 					cur_game->paddles[PADDLE_BOTTOM]->score =
1122 						90 * cur_game->paddles[PADDLE_BOTTOM]->score / 100;
1123 				cur_game->paddles[PADDLE_BOTTOM]->last_ball_contact = SDL_GetTicks();
1124 			}
1125 		}
1126 	}
1127 }
1128 /*
1129 ====================================================================
1130 All balls with target mx,my will have there 'get_target' flag
1131 set True so they compute a new target next time balls_update()
1132 is called. If 'mx' is -1 all balls will set their flag.
1133 ====================================================================
1134 */
balls_check_targets(int mx,int my)1135 void balls_check_targets(int mx, int my) {
1136 	Ball *ball;
1137 	int reset = 0;
1138 	list_reset( cur_game->balls );
1139 	while ( ( ball = list_next( cur_game->balls ) ) )
1140 		if ( !ball->attached && !ball->moving_back )
1141 		if ( mx == -1 || ( ball->target.mx == mx && ball->target.my == my ) ) {
1142 			/* As we don't have a constant velocity but assume one it is possible that
1143 			 * the ball is within a wall when this function is called because it actually
1144 			 * passed it's reset position without time expiration because of the velocity
1145 			 * change. So we have to check here if it is already behind this position
1146 			 * and if so simply reset here. This doesn't hurt as this would happen
1147 			 * before reflection, too. */
1148 			if ( ball->target.exists ) {
1149 				if ( ball->vel.y > 0 ) {
1150 					if ( ball->cur.y > ball->target.y )
1151 						reset = 1;
1152 				}
1153 				else {
1154 					if ( ball->cur.y < ball->target.y )
1155 						reset = 1;
1156 				}
1157 				if ( ball->vel.x > 0 ) {
1158 					if ( ball->cur.x > ball->target.x )
1159 						reset = 1;
1160 				}
1161 				else {
1162 					if ( ball->cur.x < ball->target.x )
1163 						reset = 1;
1164 				}
1165 				if ( reset ) {
1166 					ball->cur.x = ball->target.x;
1167 					ball->cur.y = ball->target.y;
1168 					ball->x = (int)ball->cur.x;
1169 					ball->y = (int)ball->cur.y;
1170 				}
1171 			}
1172 			ball->get_target = 1;
1173 		}
1174 }
1175 /*
1176 ====================================================================
1177 Adjust velocity of ball to spare out any illegal values.
1178 Add a little entropy to the vector if 'entropy' is True.
1179 ====================================================================
1180 */
ball_mask_vel(Ball * b,float old_vx,int entropy)1181 void ball_mask_vel(Ball *b, float old_vx, int entropy )
1182 {
1183 	float m, entropy_level = 0;
1184 
1185 	if ( b->vel.x == 0 && b->vel.y == 0 ) return;
1186 
1187 	/* b->vel.x == 0 would cause seg faults */
1188 	if (b->vel.x == 0) {
1189 		if (old_vx < 0)
1190 			b->vel.x = 0.01;
1191 		else
1192 			b->vel.x = -0.01;
1193 	}
1194 
1195 	if ( entropy == BALL_ADD_ENTROPY )
1196 		entropy_level = (float)((rand() % 81)+40)/1000.0;
1197 
1198 	m = b->vel.y / b->vel.x;
1199 	if (fabs(m) < ball_vhmask) {
1200 		/* mask angles from 70 to 110 and -110 to -70 */
1201 		if (b->vel.y < 0)
1202 			b->vel.y = -fabs(ball_vhmask * b->vel.x);
1203 		else
1204 			b->vel.y = fabs(ball_vhmask * b->vel.x);
1205 		if ( entropy == BALL_ADD_ENTROPY )
1206 			b->vel.x -= b->vel.x * entropy_level;
1207 	}
1208 	else
1209 		if (fabs(m) > ball_vvmask) {
1210 			/* mask angles from -10 to 10 and 170 to 190 */
1211 			if (b->vel.x < 0)
1212 				b->vel.x = -fabs(b->vel.y / ball_vvmask);
1213 			else
1214 				b->vel.x = fabs(b->vel.y / ball_vvmask);
1215 			if ( entropy == BALL_ADD_ENTROPY )
1216 				b->vel.x += b->vel.x * entropy_level;
1217 		}
1218 		else
1219 			if ( entropy == BALL_ADD_ENTROPY ) {
1220 				if ( rand() % 2 )
1221 					entropy_level = -entropy_level;
1222 				b->vel.x += b->vel.x * entropy_level;
1223 			}
1224 
1225 	/* avoid 45� angles */
1226 	if (b->vel.x == b->vel.y)
1227 		b->vel.x *= 0.98;
1228 
1229 	/* adjust speed */
1230 	vector_set_length( &b->vel, cur_game->ball_v );
1231 }
1232 /*
1233 ====================================================================
1234 Get target for a ball.
1235 ====================================================================
1236 */
1237 enum { TANG_LEFT = 0, TANG_RIGHT };
1238 enum { DIR_UP = 0, DIR_DOWN, DIR_LEFT, DIR_RIGHT };
ball_get_target(Ball * ball)1239 void ball_get_target( Ball *ball )
1240 {
1241     int    cur_tang;
1242     float  mono; /* monotony */
1243     Coord  tang_pts[2]; /* tangential points */
1244     Line   tang; /* current tangent */
1245     Coord  center = {
1246         ball->cur.x + ball_rad,
1247         ball->cur.y + ball_rad }; /* ball center */
1248     int    start, end, dir, line_pos, change; /* used to intersect the brick grid */
1249     Line   cur_line; /* dito */
1250     Coord  pt; /* auxiliary point. used for this 'n' that */
1251     Target targets[2]; /* targets hit by the tangents: nearest is the actual target */
1252     Target hori_target[2], vert_target[2]; /* used to get target of tangent */
1253     float  dist; /* distance between two points */
1254     Vector norm_vel; /* normed ball velocity */
1255 #ifdef WITH_BUG_REPORT
1256 	char	tang_target_chosen_str[2][128]; /* either hori or vert target chosen */
1257 	char	side_str[128];
1258     Coord   test_pts[2];
1259 #endif
1260 	Target	*prim, *sec; /* primary, secondary target */
1261 	int     maybe_corner;
1262 
1263 #ifdef WITH_BUG_REPORT
1264 	side_str[0] = 0;
1265 #endif
1266 
1267     /* balls moving back to paddle must not be reflected */
1268     if ( ball->moving_back ) return;
1269 	/* attached balls MUST NOT be reflected!!!! */
1270 	if ( ball->attached ) return;
1271 	/* balls already out of the screen though still visible don't need new reflection, too */
1272 	if ( ball->cur.y + ball_dia >= 480 - 1 ) return;
1273 
1274     /* clear ball targets */
1275     ball_clear_target( &ball->target );
1276     ball_clear_target( &targets[TANG_LEFT] );
1277     ball_clear_target( &targets[TANG_RIGHT] );
1278     /* monotony */
1279     mono = ball->vel.y / ball->vel.x;
1280     /* normed velocity */
1281     norm_vel = ball->vel; vector_norm( &norm_vel );
1282     /* tangential points */
1283     ball_get_tangents( ball, &tang_pts[TANG_LEFT], &tang_pts[TANG_RIGHT] );
1284     /* get all map bricks the tangents intersect and check target */
1285     for ( cur_tang = 0; cur_tang < 2; cur_tang++ ) {
1286         /* clear targets */
1287         ball_clear_target( &hori_target[cur_tang] );
1288         ball_clear_target( &vert_target[cur_tang] );
1289         /* current tangent */
1290         line_set( &tang, tang_pts[cur_tang].x, tang_pts[cur_tang].y, mono );
1291         /* intersect horizontal lines */
1292         /* get direction */
1293         dir = DIR_DOWN;
1294         if ( ball->vel.y < 0 ) dir = DIR_UP;
1295         /* get starting line */
1296         start = ((int)( tang_pts[cur_tang].y / BRICK_HEIGHT )) * BRICK_HEIGHT;
1297         /* get end line */
1298         if ( dir == DIR_UP )
1299             end = 0;
1300         else
1301             end = ( MAP_HEIGHT - 1 ) * BRICK_HEIGHT;
1302         /* adjust lines if ball moves up */
1303         if ( dir == DIR_UP ) {
1304             start += BRICK_HEIGHT - 1;
1305             end += BRICK_HEIGHT - 1;
1306         }
1307         /* get position change */
1308         change = BRICK_HEIGHT;
1309         if ( dir == DIR_UP ) change = -change;
1310         /* we're at this brick so we can't reflect here */
1311         start += change;
1312         /* intersect */
1313         line_pos = start;
1314         /*  end specifies the last line to be checked to we have to add
1315             another line to state the break condition.
1316             this last line is not checked */
1317         end += change;
1318         while ( line_pos != end ) {
1319             line_set_hori( &cur_line, line_pos );
1320             if ( line_intersect( &cur_line, &tang, &pt ) && ( pt.x >= 0 && pt.x < 640 ) )
1321                 if ( cur_game->bricks[(int)pt.x / BRICK_WIDTH][(int)pt.y / BRICK_HEIGHT].type != MAP_EMPTY ) {
1322                     /* we got our horizontal target */
1323                     hori_target[cur_tang].exists = 1;
1324                     hori_target[cur_tang].x = pt.x;
1325                     hori_target[cur_tang].y = pt.y;
1326                     hori_target[cur_tang].mx = (int)pt.x / BRICK_WIDTH;
1327                     hori_target[cur_tang].my = (int)pt.y / BRICK_HEIGHT;
1328                     if ( ball->vel.y < 0 )
1329                         hori_target[cur_tang].side = SIDE_BOTTOM;
1330                     else
1331                         hori_target[cur_tang].side = SIDE_TOP;
1332                     break; /* we got our target for this tangent */
1333                 }
1334             line_pos += change;
1335         }
1336         /* intersect vertical lines */
1337         /* get direction */
1338         dir = DIR_RIGHT;
1339         if ( ball->vel.x < 0 ) dir = DIR_LEFT;
1340         /* get starting line */
1341         start = ((int)( tang_pts[cur_tang].x / BRICK_WIDTH )) * BRICK_WIDTH;
1342         /* get end line */
1343         if ( dir == DIR_LEFT )
1344             end = 0;
1345         else
1346             end = ( MAP_WIDTH - 1 ) * BRICK_WIDTH;
1347         /* adjust lines if ball moves up */
1348         if ( dir == DIR_LEFT ) {
1349             start += BRICK_WIDTH - 1;
1350             end += BRICK_WIDTH - 1;
1351         }
1352         /* get position change */
1353         change = BRICK_WIDTH;
1354         if ( dir == DIR_LEFT ) change = -change;
1355         /* we're at this brick so we can't reflect here */
1356         start += change;
1357         /* intersect */
1358         line_pos = start;
1359         /*  end specifies the last line to be checked too we have to add
1360             another line to state the break condition.
1361             this last line is not checked */
1362         end += change;
1363         while ( line_pos != end ) {
1364             line_set_vert( &cur_line, line_pos );
1365             if ( line_intersect( &cur_line, &tang, &pt ) && ( pt.y >= 0 && pt.y < 480 ) )
1366                 if ( cur_game->bricks[(int)pt.x / BRICK_WIDTH][(int)pt.y / BRICK_HEIGHT].type != MAP_EMPTY ) {
1367                     /* we got our vertical target */
1368                     vert_target[cur_tang].exists = 1;
1369                     vert_target[cur_tang].x = pt.x;
1370                     vert_target[cur_tang].y = pt.y;
1371                     vert_target[cur_tang].mx = (int)pt.x / BRICK_WIDTH;
1372                     vert_target[cur_tang].my = (int)pt.y / BRICK_HEIGHT;
1373                     if ( ball->vel.x < 0 )
1374                         vert_target[cur_tang].side = SIDE_RIGHT;
1375                     else
1376                         vert_target[cur_tang].side = SIDE_LEFT;
1377                     break; /* we got our target for this tangent */
1378                 }
1379             line_pos += change;
1380         }
1381         /* get closest target */
1382         if ( !hori_target[cur_tang].exists ) {
1383             targets[cur_tang] = vert_target[cur_tang];
1384 #ifdef WITH_BUG_REPORT
1385 			if ( !vert_target[cur_tang].exists )
1386 				sprintf( tang_target_chosen_str[cur_tang], "No target chosen." );
1387 			else
1388 				sprintf( tang_target_chosen_str[cur_tang], "Vertical target chosen." );
1389 #endif
1390 		}
1391         else
1392             if ( !vert_target[cur_tang].exists ) {
1393                 targets[cur_tang] = hori_target[cur_tang];
1394 #ifdef WITH_BUG_REPORT
1395 				sprintf( tang_target_chosen_str[cur_tang], "Horizontal target chosen." );
1396 #endif
1397 			}
1398             else {
1399 				/* check the relation and choose the correct target */
1400 				/* if vertical and hori hit the same brick we have hit the corner */
1401 				if ( hori_target[cur_tang].mx == vert_target[cur_tang].mx && hori_target[cur_tang].my == vert_target[cur_tang].my ) {
1402 					/* congrats! we hit the exact corner pixel! now we have to decide by corner and
1403 					   tangent which target to use */
1404 					if ( cur_tang == TANG_LEFT ) {
1405 						/* left tangent */
1406 						if ( ball->vel.y > 0 ) {
1407 							if ( ball->vel.x > 0 ) /* upper, right */
1408 								targets[cur_tang] = vert_target[cur_tang];
1409 							else
1410 								targets[cur_tang] = hori_target[cur_tang];
1411 						}
1412 						else {
1413 							if ( ball->vel.x > 0 ) /* lower, right */
1414 								targets[cur_tang] = hori_target[cur_tang];
1415 							else
1416 								targets[cur_tang] = vert_target[cur_tang];
1417 						}
1418 					}
1419 					else {
1420 						/* right tangent */
1421 						if ( ball->vel.y > 0 ) {
1422 							if ( ball->vel.x > 0 ) /* upper, right */
1423 								targets[cur_tang] = hori_target[cur_tang];
1424 							else
1425 								targets[cur_tang] = vert_target[cur_tang];
1426 						}
1427 						else {
1428 							if ( ball->vel.x > 0 ) /* lower, right */
1429 								targets[cur_tang] = vert_target[cur_tang];
1430 							else
1431 								targets[cur_tang] = hori_target[cur_tang];
1432 						}
1433 					}
1434 #ifdef WITH_BUG_REPORT
1435 					if ( targets[cur_tang].x == hori_target[cur_tang].x && targets[cur_tang].y == hori_target[cur_tang].y )
1436 						sprintf( tang_target_chosen_str[cur_tang], "(TRICKY) Horizontal target chosen." );
1437 					else
1438 						sprintf( tang_target_chosen_str[cur_tang], "(TRICKY) Vertical target chosen." );
1439 #endif
1440 				}
1441 				else {
1442 					if ( VEC_DIST( tang_pts[cur_tang], vector_get( hori_target[cur_tang].x, hori_target[cur_tang].y ) ) < VEC_DIST( tang_pts[cur_tang], vector_get( vert_target[cur_tang].x, vert_target[cur_tang].y ) ) ) {
1443                     	targets[cur_tang] = hori_target[cur_tang];
1444 #ifdef WITH_BUG_REPORT
1445 						sprintf( tang_target_chosen_str[cur_tang], "Horizontal target chosen." );
1446 #endif
1447 					}
1448                 	else {
1449                     	targets[cur_tang] = vert_target[cur_tang];
1450 #ifdef WITH_BUG_REPORT
1451 						sprintf( tang_target_chosen_str[cur_tang], "Vertical target chosen." );
1452 #endif
1453 					}
1454 				}
1455             }
1456     } /* now we have the two targets hit by the tangents */
1457 	/* whatever's up the nearest brick is hit */
1458     if ( targets[TANG_LEFT].exists || targets[TANG_RIGHT].exists ) {
1459 		prim = sec = 0;
1460         if ( !targets[TANG_LEFT].exists || !targets[TANG_RIGHT].exists ) {
1461 			if ( targets[TANG_LEFT].exists )
1462 				prim = &targets[TANG_LEFT];
1463 			else
1464 				prim = &targets[TANG_RIGHT];
1465 		}
1466 		else {
1467 			if ( VEC_DIST( center, vector_get( targets[TANG_RIGHT].x, targets[TANG_RIGHT].y ) ) < VEC_DIST( center, vector_get( targets[TANG_LEFT].x, targets[TANG_LEFT].y ) ) )  {
1468 				prim = &targets[TANG_RIGHT];
1469 				sec  = &targets[TANG_LEFT];
1470 			}
1471 			else {
1472 				prim = &targets[TANG_LEFT];
1473 				sec  = &targets[TANG_RIGHT];
1474 			}
1475 		}
1476 		/* however, the primary target maybe be blocked by another brick or may be a corner */
1477 		/* check if side of prim  target isn't blocked by a brick */
1478 		switch ( prim->side ) {
1479 			case SIDE_TOP:
1480 				if ( cur_game->bricks[prim->mx][prim->my - 1].type != MAP_EMPTY ) {
1481 					if ( ball->vel.x > 0 )
1482 						prim->side = SIDE_LEFT;
1483 					else
1484 						prim->side = SIDE_RIGHT;
1485 #ifdef WITH_BUG_REPORT
1486 					sprintf( side_str, "Had to change side as TOP wasn't appropriate!" );
1487 #endif
1488 				}
1489 				break;
1490 			case SIDE_BOTTOM:
1491 				if ( cur_game->bricks[prim->mx][prim->my + 1].type != MAP_EMPTY ) {
1492 					if ( ball->vel.x > 0 )
1493 						prim->side = SIDE_LEFT;
1494 					else
1495 						prim->side = SIDE_RIGHT;
1496 #ifdef WITH_BUG_REPORT
1497 					sprintf( side_str, "Had to change side as BOTTOM wasn't appropriate!" );
1498 #endif
1499 				}
1500 				break;
1501 			case SIDE_LEFT:
1502 				if ( cur_game->bricks[prim->mx - 1][prim->my].type != MAP_EMPTY ) {
1503 					if ( ball->vel.y > 0 )
1504 						prim->side = SIDE_TOP;
1505 					else
1506 						prim->side = SIDE_BOTTOM;
1507 #ifdef WITH_BUG_REPORT
1508 					sprintf( side_str, "Had to change side as LEFT wasn't appropriate!" );
1509 #endif
1510 				}
1511 				break;
1512 			case SIDE_RIGHT:
1513 				if ( cur_game->bricks[prim->mx + 1][prim->my].type != MAP_EMPTY ) {
1514 					if ( ball->vel.y > 0 )
1515 						prim->side = SIDE_TOP;
1516 					else
1517 						prim->side = SIDE_BOTTOM;
1518 #ifdef WITH_BUG_REPORT
1519 					sprintf( side_str, "Had to change side as RIGHT wasn't appropriate!" );
1520 #endif
1521 				}
1522 				break;
1523 		}
1524 		/* now it still may be a corner */
1525         if ( sec == 0 || prim->mx != sec->mx || prim->my != sec->my || prim->side != sec->side ) {
1526             maybe_corner = 1;
1527             if ( ball->vel.y > 0 ) {
1528                 if ( ball->vel.x > 0 ) {
1529                     /* upper left corner */
1530                     if ( cur_game->bricks[prim->mx][prim->my - 1].type != MAP_EMPTY ) maybe_corner = 0;
1531                     if ( cur_game->bricks[prim->mx - 1][prim->my].type != MAP_EMPTY ) maybe_corner = 0;
1532                 }
1533                 else {
1534                     /* upper right corner */
1535                     if ( cur_game->bricks[prim->mx][prim->my - 1].type != MAP_EMPTY ) maybe_corner = 0;
1536                     if ( cur_game->bricks[prim->mx + 1][prim->my].type != MAP_EMPTY ) maybe_corner = 0;
1537                 }
1538             }
1539             else {
1540                 if ( ball->vel.x > 0 ) {
1541                     /* lower left corner */
1542                     if ( cur_game->bricks[prim->mx][prim->my + 1].type != MAP_EMPTY ) maybe_corner = 0;
1543                     if ( cur_game->bricks[prim->mx - 1][prim->my].type != MAP_EMPTY ) maybe_corner = 0;
1544                 }
1545                 else {
1546                     /* lower right corner */
1547                     if ( cur_game->bricks[prim->mx][prim->my + 1].type != MAP_EMPTY ) maybe_corner = 0;
1548                     if ( cur_game->bricks[prim->mx + 1][prim->my].type != MAP_EMPTY ) maybe_corner = 0;
1549                 }
1550             }
1551             if ( maybe_corner )
1552                 ball_corner_check( ball, &targets[TANG_LEFT], &targets[TANG_RIGHT], prim );
1553         }
1554 		/* we updated primary's side info correctly and may reflect now */
1555 		ball->target = *prim;
1556 		ball_reflect( ball );
1557         /* we got the reset position and the perpvector so finalize target */
1558         /* compute time: assume constant velocity: velocity change must not be too high! */
1559         dist = sqrt( SQUARE(center.x - ball->target.x) + SQUARE(center.y - ball->target.y) );
1560         ball->target.time = (int)floor(dist / cur_game->ball_v);
1561         /* target's reset position is center position right now but
1562            we need the upper left corner of the ball */
1563         ball->target.x -= ball_rad; ball->target.y -= ball_rad;
1564         /* some error information */
1565 #ifdef WITH_BUG_REPORT
1566         pt.x = ball->cur.x; pt.y = ball->cur.y;
1567         ball->cur.x = ball->target.x; ball->cur.y = ball->target.y;
1568         ball_get_tangents( ball, &test_pts[TANG_LEFT], &test_pts[TANG_RIGHT] );
1569         ball->cur.x = pt.x; ball->cur.y = pt.y;
1570 		if ( cur_game->bricks[(int)test_pts[0].x/BRICK_WIDTH][(int)test_pts[0].y/BRICK_HEIGHT].type != MAP_EMPTY ||
1571              cur_game->bricks[(int)test_pts[1].x/BRICK_WIDTH][(int)test_pts[1].y/BRICK_HEIGHT].type != MAP_EMPTY ) {
1572 			printf( "*****\n" );
1573             printf( "Test Failed: %4.2f,%4.2f (%i,%i):\n",
1574                 ball->target.x+ball_rad, ball->target.y+ball_rad,
1575                 (int)(ball->target.x+ball_rad)/BRICK_WIDTH,
1576                 (int)(ball->target.y+ball_rad)/BRICK_HEIGHT );
1577             printf( "Left Tangent %4.2f,%4.2f (%i,%i) or Right Tangent %4.2f,%4.2f (%i,%i)\n",
1578                 test_pts[0].x,test_pts[0].y,
1579                 (int)test_pts[0].x/BRICK_WIDTH,(int)test_pts[0].y/BRICK_HEIGHT,
1580                 test_pts[1].x,test_pts[1].y,
1581                 (int)test_pts[1].x/BRICK_WIDTH,(int)test_pts[1].y/BRICK_HEIGHT);
1582 			printf( "*****\n" );
1583             printf( "2.4: Balls: %i\n", cur_game->balls->count );
1584         	if ( targets[TANG_LEFT].exists ) {
1585                 printf( "Left Tangential Point: %4.2f,%4.2f\n",
1586                     tang_pts[TANG_LEFT].x, tang_pts[TANG_LEFT].y );
1587                 printf( "Left Tangent: Horizontal: %i,%i, %i (%4.2f,%4.2f)\n",
1588 						hori_target[TANG_LEFT].mx, hori_target[TANG_LEFT].my, hori_target[TANG_LEFT].side,
1589 						hori_target[TANG_LEFT].x, hori_target[TANG_LEFT].y );
1590                 printf( "Left Tangent:   Vertical: %i,%i, %i (%4.2f,%4.2f)\n",
1591 						vert_target[TANG_LEFT].mx, vert_target[TANG_LEFT].my, vert_target[TANG_LEFT].side,
1592 						vert_target[TANG_LEFT].x, vert_target[TANG_LEFT].y );
1593 				printf( "%s\n", tang_target_chosen_str[TANG_LEFT] );
1594 			}
1595         	if ( targets[TANG_RIGHT].exists ) {
1596                 printf( "Right Tangential Point: %4.2f,%4.2f\n",
1597                     tang_pts[TANG_RIGHT].x, tang_pts[TANG_RIGHT].y );
1598                 printf( "Right Tangent: Horizontal: %i,%i, %i (%4.2f,%4.2f)\n",
1599 						hori_target[TANG_RIGHT].mx, hori_target[TANG_RIGHT].my, hori_target[TANG_RIGHT].side,
1600 						hori_target[TANG_RIGHT].x, hori_target[TANG_RIGHT].y );
1601                 printf( "Right Tangent:   Vertical: %i,%i, %i (%4.2f,%4.2f)\n",
1602 						vert_target[TANG_RIGHT].mx, vert_target[TANG_RIGHT].my, vert_target[TANG_RIGHT].side,
1603 						vert_target[TANG_RIGHT].x, vert_target[TANG_RIGHT].y );
1604 				printf( "%s\n", tang_target_chosen_str[TANG_RIGHT] );
1605 			}
1606 			if ( side_str[0] != 0 ) printf( "BTW: %s\n", side_str );
1607 			printf( "-----\n" );
1608             ball_print_target_info( ball );
1609         	printf("*****\n");
1610 		printf( "\nYou encountered a bug! Please send this output to kulkanie@gmx.net. Thanks!\n" );
1611 		//exit(1);
1612 		/* move ball back to paddle as the current target is nonsense */
1613 		ball->target.exists = 0;
1614 		ball->idle_time = 0;
1615 		ball->moving_back = 1;
1616 		ball->return_allowed = 0;
1617 	}
1618 #endif
1619     }
1620 }
1621 /*
1622 ====================================================================
1623 Increase velocity acording to vel_change
1624 ====================================================================
1625 */
balls_inc_vel(int ms)1626 void balls_inc_vel( int ms )
1627 {
1628 	Ball *ball;
1629 
1630 	if ( cur_game->ball_v >= cur_game->ball_v_max ) return;
1631 
1632 	if ( !delay_timed_out( &cur_game->speedup_delay, ms ) ) return;
1633 
1634 	cur_game->ball_v += cur_game->diff->v_add;
1635 	cur_game->speedup_level++;
1636 
1637 	list_reset( cur_game->balls );
1638 	while ( ( ball = list_next( cur_game->balls ) ) != 0 ) {
1639 		if ( ball->attached ) continue;
1640 		vector_set_length( &ball->vel, cur_game->ball_v );
1641 	}
1642 }
1643 /*
1644 ====================================================================
1645 Return all balls that have ball->return_allowed True.
1646 ====================================================================
1647 */
balls_return(Paddle * paddle)1648 void balls_return( Paddle *paddle )
1649 {
1650 	Ball *ball;
1651 
1652 	list_reset( cur_game->balls );
1653 	while ( ( ball = list_next( cur_game->balls ) ) != 0 )
1654 		if ( ball->return_allowed && ball->paddle == paddle ) {
1655 			ball->moving_back = 1;
1656 			ball->target.exists = 0;
1657 			ball->return_allowed = 0;
1658 		}
1659 }
1660 
1661 /* set random starting angle for ball according to its paddle */
ball_set_random_angle(Ball * ball,double ball_v)1662 void ball_set_random_angle( Ball *ball, double ball_v )
1663 {
1664 	if ( ball->paddle->type == PADDLE_TOP )
1665 		ball->vel.y = 1.0;
1666 	else
1667 		ball->vel.y = -1.0;
1668 	ball->vel.x = (float)((rand() % 145) + 6);
1669 	if ( rand() % 2 )
1670 		ball->vel.x /= -100.0;
1671 	else
1672 		ball->vel.x /= 100.0;
1673 
1674 	/* only use 2 degree steps */
1675 	ball->angle = vec2angle( &ball->vel );
1676 	angle2vec( ball->angle, &ball->vel );
1677 	ball->vel.x *= ball_v; ball->vel.y *= ball_v;
1678 }
1679 
1680 /*
1681 ====================================================================
1682 Set velocity of all balls and get new targets if any.
1683 ====================================================================
1684 */
balls_set_velocity(List * balls,double vel)1685 void balls_set_velocity( List *balls, double vel )
1686 {
1687     Ball *b;
1688     double dist;
1689 
1690     list_reset( balls );
1691     while ( ( b = list_next( balls ) ) ) {
1692         vector_set_length( &b->vel, vel );
1693         if ( b->target.exists ) {
1694             dist = sqrt( SQUARE(b->cur.x - b->target.x) +
1695                     SQUARE(b->cur.y - b->target.y) );
1696             b->target.time = (int)floor(dist / vel);
1697             b->target.cur_tm = 0;
1698         }
1699     }
1700 }
1701 
1702 /*
1703 ====================================================================
1704 Detach all balls to the passed direction (-1 or 1) and return True
1705 if there were any balls detached. As balls within walls are not
1706 fired the result may differ from paddle::attached_ball_count!
1707 ====================================================================
1708 */
balls_detach_from_paddle(Paddle * paddle,int dir)1709 int balls_detach_from_paddle( Paddle *paddle, int dir )
1710 {
1711 	Ball *ball;
1712 	int fired = 0;
1713 
1714 	list_reset( cur_game->balls );
1715 	while ( ( ball = list_next( cur_game->balls ) ) ) {
1716 		if ( !ball->attached || ball->paddle != paddle )
1717 			continue;
1718 		/* balls in walls (hehe) are not fired */
1719 		if ( ball->x + paddle->x < BRICK_WIDTH )
1720 			continue;
1721 		if ( ball->x + ball_dia + paddle->x >= 640 - BRICK_WIDTH )
1722  			continue;
1723 		/* release ball */
1724 		ball->attached = 0;
1725 		ball->paddle->attached_ball_count--;
1726 		ball->moving_back = ball->idle_time = ball->return_allowed = 0;
1727 		ball->x += paddle->x;
1728 		ball->y += paddle->y;
1729 		ball->cur.x = ball->x;
1730 		ball->cur.y = ball->y;
1731 		if ( !cur_game->balls_use_random_angle ) {
1732 			/* when random angle is used the vector is not
1733 			 * changed but the one before the attachment is
1734 			 * used */
1735 			ball->vel.x = (float)dir;
1736 			if ( ball->paddle->type == PADDLE_TOP )
1737 				ball->vel.y = 1.2;
1738 			else
1739 				ball->vel.y = -1.2;
1740 			/* only use 2 degree steps */
1741 			ball->angle = vec2angle( &ball->vel );
1742 			angle2vec( ball->angle, &ball->vel );
1743 			vector_set_length( &ball->vel, cur_game->ball_v );
1744 		}
1745 		ball->get_target = 1;
1746 		fired = 1;
1747 	}
1748 
1749 	/* if no balls are attached anymore set last contact time */
1750 	if ( fired && paddle->attached_ball_count == 0 )
1751 		paddle->last_ball_contact = SDL_GetTicks();
1752 
1753 	return fired;
1754 }
1755