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