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 | |
44 | KToggleAction *MyMainView::pauseAction = 0; |
45 | |
46 | static 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 | |
60 | MyMainView::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 | |
121 | MyMainView::~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 | |
145 | void 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) |
154 | bool 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 | |
177 | void 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 | |
251 | void 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 | |
286 | SConfig 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 | |
299 | void 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 | |
333 | void 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 | |
363 | void 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 | |
375 | void 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 | |
383 | void 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 | |
400 | void MyMainView::stop() |
401 | { |
402 | pauseAction->setEnabled( false ); |
403 | pauseAction->setChecked( false ); |
404 | |
405 | QAbstractEventDispatcher::instance()->unregisterTimers(this); |
406 | waitForStart = true; |
407 | } |
408 | |
409 | void MyMainView::togglePause( ) |
410 | { |
411 | if( waitForStart ) |
412 | resume( ); |
413 | else |
414 | pause( ); |
415 | } |
416 | |
417 | void 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 | |
463 | void 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 | |
531 | void 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 | |
542 | void 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 | |
616 | void 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 | |
744 | void 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 | |
773 | void 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 | |
800 | void 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 | |
821 | void 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 | |
861 | void 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 | |
1062 | void 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 | |
1075 | void MyMainView::closeSettings(){ |
1076 | if(Options::lastConfig()<predefinedConfigNum) |
1077 | config=modifyConfig(predefinedConfig[Options::lastConfig()]); |
1078 | else |
1079 | config=modifyConfig(customConfig); |
1080 | } |
1081 | |
1082 | void 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 | |