//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Workfile: AvHAlienTurret.cpp $
// $Date: 2002/11/22 21:28:15 $
//
//-------------------------------------------------------------------------------
// $Log: AvHAlienTurret.cpp,v $
// Revision 1.8  2002/11/22 21:28:15  Flayra
// - mp_consistency changes
//
// Revision 1.7  2002/10/24 21:20:49  Flayra
// - Alien turrets now credit their owner with kills
//
// Revision 1.6  2002/09/23 22:08:14  Flayra
// - Removed damage upgrades for alien turrets (used to be needed for offensive upgrades)
//
// Revision 1.5  2002/07/24 18:45:40  Flayra
// - Linux and scripting changes
//
// Revision 1.4  2002/07/23 16:57:05  Flayra
// - Alien turret refactoring and fixing (the view offset in spawn() was causing them to always miss crouched players)
//
// Revision 1.3  2002/07/01 21:14:21  Flayra
// - Added auto-building, added damage upgrade (from primal scream), added vertical FOV (doesn't work yet)
//
// Revision 1.2  2002/06/03 16:24:08  Flayra
// - Moved chamber firing into an event, added turret constants
//
// Revision 1.1  2002/05/28 17:12:17  Flayra
// - Offensive chamber that shoots spit
//
//===============================================================================
#include "mod/AvHAlienTurret.h"
#include "mod/AvHConstants.h"
#include "mod/AvHPlayerUpgrade.h"
#include "mod/AvHAlienEquipmentConstants.h"
#include "mod/AvHAlienWeaponConstants.h"
#include "mod/AvHAlienWeapons.h"
#include "common/hldm.h"
#include "common/event_api.h"
#include "common/event_args.h"
#include "common/vector_util.h"
#include "mod/AvHGamerules.h"
#include "util/MathUtil.h"

// Temporary
#include "mod/AvHMarineTurret.h"
#include "mod/AvHMarineEquipment.h"
#include "mod/AvHConstants.h"
#include "mod/AvHPlayerUpgrade.h"



LINK_ENTITY_TO_CLASS(kwOffenseChamber, AvHAlienTurret);

#ifdef AVH_SERVER

LINK_ENTITY_TO_CLASS(kwSpikeProjectile, AvHSpike);

void AvHSpike::Precache(void)
{
	CBaseEntity::Precache();
	
	PRECACHE_UNMODIFIED_MODEL(kSpikeProjectileModel);
}

void AvHSpike::SetDamage(float inDamage)
{
	this->mDamage = inDamage;
}

void AvHSpike::Spawn()
{
	this->Precache();
	CBaseEntity::Spawn();
	
	this->pev->movetype = MOVETYPE_FLY;
	this->pev->classname = MAKE_STRING(kSpikeProjectileClassName);
	
	SET_MODEL(ENT(this->pev), kSpikeProjectileModel);
	this->pev->solid = SOLID_BBOX;
	this->mDamage = 0.0f;
	
	// Comment out effects line, uncomment next four, then comment out creation of temp entity in EV_SpikeGun to see server side Spike for testing
	if(!GetGameRules()->GetDrawInvisibleEntities())
	{
		this->pev->effects = EF_NODRAW;
	}
	else
	{
		this->pev->frame = 0;
		this->pev->scale = 0.5;
		this->pev->rendermode = kRenderTransAlpha;
		this->pev->renderamt = 255;
	}
	
	//UTIL_SetSize(this->pev, Vector( 0, 0, 0), Vector(0, 0, 0));
//	UTIL_SetSize(this->pev, Vector( -16, -16, -16), Vector(16, 16, 16));
	//UTIL_SetSize(this->pev, Vector( -50, -50, -50), Vector(50, 50, 50));
	
	SetTouch(&AvHSpike::SpikeTouch);
	
	// Enforce short range
	SetThink(&AvHSpike::SpikeDeath);
	this->pev->nextthink = gpGlobals->time + kSpikeLifetime;
}

void AvHSpike::SpikeDeath()
{
	// Kill the Spike entity
	UTIL_Remove(this);
	
	//	SetThink(SUB_Remove);
	//	this->pev->nextthink = gpGlobals->time + 0.01f;
}

