/* ** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved. ** ** File: weaponIGC.cpp ** ** Author: ** ** Description: ** Implementation of the CweaponIGC class. This file was initially created by ** the ATL wizard for the weapon object. ** ** History: */ // weaponIGC.cpp : Implementation of CweaponIGC #include "pch.h" #include "weaponIGC.h" ///////////////////////////////////////////////////////////////////////////// // CweaponIGC CweaponIGC::CweaponIGC(void) : m_partType(NULL), m_projectileType(NULL), m_ship(NULL), m_pshipGunner(NULL), m_fActive(false), m_fFiringShot(false), m_fFiringBurst(false), m_mountID(c_mountNA) { } HRESULT CweaponIGC::Initialize(ImissionIGC* pMission, Time now, const void* data, int dataSize) { assert (pMission); m_pMission = pMission; ZRetailAssert (data && (dataSize == sizeof(DataPartIGC))); { m_partType = ((DataPartIGC*)data)->partType; assert (m_partType); m_partType->AddRef(); m_typeData = (const DataWeaponTypeIGC*)m_partType->GetData(); m_projectileType = m_pMission->GetProjectileType(m_typeData->projectileTypeID); ZRetailAssert (m_projectileType); m_projectileType->AddRef(); } return S_OK; } void CweaponIGC::Terminate(void) { AddRef(); if (m_pshipGunner) { SetGunner(NULL); } SetShip(NULL, NA); if (m_partType) { m_partType->Release(); m_partType = NULL; } if (m_projectileType) { m_projectileType->Release(); m_projectileType = NULL; } Release(); } void CweaponIGC::SetShip(IshipIGC* newVal, Mount mount) { //There may be only a single reference to this part ... so make sure the part //does not get deleted midway through things AddRef(); if (m_ship) { m_ship->DeletePart(this); m_ship->Release(); } assert (m_mountID == c_mountNA); m_ship = newVal; if (m_ship) { m_ship->AddRef(); m_ship->AddPart(this); SetMountID(mount); } Release(); } void CweaponIGC::SetMountID(Mount newVal) { assert (m_ship); if (newVal != m_mountID) { Deactivate(); if (m_pshipGunner) SetGunner(NULL); m_ship->MountPart(this, newVal, &m_mountID); if (newVal >= 0) { //Get the corresponding hardpoint: we know one exists because the //ship allowed the part to be mounted. const IhullTypeIGC* pht = m_ship->GetBaseHullType(); assert (pht); m_pposition = &(pht->GetWeaponPosition(newVal)); m_porientation = &(pht->GetWeaponOrientation(newVal)); //Is there a turret gunner in this position (who does not have a gun) { for (ShipLinkIGC* psl = m_ship->GetChildShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetTurretID() == newVal) { SetGunner(pship); break; } } } } } } 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; } }