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