//---------------------------------------------------------------------------
// GLOCK 2020
//---------------------------------------------------------------------------
#include "weapons.h"
#include "glock.h"

// model defs
#define MDL_SHELL             MDL_SPECIAL+0

// sound defs
#define SND_FIRE              0
#define SND_LOAD              1
#define SND_BULLETDROP1       2
#define SND_BULLETDROP2       3
#define SND_BULLETDROP3       4
#define SND_BULLETDROP4       5
#define SND_BULLETDROP5       6

// frame defs
#define ANIM_RELOAD           ANIM_SPECIAL+0
#define ANIM_SHELL            ANIM_SPECIAL+1

// weapon info def
weaponInfo_t glockWeaponInfo = {
      // names, net names, icon
      "weapon_glock",
      "Glock 2020",
      "ammo_bullets",
      "Bullets",
      "iconname",

      // model files
      {"models/glock/glock.md2",
       "models/glockshell/glockshell.md2",
       NULL},

      // sound files
      {"weapons/glock/glockfire3.wav",
       "weapons/glock/glockload.wav",
       "weapons/glock/bulletdrop1.wav",
       "weapons/glock/bulletdrop2.wav",
       "weapons/glock/bulletdrop3.wav",
       "weapons/glock/bulletdrop4.wav",
       "weapons/glock/bulletdrop5.wav",
       NULL},

      // frame names
      {MDL_WEAPON_STD,"ready",0,0,
               -1,"goaway",0,0,
               -1,"shoota",0,0,
               -1,"amba",0,0,
               -1,"reload",0,0,
        MDL_SHELL,"stand000",0,0,
       0,NULL},

      // commands
      {"weapon_glock",(void (*)())glock_give,
       "select_glock",(void (*)())glock_select,
       NULL},

   glock_use,
   glock_command,
   sizeof(glockWeapon_t),

   150,
};

// local weapon info access
static weaponInfo_t *winfo=&glockWeaponInfo;

/////////////////////////////////////////////////////////////////////
// globals
/////////////////////////////////////////////////////////////////////
static	long	shell_count;

//---------------------------------------------------------------------------
// glock_give()
//---------------------------------------------------------------------------
static userInventory_t *glock_give (userEntity_t *self)
{
   glockWeapon_t *glock;

   glock=(glockWeapon_t *)weaponGive(self,winfo,40);
   glock->clip=10;                        // 40 in inv, 10 in clip, 50 total

   return((userInventory_t *)glock);
}

//---------------------------------------------------------------------------
// glock_select()
//---------------------------------------------------------------------------
static	void	glock_select (userEntity_t *self)
{
   weaponSelect(self,winfo);
}

//---------------------------------------------------------------------------
// glock_command()
//---------------------------------------------------------------------------
static	void *glock_command (struct userInventory_s *ptr, char *commandStr, void *data)
{
   glockWeapon_t *weapon=(glockWeapon_t *)ptr;
   userEntity_t *self=(userEntity_t *)data;
   playerHook_t *phook=(playerHook_t *)self->userHook;

	if (!stricmp (commandStr, "death_msg"))
	{
		killerInfo_t	*ki = (killerInfo_t *) data;
		char			msg [80];

		if (ki->victim->netname && ki->attacker->netname)
		{
			if (rnd () < 0.5)
				sprintf (msg, "%s deep throated %s's glock.\n", ki->victim->netname, ki->attacker->netname);
			else
				sprintf (msg, "%s sucks %s's glock.\n", ki->victim->netname, ki->attacker->netname);

			gstate->bprint (msg);
		}
	}
   else if (!stricmp(commandStr,"count"))
   {
      if (weapon->ammo)
      {
         weapon->ammo->command((userInventory_t *)weapon->ammo,"count",data);
         *(int *)data += weapon->clip;
      }
   }
   else
      winfoGenericCommand(winfo,ptr,commandStr,data);

	return(NULL);
}

