void CFractureEffect::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick)

//	OnUpdate
//
//	Update the effect

	{
	//	See if the effect has faded out

	m_iTick++;
	if (m_iTick >= m_iLifeTime)
		{
		Destroy(removedFromSystem, CDamageSource());
		return;
		}

	//	Update particles

	if (m_pParticles)
		{
		SParticle *pParticle = m_pParticles;
		SParticle *pParticleEnd = pParticle + m_iParticleCount;

		//	If we have an attractor, accelerate all particles towards it

		if (m_pAttractor)
			{
			bool bParticlesLeft = false;

			//	Compute the position of the attractor in the particle
			//	coordinate system.

			CVector vDist = m_pAttractor->GetPos() - GetPos();
			int xAttract = FIXED_POINT * (int)(vDist.GetX() / g_KlicksPerPixel);
			int yAttract = -FIXED_POINT * (int)(vDist.GetY() / g_KlicksPerPixel);

			while (pParticle < pParticleEnd)
				{
				int xDelta = xAttract - pParticle->x;
				int yDelta = yAttract - pParticle->y;
				int xDist = abs(xDelta);
				int yDist = abs(yDelta);

				//	If within threshold, then particles do not move

				if (xDist < (8 * FIXED_POINT) && yDist < (8 * FIXED_POINT))
					;

				//	Otherwise, move towards attractor

				else
					{
					int iDist = Max(xDist, yDist);
					int iSpeed = 256 * 256 * FIXED_POINT / iDist;
					iSpeed = Min(iSpeed, 4 * FIXED_POINT);

					int iFade = (m_iTick > 100 ? 0 : (100 - m_iTick));
					int xV = (iSpeed * xDelta / iDist) + (iFade * pParticle->xV / 100);
					int yV = (iSpeed * yDelta / iDist) + (iFade * pParticle->yV / 100);

					pParticle->x += xV;
					pParticle->y += yV;

					bParticlesLeft = true;
					}

				pParticle++;
				}

			//	If no particles left, destroy the effect

			if (!bParticlesLeft)
				{
				Destroy(removedFromSystem, CDamageSource());
				return;
				}
			}

		//	Otherwise, follow normal trajectory

		else
			{
			while (pParticle < pParticleEnd)
				{
				pParticle->x += pParticle->xV;
				pParticle->y += pParticle->yV;

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

	//	If we're moving, slow down

	SetVel(CVector(GetVel().GetX() * g_SpaceDragFactor, GetVel().GetY() * g_SpaceDragFactor));
	}
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));
	}
Beispiel #3
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++;
}
int _Spit::Callback(unsigned int msg, unsigned int wParam, int lParam)
{
	switch(msg)
	{
	case ENTITYMSG_UPDATE:
		//update velocity
		SetVel(GetDir()*m_spd.MoveUpdate(g_timeElapse));

		//update sound position
		if(m_tSnd)
		{
			BASS_3DVECTOR pos, orient, vel;

			memcpy(&pos, (float*)GetLoc(), sizeof(pos)); pos.z *= -1;
			memcpy(&orient, (float*)GetDir(), sizeof(orient)); orient.z *= -1;
			memcpy(&vel, (float*)GetVel(), sizeof(vel)); vel.z *= -1;

			BASS_ChannelSet3DPosition(m_tSnd, &pos, &orient, &vel);
		}
		break;

	case ENTITYMSG_ENTITYCOLLIDE://, (WPARAM)pEntity
		{
			EntityCommon *pEntity = (EntityCommon *)wParam;
			EntityCommon *pOwner = (EntityCommon *)IDPageQuery(GetOwner());

			//check to see if the owner is not the same type as entity
			if(pOwner)
			{
				if(pOwner->GetEntityType() != pEntity->GetEntityType())
				{
					//check to see if the entity is a creature
					if((pEntity->GetEntityType() == ENTITY_TYPE_TATA
						|| pEntity->GetEntityType() == ENTITY_TYPE_ENEMY)
						&& !pEntity->CheckFlag(CRE_FLAG_SPITIMMUNE))
					{
						//hit this fella
						Creature *pCre = (Creature *)pEntity;

						if(pCre->Hit())
						{
							int sndInd = -1;
							pOwner->Callback(ENTITYMSG_REQUESTSOUND, SND_REQ_PROJ_HIT_CRE, (LPARAM)&sndInd);

							pCre->CREPlaySound(sndInd);
						}

						SetFlag(ENTITY_FLAG_POLLDEATH, true);
					}
				}
			}

			if(!m_bBounce || m_bounceCur == m_bounceMax)
				SetFlag(ENTITY_FLAG_POLLDEATH, true);
			else
			{
				//bounce off
				gfxTrace *pTrace = (gfxTrace*)lParam;

				D3DXVECTOR3 norm(pTrace->norm), refl;

				float nd = D3DXVec3Dot(&GetDir(), &norm);

				refl = GetDir() - (2*nd*norm);

				SetDir(refl);

				m_bounceCur++;
			}
		}
		break;

	case ENTITYMSG_WORLDCOLLIDE:
		{
			if(!m_bBounce || m_bounceCur == m_bounceMax)
				SetFlag(ENTITY_FLAG_POLLDEATH, true);
			else
			{
				//bounce off
				gfxTrace *pTrace = (gfxTrace*)lParam;

				D3DXVECTOR3 norm(pTrace->norm), refl;

				float nd = D3DXVec3Dot(&GetDir(), &norm);

				refl = GetDir() - (2*nd*norm);

				SetDir(refl);

				m_bounceCur++;
			}
		}
		break;

	case ENTITYMSG_DEATH:
		//cool FX explosion
		break;

	case ENTITYMSG_ALLOWGRAVITY:
		//simply don't allow gravity
		return 0;
	}

	return RETCODE_SUCCESS;
}