//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/bullet.cpp                       $
// $Revision:: 71                                                             $
//   $Author:: Markd                                                          $
//     $Date:: 10/22/98 7:57p                                                 $
//
// Copyright (C) 1997 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/bullet.cpp                            $
// 
// 71    10/22/98 7:57p Markd
// put in proper pre-caching in all the classes
// 
// 70    10/20/98 8:26p Markd
// Added Attacker to DamageSurface stuff
// 
// 69    10/13/98 5:27p Markd
// revamped ricochet stuff
// 
// 68    10/13/98 12:35p Markd
// Fixed tracers and bullet holes on non-player guys
// 
// 67    10/05/98 11:30p Markd
// Added MakeBreakingSound when firing at things
// 
// 66    10/04/98 10:36p Markd
// put in ricochet code
// 
// 65    9/27/98 3:53p Aldie
// Put in some debugging information
// 
// 64    9/18/98 8:14p Markd
// rewrote surface system so that surfaces are now damaged by surface name
// instead of by surfinfo
// 
// 63    8/31/98 7:45p Aldie
// Updated surface data structure and removed surfinfo field
// 
// 62    8/29/98 9:39p Jimdose
// Added call info to G_Trace
// 
// 61    8/29/98 5:27p Markd
// added specialfx, replaced misc with specialfx where appropriate
// 
// 60    8/25/98 7:55p Markd
// Fixed weapon angles
// 
// 59    8/19/98 6:38p Markd
// Moved Assault rifle tracer into def file
// 
// 58    8/14/98 5:37p Aldie
// Moved blood stuff to sentient
// 
// 57    8/13/98 8:09p Aldie
// Moved blood particles to armor damage.
// 
// 56    8/10/98 6:52p Aldie
// Added scale to bloodsplats
// 
// 55    8/06/98 10:53p Aldie
// Added weapon tweaks and kickback.  Also modified blast radius damage and
// rocket jumping.
// 
// 54    8/04/98 3:27p Aldie
// Revert back to sending PITCH and YAW
// 
// 53    8/03/98 3:28p Aldie
// Only send over the PITCH of the gun when firing it
// 
// 52    8/01/98 4:39p Aldie
// Put some debug prints
// 
// 51    8/01/98 3:24p Aldie
// Added server effects flag for specific weapons
// 
// 50    7/31/98 7:42p Aldie
// Client side bullet effects
// 
// 49    7/25/98 5:48p Aldie
// Remove shotgun effect
// 
// 48    7/24/98 10:03p Aldie
// Fixed bullet damage with invalid traces
// 
// 47    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
// 
// 46    7/22/98 10:41p Aldie
// Fixed tracers
// 
// 45    7/21/98 1:10p Aldie
// Added meansofdeath to obituaries
// 
// 44    7/17/98 7:57p Markd
// Added radius to FullTrace
// 
// 43    7/15/98 9:57p Markd
// Added shields support
// 
// 42    6/20/98 7:48p Markd
// using FullTrace now, also checking to make sure we have a valid intersection
// before trying to determine group num etc.
// 
// 41    6/19/98 9:29p Jimdose
// Moved gun orientation code to Weapon
// 
// 40    6/15/98 10:52p Jimdose
// Disabled fulltrace until bmodels are checked.
// 
// 39    6/15/98 10:07p Jimdose
// Made bullet weapons use G_FullTrace
// 
// 38    6/11/98 3:34p Jimdose
// TraceAttack was damaging the entity before checking the blood and sparks
// flags, so if the entity died before the check, we got a NULL pointer
// TraceAttack was doing the ricochet trace before checking if it should
// ricochet
// 
// 37    6/10/98 2:10p Aldie
// Updated damage function.
// 
// 36    6/08/98 11:35a Aldie
// Updated location based damage stuff
// 
// 35    6/05/98 6:23p Aldie
// Added location to AddMultiDamage
// 
// 34    5/27/98 7:34p Markd
// commented out damage reduction if not hitting body part
// 
// 33    5/27/98 5:21p Markd
// fixed up ray intersection a bit
// 
// 32    5/27/98 5:02a Markd
// increased nominal multiplier to 0.5f
// 
// 31    5/26/98 8:44p Markd
// Put in DamageSkin suport
// 
// 30    5/26/98 4:22p Markd
// Working on tri-ray intersection stuff
// 
// 29    5/23/98 4:46p Aldie
// Added bulletholes.
// 
// 28    5/05/98 7:38p Markd
// Added timeofs to ricochets in bulllet smoke events
// 
// 27    5/03/98 8:08p Markd
// Took out old code
// 
// 26    5/03/98 4:30p Jimdose
// Changed Vector class
// 
// 25    5/02/98 8:41p Markd
// Put in recursive bullet attacks and got rid of ricochets
// 
// 24    4/10/98 1:22a Markd
// Added FL_BLOOD support
// 
// 23    4/07/98 6:42p Jimdose
// Rewrote weapon code.
// Added order to rank
// 
// 22    4/06/98 7:52p Aldie
// Increased range of bullets.
// 
// 21    4/04/98 7:31p Jimdose
// Bullets don't spark or ricochet against sky
// 
// 20    4/02/98 4:48p Jimdose
// Changed TraceAttack to properly place bloodsplats
// 
// 19    3/30/98 2:35p Jimdose
// Moved firing from Magnum to make more general
// Added Ammo
// Added world models
// 
// 18    3/27/98 6:34p Jimdose
// Added sparks to show impact of bullets
// 
// 17    3/26/98 8:16p Jimdose
// Added precaching of sounds
// 
// 16    3/23/98 1:31p Jimdose
// Revamped event and command system
// 
// 15    3/05/98 5:44p Aldie
// Support for ricochet flag.
// 
// 14    3/04/98 8:02p Aldie
// More support for damage surfaces.
// 
// 13    3/04/98 5:12p Aldie
// Added support for damage surfaces.
// 
// 12    2/19/98 2:35p Jimdose
// Updated to work with Q2 based progs
// 
// 10    12/11/97 3:30p Markd
// Moved SpawnBlood to Weapon
// 
// 9     11/18/97 5:28p Markd
// Commented out shotgun
// 
// 8     11/11/97 9:41p Markd
// tweaked minimum scale in blood splats
// 
// 7     11/11/97 9:38p Markd
// made blood splats be based off of distance as well as damage.
// 
// 6     10/31/97 9:02p Jimdose
// Made it so that bsp objects don't leave bloodsplats
// 
// 5     10/31/97 7:18p Markd
// Put in BloodSpray
// 
// 4     10/31/97 4:28p Jimdose
// Removed redefinition of owner in base class Weapon, so any reference to
// gunoffset through owner had to use type overriding.
// 
// 3     10/27/97 3:29p Jimdose
// Removed dependency on quakedef.h
// 
// 2     9/26/97 4:43p Jimdose
// Added standard Ritual header
//
// DESCRIPTION:
// Base class for all bullet (hitscan) weapons.  Includes definition for shotgun.
// 

