diff options
Diffstat (limited to 'kue/physics.cpp')
-rw-r--r-- | kue/physics.cpp | 189 |
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" |