//---------------------------------------------------------------------------
// shell_water_friction()
//---------------------------------------------------------------------------
static	void	shell_water_friction (userEntity_t *self)
{
	weapHook_t	*hook = (weapHook_t *) self->userHook;
	Vector			forward, right, up, dir, ang;
	float			pc, wobble_speed, wander_speed, r_scale;

	pc = gstate->PointContents (self->s.origin);
	if (pc != CONTENTS_LAVA && pc != CONTENTS_SLIME && pc != CONTENTS_WATER)
		return;

	if (hook->last_water != pc)
	{
//		gstate->sound (self, CHAN_AUTO, "weapons/glock/bloop.wav",125, ATTN_NORM);
		//hook->hit_water_pos = Vector (self->s.origin);
		//dir = Vector (self->velocity) * 0.5 * r_scale;
		//dir.set (self->velocity);
		//hook->change_time = gstate->time;

		
	}
	else if ((self->movetype == MOVETYPE_BOUNCE) || (self->movetype == MOVETYPE_TOSS))
	{
		/*if (hook->change_time <= gstate->time)
		{
			hook->hit_water_pos.x = self->s.origin [2] + (rnd () - 0.2) * (rnd () * 256.0 + 32.0);

			dir = Vector (hook->hit_water_pos) - Vector (self->s.origin);
			dir.Normalize ();

			com->VecToAngles (ang, dir);
			ang.AngleVectors (forward, right, up);
			
			// randomly choose left or right
			if (rnd () < 0.5)
				right = right * -1.0;

			// randomly choose towards or away
			if (rnd () < 0.5)
				right = right * -1.0;
				
			wobble_speed = rnd () * 240;
			wander_speed = rnd () * 180;

			dir = Vector (self->velocity) + dir * wander_speed;
			dir.set (self->velocity);

			right = right * wobble_speed;
			right.set (self->velocity);

			hook->change_time = gstate->time + rnd () * 1.0 + 0.1;

			r_scale = 20 / hook->size_factor;
			// change angular velocity because grenade just bounced
			com->SetVector (self->avelocity, r_scale * (rnd () - 0.5) * 700.0, r_scale * (rnd () - 0.5) * 700.0, r_scale * (rnd () - 0.5) * 700.0);
		}*/

		self->velocity [2] = self->velocity [2] * 0.85;
		self->velocity [0] = self->velocity [0] * 0.85;	
		self->velocity [1] = self->velocity [1] * 0.85;	
	}

	hook->last_water = pc;
}

//---------------------------------------------------------------------------
// shell_touch()
//---------------------------------------------------------------------------
static	void	shell_touch (userEntity_t *self, userEntity_t *other,cplane_t *plane, csurface_t *surf)
{
	weapHook_t	*hook = (weapHook_t *) self->userHook;
	Vector		temp, forward, right, up;
	float		velocity;
	int pc;

	temp = Vector (hook->last_pos) - Vector (self->s.origin);

	hook->last_pos = Vector (self->s.origin);	

	hook->rotation_scale = hook->rotation_scale;

	
	if (temp.vlen () < 1.0)
	{
		com->SetVector (self->velocity, 0.0, 0.0, 0.0);
		com->SetVector (self->avelocity, 0.0, 0.0, 0.0);
		self->s.angles [0] = 0.0;
		hook->shell_stop = 1;
		//gstate->SetSize (self, -8.0, -8.0, -8.0, 8.0, 8.0, 8.0);
	}

	velocity = qv_len (self->velocity);
	pc = gstate->PointContents (self->s.origin);
	if ((velocity != 0.0)&&(hook->shell_stop==0))
	{
	
		// change angular velocity because shell just bounced
		//com->SetVector (self->avelocity, hook->rotation_scale * (rnd () - 0.5) * 1400.0, hook->rotation_scale * (rnd () - 0.5) * 1400.0, hook->rotation_scale * (rnd () - 0.5) * 1400.0);
		if (pc != CONTENTS_LAVA && pc != CONTENTS_SLIME && pc != CONTENTS_WATER)
		{
			com->SetVector (self->avelocity, hook->rotation_scale * (rnd () - 0.35) * 50.0, hook->rotation_scale * (rnd () - 0.35) * 50.0, hook->rotation_scale * (rnd () - 0.35) * 50.0);
			temp = Vector (self->avelocity);
		
			temp = Vector (self->size);		
		
			if(velocity >290)
				gstate->sound (self, CHAN_AUTO, winfoSound(SND_BULLETDROP3), 120, ATTN_NORM);
			if((velocity>220)&&(velocity<=290))
				gstate->sound (self, CHAN_AUTO, winfoSound(SND_BULLETDROP5), 120, ATTN_NORM);
			if((velocity>160)&&(velocity<=220))
				gstate->sound (self, CHAN_AUTO, winfoSound(SND_BULLETDROP2), 120, ATTN_NORM);
			if((velocity>100)&&(velocity<=160))
				gstate->sound (self, CHAN_AUTO, winfoSound(SND_BULLETDROP1), 120, ATTN_NORM);
			if(velocity<=100)
				gstate->sound (self, CHAN_AUTO, winfoSound(SND_BULLETDROP4), 120, ATTN_NORM);
		}
	
	}

	hook->last_touch_time = gstate->time;
	
	//Reduce velocity like a shell bouncing
	if(self->velocity[2] > 50)
		self->velocity [2] = self->velocity [2] * 0.85;
	else if(self->velocity[2] > 25)
		self->velocity [2] = self->velocity [2] * 0.95;

	if(self->velocity[1] > 200)
		self->velocity [1] = self->velocity [1] * 0.85;
	else if(self->velocity[1] > 75)
		self->velocity [1] = self->velocity [1] * 0.95;

	if(self->velocity[0] > 40)
		self->velocity [0] = self->velocity [0] * 0.85;
	else if(self->velocity[0] > 20)
		self->velocity [0] = self->velocity [0] * 0.95;
}	

