1 /*
2     SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de>
3     SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
4     SPDX-FileCopyrightText: 2007-2008 Nathalie Liesse <nathalie.liesse@gmail.com>
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 
10 #include "playeritem.h"
11 #include "player.h"
12 #include "bonus.h"
13 #include "bonusitem.h"
14 #include "bomb.h"
15 #include "bombitem.h"
16 #include "bombexplosionitem.h"
17 
18 #include <QTimeLine>
19 
20 #include <KGameRenderer>
21 
22 const int PlayerItem::NB_FRAMES = 13;
23 const int PlayerItem::ANIM_SPEED = 240;
24 
PlayerItem(Player * p_model,KGameRenderer * renderer)25 PlayerItem::PlayerItem(Player* p_model, KGameRenderer* renderer) : CharacterItem(p_model, renderer)
26 {
27     connect(p_model, &Player::directionChanged, this, &PlayerItem::updateDirection);
28     connect(p_model, &Player::gameUpdated, this, &PlayerItem::manageCollision);
29     connect(p_model, &Player::stopped, this, &PlayerItem::stopAnim);
30     connect(p_model, &Player::falling, this, &PlayerItem::fallingAnimation);
31     connect(p_model, &Player::resurrected, this, &PlayerItem::resurrect);
32 
33     // A timeLine for the Player animation
34     m_animationTimer = new QTimeLine();
35     m_animationTimer->setEasingCurve(QEasingCurve::Linear);
36     m_animationTimer->setLoopCount(0);
37     m_animationTimer->setFrameRange(0, NB_FRAMES - 1);
38     // Animation speed
39     m_animationTimer->setDuration(PlayerItem::ANIM_SPEED);
40     connect(m_animationTimer, &QTimeLine::frameChanged, this, &PlayerItem::setFrame);
41 
42     int width = static_cast<int>(Granatier::CellSize * 0.9);
43     int height = static_cast<int>(Granatier::CellSize * 0.9);
44     if((static_cast<int>(Granatier::CellSize) - width) % 2 != 0)
45     {
46         width--;
47     }
48     if((static_cast<int>(Granatier::CellSize) - height) % 2 != 0)
49     {
50         height--;
51     }
52     m_itemSizeSet = QSize(width, height);
53     m_itemSizeReal = m_itemSizeSet;
54 
55     if(m_renderer->spriteExists(QStringLiteral("player_0")))
56     {
57         setSpriteKey(QStringLiteral("player_0"));
58     }
59 
60     setZValue(250);
61 
62     m_fallingAnimationCounter = 0;
63     m_resurrectionAnimationCounter = 0;
64 }
65 
~PlayerItem()66 PlayerItem::~PlayerItem()
67 {
68     delete m_animationTimer;
69 }
70 
resurrect()71 void PlayerItem::resurrect()
72 {
73     int nDirection = dynamic_cast <Player*> (m_model)->direction();
74     setZValue(250);
75     m_fallingAnimationCounter = 0;
76     m_resurrectionAnimationCounter = 10;
77     setRenderSize(m_renderSize);
78     if(m_renderer->spriteExists(QStringLiteral("player_0")))
79     {
80         setSpriteKey(QStringLiteral("player_0"));
81     }
82 
83     QTransform transform;
84     transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
85     // get the angle
86     switch(nDirection)
87     {
88         case Granatier::Direction::EAST:
89             transform.rotate(0);
90             break;
91         case Granatier::Direction::SOUTH:
92             transform.rotate(90);
93             break;
94         case Granatier::Direction::NORTH:
95             transform.rotate(-90);
96             break;
97         case Granatier::Direction::WEST:
98             transform.rotate(180);
99             break;
100         default:
101             transform.rotate(0);
102             break;
103     }
104     transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
105     setTransform(transform);
106 
107     startAnim();
108 
109     setVisible(true);
110 }
111 
updateDirection()112 void PlayerItem::updateDirection()
113 {
114     int nDirection = dynamic_cast <Player*> (m_model)->direction();
115 
116     // Rotate the item
117     QTransform transform;
118     transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
119     // get the angle
120     switch(nDirection)
121     {
122         case Granatier::Direction::EAST:
123             transform.rotate(0);
124             break;
125         case Granatier::Direction::SOUTH:
126             transform.rotate(90);
127             break;
128         case Granatier::Direction::NORTH:
129             transform.rotate(-90);
130             break;
131         case Granatier::Direction::WEST:
132             transform.rotate(180);
133             break;
134         default:
135             transform.rotate(0);
136             break;
137     }
138     transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
139     setTransform(transform);
140 }
141 
updateGraphics(qreal svgScaleFactor)142 void PlayerItem::updateGraphics(qreal svgScaleFactor)
143 {
144     updateGraphicsInternal(svgScaleFactor);
145     updateDirection();
146 }
147 
manageCollision()148 void PlayerItem::manageCollision()
149 {
150     QList<QGraphicsItem*> collidingList = collidingItems();
151     BonusItem* bonusItem;
152 
153     // The signal is emitted only if the list contains more than 1 items (to exclude the case
154     // when the player only collides with the arena)
155     if (collidingList.size() > 1)
156     {
157         for (auto & i : collidingList)
158         {
159             // The arena and the points labels have a negative zValue which allows to exclude them from the treatment of collisions
160             if (i->zValue() >= 300 && i->zValue() < 400)
161             {
162                 //((ElementItem*)collidingList[i])->getModel()->doActionOnCollision((Player*)getModel());
163                 int nExplosionID;
164                 if(i->zValue() == 315)
165                 {
166                     BombItem* bombItem = dynamic_cast <BombItem*> (i);
167                     nExplosionID = dynamic_cast <Bomb*> (bombItem->getModel())->explosionID();
168                 }
169                 else
170                 {
171                     nExplosionID = dynamic_cast <BombExplosionItem*> (i)->explosionID();
172                 }
173 
174                 if(dynamic_cast <Player*> (m_model)->shield(nExplosionID) == false)
175                 {
176                     setDead();
177                     dynamic_cast <Player*> (m_model)->die();
178                 }
179             }
180             else if (i->zValue() == 100)
181             {
182                 bonusItem = dynamic_cast <BonusItem*> (i);
183                 if(dynamic_cast <Bonus*> (bonusItem->getModel())->isTaken() == false)
184                 {
185                     dynamic_cast <Bonus*> (bonusItem->getModel())->setTaken();
186                     bonusItem->getModel()->doActionOnCollision(dynamic_cast <Player*> (this->getModel()));
187                     Q_EMIT bonusItemTaken(bonusItem);
188                 }
189             }
190         }
191     }
192 }
193 
update(qreal p_x,qreal p_y)194 void PlayerItem::update(qreal p_x, qreal p_y)
195 {
196     ElementItem::update(p_x, p_y);
197 
198     // If the player is moving
199     if (((Player*)getModel())->getXSpeed() != 0 || ((Player*)getModel())->getYSpeed() != 0)
200     {
201         startAnim();
202     }
203 }
204 
startAnim()205 void PlayerItem::startAnim()
206 {
207     // Start the animation timer if it is not active
208     if (m_animationTimer->state() != QTimeLine::Running)
209     {
210         m_animationTimer->start();
211     }
212 }
213 
pauseAnim()214 void PlayerItem::pauseAnim()
215 {
216     dynamic_cast <Player*> (m_model)->pause();
217     if (m_animationTimer->state() == QTimeLine::Running)
218     {
219         m_animationTimer->setPaused(true);
220     }
221 }
222 
resumeAnim()223 void PlayerItem::resumeAnim()
224 {
225     dynamic_cast <Player*> (m_model)->resume();
226     if (m_animationTimer->state() == QTimeLine::Paused)
227     {
228         m_animationTimer->setPaused(false);
229     }
230 }
231 
stopAnim()232 void PlayerItem::stopAnim()
233 {
234     if(m_renderer->spriteExists(QStringLiteral("player_0")))
235     {
236         setSpriteKey(QStringLiteral("player_0"));
237     }
238     if (m_animationTimer->state() != QTimeLine::NotRunning && m_resurrectionAnimationCounter == 0)
239     {
240         m_animationTimer->stop();
241     }
242 }
243 
fallingAnimation()244 void PlayerItem::fallingAnimation()
245 {
246     m_fallingAnimationCounter = 1;
247 }
248 
setFrame(const int p_frame)249 void PlayerItem::setFrame(const int p_frame)
250 {
251     if(m_renderer->spriteExists(QStringLiteral("player_%1").arg(p_frame)))
252     {
253         setSpriteKey(QStringLiteral("player_%1").arg(p_frame));
254 
255         if(m_fallingAnimationCounter > 0 || m_resurrectionAnimationCounter > 0)
256         {
257             int angle = 0;
258             int nDirection = dynamic_cast <Player*> (m_model)->direction();
259 
260             // get the angle
261             switch(nDirection)
262             {
263                 case Granatier::Direction::EAST:
264                     angle = 0;
265                     break;
266                 case Granatier::Direction::SOUTH:
267                     angle = 90;
268                     break;
269                 case Granatier::Direction::NORTH:
270                     angle = -90;
271                     break;
272                 case Granatier::Direction::WEST:
273                     angle = 180;
274                     break;
275                 default:
276                     angle = 0;
277                     break;
278             }
279 
280             if(m_fallingAnimationCounter > 0)
281             {
282                 // set z-value below the ground
283                 setZValue(-2);
284                 // shrink the item
285                 QTransform transform;
286                 transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.width() / 2.0);
287                 transform.rotate(angle);
288                 setRenderSize(m_renderSize * (1-m_fallingAnimationCounter*0.02));
289                 transform.translate(-m_itemSizeReal.width() * (1-m_fallingAnimationCounter*0.02) / 2.0, -m_itemSizeReal.width() * (1-m_fallingAnimationCounter*0.02) / 2.0);
290                 setTransform(transform);
291                 m_fallingAnimationCounter++;
292 
293                 if(m_fallingAnimationCounter > 50)
294                 {
295                     setDead();
296                     dynamic_cast <Player*> (m_model)->die();
297                     setVisible(false);
298                 }
299             }
300 
301             if(m_resurrectionAnimationCounter > 0)
302             {
303                 qreal resurrectionScale = 1;
304                 QTransform transform;
305                 transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.width() / 2.0);
306                 transform.rotate(angle);
307                 if(m_resurrectionAnimationCounter > 9)
308                 {
309                     resurrectionScale = 1.1;
310                 }
311                 else if(m_resurrectionAnimationCounter > 8)
312                 {
313                     resurrectionScale = 0.9;
314                 }
315                 else if(m_resurrectionAnimationCounter > 7)
316                 {
317                     resurrectionScale = 0.6;
318                 }
319                 else if(m_resurrectionAnimationCounter > 6)
320                 {
321                     resurrectionScale = 0.7;
322                 }
323                 else if(m_resurrectionAnimationCounter > 5)
324                 {
325                     resurrectionScale = 0.85;
326                 }
327                 else if(m_resurrectionAnimationCounter > 4)
328                 {
329                     resurrectionScale = 1;
330                 }
331                 else if(m_resurrectionAnimationCounter > 3)
332                 {
333                     resurrectionScale = 1.05;
334                 }
335                 else if(m_resurrectionAnimationCounter > 2)
336                 {
337                     resurrectionScale = 1.1;
338                 }
339                 else if(m_resurrectionAnimationCounter > 1)
340                 {
341                     resurrectionScale = 1.05;
342                 }
343                 else if(m_resurrectionAnimationCounter > 0)
344                 {
345                     resurrectionScale = 1;
346                 }
347 
348                 m_resurrectionAnimationCounter--;
349                 if(m_resurrectionAnimationCounter == 0)
350                 {
351                     resurrectionScale = 1;
352                     stopAnim();
353                 }
354 
355                 setRenderSize(m_renderSize * resurrectionScale);
356                 transform.translate(-m_itemSizeReal.width() * resurrectionScale / 2.0, -m_itemSizeReal.width() * resurrectionScale / 2.0);
357                 setTransform(transform);
358             }
359         }
360     }
361 }
362 
setDead()363 void PlayerItem::setDead()
364 {
365     stopAnim();
366     setZValue(1);
367     if(m_resurrectionAnimationCounter != 0)
368     {
369         QTransform transform;
370         transform.translate(m_itemSizeSet.width() / 2.0, m_itemSizeSet.height() / 2.0);
371         setRenderSize(m_renderSize);
372         transform.translate(-m_itemSizeReal.width() / 2.0, -m_itemSizeReal.height() / 2.0);
373         setTransform(transform);
374     }
375     if(m_renderer->spriteExists(QStringLiteral("player_death")))
376     {
377         setSpriteKey(QStringLiteral("player_death"));
378     }
379 }
380