void AvHSpike::SpikeTouch(CBaseEntity* pOther)
{
	CBaseEntity* theSpikeOwner = CBaseEntity::Instance(this->pev->owner);
	if((pOther != theSpikeOwner) && (theSpikeOwner != NULL))
	{
		float theScalar = 1.0f;
		if(GetGameRules()->CanEntityDoDamageTo(this, pOther, &theScalar))
		{
			float theDamage = this->mDamage*theScalar;

			// Give credit to the spike owner's owner (spike's owner is OC, OC's owner is player)
			CBaseEntity* theSpikeOwnerOwner = NULL;
			entvars_t* theSpikeOwnerOwnerEntVars = NULL;
			if(theSpikeOwner)
			{
				AvHTurret* theTurret = dynamic_cast<AvHTurret*>(theSpikeOwner);
				if(theTurret)
				{
					theSpikeOwnerOwner = theTurret->GetAttacker();
				}
			}

			if(!theSpikeOwnerOwner)
			{
				theSpikeOwnerOwner = theSpikeOwner;
			}

			if(theSpikeOwnerOwner)
			{
				theSpikeOwnerOwnerEntVars = theSpikeOwnerOwner->pev;
			}

			// Apply damage to receiver
			pOther->TakeDamage(theSpikeOwner->pev, theSpikeOwnerOwnerEntVars, theDamage, NS_DMG_LIGHT);
		}
		
		// Kill it off
		this->SpikeDeath();
	}
}
#endif


AvHAlienTurret::AvHAlienTurret() : AvHTurret(TECH_OFFENSE_CHAMBER, ALIEN_BUILD_OFFENSE_CHAMBER, kwsOffenseChamber, AVH_USER3_OFFENSE_CHAMBER)
{
}

AvHAlienTurret::AvHAlienTurret(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser3) : AvHTurret(inTechID, inMessageID, inClassName, inUser3)
{
}

// Energy from movement chambers subtracts off the rate of fire
bool AvHAlienTurret::Energize(float inEnergyAmount)
{
	bool theSuccess = false;

	if(this->GetIsBuilt() && (this->mEnergy < 1.0f))
	{
		if(this->m_hEnemy != NULL)
		{
			this->mEnergy = max(0.0f, min(1.0f, this->mEnergy + inEnergyAmount));
			theSuccess = true;
		}
	}

	return theSuccess;
}

char* AvHAlienTurret::GetDeploySound() const
{
	return kAlienTurretDeploy;
}

bool AvHAlienTurret::GetIsOrganic() const
{
	return true;
}

int AvHAlienTurret::GetPointValue(void) const
{
	return 2;
}

int	AvHAlienTurret::GetXYRange() const
{
	return 700;
}

void AvHAlienTurret::Precache()
{
	PRECACHE_UNMODIFIED_MODEL(kOffenseChamberModel);
	PRECACHE_UNMODIFIED_MODEL(kSpikeProjectileModel);
	PRECACHE_UNMODIFIED_SOUND(kAlienTurretFire1);
	PRECACHE_UNMODIFIED_SOUND(kAlienTurretDeploy);
	PRECACHE_UNMODIFIED_MODEL(kAlienTurretSprite);
	this->mEvent = PRECACHE_EVENT(1, kOffenseChamberEventName);
}

int	AvHAlienTurret::GetVerticalFOV() const
{
	return AvHTurret::GetVerticalFOV();
}

void AvHAlienTurret::PreBuiltThink()
{
	if(!this->GetIsBuilt())
		this->UpdateAutoBuild(kAlienBuildingThinkInterval*kAutoBuildScalar);
	else
		this->SetHasBeenBuilt();

	this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval;
}


