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