Index: code/trunk/src/orxonox/objects/controllers/PongAI.cc
===================================================================
--- code/trunk/src/orxonox/objects/controllers/PongAI.cc	(revision 2857)
+++ code/trunk/src/orxonox/objects/controllers/PongAI.cc	(revision 2860)
@@ -34,4 +34,5 @@
 #include "objects/worldentities/ControllableEntity.h"
 #include "objects/worldentities/PongBall.h"
+#include "tools/Timer.h"
 
 namespace orxonox
@@ -39,4 +40,6 @@
     CreateUnloadableFactory(PongAI);
 
+    const static float MAX_REACTION_TIME = 0.4;
+
     PongAI::PongAI(BaseObject* creator) : Controller(creator)
     {
@@ -44,9 +47,23 @@
 
         this->ball_ = 0;
+        this->ballDirection_ = Vector2::ZERO;
+        this->ballEndPosition_ = 0;
         this->randomOffset_ = 0;
         this->relHysteresisOffset_ = 0.02;
         this->strength_ = 0.5;
+        this->movement_ = 0;
 
         this->setConfigValues();
+
+//        this->randomOffsetTimer_.setTimer(MAX_REACTION_TIME * (1 - this->strength_), false, this, createExecutor(createFunctor(&PongAI::calculateRandomOffset)));
+//        this->ballEndPositionTimer_.setTimer(MAX_REACTION_TIME * (1 - this->strength_), false, this, createExecutor(createFunctor(&PongAI::calculateBallEndPosition)));
+//        this->randomOffsetTimer_.stopTimer();
+//        this->ballEndPositionTimer_.stopTimer();
+    }
+
+    PongAI::~PongAI()
+    {
+        for (std::list<std::pair<Timer<PongAI>*, char> >::iterator it = this->reactionTimers_.begin(); it != this->reactionTimers_.end(); ++it)
+            delete (*it).first;
     }
 
@@ -61,37 +78,65 @@
             return;
 
-        ControllableEntity* bat = this->getControllableEntity();
-
-        Vector3 mypos = bat->getPosition();
+        Vector3 mypos = this->getControllableEntity()->getPosition();
         Vector3 ballpos = this->ball_->getPosition();
         Vector3 ballvel = this->ball_->getVelocity();
         float hysteresisOffset = this->relHysteresisOffset_ * this->ball_->getFieldDimension().y;
 
+        char move = 0;
+
         // Check in which direction the ball is flying
         if ((mypos.x > 0 && ballvel.x < 0) || (mypos.x < 0 && ballvel.x > 0))
         {
             // Ball is flying away
-            this->calculateRandomOffset();
+            this->ballDirection_.x = -1;
+            this->ballDirection_.y = 0;
 
             if (mypos.z > hysteresisOffset)
-                bat->moveFrontBack(1);
+                move = 1;
             else if (mypos.z < -hysteresisOffset)
-                bat->moveFrontBack(-1);
+                move = -1;
         }
         else if (ballvel.x == 0)
         {
             // Ball is standing still
-            this->calculateRandomOffset();
+            this->ballDirection_.x = 0;
+            this->ballDirection_.y = 0;
         }
         else
         {
             // Ball is approaching
-            float desiredZValue = ballpos.z + this->randomOffset_;
-
-            if (mypos.z > desiredZValue + hysteresisOffset)
-                bat->moveFrontBack(1);
-            else if (mypos.z < desiredZValue - hysteresisOffset)
-                bat->moveFrontBack(-1);
-        }
+            if (this->ballDirection_.x != 1)
+            {
+                this->ballDirection_.x = 1;
+                this->ballDirection_.y = sgn(ballvel.z);
+                this->ballEndPosition_ = 0;
+                this->randomOffset_ = 0;
+
+                this->calculateRandomOffset();
+                this->calculateBallEndPosition();
+                //this->randomOffsetTimer_.setInterval(MAX_REACTION_TIME * (1 - this->strength_));
+                //this->ballEndPositionTimer_.setInterval(MAX_REACTION_TIME * (1 - this->strength_));
+                //this->randomOffsetTimer_.startTimer();
+                //this->ballEndPositionTimer_.startTimer();
+            }
+
+            if (this->ballDirection_.y != sgn(ballvel.z))
+            {
+                this->ballDirection_.y = sgn(ballvel.z);
+
+                this->calculateBallEndPosition();
+                //this->ballEndPositionTimer_.startTimer();
+            }
+
+            float desiredZValue = /*((1 - this->strength_) * ballpos.z) + */(/*this->strength_ * */this->ballEndPosition_) + this->randomOffset_;
+
+            if (mypos.z > desiredZValue + hysteresisOffset * (this->randomOffset_ < 0))
+                move = 1;
+            else if (mypos.z < desiredZValue - hysteresisOffset * (this->randomOffset_ > 0))
+                move = -1;
+        }
+
+        this->move(move);
+        this->getControllableEntity()->moveFrontBack(this->movement_);
     }
 
