//---------------------------------------------------------------------------
// SIDEWINDER
//---------------------------------------------------------------------------
#include "weapons.h"
#include "sidewinder.h"

// model defs
#define MDL_MISSILE			   MDL_SPECIAL+0
#define MDL_AMBER             MDL_SPECIAL+1

// sound defs
#define SND_FIRE              0
#define SND_EXPLODE           1
#define SND_FLYBY             2
#define SND_WATERFLYBY        3
#define SND_SPLASH            4
#define SND_WATERFIRE         5

// frame defs
#define ANIM_RELOAD           ANIM_SPECIAL+0

// weapon info def
weaponInfo_t sidewinderWeaponInfo = {
      // names, net names, icon
      "weapon_sidewinder",
      "Sidewinder",
      "ammo_ionpack",
      "shock-sphere",
      "iconname",

      // model files
      {"models/swinder/swinder.md2",
       "models/missile/missile.md2",
       "models/flares/amber/amber.sp2",
       NULL},

      // sound files
      {"weapons/sidewinder/sidewinderfire3.wav",
       "weapons/sidewinder/sidewinderexplode.wav",
       "weapons/sidewinder/sidewinderflyby.wav",
       "weapons/sidewinder/sidewinderunderwaterflyby.wav",
       "weapons/sidewinder/sidewindersplash.wav",
       "weapons/sidewinder/sidewinderunderwaterfire.wav",
       NULL},

      // frame names
      {MDL_WEAPON_STD,"readya",0,0,
               -1,"away",0,0,
               -1,"shoot",0,0,
               -1,"amba",0,0,
               -1,"reload",0,0,
       0,NULL},

      // commands
      {"weapon_sidewinder",(void (*)())sidewinder_give,
       "select_sidewinder",(void (*)())sidewinder_select,
       NULL},

   sidewinder_use,
   sidewinder_command,
   sizeof(weapon_t),

   500,
};

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

#define	SW_THINK_TIME	0.05

typedef struct
{
	int	    delta_phix;
	int     delta_phiy;
	int		delta_vel_x;
	int     delta_vel_y;
	int		frequency;
	float	remove_time;
	Vector	forward, right, up;
	frictionHook_t	*fhook;
} sidewinderHook_t;

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

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

