Пример #1
0
void CParticleDamage::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick)

//	OnUpdate
//
//	Update

	{
	m_iTick++;

	//	Update the single particle painter

	if (m_pPainter)
		m_pPainter->OnUpdate();

	//	Set up context block for particle array update

	SEffectUpdateCtx EffectCtx;
	EffectCtx.pSystem = GetSystem();
	EffectCtx.pObj = this;

	EffectCtx.pDamageDesc = m_pDesc;
	EffectCtx.iTotalParticleCount = m_iParticleCount;
	EffectCtx.pEnhancements = m_pEnhancements;
	EffectCtx.iCause = m_iCause;
	EffectCtx.bAutomatedWeapon = IsAutomatedWeapon();
	EffectCtx.Attacker = m_Source;

	//	Update (includes doing damage)

	m_Particles.Update(EffectCtx);

	//	If we're tracking, change velocity to follow target

	if (m_pTarget && m_pDesc->IsTrackingTime(m_iTick))
		m_Particles.UpdateTrackTarget(m_pTarget, m_pDesc->GetManeuverRate(), m_pDesc->GetRatedSpeed());

	//	Expired?

	if (--m_iLifeLeft <= 0)
		{
		Destroy(removedFromSystem, CDamageSource());
		return;
		}

	//	Emit new particles

	if (m_iTick < m_iEmitTime && !m_Source.IsEmpty())
		{
		InitParticles(m_pDesc->GetParticleCount(),
				m_vEmitSourcePos - GetPos(),
				GetVel(),
				m_iEmitDirection);
		}
	}
Пример #2
0
void CBeam::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick)

//	OnUpdate
//
//	Update the beam

{
    DEBUG_TRY

    bool bDestroy = false;

    m_iTick++;

    //	See if the beam hit anything

    if (m_pHit)
    {
        //	Tell the object hit that it has been damaged

        SDamageCtx Ctx;
        Ctx.pObj = m_pHit;
        Ctx.pDesc = m_pDesc;
        Ctx.Damage = m_pDesc->m_Damage;
        Ctx.Damage.AddBonus(m_iBonus);
        Ctx.Damage.SetCause(m_iCause);
        if (IsAutomatedWeapon())
            Ctx.Damage.SetAutomatedWeapon();
        Ctx.iDirection = (m_iHitDir + 360 + mathRandom(0, 30) - 15) % 360;
        Ctx.vHitPos = m_vPaintTo;
        Ctx.pCause = this;
        Ctx.Attacker = m_Source;

        EDamageResults result = m_pHit->Damage(Ctx);

        //	Set the beam to destroy itself after a hit

        if (m_pDesc->GetPassthrough() == 0
                || result == damageNoDamage
                || result == damageAbsorbedByShields
                || mathRandom(1, 100) > m_pDesc->GetPassthrough())
            bDestroy = true;
    }

    //	See if the beam has faded out

    if (bDestroy || --m_iLifeLeft <= 0)
        Destroy(removedFromSystem, CDamageSource());

    DEBUG_CATCH
}
Пример #3
0
void CMissile::CreateFragments (const CVector &vPos)

//	CreateFragments
//
//	Create fragments

{
    //	If there is an event, then let it handle the fragmentation

    if (m_pDesc->FireOnFragment(m_Source, this, vPos, m_pHit, m_pTarget))
        return;

    //	NOTE: Missile fragments don't inherit the velocity of the missile
    //	(otherwise, fragmentation weapons explode too late to do much damage)

    if (m_pDesc->HasFragments())
        GetSystem()->CreateWeaponFragments(m_pDesc,
                                           m_iBonus,
                                           m_iCause,
                                           m_Source,
                                           m_pTarget,
                                           vPos,
                                           CVector(),
                                           this);

    //	Create the hit effect

    SDamageCtx Ctx;
    Ctx.pObj = NULL;
    Ctx.pDesc = m_pDesc;
    Ctx.Damage = m_pDesc->m_Damage;
    Ctx.Damage.AddBonus(m_iBonus);
    Ctx.Damage.SetCause(m_iCause);
    if (IsAutomatedWeapon())
        Ctx.Damage.SetAutomatedWeapon();
    Ctx.iDirection = mathRandom(0, 359);
    Ctx.vHitPos = vPos;
    Ctx.pCause = this;
    Ctx.Attacker = m_Source;

    m_pDesc->CreateHitEffect(GetSystem(), Ctx);
}
Пример #4
0
void CMissile::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick)

//	OnUpdate
//
//	Update the beam

