1 /*
2 * Zaz
3 * Copyright (C) Remigiusz Dybka 2009 <remigiusz.dybka@gmail.com>
4 *
5 Zaz is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Zaz is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "ballpath.h"
20 #include "game.h"
21
BallPath(Bezier path,GLuint * textures,Scenes::Mixer ** mixer,bool drawPath,double ballSizeFactor)22 BallPath::BallPath(Bezier path, GLuint *textures, Scenes::Mixer **mixer, bool drawPath, double ballSizeFactor)
23 : ballSize(BALLSIZE), path(path), drawPath(drawPath), hasGap(false), tex(textures), rollSound(0), playRoll(false), mixer(mixer),
24 extraBallFading(false), extraBallFadeinTimeout(0)
25 {
26 // std::vector<XY> pts = path.GenerateBalls(ballSize, stepsPerBall);
27 ballSize = (int)(ballSize * ballSizeFactor);
28 std::vector<XY> pts = path.GenerateUniform((double)ballSize / (double)stepsPerBall);
29 std::vector<XY>::iterator i;
30 for (i = pts.begin(); i != pts.end(); ++i)
31 ballPath.push_back(PathStep(i->x, i->y, 0, i->under, i->hidden));
32
33 pthLen = ballPath.size();
34 GenRotation();
35 }
36
~BallPath()37 BallPath::~BallPath()
38 {
39 }
40
GenRotation()41 void BallPath::GenRotation()
42 {
43 for (uint i = 1; i < pthLen - 1; ++i)
44 {
45 XY pt1 = XY(ballPath[i - 1].x, ballPath[i - 1].y);
46 XY pt2 = XY(ballPath[i + 1].x, ballPath[i + 1].y);
47
48 double x = pt2.x - pt1.x;
49 double y = (pt2.y - pt1.y);
50
51 ballPath[i].r = atan2(y, x) * (180.0 / PI);
52 }
53 }
54
DrawBonus()55 Bonus BallPath::DrawBonus()
56 {
57 int freq = bonusFrequency;
58 if (state.bonusFrequency > 0)
59 freq = state.bonusFrequency;
60
61 int b = (rand()%(int(BONUS_BOMB))) + 1;
62
63 int d = rand()%100;
64
65 if (d > 100 - freq)
66 {
67 if (b == BONUS_BOMB)
68 {
69 if (state.survival)
70 return BONUS_NONE;
71
72 if (!state.hadBonusBomb || state.kidsMode)
73 {
74 state.hadBonusBomb = true;
75 return BONUS_BOMB;
76 }
77
78 return BONUS_NONE;
79 }
80
81 if (b == BONUS_REVERSE)
82 {
83 if (state.survival)
84 return BONUS_NONE;
85
86 if (!state.hadBonusReverse || state.kidsMode)
87 {
88 state.hadBonusReverse = true;
89 return BONUS_REVERSE;
90 }
91
92 return BONUS_NONE;
93 }
94
95 if (b == BONUS_ACCURACY && state.kidsMode)
96 return BONUS_NONE;
97
98 return Bonus(b);
99 }
100
101 return BONUS_NONE;
102 }
103
DrawBall()104 Ball BallPath::DrawBall()
105 {
106 if (!reversedBalls.empty())
107 {
108 Ball db = reversedBalls.top();
109 reversedBalls.pop();
110 return db;
111 }
112
113 if (balls.size() < 2)
114 {
115 if (state.ballsToDraw > 0)
116 state.ballsToDraw--;
117
118 return Ball(rand() % state.colors, DrawBonus(), 0);
119 }
120
121 bool ok = false;
122 int b = 0;
123
124 while (!ok)
125 {
126 ok = true;
127 b = rand() % state.colors;
128
129 if ((b == balls[0].col)
130 && (b == balls[1].col))
131 ok = false;
132 }
133
134 if (state.ballsToDraw > 0)
135 state.ballsToDraw--;
136
137 return Ball(b, DrawBonus(), 0);
138 }
139
140
Logic()141 void BallPath::Logic()
142 {
143 if (extraBallFading)
144 {
145 if (!balls.empty())
146 {
147 extraBallFadeinTimeout--;
148 if (extraBallFadeinTimeout == 0)
149 {
150 extraBallFading = false;
151 balls.push_front(Ball(state.extraBall, BONUS_NONE, balls[0].upos() - (stepsPerBall)));
152 balls[0].size = stepsPerBall;
153 state.extraBall = -1;
154 }
155 }
156 else
157 {
158 extraBallFading = false;
159 state.extraBall = -1;
160 }
161 }
162
163 if (!explosions.empty())
164 {
165 vector<Explosion>::iterator i;
166 bool clear = true;
167
168 for (i = explosions.begin(); i != explosions.end(); ++i)
169 {
170 i->fc++;
171 if (i->fc > 2)
172 {
173 i->frame++;
174 i->fc = 0;
175 }
176 if (i->frame <= 16)
177 {
178 clear = false;
179 }
180 }
181
182 if (clear)
183 explosions.clear();
184 }
185
186 if (!pointSprites.empty())
187 {
188 vector<PointSprite>::iterator i;
189 bool clear = true;
190
191 for (i = pointSprites.begin(); i != pointSprites.end(); ++i)
192 {
193 if (i->time > 0)
194 {
195 clear = false;
196 i->time--;
197 i->Recalc();
198 }
199
200 if (i->time <= 0)
201 {
202 i->time = 0;
203 }
204 }
205
206 if (clear)
207 pointSprites.clear();
208 }
209
210 if (state.bonusPause)
211 {
212 state.bonusPauseTime--;
213 if (state.bonusPauseTime < 0)
214 state.bonusPause = false;
215 }
216
217 if (state.bonusSlow)
218 {
219 state.bonusSlowTime--;
220 if (state.bonusSlowTime < 0)
221 state.bonusSlow = false;
222 }
223
224 if (playRoll || state.speedUp)
225 {
226 if (rollSound == 0)
227 if (mixer != NULL)
228 rollSound = (*mixer)->EnqueueSample (sfx_roll, sfxVol, 0, true);
229 }
230 else
231 {
232 if (rollSound != 0)
233 {
234 rollSound->Stop();
235 rollSound = 0;
236 }
237 }
238
239 if ((state.bonusReverseBallsLeft > 0) && (!state.speedUp))
240 {
241 if (balls.size() == 0)
242 {
243 state.bonusReverseBallsLeft = 0;
244 state.bonusPause = false;
245 state.bonusSlow = false;
246 }
247 else
248 {
249 playRoll = true;
250
251 if (balls[0].pos == 0)
252 {
253 reversedBalls.push(balls[0]);
254 balls.erase(balls.begin());
255 state.bonusReverseBallsLeft--;
256 return;
257 }
258 else
259 {
260 balls[0].pos-=maxPullSpeed;
261 if (balls[0].pos < 0)
262 balls[0].pos = 0;
263 }
264
265 Attract();
266 Eliminate();
267 return;
268 }
269 }
270
271 if (state.ballsFromStart > 0)
272 {
273 if (balls.size() > 0)
274 {
275 if (balls[0].pos >= stepsPerBall)
276 {
277 Ball db = DrawBall();
278 balls.push_front(Ball(db.col, db.bonus, 0));
279 --state.ballsFromStart;
280 }
281 }
282 else
283 {
284 Ball db = DrawBall();
285 balls.push_front(Ball(db.col, db.bonus, 0));
286 --state.ballsFromStart;
287 }
288 }
289
290 if (!state.ballOut)
291 {
292 if (balls.size() == 0)
293 {
294 if (state.ballsToDraw == -1 || state.ballsToDraw > 0 || !reversedBalls.empty())
295 {
296 Ball db = DrawBall();
297 balls.push_front(Ball(db.col, db.bonus, 0));
298 }
299 }
300
301 if (!balls.empty())
302 if (balls[0].pos >= stepsPerBall)
303 {
304 if (state.ballsToDraw == -1 || state.ballsToDraw > 0 || !reversedBalls.empty())
305 {
306 Ball db = DrawBall();
307 balls.push_front(Ball(db.col, db.bonus, 0));
308 }
309 else
310 {
311 if (state.extraBall != -1)
312 if (balls[0].pos > STEPSPERBALL * 2)
313 if (!extraBallFading)
314 {
315 extraBallFadeinTimeout = extraBallFadeinTime;
316 extraBallFading = true;
317 if (mixer != NULL)
318 (*mixer)->EnqueueSample(sfx_extraball, sfxVol);
319 }
320 }
321 }
322 }
323
324
325 // this could happen...
326 if (state.extraBall != -1 && balls.empty())
327 {
328 extraBallFading = false;
329 state.extraBall = -1;
330 }
331
332 if (state.ballsFromStart > 0)
333 {
334 Drive(stepsPerBall / 4);
335 }
336 else
337 {
338 if (state.speedUp)
339 {
340 Drive(state.feedRate * speedUpMultiplier);
341 state.bonusPause = false;
342 state.bonusSlow = false;
343 state.bonusReverseBallsLeft = 0;
344 }
345 else
346 {
347 Drive(state.feedRate);
348 }
349 Attract();
350 Eliminate();
351 }
352 }
353
Drive(double d)354 void BallPath::Drive(double d)
355
356 {
357 if (state.bonusPause)
358 d = 0.0;
359
360 if (state.bonusSlow)
361 d/=2.0;
362
363 if (balls.empty())
364 return;
365
366 if (d > 10)
367 playRoll = true;
368
369 balls[0].pos+=d;
370 balls[0].frame+= ((double)ballAnimFrames/(double)STEPSPERBALL/2.0) * (double)d;
371
372 if (!balls[0].elim)
373 {
374 if (balls[0].pos < stepsPerBall)
375 {
376 if (balls[0].size < stepsPerBall)
377 balls[0].size += (int)d; //ballGrowSpeed;
378 }
379 else
380 {
381 balls[0].size += ballGrowSpeed;
382 }
383 }
384
385 if (balls[0].size > stepsPerBall)
386 balls[0].size = stepsPerBall;
387
388 if (balls[0].frame > (double)ballAnimFrames)
389 balls[0].frame -= (double)ballAnimFrames;
390
391 for (uint b = 1; b < balls.size(); b++)
392 {
393 if (!balls[b].elim)
394 if (balls[b].size < stepsPerBall)
395 balls[b].size += ballGrowSpeed;
396
397 if (balls[b].size > stepsPerBall)
398 balls[b].size = stepsPerBall;
399
400 if ((balls[b].pos - balls[b - 1].pos) < balls[b - 1].size)
401 {
402 balls[b]. pos = balls[b - 1].pos + balls[b - 1].size;
403 balls[b].frame += ((double)ballAnimFrames/(double)STEPSPERBALL/2.0) * (double)d;
404 if (balls[b].frame > (double)ballAnimFrames)
405 balls[b].frame -= (double)ballAnimFrames;
406 }
407 }
408
409 // is a ball outside
410 int lball = balls.size() - 1;
411 if (balls[lball].upos() >= pthLen)
412 {
413 state.ballOut = true;
414 deque<Ball>::iterator i;
415 i = balls.begin();
416 i+=lball;
417
418 balls.erase(i);
419 }
420 }
421
InsertBall(ShotAddr addr,int col,Bonus bonus)422 void BallPath::InsertBall(ShotAddr addr, int col, Bonus bonus)
423 {
424 state.comboCnt = 0;
425 state.score -= 5;
426
427 if (addr.pos == -1)
428 {
429 balls.push_front(Ball(col, bonus, (uint)balls[0].pos - ballGrowSpeed));
430 balls[0].size = ballGrowSpeed;
431 return;
432 }
433
434 if (addr.front && addr.pos == 0)
435 addr.front = false;
436
437 if ((uint)addr.pos < balls.size())
438 {
439 if (addr.front)
440 {
441 balls[addr.pos].pos+=ballGrowSpeed;
442
443 for (int b = addr.pos; (uint)b < balls.size(); b++)
444 {
445 if ((balls[b].pos - balls[b - 1].pos) < stepsPerBall)
446 balls[b].pos = balls[b - 1].pos + stepsPerBall;
447 }
448
449 balls.push_back(balls[balls.size() - 1]);
450
451 for (int b = balls.size() - 1; b > addr.pos; --b)
452 balls[b] = balls[b - 1];
453
454 balls[addr.pos].col = col;
455 balls[addr.pos].bonus = bonus;
456 balls[addr.pos].size = ballGrowSpeed;
457 balls[addr.pos].elim = false;
458 }
459 else
460 {
461 balls.push_back(balls[balls.size() - 1]);
462
463 for (int b = balls.size() - 1; b > addr.pos; --b)
464 balls[b] = balls[b - 1];
465
466 addr.pos++;
467 balls[addr.pos].pos+=ballGrowSpeed;
468
469 for (int b = addr.pos + 1; (uint)b < balls.size(); b++)
470 {
471 if ((balls[b].pos - balls[b - 1].pos) < stepsPerBall)
472 balls[b].pos = balls[b - 1].pos + stepsPerBall;
473 }
474
475 balls[addr.pos].col = col;
476 balls[addr.pos].bonus = bonus;
477 balls[addr.pos].size = ballGrowSpeed;
478 balls[addr.pos].elim = false;
479 }
480 }
481 else
482 {
483 balls.push_back(balls[balls.size() - 1]);
484 balls[addr.pos].pos+=ballGrowSpeed;
485 balls[addr.pos].col = col;
486 balls[addr.pos].bonus = bonus;
487 balls[addr.pos].elim = false;
488
489 balls[addr.pos].size = ballGrowSpeed;
490 }
491 }
492
Eliminate()493 void BallPath::Eliminate()
494 {
495 bool updateCombo = false;
496
497 for (uint b = 0; b < balls.size(); ++b)
498 {
499 int score = 0;
500
501 if (balls[b].elim)
502 balls[b].size -= ballGrowSpeed;
503
504 if (balls[b].size < 0)
505 {
506 deque<Ball>::iterator i;
507 i = balls.begin();
508 i += b;
509
510 int pan = (int)ballPath[balls[b].upos()].x - 50;
511
512 if (balls[b].bonus == BONUS_PAUSE)
513 {
514 state.bonusPauseTime = bonusPauseTimeout;
515 state.bonusPause = true;
516 if (mixer != NULL)
517 (*mixer)->EnqueueSample(sfx_bonus, sfxVol, pan);
518 }
519
520 if (balls[b].bonus == BONUS_SLOW)
521 {
522 state.bonusSlowTime = bonusSlowTimeout;
523 state.bonusSlow = true;
524 if (mixer != NULL)
525 (*mixer)->EnqueueSample(sfx_bonus, sfxVol, pan);
526 }
527
528 if (balls[b].bonus == BONUS_REVERSE)
529 {
530 state.bonusReverseBallsLeft += bonusReverseBalls;
531 if (mixer != NULL)
532 (*mixer)->EnqueueSample(sfx_bonus, sfxVol, pan);
533 }
534
535 if (balls[b].bonus == BONUS_ACCURACY)
536 {
537 state.accuracyShotTriggered = true;
538 if (mixer != NULL)
539 (*mixer)->EnqueueSample(sfx_bonus, sfxVol, pan);
540 }
541
542 if (balls[b].bonus == BONUS_BOMB)
543 {
544 int bdist = bombDistance;
545 // if (state.kidsMode)
546 // bdist *= 1.5;
547
548 int bs = (int)balls[b].pos - bdist * stepsPerBall;
549 int be = (int)balls[b].pos + bdist * stepsPerBall;
550
551 if (bs < 0)
552 bs = 0;
553
554 if (be >= (int)pthLen)
555 be = pthLen;
556
557 for (uint f = 0; f < balls.size(); ++f)
558 {
559 if (balls[f].pos >= bs && balls[f].pos <= be)
560 {
561 balls[f].elim = true;
562 balls[f].elbomb = true;
563 balls[f].explode = true;
564 }
565 }
566
567 if (mixer != NULL)
568 (*mixer)->EnqueueSample(sfx_bonus, sfxVol, pan);
569 }
570
571 if (!balls[b].elbomb) // don't count bombs for combos
572 {
573 updateCombo = true;
574 state.comboCnt++;
575 }
576 else
577 {
578 state.comboCnt = 0;
579 }
580
581 if ((state.comboCnt > 3) && (!balls[b].elbomb))
582 {
583 score = scoreElimination * (state.comboCnt - 2);
584 state.score += score;
585 }
586 else
587 {
588 score = scoreElimination;
589 state.score += score;
590 }
591
592 double x = ballPath[balls[b].upos()].x;
593 double y = ballPath[balls[b].upos()].y;
594
595 pointSprites.push_back(PointSprite(score, x, y));
596 balls.erase(i);
597 }
598 }
599
600 if (updateCombo)
601 {
602 if (state.comboCnt > 3)
603 if (mixer != NULL)
604 (*mixer)->EnqueueSample(sfx_combo, sfxVol);
605 }
606
607 if (balls.size() < 3)
608 return;
609
610 for (uint b = 0; b < balls.size() - 2; ++b)
611 {
612 int col = balls[b].col;
613
614 if ((balls[b + 1].col == col)
615 && (balls[b + 2].col == col)
616 /* && (balls[b].size == stepsPerBall)
617 && (balls[b + 1].size == stepsPerBall)
618 && (balls[b + 2].size == stepsPerBall)
619 */ && (balls[b + 2].pos - balls[b + 1].pos < (double)STEPSPERBALL * 1.01)
620 && (balls[b + 1].pos - balls[b].pos < (double)STEPSPERBALL * 1.01))
621 {
622 if (!balls[b].elim)
623 balls[b].explode = true;
624
625 if (!balls[b + 1].elim)
626 balls[b + 1].explode = true;
627
628 if (!balls[b + 2].elim)
629 balls[b + 2].explode = true;
630
631 balls[b].elim = true;
632 balls[b + 1].elim = true;
633 balls[b + 2].elim = true;
634 }
635 }
636
637 // trigger explosions
638 for (uint b = 0; b < balls.size(); ++b)
639 {
640 if (balls[b].explode)
641 {
642 balls[b].explode = false;
643 double x = ballPath[balls[b].upos()].x;
644 double y = ballPath[balls[b].upos()].y;
645
646 explosions.push_back(Explosion(x, y));
647
648 int pan = (int)x - 50;
649
650 if (mixer != NULL)
651 (*mixer)->EnqueueSample(sfx_eliminate, sfxVol, pan);
652 }
653 }
654
655 }
656
Attract()657 void BallPath::Attract()
658 {
659 bool fullSpeed = false;
660
661 if (state.bonusReverseBallsLeft > 0)
662 fullSpeed = true;
663
664 playRoll = false;
665
666 if (balls.size() < 2)
667 return;
668
669 if (hasGap)
670 {
671 waitAttract--;
672 }
673
674 // do we have a gap ?
675 bool gap = false;
676 int gapball = 0;
677
678 for (uint b = 0; b < balls.size() - 1; ++b)
679 {
680 int d = (int)iround(balls[b + 1].pos - balls[b].pos);
681 if ((d > stepsPerBall) && (d < stepsPerBall * maxBallDistanceToAttract))
682 {
683 gap = true;
684 gapball = b + 1;
685 }
686 }
687
688 if (!gap)
689 {
690 hasGap = false;
691 return;
692 }
693
694 if (!hasGap)
695 {
696 waitAttract = attractDelay;
697 hasGap = true;
698 pullSpeed = 2.0;
699 }
700
701 if (!fullSpeed)
702 if (waitAttract > 0)
703 return;
704
705 // has a gap and it's time to do some magnetic action
706 playRoll = true;
707 pullSpeed *= 1.02;
708 if (pullSpeed > maxPullSpeed)
709 pullSpeed = maxPullSpeed;
710
711 if (fullSpeed)
712 pullSpeed = maxPullSpeed;
713
714 for (uint b = gapball; b < balls.size(); ++b)
715 {
716 balls[b].pos -= pullSpeed;
717 balls[b].frame -= pullSpeed / 2;
718 if (balls[b].frame < 0)
719 balls[b].frame += ballAnimFrames;
720 }
721
722 if ((balls[gapball].pos - balls[gapball - 1].pos) < (double)stepsPerBall)
723 {
724 int push = (int)(stepsPerBall - (balls[gapball].pos - balls[gapball - 1].pos));
725 for (uint b = gapball; b < balls.size(); ++b)
726 {
727 balls[b].pos += push;
728 balls[b].frame += push / 2;
729 if (balls[b].frame > ballAnimFrames)
730 balls[b].frame -= ballAnimFrames;
731 }
732 }
733 }
734
Render(bool render_balls,bool render_sprites,bool render_under,bool render_over)735 void BallPath::Render(bool render_balls, bool render_sprites, bool render_under, bool render_over)
736 {
737 glLoadIdentity();
738 // balls
739 glEnable(GL_TEXTURE_2D);
740 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
741 if (render_balls)
742 for (uint i = 0; i < balls.size(); ++i)
743 {
744 uint pos = balls[i].upos();
745
746 int fr = (int)balls[i].frame;
747 if (fr > ballAnimFrames - 1)
748 fr = 0;
749
750 // double ts = (ballOneTextureSize * 8.0) / double(ballTextureSize);
751 double ts = 1.0 / double(ballTextureCount);
752 double tx = (double)((fr % ballTextureCount)) * ts;
753 double ty = (double)((fr / ballTextureCount)) * ts;
754 //ts *= 0.1;
755
756 double bs = ((double)balls[i].size / (stepsPerBall)) * ballSize;
757
758 if (balls[i].pos < stepsPerBall)
759 {
760 bs = ((double)balls[i].pos / (stepsPerBall / 4)) * ballSize;
761 if (bs > ballSize)
762 bs = ballSize;
763 }
764
765 if (balls[i].pos > pthLen - stepsPerBall)
766 {
767 bs = ((double)(balls[i].pos - (pthLen - stepsPerBall))/ (stepsPerBall / 4)) * ballSize;
768 bs = ballSize - bs;
769 if (bs < 0.0)
770 bs = 0.0;
771 }
772
773 bool render_this_ball = false;
774
775 if (pos < pthLen)
776 {
777 render_this_ball = true;
778
779 if ((ballPath[pos].under || ballPath[pos].hidden) && !render_under)
780 render_this_ball = false;
781
782 if ((!ballPath[pos].under && !ballPath[pos].hidden) && !render_over)
783 render_this_ball = false;
784 }
785
786 if (render_this_ball)
787 {
788 glLoadIdentity();
789 glTranslated(ballPath[pos].x, ballPath[pos].y, 2);
790 glBindTexture(GL_TEXTURE_2D, tex[balls[i].col]);
791 if (ballPath[pos].under || ballPath[pos].hidden)
792 glTranslated(0.0, 0.0, -1);
793
794 glScaled(bs, bs, bs);
795
796 glRotated(ballPath[pos].r, 0, 0, 1.0);
797
798 glBegin(GL_QUADS);
799 glTexCoord2d(tx, ty);
800 glVertex3d(-0.5, 0.5, 0);
801 glTexCoord2d(tx, ty + ts);
802 glVertex3d(-0.5, -0.5, 0);
803 glTexCoord2d(tx + ts, ty + ts);
804 glVertex3d(0.5, -0.5, 0);
805 glTexCoord2d(tx + ts, ty);
806 glVertex3d(0.5, 0.5, 0);
807 glEnd();
808
809 if (balls[i].bonus != BONUS_NONE)
810 {
811 glLoadIdentity();
812 glTranslated(ballPath[pos].x, ballPath[pos].y, 2.1);
813
814 if (ballPath[pos].under || ballPath[pos].hidden)
815 glTranslated(0.0, 0.0, -1);
816
817 glScaled(bs, bs, bs);
818 glRotated(ballPath[pos].r, 0, 0, 1.0);
819 glBindTexture(GL_TEXTURE_2D, tex[NBALLCOLORS + (balls[i].bonus - 1) ]);
820 glBegin(GL_QUADS);
821 glTexCoord2d(0, 0);
822 glVertex3d(-0.5, 0.5, 0);
823 glTexCoord2d(0, 1);
824 glVertex3d(-0.5, -0.5, 0);
825 glTexCoord2d(1, 1);
826 glVertex3d(0.5, -0.5, 0);
827 glTexCoord2d(1, 0);
828 glVertex3d(0.5, 0.5, 0);
829 glEnd();
830 }
831 }
832 }
833
834 // extraball
835 if (render_balls)
836 if (!balls.empty())
837 if (extraBallFading && balls[0].pos > stepsPerBall)
838 {
839 float alpha = float((extraBallFadeinTime - extraBallFadeinTimeout)) / float(extraBallFadeinTime);
840 int pos = (int)(alpha * (balls[0].pos - stepsPerBall));
841
842 if (pos < 0)
843 pos = 0;
844
845 double bs = ((double)balls[0].size / (stepsPerBall)) * ballSize;
846 double x = ballPath[pos].x;
847 double y = ballPath[pos].y;
848 double ts = 1.0 / double(ballTextureCount);
849
850 glLoadIdentity();
851 glTranslated(x, y, 2);
852
853 bool render_this_ball = true;
854
855 if ((ballPath[pos].under || ballPath[pos].hidden) && !render_under)
856 render_this_ball = false;
857
858 if ((!ballPath[pos].under && !ballPath[pos].hidden) && !render_over)
859 render_this_ball = false;
860
861 if (ballPath[pos].under || ballPath[pos].hidden)
862 glTranslated(0.0, 0.0, -1);
863
864 if (render_this_ball)
865 {
866 glRotated(ballPath[pos].r, 0, 0, 1.0);
867 glScaled(bs, bs, bs);
868 glColor4f(1.0f, 1.0f, 1.0f, alpha);
869 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
870 glBindTexture(GL_TEXTURE_2D, tex[state.extraBall]);
871 glBegin(GL_QUADS);
872 glTexCoord2d(0, 0);
873 glVertex3d(-0.5, 0.5, 0);
874 glTexCoord2d(0, ts);
875 glVertex3d(-0.5, -0.5, 0);
876 glTexCoord2d(ts, ts);
877 glVertex3d(0.5, -0.5, 0);
878 glTexCoord2d(ts, 0);
879 glVertex3d(0.5, 0.5, 0);
880 glEnd();
881 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
882 }
883 }
884
885 // explosions
886 vector<Explosion>::iterator expl;
887
888 double z = 20;
889 if (render_sprites)
890 for (expl = explosions.begin(); expl != explosions.end(); ++expl)
891 {
892 if (expl->frame < 17)
893 {
894 double tx = (expl->frame % 4);
895 double ty = (expl->frame / 4);
896
897 tx*=0.25;
898 ty*=0.25;
899
900 glLoadIdentity();
901 glTranslated(expl->x, expl->y, z);
902 glRotated(expl->r, 0.0, 0.0, 1.0);
903 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
904 glBindTexture(GL_TEXTURE_2D, tex[NBALLCOLORS + BONUS_BOMB]);
905 // glBindTexture(GL_TEXTURE_2D, tex[NBALLCOLORS + 1]);
906 glColor4d(1.0, 1.0, 1.0, 0.7);
907 glScaled(expl->s, expl->s, 1.0);
908
909 glBegin(GL_QUADS);
910 glTexCoord2d(tx, ty);
911 glVertex3d(-0.5, 0.5, 0);
912 glTexCoord2d(tx, ty + 0.25);
913 glVertex3d(-0.5, -0.5, 0);
914 glTexCoord2d(tx + 0.25, ty + 0.25);
915 glVertex3d(0.5, -0.5, 0);
916 glTexCoord2d(tx + 0.25, ty);
917 glVertex3d(0.5, 0.5, 0);
918
919 glEnd();
920 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
921 }
922 z+=1.0;
923 }
924
925 // pointsprites
926 vector<PointSprite>::iterator psi;
927 if (render_sprites)
928 for (psi = pointSprites.begin(); psi != pointSprites.end(); ++psi)
929 {
930 if (psi->time)
931 {
932 float alpha = float(psi->time) / float(pointSpriteFadeoutTime);
933
934 glColor4f(1.0, 1.0, 1.0, alpha);
935 glLoadIdentity();
936 glTranslated(psi->x, psi->y, 50);
937 glScaled(0.1, 0.1, 0.1);
938
939 char scoretxt[256];
940 sprintf(scoretxt, "%d", psi->points);
941 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
942 font3->Render(scoretxt);
943 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
944 }
945 }
946
947 glDisable(GL_TEXTURE_2D);
948 }
949
Pick(double x,double y,double vx,double vy)950 int BallPath::Pick(double x, double y, double vx, double vy)
951 {
952 int ret = -1;
953 double ldist = 200;
954 bool outside = false;
955 double t = 1.0;
956 uint nballs = balls.size();
957
958 while (!outside)
959 {
960 double px = x + t * vx;
961 double py = y + t * vy;
962
963 if (px > 130 || px < -30 || py > 130 || py < -30)
964 {
965 outside = true;
966 continue;
967 }
968
969 for (uint b = 0; b < nballs; ++b)
970 {
971 if (balls[b].elim)
972 continue;
973
974 if (ballPath[balls[b].upos()].hidden)
975 continue;
976
977 double bx = ballPath[balls[b].upos()].x;
978 double by = ballPath[balls[b].upos()].y;
979
980 if (fabs(px - bx) < ((double)ballSize / 1.5) && fabs(py - by) < ((double)ballSize / 1.5))
981 {
982 double dist = sqrt(pow(fabs(bx - x), 2.0) + pow(fabs(by - y), 2.0));
983
984 if (dist < ldist)
985 {
986 return b;
987 /*ret = b;
988 ldist = dist;*/
989 }
990 }
991 }
992
993 t+=2.0;
994 }
995
996 return ret;
997 }
998
PickShot(double x,double y)999 ShotAddr BallPath::PickShot(double x, double y)
1000 {
1001 int ret = -1;
1002 double ldist = 200;
1003 bool front = true;
1004
1005 for (uint b = 0; b < balls.size(); ++b)
1006 {
1007 double bx = ballPath[balls[b].upos()].x;
1008 double by = ballPath[balls[b].upos()].y;
1009
1010 if (ballPath[balls[b].upos()].hidden)
1011 continue;
1012
1013 if (fabs(x - bx) < ((double)ballSize) && fabs(y - by) < ((double)ballSize))
1014 {
1015 double dist = sqrt(pow(fabs(bx - x), 2.0) + pow(fabs(by - y), 2.0));
1016
1017 double distf;
1018 if (balls[b].upos() > 0)
1019 {
1020 bx = ballPath[balls[b].upos() - 1].x;
1021 by = ballPath[balls[b].upos() - 1].y;
1022 distf = sqrt(pow(fabs(bx - x), 2.0) + pow(fabs(by - y), 2.0));
1023 }
1024 else
1025 {
1026 bx = ballPath[balls[b].upos()].x;
1027 by = ballPath[balls[b].upos()].y;
1028 distf = sqrt(pow(fabs(bx - x), 2.0) + pow(fabs(by - y), 2.0));
1029 }
1030
1031 bx = ballPath[balls[b].upos() + 1].x;
1032 by = ballPath[balls[b].upos() + 1].y;
1033 double distb = sqrt(pow(fabs(bx - x), 2.0) + pow(fabs(by - y), 2.0));
1034
1035 if (dist < ldist)
1036 {
1037 ret = b;
1038 ldist = dist;
1039
1040 if (distf < distb)
1041 {
1042 front = true;
1043 }
1044 else
1045 {
1046 front = false;
1047 }
1048 }
1049 }
1050 }
1051
1052 return ShotAddr(ret, front);
1053 }
1054