summaryrefslogtreecommitdiffstats
path: root/kue/physics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kue/physics.cpp')
-rw-r--r--kue/physics.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/kue/physics.cpp b/kue/physics.cpp
new file mode 100644
index 00000000..f9822f2e
--- /dev/null
+++ b/kue/physics.cpp
@@ -0,0 +1,189 @@
+#include "physics.h"
+#include "rules.h"
+#include "interface.h"
+#include "global.h"
+
+#include <GL/gl.h>
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ntqsize.h>
+#include <stdio.h>
+#include <kdebug.h>
+
+const double COLLISION_DRAG = 0.95;
+const double BUMPER_DRAG = 0.8;
+
+KuePhysics::KuePhysics() : _running(false), _field_width(0.0), _field_height(0.0)
+{
+ _billiards.setAutoDelete(true);
+ _pockets.setAutoDelete(true);
+}
+
+KuePhysics::~KuePhysics()
+{
+}
+
+void KuePhysics::timerEvent ( TQTimerEvent * )
+{
+ // Have all the balls stopped?
+ if (!run(TIME_CHUNK))
+ {
+ // Tell the rules manager
+ emit(motionStopped());
+ return;
+ }
+
+ // We need a redisplay
+ KueGlobal::glWidget()->updateGL();
+}
+
+void KuePhysics::seperate(KueBilliard *a, KueBilliard *b)
+{
+ double distance = a->distance(*b);
+ double delta = (a->radius() + b->radius() - distance) / 2.0;
+ double angle = a->angle(*b);
+ double delta_x = cos(angle) * delta;
+ double delta_y = sin(angle) * delta;
+
+ a->setPositionX(a->positionX() - delta_x);
+ a->setPositionY(a->positionY() - delta_y);
+
+ b->setPositionX(b->positionX() + delta_x);
+ b->setPositionY(b->positionY() + delta_y);
+}
+
+bool KuePhysics::allStop()
+{
+ for (unsigned int i = 0;i < _billiards.size();i++)
+ if (_billiards[i])
+ if (!_billiards[i]->isStopped())
+ return false;
+
+ return true;
+}
+
+void KuePhysics::doPocketing()
+{
+ for (unsigned int b = 0;b < _billiards.size();b++)
+ {
+ if (!_billiards[b])
+ continue;
+
+ for (unsigned int p = 0;p < _pockets.size();p++)
+ {
+ if (!_pockets[p])
+ continue;
+
+ if (_billiards[b]->distance(*_pockets[p]) <= _pockets[p]->radius())
+ {
+ emit(billiardSunk(b, p));
+
+ _billiards.remove(b);
+
+ break;
+ }
+ }
+ }
+}
+
+void KuePhysics::insertBilliard(unsigned int index, const KueBilliard &b)
+{
+ // Do we need to grow the vector?
+ if (index >= _billiards.size())
+ _billiards.resize(index + 1);
+
+ // Insert the new billiard
+ _billiards.insert(index, new KueBilliard(b));
+}
+
+void KuePhysics::insertPocket(unsigned int index, const KuePocket &p)
+{
+ // Grow the vector as needed
+ if (index >= _pockets.size())
+ _pockets.resize(index + 1);
+
+ // Insert the new pocket
+ _pockets.insert(index, new KuePocket(p));
+}
+
+bool KuePhysics::run(int milliseconds)
+{
+ // The collison code accepts values in seconds, not milliseconds
+ double seconds = milliseconds / 1000.0;
+
+ for (int i = _billiards.size() - 1;i >= 0;i--) {
+ if (!_billiards[i])
+ continue;
+
+ // Move the billiards forwards along their velocity vectors
+ _billiards[i]->step(seconds);
+
+ // Save the x, and radius y values so we don't waste a bunch of
+ // function calls
+ double x = _billiards[i]->positionX();
+ double y = _billiards[i]->positionY();
+ double radius = _billiards[i]->radius();
+
+ // Check if the billiard intersects with any edge, and if it does
+ // reflect it along the side it hit, and then move the billiard
+ // back on the playing field.
+ // Pretty terse code, but it really needs to be fast
+ if ((x + radius) > _field_width)
+ {
+ _billiards[i]->reflect(0.0);
+ _billiards[i]->velocity() *= BUMPER_DRAG;
+ _billiards[i]->setPositionX(_field_width - radius);
+ }
+ else if (x < radius)
+ {
+ _billiards[i]->reflect(0.0);
+ _billiards[i]->velocity() *= BUMPER_DRAG;
+ _billiards[i]->setPositionX(radius);
+ }
+
+ if ((y + radius) > _field_height)
+ {
+ _billiards[i]->reflect(M_PI / 2.0);
+ _billiards[i]->velocity() *= BUMPER_DRAG;
+ _billiards[i]->setPositionY(_field_height - radius);
+ }
+ else if (y < radius)
+ {
+ _billiards[i]->reflect(M_PI / 2.0);
+ _billiards[i]->velocity() *= BUMPER_DRAG;
+ _billiards[i]->setPositionY(radius);
+ }
+
+ for (unsigned int j = i + 1;j <= _billiards.size() - 1;j++) {
+ // If this isn't a billiard anymore, skip it
+ if (!_billiards[j])
+ continue;
+
+ // Are these billiards intersecting (colliding)?
+ if (_billiards[i]->intersects(*_billiards[j]))
+ {
+ // Update their velocity vectors
+ _billiards[i]->collide(*_billiards[j]);
+ // Physically seperate the two balls so we don't
+ // call the collision code again
+ seperate(_billiards[i], _billiards[j]);
+
+ // Factor in collision drag
+ _billiards[i]->velocity() *= COLLISION_DRAG;
+ _billiards[j]->velocity() *= COLLISION_DRAG;
+
+ // Tell the rules engine that we hit a ball
+ emit(billiardHit(i, j));
+ }
+ }
+ }
+
+ doPocketing();
+
+ // Return true if we aren't done yet
+ return (!allStop());
+}
+
+#include "physics.moc"