1/*
2 * Copyright 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
3 * Copyright 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "ghost.h"
20#include "time.h"
21
22#include <QPointF>
23#include <KgDifficulty>
24#include <cstdlib>
25
26const qreal Ghost::MAX_SPEED_RATIO = 2.0;
27const int Ghost::POINTS = 200;
28
29Ghost::Ghost(qreal p_x, qreal p_y, const QString & p_imageId, Maze* p_maze) : Character(p_x, p_y, p_maze) {
30 // Initialize the ghost attributes
31 m_imageId = p_imageId;
32 m_points = Ghost::POINTS;
33 m_type = Element::GHOST;
34 m_state = Ghost::HUNTER;
35 m_maxSpeed = m_normalSpeed * MAX_SPEED_RATIO;
36 // Initialize the random-number generator
37 srand(time(NULL));
38 // Makes the ghost move as soon as the game is created
39 goLeft();
40}
41
42Ghost::~Ghost() {
43
44}
45
46void Ghost::goUp() {
47 m_xSpeed = 0;
48 m_ySpeed = -m_speed;
49}
50
51void Ghost::goDown() {
52 m_xSpeed = 0;
53 m_ySpeed = m_speed;
54}
55
56void Ghost::goRight() {
57 m_xSpeed = m_speed;
58 m_ySpeed = 0;
59}
60
61void Ghost::goLeft() {
62 m_xSpeed = -m_speed;
63 m_ySpeed = 0;
64}
65
66void Ghost::updateMove() {
67 // Get the current cell coordinates from the character coordinates
68 int curCellRow = m_maze->getRowFromY(m_y);
69 int curCellCol = m_maze->getColFromX(m_x);
70 // Flag to know when the ghost has no choice but go back
71 bool halfTurnRequired = true;
72 // Contains the different directions a ghost can choose when on a cell center
73 QList<QPointF> directionsList;
74 int nb = 0;
75
76 // If the ghost is not "eaten"
77 if (m_state != Ghost::EATEN) {
78 // If the ghost gets on a Cell center
79 if (onCenter()) {
80 // We retrieve all the directions the ghost can choose (save the turnning back)
81 if (m_maze->getCell(curCellRow, curCellCol + 1).getType() == Cell::CORRIDOR ||
82 (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP &&
83 m_maze->getCell(curCellRow, curCellCol + 1).getType() == Cell::GHOSTCAMP)) {
84 if (m_xSpeed >= 0) {
85 directionsList.append(QPointF(m_speed, 0.0));
86 halfTurnRequired = false;
87 }
88 }
89 if (m_maze->getCell(curCellRow + 1, curCellCol).getType() == Cell::CORRIDOR ||
90 (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP &&
91 m_maze->getCell(curCellRow + 1, curCellCol).getType() == Cell::GHOSTCAMP)) {
92 if (m_ySpeed >= 0) {
93 directionsList.append(QPointF(0.0, m_speed));
94 halfTurnRequired = false;
95 }
96 }
97 if (m_maze->getCell(curCellRow - 1, curCellCol).getType() == Cell::CORRIDOR ||
98 (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP &&
99 m_maze->getCell(curCellRow - 1, curCellCol).getType() == Cell::GHOSTCAMP)) {
100 if (m_ySpeed <= 0) {
101 directionsList.append(QPointF(0.0, -m_speed));
102 halfTurnRequired = false;
103 }
104 }
105 if (m_maze->getCell(curCellRow, curCellCol - 1).getType() == Cell::CORRIDOR ||
106 (m_maze->getCell(curCellRow, curCellCol).getType() == Cell::GHOSTCAMP &&
107 m_maze->getCell(curCellRow, curCellCol - 1).getType() == Cell::GHOSTCAMP)) {
108 if (m_xSpeed <= 0) {
109 directionsList.append(QPointF(-m_speed, 0.0));
110 halfTurnRequired = false;
111 }
112 }
113 // Random number generation to choose one of the directions
114 nb = int(double(rand()) / (double(RAND_MAX) + 1) * directionsList.size());
115 // If there is no directions in the list, the character goes backward
116 if (directionsList.size() == 0) {
117 m_xSpeed = -m_xSpeed;
118 m_ySpeed = -m_ySpeed;
119 } else if ((m_xSpeed != 0 && m_xSpeed != directionsList[nb].x()) ||
120 (m_ySpeed != 0 && m_ySpeed != directionsList[nb].y())) { // If the chosen direction isn't forward
121 // We move the ghost on the center of the cell and update the directions
122 moveOnCenter();
123 m_xSpeed = directionsList[nb].x();
124 m_ySpeed = directionsList[nb].y();
125 }
126 }
127 // We move the ghost
128 move();
129 } else { // If the ghost has been eaten
130 if (onCenter()) {
131 // If the ghost has not reached the camp yet
132 if (m_pathToCamp.size() != 0) {
133 // Go to the next cell to the camp
134 updateMove(m_pathToCamp.first().y(), m_pathToCamp.first().x());
135 // Remove the cell the ghost has reached from the path
136 m_pathToCamp.removeFirst();
137 } else {
138 // If the ghost is not at home (that means it has just been eaten)
139 if (curCellRow != m_maze->getResurrectionCell().y() || curCellCol != m_maze->getResurrectionCell().x()) {
140 // Compute the path to go back to the camp
141 m_pathToCamp = m_maze->getPathToGhostCamp(curCellRow, curCellCol);
142 if (!m_pathToCamp.isEmpty()) {
143 updateMove(m_pathToCamp.first().y(), m_pathToCamp.first().x());
144 } else {
145 // Set the ghost at home
146 m_x = m_maze->getResurrectionCell().x() * Cell::SIZE + Cell::SIZE / 2;
147 m_y = m_maze->getResurrectionCell().y() * Cell::SIZE + Cell::SIZE / 2;
148 setState(Ghost::HUNTER);
149 }
150 } else { // The ghost has reached the ghost camp
151 setState(Ghost::HUNTER);
152 }
153 }
154 }
155 move();
156 }
157}
158
159void Ghost::updateMove(int p_row, int p_col) {
160 // Get the current cell coordinates from the ghost coordinates
161 int curGhostRow = m_maze->getRowFromY(m_y);
162 int curGhostCol = m_maze->getColFromX(m_x);
163
164 if (onCenter()) {
165 if (curGhostRow == p_row) {
166 if (p_col > curGhostCol) {
167 m_xSpeed = m_speed;
168 m_ySpeed = 0;
169 } else {
170 m_xSpeed = -m_speed;
171 m_ySpeed = 0;
172 }
173 } else {
174 if (p_row > curGhostRow) {
175 m_xSpeed = 0;
176 m_ySpeed = m_speed;
177 } else {
178 m_xSpeed = 0;
179 m_ySpeed = -m_speed;
180 }
181 }
182 }
183 // We move the ghost
184 move();
185}
186
187QString Ghost::getImageId() const {
188 return m_imageId;
189}
190
191Ghost::State Ghost::getState() const {
192 return m_state;
193}
194
195void Ghost::setState(Ghost::State p_state) {
196 // Change the state
197 m_state = p_state;
198 switch (m_state) {
199 case Ghost::PREY:
200 m_speed = m_normalSpeed / 2;
201 break;
202 case HUNTER:
203 case EATEN:
204 m_speed = m_normalSpeed;
205 break;
206 }
207 emit(stateChanged());
208}
209
210void Ghost::doActionOnCollision(Kapman*) {
211 switch (m_state) {
212 case Ghost::HUNTER:
213 emit(lifeLost());
214 break;
215 case Ghost::PREY:
216 emit(ghostEaten(this));
217 break;
218 case Ghost::EATEN:
219 // Do nothing
220 break;
221 }
222}
223
224void Ghost::initSpeedInc() {
225 // Ghosts speed increase when level up
226 switch ((int) Kg::difficultyLevel())
227 {
228 case KgDifficultyLevel::Easy:
229 m_speedIncrease = Character::LOW_SPEED_INC;
230 break;
231 case KgDifficultyLevel::Medium:
232 m_speedIncrease = Character::MEDIUM_SPEED_INC;
233 break;
234 case KgDifficultyLevel::Hard:
235 m_speedIncrease = Character::HIGH_SPEED_INC;
236 break;
237 }
238}
239
240