Пример #1
0
// Fire an anti-missile. Returns true if the missile should be killed.
bool Armament::Weapon::FireAntiMissile(Ship &ship, const Projectile &projectile, list<Effect> &effects)
{
	int strength = outfit->AntiMissile();
	if(!strength)
		return false;
	
	double range = outfit->Velocity();
	
	// Check if the missile is in range.
	Point start = ship.Position() + ship.Facing().Rotate(point);
	Point offset = projectile.Position() - start;
	if(offset.Length() > range)
		return false;
	
	// Figure out where the effect should be placed. Anti-missiles do not create
	// projectiles; they just create a blast animation.
	start += (.5 * range) * offset.Unit();
	Angle aim = TO_DEG * atan2(offset.X(), -offset.Y());
	for(const auto &eit : outfit->HitEffects())
		for(int i = 0; i < eit.second; ++i)
		{
			effects.push_back(*eit.first);
			effects.back().Place(start, ship.Velocity(), aim);
		}
	
	Fire(ship);
	
	return (Random::Int(strength) > Random::Int(projectile.MissileStrength()));
}
Пример #2
0
void AI::CircleAround(Ship &ship, Command &command, const Ship &target)
{
	// This is not the behavior I want, but it's reasonable.
	Point direction = target.Position() - ship.Position();
	command.SetTurn(TurnToward(ship, direction));
	if(ship.Facing().Unit().Dot(direction) >= 0. && direction.Length() > 200.)
		command |= Command::FORWARD;
}
Пример #3
0
// Check whether the mask contains the given point.
bool Mask::Contains(Point point, Angle facing) const
{
	if(outline.empty() || point.Length() > radius)
		return false;
	
	// Rotate into the mask's frame of reference.
	return Contains((-facing).Rotate(point));
}
Пример #4
0
void
Invert::Transform (real &w, Point& p)
  {
  const Point diff = p-C_ptr->Center();
  real r=diff.Length();
  real ratio=RadiusSq/(r*r);
  p = C_ptr->Center()+diff*ratio;
  w *= ratio*ratio;
  }
Пример #5
0
void
E2toIS::Transform(real& w, Point& p)
      {
      InfiniteStrip& s = *IS_ptr;
      Point D  = s.B()- s.A();
      Point C(-D.Y(),D.X());
      C = C/C.Length();
      w *= D.Length()*.5*(1-tanh(p.X())*tanh(p.X()));
      p = s.A() + p.Y()*C + (.5*(1+tanh(p.X())))*D;
      }
