//---------------------------------------------------------------------------
// ION BLASTER
//---------------------------------------------------------------------------
#include "weapons.h"
#include "ionblaster.h"

// model defs
#define MDL_SHOT              MDL_SPECIAL+0
#define MDL_DISSIPATE         MDL_SPECIAL+1
#define MDL_WALLEXP           MDL_SPECIAL+2

// sound defs
#define SND_FIRE              0
#define SND_EXPLODE1          1
#define SND_EXPLODE2          2
#define SND_EXPLODE3          3
#define SND_FLYBY             4
#define SND_HIT               5

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

// weapon info def
weaponInfo_t ionblasterWeaponInfo = {
      // names, net names, icon
      "weapon_ionblaster",
      "Ion Blaster",
      "ammo_ionglob",
      "Ion Glob",
      "iconname",

      // model files
      {"models/ionblaster/ionblaster.md2",
       "models/flare/flare.sp2",
       "models/f_dis/f_dis.sp2",
       "models/g_expl/g_expl.sp2",
       NULL},

      // sound files
      {"weapons/ionblaster/ionfire.wav",
       "weapons/ionblaster/ionexplode.wav",
       "weapons/ionblaster/ionexplode2.wav",
       "weapons/ionblaster/ionexplode3.wav",
       "weapons/ionblaster/ionflyby.wav",
       "weapons/ionblaster/ionhit.wav",
       NULL},

      // frame names
      {MDL_WEAPON_STD,"ready",0,0,
               -1,"away",0,0,
               -1,"shoota",0,0,
               -1,"amba",0,0,
               -1,"reload",0,0,
       MDL_WALLEXP,"sp2",0,0,
       0,NULL},

      // commands
      {"weapon_ionblaster",(void (*)())ionblaster_give,
       "select_ionblaster",(void (*)())ionblaster_select,
       NULL},

   ionblaster_use,
   ionblaster_command,
   sizeof(weapon_t),

   200,
};

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

//---------------------------------------------------------------------------
// ionblaster_give()
//---------------------------------------------------------------------------
static	userInventory_t *ionblaster_give (userEntity_t *self)
{
   return(weaponGive(self,winfo,100));
}

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

//---------------------------------------------------------------------------
// ionblaster_command()
//---------------------------------------------------------------------------
static	void *ionblaster_command (struct userInventory_s *ptr, char *commandStr, void *data)
{
   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];

		sprintf (msg, "%s burns %s a new anus.\n", ki->victim->netname, ki->attacker->netname);

		gstate->bprint (msg);
	}
	else if (!stricmp (commandStr, "suicide_msg"))
	{
		killerInfo_t	*ki = (killerInfo_t *) data;
		char			msg [80];

		sprintf (msg, "%s burns himself a new anus.\n", ki->attacker->netname);

		gstate->bprint (msg);
	}
	else if (!stricmp (commandStr, "ambient"))
   {     // no ambient for this weapon... delete this section when it gets one
   }
   else
      winfoGenericCommand(winfo,ptr,commandStr,data);

	return(NULL);
}

//---------------------------------------------------------------------------
// ionblaster_touch()
//---------------------------------------------------------------------------
static void ionblaster_touch(userEntity_t *self,userEntity_t *other,cplane_t *plane, csurface_t *surf)
{
	ionHook_t	*hook = (ionHook_t *) self->userHook;
	playerHook_t *phook = (playerHook_t *) self->owner->userHook;
	Vector		temp, forward, right, up, pos;

   hook->bounce_count++;

   // hit wall 3 times???         OR      hit sky??
   if ((hook->bounce_count >= 3) || (surf && (surf->flags & SURF_SKY)))
   {
      hook->dissolve=true;
      winfoSetModel(self,MDL_DISSIPATE);
      self->s.frame=0;
      self->touch=false;
   }

	// hit entity?
   if (other->takedamage != DAMAGE_NO)
	{
	   //	Nelno:	new damage routine
		com->Damage(other, self, self->owner, self->s.origin, zero_vector, 30, DAMAGE_NONE | DAMAGE_INFLICTOR_VEL);
		phook->hit++;

		winfoPlay(SND_EXPLODE1+rnd()*2.5,255);

		// move the shot back a few units to keep it out of walls
		temp = Vector (self->velocity);
		temp.Normalize ();
		temp = Vector (self->s.origin) - temp * 8.0;

      winfoSetModel(self,MDL_WALLEXP);
      self->think=ionExplodeThink;
      self->touch=false;

      self->s.effects &= ~EF_BFG;
      com->ZeroVector(self->velocity);

      spawnIonExplode(Vector(self->s.origin),Vector(plane->normal));

   	com->free(self->userHook);
      return;
	}
   else if (!hook->dissolve)
   {
		winfoPlay(SND_HIT,255);
      spawnIonExplode(Vector(self->s.origin),Vector(plane->normal));
   }
}

//---------------------------------------------------------------------------
// spawnIonExplode()
//---------------------------------------------------------------------------
static void spawnIonExplode(Vector pos, Vector normal)
{
	Vector ang,org;

	normal.Normalize();
	com->VecToAngles(ang,normal);
	org=pos+normal*0.125;

   winfoClientExplosion(winfo,org,ang,1.0,MDL_WALLEXP,ANIM_WALLEXP);
}

