1/*
2 Copyright (C) 1998-2001 Andreas Zehender <az@azweb.de>
3 Copyright (C) 2006-2007 Dirk Rathlev <dirkrathlev@gmx.de>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#include "mainview.h"
21
22#include <math.h>
23
24#include <QAbstractEventDispatcher>
25#include <QBrush>
26#include <QGraphicsSimpleTextItem>
27#include <QKeyEvent>
28#include <QResizeEvent>
29#include <QTimerEvent>
30
31#include <kaction.h>
32#include <kactioncollection.h>
33#include <kglobalsettings.h>
34#include <klocale.h>
35#include <kstandarddirs.h>
36#include <ktoggleaction.h>
37#include <kconfiggroup.h>
38
39#include <QSvgRenderer>
40
41#include "ai.h"
42#include "options.h"
43
44KToggleAction *MyMainView::pauseAction = 0;
45
46static struct
47 {
48 int id;
49 const char *path;
50 }
51 kspd_animations [] =
52 {
53 { ID_EXPLOSION, "explos%1"},
54 { ID_MINE1, "mine_red%1"},
55 { ID_MINE2, "mine_blue%1"},
56 { ID_MINEEXPLO, "mineex%1"},
57 { 0, 0}
58 };
59
60MyMainView::MyMainView(QWidget *parent)
61 :QWidget(parent),
62 field(this),//0,0,DEF_WIDTH,DEF_HEIGHT),
63 view(&field,this)
64{
65 int i,p;
66 setMinimumSize(600,400);
67 random.setSeed(0);
68 QPixmap backgr(KStandardDirs::locate("appdata",QLatin1String( MV_BACKGROUND) ));
69
70 field.setBackgroundBrush(QBrush(backgr));
71 view.setCacheMode(QGraphicsView::CacheBackground);
72
73 setFocusPolicy(Qt::StrongFocus);
74 // FIXME: is this needed anymore?
75 //view.setResizePolicy(Q3ScrollView::AutoOne);
76 view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
77 view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
78 view.setFrameStyle(QFrame::NoFrame);
79
80 for(p=0;p<2;p++)
81 {
82 for(i=0;i<PlayerKeyNum;i++)
83 playerKeyPressed[p][i]=false;
84 bulletShot[p]=false;
85 minePut[p]=false;
86 }
87
88 svgrender = new QSvgRenderer(KStandardDirs::locate("appdata",QLatin1String( MV_SVG_FILE )));
89
90 sun=new SunSprite(svgrender,QLatin1String( MV_SUN ));
91 field.addItem(sun);
92 sun->setPos(QPointF(width()/2-1-(sun->width()/2),
93 height()/2-1-(sun->height()/2)));
94
95 powerupelements[0] = QLatin1String( MV_POWERMINE );
96 powerupelements[1] = QLatin1String( MV_POWERBULLET );
97 powerupelements[2] = QLatin1String( MV_POWERSHIELD );
98 powerupelements[3] = QLatin1String( MV_POWERENERGY );
99
100 ship[0]=new ShipSprite(svgrender, QLatin1String( MV_SHIP1 ), 0);
101 ship[1]=new ShipSprite(svgrender, QLatin1String( MV_SHIP2 ), 1);
102 field.addItem(ship[0]);
103 field.addItem(ship[1]);
104
105 readSprites();
106
107 for(i=0;i<2;i++)
108 {
109 // ship[i]->setBoundsAction(QwRealMobileSprite::Wrap);
110 ship[i]->hide();
111 bullets[i]=new QList<BulletSprite*>;
112 mines[i]=new QList<MineSprite*>;
113
114 }
115
116 waitForStart=false;
117 textSprite=0;
118 readConfig();
119}
120
121MyMainView::~MyMainView()
122{
123 int i;
124 QAbstractEventDispatcher::instance()->unregisterTimers(this);
125
126 for(i=0;i<2;i++)
127 {
128 delete ai[i];
129 qDeleteAll(*mines[i]);
130 delete mines[i];
131 qDeleteAll(*bullets[i]);
132 delete bullets[i];
133 }
134
135 qDeleteAll(powerups);
136 powerups.clear();
137
138 qDeleteAll(explosions);
139 explosions.clear();
140
141 writeConfig();
142 delete svgrender;
143}
144
145void MyMainView::setActionCollection(KActionCollection *a)
146{
147 actionCollection = a;
148}
149
150/* Assumes that there are no gaps between animation frames. ie 1,2,3 will only have frames 1&2
151 recognized. It also assumes that there is at least one frame. */
152// FIXME: Add Check for existence of first frame
153// TODO: Add support for missing frames (ie 1,2,5)
154bool MyMainView::readSprites()
155{
156 int i = 0;
157 while ( kspd_animations[i].id )
158 {
159 QList<QString> anim;
160 short frame = 0;
161 QString element =QLatin1String( kspd_animations[i].path );
162 QString elem = element.arg(frame, 2, 10, QLatin1Char('0'));
163 do
164 {
165 anim.append(elem);
166 frame++;
167 elem = element.arg(frame, 2, 10, QLatin1Char('0'));
168 } while ( svgrender->elementExists(elem) );
169 animation.insert( kspd_animations[i].id, anim );
170 i++;
171 }
172
173 // FIXME: Perform test!
174 return true;
175}
176
177void MyMainView::readConfig()
178{
179 KConfigGroup game(KGlobal::config(), "Game");
180 int i;
181
182 customConfig.gamespeed=game.readEntry("gamespeed",
183 predefinedConfig[0].gamespeed);
184
185 customConfig.gravity=
186 game.readEntry("gravity",predefinedConfig[0].gravity);
187 customConfig.acc=
188 game.readEntry("acceleration",predefinedConfig[0].acc);
189 customConfig.bulletDamage=
190 game.readEntry("bulletDamage",predefinedConfig[0].bulletDamage);
191 customConfig.bulletLifeTime=
192 game.readEntry("bulletLifeTime",predefinedConfig[0].bulletLifeTime);
193 customConfig.bulletReloadTime=
194 game.readEntry("bulletReloadTime",predefinedConfig[0].bulletReloadTime);
195 customConfig.mineDamage=
196 game.readEntry("mineDamage",predefinedConfig[0].mineDamage);
197 customConfig.shipDamage=
198 game.readEntry("shipDamage",predefinedConfig[0].shipDamage);
199 customConfig.maxBullets=
200 game.readEntry("maxBullets",predefinedConfig[0].maxBullets);
201 customConfig.maxMines=
202 game.readEntry("maxMines",predefinedConfig[0].maxMines);
203 customConfig.mineReloadTime=
204 game.readEntry("mineReloadTime",predefinedConfig[0].mineReloadTime);
205 customConfig.rotationSpeed=
206 game.readEntry("rotationSpeed",predefinedConfig[0].rotationSpeed);
207 customConfig.shotSpeed=
208 game.readEntry("shotSpeed",predefinedConfig[0].shotSpeed);
209 customConfig.energyNeed=
210 game.readEntry("accEnergyNeed",predefinedConfig[0].energyNeed);
211 customConfig.rotationEnergyNeed=
212 game.readEntry("rotationEnergyNeed",predefinedConfig[0].rotationEnergyNeed);
213 customConfig.sunEnergy=
214 game.readEntry("sunEnergy",predefinedConfig[0].sunEnergy);
215 customConfig.mineActivateTime=
216 game.readEntry("mineActivateTime",predefinedConfig[0].mineActivateTime);
217 customConfig.mineFuel=
218 game.readEntry("mineFuel",predefinedConfig[0].mineFuel);
219 customConfig.shotEnergyNeed=
220 game.readEntry("shotEnergyNeed",predefinedConfig[0].shotEnergyNeed);
221 customConfig.mineEnergyNeed=
222 game.readEntry("mineEnergyNeed",predefinedConfig[0].mineEnergyNeed);
223 customConfig.startPosX=
224 game.readEntry("startPosX",predefinedConfig[0].startPosX);
225 customConfig.startPosY=
226 game.readEntry("startPosY",predefinedConfig[0].startPosY);
227 customConfig.startVelX=
228 game.readEntry("startVelX",predefinedConfig[0].startVelX);
229 customConfig.startVelY=
230 game.readEntry("startVelY",predefinedConfig[0].startVelY);
231 customConfig.powerupLifeTime=
232 game.readEntry("powerupLifeTime",predefinedConfig[0].powerupLifeTime);
233 customConfig.powerupRefreshTime=
234 game.readEntry("powerupRefreshTime",predefinedConfig[0].powerupRefreshTime);
235 customConfig.powerupShieldAmount=
236 game.readEntry("powerupShieldAmount",
237 predefinedConfig[0].powerupShieldAmount);
238 customConfig.powerupEnergyAmount=
239 game.readEntry("powerupEnergyAmount",
240 predefinedConfig[0].powerupEnergyAmount);
241
242 if(Options::lastConfig() < predefinedConfigNum)
243 config=modifyConfig(predefinedConfig[Options::lastConfig()]);
244 else
245 config=modifyConfig(customConfig);
246
247 for(i=0;i<2;i++)
248 ai[i]=new Ai(i,ship,bullets,mines,&config);
249}
250
251void MyMainView::writeConfig()
252{
253 KConfigGroup game(KGlobal::config(), "Game");
254
255 game.writeEntry("gravity",customConfig.gravity);
256 game.writeEntry("acceleration",customConfig.acc);
257 game.writeEntry("bulletDamage",customConfig.bulletDamage);
258 game.writeEntry("bulletLifeTime",customConfig.bulletLifeTime);
259 game.writeEntry("bulletReloadTime",customConfig.bulletReloadTime);
260 game.writeEntry("mineDamage",customConfig.mineDamage);
261 game.writeEntry("shipDamage",customConfig.shipDamage);
262 game.writeEntry("maxBullets",customConfig.maxBullets);
263 game.writeEntry("maxMines",customConfig.maxMines);
264 game.writeEntry("rotationSpeed",customConfig.rotationSpeed);
265 game.writeEntry("shotSpeed",customConfig.shotSpeed);
266 game.writeEntry("accEnergyNeed",customConfig.energyNeed);
267 game.writeEntry("rotationEnergyNeed",customConfig.rotationEnergyNeed);
268 game.writeEntry("sunEnergy",customConfig.sunEnergy);
269 game.writeEntry("mineActivateTime",customConfig.mineActivateTime);
270 game.writeEntry("mineReloadTime",customConfig.mineReloadTime);
271 game.writeEntry("mineFuel",customConfig.mineFuel);
272 game.writeEntry("shotEnergyNeed",customConfig.shotEnergyNeed);
273 game.writeEntry("mineEnergyNeed",customConfig.mineEnergyNeed);
274
275 game.writeEntry("startPosX",customConfig.startPosX);
276 game.writeEntry("startPosY",customConfig.startPosY);
277 game.writeEntry("startVelX",customConfig.startVelX);
278 game.writeEntry("startVelY",customConfig.startVelY);
279
280 game.writeEntry("powerupLifeTime",customConfig.powerupLifeTime);
281 game.writeEntry("powerupRefreshTime",customConfig.powerupRefreshTime);
282 game.writeEntry("powerupShieldAmount",customConfig.powerupShieldAmount);
283 game.writeEntry("powerupEnergyAmount",customConfig.powerupEnergyAmount);
284}
285
286SConfig MyMainView::modifyConfig(SConfig conf)
287{
288 SConfig newConfig=conf;
289 newConfig.gamespeed*=Options::refreshTime()/33.0;
290 newConfig.acc*=newConfig.gamespeed;
291 newConfig.rotationSpeed*=newConfig.gamespeed*M_PI/ROTNUM*4;
292 newConfig.energyNeed*=newConfig.gamespeed;
293 newConfig.rotationEnergyNeed*=newConfig.gamespeed;
294 newConfig.mineActivateTime*=newConfig.gamespeed;
295
296 return newConfig;
297}
298
299void MyMainView::keyPressEvent(QKeyEvent *ev)
300{
301 // if-statement kept for historical reasons, maybe not needed anymore
302 if ( ((gameEnd>0.0) || (gameEnd<=-2.0)) && (!waitForStart) )
303 {
304 if(actionCollection->action(QLatin1String( "P1KeyLeft" ))->shortcuts().contains(ev->key()))
305 playerKeyPressed[0][PlayerKeyLeft]=true;
306 else if(actionCollection->action(QLatin1String( "P2KeyLeft" ))->shortcuts().contains(ev->key()))
307 playerKeyPressed[1][PlayerKeyLeft]=true;
308
309 else if(actionCollection->action(QLatin1String( "P1KeyRight" ))->shortcuts().contains(ev->key()))
310 playerKeyPressed[0][PlayerKeyRight]=true;
311 else if(actionCollection->action(QLatin1String( "P2KeyRight" ))->shortcuts().contains(ev->key()))
312 playerKeyPressed[1][PlayerKeyRight]=true;
313
314 else if(actionCollection->action(QLatin1String( "P1KeyAcc" ))->shortcuts().contains(ev->key()))
315 playerKeyPressed[0][PlayerKeyAcc]=true;
316 else if(actionCollection->action(QLatin1String( "P2KeyAcc" ))->shortcuts().contains(ev->key()))
317 playerKeyPressed[1][PlayerKeyAcc]=true;
318
319 else if(actionCollection->action(QLatin1String( "P1Shot" ))->shortcuts().contains(ev->key()))
320 playerKeyPressed[0][PlayerKeyShot]=true;
321 else if(actionCollection->action(QLatin1String( "P2Shot" ))->shortcuts().contains(ev->key()))
322 playerKeyPressed[1][PlayerKeyShot]=true;
323
324 else if(actionCollection->action(QLatin1String( "P1Mine" ))->shortcuts().contains(ev->key()))
325 playerKeyPressed[0][PlayerKeyMine]=true;
326 else if(actionCollection->action(QLatin1String( "P2Mine" ))->shortcuts().contains(ev->key()))
327 playerKeyPressed[1][PlayerKeyMine]=true;
328 else
329 ev->ignore();
330 }
331}
332
333void MyMainView::keyReleaseEvent(QKeyEvent *ev)
334{
335 if(actionCollection->action(QLatin1String( "P1KeyLeft" ))->shortcuts().contains(ev->key()))
336 playerKeyPressed[0][PlayerKeyLeft]=false;
337 else if(actionCollection->action(QLatin1String( "P2KeyLeft" ))->shortcuts().contains(ev->key()))
338 playerKeyPressed[1][PlayerKeyLeft]=false;
339
340 else if(actionCollection->action(QLatin1String( "P1KeyRight" ))->shortcuts().contains(ev->key()))
341 playerKeyPressed[0][PlayerKeyRight]=false;
342 else if(actionCollection->action(QLatin1String( "P2KeyRight" ))->shortcuts().contains(ev->key()))
343 playerKeyPressed[1][PlayerKeyRight]=false;
344
345 else if(actionCollection->action(QLatin1String( "P1KeyAcc" ))->shortcuts().contains(ev->key()))
346 playerKeyPressed[0][PlayerKeyAcc]=false;
347 else if(actionCollection->action(QLatin1String( "P2KeyAcc" ))->shortcuts().contains(ev->key()))
348 playerKeyPressed[1][PlayerKeyAcc]=false;
349
350 else if(actionCollection->action(QLatin1String( "P1Shot" ))->shortcuts().contains(ev->key()))
351 playerKeyPressed[0][PlayerKeyShot]=false;
352 else if(actionCollection->action(QLatin1String( "P2Shot" ))->shortcuts().contains(ev->key()))
353 playerKeyPressed[1][PlayerKeyShot]=false;
354
355 else if(actionCollection->action(QLatin1String( "P1Mine" ))->shortcuts().contains(ev->key()))
356 playerKeyPressed[0][PlayerKeyMine]=false;
357 else if(actionCollection->action(QLatin1String( "P2Mine" ))->shortcuts().contains(ev->key()))
358 playerKeyPressed[1][PlayerKeyMine]=false;
359 else
360 ev->ignore();
361}
362
363void MyMainView::pause()
364{
365 if( !waitForStart )
366 {
367 pauseAction->setChecked( true );
368
369 waitForStart=true;
370 QAbstractEventDispatcher::instance()->unregisterTimers(this);
371 emit setStatusText(i18n(" paused "), IDS_PAUSE);
372 }
373}
374
375void MyMainView::resume()
376{
377 waitForStart=false;
378 timerID=startTimer(Options::refreshTime());
379 emit(setStatusText(QLatin1String( "" ),IDS_PAUSE));
380 emit(setStatusText(QLatin1String( "" ),IDS_MAIN));
381}
382
383void MyMainView::start( )
384{
385 if( ( gameEnd <= 0.0 ) && ( gameEnd > -2.0 ) )
386 {
387 newRound( );
388 }
389 else if( waitForStart )
390 {
391 waitForStart = false;
392 timerID=startTimer(Options::refreshTime());
393 emit(setStatusText(QLatin1String( "" ),IDS_PAUSE));
394 emit(setStatusText(QLatin1String( "" ),IDS_MAIN));
395 pauseAction->setEnabled( true );
396 pauseAction->setChecked( false );
397 }
398}
399
400void MyMainView::stop()
401{
402 pauseAction->setEnabled( false );
403 pauseAction->setChecked( false );
404
405 QAbstractEventDispatcher::instance()->unregisterTimers(this);
406 waitForStart = true;
407}
408
409void MyMainView::togglePause( )
410{
411 if( waitForStart )
412 resume( );
413 else
414 pause( );
415}
416
417void MyMainView::resizeEvent(QResizeEvent *event)
418{
419 double mx,my;
420 int i,current;
421 int listsize; // used for caching QtList::size()
422
423 mx=(event->size().width()-event->oldSize().width())/2.0;
424 my=(event->size().height()-event->oldSize().height())/2.0;
425 QWidget::resizeEvent(event);
426 view.resize(width(),height());
427 field.setSceneRect(0, 0, width(),height());
428
429 // printf("%d %d\n",field.width(),field.height());
430
431 sun->setPos(QPointF(width()/2-1-(sun->width()/2),
432 height()/2-1-(sun->height()/2)));
433
434 for(i=0;i<2;i++)
435 {
436 // ship[i]->adoptSpritefieldBounds();
437 ship[i]->moveBy(mx,my);
438
439 listsize = mines[i]->size();
440 for (current=0; current<listsize; current++)
441 {
442 // mine->adoptSpritefieldBounds();
443 mines[i]->value(current)->moveBy(mx,my);
444 }
445
446 listsize = bullets[i]->size();
447 for (current=0; current<listsize; current++)
448 {
449 // bullet->adoptSpritefieldBounds();
450 bullets[i]->value(current)->moveBy(mx,my);
451 }
452 }
453 if(textSprite)
454 textSprite->moveBy((int)mx,(int)my);
455
456 listsize = powerups.size();
457 for (current=0; current<listsize; current++)
458 {
459 powerups[current]->moveBy(mx,my);
460 }
461}
462
463void MyMainView::newRound()
464{
465 double mx,my;
466 int i;
467
468 timeToNextPowerup=random.getDouble() * config.powerupRefreshTime;
469 qDeleteAll(powerups);
470 powerups.clear();
471
472 QAbstractEventDispatcher::instance()->unregisterTimers(this);
473 mx=width()/2.0;
474 my=height()/2.0;
475 ship[0]->setPos(QPointF(mx+config.startPosX-(ship[0]->width()/2),
476 my+config.startPosY-(ship[0]->height()/2)));
477 ship[0]->setRotation(0.0);
478
479 ship[1]->setPos(QPointF(mx-config.startPosX-(ship[1]->width()/2),
480 my-config.startPosY-(ship[1]->height()/2)));
481 ship[1]->setRotation(M_PI);
482
483 ship[0]->setVelocity(config.startVelX,config.startVelY);
484 ship[1]->setVelocity(-config.startVelX,-config.startVelY);
485 for(i=0;i<2;i++)
486 {
487 ship[i]->show();
488 ship[i]->setEnergy(MAX_ENERGY);
489 ship[i]->setHitPoints(Options::startHitPoints(i));
490 ship[i]->stop(false);
491 ship[i]->setExplosion(-1);
492 emit(energy(i,(int)ship[i]->getEnergy()));
493 emit(hitPoints(i,ship[i]->getHitPoints()));
494 bulletShot[i]=false;
495 qDeleteAll(*bullets[i]);
496 bullets[i]->clear();
497
498 qDeleteAll(*mines[i]);
499 mines[i]->clear();
500
501 ship[i]->mine(0.0);
502 ship[i]->bullet(0.0);
503 ship[i]->setBulletPowerups(0);
504 ship[i]->setMinePowerups(0);
505
506 ai[i]->newRound();
507 }
508 qDeleteAll(explosions);
509 explosions.clear();
510 gameEnd=-10.0;
511 for(i=0;i<PlayerKeyNum;i++)
512 {
513 playerKeyPressed[0][i]=false;
514 playerKeyPressed[1][i]=false;
515 }
516 if(textSprite)
517 {
518 textSprite->hide();
519 delete textSprite;
520 textSprite=0;
521 }
522 //field.update();
523
524 QString str = i18n("Press %1 to start",
525 KShortcut(GAME_START_SHORTCUT).primary().toString(QKeySequence::NativeText));
526 emit(setStatusText(str,IDS_MAIN));
527 emit(setStatusText( QLatin1String( "" ), IDS_PAUSE ));
528 stop( );
529}
530
531void MyMainView::newGame()
532{
533 int i;
534 for(i=0;i<2;i++)
535 {
536 ship[i]->setWins(0);
537 emit(wins(i,0));
538 }
539 newRound();
540}
541
542void MyMainView::timerEvent(QTimerEvent *event)
543{
544 unsigned w;
545 int i;
546 bool stopped = false;
547
548 if(event->timerId()==timerID)
549 {
550 QAbstractEventDispatcher::instance()->unregisterTimers(this);
551 if(gameEnd>0.0)
552 {
553 gameEnd-=1.0;
554 if(gameEnd<=0.0)
555 {
556 stopped = true;
557 if(textSprite)
558 {
559 textSprite->hide();
560 delete textSprite;
561 textSprite=0;
562 }
563 textSprite=new QGraphicsSimpleTextItem(0,&field);
564 // FIXME
565 // textSprite->setTextFlags(Qt::AlignCenter);
566 textSprite->setBrush(QBrush(QColor(255,160,0)));
567 textSprite->setFont(QFont(KGlobalSettings::generalFont().family(),14));
568 textSprite->show( );
569 if(ship[0]->getHitPoints()==0)
570 {
571 if(ship[1]->getHitPoints()==0)
572 textSprite->setText(i18n("draw round"));
573 else
574 {
575 textSprite->setText(i18n("blue player won the round"));
576 w=ship[1]->getWins()+1;
577 ship[1]->setWins(w);
578 emit(wins(1,w));
579 }
580 }
581 else
582 {
583 textSprite->setText(i18n("red player won the round"));
584 w=ship[0]->getWins()+1;
585 ship[0]->setWins(w);
586 emit(wins(0,w));
587 }
588 // must do this after setting text, because length is unknown until now
589 textSprite->setPos(QPointF((width()-textSprite->boundingRect().width()) / 2,height()/2-90));
590
591 QString str = i18n("Press %1 for new round",
592 KShortcut(GAME_START_SHORTCUT).primary().toString(QKeySequence::NativeText));
593 emit(setStatusText(str,IDS_MAIN));
594 stop( );
595 }
596 }
597
598 if( !stopped )
599 {
600 for(i=0;i<2;i++)
601 if(Options::playerIsAi(i)&&(ship[i]->getHitPoints()>0))
602 ai[i]->think();
603
604 moveMines();
605 moveBullets();
606 moveExplosions();
607 moveShips();
608 calculatePowerups();
609 collisions();
610 timerID=startTimer(Options::refreshTime());
611 }
612 //field.update();
613 }
614}
615
616void MyMainView::moveShips()
617{
618 int i,nf,olde;
619 double nx,ny,en,nr;
620 BulletSprite *bullet;
621 MineSprite *mine;
622
623
624 for(i=0;i<2;i++)
625 {
626 bool playerIsAi = Options::playerIsAi(i);
627 olde=(int)ship[i]->getEnergy();
628 if(ship[i]->getHitPoints()==0)
629 {
630 ship[i]->forward(config.gamespeed);
631 ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy,
632 config.gamespeed);
633 }
634 else
635 {
636 ship[i]->calculateGravityAndEnergy(config.gravity,config.sunEnergy,
637 config.gamespeed);
638
639
640 if(!playerIsAi&&playerKeyPressed[i][PlayerKeyRight]
641 || playerIsAi&&ai[i]->rotateRight())
642 ship[i]->rotateRight(config.rotationEnergyNeed,
643 config.rotationSpeed);
644
645 if(!playerIsAi&&playerKeyPressed[i][PlayerKeyLeft]
646 || playerIsAi&&ai[i]->rotateLeft())
647 ship[i]->rotateLeft(config.rotationEnergyNeed,
648 config.rotationSpeed);
649
650 en=ship[i]->getEnergy();
651 nr=ship[i]->getRotation();
652
653 nf = 0;
654 nx=cos(nr);
655 ny=sin(nr);
656 if ( ((!playerIsAi && playerKeyPressed[i][PlayerKeyAcc])
657 || playerIsAi&&ai[i]->accelerate())
658 && (en>config.energyNeed) )
659 {
660 en-=config.energyNeed;
661 ship[i]->setVelocity(ship[i]->xVelocity()+nx*config.acc,
662 ship[i]->yVelocity()-ny*config.acc);
663
664 // limit speed to avoid "tunneling" through other objects
665 // FIXME: find a more elegant way
666 if (ship[i]->xVelocity()*ship[i]->xVelocity()+
667 ship[i]->yVelocity()*ship[i]->yVelocity() > MAX_VELOCITY*MAX_VELOCITY)
668 {
669 double alpha;
670 alpha = fabs(atan(ship[i]->yVelocity()/ship[i]->xVelocity()));
671 ship[i]->setVelocity(MAX_VELOCITY*cos(alpha)*fabs(ship[i]->xVelocity())/ship[i]->xVelocity(),
672 MAX_VELOCITY*sin(alpha)*fabs(ship[i]->yVelocity())/ship[i]->yVelocity());
673 }
674 }
675 if(en>MAX_ENERGY)
676 en=MAX_ENERGY;
677
678 ship[i]->forward(config.gamespeed);
679
680 //Bullets and Mines
681 if(!playerIsAi&&playerKeyPressed[i][PlayerKeyShot]
682 ||playerIsAi&&ai[i]->shootBullet())
683 {
684 if((en>config.shotEnergyNeed) && (!ship[i]->reloadsBullet()))
685 {
686 if(bullets[i]->count() <
687 (config.maxBullets+ship[i]->getBulletPowerups()))
688 {
689 ship[i]->bullet(config.bulletReloadTime);
690 en-=config.shotEnergyNeed;
691 if(i)
692 bullet=new BulletSprite(svgrender, QLatin1String( MV_BULLET2 ), i,
693 config.bulletLifeTime);
694 else
695 bullet=new BulletSprite(svgrender, QLatin1String( MV_BULLET1 ), i,
696 config.bulletLifeTime);
697 field.addItem(bullet);
698 QPointF p;
699 p = ship[i]->mapToScene(ship[i]->center());
700 bullet->setPos(QPointF(p.x()+nx*SHOTDIST,p.y()-ny*SHOTDIST));
701 bullet->setVelocity(ship[i]->xVelocity()+nx*config.shotSpeed,
702 ship[i]->yVelocity()-ny*config.shotSpeed);
703 // bullet->setBoundsAction(QwRealMobileSprite::Wrap);
704 bullet->show();
705 bullets[i]->append(bullet);
706 }
707 }
708 }
709 if(!Options::playerIsAi(i)&&playerKeyPressed[i][PlayerKeyMine]
710 || Options::playerIsAi(i)&&ai[i]->layMine())
711 {
712 if((en>config.mineEnergyNeed) && (!ship[i]->reloadsMine()))
713 {
714 if(mines[i]->count() <
715 (config.maxMines+ship[i]->getMinePowerups()))
716 {
717 ship[i]->mine(config.mineReloadTime);
718 en-=config.mineEnergyNeed;
719 if (i==0)
720 mine=new MineSprite(svgrender,animation[ID_MINE1],animation[ID_MINEEXPLO],i,
721 config.mineActivateTime,config.mineFuel);
722 else
723 mine=new MineSprite(svgrender,animation[ID_MINE2],animation[ID_MINEEXPLO],i,
724 config.mineActivateTime,config.mineFuel);
725 field.addItem(mine);
726 QPointF p;
727 mine->setPos(ship[i]->mapToScene(ship[i]->center()));
728 // move mine to center
729 mine->moveBy(-mine->center().x(),-mine->center().y());
730 mine->setVelocity(0,0);
731 //mine->setBoundsAction(QwRealMobileSprite::Wrap);
732 mine->show();
733 mines[i]->append(mine);
734 }
735 }
736 }
737 ship[i]->setEnergy(en);
738 if(olde!=(int)en)
739 emit(energy(i,(int)en));
740 }
741 }
742}
743
744void MyMainView::moveMines()
745{
746 int i;
747 MineSprite* mine;
748 int p;
749 int listsize; // used for caching QtList::size()
750
751 for(p=0;p<2;p++)
752 {
753 i=0;
754 listsize = mines[p]->size();
755 while (i<listsize)
756 {
757 mine = mines[p]->value(i);
758 mine->calculateGravity(config.gravity,config.gamespeed);
759 mine->forward(config.gamespeed);
760 if(mine->over())
761 {
762 mine->hide();
763 mines[p]->removeAt(i);
764 delete mine;
765 listsize--;
766 }
767 else
768 i++;
769 }
770 }
771}
772
773void MyMainView::moveBullets()
774{
775 int i,j;
776 BulletSprite *sp;
777 int listsize; // used for caching QtList::size()
778
779 for(i=0;i<2;i++)
780 {
781 j=0;
782 listsize = bullets[i]->size();
783 while (j<listsize)
784 {
785 sp = bullets[i]->value(j);
786 sp->calculateGravity(config.gravity,config.gamespeed);
787 sp->forward(config.gamespeed);
788 if(sp->timeOut())
789 {
790 sp->hide();
791 bullets[i]->removeAll(sp);
792 listsize--;
793 }
794 else
795 j++;
796 }
797 }
798}
799
800void MyMainView::moveExplosions()
801{
802 int i=0;
803 ExplosionSprite *ex;
804 int listsize; // used for caching QtList::size()
805 listsize = explosions.size();
806 while (i<listsize)
807 {
808 ex = explosions[i];
809 ex->forward(config.gamespeed);
810 if(ex->isOver())
811 {
812 explosions.removeAt(i);
813 delete ex;
814 listsize--;
815 }
816 else
817 i++;
818 }
819}
820
821void MyMainView::calculatePowerups()
822{
823 int i=0;
824 PowerupSprite *sp;
825 int listsize; // used for caching QtList::size()
826 listsize = powerups.size();
827 while (i<listsize)
828 {
829 sp = powerups[i];
830 sp->setLifetime(sp->getLifetime()-config.gamespeed);
831 if (sp->getLifetime()<0)
832 {
833 powerups.removeAt(i);
834 delete sp;
835 listsize--;
836 }
837 else
838 i++;
839 }
840 timeToNextPowerup-=config.gamespeed;
841 if(timeToNextPowerup<0)
842 {
843 int type,x,y;
844 timeToNextPowerup= random.getDouble() * config.powerupRefreshTime;
845 type= random.getLong(PowerupSprite::PowerupNum);
846 sp=new PowerupSprite(svgrender,powerupelements[type],type,
847 config.powerupLifeTime);
848 field.addItem(sp);
849 do
850 {
851 x = random.getLong(width()-40)+20;
852 y = random.getLong(height()-40)+20;
853 }
854 while(((x-width()/2)*(x-width()/2)+(y-height()/2)*(y-height()/2))<(50*50));
855 sp->setPos(QPointF(x,y));
856 powerups.append(sp);
857 sp->show();
858 }
859}
860
861void MyMainView::collisions()
862{
863 int pl,hp,op,oldhp[2],ohp;
864 QList<QGraphicsItem *> unexact;
865 QGraphicsItem *sprite;
866 BulletSprite *bullet;
867 MineSprite *mine;
868 ExplosionSprite *expl;
869 ShipSprite *s;
870 PowerupSprite *power;
871 QList<QGraphicsItem *> hitlist;
872 double ndx[2],ndy[2];
873 double en;
874 int i;
875 int listsize; // used for caching QtList::size()
876
877 for(pl=0;pl<2;pl++)
878 {
879 if(!ship[pl]->isStopped())
880 {
881 unexact.clear();
882 unexact = ship[pl]->collidingItems(Qt::IntersectsItemBoundingRect);
883 oldhp[pl]=hp=ship[pl]->getHitPoints();
884 hitlist.clear();
885 foreach (sprite, unexact)
886 {
887 if((sprite->type()!=S_EXPLOSION)
888 && !((sprite->type()!=S_SUN)&&(ship[pl]->getHitPoints()==0)))
889 if(ship[pl]->collidesWithItem(sprite,Qt::IntersectsItemShape))
890 if(!hitlist.contains(sprite))
891 hitlist.append(sprite);
892 }
893
894 foreach (sprite, hitlist)
895 {
896 switch(sprite->type())
897 {
898 case S_SUN:
899 hp=0;
900 ship[pl]->stop();
901 break;
902 case S_BULLET:
903 bullet=(BulletSprite *)sprite;
904 bullet->hide();
905 //bullets[bullet->getPlayerNumber()]->removeRef(bullet);
906 bullets[bullet->getPlayerNumber()]->removeAll(bullet);
907 delete bullet;
908 hp-=config.bulletDamage;
909 break;
910 case S_SHIP:
911 s=(ShipSprite*)sprite;
912 ohp=s->getHitPoints();
913 if(ohp>0)
914 {
915 s->setHitPoints(ohp-hp-config.shipDamage);
916 emit(hitPoints(s->getPlayerNumber(),s->getHitPoints()));
917 ndx[0]=((1-EPSILON)*ship[0]->xVelocity()+(1+EPSILON)*ship[1]->xVelocity())/2.0;
918 ndy[0]=((1-EPSILON)*ship[0]->yVelocity()+(1+EPSILON)*ship[1]->yVelocity())/2.0;
919 ndx[1]=((1-EPSILON)*ship[1]->xVelocity()+(1+EPSILON)*ship[0]->xVelocity())/2.0;
920 ndy[1]=((1-EPSILON)*ship[1]->yVelocity()+(1+EPSILON)*ship[0]->yVelocity())/2.0;
921 ship[0]->setVelocity(ndx[0],ndy[0]);
922 ship[1]->setVelocity(ndx[1],ndy[1]);
923 hp-=ohp+config.shipDamage;
924 }
925 break;
926 case S_MINE:
927 mine=(MineSprite *)sprite;
928 if(mine->isActive()&& !mine->explodes())
929 {
930 mine->explode();
931 ndx[0]=(ship[pl]->xVelocity()+0.3*mine->xVelocity())/1.3;
932 ndy[0]=(ship[pl]->yVelocity()+0.3*mine->yVelocity())/1.3;
933 ship[pl]->setVelocity(ndx[0],ndy[0]);
934 mine->setVelocity(ndx[0],ndy[0]);
935 hp-=config.mineDamage;
936 }
937 break;
938 case S_POWERUP:
939 power=(PowerupSprite *)sprite;
940 switch(power->getType())
941 {
942 case PowerupSprite::PowerupShield:
943 hp+=config.powerupShieldAmount;
944 break;
945 case PowerupSprite::PowerupEnergy:
946 en=ship[pl]->getEnergy()+config.powerupEnergyAmount;
947 if(en>MAX_ENERGY)
948 en=MAX_ENERGY;
949 ship[pl]->setEnergy(en);
950 break;
951 case PowerupSprite::PowerupMine:
952 ship[pl]->setMinePowerups(
953 ship[pl]->getMinePowerups()+1);
954 break;
955 case PowerupSprite::PowerupBullet:
956 ship[pl]->setBulletPowerups(
957 ship[pl]->getMinePowerups()+1);
958 break;
959 }
960 power->hide();
961 powerups.removeAll(power);
962 delete power;
963 break;
964 }
965 }
966 if(hp>MAX_HP)
967 hp=MAX_HP;
968 ship[pl]->setHitPoints(hp);
969 }
970
971 listsize = mines[pl]->size();
972 for (i=0; i<listsize; i++)
973 {
974 mine = mines[pl]->value(i);
975 if(!mine->explodes())
976 {
977 unexact.clear();
978 unexact=mine->collidingItems(Qt::IntersectsItemBoundingRect);
979 hitlist.clear();
980 foreach (sprite, unexact)
981 {
982 if(sprite->type()==S_BULLET)
983 if(mine->collidesWithItem(sprite))
984 if(!hitlist.contains(sprite))
985 hitlist.append(sprite);
986 }
987 if(hitlist.count()>0)
988 {
989 mine->explode();
990 foreach (QGraphicsItem *item, hitlist)
991 {
992 // FIXME: why does it crash with qgraphicsitem_cast?
993 bullet = static_cast<BulletSprite*>(item);// qgraphicsitem_cast<BulletSprite*>(item);
994// bullets[bullet->getPlayerNumber()]->removeRef(bullet);
995 bullets[bullet->getPlayerNumber()]->removeAll(bullet);
996 delete bullet;
997 }
998 }
999 }
1000 }
1001 }
1002
1003 hitlist.clear();
1004 unexact.clear();
1005 unexact=sun->collidingItems(Qt::IntersectsItemBoundingRect);
1006 foreach (sprite, unexact)
1007 {
1008 switch(sprite->type())
1009 {
1010 case S_BULLET:
1011 if(sun->collidesWithItem(sprite))
1012 if(!hitlist.contains(sprite))
1013 hitlist.append(sprite);
1014 break;
1015 case S_MINE:
1016 if(!((MobileSprite*)sprite)->isStopped())
1017 if(sun->collidesWithItem(sprite))
1018 if(!hitlist.contains(sprite))
1019 hitlist.append(sprite);
1020 break;
1021 }
1022 }
1023
1024 foreach (sprite, hitlist)
1025 {
1026 switch(sprite->type())
1027 {
1028 case S_BULLET:
1029 bullet=(BulletSprite *)sprite;
1030 bullet->hide();
1031 bullets[bullet->getPlayerNumber()]->removeAll(bullet);
1032 delete bullet;
1033 break;
1034 case S_MINE:
1035 mine=(MineSprite*)sprite;
1036 mine->stop();
1037 if(!mine->explodes())
1038 mine->explode();
1039 break;
1040 }
1041 }
1042
1043
1044 for(pl=0;pl<2;pl++)
1045 {
1046 hp=ship[pl]->getHitPoints();
1047 if(hp!=oldhp[pl])
1048 emit(hitPoints(pl,hp));
1049 if((hp==0)&&(ship[pl]->getExplosion()<0))
1050 {
1051 op=(pl+1)%2;
1052 ship[pl]->setExplosion((int)(EXPLOSION_TIME/config.gamespeed));
1053 expl = new ExplosionSprite(svgrender,animation[ID_EXPLOSION],ship[pl]);
1054 field.addItem(expl);
1055 expl->show();
1056 explosions.append(expl);
1057 gameEnd=Options::timeAfterKill()/config.gamespeed;
1058 }
1059 }
1060}
1061
1062void MyMainView::gameSetup()
1063{
1064 if(!waitForStart)
1065 pause();
1066
1067 if (KConfigDialog::showDialog( QLatin1String( "settings" )))
1068 return;
1069
1070 SettingsDialog *settings=new SettingsDialog(&customConfig,this,"settings");
1071 connect(settings, SIGNAL(settingsUpdated()),this,SLOT(closeSettings()));
1072 settings->show();
1073}
1074
1075void MyMainView::closeSettings(){
1076 if(Options::lastConfig()<predefinedConfigNum)
1077 config=modifyConfig(predefinedConfig[Options::lastConfig()]);
1078 else
1079 config=modifyConfig(customConfig);
1080}
1081
1082void MyMainView::focusOutEvent (QFocusEvent * /*event*/)
1083{
1084 /* FIXME: ugly hack, I think
1085 it's maybe better to declare something like QMyScene:public QGraphicsScene and process input there */
1086 setFocus(Qt::OtherFocusReason);
1087}
1088
1089#include "mainview.moc"
1090