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

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
0003     SPDX-FileCopyrightText: 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "ghost.h"
0009 
0010 #include <KGameDifficulty>
0011 #include <QPointF>
0012 #include <QRandomGenerator>
0013 #include <cstdlib>
0014 #include <ctime>
0015 
0016 const qreal Ghost::MAX_SPEED_RATIO = 2.0;
0017 const int Ghost::POINTS = 200;
0018 
0019 Ghost::Ghost(qreal p_x, qreal p_y, const QString &p_imageId, Maze *p_maze)
0020     : Character(p_x, p_y, p_maze)
0021     , m_preyStateAlmostOver(false)
0022 {
0023     // Initialize the ghost attributes
0024     m_imageId = p_imageId;
0025     m_points = Ghost::POINTS;
0026     m_type = Element::GHOST;
0027     m_state = Ghost::HUNTER;
0028     m_maxSpeed = m_normalSpeed * MAX_SPEED_RATIO;
0029     // Makes the ghost move as soon as the game is created
0030     goLeft();
0031 }
0032 
0033 Ghost::~Ghost() = default;
0034 
0035 void Ghost::goUp()
0036 {
0037     m_xSpeed = 0;
0038     m_ySpeed = -m_speed;
0039 }
0040 
0041 void Ghost::goDown()
0042 {
0043     m_xSpeed = 0;
0044     m_ySpeed = m_speed;
0045 }
0046 
0047 void Ghost::goRight()
0048 {
0049     m_xSpeed = m_speed;
0050     m_ySpeed = 0;
0051 }
0052 
0053 void Ghost::goLeft()
0054 {
0055     m_xSpeed = -m_speed;
0056     m_ySpeed = 0;
0057 }
0058 
0059 void Ghost::updateMove()
0060 {
0061     // Get the current cell coordinates from the character coordinates
0062     int curCellRow = m_maze->getRowFromY(m_y);
0063     int curCellCol = m_maze->getColFromX(m_x);
0064     // Contains the different directions a ghost can choose when on a cell center
0065     QList<QPointF> directionsList;
0066     int nb = 0;
0067 
0068     // If the ghost is not "eaten"
0069     if (m_state != Ghost::EATEN) {
0070         // If the ghost gets on a Cell center
0071         if (onCenter()) {
0072             // We retrieve all the directions the ghost can choose (save the turning back)
0073             if (m_maze->getCell(curCellRow, curCellCol + 1).getType() == Cell::CORRIDOR
0074                 || (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP
0075                     && m_maze->getCell(curCellRow, curCellCol + 1).getType() == Cell::GHOSTCAMP)) {
0076                 if (m_xSpeed >= 0) {
0077                     directionsList.append(QPointF(m_speed, 0.0));
0078                 }
0079             }
0080             if (m_maze->getCell(curCellRow + 1, curCellCol).getType() == Cell::CORRIDOR
0081                 || (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP
0082                     && m_maze->getCell(curCellRow + 1, curCellCol).getType() == Cell::GHOSTCAMP)) {
0083                 if (m_ySpeed >= 0) {
0084                     directionsList.append(QPointF(0.0, m_speed));
0085                 }
0086             }
0087             if (m_maze->getCell(curCellRow - 1, curCellCol).getType() == Cell::CORRIDOR
0088                 || (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP
0089                     && m_maze->getCell(curCellRow - 1, curCellCol).getType() == Cell::GHOSTCAMP)) {
0090                 if (m_ySpeed <= 0) {
0091                     directionsList.append(QPointF(0.0, -m_speed));
0092                 }
0093             }
0094             if (m_maze->getCell(curCellRow, curCellCol - 1).getType() == Cell::CORRIDOR
0095                 || (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP
0096                     && m_maze->getCell(curCellRow, curCellCol - 1).getType() == Cell::GHOSTCAMP)) {
0097                 if (m_xSpeed <= 0) {
0098                     directionsList.append(QPointF(-m_speed, 0.0));
0099                 }
0100             }
0101             // Random number generation to choose one of the directions
0102             nb = int(double(QRandomGenerator::global()->bounded(RAND_MAX)) / (double(RAND_MAX) + 1) * directionsList.size());
0103             // If there is no directions in the list, the character goes backward
0104             if (directionsList.size() == 0) {
0105                 m_xSpeed = -m_xSpeed;
0106                 m_ySpeed = -m_ySpeed;
0107             } else if ((m_xSpeed != 0 && m_xSpeed != directionsList[nb].x())
0108                        || (m_ySpeed != 0 && m_ySpeed != directionsList[nb].y())) { // If the chosen direction isn't forward
0109                 // We move the ghost in the center of the cell and update the directions
0110                 moveOnCenter();
0111                 m_xSpeed = directionsList[nb].x();
0112                 m_ySpeed = directionsList[nb].y();
0113             }
0114         }
0115         // We move the ghost
0116         move();
0117     } else { // If the ghost has been eaten
0118         if (onCenter()) {
0119             // If the ghost has not reached the camp yet
0120             if (m_pathToCamp.size() != 0) {
0121                 // Go to the next cell to the camp
0122                 updateMove(m_pathToCamp.first().y(), m_pathToCamp.first().x());
0123                 // Remove the cell the ghost has reached from the path
0124                 m_pathToCamp.removeFirst();
0125             } else {
0126                 // If the ghost is not at home (that means it has just been eaten)
0127                 if (curCellRow != m_maze->getResurrectionCell().y() || curCellCol != m_maze->getResurrectionCell().x()) {
0128                     // Compute the path to go back to the camp
0129                     m_pathToCamp = m_maze->getPathToGhostCamp(curCellRow, curCellCol);
0130                     if (!m_pathToCamp.isEmpty()) {
0131                         updateMove(m_pathToCamp.first().y(), m_pathToCamp.first().x());
0132                     } else {
0133                         // Set the ghost at home
0134                         m_x = m_maze->getResurrectionCell().x() * Cell::SIZE + Cell::SIZE / 2;
0135                         m_y = m_maze->getResurrectionCell().y() * Cell::SIZE + Cell::SIZE / 2;
0136                         setState(Ghost::HUNTER);
0137                     }
0138                 } else { // The ghost has reached the ghost camp
0139                     setState(Ghost::HUNTER);
0140                 }
0141             }
0142         }
0143         move();
0144     }
0145 }
0146 
0147 void Ghost::updateMove(int p_row, int p_col)
0148 {
0149     // Get the current cell coordinates from the ghost coordinates
0150     int curGhostRow = m_maze->getRowFromY(m_y);
0151     int curGhostCol = m_maze->getColFromX(m_x);
0152 
0153     if (onCenter()) {
0154         if (curGhostRow == p_row) {
0155             if (p_col > curGhostCol) {
0156                 m_xSpeed = m_speed;
0157                 m_ySpeed = 0;
0158             } else {
0159                 m_xSpeed = -m_speed;
0160                 m_ySpeed = 0;
0161             }
0162         } else {
0163             if (p_row > curGhostRow) {
0164                 m_xSpeed = 0;
0165                 m_ySpeed = m_speed;
0166             } else {
0167                 m_xSpeed = 0;
0168                 m_ySpeed = -m_speed;
0169             }
0170         }
0171     }
0172     // We move the ghost
0173     move();
0174 }
0175 
0176 QString Ghost::getImageId() const
0177 {
0178     return m_imageId;
0179 }
0180 
0181 Ghost::State Ghost::getState() const
0182 {
0183     return m_state;
0184 }
0185 
0186 void Ghost::setState(Ghost::State p_state)
0187 {
0188     // Change the state
0189     m_state = p_state;
0190     switch (m_state) {
0191     case Ghost::PREY:
0192         m_speed = m_normalSpeed / 2;
0193         break;
0194     case HUNTER:
0195     case EATEN:
0196         m_speed = m_normalSpeed;
0197         break;
0198     }
0199     Q_EMIT stateChanged();
0200 }
0201 
0202 void Ghost::doActionOnCollision(Kapman *)
0203 {
0204     switch (m_state) {
0205     case Ghost::HUNTER:
0206         Q_EMIT lifeLost();
0207         break;
0208     case Ghost::PREY:
0209         Q_EMIT ghostEaten(this);
0210         break;
0211     case Ghost::EATEN:
0212         // Do nothing
0213         break;
0214     }
0215 }
0216 
0217 void Ghost::initSpeedInc()
0218 {
0219     // Ghosts speed increase when level up
0220     switch ((int)KGameDifficulty::globalLevel()) {
0221     case KGameDifficultyLevel::Easy:
0222         m_speedIncrease = Character::LOW_SPEED_INC;
0223         break;
0224     case KGameDifficultyLevel::Medium:
0225         m_speedIncrease = Character::MEDIUM_SPEED_INC;
0226         break;
0227     case KGameDifficultyLevel::Hard:
0228         m_speedIncrease = Character::HIGH_SPEED_INC;
0229         break;
0230     }
0231 }
0232 
0233 void Ghost::setPreyStateAlmostOverEnabled(bool enable)
0234 {
0235     m_preyStateAlmostOver = enable;
0236 }
0237 
0238 bool Ghost::preyStateAlmostOver() const
0239 {
0240     return m_preyStateAlmostOver;
0241 }
0242 
0243 #include "moc_ghost.cpp"