#include "g_local.h"
#include "specialfx.h"
#include "misc.h"
#include "bullet.h"
#include "q_shared.h"
#include "surface.h"

CLASS_DECLARATION( Weapon, BulletWeapon, NULL );

ResponseDef BulletWeapon::Responses[] =
	{
		{ NULL, NULL }
	};

BulletWeapon::BulletWeapon()
	{
   modelIndex( "sprites/tracer.spr" );
   modelIndex( "sprites/bullethole.spr" );
	}

void BulletWeapon::TraceAttack
	(
	Vector	start,
	Vector	end,
	int		damage,
	trace_t	*trace,
   int      numricochets,
   int      kick,
   int      dflags,
   int      meansofdeath,
   qboolean server_effects
	)
	
	{
	Vector		org;
	Vector		dir;
   Vector		endpos;
   int			surfflags;
   int			surftype;
   int			timeofs;
	Entity		*ent;
   qboolean    ricochet;

	if ( HitSky( trace ) )
		{
		return;
		}

   ricochet = false;
	dir = end - start;
	dir.normalize();

	org = end - dir;

	ent = trace->ent->entity;

	if ( !trace->surface )
		{
      surfflags = 0;
      surftype = 0;
		}
   else
      {
      surfflags = trace->surface->flags;
      surftype = SURFACETYPE_FROM_FLAGS( surfflags );
      surfaceManager.DamageSurface( trace, damage, owner );

      if ( surfflags & SURF_RICOCHET )
         ricochet = true;
      }
   if ( trace->intersect.valid && ent )
      {
      //
      // see if the parent group has ricochet turned on
      //
      if ( trace->intersect.parentgroup >= 0 )
         {
         int flags;

         flags = gi.Group_Flags( ent->edict->s.modelindex, trace->intersect.parentgroup );
         if ( flags & MDL_GROUP_RICOCHET )
            {
            surftype = ( flags >> 8 ) & 0xf;
            ricochet = true;
            }
         }
      }

	if ( ent )
		{
	   if ( !(ent->flags & FL_SHIELDS) )
		   {
		   if ( ent->flags & FL_SPARKS )
			   {
            // Take care of ricochet effects on the server
            if ( server_effects && !ricochet )
               {
		         timeofs = MAX_RICOCHETS - numricochets;
		         if ( timeofs > 0xf )
			         {
			         timeofs = 0xf;
			         }
               gi.WriteByte( svc_temp_entity );
	   		   gi.WriteByte( TE_GUNSHOT );
		   	   gi.WritePosition( org.vec3() );
			      gi.WriteDir( trace->plane.normal );
			      gi.WriteByte( 0 );
			      gi.multicast( org.vec3(), MULTICAST_PVS );
			      }
            MadeBreakingSound( org, owner );
            }

		   if ( ent->takedamage )
			   {    
            if ( trace->intersect.valid )
               {
   			   // We hit a valid group so send in location based damage
               ent->Damage( this,
                            owner,
                            damage,
                            trace->endpos,
                            dir,
                            trace->plane.normal,
                            kick,
                            dflags,
                            meansofdeath,
                            trace->intersect.parentgroup,
                            -1,
                            trace->intersect.damage_multiplier );
               }
            else
               {
               // We didn't hit any groups, so send in generic damage
   			   ent->Damage( this,
                            owner,
                            damage,
                            trace->endpos,
                            dir,
                            trace->plane.normal,
                            kick,
                            dflags,
                            meansofdeath,
                            -1,
                            -1,
                            1 );
               }
			   }
		   }
	   else
		   {
         surftype = SURF_TYPE_METAL;
         ricochet = true;
         }
      }

   if ( ricochet && ( server_effects || ( numricochets < MAX_RICOCHETS ) ) )
      {
		timeofs = MAX_RICOCHETS - numricochets;
		if ( timeofs > 0xf )
			{
			timeofs = 0xf;
			}
      gi.WriteByte( svc_temp_entity );
	   gi.WriteByte( TE_GUNSHOT );
		gi.WritePosition( org.vec3() );
		gi.WriteDir( trace->plane.normal );
		gi.WriteByte( timeofs );
		gi.multicast( org.vec3(), MULTICAST_PVS );
      }

	if ( 
      ricochet &&
      numricochets && 
      damage
      )
      {
      dir += Vector( trace->plane.normal ) * 2;
  		endpos = org + dir * 8192;

      //
      // since this is a ricochet, we don't ignore the wewapon owner this time.
      //
      *trace = G_FullTrace( org, vec_zero, vec_zero, endpos, 5, NULL, MASK_SHOT, "BulletWeapon::TraceAttack" );
		if ( trace->fraction != 1.0 )
			{
			endpos = trace->endpos;
			TraceAttack( org, endpos, damage * 0.8f, trace, numricochets - 1, kick, dflags, meansofdeath, true );
			}
      }
	}