//---------------------------------------------------------------------------
// shell_think()
//---------------------------------------------------------------------------
static	void	shell_think (userEntity_t *self)
{
	weapHook_t		*hook = (weapHook_t *) self->userHook;

	shell_water_friction (self);
	if((hook->my_count < (shell_count - 10))||(hook->my_count == -1))
	{
		//gstate->Con_Printf("Killed Myself!\n");
		com->free (self->userHook);
		gstate->RemoveEntity (self);
      return;
	}
	//in case we roll over shell_count
	//we will just make sure that we kill ourselves soon
	if(hook->my_count > shell_count)
		hook->my_count = -1;
	
	self->nextthink = gstate->time + 0.05;
	self->think = shell_think;
}

//---------------------------------------------------------------------------
// glock_reload()
//---------------------------------------------------------------------------
int   glock_reload(userEntity_t *self)      
{
   playerHook_t   *phook;
   glockWeapon_t  *invWeapon = (glockWeapon_t *) self->curWeapon;
   ammo_t         *ammo = invWeapon->ammo;   
   int            ammo_count;

   // get the number of total rounds that we have
   ammo->command ((userInventory_t *) ammo, "count", &ammo_count);
   if (ammo_count <= 0)
      return   FALSE;

   // cap ammo_count at clip size
   if (ammo_count > 10)
      ammo_count = 10;
   
   // subtract the ammo
   if (!weaponUseAmmo(self->curWeapon,ammo_count))
      return FALSE;

   // put the bullets in the clip
   invWeapon->clip = invWeapon->clip + ammo_count;
   
//   gstate->sound (self, CHAN_AUTO, winfoSound(SND_LOAD), 255, ATTN_NORM);
// gstate->Con_Printf("Bullets in Clip:%d\n",invWeapon->clip);
// gstate->Con_Printf("Bullets left:%d\n",ammo->count);

   phook = (playerHook_t *) self->userHook;  
   winfoAnimDelay(self,winfo,ANIM_RELOAD,.05);
//   phook->attack_finished=gstate->time+3.0;            // remove when reload anim is added

   return   TRUE;
}

