File indexing completed on 2024-12-01 06:50:34
0001 /* 0002 SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de> 0003 SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr> 0004 SPDX-FileCopyrightText: 2007-2008 Pierre-BenoƮt Besse <besse.pb@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "bomb.h" 0010 #include "arena.h" 0011 #include "granatier_random.h" 0012 0013 #include <QTimer> 0014 0015 #include <cstdlib> 0016 0017 const int nMortarRampEnd = static_cast<int>(Granatier::FPS * 50 / 1000.0); 0018 const int nMortarPeak = static_cast<int>((Granatier::FPS * 800 / 1000.0) / 2 + nMortarRampEnd); 0019 const int nMortarGround = static_cast<int>((Granatier::FPS * 800 / 1000.0) + nMortarRampEnd); 0020 0021 Bomb::Bomb(qreal fX, qreal fY, Arena* p_arena, int nBombID, int nDetonationCountdown) : Element(fX, fY, p_arena), m_xSpeed(0), m_ySpeed(0) 0022 { 0023 m_type = Granatier::Element::BOMB; 0024 0025 m_xInit = fX; 0026 m_yInit = fY; 0027 m_x = fX; 0028 m_y = fY; 0029 0030 m_bombPower = 1; 0031 0032 int currentRow = m_arena->getRowFromY(m_y); 0033 int currentCol = m_arena->getColFromX(m_x); 0034 m_arena->setCellElement(currentRow, currentCol, this); 0035 0036 m_detonated = false; 0037 0038 m_bombID = nBombID; 0039 m_explosionID = nBombID; 0040 0041 // Define the timer which sets the puls frequency 0042 m_detonationCountdownTimer = new QTimer(this); 0043 m_detonationCountdownTimer->setInterval(nDetonationCountdown); 0044 m_detonationCountdownTimer->setSingleShot(true); 0045 m_detonationCountdownTimer->start(); 0046 connect(m_detonationCountdownTimer, &QTimer::timeout, this, &Bomb::detonate); 0047 0048 m_mortarTimer = nullptr; 0049 m_mortarState = -1; 0050 m_thrown = false; 0051 m_stopOnCenter = false; 0052 m_falling = false; 0053 0054 moveOnCenter(); 0055 } 0056 0057 Bomb::~Bomb() 0058 { 0059 delete m_detonationCountdownTimer; 0060 delete m_mortarTimer; 0061 } 0062 0063 void Bomb::goUp() 0064 { 0065 } 0066 0067 void Bomb::goDown() 0068 { 0069 } 0070 0071 void Bomb::goRight() 0072 { 0073 } 0074 0075 void Bomb::goLeft() 0076 { 0077 } 0078 0079 void Bomb::updateMove() 0080 { 0081 if(m_detonated) 0082 { 0083 return; 0084 } 0085 0086 int currentRow = m_arena->getRowFromY(m_y); 0087 int currentCol = m_arena->getColFromX(m_x); 0088 //check if the bomb is on an arrow, mortar or hole 0089 if(m_mortarState < 0 && m_xSpeed == 0 && m_ySpeed == 0) 0090 { 0091 switch (m_arena->getCell(currentRow, currentCol).getType()) 0092 { 0093 case Granatier::Cell::ARROWUP: 0094 setYSpeed(-5); 0095 break; 0096 case Granatier::Cell::ARROWRIGHT: 0097 setXSpeed(5); 0098 break; 0099 case Granatier::Cell::ARROWDOWN: 0100 setYSpeed(5); 0101 break; 0102 case Granatier::Cell::ARROWLEFT: 0103 setXSpeed(-5); 0104 break; 0105 case Granatier::Cell::BOMBMORTAR: 0106 m_mortarTimer = new QTimer; 0107 m_mortarTimer->setSingleShot(true); 0108 m_mortarTimer->setInterval(1500); 0109 m_mortarTimer->start(); 0110 m_detonationCountdownTimer->stop(); 0111 m_mortarState = 0; 0112 updateMortarState(); 0113 break; 0114 case Granatier::Cell::HOLE: 0115 if(!m_falling) 0116 { 0117 m_falling = true; 0118 m_type = Granatier::Element::NONE; 0119 m_detonationCountdownTimer->stop(); 0120 Q_EMIT falling(); 0121 Q_EMIT releaseBombArmory(); 0122 } 0123 break; 0124 default: 0125 break; 0126 } 0127 } 0128 0129 if(m_xSpeed != 0 || m_ySpeed != 0) 0130 { 0131 bool bOnCenter = false; 0132 int xDirection = 0; 0133 int xDeltaCenter = static_cast<int>(Granatier::CellSize/2 - (m_x - currentCol * Granatier::CellSize)); 0134 bool bMoveAwayFromCenter = false; 0135 bool bIsHurdleCurrentCell = false; 0136 bool bIsHurdleNextCell = false; 0137 0138 if (m_xSpeed > 0) 0139 { 0140 xDirection = 1; 0141 } 0142 else if (m_xSpeed < 0) 0143 { 0144 xDirection = -1; 0145 } 0146 int yDirection = 0; 0147 int yDeltaCenter = static_cast<int>(Granatier::CellSize/2 - (m_y - currentRow * Granatier::CellSize)); 0148 if (m_ySpeed > 0) 0149 { 0150 yDirection = 1; 0151 } 0152 else if (m_ySpeed < 0) 0153 { 0154 yDirection = -1; 0155 } 0156 0157 if((xDirection != 0 && xDeltaCenter == 0) || (yDirection != 0 && yDeltaCenter == 0)) 0158 { 0159 bOnCenter = true; 0160 } 0161 0162 int newRow = m_arena->getRowFromY(m_y + m_ySpeed); 0163 int newCol = m_arena->getColFromX(m_x + m_xSpeed); 0164 int nextRow; 0165 int nextCol; 0166 if(xDirection > 0 || yDirection > 0) 0167 { 0168 nextRow = m_arena->getRowFromY(m_y + yDirection * Granatier::CellSize/2); //this is needed because the bombs won't move if they are coming from the top, hit an up arrow and there is a hurdle below the arrow 0169 nextCol = m_arena->getColFromX(m_x + xDirection * Granatier::CellSize/2); 0170 } 0171 else 0172 { 0173 nextRow = m_arena->getRowFromY(m_y + m_ySpeed + yDirection * Granatier::CellSize/2); 0174 nextCol = m_arena->getColFromX(m_x + m_xSpeed + xDirection * Granatier::CellSize/2); 0175 } 0176 0177 bIsHurdleCurrentCell = !(m_arena->getCell(currentRow, currentCol).isWalkable(this)); 0178 bIsHurdleNextCell = !(m_arena->getCell(nextRow, nextCol).isWalkable(this)); 0179 0180 if(xDirection * xDeltaCenter <= 0 || yDirection * yDeltaCenter <= 0) 0181 { 0182 bMoveAwayFromCenter = true; 0183 } 0184 0185 //at first, check if move over cell center or currently on cell center 0186 if((bOnCenter || (xDirection * xDeltaCenter < 0 && xDirection * (m_xSpeed + xDeltaCenter) >= 0) || (yDirection * yDeltaCenter < 0 && yDirection * (m_ySpeed + yDeltaCenter) >= 0)) && m_mortarState < 0) 0187 { 0188 bool bIsMortar = false; 0189 bool bIsNewDirection = false; 0190 0191 switch (m_arena->getCell(currentRow, currentCol).getType()) 0192 { 0193 case Granatier::Cell::ARROWUP: 0194 if(yDirection != -1) 0195 { 0196 bIsNewDirection = true; 0197 bMoveAwayFromCenter = true; 0198 } 0199 break; 0200 case Granatier::Cell::ARROWRIGHT: 0201 if(xDirection != 1) 0202 { 0203 bIsNewDirection = true; 0204 bMoveAwayFromCenter = true; 0205 } 0206 break; 0207 case Granatier::Cell::ARROWDOWN: 0208 if(yDirection != 1) 0209 { 0210 bIsNewDirection = true; 0211 bMoveAwayFromCenter = true; 0212 } 0213 break; 0214 case Granatier::Cell::ARROWLEFT: 0215 if(xDirection != -1) 0216 { 0217 bIsNewDirection = true; 0218 bMoveAwayFromCenter = true; 0219 } 0220 break; 0221 case Granatier::Cell::BOMBMORTAR: 0222 bIsMortar = true; 0223 break; 0224 case Granatier::Cell::HOLE: 0225 m_stopOnCenter = true; 0226 break; 0227 default: 0228 break; 0229 } 0230 0231 //if two bombs move towards them, stop them and move them to the next cell center 0232 if(((bIsHurdleCurrentCell && !bMoveAwayFromCenter) || bIsHurdleNextCell) && !m_stopOnCenter) 0233 { 0234 if(bOnCenter) 0235 { 0236 setXSpeed(0); 0237 setYSpeed(0); 0238 } 0239 else 0240 { 0241 m_stopOnCenter = true; 0242 setXSpeed(-m_xSpeed); 0243 setYSpeed(-m_ySpeed); 0244 } 0245 } 0246 0247 //stop at cell center if direction change or bomb mortar in current cell 0248 else if(bIsMortar || bIsNewDirection || m_stopOnCenter) 0249 { 0250 move((currentCol+0.5) * Granatier::CellSize, (currentRow+0.5) * Granatier::CellSize); 0251 setXSpeed(0); 0252 setYSpeed(0); 0253 m_stopOnCenter = false; 0254 } 0255 else 0256 { 0257 if((newRow != currentRow || newCol != currentCol) && m_mortarState < 0) 0258 { 0259 m_arena->removeCellElement(currentRow, currentCol, this); 0260 m_arena->setCellElement(newRow, newCol, this); 0261 } 0262 move(m_x + m_xSpeed, m_y + m_ySpeed); 0263 } 0264 } 0265 else 0266 { 0267 //if two bombs move towards them, stop them and move them to the next cell center 0268 if(((bIsHurdleCurrentCell && !bMoveAwayFromCenter) || bIsHurdleNextCell) && !m_stopOnCenter && m_mortarState < 0) 0269 { 0270 if(bOnCenter) 0271 { 0272 setXSpeed(0); 0273 setYSpeed(0); 0274 } 0275 else 0276 { 0277 m_stopOnCenter = true; 0278 setXSpeed(-m_xSpeed); 0279 setYSpeed(-m_ySpeed); 0280 } 0281 } 0282 else 0283 { 0284 if((newRow != currentRow || newCol != currentCol) && m_mortarState < 0) 0285 { 0286 m_arena->removeCellElement(currentRow, currentCol, this); 0287 m_arena->setCellElement(newRow, newCol, this); 0288 } 0289 move(m_x + m_xSpeed, m_y + m_ySpeed); 0290 } 0291 } 0292 } 0293 0294 if(m_mortarState >= 0 && !(m_mortarTimer->isActive())) 0295 { 0296 updateMortarState(); 0297 } 0298 } 0299 0300 void Bomb::move(qreal x, qreal y) 0301 { 0302 // Move the Bomb 0303 m_x = x; 0304 m_y = y; 0305 Q_EMIT moved(m_x, m_y); 0306 } 0307 0308 qreal Bomb::getXSpeed() const 0309 { 0310 return m_xSpeed; 0311 } 0312 0313 qreal Bomb::getYSpeed() const 0314 { 0315 return m_ySpeed; 0316 } 0317 0318 qreal Bomb::getSpeed() 0319 { 0320 return m_speed; 0321 } 0322 0323 void Bomb::setXSpeed(qreal p_xSpeed) 0324 { 0325 m_xSpeed = p_xSpeed; 0326 } 0327 0328 void Bomb::setYSpeed(qreal p_ySpeed) 0329 { 0330 m_ySpeed = p_ySpeed; 0331 } 0332 0333 void Bomb::setThrown(int nDirection) 0334 { 0335 if(!m_mortarTimer) 0336 { 0337 m_mortarTimer = new QTimer; 0338 m_mortarTimer->setSingleShot(true); 0339 if(m_detonationCountdownTimer) 0340 { 0341 m_detonationCountdownTimer->stop(); 0342 } 0343 } 0344 qreal fSpeed = 2 * Granatier::CellSize / (Granatier::FPS * 800 / 1000.0 + 1); 0345 switch(nDirection) 0346 { 0347 case Granatier::Direction::NORTH: 0348 setXSpeed(0); 0349 setYSpeed(-fSpeed); 0350 break; 0351 case Granatier::Direction::EAST: 0352 setXSpeed(fSpeed); 0353 setYSpeed(0); 0354 break; 0355 case Granatier::Direction::SOUTH: 0356 setXSpeed(0); 0357 setYSpeed(fSpeed); 0358 break; 0359 case Granatier::Direction::WEST: 0360 setXSpeed(-fSpeed); 0361 setYSpeed(0); 0362 break; 0363 } 0364 0365 int curCellRow = m_arena->getRowFromY(m_y); 0366 int curCellCol = m_arena->getColFromX(m_x); 0367 m_arena->removeCellElement(curCellRow, curCellCol, this); 0368 0369 m_type = Granatier::Element::NONE; 0370 m_thrown = true; 0371 m_mortarState = nMortarRampEnd; 0372 } 0373 0374 void Bomb::setKicked(int nDirection) 0375 { 0376 switch(nDirection) 0377 { 0378 case Granatier::Direction::NORTH: 0379 setXSpeed(0); 0380 setYSpeed(-5); 0381 break; 0382 case Granatier::Direction::EAST: 0383 setXSpeed(5); 0384 setYSpeed(0); 0385 break; 0386 case Granatier::Direction::SOUTH: 0387 setXSpeed(0); 0388 setYSpeed(5); 0389 break; 0390 case Granatier::Direction::WEST: 0391 setXSpeed(-5); 0392 setYSpeed(0); 0393 break; 0394 } 0395 } 0396 0397 bool Bomb::onCenter() 0398 { 0399 // Get the current cell center coordinates 0400 qreal centerX = (m_arena->getColFromX(m_x) + 0.5) * Granatier::CellSize; 0401 qreal centerY = (m_arena->getRowFromY(m_y) + 0.5) * Granatier::CellSize; 0402 bool willGoPast = false; 0403 0404 // Will the character go past the center of the cell it's on ? 0405 // If goes right 0406 if (m_xSpeed > 0) { 0407 willGoPast = (m_x <= centerX && m_x + m_xSpeed >= centerX); 0408 } 0409 // If goes left 0410 else if (m_xSpeed < 0) { 0411 willGoPast = (m_x >= centerX && m_x + m_xSpeed <= centerX); 0412 } 0413 // If goes down 0414 else if (m_ySpeed > 0) { 0415 willGoPast = (m_y <= centerY && m_y + m_ySpeed >= centerY); 0416 } 0417 // If goes up 0418 else if (m_ySpeed < 0) { 0419 willGoPast = (m_y >= centerY && m_y + m_ySpeed <= centerY); 0420 } 0421 // If does not move 0422 else { 0423 willGoPast = (m_x == centerX && m_y == centerY); 0424 } 0425 0426 return willGoPast; 0427 } 0428 0429 void Bomb::moveOnCenter() 0430 { 0431 setX((m_arena->getColFromX(m_x) + 0.5) * Granatier::CellSize); 0432 setY((m_arena->getRowFromY(m_y) + 0.5) * Granatier::CellSize); 0433 } 0434 0435 int Bomb::bombPower() 0436 { 0437 return m_bombPower; 0438 } 0439 0440 void Bomb::setBombPower(int bombPower) 0441 { 0442 m_bombPower = bombPower; 0443 } 0444 0445 void Bomb::pause() 0446 { 0447 if(m_detonationCountdownTimer) 0448 { 0449 m_detonationCountdownTimer->stop(); 0450 } 0451 } 0452 0453 void Bomb::resume() 0454 { 0455 if(m_detonationCountdownTimer) 0456 { 0457 m_detonationCountdownTimer->start(); 0458 } 0459 } 0460 0461 void Bomb::detonate() 0462 { 0463 if(!m_detonated) 0464 { 0465 m_detonated = true; 0466 delete m_detonationCountdownTimer; 0467 m_detonationCountdownTimer = nullptr; 0468 Q_EMIT bombDetonated(this); 0469 Q_EMIT releaseBombArmory(); 0470 } 0471 } 0472 0473 bool Bomb::isDetonated() 0474 { 0475 return m_detonated; 0476 } 0477 0478 int Bomb::explosionID() 0479 { 0480 return m_explosionID; 0481 } 0482 0483 void Bomb::initDetonation(int nBombID, int nDetonationTimeout) 0484 { 0485 if(m_bombID == m_explosionID) 0486 { 0487 m_explosionID = nBombID; 0488 } 0489 0490 if((!m_detonationCountdownTimer)) 0491 { 0492 return; 0493 } 0494 0495 if(m_detonationCountdownTimer->interval() > nDetonationTimeout) 0496 { 0497 m_detonationCountdownTimer->setInterval(nDetonationTimeout); 0498 } 0499 0500 if(!(m_detonationCountdownTimer->isActive())) 0501 { 0502 m_detonationCountdownTimer->start(); 0503 } 0504 } 0505 0506 void Bomb::slot_detonationCompleted() 0507 { 0508 //TODO: call from game 0509 m_arena->removeCellElement(m_arena->getRowFromY(m_y), m_arena->getColFromX(m_x), this); 0510 } 0511 0512 void Bomb::updateMortarState() 0513 { 0514 Q_EMIT mortar(m_mortarState, nMortarRampEnd, nMortarPeak, nMortarGround); 0515 0516 if(m_mortarState <= 0) 0517 { 0518 int curCellRow = m_arena->getRowFromY(m_y); 0519 int curCellCol = m_arena->getColFromX(m_x); 0520 0521 setXSpeed(0); 0522 setYSpeed(0); 0523 0524 m_thrown = false; 0525 m_type = Granatier::Element::NONE; 0526 m_arena->removeCellElement(curCellRow, curCellCol, this); 0527 } 0528 else if(m_mortarState == nMortarRampEnd) 0529 { 0530 int curCellRow = m_arena->getRowFromY(m_y); 0531 int curCellCol = m_arena->getColFromX(m_x); 0532 0533 if(!m_thrown) 0534 { 0535 int nRow; 0536 int nCol; 0537 bool bFound = false; 0538 0539 do 0540 { 0541 nRow = granatier::RNG::fromRange(0, m_arena->getNbRows()-1); 0542 nCol = granatier::RNG::fromRange(0, m_arena->getNbColumns()-1); 0543 if(m_arena->getCell(nRow, nCol).getType() != Granatier::Cell::WALL) 0544 { 0545 bFound = true; 0546 } 0547 } 0548 while (!bFound); 0549 0550 setXSpeed((nCol - curCellCol) * Granatier::CellSize / (Granatier::FPS * 800 / 1000.0)); 0551 setYSpeed((nRow - curCellRow) * Granatier::CellSize / (Granatier::FPS * 800 / 1000.0)); 0552 } 0553 0554 m_type = Granatier::Element::NONE; 0555 m_arena->removeCellElement(curCellRow, curCellCol, this); 0556 } 0557 else if(m_mortarState == nMortarGround) 0558 { 0559 setXSpeed(0); 0560 setYSpeed(0); 0561 } 0562 else if (m_mortarState > nMortarGround) 0563 { 0564 int curCellRow = m_arena->getRowFromY(m_y); 0565 int curCellCol = m_arena->getColFromX(m_x); 0566 m_type = Granatier::Element::BOMB; 0567 m_arena->setCellElement(curCellRow, curCellCol, this); 0568 0569 m_mortarState = -1; 0570 if(m_detonationCountdownTimer) 0571 { 0572 if(!m_thrown) 0573 { 0574 initDetonation(m_bombID, 40); 0575 } 0576 else 0577 { 0578 m_detonationCountdownTimer->setInterval(2000); 0579 m_detonationCountdownTimer->start(); 0580 } 0581 } 0582 } 0583 0584 if(m_mortarState >= 0) 0585 { 0586 m_mortarState++; 0587 } 0588 } 0589 0590 #include "moc_bomb.cpp"