Initial implementation of the new abstraction architecture.
[tagua.git] / src / animation.cpp
blob36178d5a519a6d797894fff9ea6496e9c21d922d
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));
70 // }
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, piece->pos(), 255, 0)));
263 addPreAnimation(shared_ptr<FadeAnimation>(new FadeAnimation(promoted, promoted->pos(), 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 void FadeAnimation::start() {
325 m_state = Active;
327 m_piece->moveTo(m_to);
328 m_piece->setOpacity(m_fadeFrom);
329 m_piece->show();
332 Animation::State FadeAnimation::animationAdvance(int msec) {
333 switch (m_state) {
334 case Inactive:
335 m_start = msec;
336 start();
337 break;
338 case Aborted:
339 break;
340 case Active:
342 double t = (msec - m_start) / 400.0;
343 Q_ASSERT(t >= 0);
345 if(t >= 1)
346 stop();
347 else {
348 int op = static_cast<int>(m_fadeTo * t + m_fadeFrom * (1 - t));
349 m_piece->setOpacity(op);
352 break;
355 return m_state;
358 void FadeAnimation::stop() {
359 m_piece->setOpacity(m_fadeTo);
360 m_state = Inactive;
363 void FadeAnimation::abort() {
364 m_state = Aborted;
368 //END FadeAnimation
372 //BEGIN GrowAnimation
374 GrowAnimation::GrowAnimation(const SpritePtr& sprite)
375 : ConcreteAnimation(sprite)
376 , m_state(Inactive) { }
378 void GrowAnimation::start() {
379 m_state = Active;
381 m_piece->show();
382 m_piece->setScale(0.0);
385 Animation::State GrowAnimation::animationAdvance(int msec) {
386 switch (m_state) {
387 case Inactive:
388 m_start = msec;
389 start();
390 break;
391 case Aborted:
392 break;
393 case Active:
395 double t = (msec - m_start) / 700.0;
396 Q_ASSERT(t >= 0);
398 if(t >= 1)
399 stop();
400 else {
401 t = sin(t*M_PI/2);
402 m_piece->setScale(t);
405 break;
408 return m_state;
411 void GrowAnimation::stop() {
412 m_piece->setScale(1.0);
413 m_state = Inactive;
416 void GrowAnimation::abort() {
417 m_state = Aborted;
421 //END GrowAnimation
424 //BEGIN ExplodeAnimation
426 ExplodeAnimation::ExplodeAnimation(const SpritePtr& sprite, Random& random)
427 : ConcreteAnimation(sprite)
428 , m_state(Inactive)
429 , m_random(random) { }
431 void ExplodeAnimation::start() {
432 m_state = Active;
434 m_piece->show();
435 m_piece->setupExplosion(m_random);
438 Animation::State ExplodeAnimation::animationAdvance(int msec) {
439 switch (m_state) {
440 case Inactive:
441 m_start = msec;
442 start();
443 break;
444 case Aborted:
445 break;
446 case Active:
448 double t = (msec - m_start) / 700.0;
449 Q_ASSERT(t >= 0);
451 if(t >= 1)
452 stop();
453 else {
454 t = sin(t*M_PI/2);
455 m_piece->setExplosionStep(sin(t*M_PI/2)*0.7);
456 //m_piece->setScale(1.0+t*0.2);
457 m_piece->setOpacity(int(cos(t*M_PI/2)*255));
460 break;
463 return m_state;
466 void ExplodeAnimation::stop() {
467 m_piece->setExplosionStep(0.7);
468 //m_piece->setScale(1.2);
469 m_state = Inactive;
472 void ExplodeAnimation::abort() {
473 m_state = Aborted;
477 //END ExplodeAnimation
479 //BEGIN AnimationGroup
481 AnimationGroup::AnimationGroup(bool persistent)
482 : Animation()
483 , m_active(false)
484 , m_persistent(persistent)
485 , m_chain_abortions(!persistent) {
488 void AnimationGroup::start() {
489 m_active = true;
492 Animation::State AnimationGroup::animationAdvance(int msec) {
493 if (!m_active) {
494 start();
498 if (!pre.empty()) {
499 // advance pre-animations
500 for (Iterator i = pre.begin(); i != pre.end(); ) {
501 State animation_state = (*i)->animationAdvance(msec);
502 switch (animation_state) {
503 case Aborted:
504 #if 0
505 if (m_chain_abortions) {
506 for (Iterator j = pre.begin(); j != pre.end(); ) {
507 (*j)->stop();
508 j = pre.erase(j);
510 post.clear();
511 return Aborted;
513 #endif
514 case Inactive:
515 // the animation ended or has been aborted,
516 // remove it from the list
517 i = pre.erase(i);
518 break;
519 case Active:
520 // handle next animation
521 ++i;
522 break;
526 // if there are still pre-animations, we're done
527 if (!pre.empty())
528 return Active;
530 else {
531 // no pre-animations: handle post animations
532 for (Iterator i = post.begin(); i != post.end(); ) {
533 State animation_state = (*i)->animationAdvance(msec);
534 switch (animation_state) {
535 case Aborted:
536 #if 0
537 if (m_chain_abortions) {
538 return Aborted;
540 #endif
541 case Inactive:
542 // the animation ended or has been aborted,
543 // remove it from the list
544 i = pre.erase(i);
545 break;
546 case Active:
547 // handle next animation
548 ++i;
549 break;
554 // go on animating if there are still animations left
555 // or the group is persistent
556 m_active = m_persistent || !post.empty();
557 return m_active ? Active : Inactive;
560 void AnimationGroup::addPreAnimation(AnimationPtr anim) {
561 if (!anim) return;
563 if (m_active)
564 // cannot dynamically add pre-animations
565 // adding it as a post-animation, instead
566 addPostAnimation(anim);
567 else
568 pre.push_back(anim);
571 void AnimationGroup::addPostAnimation(AnimationPtr anim) {
572 if (!anim) return;
573 post.push_back(anim);
576 void AnimationGroup::stop() {
577 // stop all animations
578 for (Iterator i = pre.begin(); i != pre.end(); ) {
579 (*i)->stop();
580 i = pre.erase(i);
582 for (Iterator i = post.begin(); i != post.end(); ) {
583 (*i)->stop();
584 i = post.erase(i);
587 if (!m_persistent)
588 m_active = false;
591 //END AnimationGroup
593 //BEGIN TeleportAnimation
595 TeleportAnimation::TeleportAnimation(const SpritePtr& sprite,
596 const QPoint& from, const QPoint& to)
597 : AnimationGroup(1.0) {
599 const SpritePtr& copy(sprite->duplicate());
600 copy->show();
602 addPreAnimation(AnimationPtr(new FadeAnimation(copy, from, 255, 0)));
603 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
606 TeleportAnimation::TeleportAnimation(const const SpritePtr&& sprite,
607 const QPoint& to)
608 : AnimationGroup(1.0) {
610 const SpritePtr& copy(sprite->duplicate());
611 copy->show();
613 addPreAnimation(AnimationPtr(new FadeAnimation(copy, copy->pos(), 255, 0)));
614 addPreAnimation(AnimationPtr(new FadeAnimation(sprite, to, 0, 255)));
618 //END TeleportAnimation
620 //BEGIN DelayedAnimationSet
622 DelayedAnimationSet::DelayedAnimationSet(Random& random)
623 : m_random(random)
624 , m_state(Inactive) { }
626 void DelayedAnimationSet::addAnimation(const shared_ptr<Animation>& animation) {
627 if (animation && m_state == Inactive)
628 m_animations.push_back(animation);
631 void DelayedAnimationSet::start() {
632 const int n = m_animations.size();
634 if (n == 0)
635 m_state = Inactive;
636 else {
637 // animation time is proportional to the
638 // number of animations
639 m_time = 100 * n;
641 // one animation starts immediately
642 int immediate = m_random.rand(0, n - 1)();
643 m_events.push_back(Event(immediate, m_start));
645 // generate event times
646 Random::IntegerGenerator event_time = m_random.rand(m_start, m_start + m_time);
647 for (int i = 0; i < n; i++) {
648 if (i == immediate) continue;
649 m_events.push_back(Event(i, event_time()));
652 // sort events
653 sort(m_events.begin(), m_events.end());
655 // set next event
656 m_next_event = m_events.begin();
658 m_state = Active;
662 Animation::State DelayedAnimationSet::animationAdvance(int msec) {
663 switch (m_state) {
664 case Inactive:
665 m_start = msec;
666 start();
667 if (m_state == Inactive) abort();
668 break;
669 case Aborted:
670 break;
671 case Active:
673 // if there are still events left
674 if (m_next_event != m_events.end()) {
676 // if the event period is elapsed
677 if (msec >= m_start + m_time) {
678 // execute all remaining animations
679 while (m_next_event != m_events.end())
680 execute((m_next_event++)->index());
683 else {
684 // if the event time has elapsed
685 while (m_next_event != m_events.end() && msec >= m_next_event->time()) {
686 // run the corresponding animation
687 execute((m_next_event++)->index());
692 // step all active animations
693 // continue animating until m_group is over
694 m_state = m_group.animationAdvance(msec);
697 break;
700 return m_state;
703 void DelayedAnimationSet::execute(int index) {
704 // start animation adding it to m_group
705 m_group.addPostAnimation(m_animations[index]);
707 // remove it from the animation vector
708 m_animations[index] = shared_ptr<Animation>();
711 void DelayedAnimationSet::stop() {
712 m_group.stop();
713 m_animations.clear();
714 m_state = Inactive;
717 void DelayedAnimationSet::abort() {
718 m_state = Aborted;
719 m_animations.clear();
722 //END DelayedAnimationSet