Beispiel #1
0
bool Stage::hasVision(Line2D *visionLine) {
  for (int z = 0; z < numInnerWallLines_; z++) {
    Line2D* wallLine = innerWallLines_[z];
    if (wallLine->intersects(visionLine)) {
      return false;
    }
  }
  return true;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}