{
    //	If we're already destroyed, then just update the timer until the
    //	vapor trail fades out

    if (m_fDestroyed)
    {
        //	Update the painter

        if (m_pPainter)
        {
            m_pPainter->OnUpdate();

            //	LATER: We shouldn't have to update bounds here because
            //	it is set in OnMove.

            SetBounds(m_pPainter);
        }

        //	Done?

        if (--m_iLifeLeft <= 0)
        {
            Destroy(removedFromSystem, CDamageSource());
            return;
        }
    }

    //	Otherwise, update

    else
    {
        int i;
        CSystem *pSystem = GetSystem();
        int iTick = m_iTick + GetDestiny();
        bool bDestroy = false;

        //	Accelerate, if necessary

        if (m_pDesc->m_iAccelerationFactor > 0
                && (iTick % 10 ) == 0)
        {
            if (m_pDesc->m_iAccelerationFactor < 100
                    || GetVel().Length() < m_pDesc->m_rMaxMissileSpeed)
                SetVel(GetVel() * (Metric)(m_pDesc->m_iAccelerationFactor / 100.0));
        }

        //	If we can choose new targets, see if we need one now

        if (m_pDesc->CanAutoTarget() && m_pTarget == NULL)
            m_pTarget = GetNearestEnemy(MAX_TARGET_RANGE, false);

        //	If this is a tracking missile, change direction to face the target

        if (m_pDesc->IsTrackingTime(iTick)
                && m_pTarget)
        {
            //	Get the position and velocity of the target

            CVector vTarget = m_pTarget->GetPos() - GetPos();
            CVector vTargetVel = m_pTarget->GetVel() - GetVel();

            //	Figure out which direction to move in

            int iFireAngle;
            Metric rCurrentSpeed = GetVel().Length();
            Metric rTimeToIntercept = CalcInterceptTime(vTarget, vTargetVel, rCurrentSpeed);
            if (rTimeToIntercept > 0.0)
            {
                CVector vInterceptPoint = vTarget + vTargetVel * rTimeToIntercept;
                iFireAngle = VectorToPolar(vInterceptPoint, NULL);
            }
            else
                iFireAngle = VectorToPolar(vTarget);

            //	Turn to desired direction.

            if (!AreAnglesAligned(iFireAngle, m_iRotation, 1))
            {
                int iTurn = (iFireAngle + 360 - m_iRotation) % 360;

                if (iTurn >= 180)
                {
                    int iTurnAngle = Min((360 - iTurn), m_pDesc->GetManeuverRate());
                    m_iRotation = (m_iRotation + 360 - iTurnAngle) % 360;
                }
                else
                {
                    int iTurnAngle = Min(iTurn, m_pDesc->GetManeuverRate());
                    m_iRotation = (m_iRotation + iTurnAngle) % 360;
                }
            }

            SetVel(PolarToVector(m_iRotation, rCurrentSpeed));
        }

        //	Update exhaust

        if (m_pExhaust)
        {
            if (iTick % m_pDesc->m_iExhaustRate)
            {
                if (m_pExhaust->GetCount() == m_pExhaust->GetMaxCount())
                    m_pExhaust->Dequeue();

                SExhaustParticle &New = m_pExhaust->GetAt(m_pExhaust->Queue());
                New.vPos = GetPos();
                New.vVel = GetVel();
            }

            for (int i = 0; i < m_pExhaust->GetCount(); i++)
            {
                SExhaustParticle &Particle = m_pExhaust->GetAt(i);
                Particle.vVel = m_pDesc->m_rExhaustDrag * Particle.vVel;
                Particle.vPos = Particle.vPos + Particle.vVel * g_SecondsPerUpdate;
            }
        }

        //	Update the painter

        if (m_pPainter)
        {
            m_pPainter->OnUpdate();

            //	LATER: We shouldn't have to update bounds here because
            //	it is set in OnMove.

            SetBounds(m_pPainter);
        }

        //	If we have a vapor trail and need to save rotation, do it

        if (m_pDesc->GetVaporTrailLength()
                && m_pDesc->IsTracking())
        {
            //	Compute the current rotation

            int iDirection = (m_iRotation + 180) % 360;

            //	Add the current rotation to the list of saved rotations

            if (m_pSavedRotations == NULL)
            {
                m_pSavedRotations = new int [m_pDesc->GetVaporTrailLength()];
                m_iSavedRotationsCount = 0;
            }

            int iStart = Min(m_iSavedRotationsCount, m_pDesc->GetVaporTrailLength() - 1);
            for (i = iStart; i > 0; i--)
                m_pSavedRotations[i] = m_pSavedRotations[i - 1];

            m_pSavedRotations[0] = iDirection;
            if (m_iSavedRotationsCount < m_pDesc->GetVaporTrailLength())
                m_iSavedRotationsCount++;
        }

        //	See if the missile hit anything

        if (m_fDetonate && m_pDesc->ProximityBlast())
        {
            CreateFragments(GetPos());
            bDestroy = true;
        }
        else if (m_pHit)
        {
            //	If we have fragments, then explode now

            if (m_iHitDir == -1
                    && m_pDesc->ProximityBlast()
                    && m_iTick >= m_pDesc->GetProximityFailsafe())
            {
                CreateFragments(m_vHitPos);
                bDestroy = true;
            }

            //	Otherwise, if this was a direct hit, then we do damage

            else if (m_iHitDir != -1)
            {
                SDamageCtx DamageCtx;
                DamageCtx.pObj = m_pHit;
                DamageCtx.pDesc = m_pDesc;
                DamageCtx.Damage = m_pDesc->m_Damage;
                DamageCtx.Damage.AddBonus(m_iBonus);
                DamageCtx.Damage.SetCause(m_iCause);
                if (IsAutomatedWeapon())
                    DamageCtx.Damage.SetAutomatedWeapon();
                DamageCtx.iDirection = (m_iHitDir + 360 + mathRandom(0, 30) - 15) % 360;
                DamageCtx.vHitPos = m_vHitPos;
                DamageCtx.pCause = this;
                DamageCtx.Attacker = m_Source;

                EDamageResults result = m_pHit->Damage(DamageCtx);

                //	If we hit another missile (or some small object) there is a chance
                //	that we continue

                if (result == damagePassthrough || result == damagePassthroughDestroyed)
                {
                    m_iHitPoints = m_iHitPoints / 2;
                    bDestroy = (m_iHitPoints == 0);
                }

                //	Set the missile to destroy itself after a hit, if we did not
                //	pass through

                else if (!m_fPassthrough)
                    bDestroy = true;
            }
        }

        //	See if the missile has faded out

        if (bDestroy || --m_iLifeLeft <= 0)
        {
            //	If this is a fragmentation weapon, then we explode at the end of life

            if (!bDestroy && m_pDesc->ProximityBlast())
                CreateFragments(GetPos());

            //	If we've got a vapor trail effect, then keep the missile object alive
            //	but mark it destroyed

            int iFadeLife;
            if (m_pDesc->GetVaporTrailLength())
            {
                m_fDestroyed = true;
                m_iLifeLeft = m_pDesc->GetVaporTrailLength();
            }

            //	If we've got an effect that needs time to fade out, then keep
            //	the missile object alive

            else if (m_pPainter && (iFadeLife = m_pPainter->GetFadeLifetime()))
            {
                m_pPainter->OnBeginFade();

                m_fDestroyed = true;
                m_iLifeLeft = iFadeLife;
            }

            //	Otherwise, destroy the missile

            else
            {
                Destroy(removedFromSystem, CDamageSource());
                return;
            }
        }
    }

    m_iTick++;
}
Пример #5
0
void CParticleEffect::OnUpdate (SUpdateCtx &Ctx, 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->pDamageDesc)
			{
			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->pDamageDesc))
			{
			Metric rMaxInfluenceDist2 = rMaxDist2;
			for (int i = 0; i < GetSystem()->GetObjectCount(); i++)
				{
				CSpaceObject *pObj = GetSystem()->GetObject(i);

				if (pObj 
						&& pObj->GetCategory() == catShip
						&& !pObj->IsInactive()
						&& pObj->CanBeHit()
						&& !pObj->IsDestroyed()
						&& 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->pDamageDesc)
							{
							CVector vDeltaV = pObj->GetVel() - GetVel();
							int iSpeed = (int)(vDeltaV.Length() / g_KlicksPerPixel);
							if (iSpeed == 0)
								iSpeed = 1;

							if (mathRandom(1, 1000) < (iDensity * iSpeed))
								{
								SDamageCtx Ctx;
								Ctx.pObj = pObj;
								Ctx.pDesc = pType->pDamageDesc;
								Ctx.Damage = pType->pDamageDesc->m_Damage;
								if (IsAutomatedWeapon())
									Ctx.Damage.SetAutomatedWeapon();
								Ctx.iDirection = VectorToPolar(vDeltaV);
								Ctx.vHitPos = pObj->GetPos();
								Ctx.pCause = this;
								Ctx.Attacker = CDamageSource(this, killedByDamage);

								pObj->Damage(Ctx);
								}
							}
						}
					}
				}
			}

		//	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, CDamageSource());
		return;
		}

	//	If we're moving, slow down

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