File indexing completed on 2023-10-01 08:02:05
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 <KgDifficulty> 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 on 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)Kg::difficultyLevel()) { 0221 case KgDifficultyLevel::Easy: 0222 m_speedIncrease = Character::LOW_SPEED_INC; 0223 break; 0224 case KgDifficultyLevel::Medium: 0225 m_speedIncrease = Character::MEDIUM_SPEED_INC; 0226 break; 0227 case KgDifficultyLevel::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"