Fixed doxygen header.
[tagua/yd.git] / src / animation.cpp
blob35512da83c4417b5e26350d43b139d0ebc6ff830
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
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, double speed)
39 : ConcreteAnimation(piece)
40 , m_source(piece)
41 , m_target(piece)
42 , m_destination(destination)
43 , m_speed(speed)
44 , m_state(Inactive) {
45 #ifdef ANIMATION_DEBUG
46 cout << "creating animation " << this << " for piece " << piece.get() << endl;
47 #endif
50 MovementAnimation::~MovementAnimation() {
51 #ifdef ANIMATION_DEBUG
52 cout << "destroying animation " << this << " for piece " << m_piece.get() << endl;
53 if (m_piece->movementAnimation().lock())
54 cout << " *********** BUG **************" << endl;
55 #endif
56 // m_piece->setMovementAnimation(0);
59 void MovementAnimation::setTarget(const SpritePtr& target) {
60 m_target = target;
63 void MovementAnimation::setSource(const SpritePtr& source) {
64 m_source = source;
67 boost::shared_ptr<Movement>
68 MovementAnimation::createMovement(const QPoint& from, const QPoint& to) const {
69 return boost::shared_ptr<Movement>(new SigmoidalMovement<LinearMovement>(from, to));
72 void MovementAnimation::start() {
73 #ifdef ANIMATION_DEBUG
74 cout << "starting movement animation " << this
75 << " on piece " << m_piece.get() << endl;
76 #endif
79 if (shared_ptr<Animation> animation = m_source->movementAnimation().lock()) {
80 #ifdef ANIMATION_DEBUG
81 cout << "aborting movement animation " << animation
82 << " on piece " << m_source.get() << endl;
83 #endif
84 animation->abort();
88 m_source->setMovementAnimation(shared_ptr<Animation>());
89 m_target->setMovementAnimation(shared_from_this());
91 m_piece->raise();
92 m_movement = createMovement(m_piece->pos(), m_destination);
94 // The total time of the animation is proportional to the square root
95 // of the distance and to the inverse of the speed factor.
96 double distance = Point(m_destination - m_piece->pos()).norm();
98 Q_ASSERT(m_speed > 0.0);
99 m_time = 35 * sqrt(distance) / m_speed;
100 m_state = m_time > 0 ? Active : Inactive;
103 Animation::State MovementAnimation::animationAdvance(int msec) {
104 switch (m_state) {
105 case Inactive:
106 m_start = msec;
107 start();
108 if (m_state == Inactive) abort();
109 break;
110 case Aborted:
111 break;
112 case Active:
114 // reparameterize the time variable in [0,1]
115 Q_ASSERT(m_time > 0);
116 double t = (msec - m_start) / m_time;
117 Q_ASSERT(t >= 0);
119 #ifdef ANIMATION_DEBUG
120 cout << "active: t = " << t << endl;
121 #endif
123 if (t >= 1)
124 stop();
125 else {
126 QPoint to = m_movement->pos(t);
127 m_piece->moveTo(to);
128 m_piece->setRotation(m_movement->rotation(t));
132 break;
135 #ifdef ANIMATION_DEBUG
136 cout << "advance (" << this << ") state = " << m_state << endl;
137 #endif
138 return m_state;
141 void MovementAnimation::stop() {
142 #ifdef ANIMATION_DEBUG
143 cout << "stopping animation " << this
144 << " on piece " << m_piece.get() << endl;
145 #endif
146 m_piece->moveTo(m_destination);
147 m_piece->setRotation(0);
148 abort();
149 m_state = Inactive;
152 void MovementAnimation::abort() {
153 #ifdef ANIMATION_DEBUG
154 cout << "aborting animation " << this
155 << " on piece " << m_piece.get() << endl;
156 #endif
157 m_state = Aborted;
158 m_target->setMovementAnimation(shared_ptr<MovementAnimation>());
161 //END MovementAnimation
163 //BEGIN KnightMovementAnimation
165 KnightMovementAnimation::KnightMovementAnimation(const SpritePtr& piece, const QPoint& destination,
166 bool rotate, double speed)
167 : MovementAnimation(piece, destination, speed)
168 , m_rotate(rotate) {
171 boost::shared_ptr<Movement>
172 KnightMovementAnimation::createMovement(const QPoint& from, const QPoint& to) const {
173 return boost::shared_ptr<Movement>(new SigmoidalMovement<LMovement>(from, to, m_rotate));
176 //END KnightMovementAnimation
178 //BEGIN OneShotAnimation
180 OneShotAnimation::OneShotAnimation(const SpritePtr& piece)
181 : ConcreteAnimation(piece) { }
183 Animation::State OneShotAnimation::animationAdvance(int) {
184 shoot();
185 return Inactive;
188 //END OneShotAnimation
190 //BEGIN InstantAnimation
192 InstantAnimation::InstantAnimation(const SpritePtr& piece, const QPoint& destination)
193 : OneShotAnimation(piece)
194 , m_destination(destination) { }
196 void InstantAnimation::shoot() {
197 m_piece->moveTo(m_destination);
199 if (shared_ptr<Animation> animation = m_piece->movementAnimation().lock()) {
200 animation->abort();
201 m_piece->setMovementAnimation(shared_ptr<Animation>());
205 //END InstantAnimation
207 //BEGIN CaptureAnimation
209 CaptureAnimation::CaptureAnimation(const SpritePtr& piece)
210 : OneShotAnimation(piece) { }
212 void CaptureAnimation::shoot() {
213 m_piece->hide();
215 if (shared_ptr<Animation> animation = m_piece->movementAnimation().lock()) {
216 animation->abort();
217 m_piece->setMovementAnimation(shared_ptr<Animation>());
222 //END CaptureAnimation
224 //BEGIN DropAnimation
226 DropAnimation::DropAnimation(const SpritePtr& piece)
227 : OneShotAnimation(piece)
228 , m_valid_position(false) { }
230 DropAnimation::DropAnimation(const SpritePtr& piece, const QPoint& pos)
231 : OneShotAnimation(piece)
232 , m_valid_position(true)
233 , m_position(pos) { }
235 void DropAnimation::shoot() {
236 if (m_valid_position)
237 m_piece->moveTo(m_position);
238 m_piece->show();
241 //END DropAnimation
243 //BEGIN PromotionAnimation
245 PromotionAnimation::PromotionAnimation(const SpritePtr& piece,
246 const SpritePtr& promoted)
247 : OneShotAnimation(piece)
248 , m_promoted(promoted) { }
250 void PromotionAnimation::shoot() {
251 m_piece->hide();
252 m_promoted->show();
255 //END PromotionAnimation
257 //BEGIN CrossFadingAnimation
259 CrossFadingAnimation::CrossFadingAnimation(const SpritePtr& piece,
260 const SpritePtr& promoted)
261 : m_piece(piece) {
262 addPreAnimation(shared_ptr<FadeAnimation>(new FadeAnimation(piece, 255, 0)));
263 addPreAnimation(shared_ptr<FadeAnimation>(new FadeAnimation(promoted, 0, 255)));
266 void CrossFadingAnimation::start() {
267 AnimationGroup::start();
270 //END CrossFadingAnimation
274 //BEGIN DelayAnimation
276 DelayAnimation::DelayAnimation(int secs)
277 : Animation()
278 , m_state(Inactive)
279 , m_msecs(secs) { }
281 void DelayAnimation::start() {
282 m_state = Active;
285 Animation::State DelayAnimation::animationAdvance(int msec) {
286 switch (m_state) {
287 case Inactive:
288 m_start = msec;
289 start();
290 break;
291 case Aborted:
292 break;
293 case Active:
294 if(msec > m_start + m_msecs)
295 stop();
296 break;
299 return m_state;
302 void DelayAnimation::stop() {
303 m_state = Inactive;
306 void DelayAnimation::abort() {
307 m_state = Aborted;
311 //END DelayAnimation
314 //BEGIN FadeAnimation
316 FadeAnimation::FadeAnimation(const SpritePtr& sprite, const QPoint& to,
317 int fadeFrom, int fadeTo)
318 : ConcreteAnimation(sprite)
319 , m_fadeFrom(fadeFrom)
320 , m_fadeTo(fadeTo)
321 , m_to(to)
322 , m_state(Inactive) { }
324 FadeAnimation::FadeAnimation(const SpritePtr& sprite, int fadeFrom, int fadeTo)
325 : ConcreteAnimation(sprite)
326 , m_fadeFrom(fadeFrom)
327 , m_fadeTo(fadeTo)
328 , m_to(Point::invalid())
329 , m_state(Inactive) { }
331 void FadeAnimation::start() {
332 m_state = Active;
334 if (m_to != Point::invalid()) {
335 m_piece->moveTo(m_to);
338 m_piece->setOpacity(m_fadeFrom);
339 m_piece->show();
342 Animation::State FadeAnimation::animationAdvance(int msec) {
343 switch (m_state) {
344 case Inactive:
345 m_start = msec;
346 start();
347 break;
348 case Aborted:
349 break;
350 case Active:
352 double t = (msec - m_start) / 400.0;
353 Q_ASSERT(t >= 0);
355 if(t >= 1)
356 stop();
357 else {
358 int op = static_cast<int>(m_fadeTo * t + m_fadeFrom * (1 - t));
359 m_piece->setOpacity(op);
362 break;
365 return m_state;
368 void FadeAnimation::stop() {
369 m_piece->setOpacity(m_fadeTo);
370 m_state = Inactive;
373 void FadeAnimation::abort() {
374 m_state = Aborted;
378 //END FadeAnimation
382 //BEGIN GrowAnimation
384 GrowAnimation::GrowAnimation(const SpritePtr& sprite)
385 : ConcreteAnimation(sprite)
386 , m_state(Inactive) { }
388 void GrowAnimation::start() {
389 m_state = Active;
391 m_piece->show();
392 m_piece->setScale(0.0);
395 Animation::State GrowAnimation::animationAdvance(int msec) {
396 switch (m_state) {
397 case Inactive:
398 m_start = msec;
399 start();
400 break;
401 case Aborted:
402 break;
403 case Active:
405 double t = (msec - m_start) / 700.0;
406 Q_ASSERT(t >= 0);
408 if(t >= 1)
409 stop();
410 else {
411 t = sin(t*M_PI/2);
412 m_piece->setScale(t);
415 break;
418 return m_state;
421 void GrowAnimation::stop() {
422 m_piece->setScale(1.0);
423 m_state = Inactive;
426 void GrowAnimation::abort() {
427 m_state = Aborted;
431 //END GrowAnimation
434 //BEGIN ExplodeAnimation
436 ExplodeAnimation::ExplodeAnimation(const SpritePtr& sprite, Random& random)
437 : ConcreteAnimation(sprite)
438 , m_state(Inactive)
439 , m_random(random) { }
441 void ExplodeAnimation::start() {
442 m_state = Active;
444 m_piece->show();
445 m_piece->setupExplosion(m_random);
448 Animation::State ExplodeAnimation::animationAdvance(int msec) {
449 switch (m_state) {
450 case Inactive:
451 m_start = msec;
452 start();
453 break;
454 case Aborted:
455 break;
456 case Active:
458 double t = (msec - m_start) / 700.0;
459 Q_ASSERT(t >= 0);
461 if(t >= 1)
462 stop();
463 else {
464 t = sin(t*M_PI/2);
465 m_piece->setExplosionStep(sin(t*M_PI/2)*0.7);
466 //m_piece->setScale(1.0+t*0.2);
467 m_piece->setOpacity(int(cos(t*M_PI/2)*255));
470 break;
473 return m_state;
476 void ExplodeAnimation::stop() {
477 m_piece->setExplosionStep(0.7);
478 //m_piece->setScale(1.2);
479 m_state = Inactive;
482 void ExplodeAnimation::abort() {
483 m_state = Aborted;
487 //END ExplodeAnimation
489 //BEGIN AnimationGroup
491 AnimationGroup::AnimationGroup(bool persistent)
492 : Animation()
493 , m_active(false)
494 , m_persistent(persistent)
495 , m_chain_abortions(!persistent) {
498 void AnimationGroup::start() {
499 m_active = true;
502 Animation::State AnimationGroup::animationAdvance(int msec) {
503 if (!m_active) {
504 start();
508 if (!pre.empty()) {
509 // advance pre-animations
510 for (Iterator i = pre.begin(); i != pre.end(); ) {
511 State animation_state = (*i)->animationAdvance(msec);
512 switch (animation_state) {
513 case Aborted:
514 #if 0
515 if (m_chain_abortions) {
516 for (Iterator j = pre.begin(); j != pre.end(); ) {
517 (*j)->stop();
518 j = pre.erase(j);
520 post.clear();
521 return Aborted;
523 #endif
524 case Inactive:
525 // the animation ended or has been aborted,
526 // remove it from the list
527 i = pre.erase(i);
528 break;
529 case Active:
530 // handle next animation
531 ++i;
532 break;
536 // if there are still pre-animations, we're done
537 if (!pre.empty())
538 return Active;
540 else {
541 // no pre-animations: handle post animations
542 for (Iterator i = post.begin(); i != post.end(); ) {
543 State animation_state = (*i)->animationAdvance(msec);
544 switch (animation_state) {
545 case Aborted:
546 #if 0
547 if (m_chain_abortions) {
548 return Aborted;
550 #endif
551 case Inactive:
552 // the animation ended or has been aborted,
553 // remove it from the list
554 i = pre.erase(i);
555 break;
556 case Active:
557 // handle next animation
558 ++i;
559 break;
564 // go on animating if there are still animations left
565 // or the group is persistent
566 m_active = m_persistent || !post.empty();
567 return m_active ? Active : Inactive;
570 void AnimationGroup::addPreAnimation(AnimationPtr anim) {
571 if (!anim) return;
573 if (m_active)
574 // cannot dynamically add pre-animations
575 // adding it as a post-animation, instead
576 addPostAnimation(anim);
577 else
578 pre.push_back(anim);
581 void AnimationGroup::addPostAnimation(AnimationPtr anim) {
582 if (!anim) return;
583 post.push_back(anim);
586 void AnimationGroup::stop() {
587 // stop all animations
588 for (Iterator i = pre.begin(); i != pre.end(); ) {
589 (*i)->stop();
590 i = pre.erase(i);
592 for (Iterator i = post.begin(); i != post.end(); ) {
593 (*i)->stop();
594 i = post.erase(i);
597 if (!m_persistent)
598 m_active = false;
601 //END AnimationGroup
603 //BEGIN TeleportAnimation
605 TeleportAnimation::TeleportAnimation(const SpritePtr& sprite,
606 const QPoint& from, const QPoint& to)
607 : AnimationGroup(1.0) {
609 SpritePtr copy(sprite->duplicate());
610 copy->show();
612 addPreAnimation(AnimationPtr(new FadeAnimation(copy, from, 255, 0)));
613 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
616 TeleportAnimation::TeleportAnimation(const SpritePtr& sprite,
617 const QPoint& to)
618 : AnimationGroup(1.0) {
620 SpritePtr copy(sprite->duplicate());
621 copy->show();
623 addPreAnimation(AnimationPtr(new FadeAnimation(copy, copy->pos(), 255, 0)));
624 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
628 //END TeleportAnimation
630 //BEGIN DelayedAnimationSet
632 DelayedAnimationSet::DelayedAnimationSet(Random& random)
633 : m_random(random)
634 , m_state(Inactive) { }
636 void DelayedAnimationSet::addAnimation(const shared_ptr<Animation>& animation) {
637 if (animation && m_state == Inactive)
638 m_animations.push_back(animation);
641 void DelayedAnimationSet::start() {
642 const int n = m_animations.size();
644 if (n == 0)
645 m_state = Inactive;
646 else {
647 // animation time is proportional to the
648 // number of animations
649 m_time = 100 * n;
651 // one animation starts immediately
652 int immediate = m_random.rand(0, n - 1)();
653 m_events.push_back(Event(immediate, m_start));
655 // generate event times
656 Random::IntegerGenerator event_time = m_random.rand(m_start, m_start + m_time);
657 for (int i = 0; i < n; i++) {
658 if (i == immediate) continue;
659 m_events.push_back(Event(i, event_time()));
662 // sort events
663 sort(m_events.begin(), m_events.end());
665 // set next event
666 m_next_event = m_events.begin();
668 m_state = Active;
672 Animation::State DelayedAnimationSet::animationAdvance(int msec) {
673 switch (m_state) {
674 case Inactive:
675 m_start = msec;
676 start();
677 if (m_state == Inactive) abort();
678 break;
679 case Aborted:
680 break;
681 case Active:
683 // if there are still events left
684 if (m_next_event != m_events.end()) {
686 // if the event period is elapsed
687 if (msec >= m_start + m_time) {
688 // execute all remaining animations
689 while (m_next_event != m_events.end())
690 execute((m_next_event++)->index());
693 else {
694 // if the event time has elapsed
695 while (m_next_event != m_events.end() && msec >= m_next_event->time()) {
696 // run the corresponding animation
697 execute((m_next_event++)->index());
702 // step all active animations
703 // continue animating until m_group is over
704 m_state = m_group.animationAdvance(msec);
707 break;
710 return m_state;
713 void DelayedAnimationSet::execute(int index) {
714 // start animation adding it to m_group
715 m_group.addPostAnimation(m_animations[index]);
717 // remove it from the animation vector
718 m_animations[index] = shared_ptr<Animation>();
721 void DelayedAnimationSet::stop() {
722 m_group.stop();
723 m_animations.clear();
724 m_state = Inactive;
727 void DelayedAnimationSet::abort() {
728 m_state = Aborted;
729 m_animations.clear();
732 //END DelayedAnimationSet