//---------------------------------------------------------------------------
// glock_use()
//---------------------------------------------------------------------------
static	void	glock_use (userInventory_s *ptr, userEntity_t *self)
{
	userEntity_t	*glock;
	Vector			forward, right, up, temp, end;
	weapHook_t      *hook;
	playerHook_t	*phook;
	//userTrace_t     trace;
	trace_t			trace;
	glockWeapon_t         *glockp;
	vec3_t          hit_spot;
	int				pc;


   self->s.renderfx |= RF_MUZZLEFLASH;

	phook = (playerHook_t *) self->userHook;

	phook->viewmodel_start_frame = -1;
	phook->viewmodel_end_frame = -1;
	phook->viewmodel_frame_time = 0.1;

	glockp=(glockWeapon_t *)self->curWeapon;
	if(!glockp->clip)
	{
		glock_reload(self);
	   return;
	}
	glockp->clip--;
	
	//Check to see if we have fired 10 bullets
	//If so make them reload & alter the attack rate for the loading sequence
	pc = gstate->PointContents (self->s.origin);
	if (pc == CONTENTS_WATER || pc == CONTENTS_LAVA ||
		pc == CONTENTS_SLIME)
		gstate->sound (self, CHAN_AUTO, winfoSound(SND_FIRE), 255, ATTN_NORM);
	else
		gstate->sound (self, CHAN_AUTO, winfoSound(SND_FIRE), 255, ATTN_NORM);
	
	temp = Vector (self->client->v_angle);
	temp.AngleVectors (forward, right, up);

	phook->fired++;
	
//	forward = forward * 2000;

   temp=Vector(self->s.origin)+weaponHandVector(self,forward*8,Vector(0,0,0))+up*20;

//	temp = Vector (self->s.origin) + forward * 8.0 + up * 20;	
	end = temp + forward * 2000;
	//self->TraceLine(vec3_t start, vec3_t end, int nomonsters, userEntity_t *passent, userTrace_t *trace);
	
	trace = gstate->trace_q2 (temp.vec (), NULL, NULL, end.vec (), self, MASK_SHOT);

	//See if we actually hit something
	if(trace.fraction!=1)
	{
		//Make sure we are damaging something that is meant to be damaged
		if (trace.ent->takedamage != DAMAGE_NO)
		{
			com->Damage (trace.ent, self, self, trace.endpos, forward.vec (), 20, DAMAGE_INERTIAL);

			phook->hit++;
		}

		if (trace.ent->solid == SOLID_BSP || trace.ent->takedamage == DAMAGE_NO)
		{
			vec3_t vec3;

			gstate->WriteByte(SVC_TEMP_ENTITY);
			gstate->WriteByte(TE_GUNSHOT);
			gstate->WritePosition(trace.endpos);
			forward.set(vec3);
			gstate->WriteDir(vec3);
			gstate->MultiCast(self->s.origin,MULTICAST_ALL);
		}
	}	
	
	///////////////////////////////////////
	//Glock ejected shell code starts here
	///////////////////////////////////////


	glock = gstate->SpawnEntity ();
	glock->owner = self;
	glock->movetype = MOVETYPE_BOUNCE;
	glock->solid = SOLID_BBOX;
	glock->className = "glock_shell";
	glock->s.effects=256;

	glock->userHook = com->malloc_hook (sizeof (weapHook_t), MEM_MALLOC);
	hook = (weapHook_t *) glock->userHook;
	
	temp = Vector (self->client->v_angle);
	temp.AngleVectors (forward, right, up);
	
	hook->forward=forward;
	hook->right=right;
	hook->up=up;
	hook->rotate_x = 0;
	hook->shell_stop = 0;	
	
	temp = forward * 28.0 + right * 10.25 + Vector (self->s.origin);
	temp.z = temp.z + 10.0 ;

	temp.set (glock->s.origin);

	pc = gstate->PointContents (glock->s.origin);
	if (pc == CONTENTS_WATER || pc == CONTENTS_LAVA || pc == CONTENTS_SLIME)
		temp = forward * 50.0 + right * 150 + (up * (lim_rand(25) - 2));
	else
		temp = (forward * 75.00) + (up * (75 + lim_rand(75))) + (right * (150 + lim_rand(50)));
		
	temp.set (glock->velocity);

	com->VecToAngles (temp, forward);
	temp.set (glock->s.angles);

	glock->touch = (touch_t)shell_touch;
	glock->nextthink = gstate->time + 0.1;
	glock->think = (think_t)shell_think;

	hook->remove_time = gstate->time + 10.0;
	hook->last_water = CONTENTS_EMPTY;
	hook->hit_water_pos = Vector (glock->s.origin);
	hook->reload = gstate->time + .50;

	winfoSetModel(glock,MDL_SHELL);
   winfoAnim(glock,ANIM_SHELL,.05);
	gstate->SetSize(glock, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
   com->SetVector(glock->s.render_scale,3,3,3);
	gstate->SetOrigin(glock, glock->s.origin [0], glock->s.origin [1], glock->s.origin [2]);

	shell_count++;
	hook->my_count = shell_count;

   winfoAnimDelay(self,winfo,ANIM_SHOOT_STD,.05);
   phook->attack_finished=gstate->time + ((winfoNumFrames(ANIM_SHOOT_STD)*.5) * .05) + .1;

   gstate->LinkEntity(glock);
}

