//======== (C) Copyright 2001 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: The marine siege cannon
//
// $Workfile: AvHSiegeTurret.cpp$
// $Date: 2002/11/22 21:26:06 $
//
//-------------------------------------------------------------------------------
// $Log: AvHSiegeTurret.cpp,v $
// Revision 1.12  2002/11/22 21:26:06  Flayra
// - Fixed turret factory abuse, where turrets became active after recycling the nearby turret factory before turret was fully contructed.
// - Fixed bug where siege turrets became re-activated after building a regular turret factory nearby.
// - mp_consistency changes
//
// Revision 1.11  2002/11/12 02:29:11  Flayra
// - Removed check for mp_testing with siege
//
// Revision 1.10  2002/11/03 04:52:18  Flayra
// - Removed server variables, hard-coded them
//
// Revision 1.9  2002/10/16 01:07:36  Flayra
// - Removed unused sounds
//
// Revision 1.8  2002/09/23 22:32:14  Flayra
// - Removed minimum range for siege
//
// Revision 1.7  2002/08/16 02:48:09  Flayra
// - New damage model
//
// Revision 1.6  2002/07/26 23:08:14  Flayra
// - Siege don't fire at babblers
//
// Revision 1.5  2002/07/24 18:55:52  Flayra
// - Linux case sensitivity stuff
//
// Revision 1.4  2002/07/23 17:27:47  Flayra
// - Siege no longer requires LOS (so it's actual siege)
//
// Revision 1.3  2002/07/01 21:47:27  Flayra
// - Added siege shockwave effect, added view shaking effects
//
// Revision 1.2  2002/05/28 18:07:19  Flayra
// - Reduced tracking rate for siege
//
// Revision 1.1  2002/05/23 02:33:20  Flayra
// - Post-crash checkin.  Restored @Backup from around 4/16.  Contains changes for last four weeks of development.
//
//===============================================================================
#include "mod/AvHSiegeTurret.h"
#include "mod/AvHMarineEquipment.h"
#include "mod/AvHGamerules.h"
#include "mod/AvHServerUtil.h"
#include "mod/AvHPlayerUpgrade.h"
#include "util/MathUtil.h"

LINK_ENTITY_TO_CLASS(kwSiegeTurret, AvHSiegeTurret);

extern int		gSiegeHitEventID;
extern int		gSiegeViewHitEventID;

AvHSiegeTurret::AvHSiegeTurret() : AvHMarineTurret(TECH_NULL, BUILD_SIEGE, kwsSiegeTurret, AVH_USER3_SIEGETURRET)
{
	float theStartTime = RANDOM_FLOAT(0, BALANCE_VAR(kSiegeROF));
	this->mTimeLastFired = gpGlobals->time - theStartTime;
}

void AvHSiegeTurret::CheckEnabledState()
{
	bool theEnabledState = false;

	if(this->GetHasBeenBuilt() && !this->GetIsRecycling())
	{
		FOR_ALL_ENTITIES(kwsAdvancedTurretFactory, AvHTurretFactory*)
		if((theEntity->pev->team == this->pev->team) && theEntity->GetIsBuilt() && !GetHasUpgrade(theEntity->pev->iuser4, MASK_RECYCLING))
		{
			// If they are a friendly, alive, turret factory
			float the2DDistance = VectorDistance2D(this->pev->origin, theEntity->pev->origin);
			
			// Enabled state is true
			if(the2DDistance <= BALANCE_VAR(kTurretFactoryBuildDistance))
			{
				theEnabledState = true;
				break;
			}
		}
		END_FOR_ALL_ENTITIES(kwsAdvancedTurretFactory)
	}

	// Set enabled state
	this->SetEnabledState(theEnabledState);
}

bool AvHSiegeTurret::GetIsValidTarget(CBaseEntity* inEntity) const
{
	bool theTargetIsValid = false;
	
	if(AvHMarineTurret::GetIsValidTarget(inEntity))
	{
		if(!inEntity->IsPlayer() && !FStrEq(STRING(inEntity->pev->classname), kwsBabblerProjectile))
		{
			float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, inEntity);
			//if(theDistanceToCurrentEnemy >= this->GetMinimumRange())
			//{
				// We have to see it as well
				//Vector vecMid = this->pev->origin + this->pev->view_ofs;
				//Vector vecMidEnemy = inEntity->BodyTarget(vecMid);
				//if(FBoxVisible(this->pev, inEntity->pev, vecMidEnemy))
				//{

				// Entities must be sighted to be hit (in view of player or scanned)
				AvHSiegeTurret* thisTurret = const_cast<AvHSiegeTurret*>(this);
				if(GetHasUpgrade(inEntity->pev->iuser4, MASK_VIS_SIGHTED) || inEntity->FVisible(thisTurret))
				{
					theTargetIsValid = true;
				}
				//}
			//}
		}
	}
	return theTargetIsValid;
}

int AvHSiegeTurret::GetDamageType() const
{
	return NS_DMG_STRUCTURAL;
}

char* AvHSiegeTurret::GetDeploySound() const
{
	return kSiegeDeploy;
}

char* AvHSiegeTurret::GetPingSound() const
{
	return kSiegePing;
}

int AvHSiegeTurret::GetPointValue(void) const
{
	return BALANCE_VAR(kScoringSiegeValue);
}

//int	AvHSiegeTurret::GetMinimumRange() const
//{
//	return this->GetXYRange()*kSiegeTurretMinRangeScalar;
//}

char* AvHSiegeTurret::GetModelName() const
{
	return kSiegeTurretModel;
}

