File indexing completed on 2024-04-28 07:51:04

0001 /*
0002     SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de>
0003     SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "player.h"
0009 #include "bonus.h"
0010 #include "bomb.h"
0011 #include "arena.h"
0012 #include "config/playersettings.h"
0013 #include "settings.h"
0014 
0015 #include <QKeyEvent>
0016 #include <QTimer>
0017 
0018 #include <cmath>
0019 
0020 constexpr int onIceSpeedIncrease = 2;
0021 constexpr int badBonusTimerTimeout = 100;
0022 constexpr int badBonusCountdown = 10000;
0023 
0024 Player::Player(qreal p_x, qreal p_y, const QString& p_playerID, const PlayerSettings* p_playerSettings, Arena* p_arena)
0025  : Character(p_x, p_y, p_arena)
0026  , m_throwBomb(false)
0027  , m_kickBomb(false)
0028 {
0029     m_type = Granatier::Element::PLAYER;
0030     m_desktopFilePath = p_playerSettings->playerDesktopFilePath(p_playerID);
0031     m_graphicsFile = p_playerSettings->playerGraphicsFile(p_playerID);
0032     m_playerName = p_playerSettings->playerName(p_playerID);
0033 
0034     m_points = 0;
0035 
0036     m_direction = Granatier::Direction::EAST;
0037 
0038     m_badBonusCountdownTimer = new QTimer;
0039     m_badBonusCountdownTimer->setInterval(badBonusTimerTimeout);
0040     m_badBonusMillisecondsToElapse = 0;
0041     connect(m_badBonusCountdownTimer, &QTimer::timeout, this, &Player::slot_badBonusTimerTimeout);
0042 
0043     resurrect();
0044 
0045     m_key.moveLeft = p_playerSettings->keyLeft(p_playerID);
0046     m_key.moveRight = p_playerSettings->keyRight(p_playerID);
0047     m_key.moveUp = p_playerSettings->keyUp(p_playerID);
0048     m_key.moveDown = p_playerSettings->keyDown(p_playerID);
0049     m_key.dropBomb = p_playerSettings->keyPutBomb(p_playerID);
0050 }
0051 
0052 Player::~Player()
0053 {
0054     delete m_badBonusCountdownTimer;
0055 }
0056 
0057 void Player::setShortcuts(const Shortcuts &keys)
0058 {
0059     m_key = keys;
0060 }
0061 
0062 QString Player::getGraphicsFile() const
0063 {
0064     return m_graphicsFile;
0065 }
0066 
0067 QString Player::getDesktopFilePath() const
0068 {
0069   return m_desktopFilePath;
0070 }
0071 
0072 QString Player::getPlayerName() const
0073 {
0074     return m_playerName;
0075 }
0076 
0077 void Player::init()
0078 {
0079     updateDirection();
0080     stopMoving();
0081     int row = m_arena->getRowFromY(m_y);
0082     int column = m_arena->getColFromX(m_x);
0083     m_arena->setCellElement(row, column, this);
0084 }
0085 
0086 void Player::pause()
0087 {
0088     m_badBonusCountdownTimer->stop();
0089 }
0090 
0091 void Player::resume()
0092 {
0093     if(m_badBonusMillisecondsToElapse > 0)
0094     {
0095         m_badBonusCountdownTimer->start();
0096     }
0097 }
0098 
0099 void Player::goUp()
0100 {
0101     m_askedXSpeed = 0;
0102 
0103     qreal nSpeed = m_speed;
0104     if(m_onIce)
0105     {
0106         nSpeed = m_speed + onIceSpeedIncrease;
0107     }
0108     m_askedYSpeed = -nSpeed;
0109 
0110     m_direction = Granatier::Direction::NORTH;
0111 }
0112 
0113 void Player::goDown()
0114 {
0115     m_askedXSpeed = 0;
0116 
0117     qreal nSpeed = m_speed;
0118     if(m_onIce)
0119     {
0120         nSpeed = m_speed + onIceSpeedIncrease;
0121     }
0122     m_askedYSpeed = nSpeed;
0123 
0124     m_direction = Granatier::Direction::SOUTH;
0125 }
0126 
0127 void Player::goRight()
0128 {
0129     qreal nSpeed = m_speed;
0130     if(m_onIce)
0131     {
0132         nSpeed = m_speed + onIceSpeedIncrease;
0133     }
0134     m_askedXSpeed = nSpeed;
0135 
0136     m_askedYSpeed = 0;
0137 
0138     m_direction = Granatier::Direction::EAST;
0139 }
0140 
0141 void Player::goLeft()
0142 {
0143     qreal nSpeed = m_speed;
0144     if(m_onIce)
0145     {
0146         nSpeed = m_speed + onIceSpeedIncrease;
0147     }
0148     m_askedXSpeed = -nSpeed;
0149 
0150     m_askedYSpeed = 0;
0151 
0152     m_direction = Granatier::Direction::WEST;
0153 }
0154 
0155 void Player::updateDirection()
0156 {
0157     if(m_death)
0158     {
0159         return;
0160     }
0161 
0162     setXSpeed(m_askedXSpeed);
0163     setYSpeed(m_askedYSpeed);
0164     m_askedXSpeed = 0;
0165     m_askedYSpeed = 0;
0166     // Signal to the player item that the direction changed
0167     Q_EMIT directionChanged();
0168 }
0169 
0170 void Player::updateMove()
0171 {
0172     if(m_death)
0173     {
0174         return;
0175     }
0176 
0177     //check if there is a hurdle in the way
0178     if(m_askedXSpeed != 0 || m_xSpeed != 0 || m_askedYSpeed != 0 || m_ySpeed != 0)
0179     {
0180         int xDirection = 0;                 //x-direction: -1: move left; 0: not moving; 1: move right
0181         int yDirection = 0;                 //y-direction: -1: move up; 0: not moving; 1: move down
0182         int straightDirection = 0;          //straight direction: -1: backward; 1:foreward; while foreward is right for moving in x-direction and down for y-direction
0183         qreal deltaStraightMove = 0;        //the move in straight direction
0184         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
0185         qreal deltaAskedMove;               //how far to move; positive for right/down move and negative for left/up
0186         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
0187         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
0188         qreal deltaStraightCellCenter;      //distance to the cell center in moving direction; positive if left/up from cell center, negative if right/down
0189         qreal deltaPerpendicularCellCenter; //distance to the cell center perpendicular to moving direction; positive if up/left from cell center, negative if down/right
0190         bool bMoveWithinNextCellCenter = false;     //move is completed without exceeding the cell center
0191         int cellCol;
0192         int cellRow;
0193 
0194         // Get the current cell coordinates from the character coordinates
0195         int moveStartRow = m_arena->getRowFromY(m_y);
0196         int moveStartCol = m_arena->getColFromX(m_x);
0197         int curCellRow = moveStartRow;
0198         int curCellCol = moveStartCol;
0199 
0200         //set variables for right/left move
0201         if(m_askedXSpeed != 0 || m_xSpeed != 0)
0202         {
0203             //how far to move
0204             deltaAskedMove = (m_askedXSpeed != 0 ? m_askedXSpeed : m_xSpeed);
0205 
0206             //direction to move
0207             xDirection = sign(deltaAskedMove);
0208             straightDirection = xDirection;
0209 
0210             deltaStraightCellCorner = m_x - curCellCol * Granatier::CellSize;
0211             deltaPerpendicularCellCorner = m_y - curCellRow * Granatier::CellSize;
0212         }
0213         else        //set variables for down/up move
0214         {
0215             //how far to move
0216             deltaAskedMove = (m_askedYSpeed != 0 ? m_askedYSpeed : m_ySpeed);
0217 
0218             //direction to move
0219             yDirection = sign(deltaAskedMove);
0220             straightDirection = yDirection;
0221 
0222             deltaStraightCellCorner = m_y - curCellRow * Granatier::CellSize;
0223             deltaPerpendicularCellCorner = m_x - curCellCol * Granatier::CellSize;
0224         }
0225 
0226         //how far to current cell center
0227         deltaStraightCellCenter = Granatier::CellSize/2 - deltaStraightCellCorner;
0228         deltaPerpendicularCellCenter = Granatier::CellSize/2 - deltaPerpendicularCellCorner;
0229 
0230         //check if the move exceeds a cell center
0231         if(straightDirection*deltaStraightCellCenter >= 0)
0232         {
0233             if(fabs(deltaAskedMove) <= fabs(deltaStraightCellCenter))
0234             {
0235                 bMoveWithinNextCellCenter = true;
0236             }
0237         }
0238         else if(fabs(deltaAskedMove) + fabs(deltaStraightCellCenter) <= Granatier::CellSize)
0239         {
0240             bMoveWithinNextCellCenter = true;
0241         }
0242 
0243         //the move is within two cell centers
0244         if(bMoveWithinNextCellCenter)
0245         {
0246             bool isHurdle = false;
0247             if(!(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::SCATTY))
0248             {
0249                 bool kickBomb = false;
0250                 QList<Element*> bombElements;
0251                 //moving towards cell center; don't move if there is a bomb in the cell
0252                 if(deltaStraightCellCenter * straightDirection > 0 && !m_arena->getCell(moveStartRow, moveStartCol).isWalkable(this) && !m_omitBombCurrentCell)
0253                 {
0254                     isHurdle = true;
0255                     if(m_kickBomb)
0256                     {
0257                         kickBomb = true;
0258                         bombElements =  m_arena->getCell(moveStartRow, moveStartCol).getElements(Granatier::Element::BOMB);
0259                     }
0260                 }
0261                 //moving away of cell center; don't move if there is a bomb in the next cell; ignore a bomb in the current cell
0262                 else if(deltaStraightCellCenter * straightDirection < 0 && !m_arena->getCell(moveStartRow + yDirection, moveStartCol + xDirection).isWalkable(this))
0263                 {
0264                     isHurdle = true;
0265                     if(m_kickBomb)
0266                     {
0267                         kickBomb = true;
0268                         bombElements =  m_arena->getCell(moveStartRow + yDirection, moveStartCol + xDirection).getElements(Granatier::Element::BOMB);
0269                     }
0270                 }
0271                 if(kickBomb)
0272                 {
0273                     for(auto& element: bombElements)
0274                     {
0275                         dynamic_cast <Bomb*> (element)->setKicked(m_direction);
0276                     }
0277                 }
0278             }
0279             if(!isHurdle)
0280             {
0281                 deltaStraightMove += deltaAskedMove;
0282                 //move to perpendicular center if needed
0283                 if(deltaPerpendicularCellCenter != 0 && (straightDirection * deltaStraightCellCenter) < 0)  //not in perpendicular center and entering a new cell
0284                 {
0285                     if(fabs(deltaPerpendicularCellCenter) > Granatier::CellSize/2 - fabs(deltaStraightMove - deltaStraightCellCenter))   //check if it already can collide with a hurdle
0286                     {
0287                         cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0288                         cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0289                         if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
0290                         {
0291                             deltaPerpendicularMove = deltaPerpendicularCellCenter + signZeroPositive(deltaPerpendicularCellCenter) * (fabs(deltaStraightMove - deltaStraightCellCenter) - Granatier::CellSize/2);
0292                             if(fabs(deltaPerpendicularMove) > fabs(deltaPerpendicularCellCenter))       //check if moved over perpendicular center
0293                             {
0294                                 deltaPerpendicularMove = deltaPerpendicularCellCenter;
0295                             }
0296                         }
0297                     }
0298                 }
0299             }
0300         }
0301         else    //the move exceeds a cell center
0302         {
0303             //at first move to the cell center
0304             deltaStraightMove += deltaStraightCellCenter;
0305             deltaAskedMove -= deltaStraightCellCenter;
0306             if(straightDirection * deltaStraightCellCenter < 0)     //the cell center to move is in the next cell
0307             {
0308                 deltaStraightMove += straightDirection * Granatier::CellSize;
0309                 deltaAskedMove -= straightDirection * Granatier::CellSize;
0310 
0311                 //move to perpendicular center if needed
0312                 if(deltaPerpendicularCellCenter != 0)
0313                 {
0314                     cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0315                     cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0316                     if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
0317                     {
0318                         deltaPerpendicularMove = deltaPerpendicularCellCenter;
0319                     }
0320                 }
0321 
0322                 //update the current cell and row
0323                 curCellCol += xDirection;
0324                 curCellRow += yDirection;
0325             }
0326             while(fabs(deltaAskedMove) > 0)     //complete the move
0327             {
0328                 if(m_arena->getCell(curCellRow + yDirection, curCellCol + xDirection).isWalkable(this))     //check if next cell is walkable
0329                 {
0330                     if(fabs(deltaAskedMove) > Granatier::CellSize)   //move to next cell center if the remaining move exceeds a cell center
0331                     {
0332                         deltaStraightMove += straightDirection * Granatier::CellSize;
0333                         deltaAskedMove -= straightDirection * Granatier::CellSize;
0334                         //move to perpendicular center if needed
0335                         if(deltaPerpendicularCellCenter != 0)
0336                         {
0337                             cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0338                             cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0339                             if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
0340                             {
0341                                 deltaPerpendicularMove = deltaPerpendicularCellCenter;
0342                             }
0343                         }
0344                     }
0345                     else
0346                     {
0347                         deltaStraightMove += deltaAskedMove;
0348                         //move to perpendicular center if needed
0349                         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
0350                         {
0351                             cellRow = curCellRow + yDirection - qAbs(xDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0352                             cellCol = curCellCol + xDirection - qAbs(yDirection)*signZeroPositive(deltaPerpendicularCellCenter);
0353                             if(!m_arena->getCell(cellRow, cellCol).isWalkable(this))
0354                             {
0355                                 deltaPerpendicularMove = signZeroPositive(deltaPerpendicularCellCenter) * fabs(deltaAskedMove);
0356                                 if(fabs(deltaPerpendicularMove) > fabs(deltaPerpendicularCellCenter))
0357                                 {
0358                                     deltaPerpendicularMove = deltaPerpendicularCellCenter;      //check if moved over perpendicular center
0359                                 }
0360                             }
0361                         }
0362                         deltaAskedMove = 0;
0363                     }
0364                     //update the current cell and row
0365                     curCellCol += xDirection;
0366                     curCellRow += yDirection;
0367                 }
0368                 else    //there is a hurdle in the next cell, so stop moving
0369                 {
0370                     deltaAskedMove = 0;
0371                     cellRow = curCellRow + yDirection;
0372                     cellCol = curCellCol + xDirection;
0373                     //check if bomb
0374                     if(m_kickBomb)
0375                     {
0376                         QList<Element*> bombElements =  m_arena->getCell(cellRow, cellCol).getElements(Granatier::Element::BOMB);
0377                         for(auto& element: bombElements)
0378                         {
0379                             dynamic_cast <Bomb*> (element)->setKicked(m_direction);
0380                         }
0381                     }
0382                 }
0383             }
0384         }
0385 
0386         // Update the direction
0387         if(m_askedXSpeed != 0 || m_askedYSpeed != 0)
0388         {
0389             updateDirection();
0390         }
0391 
0392         // Move the player
0393         if(xDirection != 0)
0394         {
0395             move(m_x + deltaStraightMove, m_y + deltaPerpendicularMove);
0396         }
0397         else
0398         {
0399             move(m_x + deltaPerpendicularMove, m_y + deltaStraightMove);
0400         }
0401 
0402         //check if the player is on ice
0403         // Get the current cell coordinates from the character coordinates
0404         int newCellRow = m_arena->getRowFromY(m_y);
0405         int newCellCol = m_arena->getColFromX(m_x);
0406         if(!m_onIce)
0407         {
0408             if(m_arena->getCell(newCellRow, newCellCol).getType() == Granatier::Cell::ICE)
0409             {
0410                 if(xDirection != 0)
0411                 {
0412                     setXSpeed(m_xSpeed + xDirection * onIceSpeedIncrease);
0413                 }
0414                 else
0415                 {
0416                     setYSpeed(m_ySpeed + yDirection * onIceSpeedIncrease);
0417                 }
0418                 m_onIce = true;
0419             }
0420         }
0421         else
0422         {
0423             if(m_arena->getCell(newCellRow, newCellCol).getType() != Granatier::Cell::ICE)
0424             {
0425                 if(m_arena->getCell(newCellRow, newCellCol).getType() != Granatier::Cell::HOLE)
0426                 {
0427                     if(xDirection != 0)
0428                     {
0429                         setXSpeed(m_xSpeed - xDirection * onIceSpeedIncrease);
0430                     }
0431                     else
0432                     {
0433                         setYSpeed(m_ySpeed - yDirection * onIceSpeedIncrease);
0434                     }
0435                 }
0436                 m_onIce = false;
0437 
0438                 if(m_xSpeed == 0 && m_ySpeed == 0 && m_askedXSpeed == 0 && m_askedYSpeed == 0)
0439                 {
0440                     stopMoving();
0441                 }
0442             }
0443         }
0444 
0445         //check if the player move in a hole
0446         if(m_arena->getCell(newCellRow, newCellCol).getType() == Granatier::Cell::HOLE)
0447         {
0448             m_falling = true;
0449             //check if cell center passed
0450             if(xDirection != 0)
0451             {
0452                 qreal cellCenter = newCellCol * Granatier::CellSize + 0.5 * Granatier::CellSize;
0453                 qreal deltaCellCenter = cellCenter - (m_x + m_xSpeed);
0454                 if (cellCenter - m_x == 0)
0455                 {
0456                     setXSpeed(0);
0457                     Q_EMIT falling();
0458                 }
0459                 else if (xDirection * deltaCellCenter < 0)
0460                 {
0461                     setXSpeed(cellCenter - m_x);
0462                 }
0463             }
0464             else if (yDirection != 0)
0465             {
0466                 qreal cellCenter = newCellRow * Granatier::CellSize + 0.5 * Granatier::CellSize;
0467                 qreal deltaCellCenter = cellCenter - (m_y + m_ySpeed);
0468                 if (cellCenter - m_y == 0)
0469                 {
0470                     setYSpeed(0);
0471                     Q_EMIT falling();
0472                 }
0473                 else if (yDirection * deltaCellCenter < 0)
0474                 {
0475                     setYSpeed(cellCenter - m_y);
0476                 }
0477             }
0478         }
0479 
0480         if(moveStartCol != newCellCol || moveStartRow != newCellRow)
0481         {
0482             m_arena->removeCellElement(moveStartRow, moveStartCol, this);
0483             m_arena->setCellElement(newCellRow, newCellCol, this);
0484             m_omitBombCurrentCell = false;
0485         }
0486     }
0487 
0488     //check if bad bonus scatty and drop bombs
0489     if(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::SCATTY  && m_bombArmory > 0)
0490     {
0491         //TODO: improve
0492         Q_EMIT bombDropped(this, m_x, m_y, true, 0);
0493     }
0494 }
0495 
0496 void Player::move(qreal x, qreal y)
0497 {
0498     // Move the Character
0499     m_x = x;
0500     m_y = y;
0501     Q_EMIT moved(m_x, m_y);
0502 }
0503 
0504 void Player::addBonus(Bonus* p_bonus)
0505 {
0506     Granatier::Bonus::Type bonusType = p_bonus->getBonusType();
0507 
0508     if(m_badBonusCountdownTimer->isActive())
0509     {
0510         m_badBonusCountdownTimer->stop();
0511         slot_removeBadBonus();
0512     }
0513 
0514     switch (bonusType)
0515     {
0516         case Granatier::Bonus::SPEED:
0517             m_speed += 1;
0518             if(m_speed > m_maxSpeed)
0519             {
0520                 m_speed = m_maxSpeed;
0521             }
0522             setXSpeed(sign(m_xSpeed) * m_speed);
0523             setYSpeed(sign(m_ySpeed) * m_speed);
0524             break;
0525         case Granatier::Bonus::POWER:
0526             m_bombPower++;
0527             if(m_bombPower > 10)
0528             {
0529                 m_bombPower = 10;
0530             }
0531             break;
0532         case Granatier::Bonus::BOMB:
0533             m_maxBombArmory++;
0534             if(m_maxBombArmory > 10)
0535             {
0536                 m_maxBombArmory = 10;
0537             }
0538             m_bombArmory++;
0539             if(m_bombArmory > m_maxBombArmory)
0540             {
0541                 m_bombArmory = m_maxBombArmory;
0542             }
0543             break;
0544         case Granatier::Bonus::SHIELD:
0545             if(m_listShield.isEmpty() || m_listShield.last() != 0)
0546             {
0547                 m_listShield.append(0);
0548             }
0549             break;
0550         case Granatier::Bonus::THROW:
0551             m_throwBomb = true;
0552             break;
0553         case Granatier::Bonus::KICK:
0554             m_kickBomb = true;
0555             break;
0556         case Granatier::Bonus::HYPERACTIVE:
0557             {
0558                 qreal askedXSpeedTemp = m_askedXSpeed;
0559                 qreal askedYSpeedTemp = m_askedYSpeed;
0560                 m_normalSpeed = m_speed;
0561                 m_speed = m_maxSpeed * 3;
0562                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
0563                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
0564                 updateDirection();
0565                 m_askedXSpeed = askedXSpeedTemp;
0566                 m_askedYSpeed = askedYSpeedTemp;
0567 
0568                 m_badBonusType = Granatier::Bonus::HYPERACTIVE;
0569                 m_badBonusMillisecondsToElapse = badBonusCountdown;
0570                 m_badBonusCountdownTimer->start();
0571             }
0572             break;
0573         case Granatier::Bonus::SLOW:
0574             {
0575                 qreal askedXSpeedTemp = m_askedXSpeed;
0576                 qreal askedYSpeedTemp = m_askedYSpeed;
0577                 m_normalSpeed = m_speed;
0578                 m_speed = 1;
0579                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
0580                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
0581                 updateDirection();
0582                 m_askedXSpeed = askedXSpeedTemp;
0583                 m_askedYSpeed = askedYSpeedTemp;
0584 
0585                 m_badBonusType = Granatier::Bonus::SLOW;
0586                 m_badBonusMillisecondsToElapse = badBonusCountdown;
0587                 m_badBonusCountdownTimer->start();
0588             }
0589             break;
0590         case Granatier::Bonus::MIRROR:
0591             {
0592                 qreal askedXSpeedTemp = m_askedXSpeed;
0593                 qreal askedYSpeedTemp = m_askedYSpeed;
0594                 m_askedXSpeed = -m_xSpeed;
0595                 m_askedYSpeed = -m_ySpeed;
0596                 switch(m_direction)
0597                 {
0598                     case Granatier::Direction::EAST:
0599                         m_direction = Granatier::Direction::WEST;
0600                         break;
0601                     case Granatier::Direction::WEST:
0602                         m_direction = Granatier::Direction::EAST;
0603                         break;
0604                     case Granatier::Direction::NORTH:
0605                         m_direction = Granatier::Direction::SOUTH;
0606                         break;
0607                     case Granatier::Direction::SOUTH:
0608                         m_direction = Granatier::Direction::NORTH;
0609                         break;
0610                 }
0611                 updateDirection();
0612                 m_askedXSpeed = -askedXSpeedTemp;
0613                 m_askedYSpeed = -askedYSpeedTemp;
0614 
0615                 QKeySequence tempKey = m_key.moveLeft;
0616                 m_key.moveLeft = m_key.moveRight;
0617                 m_key.moveRight = tempKey;
0618                 tempKey = m_key.moveUp;
0619                 m_key.moveUp = m_key.moveDown;
0620                 m_key.moveDown = tempKey;
0621 
0622                 m_moveMirrored = true;
0623                 m_badBonusType = Granatier::Bonus::MIRROR;
0624                 m_badBonusMillisecondsToElapse = badBonusCountdown;
0625                 m_badBonusCountdownTimer->start();
0626             }
0627             break;
0628         case Granatier::Bonus::SCATTY:
0629             m_badBonusType = Granatier::Bonus::SCATTY;
0630             m_badBonusMillisecondsToElapse = badBonusCountdown;
0631             m_badBonusCountdownTimer->start();
0632             break;
0633         case Granatier::Bonus::RESTRAIN:
0634             m_normalBombArmory = m_bombArmory;
0635             m_bombArmory = 0;
0636             m_badBonusType = Granatier::Bonus::RESTRAIN;
0637             m_badBonusMillisecondsToElapse = badBonusCountdown;
0638             m_badBonusCountdownTimer->start();
0639             break;
0640         case Granatier::Bonus::RESURRECT:
0641             Q_EMIT resurrectBonusTaken();
0642             break;
0643         default:
0644             break;
0645     }
0646 
0647     Q_EMIT bonusUpdated(this, bonusType, 0);
0648 }
0649 
0650 bool Player::shield(int nExplosionID)
0651 {
0652     for(int i = 0; i < m_listShield.count(); i++)
0653     {
0654         if(m_listShield[i] == nExplosionID)
0655         {
0656             return true;
0657         }
0658         else if(m_listShield[i] == 0)
0659         {
0660             m_listShield[i] = nExplosionID;
0661             if(i == m_listShield.count()-1)
0662             {
0663                 Q_EMIT bonusUpdated(this, Granatier::Bonus::SHIELD, 100);
0664             }
0665             return true;
0666         }
0667     }
0668     return false;
0669 }
0670 
0671 bool Player::hasShield()
0672 {
0673     if(m_listShield.count() > 0 && m_listShield.last() == 0)
0674     {
0675         return true;
0676     }
0677     return false;
0678 }
0679 
0680 bool Player::hasThrowBomb()
0681 {
0682     return m_throwBomb;
0683 }
0684 
0685 bool Player::hasKickBomb()
0686 {
0687     return m_kickBomb;
0688 }
0689 
0690 bool Player::hasBadBonus()
0691 {
0692     if(m_badBonusCountdownTimer->isActive())
0693     {
0694         return true;
0695     }
0696     return false;
0697 }
0698 
0699 void Player::die()
0700 {
0701     if(!m_death)
0702     {
0703         m_death = true;
0704         Q_EMIT dying();
0705         m_xSpeed = 0;
0706         m_xSpeed = 0;
0707 
0708         if(m_badBonusCountdownTimer->isActive())
0709         {
0710             m_badBonusCountdownTimer->stop();
0711             slot_removeBadBonus();
0712         }
0713         int row = m_arena->getRowFromY(m_y);
0714         int column = m_arena->getColFromX(m_x);
0715         m_arena->removeCellElement(row, column, this);
0716     }
0717 }
0718 
0719 bool Player::isAlive() const
0720 {
0721     return !m_death;
0722 }
0723 
0724 void Player::resurrect()
0725 {
0726     if(m_badBonusMillisecondsToElapse > 0)
0727     {
0728         slot_removeBadBonus();
0729     }
0730 
0731     m_onIce = false;
0732     m_falling = false;
0733     m_death = false;
0734     m_maxSpeed = 10;
0735     m_speed = Settings::self()->initialSpeed();
0736     m_normalSpeed = m_speed;
0737     m_moveMirrored = false;
0738     m_bombPower = Settings::self()->initialBombPower();
0739     m_maxBombArmory = Settings::self()->initialBombArmory();
0740     m_bombArmory = m_maxBombArmory;
0741     if(m_listShield.count() != 0)
0742     {
0743         m_listShield.clear();
0744         Q_EMIT bonusUpdated(this, Granatier::Bonus::SHIELD, 100);
0745     }
0746     if(m_throwBomb)
0747     {
0748         m_throwBomb = false;
0749         Q_EMIT bonusUpdated(this, Granatier::Bonus::THROW, 100);
0750     }
0751     if(m_kickBomb)
0752     {
0753         m_kickBomb = false;
0754         Q_EMIT bonusUpdated(this, Granatier::Bonus::KICK, 100);
0755     }
0756     m_omitBombCurrentCell = false;
0757     if(m_badBonusCountdownTimer->isActive())
0758     {
0759         m_badBonusCountdownTimer->stop();
0760         slot_removeBadBonus();
0761     }
0762 
0763     //check if the player is above a hole
0764     if(m_arena)
0765     {
0766         int cellRow = m_arena->getRowFromY(m_y);
0767         int cellCol = m_arena->getColFromX(m_x);
0768 
0769         m_arena->removeCellElement(cellRow, cellCol, this); //just to be really sure
0770 
0771         if(m_arena->getCell(cellRow, cellCol).getType() == Granatier::Cell::HOLE)
0772         {
0773             move(m_xInit, m_yInit);
0774             cellRow = m_arena->getRowFromY(m_yInit);
0775             cellCol = m_arena->getColFromX(m_xInit);
0776         }
0777 
0778         m_arena->setCellElement(cellRow, cellCol, this);
0779     }
0780 
0781     Q_EMIT resurrected();
0782 }
0783 
0784 int Player::points() const
0785 {
0786     return m_points;
0787 }
0788 
0789 void Player::addPoint()
0790 {
0791     m_points++;
0792 }
0793 
0794 void Player::emitGameUpdated()
0795 {
0796     Q_EMIT gameUpdated();
0797 }
0798 
0799 qreal Player::getAskedXSpeed() const
0800 {
0801     return m_askedXSpeed;
0802 }
0803 
0804 qreal Player::getAskedYSpeed() const
0805 {
0806     return m_askedYSpeed;
0807 }
0808 
0809 int Player::direction()
0810 {
0811     return m_direction;
0812 }
0813 
0814 int Player::getBombPower() const
0815 {
0816     return m_bombPower;
0817 }
0818 
0819 void Player::decrementBombArmory()
0820 {
0821     m_bombArmory--;
0822     if(m_bombArmory < 0)
0823     {
0824         m_bombArmory = 0;
0825     }
0826 }
0827 
0828 void Player::slot_refillBombArmory()
0829 {
0830     int* bombArmory = &m_bombArmory;
0831     if(m_badBonusCountdownTimer->isActive() && m_badBonusType == Granatier::Bonus::RESTRAIN)
0832     {
0833         bombArmory = &m_normalBombArmory;
0834     }
0835     (*bombArmory)++;
0836     if((*bombArmory) > m_maxBombArmory)
0837     {
0838         (*bombArmory) = m_maxBombArmory;
0839     }
0840 }
0841 
0842 void Player::slot_badBonusTimerTimeout()
0843 {
0844     m_badBonusMillisecondsToElapse -= badBonusTimerTimeout;
0845     if(m_badBonusMillisecondsToElapse <= 0)
0846     {
0847         slot_removeBadBonus();
0848     }
0849     else
0850     {
0851         bonusUpdated(this, m_badBonusType, (badBonusCountdown - m_badBonusMillisecondsToElapse) * 100 / badBonusCountdown);
0852     }
0853 }
0854 
0855 void Player::slot_removeBadBonus()
0856 {
0857     m_badBonusCountdownTimer->stop();
0858     m_badBonusMillisecondsToElapse = 0;
0859 
0860     switch (m_badBonusType)
0861     {
0862         case Granatier::Bonus::HYPERACTIVE:
0863         case Granatier::Bonus::SLOW:
0864             {
0865                 qreal askedXSpeedTemp = m_askedXSpeed;
0866                 qreal askedYSpeedTemp = m_askedYSpeed;
0867                 m_speed = m_normalSpeed;
0868                 m_askedXSpeed = sign(m_xSpeed) * m_speed;
0869                 m_askedYSpeed = sign(m_ySpeed) * m_speed;
0870                 updateDirection();
0871                 m_askedXSpeed = askedXSpeedTemp;
0872                 m_askedYSpeed = askedYSpeedTemp;
0873             }
0874             break;
0875         case Granatier::Bonus::MIRROR:
0876             {
0877                 qreal askedXSpeedTemp = m_askedXSpeed;
0878                 qreal askedYSpeedTemp = m_askedYSpeed;
0879                 m_askedXSpeed = -m_xSpeed;
0880                 m_askedYSpeed = -m_ySpeed;
0881                 switch(m_direction)
0882                 {
0883                     case Granatier::Direction::EAST:
0884                         m_direction = Granatier::Direction::WEST;
0885                         break;
0886                     case Granatier::Direction::WEST:
0887                         m_direction = Granatier::Direction::EAST;
0888                         break;
0889                     case Granatier::Direction::NORTH:
0890                         m_direction = Granatier::Direction::SOUTH;
0891                         break;
0892                     case Granatier::Direction::SOUTH:
0893                         m_direction = Granatier::Direction::NORTH;
0894                         break;
0895                 }
0896                 updateDirection();
0897                 m_askedXSpeed = -askedXSpeedTemp;
0898                 m_askedYSpeed = -askedYSpeedTemp;
0899 
0900                 QKeySequence tempKey = m_key.moveLeft;
0901                 m_key.moveLeft = m_key.moveRight;
0902                 m_key.moveRight = tempKey;
0903                 tempKey = m_key.moveUp;
0904                 m_key.moveUp = m_key.moveDown;
0905                 m_key.moveDown = tempKey;
0906 
0907                 m_moveMirrored = false;
0908             }
0909             break;
0910         case Granatier::Bonus::RESTRAIN:
0911             m_bombArmory = m_normalBombArmory;
0912             break;
0913         default:
0914             break;
0915     }
0916 
0917     Q_EMIT bonusUpdated(this, m_badBonusType, 100);
0918 }
0919 
0920 void Player::stopMoving()
0921 {
0922     setXSpeed(0);
0923     setYSpeed(0);
0924     m_askedXSpeed = 0;
0925     m_askedYSpeed = 0;
0926     Q_EMIT stopped();
0927 }
0928 
0929 void Player::keyPressed(QKeyEvent* keyEvent)
0930 {
0931     if(m_death || m_falling)
0932     {
0933         return;
0934     }
0935 
0936     QKeySequence key = QKeySequence(keyEvent->key());
0937 
0938     if(key == m_key.moveLeft || key == m_key.moveRight || key == m_key.moveUp || key == m_key.moveDown || key == m_key.dropBomb)
0939     {
0940         keyEvent->accept();
0941         if(keyEvent->isAutoRepeat())
0942         {
0943             return;
0944         }
0945     }
0946     else
0947     {
0948         return;
0949     }
0950 
0951     if(key == m_key.moveLeft)
0952     {
0953         goLeft();
0954         updateDirection();
0955     }
0956     else if(key == m_key.moveRight)
0957     {
0958         goRight();
0959         updateDirection();
0960     }
0961     else if(key == m_key.moveUp)
0962     {
0963         goUp();
0964         updateDirection();
0965     }
0966     else if(key == m_key.moveDown)
0967     {
0968         goDown();
0969         updateDirection();
0970     }
0971     else if(key == m_key.dropBomb)
0972     {
0973         if(m_bombArmory > 0)
0974         {
0975             Q_EMIT bombDropped(this, m_x, m_y, true, 2);
0976             m_omitBombCurrentCell = true;
0977         }
0978         else
0979         {
0980             Q_EMIT bombDropped(this, m_x, m_y, false, 2);
0981         }
0982     }
0983 
0984 }
0985 
0986 void Player::keyReleased(QKeyEvent* keyEvent)
0987 {
0988     if(m_death || m_falling)
0989     {
0990         return;
0991     }
0992 
0993     QKeySequence key = QKeySequence(keyEvent->key());
0994 
0995     if(key == m_key.moveLeft || key == m_key.moveRight || key == m_key.moveUp || key == m_key.moveDown || key == m_key.dropBomb)
0996     {
0997         keyEvent->accept();
0998         if(keyEvent->isAutoRepeat())
0999         {
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 
1038 int Player::signZeroPositive(const qreal value)
1039 {
1040     return (value >= 0 ? 1 : -1);
1041 }
1042 
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 
1052 #include "moc_player.cpp"