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