Update copyright headers
[qt:qt.git] / demos / mobile / quickhit / gameengine.cpp
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 QtDeclarative module 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 #include "gameengine.h"
42 #include "plugins/levelplugininterface.h"
43 #include "InvSounds.h"
44
45 #include <QDebug>
46 #include <QTimerEvent>
47 #include <QTime>
48 #include <QDesktopServices>
49
50 const int TIMER_SPEED = 80;
51
52 GameEngine::GameEngine(QObject* parent)
53     :QObject(parent)
54 {
55     m_timerId = 0;
56     m_doEnemyMissile = 1500 / TIMER_SPEED;
57     m_GameGml = 0;
58     m_gameLevel = 0;
59
60     clearQmlObjects();
61
62     // For random
63     QTime time = QTime::currentTime();
64     qsrand((uint)time.msec());
65
66     // Sound engine
67     m_soundEngine = new CInvSounds(this);
68
69     // Device profile
70     m_silent = false;
71
72 #ifdef Q_OS_SYMBIAN
73     iVibrate = CHWRMVibra::NewL();
74 #endif
75
76     // Get device profile, is it silent?
77 #if defined Q_OS_SYMBIAN || defined Q_WS_MAEMO_5
78     m_systemDeviceInfo = new QSystemDeviceInfo(this);
79     QObject::connect(m_systemDeviceInfo,SIGNAL(currentProfileChanged(QSystemDeviceInfo::Profile)),this,
80                      SLOT(currentProfileChanged(QSystemDeviceInfo::Profile)));
81     QSystemDeviceInfo::Profile p = m_systemDeviceInfo->currentProfile();
82     if (p == QSystemDeviceInfo::SilentProfile) {
83         m_silent = true;
84     }
85 #endif
86
87 }
88
89
90 GameEngine::~GameEngine()
91 {
92 #ifdef Q_OS_SYMBIAN
93     delete iVibrate;
94 #endif
95
96 }
97
98 void GameEngine::gameStartSound()
99 {
100     if (!m_silent)
101         m_soundEngine->gameStartSound();
102 }
103
104 #if defined Q_OS_SYMBIAN || defined Q_WS_MAEMO_5
105 void GameEngine::currentProfileChanged(QSystemDeviceInfo::Profile p)
106 {
107     if (p == QSystemDeviceInfo::SilentProfile) {
108         enableSounds(QVariant(false));
109     } else {
110         enableSounds(QVariant(true));
111     }
112 }
113 #endif
114
115 void GameEngine::enableSounds(QVariant enable)
116 {
117     m_silent = !enable.toBool();
118
119     if (m_silent)
120         this->m_soundEngine->enableSounds(false);
121     else
122         this->m_soundEngine->enableSounds(true);
123
124 }
125
126 QVariant GameEngine::randInt(QVariant low, QVariant high)
127 {
128     // Random number between low and high
129     return qrand() % ((high.toInt() + 1) - low.toInt()) + low.toInt();
130 }
131
132 void GameEngine::setGameLevel(LevelPluginInterface* level)
133 {
134     // Set used game level
135     m_gameLevel = level;
136
137     if (m_gameLevel) {
138         // Set used sound from the level into sound engine
139         m_soundEngine->enableSounds(m_gameLevel->levelSounds());
140         // Invoke QML to take new level in use
141         QMetaObject::invokeMethod(m_GameGml, "levelReadyForCreation", Qt::AutoConnection);
142
143         m_doEnemyMissile = m_gameLevel->enemyFireSpeed().toInt() / TIMER_SPEED;
144     }
145 }
146
147 void GameEngine::setPluginList(QList<QPluginLoader*> plugins)
148 {
149     m_pluginList = plugins;
150 }
151
152 QVariant GameEngine::pluginList()
153 {
154     QStringList list;
155     QPluginLoader* loader;
156     foreach (loader,m_pluginList) {
157         QString s = loader->fileName();
158         s = s.mid(s.lastIndexOf("/")+1);
159         s = s.left(s.lastIndexOf("."));
160         s = s.toUpper();
161 #ifdef Q_WS_MAEMO_5
162         if (s.contains("LIB")) {
163             s = s.right(s.length() - (s.indexOf("LIB")+3));
164         }
165 #endif
166         list.append(s);
167     }
168     return QVariant(list);
169 }
170
171 void GameEngine::pauseLevel(QVariant doPause)
172 {
173     bool enableTimer = !doPause.toBool();
174     enableEngineTimer(QVariant(enableTimer));
175     QMetaObject::invokeMethod(m_levelQml, "pause", Qt::AutoConnection,Q_ARG(QVariant, doPause));
176 }
177
178
179 void GameEngine::findQmlObjects()
180 {
181     if (m_GameGml) {
182         qDebug() << "GameEngine::findQmlObjects()";
183
184         // Find Missiles objects
185         m_missileList.clear();
186         m_enemyMissileList.clear();
187         findMissiles(m_GameGml);
188
189         // Set QMLs
190         setLevelQml(m_GameGml->findChild<QObject*>("level"));
191         setEnemiesGridQml(m_GameGml->findChild<QObject*>("enemiesGrid"));
192         setMyShipQml(m_GameGml->findChild<QObject*>("myShip"));
193
194         // Find Enemies objects
195         m_enemyList.clear();
196         qDebug() << "GameEngine::findQmlObjects() find enemies from: level QML";
197         findEnemies(m_levelQml);
198
199
200     } else {
201         qDebug() << "GameEngine::findQmlObjects() rootObject NULL";
202     }
203 }
204
205 void GameEngine::clearQmlObjects()
206 {
207     m_missileList.clear();
208     m_enemyMissileList.clear();
209     m_enemyList.clear();
210     m_levelQml = 0;
211     m_enemiesGridGml = 0;
212     m_myShipGml = 0;
213     //m_GameGml = 0; // NOTE: Do not delete this
214 }
215
216
217 void GameEngine::findMissiles(QObject *rootObject)
218 {
219     if (rootObject) {
220         QObjectList list = rootObject->children();
221         QObject* item;
222         foreach (item,list) {
223             if (item->children().count()>0) {
224                 findMissiles(item);
225             } else {
226                 if (rootObject->objectName()=="missile") {
227                     QDeclarativeItem* missile = static_cast<QDeclarativeItem*>(rootObject);
228                     m_missileList.append(missile);
229                 } else if (rootObject->objectName()=="enemy_missile") {
230                     QDeclarativeItem* enemyMissile = static_cast<QDeclarativeItem*>(rootObject);
231                     m_enemyMissileList.append(enemyMissile);
232                 }
233             }
234         }
235     } else {
236         qDebug() << "GameEngine::findMissiles() rootObject NULL";
237     }
238 }
239
240 void GameEngine::findEnemies(QObject *rootObject)
241 {
242     if (rootObject) {
243         QObjectList list = rootObject->children();
244         QObject* item;
245         foreach (item,list) {
246             if (item->children().count()>0 && item->objectName()!="enemy") {
247                 //qDebug() << "Enemy children found from: " << item->objectName();
248                 findEnemies(item);
249             } else {
250                 if (item->objectName()=="enemy") {
251                     //qDebug() << "Enemy child founds: " << item->objectName();
252                     QDeclarativeItem* enemy = static_cast<QDeclarativeItem*>(item);
253                     m_enemyList.append(enemy);
254                 }
255             }
256         }
257     } else {
258         qDebug() << "GameEngine::findEnemies() rootObject NULL";
259     }
260 }
261
262 void GameEngine::setEnemiesGridQml(QObject* o)
263 {
264     m_enemiesGridGml = static_cast<QDeclarativeItem*>(o);
265 }
266
267 void GameEngine::setMyShipQml(QObject* o)
268 {
269     m_myShipGml = static_cast<QDeclarativeItem*>(o);
270 }
271
272 void GameEngine::setGameQml(QObject* o)
273 {
274     m_GameGml = static_cast<QDeclarativeItem*>(o);
275 }
276
277
278 void GameEngine::timerEvent(QTimerEvent *e)
279 {
280     if (e->timerId()==m_timerId) {
281         // Do hit test
282         doHitTest();
283
284         m_doEnemyMissile--;
285
286         if (m_gameLevel && m_doEnemyMissile<0) {
287             m_doEnemyMissile = m_gameLevel->enemyFireSpeed().toInt() / TIMER_SPEED;
288             // Do emeny missile launch
289             doEnemyMissile();
290         }
291     }
292 }
293
294 void GameEngine::enableEngineTimer(QVariant enable)
295 {
296     if (m_gameLevel) {
297         if (m_timerId==0 && enable.toBool()) {
298             m_timerId = QObject::startTimer(TIMER_SPEED);
299         }
300         else if (m_timerId != 0 && !enable.toBool()) {
301             QObject::killTimer(m_timerId);
302             m_timerId = 0;
303         }
304     }
305 }
306
307 void GameEngine::selectVisibleEnemy(int& start, int& end)
308 {
309     QDeclarativeItem* enemy = 0;
310     for (int i=0 ; i<m_enemyList.count() ; i++) {
311         enemy = m_enemyList[i];
312         if (enemy->opacity()==1) {
313             start = i;
314             break;
315         }
316     }
317     enemy = 0;
318     for (int e=m_enemyList.count()-1 ; e>0 ; e--) {
319         enemy = m_enemyList[e];
320         if (enemy->opacity()==1) {
321             end = e;
322             break;
323         }
324     }
325 }
326
327 void GameEngine::doEnemyMissile()
328 {
329     QMutexLocker locker(&m_enemyListMutex);
330
331     QDeclarativeItem* missile = 0;
332     QDeclarativeItem* enemy = 0;
333
334     // Find free missile
335     foreach (missile, m_enemyMissileList) {
336         if (missile->opacity()==0){
337             // Random select enemy who fire
338             int start=0; int end=0;
339             selectVisibleEnemy(start,end);
340             int whoWillFire = randInt(QVariant(start),QVariant(end)).toInt();
341             if (m_enemyList.count() < whoWillFire+1)
342                 break;
343
344             enemy = m_enemyList.at(whoWillFire);
345             if (enemy && enemy->opacity()==1) {
346                 QPointF enemyP =  enemy->pos();
347                 if (m_enemiesGridGml) {
348                     enemyP += m_enemiesGridGml->pos();
349                 }
350                 //qDebug() << "QMetaObject::invokeMethod() - fireEnemyMissile";
351                 QMetaObject::invokeMethod(m_GameGml, "fireEnemyMissile", Qt::AutoConnection,
352                                           Q_ARG(QVariant, enemyP.x()+enemy->boundingRect().width()/4),
353                                           Q_ARG(QVariant, enemyP.y()+enemy->boundingRect().height()),
354                                           Q_ARG(QVariant, m_GameGml->boundingRect().height()));
355             }
356             break;
357         }
358     }
359 }
360
361 void GameEngine::doHitTest()
362 {
363     QMutexLocker locker(&m_enemyListMutex);
364
365     QDeclarativeItem* missile = 0;
366     QDeclarativeItem* enemy = 0;
367
368     // No enemies?
369     if (m_enemyList.count()==0) {
370         enableEngineTimer(QVariant(false));
371         qDebug() << "No enemies left";
372         gameOver(true);
373         return;
374     }
375
376     if (!m_myShipGml) {
377         return;
378     }
379
380     // Check ship collision
381     if (m_myShipGml->opacity()==1) {
382         for (int e=0; e<m_enemyList.count(); e++) {
383             enemy = m_enemyList[e];
384             if (enemy->opacity()==0) {
385                 break;
386             }
387             QPointF enemyP =  enemy->pos();
388             if (m_enemiesGridGml) {
389                 enemyP += m_enemiesGridGml->pos();
390             }
391             QRectF enemyR(enemyP,QSize(enemy->boundingRect().width(),enemy->boundingRect().height()));
392             // Collision?
393             if (enemyR.contains(m_myShipGml->pos())) {
394                 enableEngineTimer(QVariant(false));
395
396                 // Collision explosion
397                 QPointF myP =  m_myShipGml->pos();
398                 playSound(1);
399                 QMetaObject::invokeMethod(m_levelQml, "explode", Qt::AutoConnection,
400                                           Q_ARG(QVariant, myP.x()+m_myShipGml->boundingRect().width()/2),
401                                           Q_ARG(QVariant, myP.y()+m_myShipGml->boundingRect().height()));
402                 m_myShipGml->setOpacity(0);
403
404                 gameOver(false);
405                 qDebug() << "Collision";
406                 return;
407             }
408             // Enemy too deep?
409             else if (enemyR.bottomLeft().y() > m_myShipGml->pos().y()+m_myShipGml->pos().y()*0.1) {
410                 enableEngineTimer(QVariant(false));
411
412                 // Enemy too deep explosion
413                 QPointF myP =  m_myShipGml->pos();
414                 playSound(1);
415                 QMetaObject::invokeMethod(m_levelQml, "explode", Qt::AutoConnection,
416                                           Q_ARG(QVariant, myP.x()+m_myShipGml->boundingRect().width()/2),
417                                           Q_ARG(QVariant, myP.y()+m_myShipGml->boundingRect().height()));
418                 m_myShipGml->setOpacity(0);
419
420                 gameOver(false);
421                 qDebug() << "Enemy too deep";
422                 return;
423             }
424         }
425     }
426
427     // Check your missiles hit to enemies
428     foreach (missile, m_missileList) {
429         if (missile->opacity()==1){
430             for (int e=0; e<m_enemyList.count(); e++) {
431                 enemy = m_enemyList[e];
432                 if (enemy->opacity()<1) {
433                     break;
434                 }
435                 QPointF missileP = missile->pos();
436                 missileP.setX(missileP.rx() + missile->boundingRect().width()/2);
437
438                 QPointF enemyP =  enemy->pos();
439                 if (m_enemiesGridGml) {
440                     enemyP += m_enemiesGridGml->pos();
441                 }
442
443                 QRectF r(enemyP,QSize(enemy->boundingRect().width(),enemy->boundingRect().height()));
444                 if (r.contains(missileP)) {
445                     // Hit !
446                     playSound(0);
447                     //qDebug() << "QMetaObject::invokeMethod() - explode";
448                     QMetaObject::invokeMethod(m_levelQml, "explode", Qt::AutoConnection,
449                                               Q_ARG(QVariant, enemyP.x()+enemy->boundingRect().width()/2),
450                                               Q_ARG(QVariant, enemyP.y()+enemy->boundingRect().height()));
451                     missile->setOpacity(0);
452                     //fastVibra();
453                     if (m_enemiesGridGml) {
454                         // Set transparent placeholder for enemy when using GridView
455                         enemy->setProperty("source",QVariant("file:/"+m_gameLevel->pathToTransparentEnemyPic().toString()));
456                     } else {
457                         // Hide enemy after explode
458                         enemy->setOpacity(0);
459                     }
460
461                     // Remove enemy from list
462                     m_enemyList.removeAt(e);
463                     e--;
464                 }
465                 enemy = 0;
466             }
467         }
468     }
469
470     // Check enemies missiles hit to you
471     if (m_myShipGml->opacity()==1) {
472         foreach (missile, m_enemyMissileList) {
473             if (missile->opacity()==1){
474                 QPointF missileP = missile->pos();
475                 missileP.setX(missileP.rx() + missile->boundingRect().width()/2);
476
477                 QPointF myP =  m_myShipGml->pos();
478
479                 QRectF r(myP,QSize(m_myShipGml->boundingRect().width(),m_myShipGml->boundingRect().height()));
480                 if (r.contains(missileP)) {
481                     // Hit !
482                     playSound(1);
483                     //qDebug() << "QMetaObject::invokeMethod() - explode";
484                     QMetaObject::invokeMethod(m_levelQml, "explode", Qt::AutoConnection,
485                                               Q_ARG(QVariant, myP.x()+m_myShipGml->boundingRect().width()/2),
486                                               Q_ARG(QVariant, myP.y()+m_myShipGml->boundingRect().height()));
487                     missile->setOpacity(0);
488                     m_myShipGml->setOpacity(0);
489                     break;
490                 }
491             }
492         }
493     } else {
494         // You was killed
495         enableEngineTimer(QVariant(false));
496         gameOver(false);
497         qDebug() << "You was killed by enemy missile";
498     }
499
500 }
501
502
503 void GameEngine::playSound(QVariant index)
504 {
505     if (!m_silent) {
506         int i = index.toInt();
507         m_soundEngine->playSound(i);
508     }
509 }
510
511 void GameEngine::playSounds(QVariant index, QVariant count)
512 {
513     if (!m_silent) {
514         m_soundEngine->playSounds(index.toInt(),count.toInt());
515     }
516 }
517
518 void GameEngine::playInternalSound(QVariant index)
519 {
520     if (!m_silent) {
521         m_soundEngine->playInternalSound(index.toInt());
522     }
523 }
524
525 void GameEngine::playInternalSounds(QVariant index, QVariant count)
526 {
527     if (!m_silent) {
528         m_soundEngine->playInternalSounds(index.toInt(),count.toInt());
529     }
530 }
531
532 void GameEngine::gameOver(bool youWin)
533 {
534     qDebug() << "GameEngine::gameOver() "<< youWin;
535     QMetaObject::invokeMethod(m_GameGml, "gameOver", Qt::AutoConnection,Q_ARG(QVariant, youWin));
536 }
537
538 void GameEngine::pauseGame() {
539     QMetaObject::invokeMethod(m_GameGml, "pauseGame", Qt::AutoConnection);
540 }
541
542
543 QVariant GameEngine::isSymbian()
544 {
545 #ifdef Q_OS_SYMBIAN
546     return QVariant(true);
547 #else
548     return QVariant(false);
549 #endif
550 }
551
552 QVariant GameEngine::isMaemo()
553 {
554 #ifdef Q_WS_MAEMO_5
555     return QVariant(true);
556 #else
557     return QVariant(false);
558 #endif
559 }
560
561 QVariant GameEngine::isWindows()
562 {
563 #ifdef Q_OS_WIN
564     return QVariant(true);
565 #else
566     return QVariant(false);
567 #endif
568 }
569
570 void GameEngine::vibra()
571 {
572 #ifdef Q_OS_SYMBIAN
573     if (iVibrate){
574         TRAPD(err, iVibrate->StartVibraL(4000,KHWRMVibraMaxIntensity));
575     }
576 #endif
577 }
578
579 void GameEngine::fastVibra()
580 {
581 #ifdef Q_OS_SYMBIAN
582     if (iVibrate){
583         TRAPD(err, iVibrate->StartVibraL(100,KHWRMVibraMaxIntensity));
584     }
585 #endif
586 }
587
588 void GameEngine::openLink(QVariant link)
589 {
590 QDesktopServices::openUrl(QUrl(link.toString()));
591 }
592