int	AvHSiegeTurret::GetXYRange() const
{
	return BALANCE_VAR(kSiegeTurretRange);
}

bool AvHSiegeTurret::GetRequiresLOS() const
{
	return false;
}

void AvHSiegeTurret::Precache(void)
{
	AvHMarineTurret::Precache();
	
	PRECACHE_UNMODIFIED_MODEL(kSiegeTurretModel);
	PRECACHE_UNMODIFIED_SOUND(kSiegeTurretFire1);
	PRECACHE_UNMODIFIED_SOUND(kSiegeDeploy);
	PRECACHE_UNMODIFIED_SOUND(kSiegePing);
	PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound1);
	PRECACHE_UNMODIFIED_SOUND(kSiegeHitSound2);
	this->mShockwaveTexture = PRECACHE_UNMODIFIED_MODEL(kSiegeTurretShockWave);
}


void AvHSiegeTurret::Shoot(const Vector &inOrigin, const Vector &inToEnemy, const Vector& inVecEnemyVelocity)
{
	// Only fire once every few seconds...this is hacky but there's no way to override think functions so it must be done
	// I wish it was easier to change the update rate but it's not so...
	if((gpGlobals->time - this->mTimeLastFired) > BALANCE_VAR(kSiegeROF))
	{
		// Find enemy player in range, ignore walls and everything else
		if(this->m_hEnemy != NULL)
		{
			edict_t* theEnemyEdict = this->m_hEnemy->edict();
			entvars_t* theEnemyEntVars = this->m_hEnemy->pev;
			CBaseEntity* theEnemyEntity = (CBaseEntity*)(this->m_hEnemy);

			if(this->GetIsValidTarget(this->m_hEnemy) && theEnemyEdict && theEnemyEntVars && theEnemyEntity)
			{
				// Apply damage, taking upgrade into account
				float theDamageMultiplier;
				AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageMultiplier);
				float theDamage = theDamageMultiplier*BALANCE_VAR(kSiegeDamage);

				// Play view shake, because a big gun is going off
				float theShakeAmplitude = 20;
				float theShakeFrequency = 80;
				float theShakeDuration = .3f;
				float theShakeRadius = 240;
				UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);

				float theSiegeSplashRadius = kSiegeSplashRadius;
				
				// Play fire sound
				EMIT_SOUND_DYN(ENT(this->pev), CHAN_AUTO, kSiegeTurretFire1, 1.0, ATTN_NORM, 0, PITCH_NORM);
				
				this->pev->effects |= EF_MUZZLEFLASH;
				
				// Send normal effect to all
				PLAYBACK_EVENT_FULL(0, theEnemyEdict, gSiegeHitEventID, 0, theEnemyEntVars->origin, theEnemyEntVars->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );

				// Play view shake where it hits as well
				theShakeAmplitude = 60;
				theShakeFrequency = 120;
				theShakeDuration = 1.0f;
				theShakeRadius = 650;
				UTIL_ScreenShake(theEnemyEntVars->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);
				
				if(theEnemyEntity->IsPlayer())
				{
					// Send personal view shake to recipient only (check for splash here, pass param to lessen effect for others?)
					// TODO: Use upgrade level to parameterize screen shake and fade?
					PLAYBACK_EVENT_FULL(FEV_HOSTONLY, theEnemyEdict, gSiegeViewHitEventID, 0, theEnemyEntVars->origin, theEnemyEntVars->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
					
					Vector theFadeColor;
					theFadeColor.x = 255;
					theFadeColor.y = 100;
					theFadeColor.z = 100;
					UTIL_ScreenFade(this->m_hEnemy, theFadeColor, .3f, 0.0f, 255, FFADE_OUT);
				}

				// blast circles
				MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, theEnemyEntVars->origin );
					WRITE_BYTE( TE_BEAMCYLINDER );
					WRITE_COORD( theEnemyEntVars->origin.x);
					WRITE_COORD( theEnemyEntVars->origin.y);
					WRITE_COORD( theEnemyEntVars->origin.z + 16);
					WRITE_COORD( theEnemyEntVars->origin.x);
					WRITE_COORD( theEnemyEntVars->origin.y);
					WRITE_COORD( theEnemyEntVars->origin.z + 16 + theSiegeSplashRadius / .2); // reach damage radius over .3 seconds
					WRITE_SHORT( this->mShockwaveTexture );
					WRITE_BYTE( 0 ); // startframe
					WRITE_BYTE( 0 ); // framerate
					WRITE_BYTE( 2 ); // life
					WRITE_BYTE( 16 );  // width
					WRITE_BYTE( 0 );   // noise
				
					// Write color
					WRITE_BYTE(188);
					WRITE_BYTE(220);
					WRITE_BYTE(255);
				
					WRITE_BYTE( 255 );		//brightness
					WRITE_BYTE( 0 );		// speed
				MESSAGE_END();

				// Finally, do damage (do damage after sending effects because m_hEnemy seems to be going to NULL)
				::RadiusDamage(theEnemyEntVars->origin, this->pev, this->GetAttacker()->pev, theDamage, theSiegeSplashRadius, CLASS_NONE, this->GetDamageType());
			}
			else
			{
				this->m_hEnemy = NULL;
			}
		}

		this->mTimeLastFired = gpGlobals->time;
	}
}

void AvHSiegeTurret::ResetEntity()
{
	AvHMarineTurret::ResetEntity();

	this->mTimeLastFired = -1;
}

void AvHSiegeTurret::Spawn()
{
	AvHMarineTurret::Spawn();

	this->m_fTurnRate = M_PI/3.0f;
}