#include "physics.h" #include "rules.h" #include "interface.h" #include "global.h" #include #include #include #include #include #include #include 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"