//---------------------------------------------------------------------------
// ionExplodeThink()
//---------------------------------------------------------------------------
static void ionExplodeThink(userEntity_t *self)
{
   if (++self->s.frame >= 10)
   {
      untrackLight(self);
      gstate->RemoveEntity(self);
      return;
   }

   self->nextthink=gstate->time+.1;
}

//---------------------------------------------------------------------------
// ionblaster_think()
//---------------------------------------------------------------------------
static	void	ionblaster_think (userEntity_t *self)
{
	ionHook_t		*hook = (ionHook_t *) self->userHook;
   short loop;

	mikef=qv_len(self->velocity);

	if (qv_len(self->velocity) == 0 || self->groundEntity)
   {
      untrackLight(self);
   	com->free (self->userHook);
		gstate->RemoveEntity (self);
		return;
   }

	if(hook->flyby_timer < gstate->time)
	{
		hook->flyby_timer = gstate->time + .20;
		gstate->sound (self, CHAN_AUTO, winfoSound(SND_FLYBY), 255, ATTN_NORM);
	}
	
	self->nextthink = gstate->time + 0.05;
	self->think = ionblaster_think;

// code to rotate sphere......
//   self->s.angles[2] += 10;
//   self->client->v_angle[2] += 10;

   for (loop=0; loop<3; loop++)
   {
      self->s.angles[loop]++;
      self->client->v_angle[loop]++;
   }
//.............................

	self->s.frame++;
	if (hook->dissolve)
   {
      self->velocity[0] *= .7;
      self->velocity[1] *= .7;
      self->velocity[2] *= .7;

      if (self->s.frame >= 7)
      {
         untrackLight(self);
         com->free(self->userHook);
         gstate->RemoveEntity(self);
      }
   }
   else
   	if (self->s.frame > 9)
	   	self->s.frame = 0;
}

//---------------------------------------------------------------------------
// ionblaster_use()
//---------------------------------------------------------------------------
void	ionblaster_use (userInventory_s *ptr, userEntity_t *self)
{
	userEntity_t	*ionblaster_shot;
	Vector			forward, right, up, temp;
	ionHook_t		*hook;
	playerHook_t	*phook;
	weapon_t    *ionblaster;
	int				pc;

	
	///////////////////////////////////
	// check for ammo for this weapon
	///////////////////////////////////

	if (!weaponUseAmmo(self->curWeapon,1))
		return;

	phook = (playerHook_t *) self->userHook;
	ionblaster = (weapon_t *) self->curWeapon;

//	self->s.effects |= RF_MUZZLEFLASH;
	
	temp = Vector (18.0, 6.0, 12.0);
	ionblaster_shot = weapon_spawn_projectile (self, temp, 800.0, 0, "projectile_ion");
	ionblaster_shot->movetype = MOVETYPE_BOUNCEMISSILE;
   ionblaster_shot->elasticity=1.9;
//	ionblaster_shot->flags = FL_EXPLOSIVE;
//	ionblaster_shot->s.effects = EF_BFG;

	// malloc the memory for the weapon hook
	ionblaster_shot->userHook = com->malloc_hook (sizeof (ionHook_t), MEM_MALLOC);
	hook = (ionHook_t *) ionblaster_shot->userHook;

	hook->flyby_timer=gstate->time + .98;
	
	ionblaster_shot->touch = (touch_t)ionblaster_touch;
	ionblaster_shot->nextthink = gstate->time + 0.1;
	ionblaster_shot->think = (think_t)ionblaster_think;

//	hook->remove_time = gstate->time + 4.0;
   hook->bounce_count=0;
   hook->dissolve=0;

   winfoSetModel(ionblaster_shot,MDL_SHOT);
	gstate->SetSize (ionblaster_shot, -7.0, -7.0, -7.0, 7.0, 7.0, 7.0);
	gstate->SetOrigin (ionblaster_shot, ionblaster_shot->s.origin [0], ionblaster_shot->s.origin [1], ionblaster_shot->s.origin [2]);

   winfoPlay(SND_FIRE,255);
	
	ionblaster_shot->translucent = 3;
	ionblaster_shot->avelocity [0] = rnd () * 360 - 180;
	ionblaster_shot->avelocity [1] = rnd () * 360 - 180;
	ionblaster_shot->avelocity [2] = rnd () * 360 - 180;

   winfoAnimDelay(self,winfo,ANIM_SHOOT_STD,.05);

	ionblaster_shot->s.renderfx |= SPR_ALPHACHANNEL;

   trackLight(ionblaster_shot,175,0,.8,0);

   trackLight(self,300,0,1,0);
   phook->fxFrameNum=winfoFirstFrame(ANIM_SHOOT_STD)+3;
   phook->fxFrameFunc=fireFlashOff;
	phook->fired++;
}

//---------------------------------------------------------------------------
// fireFlashOff()
//---------------------------------------------------------------------------
static void fireFlashOff(userEntity_t *self)
{
   playerHook_t *phook=(playerHook_t *)self->userHook;

   untrackLight(self);
   phook->fxFrameFunc=NULL;
}