// Check if the given ship is within this projectile's blast radius. (The
// projectile will not explode unless it is also within the trigger radius.)
bool Projectile::InBlastRadius(const Ship &ship, int step, double closestHit) const
{
	// "Invisible" ships can be killed by weapons with blast radii.
	Point offset = position + closestHit * velocity - ship.Position();
	if(offset.Length() <= weapon->BlastRadius())
		return true;
	
	const Mask &mask = ship.GetMask(step);
	return mask.WithinRange(offset, ship.Facing(), weapon->BlastRadius());
}
Пример #7
0
void AI::Attack(Ship &ship, Command &command, const Ship &target)
{
	Point d = target.Position() - ship.Position();
	
	// First, figure out what your shortest-range weapon is.
	double shortestRange = 4000.;
	for(const Armament::Weapon &weapon : ship.Weapons())
	{
		const Outfit *outfit = weapon.GetOutfit();
		if(outfit && !weapon.IsAntiMissile())
			shortestRange = min(outfit->Range(), shortestRange);
	}
	
	// Deploy any fighters you are carrying.
	if(!ship.IsYours())
		command |= Command::DEPLOY;
	// If this ship only has long-range weapons, it should keep its distance
	// instead of trying to close with the target ship.
	if(shortestRange > 1000. && d.Length() < .5 * shortestRange)
	{
		command.SetTurn(TurnToward(ship, -d));
		if(ship.Facing().Unit().Dot(d) <= 0.)
			command |= Command::FORWARD;
		return;
	}
	
	// First of all, aim in the direction that will hit this target.
	command.SetTurn(TurnToward(ship, TargetAim(ship)));
	
	// Calculate this ship's "turning radius; that is, the smallest circle it
	// can make while at full speed.
	double stepsInFullTurn = 360. / ship.TurnRate();
	double circumference = stepsInFullTurn * ship.Velocity().Length();
	double diameter = max(200., circumference / PI);
	
	// This isn't perfect, but it works well enough.
	if((ship.Facing().Unit().Dot(d) >= 0. && d.Length() > diameter)
			|| (ship.Velocity().Dot(d) < 0. && ship.Facing().Unit().Dot(d.Unit()) >= .9))
		command |= Command::FORWARD;
}
Пример #8
0
// This ship just got hit by the given projectile. Take damage according to
// what sort of weapon the projectile it.
int Ship::TakeDamage(const Projectile &projectile, bool isBlast)
{
	int type = 0;
	
	const Outfit &weapon = projectile.GetWeapon();
	double shieldDamage = weapon.ShieldDamage();
	double hullDamage = weapon.HullDamage();
	double hitForce = weapon.HitForce();
	double heatDamage = weapon.HeatDamage();
	double ionDamage = weapon.IonDamage();
	bool wasDisabled = IsDisabled();
	bool wasDestroyed = IsDestroyed();
	
	if(shields > shieldDamage)
	{
		shields -= shieldDamage;
		heat += .5 * heatDamage;
		ionization += .5 * ionDamage;
	}
	else if(!shields || shieldDamage)
	{
		if(shieldDamage)
		{
			hullDamage *= (1. - (shields / shieldDamage));
			shields = 0.;
		}
		hull -= hullDamage;
		heat += heatDamage;
		ionization += ionDamage;
	}
	
	if(hitForce && !IsHyperspacing())
	{
		Point d = position - projectile.Position();
		double distance = d.Length();
		if(distance)
			ApplyForce((hitForce / distance) * d);
	}
	
	if(!wasDisabled && IsDisabled())
		type |= ShipEvent::DISABLE;
	if(!wasDestroyed && IsDestroyed())
		type |= ShipEvent::DESTROY;
	// If this ship was hit directly and did not consider itself an enemy of the
	// ship that hit it, it is now "provoked" against that government.
	if(!isBlast && projectile.GetGovernment() && !projectile.GetGovernment()->IsEnemy(government)
			&& (Shields() < .9 || Hull() < .9 || !personality.IsForbearing())
			&& !personality.IsPacifist())
		type |= ShipEvent::PROVOKE;
	
	return type;
}
Пример #9
0
// Check if this ship is currently able to begin landing on its target.
bool Ship::CanLand() const
{
	if(!GetTargetPlanet() || !GetTargetPlanet()->GetPlanet() || isDisabled || IsDestroyed())
		return false;
	
	if(!GetTargetPlanet()->GetPlanet()->CanLand(*this))
		return false;
	
	Point distance = GetTargetPlanet()->Position() - position;
	double speed = velocity.Length();
	
	return (speed < 1. && distance.Length() < GetTargetPlanet()->Radius());
}
Пример #10
0
// Check if this mask intersects the given line segment (from sA to vA). If
// it does, return the fraction of the way along the segment where the
// intersection occurs. The sA should be relative to this object's center.
// If this object contains the given point, the return value is 0. If there
// is no collision, the return value is 1.
double Mask::Collide(Point sA, Point vA, Angle facing) const
{
	// Bail out if we're too far away to possibly be touching.
	double distance = sA.Length();
	if(outline.empty() || distance > radius + vA.Length())
		return 1.;
	
	// Rotate into the mask's frame of reference.
	sA = (-facing).Rotate(sA);
	vA = (-facing).Rotate(vA);
	
	// If this point is contained within the mask, a ray drawn out from it will
	// intersect the mask an even number of times. If that ray coincides with an
	// edge, ignore that edge, and count all segments as closed at the start and
	// open at the end to avoid double-counting.
	
	// For simplicity, use a ray pointing straight downwards. A segment then
	// intersects only if its x coordinates span the point's coordinates.
	if(distance <= radius && Contains(sA))
		return 0.;
	
	return Intersection(sA, vA);
}
Пример #11
0
double AI::TurnToward(const Ship &ship, const Point &vector)
{
	static const double RAD_TO_DEG = 180. / 3.14159265358979;
	Point facing = ship.Facing().Unit();
	double cross = vector.Cross(facing);
	
	if(vector.Dot(facing) > 0.)
	{
		double angle = asin(cross / vector.Length()) * RAD_TO_DEG;
		if(fabs(angle) <= ship.TurnRate())
			return -angle / ship.TurnRate();
	}
	
	bool left = cross < 0.;
	return left - !left;
}
Пример #12
0
// Find out how close this mask is to the given point. Again, the mask is
// assumed to be rotated and scaled according to the given unit vector.
bool Mask::WithinRange(Point point, Angle facing, double range) const
{
	// Bail out if the object is too far away to possible be touched.
	if(outline.empty() || range < point.Length() - radius)
		return false;
	
	// Rotate into the mask's frame of reference.
	point = (-facing).Rotate(point);
	// For efficiency, compare to range^2 instead of range.
	range *= range;
	
	for(const Point &p : outline)
		if(p.DistanceSquared(point) < range)
			return true;
	
	return false;
}
Пример #13
0
// Draw the radar display at the given coordinates.
void Radar::Draw(const Point &center, double scale, double radius, double pointerRadius) const
{
	RingShader::Bind();
	for(const Object &object : objects)
	{
		Point position = object.position * scale;
		double length = position.Length();
		if(length > radius)
			position *= radius / length;
		position += center;
		
		RingShader::Add(position, object.outer, object.inner, object.color);
	}
	RingShader::Unbind();
	
	PointerShader::Bind();
	for(const Pointer &pointer : pointers)
		PointerShader::Add(center, pointer.unit, 10., 10., pointerRadius, pointer.color);
	PointerShader::Unbind();
}
Пример #14
0
bool AI::MoveTo(Ship &ship, Command &command, const Point &target, double radius, double slow)
{
	const Point &position = ship.Position();
	const Point &velocity = ship.Velocity();
	const Angle &angle = ship.Facing();
	Point distance = target - position;
	
	double speed = velocity.Length();
	
	bool isClose = (distance.Length() < radius);
	if(isClose && speed < slow)
		return true;
	
	distance = target - StoppingPoint(ship);
	bool isFacing = (distance.Unit().Dot(angle.Unit()) > .8);
	if(!isClose || !isFacing)
		command.SetTurn(TurnToward(ship, distance));
	if(isFacing)
		command |= Command::FORWARD;
	
	return false;
}
Пример #15
0
void OutlineShader::Draw(const Sprite *sprite, const Point &pos, const Point &size, const Color &color, const Point &unit, float frame)
{
	glUseProgram(shader.Object());
	glBindVertexArray(vao);
	
	GLfloat scale[2] = {2.f / Screen::Width(), -2.f / Screen::Height()};
	glUniform2fv(scaleI, 1, scale);
	
	GLfloat off[2] = {
		static_cast<float>(.5 / size.X()),
		static_cast<float>(.5 / size.Y())};
	glUniform2fv(offI, 1, off);
	
	glUniform1f(frameI, frame);
	glUniform1f(frameCountI, sprite->Frames());
	
	Point uw = unit * size.X();
	Point uh = unit * size.Y();
	GLfloat transform[4] = {
		static_cast<float>(-uw.Y()),
		static_cast<float>(uw.X()),
		static_cast<float>(-uh.X()),
		static_cast<float>(-uh.Y())
	};
	glUniformMatrix2fv(transformI, 1, false, transform);
	
	GLfloat position[2] = {
		static_cast<float>(pos.X()), static_cast<float>(pos.Y())};
	glUniform2fv(positionI, 1, position);
	
	glUniform4fv(colorI, 1, color.Get());
	
	glBindTexture(GL_TEXTURE_2D_ARRAY, sprite->Texture(unit.Length() * Screen::Zoom() > 50.));
	
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
	glBindVertexArray(0);
	glUseProgram(0);
}
Пример #16
0
void Gao(Point r1[3],Point r2[3],Point v1,Point v2,bool &flag,double &res)
{
    v.x = v1.x-v2.x;
    v.y = v1.y-v2.y;
    res = 1e100;
    flag = false;
    double speed = v.Length();
    if (cmp(speed,0.0) > 0)
    {
        for (int i = 0; i < 3; i++)
        {
            l1 = Line(r1[i],Point(r1[i].x+v.x,r1[i].y+v.y));
            double curmin = 1e100;
            bool tflag = false;
            for (int j = 0; j < 3; j++)
            {
                Point tmp = Point(r2[j],r2[(j+1)%3]);
                l2 = Line(r2[j],Point(r2[j].x+tmp.x,r2[j].y+tmp.y));
                if (cmp(xmult(l1,l2),0.0) == 0) continue;
                xp = LineToLine(l1,l2);
                if (OnSeg(xp,l2) == false)  continue;
                if (cmp(v.x,0.0) != 0)
                    if (cmp(Point(r1[i],xp).x/v.x,0.0) < 0)    continue;
                if (cmp(v.y,0.0) != 0)
                    if (cmp(Point(r1[i],xp).y/v.y,0.0) < 0)    continue;
                curmin = min(curmin,Point(r1[i],xp).Length());
                tflag = true;
            }
            if (tflag == true)
            {
                res = min(res,curmin);
                flag = true;
            }
        }
    }
    if (flag == true)
        res = sqrt(res/speed);
}
void InscribedCircle()
{
    for (int i = 0; i < 3; i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    if (xmult(Point(p[0],p[1]),Point(p[0],p[2])) < 0)
        swap(p[1],p[2]);
    for (int i = 0; i < 3; i++)
        len[i] = Point(p[i],p[(i+1)%3]).Length();
    tr = (len[0]+len[1]+len[2])/2;
    r = sqrt((tr-len[0])*(tr-len[1])*(tr-len[2])/tr);
    for (int i = 0; i < 2; i++)
    {
        v = Point(p[i],p[i+1]);
        tv = Point(-v.y,v.x);
        tr = tv.Length();
        tv = Point(tv.x*r/tr,tv.y*r/tr);
        tp = Point(p[i].x+tv.x,p[i].y+tv.y);
        l[i].s = tp;
        tp = Point(p[i+1].x+tv.x,p[i+1].y+tv.y);
        l[i].e = tp;
    }
    tp = LineToLine(l[0],l[1]);
    printf("(%.6f,%.6f,%.6f)\n",tp.x,tp.y,r);
}
// Begin the next step of calculations.
void Engine::Step(bool isActive)
{
	events.swap(eventQueue);
	eventQueue.clear();
	
	// The calculation thread is now paused, so it is safe to access things.
	const shared_ptr<Ship> flagship = player.FlagshipPtr();
	const StellarObject *object = player.GetStellarObject();
	if(object)
	{
		center = object->Position();
		centerVelocity = Point();
	}
	else if(flagship)
	{
		center = flagship->Position();
		centerVelocity = flagship->Velocity();
		if(doEnter && flagship->Zoom() == 1. && !flagship->IsHyperspacing())
		{
			doEnter = false;
			events.emplace_back(flagship, flagship, ShipEvent::JUMP);
		}
		if(flagship->IsEnteringHyperspace()
				|| (flagship->Commands().Has(Command::WAIT) && !flagship->IsHyperspacing()))
		{
			if(jumpCount < 100)
				++jumpCount;
			const System *from = flagship->GetSystem();
			const System *to = flagship->GetTargetSystem();
			if(from && to && from != to)
			{
				jumpInProgress[0] = from;
				jumpInProgress[1] = to;
			}
		}
		else if(jumpCount > 0)
			--jumpCount;
	}
	ai.UpdateEvents(events);
	ai.UpdateKeys(player, clickCommands, isActive && wasActive);
	wasActive = isActive;
	Audio::Update(center);
	
	// Any of the player's ships that are in system are assumed to have
	// landed along with the player.
	if(flagship && flagship->GetPlanet() && isActive)
		player.SetPlanet(flagship->GetPlanet());
	
	const System *currentSystem = player.GetSystem();
	// Update this here, for thread safety.
	if(!player.HasTravelPlan() && flagship && flagship->GetTargetSystem())
		player.TravelPlan().push_back(flagship->GetTargetSystem());
	if(player.HasTravelPlan() && currentSystem == player.TravelPlan().back())
		player.PopTravel();
	if(doFlash)
	{
		flash = .4;
		doFlash = false;
	}
	else if(flash)
		flash = max(0., flash * .99 - .002);
	
	targets.clear();
	
	// Update the player's ammo amounts.
	ammo.clear();
	if(flagship)
		for(const auto &it : flagship->Outfits())
		{
			if(!it.first->Icon())
				continue;
			
			if(it.first->Ammo())
				ammo.emplace_back(it.first,
					flagship->OutfitCount(it.first->Ammo()));
			else if(it.first->FiringFuel())
			{
				double remaining = flagship->Fuel()
					* flagship->Attributes().Get("fuel capacity");
				ammo.emplace_back(it.first,
					remaining / it.first->FiringFuel());
			}
			else
				ammo.emplace_back(it.first, -1);
		}
	
	// Display escort information for all ships of the "Escort" government,
	// and all ships with the "escort" personality, except for fighters that
	// are not owned by the player.
	escorts.Clear();
	bool fleetIsJumping = (flagship && flagship->Commands().Has(Command::JUMP));
	for(const auto &it : ships)
		if(it->GetGovernment()->IsPlayer() || it->GetPersonality().IsEscort())
			if(!it->IsYours() && !it->CanBeCarried())
				escorts.Add(*it, it->GetSystem() == currentSystem, fleetIsJumping);
	for(const shared_ptr<Ship> &escort : player.Ships())
		if(!escort->IsParked() && escort != flagship)
			escorts.Add(*escort, escort->GetSystem() == currentSystem, fleetIsJumping);
	
	// Create the status overlays.
	statuses.clear();
	if(isActive && Preferences::Has("Show status overlays"))
		for(const auto &it : ships)
		{
			if(!it->GetGovernment() || it->GetSystem() != currentSystem || it->Cloaking() == 1.)
				continue;
		
			bool isEnemy = it->GetGovernment()->IsEnemy();
			if(isEnemy || it->GetGovernment()->IsPlayer() || it->GetPersonality().IsEscort())
			{
				double width = min(it->Width(), it->Height());
				statuses.emplace_back(it->Position() - center, it->Shields(), it->Hull(),
					max(20., width * .5), isEnemy);
			}
		}
	
	// Create the planet labels.
	labels.clear();
	if(currentSystem && Preferences::Has("Show planet labels"))
	{
		for(const StellarObject &object : currentSystem->Objects())
		{
			if(!object.GetPlanet())
				continue;
			
			Point pos = object.Position() - center;
			if(pos.Length() < 600. + object.Radius())
				labels.emplace_back(pos, object, currentSystem);
		}
	}
	
	if(flagship && flagship->IsOverheated())
		Messages::Add("Your ship has overheated.");
	
	if(flagship && flagship->Hull())
		info.SetSprite("player sprite", flagship->GetSprite());
	else
		info.SetSprite("player sprite", nullptr);
	if(currentSystem)
		info.SetString("location", currentSystem->Name());
	info.SetString("date", player.GetDate().ToString());
	if(flagship)
	{
		info.SetBar("fuel", flagship->Fuel(),
			flagship->Attributes().Get("fuel capacity") * .01);
		info.SetBar("energy", flagship->Energy());
		info.SetBar("heat", flagship->Heat());
		info.SetBar("shields", flagship->Shields());
		info.SetBar("hull", flagship->Hull(), 20.);
	}
	else
	{
		info.SetBar("fuel", 0.);
		info.SetBar("energy", 0.);
		info.SetBar("heat", 0.);
		info.SetBar("shields", 0.);
		info.SetBar("hull", 0.);
	}
	info.SetString("credits",
		Format::Number(player.Accounts().Credits()) + " credits");
	if(flagship && flagship->GetTargetPlanet() && !flagship->Commands().Has(Command::JUMP))
	{
		const StellarObject *object = flagship->GetTargetPlanet();
		info.SetString("navigation mode", "Landing on:");
		const string &name = object->Name();
		info.SetString("destination", name);
		
		targets.push_back({
			object->Position() - center,
			Angle(45.),
			object->Radius(),
			object->GetPlanet()->CanLand() ? Radar::FRIENDLY : Radar::HOSTILE});
	}
	else if(flagship && flagship->GetTargetSystem())
	{
		info.SetString("navigation mode", "Hyperspace:");
		if(player.HasVisited(flagship->GetTargetSystem()))
			info.SetString("destination", flagship->GetTargetSystem()->Name());
		else
			info.SetString("destination", "unexplored system");
	}
	else
	{
		info.SetString("navigation mode", "Navigation:");
		info.SetString("destination", "no destination");
	}
	// Use the radar that was just populated. (The draw tick-tock has not
	// yet been toggled, but it will be at the end of this function.)
	shared_ptr<const Ship> target;
	targetAngle = Point();
	if(flagship)
		target = flagship->GetTargetShip();
	if(!target)
	{
		info.SetSprite("target sprite", nullptr);
		info.SetString("target name", "no target");
		info.SetString("target type", "");
		info.SetString("target government", "");
		info.SetBar("target shields", 0.);
		info.SetBar("target hull", 0.);
	}
	else
	{
		if(target->GetSystem() == player.GetSystem() && target->Cloaking() < 1.)
			targetUnit = target->Facing().Unit();
		info.SetSprite("target sprite", target->GetSprite(), targetUnit);
		info.SetString("target name", target->Name());
		info.SetString("target type", target->ModelName());
		if(!target->GetGovernment())
			info.SetString("target government", "No Government");
		else
			info.SetString("target government", target->GetGovernment()->GetName());
		
		int targetType = RadarType(*target);
		info.SetOutlineColor(Radar::GetColor(targetType));
		if(target->GetSystem() == player.GetSystem() && target->IsTargetable())
		{
			info.SetBar("target shields", target->Shields());
			info.SetBar("target hull", target->Hull(), 20.);
		
			// The target area will be a square, with sides proportional to the average
			// of the width and the height of the sprite.
			double size = (target->Width() + target->Height()) * .35;
			targets.push_back({
				target->Position() - center,
				Angle(45.) + target->Facing(),
				size,
				targetType});
			
			// Don't show the angle to the target if it is very close.
			targetAngle = target->Position() - center;
			double length = targetAngle.Length();
			if(length > 20.)
				targetAngle /= length;
			else
				targetAngle = Point();
		}
		else
		{
			info.SetBar("target shields", 0.);
			info.SetBar("target hull", 0.);
		}
	}
}
Пример #19
0
// This returns false if it is time to delete this projectile.
bool Projectile::Move(list<Effect> &effects)
{
	if(--lifetime <= 0)
	{
		if(lifetime > -100)
			for(const auto &it : weapon->DieEffects())
				for(int i = 0; i < it.second; ++i)
				{
					effects.push_back(*it.first);
					effects.back().Place(position, velocity, angle);
				}
		
		return false;
	}
	for(const auto &it : weapon->LiveEffects())
		if(!Random::Int(it.second))
		{
			effects.push_back(*it.first);
			effects.back().Place(position, velocity, angle);
		}
	
	// If the target has left the system, stop following it. Also stop if the
	// target has been captured by a different government.
	const Ship *target = cachedTarget;
	if(target)
	{
		target = targetShip.lock().get();
		if(!target || !target->IsTargetable() || target->GetGovernment() != targetGovernment)
		{
			targetShip.reset();
			cachedTarget = nullptr;
			target = nullptr;
		}
	}
	
	double turn = weapon->Turn();
	double accel = weapon->Acceleration();
	int homing = weapon->Homing();
	if(target && homing && !Random::Int(60))
		CheckLock(*target);
	if(target && homing && hasLock)
	{
		Point d = position - target->Position();
		double drag = weapon->Drag();
		double trueVelocity = drag ? accel / drag : velocity.Length();
		double stepsToReach = d.Length() / trueVelocity;
		bool isFacingAway = d.Dot(angle.Unit()) > 0.;
		
		// At the highest homing level, compensate for target motion.
		if(homing >= 4)
		{
			// Adjust the target's position based on where it will be when we
			// reach it (assuming we're pointed right towards it).
			d -= stepsToReach * target->Velocity();
			stepsToReach = d.Length() / trueVelocity;
		}
		
		double cross = d.Unit().Cross(angle.Unit());
		
		// The very dumbest of homing missiles lose their target if pointed
		// away from it.
		if(isFacingAway && homing == 1)
			targetShip.reset();
		else
		{
			double desiredTurn = TO_DEG * asin(cross);
			if(fabs(desiredTurn) > turn)
				turn = copysign(turn, desiredTurn);
			else
				turn = desiredTurn;
			
			// Levels 3 and 4 stop accelerating when facing away.
			if(homing >= 3)
			{
				double stepsToFace = desiredTurn / turn;
		
				// If you are facing away from the target, stop accelerating.
				if(stepsToFace * 1.5 > stepsToReach)
					accel = 0.;
			}
		}
	}
	// If a weapon is homing but has no target, do not turn it.
	else if(homing)
		turn = 0.;
	
	if(turn)
		angle += Angle(turn);
	
	if(accel)
	{
		velocity += accel * angle.Unit();
		velocity *= 1. - weapon->Drag();
	}
	
	position += velocity;
	
	if(target && (position - target->Position()).Length() < weapon->SplitRange() && !Random::Int(10))
		lifetime = 0;
	
	return true;
}
Пример #20
0
PlanetLabel::PlanetLabel(const Point &position, const StellarObject &object, const System *system)
	: position(position), radius(object.Radius())
{
	const Planet &planet = *object.GetPlanet();
	color = object.TargetColor();
	name = planet.Name();
	if(!planet.IsWormhole())
	{
		if(planet.GetGovernment())
		{
			government = "(" + planet.GetGovernment()->GetName() + ")";
			if(planet.CanLand())
			{
				color = planet.GetGovernment()->GetColor();
				color = Color(color.Get()[0] * .5 + .3, color.Get()[1] * .5 + .3, color.Get()[2] * .5 + .3);
			}
			else
				hostility = 3 + 2 * planet.GetGovernment()->IsEnemy();
		}
		else
			government = "(No government)";
	}
	double alpha = min(.5, max(0., .6 - (position.Length() - radius) * .001));
	color = Color(color.Get()[0] * alpha, color.Get()[1] * alpha, color.Get()[2] * alpha, 0.);
	
	if(!system)
		return;
	
	// Figure out how big the label has to be.
	double width = max(FontSet::Get(18).Width(name), FontSet::Get(14).Width(government)) + 8.;
	for(int d = 0; d < 4; ++d)
	{
		bool overlaps = false;
		
		Point start = object.Position() +
			(radius + INNER_SPACE + LINE_GAP + LINE_LENGTH) * Angle(LINE_ANGLE[d]).Unit();
		Point unit(LINE_ANGLE[d] > 180. ? -1. : 1., 0.);
		Point end = start + unit * width;
		
		for(const StellarObject &other : system->Objects())
		{
			if(&other == &object)
				continue;
			
			double minDistance = other.Radius() + MIN_DISTANCE;
			
			double startDistance = other.Position().Distance(start);
			double endDistance = other.Position().Distance(end);
			overlaps |= (startDistance < minDistance || endDistance < minDistance);
			double projection = (other.Position() - start).Dot(unit);
			if(projection > 0. && projection < width)
			{
				double distance = sqrt(startDistance * startDistance - projection * projection);
				overlaps |= (distance < minDistance);
			}
			if(overlaps)
				break;
		}
		if(!overlaps)
		{
			direction = d;
			break;
		}
	}
}
// get/update camera from values in node
GvBool	GvViewpoint::getCamera(GCamera &camera, 
							   BBox &bbox,
							   float visibilityLimitNear,float visibilityLimit,
							   Matrix *cameraTransform)
{
	Matrix m;
    float viewAngle = 0.785398;
	float aspectRatio = 1.0f;
    orientation.get(m);
	camera.position=position;
    viewAngle = (float) fieldOfView;
	Point dir(0,0,-1.0);
	Point up(0,1.0,0);
			
    // apply orientation to standard dir and up vectors
    dir *= m;	
	up *= m;	up.Normalize();
			
    if (cameraTransform) {
        camera.position *= *cameraTransform;
        dir = RotateOnly(*cameraTransform,dir);
        up = RotateOnly(*cameraTransform,up);
        // near far focalDistance ??????????
    }
    dir.Normalize();
    up.Normalize();

	Point size = bbox.Size(); // computed bounding box
		    
	float field = max(max(fabs(size.x),fabs(size.y)),fabs(size.z));
			
	int positionInBox = bbox.Inside(camera.position);
  			
	if (bbox.IsEmpty() || (field<=1E-20f)) 
		field = 2.0f; // no bounding box yet, bad 
  			
            // compute distance to target point
  			//xx float targetDistance = field*2.0f;
  			float targetDistance = field*1.0f;
			
			// viewpoint inside scene 
			if (positionInBox) targetDistance = 0.2 * field; 

            camera.targetDistanceIsDefault=1;
            camera.zrangeIsDefault=1;

            // compute a reasonable z-range 

				if (visibilityLimit >0.0f) {
					camera.zfar = visibilityLimit;
					camera.zrangeIsDefault=0;
				}
				else {
					if (positionInBox) 
						camera.zfar = field*1.5;
					else camera.zfar = field*3.0f;
					
					Point center = bbox.Center();
					Point d = camera.position - center;
					float dist = d.Length();
					
					// make shure object is visible from viewpoint 
					if ((dist+field) > camera.zfar)
						camera.zfar = dist + field;

				}

				if (visibilityLimitNear > 0.0f)
				    camera.znear = visibilityLimitNear;
				else 
					 camera.znear = camera.zfar * camera.znearFactor;

            // compute target 
			camera.target = camera.position + targetDistance*dir;
			camera.up = up;

            // field of view 
			camera.height = 2.0 * tan(viewAngle * 0.5)*targetDistance;
			camera.width = camera.height *  aspectRatio;

			if (!bbox.IsEmpty())
				camera.SetWorldReference(bbox);

			camera.ComputeWorldUpFromUp(); 

            camera.OnChanged();

	return gtrue;

}
Пример #22
0
Float
DottedCornerFinder::FindNext(Float overlap)
{
  Float lower = mLastT;
  Float upper = 1.0f;
  Float t;

  Point C = mLastC;
  Float r = 0.0f;

  Float factor = (1.0f - overlap);

  Float circlesDist = 0.0f;
  Float expectedDist = 0.0f;

  const Float DIST_MARGIN = 0.1f;
  if (mType == SINGLE_CURVE_AND_RADIUS) {
    r = mR0;

    expectedDist = (r + mLastR) * factor;

    // Find C_i on the center curve.
    for (size_t i = 0; i < MAX_LOOP; i++) {
      t = (upper + lower) / 2.0f;
      C = GetBezierPoint(mCenterBezier, t);

      // Check overlap along arc.
      circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
      if (circlesDist < expectedDist - DIST_MARGIN) {
        lower = t;
      } else if (circlesDist > expectedDist + DIST_MARGIN) {
        upper = t;
      } else {
        break;
      }
    }
  } else if (mType == SINGLE_CURVE) {
    // Find C_i on the center curve, and calculate r_i.
    for (size_t i = 0; i < MAX_LOOP; i++) {
      t = (upper + lower) / 2.0f;
      C = GetBezierPoint(mCenterBezier, t);

      Point Diff = GetBezierDifferential(mCenterBezier, t);
      Float DiffLength = Diff.Length();
      if (DiffLength == 0.0f) {
        // Basically this shouldn't happen.
        // If differential is 0, we cannot calculate tangent circle,
        // skip this point.
        t = (t + upper) / 2.0f;
        continue;
      }

      Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign);
      r = CalculateDistanceToEllipticArc(C, normal, mInnerCurveOrigin,
                                         mInnerWidth, mInnerHeight);

      // Check overlap along arc.
      circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
      expectedDist = (r + mLastR) * factor;
      if (circlesDist < expectedDist - DIST_MARGIN) {
        lower = t;
      } else if (circlesDist > expectedDist + DIST_MARGIN) {
        upper = t;
      } else {
        break;
      }
    }
  } else {
    Float distSquareMax = Square(mMaxR * 3.0f);
    Float circlesDistSquare = 0.0f;

    // Find C_i and r_i.
    for (size_t i = 0; i < MAX_LOOP; i++) {
      t = (upper + lower) / 2.0f;
      Point innerTangent = GetBezierPoint(mInnerBezier, t);
      if ((innerTangent - mLastC).LengthSquare() > distSquareMax) {
        // It's clear that this tangent point is too far, skip it.
        upper = t;
        continue;
      }

      Point Diff = GetBezierDifferential(mInnerBezier, t);
      Float DiffLength = Diff.Length();
      if (DiffLength == 0.0f) {
        // Basically this shouldn't happen.
        // If differential is 0, we cannot calculate tangent circle,
        // skip this point.
        t = (t + upper) / 2.0f;
        continue;
      }

      Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign;
      FindPointAndRadius(C, r, innerTangent, normal, t);

      // Check overlap with direct distance.
      circlesDistSquare = (C - mLastC).LengthSquare();
      expectedDist = (r + mLastR) * factor;
      if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) {
        lower = t;
      } else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) {
        upper = t;
      } else {
        break;
      }
    }

    circlesDist = sqrt(circlesDistSquare);
  }

  if (mHasZeroBorderWidth) {
    // When calculating circle around r=0, it may result in wrong radius that
    // is bigger than previous circle.  Detect it and stop calculating.
    const Float R_MARGIN = 0.1f;
    if (mLastR < R_MARGIN && r > mLastR) {
      mHasMore = false;
      mLastR = 0.0f;
      return 0.0f;
    }
  }

  mLastT = t;
  mLastC = C;
  mLastR = r;

  if (mHasZeroBorderWidth) {
    const Float T_MARGIN = 0.001f;
    if (mLastT >= 1.0f - T_MARGIN ||
        (mLastC - mCn).LengthSquare() < Square(mLastR)) {
      mHasMore = false;
    }
  }

  if (expectedDist == 0.0f) {
    return 0.0f;
  }

  return 1.0f - circlesDist * factor / expectedDist;
}
Пример #23
0
void Pony48Engine::drawBoard()
{
	float fTotalWidth = BOARD_WIDTH * TILE_WIDTH + (BOARD_WIDTH + 1) * TILE_SPACING;
	float fTotalHeight = BOARD_HEIGHT * TILE_HEIGHT + (BOARD_HEIGHT + 1) * TILE_SPACING;
	//Fill in bg
	fillRect(Point(-fTotalWidth/2.0, fTotalHeight/2.0), Point(fTotalWidth/2.0, -fTotalHeight/2.0), m_BoardBg);	//Draw at z = 0
	//Fill in bg for individual tiles
	for(int i = 0; i < BOARD_HEIGHT; i++)
	{
		for(int j = 0; j < BOARD_WIDTH; j++)
		{
			//Draw tile bg
			glPushMatrix();
			glTranslatef(0, 0, TILEBG_DRAWZ);
			Point ptDrawPos(-fTotalWidth/2.0 + TILE_SPACING + (TILE_SPACING + TILE_WIDTH) * j,
							fTotalHeight/2.0 - TILE_SPACING - (TILE_SPACING + TILE_HEIGHT) * i);
			fillRect(ptDrawPos, Point(ptDrawPos.x + TILE_WIDTH, ptDrawPos.y - TILE_HEIGHT), m_TileBg[j][i]);
			glPopMatrix();
		}
	}
	
	//Draw joining-tile animations
	for(list<TilePiece*>::iterator i = m_lSlideJoinAnimations.begin(); i != m_lSlideJoinAnimations.end(); i++)
	{
		Point ptDrawPos(-fTotalWidth/2.0 + TILE_SPACING + (TILE_SPACING + TILE_WIDTH) * (*i)->destx,
						fTotalHeight/2.0 - TILE_SPACING - (TILE_SPACING + TILE_HEIGHT) * (*i)->desty);
		glPushMatrix();
		glTranslatef(ptDrawPos.x+TILE_WIDTH/2.0+(*i)->drawSlide.x, ptDrawPos.y-TILE_HEIGHT/2.0+(*i)->drawSlide.y, JOINANIM_DRAWZ);
		(*i)->draw();
		glPopMatrix();
	}
	
	//Draw tiles themselves (separate loop because z-order alpha issues with animations)
	for(int i = 0; i < BOARD_HEIGHT; i++)
	{
		for(int j = 0; j < BOARD_WIDTH; j++)
		{
			Point ptDrawPos(-fTotalWidth/2.0 + TILE_SPACING + (TILE_SPACING + TILE_WIDTH) * j,
							fTotalHeight/2.0 - TILE_SPACING - (TILE_SPACING + TILE_HEIGHT) * i);
			//Draw tile
			if(m_Board[j][i] != NULL)
			{
				glPushMatrix();
				glTranslatef(ptDrawPos.x+TILE_WIDTH/2.0+m_Board[j][i]->drawSlide.x, ptDrawPos.y-TILE_HEIGHT/2.0+m_Board[j][i]->drawSlide.y, TILE_DRAWZ);
				m_Board[j][i]->draw();
				glPopMatrix();
			}
		}
	}
	
	//Draw particle fx for highest tile
	if(m_highestTile != NULL)
	{
		for(int i = 0; i < BOARD_HEIGHT; i++)
		{
			for(int j = 0; j < BOARD_WIDTH; j++)
			{
				//Draw tile
				if(m_Board[j][i] == m_highestTile)
				{
					Point ptDrawPos(-fTotalWidth/2.0 + TILE_SPACING + (TILE_SPACING + TILE_WIDTH) * j,
								fTotalHeight/2.0 - TILE_SPACING - (TILE_SPACING + TILE_HEIGHT) * i);
					glPushMatrix();
					glTranslatef(ptDrawPos.x+TILE_WIDTH/2.0+m_Board[j][i]->drawSlide.x, ptDrawPos.y-TILE_HEIGHT/2.0+m_Board[j][i]->drawSlide.y, TILE_DRAWZ + 0.1);
					m_newHighTile->img = m_highestTile->bg->img;
					m_newHighTile->draw();
					m_newHighTile->img = m_highestTile->seg->img;
					m_newHighTile->draw();
					glPopMatrix();
					break;
				}
			}
		}
		
	}
	
	//Draw arrows for direction the mouse will move the board
	if(m_iMouseControl >= MOUSE_MOVE_TRIP_AMT && m_iCurMode == PLAYING)
	{
		glPushMatrix();
		Point ptMoveDir = worldPosFromCursor(getCursorPos());
		//Rotate first to simplify logic. Hooray!
		switch(getDirOfVec2(ptMoveDir))
		{
			case UP:
				glRotatef(90, 0, 0, 1);
				break;
				
			case DOWN:
				glRotatef(-90, 0, 0, 1);
				break;
				
			case LEFT:
				glRotatef(180, 0, 0, 1);
				break;
		}
		//Determine the drawing alpha based on how far away from the center the mouse is
		float32 fDestAlpha = min(fabs(ptMoveDir.Length() / (getCameraView().height() / 2.0)) - 0.4, 0.4);
		//Draw 16 arrows pointing in the direction we'll move
		for(int y = 0; y < BOARD_HEIGHT; y++)
		{
			for(int x = 0; x < BOARD_WIDTH; x++)
			{
				//Position to draw this arrow at
				Point ptDrawPos(-fTotalWidth/2.0 + (TILE_SPACING + TILE_WIDTH) * x + TILE_WIDTH / 2.0 + TILE_SPACING + m_fArrowAdd,
								fTotalHeight/2.0 - (TILE_SPACING + TILE_WIDTH) * y - TILE_HEIGHT / 2.0 - TILE_SPACING);
				
				//If this arrow is reaching the end of its lifespan, fade out
				float32 fDrawAlpha = fDestAlpha;
				if(m_fArrowAdd >= MOVEARROW_FADEOUTDIST && x == BOARD_WIDTH - 1)
					fDrawAlpha *= 1.0f - ((m_fArrowAdd - MOVEARROW_FADEOUTDIST) / MOVEARROW_FADEOUTDIST);
				fDrawAlpha = min(fDrawAlpha, 1.0f);
				fDrawAlpha = max(fDrawAlpha, 0.0f);
				
				//Now draw
				glColor4f(1,1,1,fDrawAlpha);
				glPushMatrix();
				glTranslatef(ptDrawPos.x, ptDrawPos.y, MOVEARROW_DRAWZ);
				m_imgMouseMoveArrow->render(Point(1,1));
				glPopMatrix();
				
				//See if we should draw new arrow spawning
				if(!x && (ARROW_RESET - m_fArrowAdd) <= MOVEARROW_FADEINDIST)
				{
					fDrawAlpha = fDestAlpha;
					fDrawAlpha *= 1.0 - (ARROW_RESET - m_fArrowAdd) / MOVEARROW_FADEINDIST;
					fDrawAlpha = min(fDrawAlpha, 1.0f);
					fDrawAlpha = max(fDrawAlpha, 0.0f);
					glColor4f(1,1,1,fDrawAlpha);
					glPushMatrix();
					glTranslatef(ptDrawPos.x - (TILE_SPACING + TILE_WIDTH), ptDrawPos.y, MOVEARROW_DRAWZ);
					m_imgMouseMoveArrow->render(Point(1,1));
					glPopMatrix();
				}
			}
		}
		glPopMatrix();
	}
}
Пример #24
0
int main()
{
    while (scanf("%d",&n) != EOF)
    {
        for (int i = 0;i < n;i++)
            scanf("%lf%lf%lf",&c[i].c.x,&c[i].c.y,&c[i].r);
        for (int i = 1;i <= n;i++)
            ans[i] = 0.0;
        for (int i = 0;i < n;i++)
        {
            tote = 0;
            e[tote++] = Event(-pi,1);
            e[tote++] = Event(pi,-1);
            for (int j = 0;j < n;j++)
                if (j != i)
                {
                    lab = Point(c[j].c.x-c[i].c.x,c[j].c.y-c[i].c.y);
                    AB = lab.Length();
                    AC = c[i].r;
                    BC = c[j].r;
                    if (cmp(AB+AC,BC) <= 0)
                    {
                        e[tote++] = Event(-pi,1);
                        e[tote++] = Event(pi,-1);
                        continue;
                    }
                    if (cmp(AB+BC,AC) <= 0) continue;
                    if (cmp(AB,AC+BC) > 0)  continue;
                    theta = atan2(lab.y,lab.x);
                    fai = acos((AC*AC+AB*AB-BC*BC)/(2.0*AC*AB));
                    a0 = theta-fai;
                    if (cmp(a0,-pi) < 0)    a0 += 2*pi;
                    a1 = theta+fai;
                    if (cmp(a1,pi) > 0)     a1 -= 2*pi;
                    if (cmp(a0,a1) > 0)
                    {
                        e[tote++] = Event(a0,1);
                        e[tote++] = Event(pi,-1);
                        e[tote++] = Event(-pi,1);
                        e[tote++] = Event(a1,-1);
                    }
                    else
                    {
                        e[tote++] = Event(a0,1);
                        e[tote++] = Event(a1,-1);
                    }
                }
            sort(e,e+tote,Eventcmp);
            cur = 0;
            for (int j = 0;j < tote;j++)
            {
                if (cur != 0 && cmp(e[j].tim,pre[cur]) != 0)
                {
                    ans[cur] += Area(e[j].tim-pre[cur],c[i].r);
                    ans[cur] += xmult(Point(c[i].c.x+c[i].r*cos(pre[cur]),c[i].c.y+c[i].r*sin(pre[cur])),
                                        Point(c[i].c.x+c[i].r*cos(e[j].tim),c[i].c.y+c[i].r*sin(e[j].tim)))/2.0;
                }
                cur += e[j].typ;
                pre[cur] = e[j].tim;
            }
        }
        for (int i = 1;i < n;i++)
            ans[i] -= ans[i+1];
        for (int i = 1;i <= n;i++)
            printf("[%d] = %.3f\n",i,ans[i]);
    }
    return 0;
}
Пример #25
0
float Point::Distance(const Point& p0, const Point& p1) {
  Point diff = p1 - p0;
  return diff.Length();
}
Пример #26
0
// Move this ship. A ship may create effects as it moves, in particular if
// it is in the process of blowing up. If this returns false, the ship
// should be deleted.
bool Ship::Move(list<Effect> &effects)
{
	// Check if this ship has been in a different system from the player for so
	// long that it should be "forgotten." Also eliminate ships that have no
	// system set because they just entered a fighter bay.
	forget += !isInSystem;
	if((!isSpecial && forget >= 1000) || !currentSystem)
		return false;
	isInSystem = false;
	if(!fuel || !(attributes.Get("hyperdrive") || attributes.Get("jump drive")))
		hyperspaceSystem = nullptr;
	
	// Handle ionization effects.
	if(ionization)
	{
		ionization *= .99;
		
		const Effect *effect = GameData::Effects().Get("ion spark");
		double ion = ionization * .1;
		while(!forget)
		{
			ion -= Random::Real();
			if(ion <= 0.)
				break;
			
			Point point((Random::Real() - .5) * .5 * sprite.Width(),
				(Random::Real() - .5) * .5 * sprite.Height());
			if(sprite.GetMask(0).Contains(point, Angle()))
			{
				effects.push_back(*effect);
				effects.back().Place(angle.Rotate(point) + position, velocity, angle);
			}
		}
	}
	// Jettisoned cargo effects.
	static const int JETTISON_BOX = 5;
	if(jettisoned >= JETTISON_BOX)
	{
		jettisoned -= JETTISON_BOX;
		effects.push_back(*GameData::Effects().Get("box"));
		effects.back().Place(position, velocity, angle);
	}
	
	// When ships recharge, what actually happens is that they can exceed their
	// maximum capacity for the rest of the turn, but must be clamped to the
	// maximum here before they gain more. This is so that, for example, a ship
	// with no batteries but a good generator can still move.
	energy = min(energy, attributes.Get("energy capacity"));
	
	heat *= heatDissipation;
	if(heat > Mass() * 100.)
		isOverheated = true;
	else if(heat < Mass() * 90.)
		isOverheated = false;
	
	double maxShields = attributes.Get("shields");
	shields = min(shields, maxShields);
	double maxHull = attributes.Get("hull");
	hull = min(hull, maxHull);
	isDisabled = isOverheated || IsDisabled();
	
	// Update ship supply levels.
	if(!isDisabled)
	{
		// If you have a ramscoop, you recharge enough fuel to make one jump in
		// a little less than a minute - enough to be an inconvenience without
		// being totally aggravating.
		if(attributes.Get("ramscoop"))
			TransferFuel(-.03 * sqrt(attributes.Get("ramscoop")), nullptr);
		
		energy += attributes.Get("energy generation") - ionization;
		energy = max(0., energy);
		heat += attributes.Get("heat generation");
		heat -= attributes.Get("cooling");
		heat = max(0., heat);
		
		// Hull repair.
		double oldHull = hull;
		hull = min(hull + attributes.Get("hull repair rate"), maxHull);
		static const double HULL_EXCHANGE_RATE = 1.;
		energy -= HULL_EXCHANGE_RATE * (hull - oldHull);
		
		// Recharge shields, but only up to the max. If there is extra shield
		// energy, use it to recharge fighters and drones.
		shields += attributes.Get("shield generation");
		static const double SHIELD_EXCHANGE_RATE = 1.;
		energy -= SHIELD_EXCHANGE_RATE * attributes.Get("shield generation");
		double excessShields = max(0., shields - maxShields);
		shields -= excessShields;
		
		for(Bay &bay : fighterBays)
		{
			if(!bay.ship)
				continue;
			
			double myGen = bay.ship->Attributes().Get("shield generation");
			double myMax = bay.ship->Attributes().Get("shields");
			bay.ship->shields = min(myMax, bay.ship->shields + myGen);
			if(excessShields > 0. && bay.ship->shields < myMax)
			{
				double extra = min(myMax - bay.ship->shields, excessShields);
				bay.ship->shields += extra;
				excessShields -= extra;
			}
		}
		for(Bay &bay : droneBays)
		{
			if(!bay.ship)
				continue;
			
			double myGen = bay.ship->Attributes().Get("shield generation");
			double myMax = bay.ship->Attributes().Get("shields");
			bay.ship->shields = min(myMax, bay.ship->shields + myGen);
			if(excessShields > 0. && bay.ship->shields < myMax)
			{
				double extra = min(myMax - bay.ship->shields, excessShields);
				bay.ship->shields += extra;
				excessShields -= extra;
			}
		}
		// If you do not need the shield generation, apply the extra back to
		// your energy. On the other hand, if recharging shields drives your
		// energy negative, undo that part of the recharge.
		energy += SHIELD_EXCHANGE_RATE * excessShields;
		if(energy < 0.)
		{
			shields += energy / SHIELD_EXCHANGE_RATE;
			energy = 0.;
		}
	}
	
	
	if(IsDestroyed())
	{
		// Once we've created enough little explosions, die.
		if(explosionCount == explosionTotal || forget)
		{
			if(!forget)
			{
				const Effect *effect = GameData::Effects().Get("smoke");
				double scale = .015 * (sprite.Width() + sprite.Height()) + .5;
				double radius = .1 * (sprite.Width() + sprite.Height());
				int debrisCount = attributes.Get("mass") * .07;
				for(int i = 0; i < debrisCount; ++i)
				{
					effects.push_back(*effect);
					
					Angle angle = Angle::Random();
					Point effectVelocity = velocity + angle.Unit() * (scale * Random::Real());
					Point effectPosition = position + radius * angle.Unit();
					effects.back().Place(effectPosition, effectVelocity, angle);
				}
					
				for(unsigned i = 0; i < explosionTotal / 2; ++i)
					CreateExplosion(effects, true);
			}
			energy = 0.;
			heat = 0.;
			ionization = 0.;
			fuel = 0.;
			return false;
		}
		
		// If the ship is dead, it first creates explosions at an increasing
		// rate, then disappears in one big explosion.
		++explosionRate;
		if(Random::Int(1024) < explosionRate)
			CreateExplosion(effects);
	}
	else if(hyperspaceSystem || hyperspaceCount)
	{
		fuel -= (hyperspaceSystem != nullptr);
		
		// Enter hyperspace.
		int direction = (hyperspaceSystem != nullptr) - (hyperspaceSystem == nullptr);
		hyperspaceCount += direction;
		static const int HYPER_C = 100;
		static const double HYPER_A = 2.;
		static const double HYPER_D = 1000.;
		bool hasJumpDrive = (hyperspaceType == 200);
		
		// Create the particle effects for the jump drive. This may create 100
		// or more particles per ship per turn at the peak of the jump.
		if(hasJumpDrive && !forget)
		{
			int count = hyperspaceCount;
			count *= sprite.Width() * sprite.Height();
			count /= 160000;
			const Effect *effect = GameData::Effects().Get("jump drive");
			while(--count >= 0)
			{
				Point point((Random::Real() - .5) * .5 * sprite.Width(),
					(Random::Real() - .5) * .5 * sprite.Height());
				if(sprite.GetMask(0).Contains(point, Angle()))
				{
					effects.push_back(*effect);
					Point vel = velocity + 5. * Angle::Random(360.).Unit();
					effects.back().Place(angle.Rotate(point) + position, vel, angle);
				}
			}
		}
		
		if(hyperspaceCount == HYPER_C)
		{
			currentSystem = hyperspaceSystem;
			// If the jump fuel is higher than 100, expend extra fuel now.
			fuel -= hyperspaceType - HYPER_C;
			hyperspaceSystem = nullptr;
			SetTargetSystem(nullptr);
			SetTargetPlanet(nullptr);
			direction = -1;
			
			Point target;
			for(const StellarObject &object : currentSystem->Objects())
				if(object.GetPlanet() && object.GetPlanet()->HasSpaceport())
				{
					target = object.Position();
					break;
				}
			if(GetDestination())
				for(const StellarObject &object : currentSystem->Objects())
					if(object.GetPlanet() == GetDestination())
					{
						target = object.Position();
						break;
					}
			
			if(hasJumpDrive)
			{
				position = target + Angle::Random().Unit() * 300. * (Random::Real() + 1.);
				return true;
			}
			
			// Have all ships exit hyperspace at the same distance so that
			// your escorts always stay with you.
			double distance = (HYPER_C * HYPER_C) * .5 * HYPER_A + HYPER_D;
			position = (target - distance * angle.Unit());
			position += hyperspaceOffset;
			// Make sure your velocity is in exactly the direction you are
			// traveling in, so that when you decelerate there will not be a
			// sudden shift in direction at the end.
			velocity = velocity.Length() * angle.Unit();
		}
		if(!hasJumpDrive)
		{
			velocity += (HYPER_A * direction) * angle.Unit();
			if(!hyperspaceSystem)
			{
				// Exit hyperspace far enough from the planet to be able to land.
				// This does not take drag into account, so it is always an over-
				// estimate of how long it will take to stop.
				// We start decellerating after rotating about 150 degrees (that
				// is, about acos(.8) from the proper angle). So:
				// Stopping distance = .5*a*(v/a)^2 + (150/turn)*v.
				// Exit distance = HYPER_D + .25 * v^2 = stopping distance.
				double exitV = MaxVelocity();
				double a = (.5 / Acceleration() - .25);
				double b = 150. / TurnRate();
				double discriminant = b * b - 4. * a * -HYPER_D;
				if(discriminant > 0.)
				{
					double altV = (-b + sqrt(discriminant)) / (2. * a);
					if(altV > 0. && altV < exitV)
						exitV = altV;
				}
				if(velocity.Length() <= exitV)
				{
					velocity = angle.Unit() * exitV;
					hyperspaceCount = 0;
				}
			}
		}
		position += velocity;
		if(GetParent() && GetParent()->currentSystem == currentSystem)
		{
			hyperspaceOffset = position - GetParent()->position;
			double length = hyperspaceOffset.Length();
			if(length > 1000.)
				hyperspaceOffset *= 1000. / length;
		}
		
		return true;
	}
	else if(landingPlanet || zoom < 1.)
	{
		// Special ships do not disappear forever when they land; they
		// just slowly refuel.
		if(landingPlanet && zoom)
		{
			// Move the ship toward the center of the planet while landing.
			if(GetTargetPlanet())
				position = .97 * position + .03 * GetTargetPlanet()->Position();
			zoom -= .02;
			if(zoom < 0.)
			{
				// If this is not a special ship, it ceases to exist when it
				// lands on a true planet. If this is a wormhole, the ship is
				// instantly transported.
				if(landingPlanet->IsWormhole())
				{
					currentSystem = landingPlanet->WormholeDestination(currentSystem);
					for(const StellarObject &object : currentSystem->Objects())
						if(object.GetPlanet() == landingPlanet)
							position = object.Position();
					SetTargetPlanet(nullptr);
					landingPlanet = nullptr;
				}
				else if(!isSpecial || personality.IsFleeing())
					return false;
				
				zoom = 0.;
			}
		}
		// Only refuel if this planet has a spaceport.
		else if(fuel == attributes.Get("fuel capacity")
				|| !landingPlanet || !landingPlanet->HasSpaceport())
		{
			zoom = min(1., zoom + .02);
			landingPlanet = nullptr;
		}
		else
			fuel = min(fuel + 1., attributes.Get("fuel capacity"));
		
		// Move the ship at the velocity it had when it began landing, but
		// scaled based on how small it is now.
		position += velocity * zoom;
		
		return true;
	}
	if(commands.Has(Command::LAND) && CanLand())
		landingPlanet = GetTargetPlanet()->GetPlanet();
	else if(commands.Has(Command::JUMP))
	{
		hyperspaceType = CheckHyperspace();
		if(hyperspaceType)
			hyperspaceSystem = GetTargetSystem();
	}
	
	double cloakingSpeed = attributes.Get("cloak");
	bool canCloak = (zoom == 1. && !isDisabled && !hyperspaceCount && cloakingSpeed
		&& fuel >= attributes.Get("cloaking fuel")
		&& energy >= attributes.Get("cloaking energy"));
	if(commands.Has(Command::CLOAK) && canCloak)
	{
		cloak = min(1., cloak + cloakingSpeed);
		fuel -= attributes.Get("cloaking fuel");
		energy -= attributes.Get("cloaking energy");
	}
	else if(cloakingSpeed)
		cloak = max(0., cloak - cloakingSpeed);
	else
		cloak = 0.;
	
	int requiredCrew = RequiredCrew();
	if(pilotError)
		--pilotError;
	else if(pilotOkay)
		--pilotOkay;
	else if(requiredCrew && static_cast<int>(Random::Int(requiredCrew)) >= Crew())
	{
		pilotError = 30;
		Messages::Add("Your ship is moving erratically because you do not have enough crew to pilot it.");
	}
	else
		pilotOkay = 30;
	
	// This ship is not landing or entering hyperspace. So, move it. If it is
	// disabled, all it can do is slow down to a stop.
	double mass = Mass();
	if(isDisabled)
		velocity *= 1. - attributes.Get("drag") / mass;
	else if(!pilotError)
	{
		double thrustCommand = commands.Has(Command::FORWARD) - commands.Has(Command::BACK);
		Point acceleration;
		if(thrustCommand)
		{
			// Check if we are able to apply this thrust.
			double cost = attributes.Get((thrustCommand > 0.) ?
				"thrusting energy" : "reverse thrusting energy");
			if(energy < cost)
				thrustCommand = 0.;
			else
			{
				// If a reverse thrust is commanded and the capability does not
				// exist, ignore it (do not even slow under drag).
				double thrust = attributes.Get((thrustCommand > 0.) ?
					"thrust" : "reverse thrust");
				if(!thrust)
					thrustCommand = 0.;
				else
				{
					energy -= cost;
					heat += attributes.Get((thrustCommand > 0.) ?
						"thrusting heat" : "reverse thrusting heat");
					acceleration += angle.Unit() * (thrustCommand * thrust / mass);
				}
			}
		}
		bool applyAfterburner = commands.Has(Command::AFTERBURNER) && !CannotAct();
		if(applyAfterburner)
		{
			double thrust = attributes.Get("afterburner thrust");
			double cost = attributes.Get("afterburner fuel");
			double energyCost = attributes.Get("afterburner energy");
			if(!thrust || fuel < cost || energy < energyCost)
				applyAfterburner = false;
			else
			{
				heat += attributes.Get("afterburner heat");
				fuel -= cost;
				energy -= energyCost;
				acceleration += angle.Unit() * thrust / mass;
				
				if(!forget)
					for(const Point &point : enginePoints)
					{
						Point pos = angle.Rotate(point) * .5 * Zoom() + position;
						for(const auto &it : attributes.AfterburnerEffects())
							for(int i = 0; i < it.second; ++i)
							{
								effects.push_back(*it.first);
								effects.back().Place(pos + velocity, velocity - 6. * angle.Unit(), angle);
							}
					}
			}
		}
		if(acceleration)
		{
			Point dragAcceleration = acceleration - velocity * (attributes.Get("drag") / mass);
			// Make sure dragAcceleration has nonzero length, to avoid divide by zero.
			if(dragAcceleration)
			{
				// What direction will the net acceleration be if this drag is applied?
				// If the net acceleration will be opposite the thrust, do not apply drag.
				dragAcceleration *= .5 * (acceleration.Unit().Dot(dragAcceleration.Unit()) + 1.);
				velocity += dragAcceleration;
			}
		}
		if(commands.Turn())
		{
			// Check if we are able to turn.
			double cost = attributes.Get("turning energy");
			if(energy < cost)
				commands.SetTurn(0.);
			else
			{
				energy -= cost;
				heat += attributes.Get("turning heat");
				angle += commands.Turn() * TurnRate();
			}
		}
	}
	
	// Boarding:
	if(isBoarding && (commands.Has(Command::FORWARD | Command::BACK) || commands.Turn()))
		isBoarding = false;
	shared_ptr<const Ship> target = (CanBeCarried() ? GetParent() : GetTargetShip());
	if(target && !isDisabled)
	{
		Point dp = (target->position - position);
		double distance = dp.Length();
		Point dv = (target->velocity - velocity);
		double speed = dv.Length();
		isBoarding |= (distance < 50. && speed < 1. && commands.Has(Command::BOARD));
		if(isBoarding && !CanBeCarried())
		{
			if(!target->IsDisabled() && government->IsEnemy(target->government))
				isBoarding = false;
			else if(target->IsDestroyed() || target->IsLanding() || target->IsHyperspacing()
					|| target->GetSystem() != GetSystem())
				isBoarding = false;
		}
		if(isBoarding && !pilotError)
		{
			Angle facing = angle;
			bool left = target->Unit().Cross(facing.Unit()) < 0.;
			double turn = left - !left;
			
			// Check if the ship will still be pointing to the same side of the target
			// angle if it turns by this amount.
			facing += TurnRate() * turn;
			bool stillLeft = target->Unit().Cross(facing.Unit()) < 0.;
			if(left != stillLeft)
				turn = 0.;
			angle += TurnRate() * turn;
			
			velocity += dv.Unit() * .1;
			position += dp.Unit() * .5;
			
			if(distance < 10. && speed < 1. && (CanBeCarried() || !turn))
			{
				isBoarding = false;
				if(government->IsEnemy(target->government) && target->Attributes().Get("self destruct"))
				{
					Messages::Add("The " + target->ModelName() + " \"" + target->Name()
						+ "\" has activated its self-destruct mechanism.");
					shared_ptr<Ship> victim = targetShip.lock();
					victim->hull = -1.;
					victim->explosionRate = 1024;
				}
				else
					hasBoarded = true;
			}
		}
	}
	
	// And finally: move the ship!
	position += velocity;
	
	return true;
}
Пример #27
0
// Fire whichever of the given ship's weapons can hit a hostile target.
Command AI::AutoFire(const Ship &ship, const list<shared_ptr<Ship>> &ships, bool secondary) const
{
	Command command;
	int index = -1;
	
	// Special case: your target is not your enemy. Do not fire, because you do
	// not want to risk damaging that target. The only time a ship other than
	// the player will target a friendly ship is if the player has asked a ship
	// for assistance.
	shared_ptr<Ship> currentTarget = ship.GetTargetShip();
	const Government *gov = ship.GetGovernment();
	bool isSharingTarget = ship.IsYours() && currentTarget == sharedTarget.lock();
	bool currentIsEnemy = currentTarget
		&& currentTarget->GetGovernment()->IsEnemy(gov)
		&& currentTarget->GetSystem() == ship.GetSystem();
	if(currentTarget && !(currentIsEnemy || isSharingTarget))
		currentTarget.reset();
	
	// Only fire on disabled targets if you don't want to plunder them.
	bool spareDisabled = (ship.GetPersonality().Disables() || ship.GetPersonality().Plunders());
	
	// Find the longest range of any of your non-homing weapons.
	double maxRange = 0.;
	for(const Armament::Weapon &weapon : ship.Weapons())
		if(weapon.IsReady() && !weapon.IsHoming() && (secondary || !weapon.GetOutfit()->Icon()))
			maxRange = max(maxRange, weapon.GetOutfit()->Range());
	// Extend the weapon range slightly to account for velocity differences.
	maxRange *= 1.5;
	
	// Find all enemy ships within range of at least one weapon.
	vector<shared_ptr<const Ship>> enemies;
	if(currentTarget)
		enemies.push_back(currentTarget);
	for(auto target : ships)
		if(target->IsTargetable() && gov->IsEnemy(target->GetGovernment())
				&& target->Velocity().Length() < 20.
				&& target->GetSystem() == ship.GetSystem()
				&& target->Position().Distance(ship.Position()) < maxRange
				&& target != currentTarget)
			enemies.push_back(target);
	
	for(const Armament::Weapon &weapon : ship.Weapons())
	{
		++index;
		// Skip weapons that are not ready to fire. Also skip homing weapons if
		// no target is selected, and secondary weapons if only firing primaries.
		if(!weapon.IsReady() || (!currentTarget && weapon.IsHoming()))
			continue;
		if(!secondary && weapon.GetOutfit()->Icon())
			continue;
		
		// Special case: if the weapon uses fuel, be careful not to spend so much
		// fuel that you cannot leave the system if necessary.
		if(weapon.GetOutfit()->FiringFuel())
		{
			double fuel = ship.Fuel() * ship.Attributes().Get("fuel capacity");
			fuel -= weapon.GetOutfit()->FiringFuel();
			// If the ship is not ever leaving this system, it does not need to
			// reserve any fuel.
			bool isStaying = ship.GetPersonality().IsStaying();
			if(!secondary || fuel < (isStaying ? 0. : ship.JumpFuel()))
				continue;
		}
		// Figure out where this weapon will fire from, but add some randomness
		// depending on how accurate this ship's pilot is.
		Point start = ship.Position() + ship.Facing().Rotate(weapon.GetPoint());
		start += ship.GetPersonality().Confusion();
		
		const Outfit *outfit = weapon.GetOutfit();
		double vp = outfit->Velocity();
		double lifetime = outfit->TotalLifetime();
		
		if(currentTarget && (weapon.IsHoming() || weapon.IsTurret()))
		{
			bool hasBoarded = Has(ship, currentTarget, ShipEvent::BOARD);
			if(currentTarget->IsDisabled() && spareDisabled && !hasBoarded)
				continue;
			
			Point p = currentTarget->Position() - start;
			Point v = currentTarget->Velocity() - ship.Velocity();
			// By the time this action is performed, the ships will have moved
			// forward one time step.
			p += v;
			
			if(p.Length() < outfit->BlastRadius())
				continue;
			
			double steps = Armament::RendevousTime(p, v, vp);
			if(steps == steps && steps <= lifetime)
			{
				command.SetFire(index);
				continue;
			}
		}
		// Don't fire homing weapons with no target.
		if(weapon.IsHoming())
			continue;
		
		for(const shared_ptr<const Ship> &target : enemies)
		{
			if(!target->IsTargetable()
					|| target->Velocity().Length() > 20.
					|| target->GetSystem() != ship.GetSystem())
				continue;
			
			// Don't shoot ships we want to plunder.
			bool hasBoarded = Has(ship, target, ShipEvent::BOARD);
			if(target->IsDisabled() && spareDisabled && !hasBoarded)
				continue;
			
			Point p = target->Position() - start;
			Point v = target->Velocity() - ship.Velocity();
			// By the time this action is performed, the ships will have moved
			// forward one time step.
			p += v;
			
			// Get the vector the weapon will travel along.
			v = (ship.Facing() + weapon.GetAngle()).Unit() * vp - v;
			// Extrapolate over the lifetime of the projectile.
			v *= lifetime;
			
			const Mask &mask = target->GetSprite().GetMask(step);
			if(mask.Collide(-p, v, target->Facing()) < 1.)
			{
				command.SetFire(index);
				break;
			}
		}
	}
	
	return command;
}
Пример #28
0
void MapPanel::DrawSystems() const
{
	if(commodity == SHOW_GOVERNMENT)
		closeGovernments.clear();
	
	// Draw the circles for the systems, colored based on the selected criterion,
	// which may be government, services, or commodity prices.
	for(const auto &it : GameData::Systems())
	{
		const System &system = it.second;
		// Referring to a non-existent system in a mission can create a spurious
		// system record. Ignore those.
		if(system.Name().empty())
			continue;
		if(!player.HasSeen(&system) && &system != specialSystem)
			continue;
		
		Point pos = Zoom() * (system.Position() + center);
		
		Color color = UninhabitedColor();
		if(!player.HasVisited(&system))
			color = UnexploredColor();
		else if(system.IsInhabited())
		{
			if(commodity >= SHOW_SPECIAL)
			{
				double value = 0.;
				bool showUninhabited = false;
				if(commodity >= 0)
				{
					const Trade::Commodity &com = GameData::Commodities()[commodity];
					double price = system.Trade(com.name);
					showUninhabited = !price;
					value = (2. * (price - com.low)) / (com.high - com.low) - 1.;
				}
				else if(commodity == SHOW_SHIPYARD)
				{
					double size = 0;
					for(const StellarObject &object : system.Objects())
						if(object.GetPlanet())
							size += object.GetPlanet()->Shipyard().size();
					value = size ? min(10., size) / 10. : -1.;
				}
				else if(commodity == SHOW_OUTFITTER)
				{
					double size = 0;
					for(const StellarObject &object : system.Objects())
						if(object.GetPlanet())
							size += object.GetPlanet()->Outfitter().size();
					value = size ? min(60., size) / 60. : -1.;
				}
				else if(commodity == SHOW_VISITED)
				{
					bool all = true;
					bool some = false;
					for(const StellarObject &object : system.Objects())
						if(object.GetPlanet())
						{
							bool visited = player.HasVisited(object.GetPlanet());
							all &= visited;
							some |= visited;
						}
					value = -1 + some + all;
				}
				else
					value = SystemValue(&system);
				
				color = (showUninhabited ? UninhabitedColor() : MapColor(value));
			}
			else if(commodity == SHOW_GOVERNMENT)
			{
				const Government *gov = system.GetGovernment();
				color = GovernmentColor(gov);
				
				// For every government that is draw, keep track of how close it
				// is to the center of the view. The four closest governments
				// will be displayed in the key.
				double distance = pos.Length();
				auto it = closeGovernments.find(gov);
				if(it == closeGovernments.end())
					closeGovernments[gov] = distance;
				else
					it->second = min(it->second, distance);
			}
			else
			{
				double reputation = system.GetGovernment()->Reputation();
				
				bool hasDominated = true;
				bool isInhabited = false;
				bool canLand = false;
				for(const StellarObject &object : system.Objects())
					if(object.GetPlanet() && object.GetPlanet()->HasSpaceport())
					{
						canLand |= object.GetPlanet()->CanLand();
						isInhabited |= object.GetPlanet()->IsInhabited();
						hasDominated &= (!object.GetPlanet()->IsInhabited()
							|| GameData::GetPolitics().HasDominated(object.GetPlanet()));
					}
				hasDominated &= isInhabited;
				color = ReputationColor(reputation, canLand, canLand && hasDominated);
			}
		}
		
		RingShader::Draw(pos, OUTER, INNER, color);
	}
}
Пример #29
0
void CircleCurve2d :: NormalVector (const Point<2> & p, Vec<2> & n) const
  {
  n = p - center;
  n /= n.Length();
  }