// Fire this weapon. If it is a turret, it automatically points toward // the given ship's target. If the weapon requires ammunition, it will // be subtracted from the given ship. void Armament::Weapon::Fire(Ship &ship, list<Projectile> &projectiles, list<Effect> &effects) { // Since this is only called internally by Armament (no one else has non- // const access), assume Armament checked that this is a valid call. Angle aim = ship.Facing(); // Get projectiles to start at the right position. They are drawn at an // offset of (.5 * velocity) and that velocity includes the velocity of the // ship that fired them. Point start = ship.Position() + aim.Rotate(point) - .5 * ship.Velocity(); shared_ptr<const Ship> target = ship.GetTargetShip(); // If you are boarding your target, do not fire on it. if(ship.IsBoarding() || ship.Commands().Has(Command::BOARD)) target.reset(); if(!isTurret || !target || target->GetSystem() != ship.GetSystem()) aim += angle; else { Point p = target->Position() - start + ship.GetPersonality().Confusion(); Point v = target->Velocity() - ship.Velocity(); double steps = RendezvousTime(p, v, outfit->Velocity()); // Special case: RendezvousTime() may return NaN. But in that case, this // comparison will return false. if(!(steps < outfit->TotalLifetime())) steps = outfit->TotalLifetime(); p += steps * v; aim = Angle(TO_DEG * atan2(p.X(), -p.Y())); } projectiles.emplace_back(ship, start, aim, outfit); if(outfit->WeaponSound()) Audio::Play(outfit->WeaponSound(), start); double force = outfit->FiringForce(); if(force) ship.ApplyForce(aim.Unit() * -force); for(const auto &eit : outfit->FireEffects()) for(int i = 0; i < eit.second; ++i) { effects.push_back(*eit.first); effects.back().Place(start, ship.Velocity(), aim); } Fire(ship); }
Projectile::Projectile(const Ship &parent, Point position, Angle angle, const Outfit *weapon) : weapon(weapon), animation(weapon->WeaponSprite()), position(position), velocity(parent.Velocity()), angle(angle), targetShip(parent.GetTargetShip()), government(parent.GetGovernment()), lifetime(weapon->Lifetime()) { // If you are boarding your target, do not fire on it. if(parent.IsBoarding() || parent.Commands().Has(Command::BOARD)) targetShip.reset(); cachedTarget = targetShip.lock().get(); double inaccuracy = weapon->Inaccuracy(); if(inaccuracy) this->angle += Angle::Random(inaccuracy) - Angle::Random(inaccuracy); velocity += this->angle.Unit() * weapon->Velocity(); }
Projectile::Projectile(const Ship &parent, Point position, Angle angle, const Outfit *weapon) : Body(weapon->WeaponSprite(), position, parent.Velocity(), angle), weapon(weapon), targetShip(parent.GetTargetShip()), lifetime(weapon->Lifetime()) { government = parent.GetGovernment(); // If you are boarding your target, do not fire on it. if(parent.IsBoarding() || parent.Commands().Has(Command::BOARD)) targetShip.reset(); cachedTarget = targetShip.lock().get(); if(cachedTarget) targetGovernment = cachedTarget->GetGovernment(); double inaccuracy = weapon->Inaccuracy(); if(inaccuracy) this->angle += Angle::Random(inaccuracy) - Angle::Random(inaccuracy); velocity += this->angle.Unit() * (weapon->Velocity() + Random::Real() * weapon->RandomVelocity()); // If a random lifetime is specified, add a random amount up to that amount. if(weapon->RandomLifetime()) lifetime += Random::Int(weapon->RandomLifetime() + 1); }