bool Stage::hasVision(Line2D *visionLine) { for (int z = 0; z < numInnerWallLines_; z++) { Line2D* wallLine = innerWallLines_[z]; if (wallLine->intersects(visionLine)) { return false; } } return true; }
bool Stage::touchedZone(Ship *oldShip, Ship *ship, Zone *zone) { if (inZone(ship, zone)) { return true; } Line2D *line = new Line2D(oldShip->x, oldShip->y, ship->x, ship->y); Line2D **zoneLines = zone->getLines(); for (int x = 0; x < 4; x++) { Line2D *zoneLine = zoneLines[x]; if (zoneLine->intersects(line)) { delete line; return true; } } delete line; return false; }
void Stage::moveAndCheckCollisions( Ship **oldShips, Ship **ships, int numShips, int gameTime) { ShipMoveData *shipData = new ShipMoveData[numShips]; // Calculate initial movement and decide on a common sub-tick interval. int intervals = 1; for (int x = 0; x < numShips; x++) { Ship *ship = ships[x]; ShipMoveData shipDatum = shipData[x]; shipDatum.initialized = false; shipDatum.shipCircle = 0; shipDatum.nextShipCircle = 0; if (ship->alive) { shipDatum.initialized = true; Ship *oldShip = oldShips[x]; double force = ship->thrusterForce; double forceAngle = ship->thrusterAngle; shipDatum.dxSpeed = cos(forceAngle) * force; shipDatum.dySpeed = sin(forceAngle) * force; shipDatum.startXSpeed = cos(ship->heading) * ship->speed; shipDatum.startYSpeed = sin(ship->heading) * ship->speed; shipDatum.xSpeed = shipDatum.startXSpeed + shipDatum.dxSpeed; shipDatum.ySpeed = shipDatum.startYSpeed + shipDatum.dySpeed; ship->x += shipDatum.xSpeed; ship->y += shipDatum.ySpeed; ship->hitWall = false; ship->hitShip = false; setSpeedAndHeading(oldShip, ship, &shipDatum); shipDatum.shipCircle = new Circle2D(oldShip->x, oldShip->y, SHIP_RADIUS); shipDatum.nextShipCircle = new Circle2D(0, 0, SHIP_RADIUS); shipDatum.wallCollision = false; shipDatum.minWallImpactDiff = M_PI; shipDatum.wallImpactAngle = 0; shipDatum.wallImpactLine = 0; shipDatum.shipCollision = false; shipDatum.shipCollisionData = new ShipCollisionData*[numShips]; for (int y = 0; y < numShips; y++) { shipDatum.shipCollisionData[y] = 0; } shipDatum.stopped = false; double moveDistance = sqrt(square(ship->x - oldShip->x) + square(ship->y - oldShip->y)); int moveIntervals = ceil(moveDistance / COLLISION_FRAME); intervals = std::max(intervals, moveIntervals); } shipData[x] = shipDatum; } for (int x = 0; x < numShips; x++) { if (ships[x]->alive) { shipData[x].dx = shipData[x].xSpeed / intervals; shipData[x].dy = shipData[x].ySpeed / intervals; } } for (int x = 0; x < intervals; x++) { // Move ships one interval and check for wall collisions. for (int y = 0; y < numShips; y++) { if (ships[y]->alive) { ShipMoveData *shipDatum = &(shipData[y]); if (!shipDatum->stopped) { Ship *oldShip = oldShips[y]; shipDatum->nextShipCircle->setPosition( oldShip->x + (shipDatum->dx * (x + 1)), oldShip->y + (shipDatum->dy * (x + 1))); for (int z = 0; z < numWallLines_; z++) { Line2D* line = wallLines_[z]; Point2D *p1 = 0; Point2D *p2 = 0; if (shipDatum->nextShipCircle->intersects(line, &p1, &p2)) { double thisX = shipDatum->shipCircle->h(); double thisY = shipDatum->shipCircle->k(); bool valid = true; if (p1 != 0) { Line2D vertexSightLine1(thisX, thisY, p1->getX() - (signum(p1->getX() - thisX) * VERTEX_FUDGE), p1->getY() - (signum(p1->getY() - thisY) * VERTEX_FUDGE)); valid = hasVision(&vertexSightLine1); delete p1; } if (p2 != 0) { if (valid) { Line2D vertexSightLine2(thisX, thisY, p2->getX() - (signum(p2->getX() - thisX) * VERTEX_FUDGE), p2->getY() - (signum(p2->getY() - thisY) * VERTEX_FUDGE)); valid = hasVision(&vertexSightLine2); } delete p2; } if (valid) { shipDatum->stopped = shipDatum->wallCollision = true; double heading = ships[y]->heading; double angle1 = line->theta() - M_PI_2; double angleDiff1 = abs(normalRelativeAngle(heading - angle1)); double angle2 = line->theta() + M_PI_2; double angleDiff2 = abs(normalRelativeAngle(heading - angle2)); if (angleDiff1 < shipDatum->minWallImpactDiff) { shipDatum->minWallImpactDiff = angleDiff1; shipDatum->wallImpactAngle = angle1; shipDatum->wallImpactLine = line; } if (angleDiff2 < shipDatum->minWallImpactDiff) { shipDatum->minWallImpactDiff = angleDiff2; shipDatum->wallImpactAngle = angle2; shipDatum->wallImpactLine = line; } } } } } } } // Check for ship-ship collisions. for (int y = 0; y < numShips; y++) { ShipMoveData *shipDatum = &(shipData[y]); if (ships[y]->alive && !shipDatum->stopped) { for (int z = 0; z < numShips; z++) { if (y != z && ships[z]->alive) { ShipMoveData *shipDatum2 = &(shipData[z]); if (shipDatum->nextShipCircle->overlaps(shipDatum2->shipCircle) || (!shipDatum2->stopped && shipDatum->nextShipCircle->overlaps( shipDatum2->nextShipCircle))) { shipDatum->stopped = shipDatum2->stopped = shipDatum->shipCollision = shipDatum2->shipCollision = true; if (shipDatum->shipCollisionData[z] == 0) { shipDatum->shipCollisionData[z] = new ShipCollisionData; } if (shipDatum2->shipCollisionData[y] == 0) { shipDatum2->shipCollisionData[y] = new ShipCollisionData; } } } } } } // Update for next tick and clear old locations. for (int y = 0; y < numShips; y++) { if (ships[y]->alive) { ShipMoveData *shipDatum = &(shipData[y]); if (!shipDatum->stopped) { shipDatum->shipCircle->setPosition( shipDatum->nextShipCircle->h(), shipDatum->nextShipCircle->k()); } } } } // Calculate impact of wall collisions. for (int x = 0; x < numShips; x++) { if (ships[x]->alive) { ShipMoveData *shipDatum = &(shipData[x]); if (shipDatum->wallCollision) { Ship *ship = ships[x]; Ship *oldShip = oldShips[x]; ship->hitWall = true; ship->x = shipDatum->shipCircle->h(); ship->y = shipDatum->shipCircle->k(); if (shipStopped(oldShip, ship)) { shipDatum->xSpeed = shipDatum->dxSpeed; shipDatum->ySpeed = shipDatum->dySpeed; setSpeedAndHeading(oldShip, ship, shipDatum); } double bounceForce = abs(ship->speed * cos(shipDatum->minWallImpactDiff) * (1 + WALL_BOUNCE)); double bounceAngle = shipDatum->wallImpactAngle + M_PI; shipDatum->xSpeed += cos(bounceAngle) * bounceForce; shipDatum->ySpeed += sin(bounceAngle) * bounceForce; setSpeedAndHeading(oldShip, ship, shipDatum); for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleShipHitWall( ship, bounceAngle, bounceForce, gameTime); } } } } // Update x/y/heading/speeds for ships with ship-ship collisions. for (int x = 0; x < numShips; x++) { ShipMoveData *shipDatum = &(shipData[x]); if (ships[x]->alive && shipDatum->shipCollision) { Ship *ship = ships[x]; ship->hitShip = true; ship->x = shipDatum->shipCircle->h(); ship->y = shipDatum->shipCircle->k(); if (shipStopped(oldShips[x], ship)) { shipDatum->xSpeed = shipDatum->dxSpeed; shipDatum->ySpeed = shipDatum->dySpeed; setSpeedAndHeading(oldShips[x], ship, shipDatum); } } } // Calculate momentum to be transferred between all colliding ships. for (int x = 0; x < numShips; x++) { ShipMoveData *shipDatum = &(shipData[x]); Ship *ship = ships[x]; if (ship-> alive && shipDatum->shipCollision) { double totalForce = 0; for (int y = 0; y < numShips; y++) { if (x != y && ships[y]->alive) { ShipCollisionData *collisionData = shipDatum->shipCollisionData[y]; if (collisionData != 0) { collisionData->angle = atan2(ships[y]->y - ship->y, ships[y]->x - ship->x); collisionData->force = cos(ship->heading - collisionData->angle) * ship->speed; totalForce += collisionData->force; } } } if (totalForce > ship->speed) { for (int y = 0; y < numShips; y++) { if (x != y) { ShipCollisionData *collisionData = shipDatum->shipCollisionData[y]; if (collisionData != 0) { collisionData->force *= ship->speed / totalForce; } } } } } } // Apply momentum transfers. for (int x = 0; x < numShips; x++) { ShipMoveData *shipDatum = &(shipData[x]); if (ships[x]->alive && shipDatum->shipCollision) { for (int y = 0; y < numShips; y++) { if (x != y && ships[y]->alive) { ShipMoveData *shipDatum2 = &(shipData[y]); ShipCollisionData *collisionData = shipDatum->shipCollisionData[y]; if (collisionData != 0) { double xForce = cos(collisionData->angle) * collisionData->force; double yForce = sin(collisionData->angle) * collisionData->force; shipDatum->xSpeed -= xForce; shipDatum->ySpeed -= yForce; setSpeedAndHeading(oldShips[x], ships[x], shipDatum); shipDatum2->xSpeed += xForce; shipDatum2->ySpeed += yForce; setSpeedAndHeading(oldShips[y], ships[y], shipDatum2); ShipCollisionData *collisionData2 = shipDatum2->shipCollisionData[x]; for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleShipHitShip(ships[x], ships[y], collisionData->angle, collisionData->force, collisionData2->angle, collisionData2->force, gameTime); } } } } } } // Check for laser-ship collisions, laser-wall collisions, log destroys and // damage, remove dead lasers. bool **laserHits = new bool*[numShips]; for (int x = 0; x < numShips; x++) { laserHits[x] = new bool[numShips]; for (int y = 0; y < numShips; y++) { laserHits[x][y] = false; } } bool *wasAlive = new bool[numShips]; for (int x = 0; x < numShips; x++) { wasAlive[x] = ships[x]->alive; } // For lasers fired this tick, check if they intersect any other ships at // their initial position (0-25 from origin) before moving the first time. checkLaserShipCollisions(ships, shipData, numShips, laserHits, numLasers_, gameTime, true); // Move lasers one whole tick. for (int x = 0; x < numLasers_; x++) { Laser *laser = lasers_[x]; laser->x += laser->dx; laser->y += laser->dy; laserLines_[x]->shift(laser->dx, laser->dy); } checkLaserShipCollisions(ships, shipData, numShips, laserHits, numLasers_, gameTime, false); for (int x = 0; x < numShips; x++) { Ship *ship = ships[x]; if (wasAlive[x] && !ship->alive) { int numDestroyers = 0; for (int y = 0; y < numShips; y++) { if (laserHits[y][x]) { numDestroyers++; } } Ship **destroyers = new Ship*[numDestroyers]; int destroyerIndex = 0; for (int y = 0; y < numShips; y++) { if (laserHits[y][x]) { destroyers[destroyerIndex++] = ships[y]; } } for (int y = 0; y < numShips; y++) { if (laserHits[y][x]) { double destroyScore = 1.0 / numDestroyers; if (ship->teamIndex == ships[y]->teamIndex) { ships[y]->friendlyKills += destroyScore; } else { ships[y]->kills += destroyScore; } } } for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleShipDestroyed(ship, gameTime, destroyers, numDestroyers); } delete destroyers; } } for (int x = 0; x < numLasers_; x++) { Laser *laser = lasers_[x]; Line2D *laserLine = laserLines_[x]; for (int y = 0; y < numWallLines_ && !lasers_[x]->dead; y++) { Line2D *wallLine = wallLines_[y]; if (wallLine->intersects(laserLine)) { lasers_[x]->dead = true; } } if (laser->dead) { if (numLasers_ > 1) { lasers_[x] = lasers_[numLasers_ - 1]; laserLines_[x] = laserLines_[numLasers_ - 1]; } for (int y = 0; y < numEventHandlers_; y++) { eventHandlers_[y]->handleLaserDestroyed(laser, gameTime); } delete laser; delete laserLine; numLasers_--; x--; } } for (int x = 0; x < numShips; x++) { delete laserHits[x]; } delete laserHits; // Move torpedoes and check for collisions. for (int x = 0; x < numShips; x++) { Ship *ship = ships[x]; wasAlive[x] = ship->alive; } bool **torpedoHits = new bool*[numShips]; for (int x = 0; x < numShips; x++) { torpedoHits[x] = new bool[numShips]; for (int y = 0; y < numShips; y++) { torpedoHits[x][y] = false; } } for (int x = 0; x < numTorpedos_; x++) { Torpedo *torpedo = torpedos_[x]; double distanceRemaining = torpedo->distance - torpedo->distanceTraveled; if (distanceRemaining >= TORPEDO_SPEED) { torpedo->x += torpedo->dx; torpedo->y += torpedo->dy; torpedo->distanceTraveled += TORPEDO_SPEED; } else { torpedo->x += cos(torpedo->heading) * distanceRemaining; torpedo->y += sin(torpedo->heading) * distanceRemaining; for (int y = 0; y < numShips; y++) { Ship *ship = ships[y]; if (ship->alive) { double distSq = square(torpedo->x - ship->x) + square(torpedo->y - ship->y); if (distSq < square(TORPEDO_BLAST_RADIUS)) { int firingShipIndex = torpedo->shipIndex; torpedoHits[firingShipIndex][y] = true; ShipMoveData *shipDatum = &(shipData[y]); double blastDistance = sqrt(distSq); double blastFactor = square(1.0 - (blastDistance / TORPEDO_BLAST_RADIUS)); double blastForce = blastFactor * TORPEDO_BLAST_FORCE; double blastDamage = blastFactor * (ship->energyEnabled ? TORPEDO_BLAST_DAMAGE : 0); double blastAngle = atan2(ship->y - torpedo->y, ship->x - torpedo->x); double damageScore = (blastDamage / DEFAULT_ENERGY); if (ship->teamIndex == ships[firingShipIndex]->teamIndex) { ships[firingShipIndex]->friendlyDamage += damageScore; } else { ships[firingShipIndex]->damage += damageScore; } ship->energy -= blastDamage; shipDatum->xSpeed += cos(blastAngle) * blastForce; shipDatum->ySpeed += sin(blastAngle) * blastForce; setSpeedAndHeading(oldShips[y], ship, shipDatum); for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleTorpedoHitShip(ships[torpedo->shipIndex], ship, shipDatum->startXSpeed, shipDatum->startYSpeed, blastAngle, blastForce, blastDamage, gameTime); } if (ship->energy <= 0) { ship->alive = false; } } } } for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleTorpedoExploded(torpedo, gameTime); } if (numTorpedos_ > 1) { torpedos_[x] = torpedos_[numTorpedos_ - 1]; } delete torpedo; numTorpedos_--; x--; } } for (int x = 0; x < numShips; x++) { Ship *ship = ships[x]; if (wasAlive[x] && !ship->alive) { int numDestroyers = 0; for (int y = 0; y < numShips; y++) { if (torpedoHits[y][x]) { numDestroyers++; } } Ship **destroyers = new Ship*[numDestroyers]; int destroyerIndex = 0; for (int y = 0; y < numShips; y++) { if (torpedoHits[y][x]) { destroyers[destroyerIndex++] = ships[y]; } } for (int y = 0; y < numShips; y++) { if (torpedoHits[y][x]) { double destroyScore = 1.0 / numDestroyers; if (ship->teamIndex == ships[y]->teamIndex) { ships[y]->friendlyKills += destroyScore; } else { ships[y]->kills += destroyScore; } } } for (int z = 0; z < numEventHandlers_; z++) { eventHandlers_[z]->handleShipDestroyed(ship, gameTime, destroyers, numDestroyers); } delete destroyers; } } for (int x = 0; x < numShips; x++) { delete torpedoHits[x]; } delete torpedoHits; delete wasAlive; for (int x = 0; x < numShips; x++) { ShipMoveData *shipDatum = &(shipData[x]); if (shipDatum->initialized) { delete shipDatum->shipCircle; delete shipDatum->nextShipCircle; for (int y = 0; y < numShips; y++) { ShipCollisionData *collisionData = shipDatum->shipCollisionData[y]; if (collisionData != 0) { delete collisionData; } } delete shipDatum->shipCollisionData; } } delete shipData; }