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