Пример #1
0
void        CweaponIGC::FireWeapon(Time now)
{
	assert(m_mountID >= 0);
	assert(m_ship);

	bool    fFiredShot = false;

	if (m_fActive && (m_nextFire < now))
	{
		Time    lastUpdate = m_ship->GetLastUpdate();

		//Never fire retroactively.
		if (m_nextFire < lastUpdate)
			m_nextFire = lastUpdate;

		bool    fSelected = fIsWeaponSelected();

		float   energy = m_ship->GetEnergy();                   //the energy the ship would have at "now"
		if (energy >= m_typeData->energyPerShot)
		{
			//We'd be able to fire before now and would have enough energy at now to fire, so ...
			float   rechargeRate = m_ship->GetHullType()->GetRechargeRate();

			float   energyDeficit = (m_nextFire - now) *            //this is how much we are in the hole because we
				rechargeRate;                   //are shooting sooner than "now" (must be < 0)

			short     ammo = m_ship->GetAmmo();

			if ((ammo > 0) || (m_typeData->cAmmoPerShot == 0))
			{
				// we are firing at least once this frame
				fFiredShot = true;
				m_pMission->GetIgcSite()->PlayFFEffect(effectFire, m_pshipGunner ? m_pshipGunner : m_ship);

				//Get the stuff that won't change between shots
				IclusterIGC*    cluster = m_ship->GetCluster();
				assert(cluster);

				const Orientation&  shipOrientation = m_ship->GetOrientation();

				//Orientation         orientationBfr;
				const Orientation*  pMyOrientation;
				if (m_pshipGunner)
				{
					/*
					orientationBfr = m_pshipGunner->GetOrientation() * (*m_porientation * shipOrientation);
					pMyOrientation = &orientationBfr;
					*/
					pMyOrientation = &(m_pshipGunner->GetOrientation());
				}
				else
				{
					pMyOrientation = &shipOrientation;
				}

				//This is how much energy deficit recovers between shots
				float   dtimeBurst = GetDtBurst();

				float   recharge = rechargeRate * dtimeBurst;

				const Vector&       myVelocity = m_ship->GetVelocity();
				Vector              myPosition = m_ship->GetPosition() + *m_pposition * shipOrientation; //*pMyOrientation;


				/*
				m_ship->GetThingSite()->AddMuzzleFlare(
					m_emissionPt,
					min(dtimeBurst * 0.5f, 0.05f)
				);
				*/

				DataProjectileIGC   dataProjectile;
				dataProjectile.projectileTypeID = m_projectileType->GetObjectID();

				float   lifespan = GetLifespan();

				float   speed = m_projectileType->GetSpeed();
				if (m_typeData->cAmmoPerShot)
					speed *= m_ship->GetSide()->GetGlobalAttributeSet().GetAttribute(c_gaSpeedAmmo);
				bool    absoluteF = m_projectileType->GetAbsoluteF();

				const Vector*   ppositionTarget;
				const Vector*   pvelocityTarget;
				ImodelIGC*  ptarget = NULL;
				if (m_projectileType->GetBlastPower() != 0.0f)
				{
					if (m_pshipGunner)
						ptarget = m_pshipGunner->GetCommandTarget(c_cmdCurrent);
					else
						ptarget = m_ship->GetCommandTarget(c_cmdCurrent);

					if (ptarget)
					{
						if ((ptarget->GetCluster() == cluster) && m_ship->CanSee(ptarget))
						{
							ppositionTarget = &(ptarget->GetPosition());
							pvelocityTarget = &(ptarget->GetVelocity());
						}
						else
							ptarget = NULL;
					}
				}

				int nShots = fSelected ? 10 : 0;    //Only allow a single shot if the weapon is no longer selected

				do
				{
					if (energy + energyDeficit < m_typeData->energyPerShot)
					{
						//We don't have enough energy to fire at our prefered time ... so wait until we do.
						m_nextFire += (m_typeData->energyPerShot - (energy + energyDeficit)) / rechargeRate;
					}

					//Permute the "forward" direction slightly by a random amount
					dataProjectile.forward = pMyOrientation->GetForward();
					if (m_typeData->dispersion != 0.0f)
					{
						float   r = random(0.0f, m_typeData->dispersion);
						float   a = random(0.0f, 2.0f * pi);
						dataProjectile.forward += (r * cos(a)) * pMyOrientation->GetRight();
						dataProjectile.forward += (r * sin(a)) * pMyOrientation->GetUp();

						dataProjectile.forward.SetNormalize();
					}

					dataProjectile.velocity = speed * dataProjectile.forward;
					if (!absoluteF)
						dataProjectile.velocity += myVelocity;

					Vector  position = myPosition + myVelocity * (m_nextFire - lastUpdate);
					dataProjectile.lifespan = lifespan;
					if (ptarget)
					{
						Vector      dV = *pvelocityTarget - dataProjectile.velocity;

						float       dV2 = dV.LengthSquared();
						if (dV2 != 0.0f)
						{
							Vector      dP = position - *ppositionTarget;  //reverse so time has right sense

							dataProjectile.lifespan = (dV * dP) / dV2;
							if (dataProjectile.lifespan > lifespan)
								dataProjectile.lifespan = lifespan;
							else if (dataProjectile.lifespan < 0.1f)
								dataProjectile.lifespan = 0.1f;     //Don't let it explode in our face
						}
					}

					IprojectileIGC*  p = (IprojectileIGC*)(m_pMission->CreateObject(m_nextFire, OT_projectile,
						&dataProjectile, sizeof(dataProjectile)));
					if (p)
					{
						if (m_pshipGunner)
							p->SetGunner(m_pshipGunner);
						else
							p->SetLauncher(m_ship);

						p->SetPosition(position);

						p->SetCluster(cluster);

						p->Release();
					}

					energyDeficit += rechargeRate * dtimeBurst;
					energy -= m_typeData->energyPerShot;
					ammo -= m_typeData->cAmmoPerShot;

					m_nextFire += dtimeBurst;
				} while ((nShots-- > 0) &&
					(m_nextFire < now) &&
					(energy + energyDeficit >= m_typeData->energyPerShot) &&
					(ammo > 0));

				m_ship->SetAmmo(ammo > 0 ? ammo : 0);

				if ((ammo == 0) && (m_typeData->cAmmoPerShot != 0))
					fSelected = false;

				m_ship->SetEnergy(energy);
			}
		}

		if (!fSelected)
			Deactivate();
	}

	// if we fired a shot, keep track of it (note - stored localy because the 
	// call to deactivate (above) clears the member variable).
	m_fFiringShot = fFiredShot;

	// if we are still firing and have the energy and ammo for the next shot,
	// assume we are firing a burst.
	if ((m_ship->GetEnergy() >= m_typeData->energyPerShot)
		&& (m_ship->GetAmmo() >= m_typeData->cAmmoPerShot)
		&& (m_fFiringBurst || (m_fActive && m_fFiringShot)) // a burst starts with a shot
		)
	{
		m_fFiringBurst = true;
	}
	else
	{
		m_fFiringBurst = false;
	}

}
void    CprobeIGC::Update(Time now)
{
    if (now >= m_timeExpire)	
        GetMyMission()->GetIgcSite()->KillProbeEvent(this);
    else
    {
        {
            float   dt = m_probeType->GetRipcordDelay();
            if (dt >= 0.0f)
            {
                Time    timeActivate = m_time0 + dt;
                if ((GetMyLastUpdate() < timeActivate) &&
                    (now >= timeActivate))
                {
                    GetMyMission()->GetIgcSite()->ActivateTeleportProbe(this);
                }
            }
        }

        if (m_projectileType)
        {
            if (m_nextFire < now)
            {
                IclusterIGC*    pcluster = GetCluster();
                assert (pcluster);

                //We'll be able to take a shot
                float   lifespan = GetProjectileLifespan();
                IsideIGC*   pside = GetSide();

                const Vector&       myPosition = GetPosition();

                float   speed = m_projectileType->GetSpeed();
                if (m_ammo != 0)
                    speed *= GetSide()->GetGlobalAttributeSet().GetAttribute(c_gaSpeedAmmo);

                float   accuracy = GetAccuracy();
                float   dtimeBurst = GetDtBurst();
                float   dispersion = m_probeType->GetDispersion();
                Time    lastUpdate = GetMyLastUpdate();
                if (m_nextFire < lastUpdate)
                    m_nextFire = lastUpdate;
                assert (m_nextFire <= now);

                TmodelIGC<IprobeIGC>::Update(now);

                float   dtUpdate = m_nextFire - lastUpdate;

                //If we have a target ... find the closest enemy ship who is a valid target
                ExpendableAbilityBitMask    eabm = m_probeType->GetCapabilities();
                float       distance2Min = speed * lifespan / 1.2f;
                distance2Min *= distance2Min;
                Vector      directionMin;



                ImodelIGC*  pmodelTarget = NULL;
                if (eabm & c_eabmShootOnlyTarget)
                {
                    if (m_target && (m_target->GetCluster() == pcluster))
                    {
                        ObjectType  type = m_target->GetObjectType();
                        ValidTarget((type == OT_ship) ? ((IshipIGC*)(ImodelIGC*)m_target)->GetSourceShip() : m_target,
                                    pside, myPosition, dtUpdate, accuracy, speed, lifespan, type,
                                    &pmodelTarget, &distance2Min, &directionMin);
                    }
                }
                else
                {
                    if (eabm & c_eabmShootShips)
                    {
                        //Threats to stations get highest priority
                        GetTarget((const ModelListIGC*)(pcluster->GetShips()),
                                  pside, myPosition, dtUpdate, accuracy, speed, lifespan, OT_ship,
                                  &pmodelTarget, &distance2Min, &directionMin);
                    }

                    if (eabm & c_eabmShootMissiles)
                    {
                        GetTarget((const ModelListIGC*)(pcluster->GetMissiles()),
                                  pside, myPosition, dtUpdate, accuracy, speed, lifespan, OT_missile, 
                                  &pmodelTarget, &distance2Min, &directionMin);
                    }

                    if (eabm & c_eabmShootStations)
                    {
                        GetTarget((const ModelListIGC*)(pcluster->GetStations()),
                                  pside, myPosition, dtUpdate, accuracy, speed, lifespan, OT_station, 
                                  &pmodelTarget, &distance2Min, &directionMin);
                    }
                }

                if (pmodelTarget)
                {
                    if (m_launcher && (m_launcher->GetMission() != GetMyMission()))
                        m_launcher = NULL;

                    //It is going to shoot ... make it visible to everyone in the sector
                    if (!m_bSeenByAll)
                    {
                        m_bSeenByAll = true;
                        for (SideLinkIGC*   psl = m_pMission->GetSides()->first();
                             (psl != NULL);
                             psl = psl->next())
                        {
                            IsideIGC*   psideOther = psl->data();

                            if (!SeenBySide(psideOther))
                            {
                                //Does this side have any scanners in the sector?
                                ClusterSite*    pcs = pcluster->GetClusterSite();
                                const ScannerListIGC*   psl = pcs->GetScanners(psideOther->GetObjectID());
                                if ((psl->n() != 0) || (m_pMission->GetMissionParams()->bAllowAlliedViz && psideOther->AlliedSides(psideOther,pside))) //ALLY 7/3/09 VISIBILITY 7/11/09 imago
                                    SetSideVisibility(psideOther, true);
                                else
                                    m_bSeenByAll = false;
                            }
                        }
                    }

                    //We have a target ... fire along directionMin (modulo dispersion)
                    Orientation o = GetOrientation();
                    o.TurnTo(directionMin);
                    SetOrientation(o);
                    Vector  position = myPosition + m_probeType->GetEmissionPt() * o;

                    DataProjectileIGC   dataProjectile;
                    dataProjectile.projectileTypeID = m_projectileType->GetObjectID();

                    short   nShots = 0;
                    do
                    {
                        //Permute the "forward" direction slightly by a random amount
                        dataProjectile.forward = directionMin;
                        if (dispersion != 0.0f)
                        {
                            float   r = random(0.0f, dispersion);
                            float   a = random(0.0f, 2.0f * pi);
                            dataProjectile.forward += (r * cos(a)) * o.GetRight();
                            dataProjectile.forward += (r * sin(a)) * o.GetUp();

                            dataProjectile.forward.SetNormalize();
                        }

                        //We never move, so skip all the velocity calculations
                        dataProjectile.velocity = speed * dataProjectile.forward;

                        dataProjectile.lifespan = lifespan;

                        IprojectileIGC*  p = (IprojectileIGC*)(m_pMission->CreateObject(m_nextFire, OT_projectile, 
                                                                                        &dataProjectile, sizeof(dataProjectile)));
                        assert (p);
                        {
                            p->SetLauncher(m_launcher ? ((ImodelIGC*)m_launcher) : ((ImodelIGC*)this));
                            p->SetPosition(position);

                            p->SetCluster(pcluster);

                            p->Release();
                        }

                        nShots++;
                        m_nextFire += dtimeBurst;
                    }
                    while (m_nextFire < now);

                    if (m_ammo > 0)
                    {
                        m_ammo -= nShots;
                        if (m_ammo <= 0)
                        {
                            m_ammo = 0;
                            GetMyMission()->GetIgcSite()->KillProbeEvent(this);
                        }
                    }
                }
                else
                {
                    //No shots this cycle
                    m_nextFire = now;
                }
            }
        }

        TmodelIGC<IprobeIGC>::Update(now);
    }
}