//---------------------------------------------------------------------------
// sidewinder_command()
//---------------------------------------------------------------------------
static	void *sidewinder_command (struct userInventory_s *ptr, char *commandStr, void *data)
{
	if (!stricmp (commandStr, "death_msg"))
	{
		killerInfo_t	*ki = (killerInfo_t *) data;
		char			msg [80];

		if (rnd () < 0.5)
			sprintf (msg, "%s's snake struck %s down.\n", ki->attacker->netname, ki->victim->netname);
		else
			sprintf (msg, "%s rides %s's winder.\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];

		if (rnd () < 0.5)
			sprintf (msg, "%s go boom.\n", ki->attacker->netname);
		else
			sprintf (msg, "%s got caught playing with his snake.\n", ki->attacker->netname);

		gstate->bprint (msg);
	}
   else
      winfoGenericCommand(winfo,ptr,commandStr,data);

	return(NULL);
}

//---------------------------------------------------------------------------
// sidewinder_explode()
//---------------------------------------------------------------------------
void	sidewinder_explode (userEntity_t *self)
{
	int	pc;
	sidewinderHook_t	*hook = (sidewinderHook_t *) self->userHook;
	playerHook_t		*phook = (playerHook_t *) self->owner->userHook;
	frictionHook_t		*fhook = (frictionHook_t *) hook->fhook;
	Vector				temp;
	pc = gstate->PointContents (self->s.origin);

	if (pc != CONTENTS_WATER)     //CONTENTS_SKY)// && fhook->last_water != CONTENTS_SKY)     mdm q2fix
	{
		if (com->RadiusDamage (self, self->owner, self, 90, DAMAGE_SIDEWINDER))
			phook->hit++;	

		// back off from the point of impact just a bit
		temp = Vector (self->velocity);
		temp.Normalize ();
		temp = Vector (self->s.origin) - temp * 8.0;
		temp.set (self->s.origin);

		winfoPlay(SND_EXPLODE,50);

		spawn_sprite_explosion (self, self->s.origin, FALSE);
	}

	// remove the friction hook
	com->friction_remove (hook->fhook);

	com->free (self->userHook);
	gstate->RemoveEntity (self);
}

//---------------------------------------------------------------------------
// sidewinder_touch()
//---------------------------------------------------------------------------
static	void	sidewinder_touch (userEntity_t *self, userEntity_t *other, cplane_t *plane, csurface_t *surf)
{
	if (self->owner == other || other->flags & FL_EXPLOSIVE)
		return;

   if (surf && (surf->flags & SURF_SKY))
   {
      com->free(self->userHook);
      gstate->RemoveEntity(self);
   }
   else
	   sidewinder_explode (self);
}

//---------------------------------------------------------------------------
// sidewinder_spin()
//---------------------------------------------------------------------------
static	void sidewinder_spin (sidewinderHook_t *weap_h, userEntity_t *self)
{
	int pc;
	int forward_speed;

	Vector  delta_x,delta_y;
	float sinthis[ ] = {0.017,0.515,0.874,0.999,0.857,0.484,-0.017,-0.515,-0.874,-0.999,-0.857,-0.484};
	float costhis[ ] = {0.999,0.857,0.484,-0.017,-0.515,-0.874,-0.999,-0.857,-0.484,0.017,0.515,0.874};
	
	delta_x=weap_h->right * ((weap_h->delta_vel_x) * sinthis[weap_h->delta_phix]);
	delta_y=weap_h->up * (((weap_h->delta_vel_y)) * costhis[weap_h->delta_phiy]);

	//determine if the rocket is underwater
	pc = gstate->PointContents (self->s.origin);

	if (pc == CONTENTS_WATER || pc == CONTENTS_LAVA ||
		pc == CONTENTS_SLIME)
	{
//		weap_h->delta_vel_x=40;//20
//		weap_h->delta_vel_y=40;
		forward_speed = 325.0;
	}
	else
	{
//		weap_h->delta_vel_x=80;//40
//		weap_h->delta_vel_y=80;
		forward_speed = 650.0;
	}


	delta_x=delta_x + weap_h->forward * forward_speed;
	delta_y=delta_y + delta_x;
	
	delta_y.set(self->velocity);
	
	//Increase angle by 15 degrees
	weap_h->delta_phix++;


	if(weap_h->delta_phix > 11)
		weap_h->delta_phix=0;

	weap_h->delta_phiy++;
	if(weap_h->delta_phiy > 11)
		weap_h->delta_phiy=0;


	if(weap_h->frequency==1)
	{
		if (pc == CONTENTS_WATER || pc == CONTENTS_LAVA ||
			pc == CONTENTS_SLIME)
		{
         winfoPlay(SND_WATERFLYBY,255);
			weap_h->frequency=12;
		}
		else
		{
         winfoPlay(SND_FLYBY,255);
			weap_h->frequency=12;
		}
	}
	else
		weap_h->frequency--;
}

//---------------------------------------------------------------------------
// sidewinder_think()
//---------------------------------------------------------------------------
static	void	sidewinder_think (userEntity_t *self)
{
	sidewinderHook_t		*hook = (sidewinderHook_t *) self->userHook;

	self->s.frame++;
	if (self->s.frame > 3)
		self->s.frame = 0;

	// content friction
	com->friction_apply (self, hook->fhook);

	sidewinder_spin (hook, self);

	if (gstate->time >= hook->remove_time)
		sidewinder_explode (self);
	else
	{
//		gstate->particle (self->s.origin, zero_vector, gstate->global_ltgray, 128);	//	128 for pt_fire

		self->nextthink = gstate->time + SW_THINK_TIME;
	}
}

//---------------------------------------------------------------------------
//	sidewinder_spawn_glow
//
//	attaches a glowing, alpha-blended sprite to the tail of the rocket
//	rocket should have it's facing angles set before calling this routine
//	since the glow is attached as HR_RELATIVE, which relies on initial
//	position and angles relative to the rocket (parent)
//---------------------------------------------------------------------------

static	void	sidewinder_spawn_glow (userEntity_t *rocket)
{
	userEntity_t	*glow;
	Vector			ang, org;

	ang = Vector (rocket->s.angles);
	ang.x = -ang.x;
	ang.AngleVectors (forward, right, up);

	glow = gstate->SpawnEntity ();
	glow->movetype = MOVETYPE_NONE;
	glow->solid = SOLID_NOT;

	glow->s.renderfx = SPR_ALPHACHANNEL + RF_TRANSLUCENT;

	winfoSetModel (glow,MDL_AMBER);

	VectorCopy (zero_vector, glow->s.mins);
	VectorCopy (zero_vector, glow->s.maxs);

	//	put glow x units behind rocket's origin (x is just a fudged value)
	org = Vector (rocket->s.origin);
	org.set (glow->s.origin);

	gstate->LinkEntity (glow);

	gstate->hr_InitRelative (rocket, glow, 0);
}

//---------------------------------------------------------------------------
// sidewinder_fire_rocket()
//---------------------------------------------------------------------------
static	userEntity_t	*sidewinder_fire_rocket (userEntity_t *self, Vector	offset)
{
	userEntity_t	*rocket;
	Vector			temp;
	sidewinderHook_t		*hook;
	playerHook_t	*phook;
	int				pc;
	float			forward_vel;

	pc = gstate->PointContents (self->s.origin);
	if (pc == CONTENTS_WATER || pc == CONTENTS_LAVA || pc == CONTENTS_SLIME)
		forward_vel = 250.0;
	else
		forward_vel = 650.0;

//	temp = Vector (offsetx, 8.0, 16.0);
	rocket = weapon_spawn_projectile (self, offset, forward_vel, 0, "projectile_missile");
	rocket->movetype = MOVETYPE_FLYMISSILE;
	rocket->flags = FL_EXPLOSIVE;
	rocket->s.effects = EF_ROCKET;
	rocket->clipmask = MASK_MONSTERSOLID;

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

	// set up frictionHook
	hook->fhook = com->friction_init (self, hook->fhook);
	com->friction_set_physics (self, hook->fhook, 1.0, 1.0, 650.0, 0);

	rocket->touch = sidewinder_touch;
	rocket->nextthink = gstate->time + SW_THINK_TIME;
	rocket->think = sidewinder_think;

	rocket->avelocity [2] = 180.0;

	hook->remove_time = gstate->time + 8.0;

	hook->forward = forward;
	hook->right = right;
	hook->up = up;

	com->SetVector (rocket->s.render_scale, 3.0, 3.0, 3.0);
//	gstate->SetSize (rocket, -8.0, -8.0, -8.0, 8.0, 8.0, 8.0);
	VectorCopy (zero_vector, rocket->s.mins);
	VectorCopy (zero_vector, rocket->s.maxs);
	gstate->LinkEntity (rocket);

	sidewinder_spawn_glow (rocket);

	return	rocket;
}

//---------------------------------------------------------------------------
// sidewinder_use()
//---------------------------------------------------------------------------
static	void	sidewinder_use (userInventory_t *ptr, userEntity_t *self)
{
   userEntity_t *wptr=(userEntity_t *)self->client->ps.weapon;
   weapon_t *weapon=(weapon_t *)self->curWeapon;
	userEntity_t		*rocket;
	Vector				temp;
	sidewinderHook_t	*hook;
	frictionHook_t		*fhook;
	playerHook_t		*phook;
	int					pc;

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

	phook = (playerHook_t *) self->userHook;

	/////////////////////////////////
	//	spawn first projectile
	/////////////////////////////////

	rocket = sidewinder_fire_rocket (self, Vector (10.0, 8.0, 8.0));
	winfoSetModel(rocket,MDL_MISSILE);
	rocket->s.renderfx = SPR_ALPHACHANNEL;
	hook = (sidewinderHook_t *) rocket->userHook;

	//Setup sine function for rocket path
	hook->delta_phix=0;
	hook->delta_phiy=0;
	hook->delta_vel_x=80;
	hook->delta_vel_y=80;
	hook->frequency=6;
	
	//call the function to start the motion
	sidewinder_spin (hook, rocket);
	
	////////////////////////////////
	//Second Sidewinder starts here
	////////////////////////////////

	rocket = sidewinder_fire_rocket (self, Vector (10.0, 8.0, 12.0));
	winfoSetModel(rocket,MDL_MISSILE);
	rocket->s.renderfx = SPR_ALPHACHANNEL;
	hook = (sidewinderHook_t *) rocket->userHook;

	//Setup sine function for rocket path
	hook->delta_phix=5;
	hook->delta_phiy=5;
	hook->delta_vel_x=80;
	hook->delta_vel_y=80;
	hook->frequency=3;
	
	//call the function to start the motion
	sidewinder_spin (hook, rocket);

	/*
	fhook = (frictionHook_t *) hook->fhook;	
	if (fhook->last_water == CONTENTS_WATER || fhook->last_water == CONTENTS_LAVA || hook->last_water == CONTENTS_SLIME)
		gstate->sound (self, CHAN_WEAPON, "weapons/sidewinder/sidewinderunderwaterfire.wav", 255, ATTN_NORM);
	else
		gstate->sound (self, CHAN_WEAPON, "weapons/sidewinder/sidewinderfire.wav", 255, ATTN_NORM);
	*/

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

   winfoPlay(SND_FIRE,255);

	// for stat keeping
	phook->fired++;
}

