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

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 Nathalie Liesse <nathalie.liesse@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 
0010 #include "playeritem.h"
0011 #include "player.h"
0012 #include "bonus.h"
0013 #include "bonusitem.h"
0014 #include "bomb.h"
0015 #include "bombitem.h"
0016 #include "bombexplosionitem.h"
0017 
0018 #include <QTimeLine>
0019 
0020 #include <KGameRenderer>
0021 
0022 const int PlayerItem::NB_FRAMES = 13;
0023 const int PlayerItem::ANIM_SPEED = 240;
0024 
0025 PlayerItem::PlayerItem(Player* p_model, KGameRenderer* renderer) : CharacterItem(p_model, renderer)
0026 {
0027     connect(p_model, &Player::directionChanged, this, &PlayerItem::updateDirection);
0028     connect(p_model, &Player::gameUpdated, this, &PlayerItem::manageCollision);
0029     connect(p_model, &Player::stopped, this, &PlayerItem::stopAnim);
0030     connect(p_model, &Player::falling, this, &PlayerItem::fallingAnimation);
0031     connect(p_model, &Player::resurrected, this, &PlayerItem::resurrect);
0032 
0033     // A timeLine for the Player animation
0034     m_animationTimer = new QTimeLine();
0035     m_animationTimer->setEasingCurve(QEasingCurve::Linear);
0036     m_animationTimer->setLoopCount(0);
0037     m_animationTimer->setFrameRange(0, NB_FRAMES - 1);
0038     // Animation speed
0039     m_animationTimer->setDuration(PlayerItem::ANIM_SPEED);
0040     connect(m_animationTimer, &QTimeLine::frameChanged, this, &PlayerItem::setFrame);
0041 
0042     int width = static_cast<int>(Granatier::CellSize * 0.9);
0043     int height = static_cast<int>(Granatier::CellSize * 0.9);
0044     if((static_cast<int>(Granatier::CellSize) - width) % 2 != 0)
0045     {
0046         width--;
0047     }
0048     if((static_cast<int>(Granatier::CellSize) - height) % 2 != 0)
0049     {
0050         height--;
0051     }
0052     m_itemSizeSet = QSize(width, height);
0053     m_itemSizeReal = m_itemSizeSet;
0054 
0055     if(m_renderer->spriteExists(QStringLiteral("player_0")))
0056     {
0057         setSpriteKey(QStringLiteral("player_0"));
0058     }
0059 
0060     setZValue(250);
0061 
0062     m_fallingAnimationCounter = 0;
0063     m_resurrectionAnimationCounter = 0;
0064 }
0065 
0066 PlayerItem::~PlayerItem()
0067 {
0068     delete m_animationTimer;
0069 }
0070 
0071 void PlayerItem::resurrect()
0072 {
0073     int nDirection = dynamic_cast <Player*> (m_model)->direction();
0074     setZValue(250);
0075     m_fallingAnimationCounter = 0;
0076     m_resurrectionAnimationCounter = 10;
0077     setRenderSize(m_renderSize);
0078     if(m_renderer->spriteExists(QStringLiteral("player_0")))
0079     {
0080         setSpriteKey(QStringLiteral("player_0"));
0081     }
0082 
0083     QTransform transform;
0084     transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
0085     // get the angle
0086     switch(nDirection)
0087     {
0088         case Granatier::Direction::EAST:
0089             transform.rotate(0);
0090             break;
0091         case Granatier::Direction::SOUTH:
0092             transform.rotate(90);
0093             break;
0094         case Granatier::Direction::NORTH:
0095             transform.rotate(-90);
0096             break;
0097         case Granatier::Direction::WEST:
0098             transform.rotate(180);
0099             break;
0100         default:
0101             transform.rotate(0);
0102             break;
0103     }
0104     transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
0105     setTransform(transform);
0106 
0107     startAnim();
0108 
0109     setVisible(true);
0110 }
0111 
0112 void PlayerItem::updateDirection()
0113 {
0114     int nDirection = dynamic_cast <Player*> (m_model)->direction();
0115 
0116     // Rotate the item
0117     QTransform transform;
0118     transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
0119     // get the angle
0120     switch(nDirection)
0121     {
0122         case Granatier::Direction::EAST:
0123             transform.rotate(0);
0124             break;
0125         case Granatier::Direction::SOUTH:
0126             transform.rotate(90);
0127             break;
0128         case Granatier::Direction::NORTH:
0129             transform.rotate(-90);
0130             break;
0131         case Granatier::Direction::WEST:
0132             transform.rotate(180);
0133             break;
0134         default:
0135             transform.rotate(0);
0136             break;
0137     }
0138     transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
0139     setTransform(transform);
0140 }
0141 
0142 void PlayerItem::updateGraphics(qreal svgScaleFactor)
0143 {
0144     updateGraphicsInternal(svgScaleFactor);
0145     updateDirection();
0146 }
0147 
0148 void PlayerItem::manageCollision()
0149 {
0150     QList<QGraphicsItem*> collidingList = collidingItems();
0151     BonusItem* bonusItem;
0152 
0153     // The signal is emitted only if the list contains more than 1 items (to exclude the case
0154     // when the player only collides with the arena)
0155     if (collidingList.size() > 1)
0156     {
0157         for (auto & i : collidingList)
0158         {
0159             // The arena and the points labels have a negative zValue which allows to exclude them from the treatment of collisions
0160             if (i->zValue() >= 300 && i->zValue() < 400)
0161             {
0162                 //((ElementItem*)collidingList[i])->getModel()->doActionOnCollision((Player*)getModel());
0163                 int nExplosionID;
0164                 if(i->zValue() == 315)
0165                 {
0166                     auto* bombItem = dynamic_cast <BombItem*> (i);
0167                     nExplosionID = dynamic_cast <Bomb*> (bombItem->getModel())->explosionID();
0168                 }
0169                 else
0170                 {
0171                     nExplosionID = dynamic_cast <BombExplosionItem*> (i)->explosionID();
0172                 }
0173 
0174                 if(dynamic_cast <Player*> (m_model)->shield(nExplosionID) == false)
0175                 {
0176                     setDead();
0177                     dynamic_cast <Player*> (m_model)->die();
0178                 }
0179             }
0180             else if (i->zValue() == 100)
0181             {
0182                 bonusItem = dynamic_cast <BonusItem*> (i);
0183                 if(dynamic_cast <Bonus*> (bonusItem->getModel())->isTaken() == false)
0184                 {
0185                     dynamic_cast <Bonus*> (bonusItem->getModel())->setTaken();
0186                     bonusItem->getModel()->doActionOnCollision(dynamic_cast <Player*> (this->getModel()));
0187                     Q_EMIT bonusItemTaken(bonusItem);
0188                 }
0189             }
0190         }
0191     }
0192 }
0193 
0194 void PlayerItem::update(qreal p_x, qreal p_y)
0195 {
0196     ElementItem::update(p_x, p_y);
0197 
0198     // If the player is moving
0199     if (((Player*)getModel())->getXSpeed() != 0 || ((Player*)getModel())->getYSpeed() != 0)
0200     {
0201         startAnim();
0202     }
0203 }
0204 
0205 void PlayerItem::startAnim()
0206 {
0207     // Start the animation timer if it is not active
0208     if (m_animationTimer->state() != QTimeLine::Running)
0209     {
0210         m_animationTimer->start();
0211     }
0212 }
0213 
0214 void PlayerItem::pauseAnim()
0215 {
0216     dynamic_cast <Player*> (m_model)->pause();
0217     if (m_animationTimer->state() == QTimeLine::Running)
0218     {
0219         m_animationTimer->setPaused(true);
0220     }
0221 }
0222 
0223 void PlayerItem::resumeAnim()
0224 {
0225     dynamic_cast <Player*> (m_model)->resume();
0226     if (m_animationTimer->state() == QTimeLine::Paused)
0227     {
0228         m_animationTimer->setPaused(false);
0229     }
0230 }
0231 
0232 void PlayerItem::stopAnim()
0233 {
0234     if(m_renderer->spriteExists(QStringLiteral("player_0")))
0235     {
0236         setSpriteKey(QStringLiteral("player_0"));
0237     }
0238     if (m_animationTimer->state() != QTimeLine::NotRunning && m_resurrectionAnimationCounter == 0)
0239     {
0240         m_animationTimer->stop();
0241     }
0242 }
0243 
0244 void PlayerItem::fallingAnimation()
0245 {
0246     m_fallingAnimationCounter = 1;
0247 }
0248 
0249 void PlayerItem::setFrame(const int p_frame)
0250 {
0251     if(m_renderer->spriteExists(QStringLiteral("player_%1").arg(p_frame)))
0252     {
0253         setSpriteKey(QStringLiteral("player_%1").arg(p_frame));
0254 
0255         if(m_fallingAnimationCounter > 0 || m_resurrectionAnimationCounter > 0)
0256         {
0257             int angle = 0;
0258             int nDirection = dynamic_cast <Player*> (m_model)->direction();
0259 
0260             // get the angle
0261             switch(nDirection)
0262             {
0263                 case Granatier::Direction::EAST:
0264                     angle = 0;
0265                     break;
0266                 case Granatier::Direction::SOUTH:
0267                     angle = 90;
0268                     break;
0269                 case Granatier::Direction::NORTH:
0270                     angle = -90;
0271                     break;
0272                 case Granatier::Direction::WEST:
0273                     angle = 180;
0274                     break;
0275                 default:
0276                     angle = 0;
0277                     break;
0278             }
0279 
0280             if(m_fallingAnimationCounter > 0)
0281             {
0282                 // set z-value below the ground
0283                 setZValue(-2);
0284                 // shrink the item
0285                 QTransform transform;
0286                 transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.width() / 2.0);
0287                 transform.rotate(angle);
0288                 setRenderSize(m_renderSize * (1-m_fallingAnimationCounter*0.02));
0289                 transform.translate(-m_itemSizeReal.width() * (1-m_fallingAnimationCounter*0.02) / 2.0, -m_itemSizeReal.width() * (1-m_fallingAnimationCounter*0.02) / 2.0);
0290                 setTransform(transform);
0291                 m_fallingAnimationCounter++;
0292 
0293                 if(m_fallingAnimationCounter > 50)
0294                 {
0295                     setDead();
0296                     dynamic_cast <Player*> (m_model)->die();
0297                     setVisible(false);
0298                 }
0299             }
0300 
0301             if(m_resurrectionAnimationCounter > 0)
0302             {
0303                 qreal resurrectionScale = 1;
0304                 QTransform transform;
0305                 transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.width() / 2.0);
0306                 transform.rotate(angle);
0307                 if(m_resurrectionAnimationCounter > 9)
0308                 {
0309                     resurrectionScale = 1.1;
0310                 }
0311                 else if(m_resurrectionAnimationCounter > 8)
0312                 {
0313                     resurrectionScale = 0.9;
0314                 }
0315                 else if(m_resurrectionAnimationCounter > 7)
0316                 {
0317                     resurrectionScale = 0.6;
0318                 }
0319                 else if(m_resurrectionAnimationCounter > 6)
0320                 {
0321                     resurrectionScale = 0.7;
0322                 }
0323                 else if(m_resurrectionAnimationCounter > 5)
0324                 {
0325                     resurrectionScale = 0.85;
0326                 }
0327                 else if(m_resurrectionAnimationCounter > 4)
0328                 {
0329                     resurrectionScale = 1;
0330                 }
0331                 else if(m_resurrectionAnimationCounter > 3)
0332                 {
0333                     resurrectionScale = 1.05;
0334                 }
0335                 else if(m_resurrectionAnimationCounter > 2)
0336                 {
0337                     resurrectionScale = 1.1;
0338                 }
0339                 else if(m_resurrectionAnimationCounter > 1)
0340                 {
0341                     resurrectionScale = 1.05;
0342                 }
0343                 else if(m_resurrectionAnimationCounter > 0)
0344                 {
0345                     resurrectionScale = 1;
0346                 }
0347 
0348                 m_resurrectionAnimationCounter--;
0349                 if(m_resurrectionAnimationCounter == 0)
0350                 {
0351                     resurrectionScale = 1;
0352                     stopAnim();
0353                 }
0354 
0355                 setRenderSize(m_renderSize * resurrectionScale);
0356                 transform.translate(-m_itemSizeReal.width() * resurrectionScale / 2.0, -m_itemSizeReal.width() * resurrectionScale / 2.0);
0357                 setTransform(transform);
0358             }
0359         }
0360     }
0361 }
0362 
0363 void PlayerItem::setDead()
0364 {
0365     stopAnim();
0366     setZValue(1);
0367     if(m_resurrectionAnimationCounter != 0)
0368     {
0369         QTransform transform;
0370         transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
0371         setRenderSize(m_renderSize);
0372         transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
0373         setTransform(transform);
0374     }
0375     if(m_renderer->spriteExists(QStringLiteral("player_death")))
0376     {
0377         setSpriteKey(QStringLiteral("player_death"));
0378     }
0379 }
0380 
0381 #include "moc_playeritem.cpp"