void BulletWeapon::FireBullets
	(
	int numbullets,
	Vector spread,
	int mindamage,
	int maxdamage,
   int dflags,
   int meansofdeath,
   qboolean server_effects
	)

	{
	Vector	src;
	Vector	dir;
	Vector	end;
	trace_t	trace;
	Vector	right;
	Vector	up;
	int		i;

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

	GetMuzzlePosition( &src, &dir );

	owner->angles.AngleVectors( NULL, &right, &up );

   angles = dir.toAngles();
   setAngles( angles );

	for( i = 0; i < numbullets; i++ )
		{
  		end = src + 
            dir       * 8192 +  
			   right     * G_CRandom() * spread.x + 
            up        * G_CRandom() * spread.y;

      trace = G_FullTrace( src, vec_zero, vec_zero, end, 5, owner, MASK_SHOT, "BulletWeapon::FireBullets" );
#if 0
      Com_Printf("Server OWNER  Angles:%0.2f %0.2f %0.2f\n",owner->angles[0],owner->angles[1],owner->angles[2]);
      Com_Printf("Server Bullet Angles:%0.2f %0.2f %0.2f\n",angles[0],angles[1],angles[2]);
      Com_Printf("Right               :%0.2f %0.2f %0.2f\n",right[0],right[1],right[2]);
      Com_Printf("Up                  :%0.2f %0.2f %0.2f\n",up[0],up[1],up[2]);
      Com_Printf("Direction           :%0.2f %0.2f %0.2f\n",dir[0],dir[1],dir[2]);
      Com_Printf("Endpoint            :%0.2f %0.2f %0.2f\n",end[0],end[1],end[2]);
      Com_Printf("Server Trace Start  :%0.2f %0.2f %0.2f\n",src[0],src[1],src[2]);
      Com_Printf("Server Trace End    :%0.2f %0.2f %0.2f\n",trace.endpos[0],trace.endpos[1],trace.endpos[2]);
      Com_Printf("\n");
#endif
      if ( trace.fraction != 1.0 )
			{
			TraceAttack( src, trace.endpos, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), &trace, MAX_RICOCHETS, kick, dflags, meansofdeath, server_effects );
			}
		}
   }

void BulletWeapon::FireTracer( void )
   {
   Entity   *tracer;
   Vector   src,dir,end;
   trace_t  trace;

	GetMuzzlePosition( &src, &dir );
	end = src + dir * 8192;
   trace = G_Trace( src, vec_zero, vec_zero, end, owner, MASK_SHOT, "BulletWeapon::FireTracer" );

   tracer = new Entity;

   tracer->angles = dir.toAngles();
	tracer->angles[ PITCH ] = -tracer->angles[ PITCH ] + 90;
	//tracer->angles[PITCH] *= -1;

   tracer->setAngles( tracer->angles );

 	tracer->setMoveType( MOVETYPE_NONE );
	tracer->setSolidType( SOLID_NOT );
   tracer->setModel( "sprites/tracer.spr" );
	tracer->setSize( "0 0 0", "0 0 0" );
   tracer->setOrigin( trace.endpos );
   tracer->edict->s.renderfx &= ~RF_FRAMELERP;

   VectorCopy( src, tracer->edict->s.old_origin );
   tracer->PostEvent(EV_Remove,0.1f);
   }