/*
**  Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
**
**  File:	magazineIGC.cpp
**
**  Author: 
**
**  Description:
**      Implementation of the CmagazineIGC class. This file was initially created by
**  the ATL wizard for the core object.
**
**  History:
*/
// magazineIGC.cpp : Implementation of CmagazineIGC
#include "pch.h"
#include "magazineIGC.h"

/////////////////////////////////////////////////////////////////////////////
// CmagazineIGC
CmagazineIGC::CmagazineIGC(void)
:
    m_ship(NULL),
    m_partType(NULL),
    m_missileType(NULL),
    m_mountID(c_mountNA),
    m_lock(0.0f)
{
}

CmagazineIGC::~CmagazineIGC(void)
{
}

HRESULT     CmagazineIGC::Initialize(ImissionIGC* pMission, Time now, const void* data, int dataSize)
{
    assert (pMission);
    m_pMission = pMission;

    ZRetailAssert (data && (dataSize == sizeof(DataPartIGC)));
    {
        m_partType = (IlauncherTypeIGC*)(((DataPartIGC*)data)->partType);
        assert (m_partType);
        m_partType->AddRef();

        m_missileType = (ImissileTypeIGC*)(m_partType->GetExpendableType());
        assert (m_missileType);
        assert (m_missileType->GetObjectType() == OT_missileType);
        m_missileType->AddRef();
        m_amount = 0;
    }

	return S_OK;
}

void        CmagazineIGC::Terminate(void)
{
    AddRef();

    SetShip(NULL, NA);

    if (m_partType)
    {
        m_partType->Release();
        m_partType = NULL;
    }

    if (m_missileType)
    {
        m_missileType->Release();
        m_missileType = NULL;
    }

    Release();
}

void        CmagazineIGC::Update(Time   now)
{
    assert (m_ship);

    Time    lastUpdate = m_ship->GetLastUpdate();
    float   dt = now - lastUpdate;
    assert (dt >= 0.0f);

    if (m_mountedFraction < 1.0f)
    {

        m_mountedFraction += dt * m_pMission->GetFloatConstant(c_fcidMountRate);
        if (m_mountedFraction < 1.0f)
        {
            m_lock = 0.0f;
            return;
        }

        IIgcSite*   pigc = GetMission()->GetIgcSite();
        pigc->PlayNotificationSound(mountedSound, m_ship);
        pigc->PostNotificationText(m_ship, false, "%s ready.", GetPartType()->GetName());
        m_mountedFraction = 1.0f;
    }

    assert (m_ship);
    if ((m_timeLoaded < now) && (m_amount > 0))
    {
        if (m_ship->GetRipcordModel() != NULL)
            m_lock = 0.0f;
        else
        {
            ImodelIGC*  ptarget = m_ship->GetCommandTarget(c_cmdCurrent);

            float   maxLock = m_missileType->GetMaxLock();
            if (m_ship->GetStateM() & missileFireIGC)
            {
                //Twizzle anything now so that nothing needs to be adjusted after shooting (which may cause a terminate)
                float   oldLock = m_lock;
                m_lock = 0.0f;
                m_timeLoaded = now + m_missileType->GetLoadTime();

                m_pMission->GetIgcSite()->FireMissile(m_ship,
                                                      this,
                                                      now,
                                                      ptarget,
                                                      oldLock * maxLock);

            }
            else
            {
                if (ptarget && (ptarget->GetCluster() == m_ship->GetCluster()) &&   //have a target,
                    (ptarget->GetSide() != m_ship->GetSide()) &&                    //and on different sides,
                    (m_missileType->HasCapability(c_eabmWarpBomb)                   //is a warp bomb
                     ? (ptarget->GetObjectType() == OT_warp)                        //and target is a warp
                     : (ptarget->GetAttributes() & c_mtDamagable)) &&               //is not a warp bomb and we can do damage to it
                    m_ship->CanSee(ptarget))                                        //and we can see it,
                {
                    float   lifespan = m_missileType->GetLifespan();
                    float   maxRange = m_missileType->GetInitialSpeed() * lifespan + 0.5f * m_missileType->GetAcceleration() * lifespan * lifespan;

                    Vector  dP = (ptarget->GetPosition() - m_ship->GetPosition());
                    float   l2 = dP.LengthSquared();

                    float   oldLock = m_lock;
                    m_lock = 0.0f;

                    if ((l2 > 1.0f) && (l2 < maxRange * maxRange))
                    {
                        if (maxLock == 0.0f)
                            m_lock = 1.0f;
                        else
                        {
                            dP.SetNormalize();

                            double  dot = -(dP * m_ship->GetOrientation().GetBackward());
                            float   angleOffBow = (dot >= 1.0)
                                                  ? 0.0f
                                                  : ((dot <= -1.0f)
                                                     ? pi
                                                     : (float)acos(dot));

                            float   lockTime = m_missileType->GetLockTime();
                            float   lockAngle = m_missileType->GetLockAngle();

                            float   v = (lockAngle - angleOffBow) / lockAngle;
                            if (v < 0.0)
                                v *= lockTime;

                            m_lock = oldLock + v * dt / (ptarget->GetObjectType() == OT_ship 
                                                         ? (lockTime * ((IshipIGC*)ptarget)->GetHullType()->GetECM())
                                                         : lockTime);
                        }

                        if (m_lock >= 1.0f)
                        {
                            m_lock = 1.0f;
                        }
                        else if (m_lock < 0.0f)
                            m_lock = 0.0f;
                    }
                }
            }
        }
    }
}
void        CmagazineIGC::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);

        m_emissionPt = m_ship->GetHitTest()->GetFrameOffset("missemt");

        SetMountID(mount);
    }

    Release();
}
void    CmagazineIGC::SetMountID(Mount newVal)
{
    assert (m_ship);

    if (newVal != m_mountID)
    {
        if ((newVal < 0) && (m_mountID == 0))
            Deactivate();

        m_ship->MountPart(this, newVal, &m_mountID);

        if (newVal == 0)
            Activate();
    }
}