1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 ** * Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** * Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in
18 ** the documentation and/or other materials provided with the
19 ** distribution.
20 ** * Neither the name of The Qt Company Ltd nor the names of its
21 ** contributors may be used to endorse or promote products derived
22 ** from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 /*
42 * KAsteroids - Copyright (c) Martin R. Jones 1997
43 *
44 * Part of the KDE project
45 */
46
47 #include <stdlib.h>
48 #include <math.h>
49 #include <qapplication.h>
50 #include <qnamespace.h>
51 #include <QAction>
52 #include <QMessageBox>
53 #include <QScrollArea>
54 #include <QDir>
55 #include <QGraphicsItem>
56 #include <QTimerEvent>
57 #include <QPixmap>
58 #include <QResizeEvent>
59 #include <QShowEvent>
60 #include <QtDebug>
61
62 #include "view.h"
63
64 #define IMG_BACKGROUND ":/trolltech/examples/graphicsview/portedasteroids/bg.png"
65
66 #define REFRESH_DELAY 33
67 #define SHIP_SPEED 0.3
68 #define MISSILE_SPEED 10.0
69 #define SHIP_STEPS 64
70 #define ROTATE_RATE 2
71 #define SHIELD_ON_COST 1
72 #define SHIELD_HIT_COST 30
73 #define BRAKE_ON_COST 4
74
75 #define MAX_ROCK_SPEED 2.5
76 #define MAX_POWERUP_SPEED 1.5
77 #define MAX_SHIP_SPEED 12
78 #define MAX_BRAKES 5
79 #define MAX_SHIELDS 5
80 #define MAX_FIREPOWER 5
81
82 #define TEXT_SPEED 4
83
84 #define PI_X_2 6.283185307
85 #ifndef M_PI
86 #define M_PI 3.141592654
87 #endif
88
89 static struct
90 {
91 int id;
92 const char *path;
93 int frames;
94 }
95 kas_animations [] =
96 {
97 { ID_ROCK_LARGE, "rock1/rock1%1.png", 32 },
98 { ID_ROCK_MEDIUM, "rock2/rock2%1.png", 32 },
99 { ID_ROCK_SMALL, "rock3/rock3%1.png", 32 },
100 { ID_SHIP, "ship/ship%1.png", 32 },
101 { ID_MISSILE, "missile/missile.png", 1 },
102 { ID_BIT, "bits/bits%1.png", 16 },
103 { ID_EXHAUST, "exhaust/exhaust.png", 1 },
104 { ID_ENERGY_POWERUP, "powerups/energy.png", 1 },
105 // { ID_TELEPORT_POWERUP, "powerups/teleport%1.png", 12 },
106 { ID_BRAKE_POWERUP, "powerups/brake.png", 1 },
107 { ID_SHIELD_POWERUP, "powerups/shield.png", 1 },
108 { ID_SHOOT_POWERUP, "powerups/shoot.png", 1 },
109 { ID_SHIELD, "shield/shield%1.png", 6 },
110 { 0, 0, 0 }
111 };
112
KAsteroidsView(QWidget * parent)113 KAsteroidsView::KAsteroidsView( QWidget *parent)
114 : QWidget( parent),
115 field(0, 0, 640, 440),
116 view(&field, this)
117 {
118 view.setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
119 view.setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
120 view.setCacheMode(QGraphicsView::CacheBackground);
121 view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
122 view.setOptimizationFlags(QGraphicsView::DontClipPainter
123 | QGraphicsView::DontSavePainterState
124 | QGraphicsView::DontAdjustForAntialiasing);
125 view.viewport()->setFocusProxy( this );
126
127 QPixmap pm( IMG_BACKGROUND );
128 field.setBackgroundBrush( pm );
129
130 textSprite = new QGraphicsTextItem( 0, &field );
131 QFont font( "helvetica", 18 );
132 textSprite->setFont( font );
133 textSprite->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
134
135 shield = 0;
136 shieldOn = FALSE;
137 refreshRate = REFRESH_DELAY;
138
139 initialized = readSprites();
140
141 shieldTimer = new QTimer( this );
142 connect( shieldTimer, SIGNAL(timeout()), this, SLOT(hideShield()) );
143 mTimerId = -1;
144
145 shipPower = MAX_POWER_LEVEL;
146 vitalsChanged = TRUE;
147 can_destroy_powerups = FALSE;
148
149 mPaused = TRUE;
150
151 if ( !initialized ) {
152 textSprite->setHtml( tr("<font color=red>Error: Cannot read sprite images</font>") );
153 textSprite->setPos( (field.width()-textSprite->boundingRect().width()) / 2,
154 (field.height()-textSprite->boundingRect().height()) / 2 );
155 }
156 }
157
158 // - - -
159
~KAsteroidsView()160 KAsteroidsView::~KAsteroidsView()
161 {
162 qDeleteAll(rocks); rocks.clear();
163 qDeleteAll(missiles); missiles.clear();
164 qDeleteAll(bits); bits.clear();
165 qDeleteAll(powerups); powerups.clear();
166 qDeleteAll(exhaust); exhaust.clear();
167 }
168
169 // - - -
170
reset()171 void KAsteroidsView::reset()
172 {
173 if ( !initialized )
174 return;
175 qDeleteAll(rocks); rocks.clear();
176 qDeleteAll(missiles); missiles.clear();
177 qDeleteAll(bits); bits.clear();
178 qDeleteAll(powerups); powerups.clear();
179 qDeleteAll(exhaust); exhaust.clear();
180
181 shotsFired = 0;
182 shotsHit = 0;
183
184 rockSpeed = 1.0;
185 powerupSpeed = 1.0;
186 mFrameNum = 0;
187 mPaused = FALSE;
188
189 ship->hide();
190 shield->hide();
191 /*
192 if ( mTimerId >= 0 ) {
193 killTimer( mTimerId );
194 mTimerId = -1;
195 }
196 */
197 }
198
199 // - --
200
newGame()201 void KAsteroidsView::newGame()
202 {
203 if ( !initialized )
204 return;
205 if ( shieldOn )
206 {
207 shield->hide();
208 shieldOn = FALSE;
209 }
210 reset();
211 if ( mTimerId < 0 )
212 mTimerId = startTimer( REFRESH_DELAY );
213 emit updateVitals();
214 }
215
216 // - - -
217
endGame()218 void KAsteroidsView::endGame()
219 {
220 qDeleteAll(rocks); rocks.clear();
221 qDeleteAll(missiles); missiles.clear();
222 qDeleteAll(bits); bits.clear();
223 qDeleteAll(powerups); powerups.clear();
224 qDeleteAll(exhaust); exhaust.clear();
225 }
226
pause(bool p)227 void KAsteroidsView::pause( bool p )
228 {
229 if ( !initialized )
230 return;
231 if ( !mPaused && p ) {
232 if ( mTimerId >= 0 ) {
233 killTimer( mTimerId );
234 mTimerId = -1;
235 }
236 } else if ( mPaused && !p )
237 mTimerId = startTimer( REFRESH_DELAY );
238 mPaused = p;
239 }
240
241 // - - -
242
newShip()243 void KAsteroidsView::newShip()
244 {
245 if ( !initialized )
246 return;
247 ship->setPos( width()/2, height()/2 );
248 ship->setFrame( 0 );
249 shield->setPos( width()/2, height()/2 );
250 shield->setFrame( 0 );
251 ship->setVelocity( 0.0, 0.0 );
252 shipDx = 0;
253 shipDy = 0;
254 shipAngle = 0;
255 rotateL = FALSE;
256 rotateR = FALSE;
257 thrustShip = FALSE;
258 shootShip = FALSE;
259 brakeShip = FALSE;
260 teleportShip = FALSE;
261 shieldOn = TRUE;
262 shootDelay = 0;
263 shipPower = MAX_POWER_LEVEL;
264 rotateRate = ROTATE_RATE;
265 rotateSlow = 0;
266
267 mBrakeCount = 0;
268 mTeleportCount = 0;
269 mShootCount = 0;
270
271 ship->show();
272 shield->show();
273 mShieldCount = 1; // just in case the ship appears on a rock.
274 shieldTimer->start(1000);
275 }
276
setShield(bool s)277 void KAsteroidsView::setShield( bool s )
278 {
279 if ( !initialized )
280 return;
281 if ( shieldTimer->isActive() && !s ) {
282 shieldTimer->stop();
283 hideShield();
284 } else {
285 shieldOn = s && mShieldCount;
286 }
287 }
288
brake(bool b)289 void KAsteroidsView::brake( bool b )
290 {
291 if ( !initialized )
292 return;
293 if ( mBrakeCount )
294 {
295 if ( brakeShip && !b )
296 {
297 rotateL = FALSE;
298 rotateR = FALSE;
299 thrustShip = FALSE;
300 rotateRate = ROTATE_RATE;
301 }
302
303 brakeShip = b;
304 }
305 }
306
307 // - - -
308
readSprites()309 bool KAsteroidsView::readSprites()
310 {
311 QString sprites_prefix = ":/trolltech/examples/graphicsview/portedasteroids/sprites/";
312
313 int i = 0;
314 while ( kas_animations[i].id )
315 {
316 QList<QPixmap> anim;
317 QString wildcard = sprites_prefix + kas_animations[i].path;
318 wildcard.replace("%1", "*");
319 QFileInfo fi(wildcard);
320 foreach (QString entry, QDir(fi.path(), fi.fileName()).entryList())
321 anim << QPixmap(fi.path() + "/" + entry);
322 animation.insert( kas_animations[i].id, anim );
323 i++;
324 }
325
326 ship = new AnimatedPixmapItem( animation[ID_SHIP], &field );
327 ship->hide();
328
329 shield = new KShield( animation[ID_SHIELD], &field );
330 shield->hide();
331
332 return (!ship->image(0).isNull() && !shield->image(0).isNull());
333 }
334
335 // - - -
336
addRocks(int num)337 void KAsteroidsView::addRocks( int num )
338 {
339 if ( !initialized )
340 return;
341 for ( int i = 0; i < num; i++ )
342 {
343 KRock *rock = new KRock( animation[ID_ROCK_LARGE], &field,
344 ID_ROCK_LARGE, randInt(2), randInt(2) ? -1 : 1 );
345 double dx = (2.0 - randDouble()*4.0) * rockSpeed;
346 double dy = (2.0 - randDouble()*4.0) * rockSpeed;
347 rock->setVelocity( dx, dy );
348 rock->setFrame( randInt( rock->frameCount() ) );
349 if ( dx > 0 )
350 {
351 if ( dy > 0 )
352 rock->setPos( 5, 5 );
353 else
354 rock->setPos( 5, field.height() - 25 );
355 rock->setFrame( 0 );
356 }
357 else
358 {
359 if ( dy > 0 )
360 rock->setPos( field.width() - 25, 5 );
361 else
362 rock->setPos( field.width() - 25, field.height() - 25 );
363 rock->setFrame( 0 );
364 }
365 rock->show();
366 rocks.append( rock );
367 }
368 }
369
370 // - - -
371
showText(const QString & text,const QColor & color,bool scroll)372 void KAsteroidsView::showText( const QString &text, const QColor &color, bool scroll )
373 {
374 if ( !initialized )
375 return;
376 textSprite->setHtml( QString("<font color=#%1%2%3>%4</font>")
377 .arg(color.red(), 2, 16, QLatin1Char('0'))
378 .arg(color.green(), 2, 16, QLatin1Char('0'))
379 .arg(color.blue(), 2, 16, QLatin1Char('0'))
380 .arg(text) );
381 Q_UNUSED(color);
382 // ### Porting: no such thing textSprite->setColor( color );
383
384 if ( scroll ) {
385 textSprite->setPos( (field.width()-textSprite->boundingRect().width()) / 2,
386 -textSprite->boundingRect().height() );
387 textDy = TEXT_SPEED;
388 } else {
389 textSprite->setPos( (field.width()-textSprite->boundingRect().width()) / 2,
390 (field.height()-textSprite->boundingRect().height()) / 2 );
391 textDy = 0;
392 }
393 textSprite->show();
394 }
395
396 // - - -
397
hideText()398 void KAsteroidsView::hideText()
399 {
400 textDy = -TEXT_SPEED;
401 }
402
403 // - - -
404
resizeEvent(QResizeEvent * event)405 void KAsteroidsView::resizeEvent(QResizeEvent* event)
406 {
407 QWidget::resizeEvent(event);
408 field.setSceneRect(0, 0, width()-4, height()-4);
409 view.resize(width(),height());
410 }
411
412 // - - -
413
timerEvent(QTimerEvent *)414 void KAsteroidsView::timerEvent( QTimerEvent * )
415 {
416 field.advance();
417
418 // move rocks forward
419 foreach(AnimatedPixmapItem *rock, rocks) {
420 ((KRock *)rock)->nextFrame();
421 wrapSprite( rock );
422 }
423
424 wrapSprite( ship );
425
426 // check for missile collision with rocks.
427 processMissiles();
428
429 // these are generated when a ship explodes
430 for(QList<KBit*>::iterator it = bits.begin(); it != bits.end(); it++)
431 {
432 KBit *bit = *it;
433 if( bit->expired() )
434 {
435 delete bit;
436 it = bits.erase(it);
437 break;
438 }
439 else
440 {
441 bit->growOlder();
442 bit->setFrame( ( bit->frame()+1 ) % bit->frameCount() );
443 }
444 }
445
446 qDeleteAll(exhaust);
447 exhaust.clear();
448
449 // move / rotate ship.
450 // check for collision with a rock.
451 processShip();
452
453 // move powerups and check for collision with player and missiles
454 processPowerups();
455
456 if ( textSprite->isVisible() )
457 {
458 if ( textDy < 0 &&
459 textSprite->boundingRect().y() <= -textSprite->boundingRect().height() ) {
460 textSprite->hide();
461 } else {
462 textSprite->moveBy( 0, textDy );
463 }
464
465 if ( textSprite->sceneBoundingRect().y() > (field.height()-textSprite->boundingRect().height())/2 )
466 textDy = 0;
467 }
468
469 if ( vitalsChanged && !(mFrameNum % 10) ) {
470 emit updateVitals();
471 vitalsChanged = FALSE;
472 }
473
474 mFrameNum++;
475 }
476
wrapSprite(QGraphicsItem * s)477 void KAsteroidsView::wrapSprite( QGraphicsItem *s )
478 {
479 int x = int(s->x() + s->boundingRect().width() / 2);
480 int y = int(s->y() + s->boundingRect().height() / 2);
481
482 if ( x > field.width() )
483 s->setPos( s->x() - field.width(), s->y() );
484 else if ( x < 0 )
485 s->setPos( field.width() + s->x(), s->y() );
486
487 if ( y > field.height() )
488 s->setPos( s->x(), s->y() - field.height() );
489 else if ( y < 0 )
490 s->setPos( s->x(), field.height() + s->y() );
491 }
492
493 // - - -
494
rockHit(AnimatedPixmapItem * hit)495 void KAsteroidsView::rockHit( AnimatedPixmapItem *hit )
496 {
497 KPowerup *nPup = 0;
498 int rnd = int(randDouble()*30.0) % 30;
499 switch( rnd )
500 {
501 case 4:
502 case 5:
503 nPup = new KPowerup( animation[ID_ENERGY_POWERUP], &field,
504 ID_ENERGY_POWERUP );
505 break;
506 case 10:
507 // nPup = new KPowerup( animation[ID_TELEPORT_POWERUP], &field,
508 // ID_TELEPORT_POWERUP );
509 break;
510 case 15:
511 nPup = new KPowerup( animation[ID_BRAKE_POWERUP], &field,
512 ID_BRAKE_POWERUP );
513 break;
514 case 20:
515 nPup = new KPowerup( animation[ID_SHIELD_POWERUP], &field,
516 ID_SHIELD_POWERUP );
517 break;
518 case 24:
519 case 25:
520 nPup = new KPowerup( animation[ID_SHOOT_POWERUP], &field,
521 ID_SHOOT_POWERUP );
522 break;
523 }
524 if ( nPup )
525 {
526 double r = 0.5 - randDouble();
527 nPup->setPos( hit->x(), hit->y() );
528 nPup->setFrame( 0 );
529 nPup->setVelocity( hit->xVelocity() + r, hit->yVelocity() + r );
530 powerups.append( nPup );
531 }
532
533 if ( hit->type() == ID_ROCK_LARGE || hit->type() == ID_ROCK_MEDIUM )
534 {
535 // break into smaller rocks
536 double addx[4] = { 1.0, 1.0, -1.0, -1.0 };
537 double addy[4] = { -1.0, 1.0, -1.0, 1.0 };
538
539 double dx = hit->xVelocity();
540 double dy = hit->yVelocity();
541
542 double maxRockSpeed = MAX_ROCK_SPEED * rockSpeed;
543 if ( dx > maxRockSpeed )
544 dx = maxRockSpeed;
545 else if ( dx < -maxRockSpeed )
546 dx = -maxRockSpeed;
547 if ( dy > maxRockSpeed )
548 dy = maxRockSpeed;
549 else if ( dy < -maxRockSpeed )
550 dy = -maxRockSpeed;
551
552 AnimatedPixmapItem *nrock;
553
554 for ( int i = 0; i < 4; i++ )
555 {
556 double r = rockSpeed/2 - randDouble()*rockSpeed;
557 if ( hit->type() == ID_ROCK_LARGE )
558 {
559 nrock = new KRock( animation[ID_ROCK_MEDIUM], &field,
560 ID_ROCK_MEDIUM, randInt(2), randInt(2) ? -1 : 1 );
561 emit rockHit( 0 );
562 }
563 else
564 {
565 nrock = new KRock( animation[ID_ROCK_SMALL], &field,
566 ID_ROCK_SMALL, randInt(2), randInt(2) ? -1 : 1 );
567 emit rockHit( 1 );
568 }
569
570 nrock->setPos( hit->x(), hit->y() );
571 nrock->setFrame( 0 );
572 nrock->setVelocity( dx+addx[i]*rockSpeed+r, dy+addy[i]*rockSpeed+r );
573 nrock->setFrame( randInt( nrock->frameCount() ) );
574 rocks.append( nrock );
575 }
576 }
577 else if ( hit->type() == ID_ROCK_SMALL )
578 emit rockHit( 2 );
579
580 for(QList<AnimatedPixmapItem*>::iterator it = rocks.begin(); it != rocks.end(); it++)
581 {
582 if((*it) == hit) {
583 delete *it;
584 it = rocks.erase(it);
585 break;
586 }
587 }
588
589 if ( rocks.count() == 0 )
590 emit rocksRemoved();
591 }
592
reducePower(int val)593 void KAsteroidsView::reducePower( int val )
594 {
595 shipPower -= val;
596 if ( shipPower <= 0 )
597 {
598 shipPower = 0;
599 thrustShip = FALSE;
600 if ( shieldOn )
601 {
602 shieldOn = FALSE;
603 shield->hide();
604 }
605 }
606 vitalsChanged = TRUE;
607 }
608
addExhaust(double x,double y,double dx,double dy,int count)609 void KAsteroidsView::addExhaust( double x, double y, double dx,
610 double dy, int count )
611 {
612 for ( int i = 0; i < count; i++ )
613 {
614 KExhaust *e = new KExhaust( animation[ID_EXHAUST], &field );
615 e->setPos( x + 2 - randDouble()*4, y + 2 - randDouble()*4 );
616 e->setVelocity( dx, dy );
617 exhaust.append( e );
618 }
619 }
620
processMissiles()621 void KAsteroidsView::processMissiles()
622 {
623 // if a missile has hit a rock, remove missile and break rock into smaller
624 // rocks or remove completely.
625 QList<KMissile*>::iterator itMissile = missiles.begin();
626 while(itMissile != missiles.end())
627 {
628 (*itMissile)->growOlder();
629
630 if ( (*itMissile)->expired() )
631 {
632 delete (*itMissile);
633 itMissile = missiles.erase(itMissile);
634 continue;
635 }
636
637 wrapSprite(*itMissile);
638
639 bool missileErased = false;
640 QList<QGraphicsItem*> hits = (*itMissile)->collidingItems(Qt::IntersectsItemBoundingRect);
641 QList<QGraphicsItem*>::iterator itHit = hits.begin();
642
643 while (itHit != hits.end())
644 {
645 if ( (*itHit)->type() >= ID_ROCK_LARGE &&
646 (*itHit)->type() <= ID_ROCK_SMALL && (*itHit)->collidesWithItem(*itMissile) )
647 {
648 shotsHit++;
649 rockHit( static_cast<AnimatedPixmapItem *>(*itHit) );
650 delete *itMissile;
651 itMissile = missiles.erase(itMissile);
652 missileErased = true;
653 break;
654 }
655 itHit++;
656 }
657
658 if(!missileErased)
659 itMissile++;
660 }
661 }
662
663 // - - -
664
processShip()665 void KAsteroidsView::processShip()
666 {
667 if ( ship->isVisible() )
668 {
669 if ( shieldOn )
670 {
671 shield->show();
672 reducePower( SHIELD_ON_COST );
673 static int sf = 0;
674 sf++;
675
676 if ( sf % 2 )
677 shield->setFrame( (shield->frame()+1) % shield->frameCount() );
678 shield->setPos( ship->x() - 9, ship->y() - 9 );
679
680 QList<QGraphicsItem *> hits = shield->collidingItems(Qt::IntersectsItemBoundingRect);
681 QList<QGraphicsItem *>::Iterator it;
682 for ( it = hits.begin(); it != hits.end(); ++it )
683 {
684 if ( (*it)->type() >= ID_ROCK_LARGE &&
685 (*it)->type() <= ID_ROCK_SMALL && (*it)->collidesWithItem(shield) )
686 {
687 int factor;
688 switch ( (*it)->type() )
689 {
690 case ID_ROCK_LARGE:
691 factor = 3;
692 break;
693
694 case ID_ROCK_MEDIUM:
695 factor = 2;
696 break;
697
698 default:
699 factor = 1;
700 }
701
702 if ( factor > mShieldCount )
703 {
704 // shield not strong enough
705 shieldOn = FALSE;
706 break;
707 }
708 rockHit( static_cast<AnimatedPixmapItem *>(*it) );
709 // the more shields we have the less costly
710 reducePower( factor * (SHIELD_HIT_COST - mShieldCount*2) );
711 }
712 }
713 }
714
715 if ( !shieldOn )
716 {
717 shield->hide();
718 QList<QGraphicsItem *> hits = ship->collidingItems(Qt::IntersectsItemBoundingRect);
719 QList<QGraphicsItem *>::Iterator it;
720 for ( it = hits.begin(); it != hits.end(); ++it )
721 {
722 if ( (*it)->type() >= ID_ROCK_LARGE &&
723 (*it)->type() <= ID_ROCK_SMALL && (*it)->collidesWithItem(ship))
724 {
725 KBit *bit;
726 for ( int i = 0; i < 12; i++ )
727 {
728 bit = new KBit( animation[ID_BIT], &field );
729 bit->setPos( ship->x() + 5 - randDouble() * 10,
730 ship->y() + 5 - randDouble() * 10 );
731 bit->setFrame( randInt(bit->frameCount()) );
732 bit->setVelocity( 1-randDouble()*2,
733 1-randDouble()*2 );
734 bit->setDeath( 60 + randInt(60) );
735 bits.push_back( bit );
736 }
737 ship->hide();
738 shield->hide();
739 emit shipKilled();
740 break;
741 }
742 }
743 }
744
745
746 if ( rotateSlow )
747 rotateSlow--;
748
749 if ( rotateL )
750 {
751 shipAngle -= rotateSlow ? 1 : rotateRate;
752 if ( shipAngle < 0 )
753 shipAngle += SHIP_STEPS;
754 }
755
756 if ( rotateR )
757 {
758 shipAngle += rotateSlow ? 1 : rotateRate;
759 if ( shipAngle >= SHIP_STEPS )
760 shipAngle -= SHIP_STEPS;
761 }
762
763 double angle = shipAngle * PI_X_2 / SHIP_STEPS;
764 double cosangle = cos( angle );
765 double sinangle = sin( angle );
766
767 if ( brakeShip )
768 {
769 thrustShip = FALSE;
770 rotateL = FALSE;
771 rotateR = FALSE;
772 rotateRate = ROTATE_RATE;
773 if ( fabs(shipDx) < 2.5 && fabs(shipDy) < 2.5 )
774 {
775 shipDx = 0.0;
776 shipDy = 0.0;
777 ship->setVelocity( shipDx, shipDy );
778 brakeShip = FALSE;
779 }
780 else
781 {
782 double motionAngle = atan2( -shipDy, -shipDx );
783 if ( angle > M_PI )
784 angle -= PI_X_2;
785 double angleDiff = angle - motionAngle;
786 if ( angleDiff > M_PI )
787 angleDiff = PI_X_2 - angleDiff;
788 else if ( angleDiff < -M_PI )
789 angleDiff = PI_X_2 + angleDiff;
790 double fdiff = fabs( angleDiff );
791 if ( fdiff > 0.08 )
792 {
793 if ( angleDiff > 0 )
794 rotateL = TRUE;
795 else if ( angleDiff < 0 )
796 rotateR = TRUE;
797 if ( fdiff > 0.6 )
798 rotateRate = mBrakeCount + 1;
799 else if ( fdiff > 0.4 )
800 rotateRate = 2;
801 else
802 rotateRate = 1;
803
804 if ( rotateRate > 5 )
805 rotateRate = 5;
806 }
807 else if ( fabs(shipDx) > 1 || fabs(shipDy) > 1 )
808 {
809 thrustShip = TRUE;
810 // we'll make braking a bit faster
811 shipDx += cosangle/6 * (mBrakeCount - 1);
812 shipDy += sinangle/6 * (mBrakeCount - 1);
813 reducePower( BRAKE_ON_COST );
814 addExhaust( ship->x() + 20 - cosangle*22,
815 ship->y() + 20 - sinangle*22,
816 shipDx-cosangle, shipDy-sinangle,
817 mBrakeCount+1 );
818 }
819 }
820 }
821
822 if ( thrustShip )
823 {
824 // The ship has a terminal velocity, but trying to go faster
825 // still uses fuel (can go faster diagonally - don't care).
826 double thrustx = cosangle/4;
827 double thrusty = sinangle/4;
828 if ( fabs(shipDx + thrustx) < MAX_SHIP_SPEED )
829 shipDx += thrustx;
830 if ( fabs(shipDy + thrusty) < MAX_SHIP_SPEED )
831 shipDy += thrusty;
832 ship->setVelocity( shipDx, shipDy );
833 reducePower( 1 );
834 addExhaust( ship->x() + 20 - cosangle*20,
835 ship->y() + 20 - sinangle*20,
836 shipDx-cosangle, shipDy-sinangle, 3 );
837 }
838
839 ship->setFrame( shipAngle >> 1 );
840
841 if ( shootShip )
842 {
843 if ( !shootDelay && (int)missiles.size() < mShootCount + 2 )
844 {
845 KMissile *missile = new KMissile( animation[ID_MISSILE], &field );
846 missile->setPos( 21+ship->x()+cosangle*21,
847 21+ship->y()+sinangle*21 );
848 missile->setFrame( 0 );
849 missile->setVelocity( shipDx + cosangle*MISSILE_SPEED,
850 shipDy + sinangle*MISSILE_SPEED );
851 missiles.push_back( missile );
852 shotsFired++;
853 reducePower( 1 );
854
855 shootDelay = 5;
856 }
857
858 if ( shootDelay )
859 shootDelay--;
860 }
861
862 if ( teleportShip )
863 {
864 int ra = qrand() % 10;
865 if( ra == 0 )
866 ra += qrand() % 20;
867 int xra = ra * 60 + ( (qrand() % 20) * (qrand() % 20) );
868 int yra = ra * 50 - ( (qrand() % 20) * (qrand() % 20) );
869 ship->setPos( xra, yra );
870 }
871
872 vitalsChanged = TRUE;
873 }
874 }
875
876 // - - -
877
processPowerups()878 void KAsteroidsView::processPowerups()
879 {
880 // if player gets the powerup remove it from the screen, if option
881 // "Can destroy powerups" is enabled and a missile hits the powerup
882 // destroy it
883 QList<KPowerup*>::iterator itPup = powerups.begin();
884
885 while(itPup != powerups.end())
886 {
887 (*itPup)->growOlder();
888
889 if((*itPup)->expired())
890 {
891 delete *itPup;
892 itPup = powerups.erase(itPup);
893 continue;
894 }
895
896 wrapSprite(*itPup);
897
898 bool pupErased = false;
899
900 QList<QGraphicsItem *> hits = (*itPup)->collidingItems();
901 for(QList<QGraphicsItem *>::Iterator itHits = hits.begin(); itHits != hits.end(); itHits++)
902 {
903 if ( (*itHits) == ship )
904 {
905 switch( (*itPup)->type() )
906 {
907 case ID_ENERGY_POWERUP:
908 shipPower += 150;
909 if ( shipPower > MAX_POWER_LEVEL )
910 shipPower = MAX_POWER_LEVEL;
911 break;
912 case ID_TELEPORT_POWERUP:
913 mTeleportCount++;
914 break;
915 case ID_BRAKE_POWERUP:
916 if ( mBrakeCount < MAX_BRAKES )
917 mBrakeCount++;
918 break;
919 case ID_SHIELD_POWERUP:
920 if ( mShieldCount < MAX_SHIELDS )
921 mShieldCount++;
922 break;
923 case ID_SHOOT_POWERUP:
924 if ( mShootCount < MAX_FIREPOWER )
925 mShootCount++;
926 break;
927 }
928
929 delete *itPup;
930 itPup = powerups.erase(itPup);
931 pupErased = true;
932 vitalsChanged = TRUE;
933 break;
934 }
935 else if((*itHits) == shield )
936 {
937 delete *itPup;
938 itPup = powerups.erase(itPup);
939 pupErased = true;
940 break;
941 }
942 else if ( (*itHits)->type() == ID_MISSILE )
943 {
944 if ( can_destroy_powerups )
945 {
946 delete *itPup;
947 itPup = powerups.erase(itPup);
948 pupErased = true;
949 break;
950 }
951 }
952 }
953
954 if(!pupErased)
955 itPup++;
956 }
957 }
958
959 // - - -
960
hideShield()961 void KAsteroidsView::hideShield()
962 {
963 shield->hide();
964 mShieldCount = 0;
965 shieldOn = FALSE;
966 }
967
randDouble()968 double KAsteroidsView::randDouble()
969 {
970 int v = qrand();
971 return (double)v / (double)RAND_MAX;
972 }
973
randInt(int range)974 int KAsteroidsView::randInt( int range )
975 {
976 return qrand() % range;
977 }
978
showEvent(QShowEvent * e)979 void KAsteroidsView::showEvent( QShowEvent *e )
980 {
981 #if defined( QT_LICENSE_PROFESSIONAL )
982 static bool wasThere = FALSE;
983
984 if ( !wasThere ) {
985 wasThere = TRUE;
986 QMessageBox::information( this, tr("QGraphicsView demo"),
987 tr("This game has been implemented using the QGraphicsView class.\n"
988 "The QGraphicsView class is not part of the Light Platform Edition. Please \n"
989 "contact Nokia if you want to upgrade to the Full Platform Edition.") );
990 }
991 #endif
992
993 QWidget::showEvent( e );
994 }
995