bool CNavigationPath::PathIsClear (CSystem *pSystem,
								   CSovereign *pSovereign,
								   const CVector &vFrom, 
								   const CVector &vTo, 
								   CSpaceObject **retpEnemy, 
								   CVector *retvAway)

//	PathIsClear
//
//	Returns TRUE if the path from vFrom to vTo is free from enemy stations.
//	If FALSE, retpEnemy is initialized with the enemy that is blocking the
//	path and retvAway is a unit vector away from the enemy that avoids it

	{
	int i;

	//	Loop over all objects in the system

	CSpaceObject *pNearestEnemy = NULL;
	Metric rNearestDist = MAX_SAFE_DIST;
	CVector vNearestAway;

	for (i = 0; i < pSystem->GetObjectCount(); i++)
		{
		CSpaceObject *pObj = pSystem->GetObject(i);
		CSovereign *pObjSovereign;

		if (pObj
				&& (pObj->GetScale() == scaleStructure 
					|| ((pObj->GetScale() == scaleShip) && (pObj->GetVel().Length2() < MIN_SPEED2)))
				&& (pObjSovereign = pObj->GetSovereign())
				&& (pObjSovereign->IsEnemy(pSovereign))
				&& pObj->CanAttack())
			{
			CVector vAway;
			Metric rDist = CalcDistanceToPath(pObj->GetPos(), vFrom, vTo, NULL, &vAway);
			if (rDist < rNearestDist)
				{
				rNearestDist = rDist;
				pNearestEnemy = pObj;
				vNearestAway = vAway;
				}
			}
		}

	//	If we found a threatening object, return it

	if (pNearestEnemy)
		{
		if (retpEnemy)
			*retpEnemy = pNearestEnemy;
		if (retvAway)
			*retvAway = vNearestAway;
		return false;
		}

	//	Otherwise, the path is OK

	return true;
	}
void CParticleEffect::OnUpdate (Metric rSecondsPerTick)

