1 /*
2     SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de>
3     SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "player.h"
9 #include "bonus.h"
10 #include "bomb.h"
11 #include "arena.h"
12 #include "config/playersettings.h"
13 #include "settings.h"
14 
15 #include <QKeyEvent>
16 #include <QTimer>
17 
18 #include <cmath>
19 
20 constexpr int onIceSpeedIncrease = 2;
21 constexpr int badBonusTimerTimeout = 100;
22 constexpr int badBonusCountdown = 10000;
23 
Player(qreal p_x,qreal p_y,const QString & p_playerID,const PlayerSettings * p_playerSettings,Arena * p_arena)24 Player::Player(qreal p_x, qreal p_y, const QString& p_playerID, const PlayerSettings* p_playerSettings, Arena* p_arena)
25  : Character(p_x, p_y, p_arena)
26  , m_throwBomb(false)
27  , m_kickBomb(false)
28 {
29     m_type = Granatier::Element::PLAYER;
30     m_desktopFilePath = p_playerSettings->playerDesktopFilePath(p_playerID);
31     m_graphicsFile = p_playerSettings->playerGraphicsFile(p_playerID);
32     m_playerName = p_playerSettings->playerName(p_playerID);
33 
34     m_points = 0;
35 
36     m_direction = Granatier::Direction::EAST;
37 
38     m_badBonusCountdownTimer = new QTimer;
39     m_badBonusCountdownTimer->setInterval(badBonusTimerTimeout);
40     m_badBonusMillisecondsToElapse = 0;
41     connect(m_badBonusCountdownTimer, &QTimer::timeout, this, &Player::slot_badBonusTimerTimeout);
42 
43     resurrect();
44 
45     m_key.moveLeft = p_playerSettings->keyLeft(p_playerID);
46     m_key.moveRight = p_playerSettings->keyRight(p_playerID);
47     m_key.moveUp = p_playerSettings->keyUp(p_playerID);
48     m_key.moveDown = p_playerSettings->keyDown(p_playerID);
49     m_key.dropBomb = p_playerSettings->keyPutBomb(p_playerID);
50 }
51 
~Player()52 Player::~Player()
53 {
54     delete m_badBonusCountdownTimer;
55 }
56 
setShortcuts(const Shortcuts & keys)57 void Player::setShortcuts(const Shortcuts &keys)
58 {
59     m_key = keys;
60 }
61 
getGraphicsFile() const62 QString Player::getGraphicsFile() const
63 {
64     return m_graphicsFile;
65 }
66 
getDesktopFilePath() const67 QString Player::getDesktopFilePath() const
68 {
69   return m_desktopFilePath;
70 }
71 
getPlayerName() const72 QString Player::getPlayerName() const
73 {
74     return m_playerName;
75 }
76 
init()77 void Player::init()
78 {
79     updateDirection();
80     stopMoving();
81     int row = m_arena->getRowFromY(m_y);
82     int column = m_arena->getColFromX(m_x);
83     m_arena->setCellElement(row, column, this);
84 }
85 
pause()86 void Player::pause()
87 {
88     m_badBonusCountdownTimer->stop();
89 }
90 
resume()91 void Player::resume()
92 {
93     if(m_badBonusMillisecondsToElapse > 0)
94     {
95         m_badBonusCountdownTimer->start();
96     }
97 }
98 
goUp()99 void Player::goUp()
100 {
101     m_askedXSpeed = 0;
102 
103     qreal nSpeed = m_speed;
104     if(m_onIce)
105     {
106         nSpeed = m_speed + onIceSpeedIncrease;
107     }
108     m_askedYSpeed = -nSpeed;
109 
110     m_direction = Granatier::Direction::NORTH;
111 }
112 
goDown()113 void Player::goDown()
114 {
115     m_askedXSpeed = 0;
116 
117     qreal nSpeed = m_speed;
118     if(m_onIce)
119     {
120         nSpeed = m_speed + onIceSpeedIncrease;
121     }
122     m_askedYSpeed = nSpeed;
123 
124     m_direction = Granatier::Direction::SOUTH;
125 }
126 
goRight()127 void Player::goRight()
128 {
129     qreal nSpeed = m_speed;
130     if(m_onIce)
131     {
132         nSpeed = m_speed + onIceSpeedIncrease;
133     }
134     m_askedXSpeed = nSpeed;
135 
136     m_askedYSpeed = 0;
137 
138     m_direction = Granatier::Direction::EAST;
139 }
140 
goLeft()141 void Player::goLeft()
142 {
143     qreal nSpeed = m_speed;
144     if(m_onIce)
145     {
146         nSpeed = m_speed + onIceSpeedIncrease;
147     }
148     m_askedXSpeed = -nSpeed;
149 
150     m_askedYSpeed = 0;
151 
152     m_direction = Granatier::Direction::WEST;
153 }
154 
updateDirection()155 void Player::updateDirection()
156 {
157     if(m_death)
158     {
159         return;
160     }
161 
162     setXSpeed(m_askedXSpeed);
163     setYSpeed(m_askedYSpeed);
164     m_askedXSpeed = 0;
165     m_askedYSpeed = 0;
166     // Signal to the player item that the direction changed
167     Q_EMIT directionChanged();
168 }
169 
updateMove()170 void Player::updateMove()
171 {
172     if(m_death)
173     {
174         return;
175     }
176 
177     //check if there is a hurdle in the way
178     if(m_askedXSpeed != 0 || m_xSpeed != 0 || m_askedYSpeed != 0 || m_ySpeed != 0)
179     {
180         int xDirection = 0;                 //x-direction: -1: move left; 0: not moving; 1: move right
181         int yDirection = 0;                 //y-direction: -1: move up; 0: not moving; 1: move down
182         int straightDirection = 0;          //straight direction: -1: backward; 1:foreward; while foreward is right for moving in x-direction and down for y-direction
183         qreal deltaStraightMove = 0;        //the move in straight direction
184         qreal deltaPerpendicularMove = 0;   //the move in perpendicular direction; e.g. the player is not in cell center and will collide with a wall in the cell above, so the player has to be moved to the cell center
185         qreal deltaAskedMove;               //how far to move; positive for right/down move and negative for left/up
186         qreal deltaStraightCellCorner;      //move in x-direction: the x-pos from the top left cell corner; move in y-direction: the y-pos from the top left cell corner
187         qreal deltaPerpendicularCellCorner; //move in x-direction: the y-pos from the top left cell corner; move in y-direction: the x-pos from the top left cell corner
188         qreal deltaStraightCellCenter;      //distance to the cell center in moving direction; positive if left/up from cell center, negative if right/down
189         qreal deltaPerpendicularCellCenter; //distance to the cell center perpendicular to moving direction; positive if up/left from cell center, negative if down/right
190         bool bMoveWithinNextCellCenter = false;     //move is completed without exceeding the cell center
191         int cellCol;
192         int cellRow;
193 
194         // Get the current cell coordinates from the character coordinates
195         int moveStartRow = m_arena->getRowFromY(m_y);
196         int moveStartCol = m_arena->getColFromX(m_x);
197         int curCellRow = moveStartRow;
198         int curCellCol = moveStartCol;
199 
200         //set variables for right/left move
201         if(m_askedXSpeed != 0 || m_xSpeed != 0)
202         {
203             //how far to move
204             deltaAskedMove = (m_askedXSpeed != 0 ? m_askedXSpeed : m_xSpeed);
205 
206             //direction to move
207             xDirection = sign(deltaAskedMove);
208             straightDirection = xDirection;
209 
210             deltaStraightCellCorner = m_x - curCellCol * Granatier::CellSize;
211             deltaPerpendicularCellCorner = m_y - curCellRow * Granatier::CellSize;
212         }
213         else        //set variables for down/up move
214         {
215             //how far to move
216             deltaAskedMove = (m_askedYSpeed != 0 ? m_askedYSpeed : m_ySpeed);
217 
218             //direction to move
219             yDirection = sign(deltaAskedMove);
220             straightDirection = yDirection;
221 
222             deltaStraightCellCorner = m_y - curCellRow * Granatier::CellSize;
223             deltaPerpendicularCellCorner = m_x - curCellCol * Granatier::CellSize;
224         }
225 
226         //how far to current cell center
227         deltaStraightCellCenter = Granatier::CellSize/2 - deltaStraightCellCorner;
228         deltaPerpendicularCellCenter = Granatier::CellSize/2 - deltaPerpendicularCellCorner;
229 
230         //check if the move exceeds a cell center
231         if(straightDirection*deltaStraightCellCenter >= 0)
232         {
233             if(fabs(deltaAskedMove) <= fabs(deltaStraightCellCenter))
234             {
235                 bMoveWithinNextCellCenter = true;
236             }
237         }
238         else if(fabs(deltaAskedMove) + fabs(deltaStraightCellCenter) <= Granatier::CellSize)
239         {
240             bMoveWithinNextCellCenter = true;
241         }
242 
243         //the move is within two cell centers
244         if(bMoveWithinNextCellCenter)
245         {
246             bool isHurdle = false;
247             if(!(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::SCATTY))
248             {
249                 bool kickBomb = false;
250                 QList<Element*> bombElements;
251                 //moving towards cell center; don't move if there is a bomb in the cell
252                 if(deltaStraightCellCenter * straightDirection > 0 && !m_arena->getCell(moveStartRow, moveStartCol).isWalkable(this) && !m_omitBombCurrentCell)
253                 {
254                     isHurdle = true;
255                     if(m_kickBomb)
256                     {
257                         kickBomb = true;
258                         bombElements =  m_arena->getCell(moveStartRow, moveStartCol).getElements(Granatier::Element::BOMB);
259                     }
260                 }
261                 //moving away of cell center; don't move if there is a bomb in the next cell; ignore a bomb in the current cell
262                 else if(deltaStraightCellCenter * straightDirection < 0 && !m_arena->getCell(moveStartRow + yDirection, moveStartCol + xDirection).isWalkable(this))
263                 {
264                     isHurdle = true;
265                     if(m_kickBomb)
266                     {
267                         kickBomb = true;
268                         bombElements =  m_arena->getCell(moveStartRow + yDirection, moveStartCol + xDirection).getElements(Granatier::Element::BOMB);
269                     }
270                 }
271                 if(kickBomb)
272                 {
273                     for(auto& element: bombElements)
274                     {
275                         dynamic_cast <Bomb*> (element)->setKicked(m_direction);
276                     }
277                 }
278             }
279             if(!isHurdle)
280             {
281                 deltaStraightMove += deltaAskedMove;
282                 //move to perpendicular center if needed
283                 if(deltaPerpendicularCellCenter != 0 && (straightDirection * deltaStraightCellCenter) < 0)  //not in perpendicular center and entering a new cell
284                 {
285                     if(fabs(deltaPerpendicularCellCenter) > Granatier::CellSize/2 - fabs(deltaStraightMove - deltaStraightCellCenter))   //check if it already can collide with a hurdle
286                     {
287                         cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
288                         cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
289                         if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
290                         {
291                             deltaPerpendicularMove = deltaPerpendicularCellCenter + signZeroPositive(deltaPerpendicularCellCenter) * (fabs(deltaStraightMove - deltaStraightCellCenter) - Granatier::CellSize/2);
292                             if(fabs(deltaPerpendicularMove) > fabs(deltaPerpendicularCellCenter))       //check if moved over perpendicular center
293                             {
294                                 deltaPerpendicularMove = deltaPerpendicularCellCenter;
295                             }
296                         }
297                     }
298                 }
299             }
300         }
301         else    //the move exceeds a cell center
302         {
303             //at first move to the cell center
304             deltaStraightMove += deltaStraightCellCenter;
305             deltaAskedMove -= deltaStraightCellCenter;
306             if(straightDirection * deltaStraightCellCenter < 0)     //the cell center to move is in the next cell
307             {
308                 deltaStraightMove += straightDirection * Granatier::CellSize;
309                 deltaAskedMove -= straightDirection * Granatier::CellSize;
310 
311                 //move to perpendicular center if needed
312                 if(deltaPerpendicularCellCenter != 0)
313                 {
314                     cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
315                     cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
316                     if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
317                     {
318                         deltaPerpendicularMove = deltaPerpendicularCellCenter;
319                     }
320                 }
321 
322                 //update the current cell and row
323                 curCellCol += xDirection;
324                 curCellRow += yDirection;
325             }
326             while(fabs(deltaAskedMove) > 0)     //complete the move
327             {
328                 if(m_arena->getCell(curCellRow + yDirection, curCellCol + xDirection).isWalkable(this))     //check if next cell is walkable
329                 {
330                     if(fabs(deltaAskedMove) > Granatier::CellSize)   //move to next cell center if the remaining move exceeds a cell center
331                     {
332                         deltaStraightMove += straightDirection * Granatier::CellSize;
333                         deltaAskedMove -= straightDirection * Granatier::CellSize;
334                         //move to perpendicular center if needed
335                         if(deltaPerpendicularCellCenter != 0)
336                         {
337                             cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
338                             cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
339                             if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
340                             {
341                                 deltaPerpendicularMove = deltaPerpendicularCellCenter;
342                             }
343                         }
344                     }
345                     else
346                     {
347                         deltaStraightMove += deltaAskedMove;
348                         //move to perpendicular center if needed
349                         if(deltaPerpendicularMove != deltaPerpendicularCellCenter && fabs(deltaPerpendicularCellCenter) > (Granatier::CellSize/2 - fabs(deltaStraightMove - deltaStraightCellCenter)))   //check if it is in or already moved to perpendicular center and if it already can collide with a hurdle ***TODO: it seems to be wrong to use deltaStraightMove here, because ist could be greater than Granatier::CellSize
350                         {
351                             cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
352                             cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
353                             if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
354                             {
355                                 deltaPerpendicularMove = signZeroPositive(deltaPerpendicularCellCenter) * fabs(deltaAskedMove);
356                                 if(fabs(deltaPerpendicularMove) > fabs(deltaPerpendicularCellCenter))
357                                 {
358                                     deltaPerpendicularMove = deltaPerpendicularCellCenter;      //check if moved over perpendicular center
359                                 }
360                             }
361                         }
362                         deltaAskedMove = 0;
363                     }
364                     //update the current cell and row
365                     curCellCol += xDirection;
366                     curCellRow += yDirection;
367                 }
368                 else    //there is a hurdle in the next cell, so stop moving
369                 {
370                     deltaAskedMove = 0;
371                     cellRow = curCellRow + yDirection;
372                     cellCol = curCellCol + xDirection;
373                     //check if bomb
374                     if(m_kickBomb)
375                     {
376                         QList<Element*> bombElements =  m_arena->getCell(cellRow, cellCol).getElements(Granatier::Element::BOMB);
377                         for(auto& element: bombElements)
378                         {
379                             dynamic_cast <Bomb*> (element)->setKicked(m_direction);
380                         }
381                     }
382                 }
383             }
384         }
385 
386         // Update the direction
387         if(m_askedXSpeed != 0 || m_askedYSpeed != 0)
388         {
389             updateDirection();
390         }
391 
392         // Move the player
393         if(xDirection != 0)
394         {
395             move(m_x + deltaStraightMove, m_y + deltaPerpendicularMove);
396         }
397         else
398         {
399             move(m_x + deltaPerpendicularMove, m_y + deltaStraightMove);
400         }
401 
402         //check if the player is on ice
403         // Get the current cell coordinates from the character coordinates
404         int newCellRow = m_arena->getRowFromY(m_y);
405         int newCellCol = m_arena->getColFromX(m_x);
406         if(!m_onIce)
407         {
408             if(m_arena->getCell(newCellRow, newCellCol).getType() == Granatier::Cell::ICE)
409             {
410                 if(xDirection != 0)
411                 {
412                     setXSpeed(m_xSpeed + xDirection * onIceSpeedIncrease);
413                 }
414                 else
415                 {
416                     setYSpeed(m_ySpeed + yDirection * onIceSpeedIncrease);
417                 }
418                 m_onIce = true;
419             }
420         }
421         else
422         {
423             if(m_arena->getCell(newCellRow, newCellCol).getType() != Granatier::Cell::ICE)
424             {
425                 if(m_arena->getCell(newCellRow, newCellCol).getType() != Granatier::Cell::HOLE)
426                 {
427                     if(xDirection != 0)
428                     {
429                         setXSpeed(m_xSpeed - xDirection * onIceSpeedIncrease);
430                     }
431                     else
432                     {
433                         setYSpeed(m_ySpeed - yDirection * onIceSpeedIncrease);
434                     }
435                 }
436                 m_onIce = false;
437 
438                 if(m_xSpeed == 0 && m_ySpeed == 0 && m_askedXSpeed == 0 && m_askedYSpeed == 0)
439                 {
440                     stopMoving();
441                 }
442             }
443         }
444 
445         //check if the player move in a hole
446         if(m_arena->getCell(newCellRow, newCellCol).getType() == Granatier::Cell::HOLE)
447         {
448             m_falling = true;
449             //check if cell center passed
450             if(xDirection != 0)
451             {
452                 qreal cellCenter = newCellCol * Granatier::CellSize + 0.5 * Granatier::CellSize;
453                 qreal deltaCellCenter = cellCenter - (m_x + m_xSpeed);
454                 if (cellCenter - m_x == 0)
455                 {
456                     setXSpeed(0);
457                     Q_EMIT falling();
458                 }
459                 else if (xDirection * deltaCellCenter < 0)
460                 {
461                     setXSpeed(cellCenter - m_x);
462                 }
463             }
464             else if (yDirection != 0)
465             {
466                 qreal cellCenter = newCellRow * Granatier::CellSize + 0.5 * Granatier::CellSize;
467                 qreal deltaCellCenter = cellCenter - (m_y + m_ySpeed);
468                 if (cellCenter - m_y == 0)
469                 {
470                     setYSpeed(0);
471                     Q_EMIT falling();
472                 }
473                 else if (yDirection * deltaCellCenter < 0)
474                 {
475                     setYSpeed(cellCenter - m_y);
476                 }
477             }
478         }
479 
480         if(moveStartCol != newCellCol || moveStartRow != newCellRow)
481         {
482             m_arena->removeCellElement(moveStartRow, moveStartCol, this);
483             m_arena->setCellElement(newCellRow, newCellCol, this);
484             m_omitBombCurrentCell = false;
485         }
486     }
487 
488     //check if bad bonus scatty and drop bombs
489     if(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::SCATTY  && m_bombArmory > 0)
490     {
491         //TODO: improve
492         Q_EMIT bombDropped(this, m_x, m_y, true, 0);
493     }
494 }
495 
move(qreal x,qreal y)496 void Player::move(qreal x, qreal y)
497 {
498     // Move the Character
499     m_x = x;
500     m_y = y;
501     Q_EMIT moved(m_x, m_y);
502 }
503 
addBonus(Bonus * p_bonus)504 void Player::addBonus(Bonus* p_bonus)
505 {
506     Granatier::Bonus::Type bonusType = p_bonus->getBonusType();
507 
508     if(m_badBonusCountdownTimer->isActive())
509     {
510         m_badBonusCountdownTimer->stop();
511         slot_removeBadBonus();
512     }
513 
514     switch (bonusType)
515     {
516         case Granatier::Bonus::SPEED:
517             m_speed += 1;
518             if(m_speed > m_maxSpeed)
519             {
520                 m_speed = m_maxSpeed;
521             }
522             setXSpeed(sign(m_xSpeed) * m_speed);
523             setYSpeed(sign(m_ySpeed) * m_speed);
524             break;
525         case Granatier::Bonus::POWER:
526             m_bombPower++;
527             if(m_bombPower > 10)
528             {
529                 m_bombPower = 10;
530             }
531             break;
532         case Granatier::Bonus::BOMB:
533             m_maxBombArmory++;
534             if(m_maxBombArmory > 10)
535             {
536                 m_maxBombArmory = 10;
537             }
538             m_bombArmory++;
539             if(m_bombArmory > m_maxBombArmory)
540             {
541                 m_bombArmory = m_maxBombArmory;
542             }
543             break;
544         case Granatier::Bonus::SHIELD:
545             if(m_listShield.isEmpty() || m_listShield.last() != 0)
546             {
547                 m_listShield.append(0);
548             }
549             break;
550         case Granatier::Bonus::THROW:
551             m_throwBomb = true;
552             break;
553         case Granatier::Bonus::KICK:
554             m_kickBomb = true;
555             break;
556         case Granatier::Bonus::HYPERACTIVE:
557             {
558                 qreal askedXSpeedTemp = m_askedXSpeed;
559                 qreal askedYSpeedTemp = m_askedYSpeed;
560                 m_normalSpeed = m_speed;
561                 m_speed = m_maxSpeed * 3;
562                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
563                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
564                 updateDirection();
565                 m_askedXSpeed = askedXSpeedTemp;
566                 m_askedYSpeed = askedYSpeedTemp;
567 
568                 m_badBonusType = Granatier::Bonus::HYPERACTIVE;
569                 m_badBonusMillisecondsToElapse = badBonusCountdown;
570                 m_badBonusCountdownTimer->start();
571             }
572             break;
573         case Granatier::Bonus::SLOW:
574             {
575                 qreal askedXSpeedTemp = m_askedXSpeed;
576                 qreal askedYSpeedTemp = m_askedYSpeed;
577                 m_normalSpeed = m_speed;
578                 m_speed = 1;
579                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
580                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
581                 updateDirection();
582                 m_askedXSpeed = askedXSpeedTemp;
583                 m_askedYSpeed = askedYSpeedTemp;
584 
585                 m_badBonusType = Granatier::Bonus::SLOW;
586                 m_badBonusMillisecondsToElapse = badBonusCountdown;
587                 m_badBonusCountdownTimer->start();
588             }
589             break;
590         case Granatier::Bonus::MIRROR:
591             {
592                 qreal askedXSpeedTemp = m_askedXSpeed;
593                 qreal askedYSpeedTemp = m_askedYSpeed;
594                 m_askedXSpeed = -m_xSpeed;
595                 m_askedYSpeed = -m_ySpeed;
596                 switch(m_direction)
597                 {
598                     case Granatier::Direction::EAST:
599                         m_direction = Granatier::Direction::WEST;
600                         break;
601                     case Granatier::Direction::WEST:
602                         m_direction = Granatier::Direction::EAST;
603                         break;
604                     case Granatier::Direction::NORTH:
605                         m_direction = Granatier::Direction::SOUTH;
606                         break;
607                     case Granatier::Direction::SOUTH:
608                         m_direction = Granatier::Direction::NORTH;
609                         break;
610                 }
611                 updateDirection();
612                 m_askedXSpeed = -askedXSpeedTemp;
613                 m_askedYSpeed = -askedYSpeedTemp;
614 
615                 QKeySequence tempKey = m_key.moveLeft;
616                 m_key.moveLeft = m_key.moveRight;
617                 m_key.moveRight = tempKey;
618                 tempKey = m_key.moveUp;
619                 m_key.moveUp = m_key.moveDown;
620                 m_key.moveDown = tempKey;
621 
622                 m_moveMirrored = true;
623                 m_badBonusType = Granatier::Bonus::MIRROR;
624                 m_badBonusMillisecondsToElapse = badBonusCountdown;
625                 m_badBonusCountdownTimer->start();
626             }
627             break;
628         case Granatier::Bonus::SCATTY:
629             m_badBonusType = Granatier::Bonus::SCATTY;
630             m_badBonusMillisecondsToElapse = badBonusCountdown;
631             m_badBonusCountdownTimer->start();
632             break;
633         case Granatier::Bonus::RESTRAIN:
634             m_normalBombArmory = m_bombArmory;
635             m_bombArmory = 0;
636             m_badBonusType = Granatier::Bonus::RESTRAIN;
637             m_badBonusMillisecondsToElapse = badBonusCountdown;
638             m_badBonusCountdownTimer->start();
639             break;
640         case Granatier::Bonus::RESURRECT:
641             Q_EMIT resurrectBonusTaken();
642             break;
643         default:
644             break;
645     }
646 
647     Q_EMIT bonusUpdated(this, bonusType, 0);
648 }
649 
shield(int nExplosionID)650 bool Player::shield(int nExplosionID)
651 {
652     for(int i = 0; i < m_listShield.count(); i++)
653     {
654         if(m_listShield[i] == nExplosionID)
655         {
656             return true;
657         }
658         else if(m_listShield[i] == 0)
659         {
660             m_listShield[i] = nExplosionID;
661             if(i == m_listShield.count()-1)
662             {
663                 Q_EMIT bonusUpdated(this, Granatier::Bonus::SHIELD, 100);
664             }
665             return true;
666         }
667     }
668     return false;
669 }
670 
hasShield()671 bool Player::hasShield()
672 {
673     if(m_listShield.count() > 0 && m_listShield.last() == 0)
674     {
675         return true;
676     }
677     return false;
678 }
679 
hasThrowBomb()680 bool Player::hasThrowBomb()
681 {
682     return m_throwBomb;
683 }
684 
hasKickBomb()685 bool Player::hasKickBomb()
686 {
687     return m_kickBomb;
688 }
689 
hasBadBonus()690 bool Player::hasBadBonus()
691 {
692     if(m_badBonusCountdownTimer->isActive())
693     {
694         return true;
695     }
696     return false;
697 }
698 
die()699 void Player::die()
700 {
701     if(!m_death)
702     {
703         m_death = true;
704         Q_EMIT dying();
705         m_xSpeed = 0;
706         m_xSpeed = 0;
707 
708         if(m_badBonusCountdownTimer->isActive())
709         {
710             m_badBonusCountdownTimer->stop();
711             slot_removeBadBonus();
712         }
713         int row = m_arena->getRowFromY(m_y);
714         int column = m_arena->getColFromX(m_x);
715         m_arena->removeCellElement(row, column, this);
716     }
717 }
718 
isAlive() const719 bool Player::isAlive() const
720 {
721     return !m_death;
722 }
723 
resurrect()724 void Player::resurrect()
725 {
726     if(m_badBonusMillisecondsToElapse > 0)
727     {
728         slot_removeBadBonus();
729     }
730 
731     m_onIce = false;
732     m_falling = false;
733     m_death = false;
734     m_maxSpeed = 10;
735     m_speed = Settings::self()->initialSpeed();
736     m_normalSpeed = m_speed;
737     m_moveMirrored = false;
738     m_bombPower = Settings::self()->initialBombPower();
739     m_maxBombArmory = Settings::self()->initialBombArmory();
740     m_bombArmory = m_maxBombArmory;
741     if(m_listShield.count() != 0)
742     {
743         m_listShield.clear();
744         Q_EMIT bonusUpdated(this, Granatier::Bonus::SHIELD, 100);
745     }
746     if(m_throwBomb)
747     {
748         m_throwBomb = false;
749         Q_EMIT bonusUpdated(this, Granatier::Bonus::THROW, 100);
750     }
751     if(m_kickBomb)
752     {
753         m_kickBomb = false;
754         Q_EMIT bonusUpdated(this, Granatier::Bonus::KICK, 100);
755     }
756     m_omitBombCurrentCell = false;
757     if(m_badBonusCountdownTimer->isActive())
758     {
759         m_badBonusCountdownTimer->stop();
760         slot_removeBadBonus();
761     }
762 
763     //check if the player is above a hole
764     if(m_arena)
765     {
766         int cellRow = m_arena->getRowFromY(m_y);
767         int cellCol = m_arena->getColFromX(m_x);
768 
769         m_arena->removeCellElement(cellRow, cellCol, this); //just to be really sure
770 
771         if(m_arena->getCell(cellRow, cellCol).getType() == Granatier::Cell::HOLE)
772         {
773             move(m_xInit, m_yInit);
774             cellRow = m_arena->getRowFromY(m_yInit);
775             cellCol = m_arena->getColFromX(m_xInit);
776         }
777 
778         m_arena->setCellElement(cellRow, cellCol, this);
779     }
780 
781     Q_EMIT resurrected();
782 }
783 
points() const784 int Player::points() const
785 {
786     return m_points;
787 }
788 
addPoint()789 void Player::addPoint()
790 {
791     m_points++;
792 }
793 
emitGameUpdated()794 void Player::emitGameUpdated()
795 {
796     Q_EMIT gameUpdated();
797 }
798 
getAskedXSpeed() const799 qreal Player::getAskedXSpeed() const
800 {
801     return m_askedXSpeed;
802 }
803 
getAskedYSpeed() const804 qreal Player::getAskedYSpeed() const
805 {
806     return m_askedYSpeed;
807 }
808 
direction()809 int Player::direction()
810 {
811     return m_direction;
812 }
813 
getBombPower() const814 int Player::getBombPower() const
815 {
816     return m_bombPower;
817 }
818 
decrementBombArmory()819 void Player::decrementBombArmory()
820 {
821     m_bombArmory--;
822     if(m_bombArmory < 0)
823     {
824         m_bombArmory = 0;
825     }
826 }
827 
slot_refillBombArmory()828 void Player::slot_refillBombArmory()
829 {
830     int* bombArmory = &m_bombArmory;
831     if(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::RESTRAIN)
832     {
833         bombArmory = &m_normalBombArmory;
834     }
835     (*bombArmory)++;
836     if((*bombArmory) > m_maxBombArmory)
837     {
838         (*bombArmory) = m_maxBombArmory;
839     }
840 }
841 
slot_badBonusTimerTimeout()842 void Player::slot_badBonusTimerTimeout()
843 {
844     m_badBonusMillisecondsToElapse -= badBonusTimerTimeout;
845     if(m_badBonusMillisecondsToElapse <= 0)
846     {
847         slot_removeBadBonus();
848     }
849     else
850     {
851         bonusUpdated(this, m_badBonusType, (badBonusCountdown - m_badBonusMillisecondsToElapse) * 100 / badBonusCountdown);
852     }
853 }
854 
slot_removeBadBonus()855 void Player::slot_removeBadBonus()
856 {
857     m_badBonusCountdownTimer->stop();
858     m_badBonusMillisecondsToElapse = 0;
859 
860     switch (m_badBonusType)
861     {
862         case Granatier::Bonus::HYPERACTIVE:
863         case Granatier::Bonus::SLOW:
864             {
865                 qreal askedXSpeedTemp = m_askedXSpeed;
866                 qreal askedYSpeedTemp = m_askedYSpeed;
867                 m_speed = m_normalSpeed;
868                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
869                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
870                 updateDirection();
871                 m_askedXSpeed = askedXSpeedTemp;
872                 m_askedYSpeed = askedYSpeedTemp;
873             }
874             break;
875         case Granatier::Bonus::MIRROR:
876             {
877                 qreal askedXSpeedTemp = m_askedXSpeed;
878                 qreal askedYSpeedTemp = m_askedYSpeed;
879                 m_askedXSpeed = -m_xSpeed;
880                 m_askedYSpeed = -m_ySpeed;
881                 switch(m_direction)
882                 {
883                     case Granatier::Direction::EAST:
884                         m_direction = Granatier::Direction::WEST;
885                         break;
886                     case Granatier::Direction::WEST:
887                         m_direction = Granatier::Direction::EAST;
888                         break;
889                     case Granatier::Direction::NORTH:
890                         m_direction = Granatier::Direction::SOUTH;
891                         break;
892                     case Granatier::Direction::SOUTH:
893                         m_direction = Granatier::Direction::NORTH;
894                         break;
895                 }
896                 updateDirection();
897                 m_askedXSpeed = -askedXSpeedTemp;
898                 m_askedYSpeed = -askedYSpeedTemp;
899 
900                 QKeySequence tempKey = m_key.moveLeft;
901                 m_key.moveLeft = m_key.moveRight;
902                 m_key.moveRight = tempKey;
903                 tempKey = m_key.moveUp;
904                 m_key.moveUp = m_key.moveDown;
905                 m_key.moveDown = tempKey;
906 
907                 m_moveMirrored = false;
908             }
909             break;
910         case Granatier::Bonus::RESTRAIN:
911             m_bombArmory = m_normalBombArmory;
912             break;
913         default:
914             break;
915     }
916 
917     Q_EMIT bonusUpdated(this, m_badBonusType, 100);
918 }
919 
stopMoving()920 void Player::stopMoving()
921 {
922     setXSpeed(0);
923     setYSpeed(0);
924     m_askedXSpeed = 0;
925     m_askedYSpeed = 0;
926     Q_EMIT stopped();
927 }
928 
keyPressed(QKeyEvent * keyEvent)929 void Player::keyPressed(QKeyEvent* keyEvent)
930 {
931     if(m_death || m_falling)
932     {
933         return;
934     }
935 
936     QKeySequence key = QKeySequence(keyEvent->key());
937 
938     if(key == m_key.moveLeft || key == m_key.moveRight || key == m_key.moveUp || key == m_key.moveDown || key == m_key.dropBomb)
939     {
940         keyEvent->accept();
941         if(keyEvent->isAutoRepeat())
942         {
943             return;
944         }
945     }
946     else
947     {
948         return;
949     }
950 
951     if(key == m_key.moveLeft)
952     {
953         goLeft();
954         updateDirection();
955     }
956     else if(key == m_key.moveRight)
957     {
958         goRight();
959         updateDirection();
960     }
961     else if(key == m_key.moveUp)
962     {
963         goUp();
964         updateDirection();
965     }
966     else if(key == m_key.moveDown)
967     {
968         goDown();
969         updateDirection();
970     }
971     else if(key == m_key.dropBomb)
972     {
973         if(m_bombArmory > 0)
974         {
975             Q_EMIT bombDropped(this, m_x, m_y, true, 2);
976             m_omitBombCurrentCell = true;
977         }
978         else
979         {
980             Q_EMIT bombDropped(this, m_x, m_y, false, 2);
981         }
982     }
983 
984 }
985 
keyReleased(QKeyEvent * keyEvent)986 void Player::keyReleased(QKeyEvent* keyEvent)
987 {
988     if(m_death || m_falling)
989     {
990         return;
991     }
992 
993     QKeySequence key = QKeySequence(keyEvent->key());
994 
995     if(key == m_key.moveLeft || key == m_key.moveRight || key == m_key.moveUp || key == m_key.moveDown || key == m_key.dropBomb)
996     {
997         keyEvent->accept();
998         if(keyEvent->isAutoRepeat())
999         {
1000             return;
1001         }
1002     }
1003     else
1004     {
1005         return;
1006     }
1007 
1008     int nSpeed = 0;
1009     if(m_onIce)
1010     {
1011         nSpeed = onIceSpeedIncrease;
1012     }
1013 
1014     if(key == m_key.moveLeft && m_xSpeed < 0)
1015     {
1016         setXSpeed(-nSpeed);
1017     }
1018     else if(key == m_key.moveRight && m_xSpeed > 0)
1019     {
1020         setXSpeed(nSpeed);
1021     }
1022     else if(key == m_key.moveUp && m_ySpeed < 0)
1023     {
1024         setYSpeed(-nSpeed);
1025     }
1026     else if(key == m_key.moveDown && m_ySpeed > 0)
1027     {
1028         setYSpeed(nSpeed);
1029     }
1030     else if(key == m_key.dropBomb)
1031     {
1032         //Q_EMIT bomb(this);
1033     }
1034 
1035     if(m_xSpeed == 0 && m_ySpeed == 0 && m_askedXSpeed == 0 && m_askedYSpeed == 0) stopMoving();
1036 }
1037 
signZeroPositive(const qreal value)1038 int Player::signZeroPositive(const qreal value)
1039 {
1040     return (value >= 0 ? 1 : -1);
1041 }
1042 
sign(const qreal value)1043 int Player::sign(const qreal value)
1044 {
1045     if(value == 0)
1046     {
1047         return 0;
1048     }
1049     return (value > 0 ? 1 : -1);
1050 }
1051