void AvHAlienTurret::Shoot(const Vector &inOrigin, const Vector &inToEnemy, const Vector& inVecEnemyVelocity)
{
	// Spawn spike
	AvHSpike* theSpike = GetClassPtr((AvHSpike*)NULL );
	theSpike->Spawn();

	// Make it invisible
	if(!GetGameRules()->GetDrawInvisibleEntities())
	{
		theSpike->pev->effects = EF_NODRAW;
	}
	else
	{
		theSpike->pev->effects = 0;
		theSpike->pev->frame = 0;
		theSpike->pev->scale = 0.5;
		theSpike->pev->rendermode = kRenderTransAlpha;
		theSpike->pev->renderamt = 255;
	}

	// Predict where enemy will be when the spike hits and shoot that way
	float theTimeToReachEnemy = inToEnemy.Length()/(float)kOffenseChamberSpikeVelocity;
	Vector theEnemyPosition;
	VectorAdd(this->pev->origin, inToEnemy, theEnemyPosition);

	float theVelocityLength = inVecEnemyVelocity.Length();
	Vector theEnemyNormVelocity = inVecEnemyVelocity.Normalize();

	// Don't always hit very fast moving targets (jetpackers)
	const float kVelocityFactor = .7f;

	Vector thePredictedPosition;
	VectorMA(theEnemyPosition, theVelocityLength*kVelocityFactor*theTimeToReachEnemy, theEnemyNormVelocity, thePredictedPosition);

	Vector theOrigin = inOrigin;
	
	//Vector theDirToEnemy = inDirToEnemy.Normalize();

	Vector theDirToPredictedEnemy;
	VectorSubtract(thePredictedPosition, this->pev->origin, theDirToPredictedEnemy);
	Vector theDirToEnemy = theDirToPredictedEnemy.Normalize();

	//Vector theAttachOrigin, theAttachAngles;
	//GetAttachment(0, theAttachOrigin, theAttachAngles);
	
	//UTIL_SetOrigin(theSpike->pev, theStartPos);
	//VectorCopy(theStartPos, theSpike->pev->origin);
	VectorCopy(inOrigin, theSpike->pev->origin);

	// Pass this velocity to event
	int theVelocityScalar = kOffenseChamberSpikeVelocity;

	Vector theInitialVelocity;
	VectorScale(theDirToEnemy, theVelocityScalar, theInitialVelocity);
	
	// Set spike owner to OC so it doesn't collide with it
	theSpike->pev->owner = this->edict();

	// Set Spike's team :)
	theSpike->pev->team = this->pev->team;

	VectorCopy(theInitialVelocity, theSpike->pev->velocity);

	// Set amount of damage it will do
	theSpike->SetDamage(BALANCE_VAR(kOffenseChamberDamage));

	// Take into account network precision
	Vector theNetworkDirToEnemy;
	VectorScale(theDirToEnemy, 100.0f, theNetworkDirToEnemy);

	PLAYBACK_EVENT_FULL(0, this->edict(), this->mEvent, 0, theOrigin, theNetworkDirToEnemy, 1.0f, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );

	// Play attack anim
	this->PlayAnimationAtIndex(6, true);

	this->Uncloak();
}

bool AvHAlienTurret::GetBaseClassAnimatesTurret() const
{
	return false;
}

int AvHAlienTurret::MoveTurret(void)
{
	// Set animation, without overriding
	int theAnimation = this->GetIdle1Animation();
	if(RANDOM_LONG(0, 1) == 0)
	{
		theAnimation = this->GetIdle2Animation();
	}
	this->PlayAnimationAtIndex(theAnimation, false);
	
	return AvHTurret::MoveTurret();
}


int	AvHAlienTurret::GetIdle1Animation() const
{
	int theAnimation = -1;
	
	if(this->GetIsBuilt())
	{
		if(RANDOM_LONG(0, 5) == 0)
		{
			theAnimation = 4;
		}
		else
		{
			theAnimation = 2;
		}
	}
	
	return theAnimation;
}

int	AvHAlienTurret::GetIdle2Animation() const
{
	int theAnimation = -1;
	
	if(this->GetIsBuilt())
	{
		theAnimation = 3;
	}
	
	return theAnimation;
}

int	AvHAlienTurret::GetTakeDamageAnimation() const
{
    int theAnimation = -1;
    
    if(this->GetIsBuilt())
    {
        theAnimation = 7;
    }

	return theAnimation;
}

char* AvHAlienTurret::GetModelName() const
{
	return kOffenseChamberModel;
}

float AvHAlienTurret::GetRateOfFire() const
{
	return .5f + RANDOM_FLOAT(0.0f, (1.0f - this->mEnergy));
}

void AvHAlienTurret::ResetEntity()
{
	AvHTurret::ResetEntity();

	this->mEnergy = 0;
}

void AvHAlienTurret::Spawn()
{
	this->mEnergy = 0.0f;

	AvHTurret::Spawn();

	SetThink(&AvHAlienTurret::PreBuiltThink);
	this->pev->nextthink = gpGlobals->time + kAlienBuildingThinkInterval;
}

void AvHAlienTurret::SetNextAttack()
{
	AvHTurret::SetNextAttack();

	this->mEnergy = 0.0f;
}