//	OnUpdate
//
//	Update the effect

	{
	int iTick = GetSystem()->GetTick() + GetDestiny();

	//	Do not bother updating everything if we are far from the POV

	bool bFarAway = false;
	if (g_pUniverse->GetPOV() 
			&& g_pUniverse->GetCurrentSystem() == GetSystem())
		{
		Metric rPOVDist2 = (GetPos() - g_pUniverse->GetPOV()->GetPos()).Length2();
		Metric rMaxUpdateDist2 = LIGHT_SECOND * LIGHT_SECOND * 3600;
		bFarAway = (rPOVDist2 > rMaxUpdateDist2);
		}

	//	Update the particles

	SParticleArray *pGroup = m_pFirstGroup;
	while (pGroup)
		{
		SParticleType *pType = pGroup->pType;

		//	Max distance for a particle in this group

		Metric rMaxDist2 = pType->rRadius * pType->rRadius;
		Metric rMinDist2 = pType->rHoleRadius * pType->rHoleRadius;

		//	If the particle field causes damage then we need to
		//	compute its average density

		int iDensity = 0;
		if (pType->m_fDamage)
			{
			Metric rRadius2 = pType->rRadius * pType->rRadius;
			Metric rArea = rRadius2 / (LIGHT_SECOND * LIGHT_SECOND);
			iDensity = (int)(4 * pGroup->iCount / rArea);
			}

		//	Get an array of objects in the particle field that
		//	may influence the particles

		CSpaceObject *Objects[ctMaxObjsInField];
		int iObjCount = 0;
		if (!bFarAway && (pType->m_fWake || pType->m_fDamage))
			{
			Metric rMaxInfluenceDist2 = rMaxDist2;
			for (int i = 0; i < GetSystem()->GetObjectCount(); i++)
				{
				CSpaceObject *pObj = GetSystem()->GetObject(i);

				if (pObj 
						&& pObj->GetCategory() == catShip
						&& pObj != this)
					{
					CVector vDist = GetPos() - pObj->GetPos();
					Metric rDist2 = vDist.Length2();

					if (rDist2 < rMaxInfluenceDist2 
							&& (pObj->GetVel().Length2() > g_KlicksPerPixel)
							&& iObjCount < ctMaxObjsInField)
						{
						Objects[iObjCount++] = pObj;

						//	See if the object should take damage

						if (pType->m_fDamage)
							{
							CVector vDeltaV = pObj->GetVel() - GetVel();
							int iSpeed = (int)(vDeltaV.Length() / g_KlicksPerPixel);
							if (iSpeed == 0)
								iSpeed = 1;

							if (mathRandom(1, 1000) < (iDensity * iSpeed))
								{
								pObj->Damage(this,
										pObj->GetPos(),
										VectorToPolar(vDeltaV),
										pType->Damage);
								}
							}
						}
					}
				}
			}

		//	If we're computing drag then we need to compute the new velocity
		//	of the whole particle system

		CVector vNewVel;
		if (pType->m_fDrag)
			vNewVel = GetVel() * g_SpaceDragFactor;

		//	Iterate over all particles

		SParticle *pParticle = pGroup->pParticles;
		SParticle *pEnd = pParticle + pGroup->iCount;
		while (pParticle < pEnd)
			{
			if (pParticle->IsValid())
				{
				//	Lifespan. If we're far away and we're regenerating,
				//	then don't bother to compute lifespan.

				if (pType->m_fLifespan 
						&& !(bFarAway && (pType->m_fRegenerate && pType->iRegenerationTimer)))
					{
					if (--pParticle->iLifeLeft == 0)
						{
						//	Do we regenerate?

						if (pType->m_fRegenerate && pType->iRegenerationTimer)
							{
							pParticle->iLifeLeft = pType->iLifespan;
							pParticle->vPos = NullVector;

							//	Speed

							Metric rSpeed = mathRandom(1, 100) * (pType->rAveSpeed / 100.0);
							if (pType->iDirection == -1)
								pParticle->vVel = PolarToVector(mathRandom(0, 359), rSpeed);
							else
								{
								int iAngle = (pType->iDirection + 360 + mathRandom(0, 2 * pType->iDirRange) - pType->iDirRange) % 360;
								pParticle->vVel = PolarToVector(iAngle, rSpeed);
								}
							}

						//	Otherwise we die

						else
							{
							pParticle->iLifeLeft = -1;
							pGroup->iAlive--;
							pParticle++;
							continue;
							}
						}
					}

				//	Update the position

				if (!bFarAway)
					{
					pParticle->vPos = pParticle->vPos + pParticle->vVel;

					//	Change the velocity to keep the particles within
					//	the radius

					if (pType->m_fMaxRadius)
						{
						Metric rDist2 = pParticle->vPos.Length2();
						if (pType->m_fMaxRadius && rDist2 > rMaxDist2)
							{
							CVector vChange = pParticle->vPos + g_KlicksPerPixel * pParticle->vPos.Perpendicular().Normal();
							pParticle->vVel = pParticle->vVel - (0.00005 * vChange);
							}
						else if (rDist2 < rMinDist2)
							{
							CVector vNormal = pParticle->vPos.Normal();
							CVector vChange = g_KlicksPerPixel * (400 * vNormal - 50 * vNormal.Perpendicular());
							pParticle->vVel = pParticle->vVel + (0.00005 * vChange);
							}
						else
							pParticle->vVel = pParticle->vVel * pType->rDampening;
						}

					if (pType->m_fDrag)
						{
						//	Compute the new absolute velocity (after drag)
						CVector vAbsolute = pType->rDampening * (pParticle->vVel + GetVel());

						//	The particle velocity is the absolute vel minus the
						//	system velocity.
						pParticle->vVel = vAbsolute - vNewVel;
						}

					//	Change the velocity based on influences from other objects

					if (pType->m_fWake && (iTick % 4) == 0)
						{
						for (int i = 0; i < iObjCount; i++)
							{
							Metric rDist2 = (Objects[i]->GetPos() - (pParticle->vPos + GetPos())).Length2();
							if (rDist2 < g_KlicksPerPixel * g_KlicksPerPixel * 1000)
								{
								if (Objects[i]->GetVel().Dot(pParticle->vVel) < Objects[i]->GetVel().Length2())
									pParticle->vVel = pParticle->vVel + 0.2 * Objects[i]->GetVel();
								}
							}
						}
					}
				}

			pParticle++;
			}

		//	Regeneration timer

		if (pType->m_fRegenerate && pType->iRegenerationTimer)
			pType->iRegenerationTimer--;

		//	If there are no more particles left alive in this group then kill
		//	the group

		if (pGroup->iAlive == 0)
			{
			SParticleArray *pNext = pGroup->pNext;
			SParticleArray *pPrev = NULL;

			//	Find the previous group

			SParticleArray *pFind = m_pFirstGroup;
			while (pFind != pGroup)
				{
				if (pPrev)
					pPrev = pPrev->pNext;
				else
					pPrev = m_pFirstGroup;

				pFind = pFind->pNext;
				}

			//	Fix up the linked list

			if (pPrev)
				pPrev->pNext = pNext;
			else
				m_pFirstGroup = pNext;

			//	Delete the group

			delete pGroup;
			pGroup = pNext;
			}

		//	Otherwise, next group

		else
			pGroup = pGroup->pNext;
		}

	//	If we have no more groups then we destroy ourselves

	if (m_pFirstGroup == NULL)
		{
		Destroy(removedFromSystem, NULL);
		return;
		}

	//	If we're moving, slow down

	SetVel(CVector(GetVel().GetX() * g_SpaceDragFactor, GetVel().GetY() * g_SpaceDragFactor));
	}