Replaced all std::cout with kDebug.
[tagua/yd.git] / src / animation.cpp
blobbbcb62e86ee6c4fd291c573ef5910b6bb4e5d1b3
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #include <math.h>
12 #include <iostream>
13 #include <functional>
14 #include <algorithm>
15 #include <boost/scoped_ptr.hpp>
17 #include "common.h"
18 #include "animation.h"
19 #include "point.h"
20 #include "sprite.h"
21 #include "movement.h"
23 using namespace std;
24 using namespace boost;
26 #undef ANIMATION_DEBUG
28 //BEGIN ConcreteAnimation
30 ConcreteAnimation::ConcreteAnimation(const SpritePtr& piece)
31 : m_piece(piece) { }
33 //END ConcreteAnimation
35 //BEGIN MovementAnimation
37 MovementAnimation::MovementAnimation(const SpritePtr& piece,
38 const QPoint& destination,
39 bool rotate, double speed)
40 : ConcreteAnimation(piece)
41 , m_source(piece)
42 , m_target(piece)
43 , m_destination(destination)
44 , m_speed(speed)
45 , m_state(Inactive)
46 , m_rotate(rotate) {
47 #ifdef ANIMATION_DEBUG
48 kDebug() << "creating animation " << this << " for piece " << piece.get() << endl;
49 #endif
52 MovementAnimation::~MovementAnimation() {
53 #ifdef ANIMATION_DEBUG
54 kDebug() << "destroying animation " << this << " for piece " << m_piece.get() << endl;
55 if (m_piece->movementAnimation().lock())
56 kDebug() << " *********** BUG **************" << endl;
57 #endif
58 // m_piece->setMovementAnimation(0);
61 void MovementAnimation::setTarget(const SpritePtr& target) {
62 m_target = target;
65 void MovementAnimation::setSource(const SpritePtr& source) {
66 m_source = source;
69 boost::shared_ptr<Movement>
70 MovementAnimation::createMovement(const QPoint& from, const QPoint& to) const {
71 return boost::shared_ptr<Movement>(new SigmoidalMovement<LinearMovement>(from, to, m_rotate));
74 void MovementAnimation::start() {
75 #ifdef ANIMATION_DEBUG
76 kDebug() << "starting movement animation " << this
77 << " on piece " << m_piece.get() << endl;
78 #endif
81 if (shared_ptr<Animation> animation = m_source->movementAnimation().lock()) {
82 #ifdef ANIMATION_DEBUG
83 kDebug() << "aborting movement animation " << animation
84 << " on piece " << m_source.get() << endl;
85 #endif
86 animation->abort();
90 m_source->setMovementAnimation(shared_ptr<Animation>());
91 m_target->setMovementAnimation(shared_from_this());
93 m_piece->raise();
94 m_movement = createMovement(m_piece->pos(), m_destination);
96 // The total time of the animation is proportional to the square root
97 // of the distance and to the inverse of the speed factor.
98 double distance = Point(m_destination - m_piece->pos()).norm();
100 Q_ASSERT(m_speed > 0.0);
101 m_time = 35 * sqrt(distance) / m_speed;
102 m_state = m_time > 0 ? Active : Inactive;
105 Animation::State MovementAnimation::animationAdvance(int msec) {
106 switch (m_state) {
107 case Inactive:
108 m_start = msec;
109 start();
110 if (m_state == Inactive) abort();
111 break;
112 case Aborted:
113 break;
114 case Active:
116 // reparameterize the time variable in [0,1]
117 Q_ASSERT(m_time > 0);
118 double t = (msec - m_start) / m_time;
119 Q_ASSERT(t >= 0);
121 #ifdef ANIMATION_DEBUG
122 kDebug() << "active: t = " << t << endl;
123 #endif
125 if (t >= 1)
126 stop();
127 else {
128 QPoint to = m_movement->pos(t);
129 m_piece->moveTo(to);
130 m_piece->setRotation(m_movement->rotation(t));
134 break;
137 #ifdef ANIMATION_DEBUG
138 kDebug() << "advance (" << this << ") state = " << m_state << endl;
139 #endif
140 return m_state;
143 void MovementAnimation::stop() {
144 #ifdef ANIMATION_DEBUG
145 kDebug() << "stopping animation " << this
146 << " on piece " << m_piece.get() << endl;
147 #endif
148 m_piece->moveTo(m_destination);
149 m_piece->setRotation(0);
150 abort();
151 m_state = Inactive;
154 void MovementAnimation::abort() {
155 #ifdef ANIMATION_DEBUG
156 kDebug() << "aborting animation " << this
157 << " on piece " << m_piece.get() << endl;
158 #endif
159 m_state = Aborted;
160 m_target->setMovementAnimation(shared_ptr<MovementAnimation>());
163 //END MovementAnimation
165 //BEGIN KnightMovementAnimation
167 KnightMovementAnimation::KnightMovementAnimation(const SpritePtr& piece, const QPoint& destination,
168 bool rotate, double speed)
169 : MovementAnimation(piece, destination, rotate, speed){
172 boost::shared_ptr<Movement>
173 KnightMovementAnimation::createMovement(const QPoint& from, const QPoint& to) const {
174 return boost::shared_ptr<Movement>(new SigmoidalMovement<LMovement>(from, to, m_rotate));
177 //END KnightMovementAnimation
179 //BEGIN OneShotAnimation
181 OneShotAnimation::OneShotAnimation(const SpritePtr& piece)
182 : ConcreteAnimation(piece) { }
184 Animation::State OneShotAnimation::animationAdvance(int) {
185 shoot();
186 return Inactive;
189 //END OneShotAnimation
191 //BEGIN InstantAnimation
193 InstantAnimation::InstantAnimation(const SpritePtr& piece, const QPoint& destination)
194 : OneShotAnimation(piece)
195 , m_destination(destination) { }
197 void InstantAnimation::shoot() {
198 m_piece->moveTo(m_destination);
200 if (shared_ptr<Animation> animation = m_piece->movementAnimation().lock()) {
201 animation->abort();
202 m_piece->setMovementAnimation(shared_ptr<Animation>());
206 //END InstantAnimation
208 //BEGIN CaptureAnimation
210 CaptureAnimation::CaptureAnimation(const SpritePtr& piece)
211 : OneShotAnimation(piece) { }
213 void CaptureAnimation::shoot() {
214 m_piece->hide();
216 if (shared_ptr<Animation> animation = m_piece->movementAnimation().lock()) {
217 animation->abort();
218 m_piece->setMovementAnimation(shared_ptr<Animation>());
223 //END CaptureAnimation
225 //BEGIN DropAnimation
227 DropAnimation::DropAnimation(const SpritePtr& piece)
228 : OneShotAnimation(piece)
229 , m_valid_position(false) { }
231 DropAnimation::DropAnimation(const SpritePtr& piece, const QPoint& pos)
232 : OneShotAnimation(piece)
233 , m_valid_position(true)
234 , m_position(pos) { }
236 void DropAnimation::shoot() {
237 if (m_valid_position)
238 m_piece->moveTo(m_position);
239 m_piece->show();
242 //END DropAnimation
244 //BEGIN PromotionAnimation
246 PromotionAnimation::PromotionAnimation(const SpritePtr& piece,
247 const SpritePtr& promoted)
248 : OneShotAnimation(piece)
249 , m_promoted(promoted) { }
251 void PromotionAnimation::shoot() {
252 m_piece->hide();
253 m_promoted->show();
256 //END PromotionAnimation
258 //BEGIN CrossFadingAnimation
260 CrossFadingAnimation::CrossFadingAnimation(const SpritePtr& piece,
261 const SpritePtr& promoted)
262 : m_piece(piece) {
263 addPreAnimation(shared_ptr<FadeAnimation>(new FadeAnimation(piece, 255, 0)));
264 addPreAnimation(shared_ptr<FadeAnimation>(new FadeAnimation(promoted, 0, 255)));
267 void CrossFadingAnimation::start() {
268 AnimationGroup::start();
271 //END CrossFadingAnimation
275 //BEGIN DelayAnimation
277 DelayAnimation::DelayAnimation(int secs)
278 : Animation()
279 , m_state(Inactive)
280 , m_msecs(secs) { }
282 void DelayAnimation::start() {
283 m_state = Active;
286 Animation::State DelayAnimation::animationAdvance(int msec) {
287 switch (m_state) {
288 case Inactive:
289 m_start = msec;
290 start();
291 break;
292 case Aborted:
293 break;
294 case Active:
295 if(msec > m_start + m_msecs)
296 stop();
297 break;
300 return m_state;
303 void DelayAnimation::stop() {
304 m_state = Inactive;
307 void DelayAnimation::abort() {
308 m_state = Aborted;
312 //END DelayAnimation
315 //BEGIN FadeAnimation
317 FadeAnimation::FadeAnimation(const SpritePtr& sprite, const QPoint& to,
318 int fadeFrom, int fadeTo)
319 : ConcreteAnimation(sprite)
320 , m_fadeFrom(fadeFrom)
321 , m_fadeTo(fadeTo)
322 , m_to(to)
323 , m_state(Inactive) { }
325 FadeAnimation::FadeAnimation(const SpritePtr& sprite, int fadeFrom, int fadeTo)
326 : ConcreteAnimation(sprite)
327 , m_fadeFrom(fadeFrom)
328 , m_fadeTo(fadeTo)
329 , m_to(Point::invalid())
330 , m_state(Inactive) { }
332 void FadeAnimation::start() {
333 m_state = Active;
335 if (m_to != Point::invalid()) {
336 m_piece->moveTo(m_to);
339 m_piece->setOpacity(m_fadeFrom);
340 m_piece->show();
343 Animation::State FadeAnimation::animationAdvance(int msec) {
344 switch (m_state) {
345 case Inactive:
346 m_start = msec;
347 start();
348 break;
349 case Aborted:
350 break;
351 case Active:
353 double t = (msec - m_start) / 400.0;
354 Q_ASSERT(t >= 0);
356 if(t >= 1)
357 stop();
358 else {
359 int op = static_cast<int>(m_fadeTo * t + m_fadeFrom * (1 - t));
360 m_piece->setOpacity(op);
363 break;
366 return m_state;
369 void FadeAnimation::stop() {
370 m_piece->setOpacity(m_fadeTo);
371 m_state = Inactive;
374 void FadeAnimation::abort() {
375 m_state = Aborted;
379 //END FadeAnimation
383 //BEGIN GrowAnimation
385 GrowAnimation::GrowAnimation(const SpritePtr& sprite)
386 : ConcreteAnimation(sprite)
387 , m_state(Inactive) { }
389 void GrowAnimation::start() {
390 m_state = Active;
392 m_piece->show();
393 m_piece->setScale(0.0);
396 Animation::State GrowAnimation::animationAdvance(int msec) {
397 switch (m_state) {
398 case Inactive:
399 m_start = msec;
400 start();
401 break;
402 case Aborted:
403 break;
404 case Active:
406 double t = (msec - m_start) / 700.0;
407 Q_ASSERT(t >= 0);
409 if(t >= 1)
410 stop();
411 else {
412 t = sin(t*M_PI/2);
413 m_piece->setScale(t);
416 break;
419 return m_state;
422 void GrowAnimation::stop() {
423 m_piece->setScale(1.0);
424 m_state = Inactive;
427 void GrowAnimation::abort() {
428 m_state = Aborted;
432 //END GrowAnimation
435 //BEGIN ExplodeAnimation
437 ExplodeAnimation::ExplodeAnimation(const SpritePtr& sprite, Random& random)
438 : ConcreteAnimation(sprite)
439 , m_state(Inactive)
440 , m_random(random) { }
442 void ExplodeAnimation::start() {
443 m_state = Active;
445 m_piece->show();
446 m_piece->setupExplosion(m_random);
449 Animation::State ExplodeAnimation::animationAdvance(int msec) {
450 switch (m_state) {
451 case Inactive:
452 m_start = msec;
453 start();
454 break;
455 case Aborted:
456 break;
457 case Active:
459 double t = (msec - m_start) / 700.0;
460 Q_ASSERT(t >= 0);
462 if(t >= 1)
463 stop();
464 else {
465 t = sin(t*M_PI/2);
466 m_piece->setExplosionStep(sin(t*M_PI/2)*0.7);
467 //m_piece->setScale(1.0+t*0.2);
468 m_piece->setOpacity(int(cos(t*M_PI/2)*255));
471 break;
474 return m_state;
477 void ExplodeAnimation::stop() {
478 m_piece->setExplosionStep(0.7);
479 //m_piece->setScale(1.2);
480 m_state = Inactive;
483 void ExplodeAnimation::abort() {
484 m_state = Aborted;
488 //END ExplodeAnimation
490 //BEGIN AnimationGroup
492 AnimationGroup::AnimationGroup(bool persistent)
493 : Animation()
494 , m_active(false)
495 , m_persistent(persistent)
496 , m_chain_abortions(!persistent) {
499 void AnimationGroup::start() {
500 m_active = true;
503 bool AnimationGroup::empty() const {
504 return pre.empty() && post.empty();
507 Animation::State AnimationGroup::animationAdvance(int msec) {
508 if (!m_active) {
509 start();
513 if (!pre.empty()) {
514 // advance pre-animations
515 for (Iterator i = pre.begin(); i != pre.end(); ) {
516 State animation_state = (*i)->animationAdvance(msec);
517 switch (animation_state) {
518 case Aborted:
519 #if 0
520 if (m_chain_abortions) {
521 for (Iterator j = pre.begin(); j != pre.end(); ) {
522 (*j)->stop();
523 j = pre.erase(j);
525 post.clear();
526 return Aborted;
528 #endif
529 case Inactive:
530 // the animation ended or has been aborted,
531 // remove it from the list
532 i = pre.erase(i);
533 break;
534 case Active:
535 // handle next animation
536 ++i;
537 break;
541 // if there are still pre-animations, we're done
542 if (!pre.empty())
543 return Active;
545 else {
546 // no pre-animations: handle post animations
547 for (Iterator i = post.begin(); i != post.end(); ) {
548 State animation_state = (*i)->animationAdvance(msec);
549 switch (animation_state) {
550 case Aborted:
551 #if 0
552 if (m_chain_abortions) {
553 return Aborted;
555 #endif
556 case Inactive:
557 // the animation ended or has been aborted,
558 // remove it from the list
559 i = pre.erase(i);
560 break;
561 case Active:
562 // handle next animation
563 ++i;
564 break;
569 // go on animating if there are still animations left
570 // or the group is persistent
571 m_active = m_persistent || !post.empty();
572 return m_active ? Active : Inactive;
575 void AnimationGroup::addPreAnimation(AnimationPtr anim) {
576 if (!anim) return;
578 if (m_active)
579 // cannot dynamically add pre-animations
580 // adding it as a post-animation, instead
581 addPostAnimation(anim);
582 else
583 pre.push_back(anim);
586 void AnimationGroup::addPostAnimation(AnimationPtr anim) {
587 if (!anim) return;
588 post.push_back(anim);
591 void AnimationGroup::stop() {
592 // stop all animations
593 for (Iterator i = pre.begin(); i != pre.end(); ) {
594 (*i)->stop();
595 i = pre.erase(i);
597 for (Iterator i = post.begin(); i != post.end(); ) {
598 (*i)->stop();
599 i = post.erase(i);
602 if (!m_persistent)
603 m_active = false;
606 //END AnimationGroup
608 //BEGIN TeleportAnimation
610 TeleportAnimation::TeleportAnimation(const SpritePtr& sprite,
611 const QPoint& from, const QPoint& to)
612 : AnimationGroup(1.0) {
614 SpritePtr copy(sprite->duplicate());
615 copy->show();
617 addPreAnimation(AnimationPtr(new FadeAnimation(copy, from, 255, 0)));
618 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
621 TeleportAnimation::TeleportAnimation(const SpritePtr& sprite,
622 const QPoint& to)
623 : AnimationGroup(1.0) {
625 SpritePtr copy(sprite->duplicate());
626 copy->show();
628 addPreAnimation(AnimationPtr(new FadeAnimation(copy, copy->pos(), 255, 0)));
629 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
633 //END TeleportAnimation
635 //BEGIN DelayedAnimationSet
637 DelayedAnimationSet::DelayedAnimationSet(Random& random)
638 : m_random(random)
639 , m_state(Inactive) { }
641 void DelayedAnimationSet::addAnimation(const shared_ptr<Animation>& animation) {
642 if (animation && m_state == Inactive)
643 m_animations.push_back(animation);
646 void DelayedAnimationSet::start() {
647 const int n = m_animations.size();
649 if (n == 0)
650 m_state = Inactive;
651 else {
652 // animation time is proportional to the
653 // number of animations
654 m_time = 100 * n;
656 // one animation starts immediately
657 int immediate = m_random.rand(0, n - 1)();
658 m_events.push_back(Event(immediate, m_start));
660 // generate event times
661 Random::IntegerGenerator event_time = m_random.rand(m_start, m_start + m_time);
662 for (int i = 0; i < n; i++) {
663 if (i == immediate) continue;
664 m_events.push_back(Event(i, event_time()));
667 // sort events
668 sort(m_events.begin(), m_events.end());
670 // set next event
671 m_next_event = m_events.begin();
673 m_state = Active;
677 Animation::State DelayedAnimationSet::animationAdvance(int msec) {
678 switch (m_state) {
679 case Inactive:
680 m_start = msec;
681 start();
682 if (m_state == Inactive) abort();
683 break;
684 case Aborted:
685 break;
686 case Active:
688 // if there are still events left
689 if (m_next_event != m_events.end()) {
691 // if the event period is elapsed
692 if (msec >= m_start + m_time) {
693 // execute all remaining animations
694 while (m_next_event != m_events.end())
695 execute((m_next_event++)->index());
698 else {
699 // if the event time has elapsed
700 while (m_next_event != m_events.end() && msec >= m_next_event->time()) {
701 // run the corresponding animation
702 execute((m_next_event++)->index());
707 // step all active animations
708 // continue animating until m_group is over
709 m_state = m_group.animationAdvance(msec);
712 break;
715 return m_state;
718 void DelayedAnimationSet::execute(int index) {
719 // start animation adding it to m_group
720 m_group.addPostAnimation(m_animations[index]);
722 // remove it from the animation vector
723 m_animations[index] = shared_ptr<Animation>();
726 void DelayedAnimationSet::stop() {
727 m_group.stop();
728 m_animations.clear();
729 m_state = Inactive;
732 void DelayedAnimationSet::abort() {
733 m_state = Aborted;
734 m_animations.clear();
737 //END DelayedAnimationSet