//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/shotrocketlauncher.cpp           $
// $Revision:: 30                                                             $
//   $Author:: Markd                                                          $
//     $Date:: 11/13/98 3:30p                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/shotrocketlauncher.cpp                $
// 
// 30    11/13/98 3:30p Markd
// put in more precaching on weapons
// 
// 29    10/26/98 2:50p Aldie
// Fixed a bug with checking of NULL owners
// 
// 28    10/25/98 10:57p Aldie
// Fixed shotrockets for generic launcher
// 
// 27    10/24/98 12:42a Markd
// changed origins to worldorigins where appropriate
// 
// 26    10/23/98 5:38a Jimdose
// Added SpawnBlastDamage
// 
// 25    10/22/98 7:57p Markd
// put in proper pre-caching in all the classes
// 
// 24    10/20/98 8:26p Markd
// Added Attacker to DamageSurface stuff
// 
// 23    10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// 
// 22    10/17/98 8:14p Jimdose
// Changed Damage to DamgeEvent
// 
// 21    9/18/98 8:14p Markd
// rewrote surface system so that surfaces are now damaged by surface name
// instead of by surfinfo
// 
// 20    9/01/98 3:05p Markd
// Rewrote explosion code
// 
// 19    8/29/98 9:46p Jimdose
// Added call info to G_Trace
// 
// 18    8/29/98 5:27p Markd
// added specialfx, replaced misc with specialfx where appropriate
// 
// 17    8/27/98 9:02p Jimdose
// Changed centroid to a variable
// 
// 16    7/26/98 8:57a Markd
// Put in auto destruct
// 
// 15    7/26/98 4:16a Aldie
// Lowered stay around time to 15 seconds
// 
// 14    7/26/98 1:48a Aldie
// Heat seekers - woohoo
// 
// 13    7/25/98 7:19p Aldie
// Client side explosion
// 
// 12    7/24/98 10:04p Aldie
// More commands for rate and speed control
// 
// 11    7/23/98 6:17p Aldie
// Updated damage system and fixed some damage related bugs.  Also put tracers
// back to the way they were, and added gib event to funcscriptmodels
// 
// 10    7/23/98 4:39p Markd
// Got rid of smoketrail
// 
// 9     7/23/98 2:32p Aldie
// Made speed of rocket a tweak cvar
// 
// 8     7/21/98 1:10p Aldie
// Added meansofdeath to obituaries
// 
// 7     7/17/98 7:57p Markd
// Added server flag SVF_USEBBOX
// 
// 6     7/14/98 5:51p Aldie
// Tweaked the shotrocket.
// 
// 5     7/10/98 2:47p Aldie
// Updated speed and attack time
// 
// 4     7/06/98 6:49p Aldie
// Fiddle with the size of the rocket.
// 
// 3     7/06/98 5:10p Aldie
// Changed the speed.
// 
// 2     7/06/98 4:09p Aldie
// First version of shotrocketlauncher
//
// DESCRIPTION:
// ShotRocketLauncher - Shoots slow moving shootable rockets

#include "g_local.h"
#include "explosion.h"
#include "shotrocketlauncher.h"
#include "rocket_turret.h"
#include "misc.h"
#include "surface.h"

CLASS_DECLARATION( Projectile, ShotRocket, NULL );

Event EV_ShotRocket_Explode( "explode" );
Event EV_ShotRocket_HeatSeek( "heatseek" );

ResponseDef ShotRocket::Responses[] =
	{
	   { &EV_Touch,				      ( Response )&ShotRocket::Explode },
      { &EV_Damage,                 ( Response )&ShotRocket::DamageEvent },
	   { &EV_ShotRocket_Explode,	   ( Response )&ShotRocket::Explode },
  	   { &EV_ShotRocket_HeatSeek,	   ( Response )&ShotRocket::HeatSeek },
		{ NULL, NULL }
	};


void ShotRocket::DamageEvent
   (
   Event *ev
   )

   {
   Entity *other;
   Event *event;
   int damage;

   damage = ev->GetInteger(1);
   
   if (damage <= 0)
      return;

   other = ev->GetEntity(2);
   
   event = new Event(EV_ShotRocket_Explode);
   event->AddEntity(other);
   ProcessEvent(event);
   }

void ShotRocket::Explode
	(
	Event *ev
	)

	{
	int			damg;
	Vector		v;
	Entity		*other;
   Entity      *owner;
   
	other = ev->GetEntity( 1 );
	assert( other );

   owner = G_GetEntity( this->owner );

   if ( !owner )
      owner = world;

   if ( other->isSubclassOf( Teleporter ) )
		{
		return;
		}

	if ( other == owner )
		{
		return;
		}

   stopsound( CHAN_VOICE );
	setSolidType( SOLID_NOT );
	hideModel();

	if ( HitSky() )
		{
		PostEvent( EV_Remove, 0 );
		return;
		}


   damg = 100 + ( int )G_Random( 20 );

   takedamage = DAMAGE_NO;

   if (other->takedamage)
      other->Damage( this, owner, damg, worldorigin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_SHOTROCKET, -1, -1, 1.0f );

   SpawnBlastDamage( &level.impact_trace, damg, owner );

	v = velocity;
	v.normalize();
	
	// don't do radius damage to the other, because all the damage
	// was done in the impact
	v = worldorigin - v * 36;
	CreateExplosion( v, damg, 1.0f, true, this, owner, other );
	PostEvent( EV_Remove, 0.1 );
	}