@@ -108,5 +153,5 @@
 
         // The position shouln't be larger than 0.5 (50% of the bat-length from the middle is the end)
-        position *= 0.45;
+        position *= 0.48;
 
         // Both sides are equally probable
@@ -116,3 +161,65 @@
         this->randomOffset_ = position * this->ball_->getBatLength() * this->ball_->getFieldDimension().y;
     }
+
+    void PongAI::calculateBallEndPosition()
+    {
+        Vector3 position = this->ball_->getPosition();
+        Vector3 velocity = this->ball_->getVelocity();
+        Vector2 dimension = this->ball_->getFieldDimension();
+
+        // calculate end-height: current height + slope * distance
+        this->ballEndPosition_ = position.z + velocity.z / velocity.x * (-position.x + dimension.x / 2 * sgn(velocity.x));
+
+        // Calculate bounces
+        for (float limit = 0.35; limit < this->strength_ || this->strength_ > 0.99; limit += 0.4)
+        {
+            if (this->ballEndPosition_ > dimension.y / 2)
+            {
+                this->ballEndPosition_ = dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
+                continue;
+            }
+            if (this->ballEndPosition_ < -dimension.y / 2)
+            {
+                this->ballEndPosition_ = -dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
+                continue;
+            }
+            break;
+        }
+    }
+
+    void PongAI::move(char direction)
+    {
+        // The current direction is either what we're doing right now (movement_) or what is last in the queue
+        char currentDirection = this->movement_;
+        if (this->reactionTimers_.size() > 0)
+            currentDirection = this->reactionTimers_.back().second;
+
+        // Only add changes of direction
+        if (direction == currentDirection)
+            return;
+
+        // Calculate delay, but only to change direction or start moving (stop works without delay)
+        if (direction != 0)
+        {
+            float delay = MAX_REACTION_TIME * (1 - this->strength_);
+
+            // Add a new Timer
+            Timer<PongAI>* timer = new Timer<PongAI>(delay, false, this, createExecutor(createFunctor(&PongAI::delayedMove)));
+            this->reactionTimers_.push_back(std::pair<Timer<PongAI>*, char>(timer, direction));
+        }
+        else
+        {
+            this->movement_ = 0;
+        }
+    }
+
+    void PongAI::delayedMove()
+    {
+        this->movement_ = this->reactionTimers_.front().second;
+
+        Timer<PongAI>* timer = this->reactionTimers_.front().first;
+        delete timer;
+
+        this->reactionTimers_.pop_front();
+    }
 }
Index: code/trunk/src/orxonox/objects/controllers/PongAI.h
===================================================================
--- code/trunk/src/orxonox/objects/controllers/PongAI.h	(revision 2857)
+++ code/trunk/src/orxonox/objects/controllers/PongAI.h	(revision 2860)
@@ -32,6 +32,9 @@
 #include "OrxonoxPrereqs.h"
 
+#include <list>
+
 #include "Controller.h"
 #include "objects/Tickable.h"
+#include "util/Math.h"
 
 namespace orxonox
@@ -41,5 +44,5 @@
         public:
             PongAI(BaseObject* creator);
-            virtual ~PongAI() {}
+            virtual ~PongAI();
 
             void setConfigValues();
@@ -52,9 +55,19 @@
         protected:
             void calculateRandomOffset();
+            void calculateBallEndPosition();
+            void move(char direction);
+            void delayedMove();
 
             PongBall* ball_;
+            Vector2 ballDirection_;
+            float ballEndPosition_;
             float randomOffset_;
             float relHysteresisOffset_;
             float strength_;
+
+//            Timer<PongAI> randomOffsetTimer_;
+//            Timer<PongAI> ballEndPositionTimer_;
+            std::list<std::pair<Timer<PongAI>*, char> > reactionTimers_;
+            char movement_;
     };
 }