float ShotRocket::Distance
	( 
	Entity *targ 
	)

   {
	Vector delta;
	
	delta = worldorigin - targ->centroid;
	return delta.length();
	}

qboolean ShotRocket::CanSee
	(
	Entity *ent
	)

	{
	Vector delta;
	Vector start;
	Vector end;
	trace_t trace;

	start = worldorigin;
	end   = ent->centroid;

	// Check if he's visible
	trace = G_Trace( start, vec_zero, vec_zero, end, this, MASK_OPAQUE, "ShotRocket::CanSee" );
	if ( trace.fraction == 1.0 || trace.ent == ent->edict )
		{
		return true;
		}

	return false;
	}

void ShotRocket::HeatSeek
   (
   Event *ev
   )

   {
   // Seek out the closest client
   
   Entity	*ent;
	edict_t	*ed;
	int		i;
	float		dist;
	float		bestdist = 99999;
   Entity	*bestent;
   Vector   delta;

   bestent = NULL;

	for( i = 0; i < game.maxclients; i++ )
		{
		ed = &g_edicts[ 1 + i ];
		if ( !ed->inuse || !ed->entity )
			{
			continue;
			}

		ent = ed->entity;
	   if ( ( ent->health < 0 ) || ( ent->flags & FL_NOTARGET ) )
			{
			continue;
			}

		dist = Distance( ent );
		if ( ( dist < bestdist ) && CanSee( ent ) )
			{
			bestent = ent;
			bestdist = dist;
			}
		}	
	
   if (!bestent)
      {
      PostEvent( ev, FRAMETIME );
      return;
      }
   if ( bestdist < 16 )
      {
      Event * ev;
      ev = new Event( EV_ShotRocket_Explode );
      ev->AddEntity( bestent );
      PostEvent( ev, 0 );
      }

	delta = bestent->centroid - worldorigin;
   delta.normalize();
   velocity = delta * speed;
   angles = delta.toAngles();
	angles[ PITCH ] = - angles[ PITCH ];
	setAngles( angles );
   PostEvent( ev, FRAMETIME );
   }

void ShotRocket::Setup
	(
	Entity *owner,
	Vector pos,
	Vector dir
	)

	{
	Event *ev;
   RocketTurret *turret;

	this->owner = owner->entnum;
	edict->owner = owner->edict;

   if ( owner->isSubclassOf( RocketTurret ) )
      {
      turret = ( RocketTurret * )owner;
      speed = turret->speed;
      }
   else
      {
      turret = NULL;
      speed = 500;
      }

	setMoveType( MOVETYPE_FLYMISSILE );
	setSolidType( SOLID_BBOX );
	edict->clipmask = MASK_PROJECTILE;

	angles = dir.toAngles();
	angles[ PITCH ] = - angles[ PITCH ];
	setAngles( angles );

   velocity = dir * speed;

	// set missile duration
	ev = new Event( EV_ShotRocket_Explode );
	ev->AddEntity( world );
	PostEvent( ev, 15 );

	takedamage = DAMAGE_YES;
	health = 1;

   setModel( "rocket.def" );
   edict->s.effects  |= EF_ROCKET;
   edict->s.effects  |= EF_EVERYFRAME;
   edict->s.angles[ROLL] = rand() % 360;
	avelocity = "0 0 90";
	gravity = 0;
   edict->svflags |= SVF_USEBBOX;

	// setup ambient thrust
	ev = new Event( EV_RandomEntitySound );
	ev->AddString( "thrust" );
   ProcessEvent( ev );

	setSize( "-8 -8 -8", "8 8 8" );
	setOrigin( pos );
   worldorigin.copyTo(edict->s.old_origin);

   ev = new Event( EV_ShotRocket_HeatSeek );
   PostEvent( ev, FRAMETIME );
	}

CLASS_DECLARATION( Weapon, ShotRocketLauncher, "weapon_shotrocketlauncher" );

ResponseDef ShotRocketLauncher::Responses[] =
	{
	   { &EV_Weapon_Shoot,	( Response )&ShotRocketLauncher::Shoot },
		{ NULL, NULL }
	};

ShotRocketLauncher::ShotRocketLauncher()
	{
	SetModels( "rlaunch.def", "view_rlaunch.def" );
	modelIndex( "rockets.def" );
	modelIndex( "rocket.def" );
	modelIndex( "sprites/blastmark.spr" );
	SetAmmo( "Rockets", 1, 5 );
	SetRank( 70, 50 );
	}

void ShotRocketLauncher::Shoot
	(
	Event *ev
	)

	{
	ShotRocket	      *rocket;
	Vector	         pos;
	Vector	         dir;
   RocketTurret      *turret;

	assert( owner );
	if ( !owner )
		{
		return;
		}

   turret = ( RocketTurret * )( Entity *)owner;

	GetMuzzlePosition( &pos, &dir );

	rocket = new ShotRocket;
	rocket->Setup( owner, pos, dir );

	MuzzleFlash( 1.0, 0.1, 0, 400, 0.6, 0.2 );
	NextAttack( turret->rate );
	}
