//---------------------------------------------------------------------------
// HAND OF ZEUS
//---------------------------------------------------------------------------
#include "weapons.h"
#include "zeus.h"

// model defs
#define MDL_BOLT              MDL_SPECIAL+0

// sound defs
#define SND_FIRE              0
#define SND_LIGHTNING         1

// frame defs
#define ANIM_BOLT             ANIM_SPECIAL+0

// weapon info def
weaponInfo_t zeusWeaponInfo = {
      // names, net names, icon
      "weapon_zeus",
      "Hand Of Zeus",
      "ammo_bolt",
      "Bolt",
      "iconname",

      // model files
      {"models/zhand/zhand.md2",
       "models/bolt/bolt.md2",
       NULL},

      // sound files
      {"weapons/handofzeus/handfire.wav",
       "weapons/handofzeus/handlightning.wav",
       NULL},

      // frame names
      {MDL_WEAPON_STD,"ready",0,0,
               -1,"away",0,0,
               -1,"shoot",0,0,
               -1,"amb",0,0,
       MDL_BOLT,"bolta",0,0,
       0,NULL},

      // commands
      {"weapon_zeus",(void (*)())give,
       "select_zeus",(void (*)())select,
       NULL},

       use,
       command,
       sizeof(zeus_t),

      600,
};

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

#define ZEUS_DAMAGE     (((float)(ZEUS_MAX_CHAINS-zap->chain)*2+1)/2)           // 2.5  1.5  .5
#define ZEUS_KILLTIME   2

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

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

//---------------------------------------------------------------------------
// command()
//---------------------------------------------------------------------------
static void *command(struct userInventory_s *ptr, char *commandStr, void *data)
{
	ammo_t *ammo=(ammo_t *)ptr;
   userEntity_t *self=(userEntity_t *)data;
   zeus_t *zeus=(zeus_t *)self->curWeapon;
   playerHook_t *phook=(playerHook_t *)self->userHook;
	
	if (!stricmp(commandStr, "death_msg"))
	{
		killerInfo_t	*ki = (killerInfo_t *) data;
		char			msg [80];

		if (rnd () < 0.5)
			sprintf (msg, "%s takes %s's axe to the head.\n", ki->victim->netname, ki->attacker->netname);
		else
			sprintf (msg, "%s shoves an axe up %s's yam! (and he likes it! HEHE) .\n", ki->attacker->netname, ki->victim->netname);
	}
	else if (!stricmp(commandStr, "ambient") && zeus->numBolts)         // if numBolts, don't do ambient
   {
   }
   else
      winfoGenericCommand(winfo,ptr,commandStr,data);

	return(NULL);
}

//---------------------------------------------------------------------------
// use()
//---------------------------------------------------------------------------
static void use(userInventory_s *ptr, userEntity_t *self)
{
   playerHook_t *phook=(playerHook_t *)self->userHook;
   zeus_t *zeus=(zeus_t *)self->curWeapon;

   if (!weaponUseAmmo(self->curWeapon,0) || zeus->numBolts)
      return;
   
   winfoPlay(SND_FIRE,255);

   killAllBolts(self);
   winfoAnimDelay(self,winfo,ANIM_SHOOT_STD,0.05);
   phook->fxFrameNum=winfoFirstFrame(ANIM_SHOOT_STD)+20;
   phook->fxFrameFunc=th_spawnBolts;
}

//---------------------------------------------------------------------------
// th_spawnBolts() - called on a specific attack frame -- builds all bolts
//---------------------------------------------------------------------------
static void th_spawnBolts(userEntity_t *self)
{
   playerHook_t *phook=(playerHook_t *)self->userHook;
   zeus_t *zeus=(zeus_t *)self->curWeapon;
   short loop;

   // reset anim fx
   phook->fxFrameNum=0;
   phook->fxFrameFunc=NULL;

   // look for entities to zap!
   memset(zeus->zap,0,sizeof(zeus->zap));
   if (!findTargets(zeus,self,5))
      return;

   winfoPlay(SND_LIGHTNING,255);

   for (loop=0; loop<ZEUS_MAX_BOLTS; loop++)
      if (zeus->zap[loop].ent)
      {
         zeusZap_t *zap=&zeus->zap[loop];

         buildBolt(self,zap,ZEUS_ADD_CHAIN,loop);
         zap->killTime=gstate->time+ZEUS_KILLTIME;
         zap->bolt->owner=self;

         zeus->owner=self;                // so zeus originator won't be hit
         zeus->numBolts++;
      }
}

//---------------------------------------------------------------------------
// th_shock() - the first bolt section of each bolt runs this think
//---------------------------------------------------------------------------
static void th_shock(userEntity_t *self)
{
   userEntity_t *ent,*owner;
   zeus_t *zeus;
   zeusZap_t *zap;
   Vector dir;
   float damage;
   short loop,sect,ammo;
   short kill=FALSE;

   owner=self->owner;
   zeus=(zeus_t *)owner->curWeapon;
   zap=&zeus->zap[(short)self->frags];          // frags holds slot for this bolt

   ammo=weaponUseAmmo(owner->curWeapon,1);
   if (!ammo)
      kill=TRUE;

   if (rnd() <= .1)
      winfoPlay(SND_LIGHTNING,255);

   // still alive?
   if (zap->ent->deadflag != DEAD_NO)
      kill=TRUE;

   // still valid target?
   if (!ai_visible(zap->owner,zap->ent))
      kill=TRUE;

   // time for bolt to die?
   if (gstate->time >= zap->killTime)
      kill=TRUE;

   // kill this lightning bolt?
   if (kill)
   {
      untrackEntity(zap->bolt,NULL,MULTICAST_ALL);
      gstate->RemoveEntity(zap->bolt);
      zeus->numBolts--;
      return;
   }

   // chain bolts from target?
   if (zap->chain < ZEUS_MAX_CHAINS)
   {
      zeusZap_t *newzap;

      if (newzap=findTargets(zeus,zap->ent,1))
      {
         buildBolt(zap->ent,newzap,ZEUS_ADD_CHAIN,newzap-zeus->zap);
         newzap->killTime=zap->killTime;
         newzap->bolt->owner=zeus->owner;
         zeus->numBolts++;
      }
   }

   // suck it down! you've been zapped!
   dir=Vector(zap->owner->s.origin)-Vector(zap->ent->s.origin);
   damage=ZEUS_DAMAGE;
   com->Damage(zap->ent,zap->bolt,zap->owner,zap->ent->s.origin,dir.vec(),damage,DAMAGE_INERTIAL);
}

//---------------------------------------------------------------------------
// findTargets() - finds a valid target and adds it to the zap list
//---------------------------------------------------------------------------
static zeusZap_t *findTargets(zeus_t *zeus,userEntity_t *src,short num)
{
   userEntity_t *ent=gstate->FirstEntity();
   zeusZap_t *newzap=NULL;
   short loop,slot,found=0;

   // don't let entities in the chain zap more than 1 other entity
   if (num==1)
      for (loop=0; loop<ZEUS_MAX_BOLTS; loop++)
      {
         zeusZap_t *zap=&zeus->zap[loop];

         if (zap->owner==src)
            return(NULL);
      }

   // find an entity to zap!
   while (ent)
   {
      if (((ent->flags & FL_CLIENT) || (ent->flags & FL_BOT) || (ent->flags & FL_MONSTER)) && 
          (ent->deadflag==DEAD_NO) && (ent != zeus->owner) && !(ent->flags & RF_TRACKENT))
      {
         if (ai_visible(src,ent))
         {
            // find open slot and determine if ent is already in list
            slot=-1;
            for (loop=0; loop<ZEUS_MAX_BOLTS; loop++)
            {
               zeusZap_t *zap=&zeus->zap[loop];

               if (zap->ent==ent)
                  break;

               if (!zap->ent && slot==-1)
                  slot=loop;
            }

            // add to list and stop searching
            if (loop==ZEUS_MAX_BOLTS && slot!=-1)
            {
               found++;
               zeus->zap[slot].ent=ent;
               newzap=&zeus->zap[slot];
               if (found==num)
                  break;
            }
         }
      }

      ent=gstate->NextEntity(ent);
   }

   return(newzap);
}

//---------------------------------------------------------------------------
// assignFingers
//---------------------------------------------------------------------------
static void assignFingers(userEntity_t *src,zeus_t *zeus)
{
}

//---------------------------------------------------------------------------
// buildBolt() - creates a lightning bolt
//---------------------------------------------------------------------------
static void buildBolt(userEntity_t *src,zeusZap_t *zap,short chain,short slot)
{
   userEntity_t *dst=zap->ent;
   trackInfo_t tinfo;
   Vector pos,angles;

   // spawn bolt
   zap->bolt=gstate->SpawnEntity();
   zap->bolt->className="zeus bolt";
	zap->bolt->movetype=MOVETYPE_FLY;
	zap->bolt->solid=SOLID_NOT;
   zap->bolt->flags=RF_TRACKENT;          // drawn by client
   zap->bolt->s.effects=EF_GIB;           // not really GIBS, makes server send to clients
	zap->bolt->s.frame=2;                  // beam diameter
	zap->bolt->s.skinnum=0xa1a2a3a4;       // beam color
   zap->bolt->s.alpha=.8;
   com->CopyVector(zap->bolt->s.origin,src->s.origin);
   gstate->LinkEntity(zap->bolt);

   // msg client to track this entity
   tinfo.flags=0;
   tinfo.ent=zap->bolt;
   tinfo.srcent=src;
   tinfo.dstent=zap->ent;
   tinfo.fru[0]=8;         // forward offset
   tinfo.fru[1]=1;       // right offset
   tinfo.fru[2]=-3;        // up offset
   tinfo.flags |= TEF_SRCINDEX|TEF_DSTINDEX|TEF_FRU;
   tinfo.flags |= RF_LIGHTNING|RF_TRANSLUCENT;
   trackEntity(&tinfo,MULTICAST_ALL);

   zap->chain += chain;                // save bolt # when chaining
   zap->owner=src;                     //

   zap->bolt->prethink=th_shock;       // first bolt section 'thinks' for entire bolt
   zap->bolt->frags=slot;              // frags holds slot for this bolt
}

//---------------------------------------------------------------------------
// killAllBolts() - kill all 'bolt section' entities
//---------------------------------------------------------------------------
static void killAllBolts(userEntity_t *self)
{
   userEntity_t *ent;
   zeus_t *zeus=(zeus_t *)self->curWeapon;
   short loop,sect;

   for (loop=0; loop<ZEUS_MAX_BOLTS; loop++)
   {
      zeusZap_t *zap=&zeus->zap[loop];

      if (!zap->ent)
         continue;

      untrackEntity(zap->bolt,NULL,MULTICAST_ALL);
      gstate->RemoveEntity(zap->bolt);
      zap->ent=NULL;
   }

   zeus->numBolts=0;
   self->nextthink=gstate->time+0.1;
}







// CAN THIS JUST BE GLOBAL???

/////////////////////////////////////////
//	ai_infront
//
//	determines if target is within 
//	self's fov (normally 90 for monsters)
/////////////////////////////////////////

int ai_infront(userEntity_t *self, userEntity_t *target)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	Vector	ang, f, r, u, dir;
	float	dp;

	// get facing vector
	ang = Vector (self->s.angles);
	ang.AngleVectors (f, r, u);
	
	// get vector to target
	// FIXME: make z's the same before getting dir
	dir = Vector (target->s.origin) - Vector (self->s.origin);
	dir.Normalize ();

	dp = dir ^ f;

	if (dp > hook->fov)
		return	TRUE;
	else
		return	FALSE;
}

/*
==============
ai_visible

determines if targ is within the fov of self and if a line
can be traces between the two
==============
*/

int ai_visible(userEntity_t *self, userEntity_t *targ)
{
	Vector			org, end;

	if (ai_infront (self, targ))
	{
		end = ((Vector (targ->absmin) + Vector (targ->absmax)) * 0.5) + targ->view_ofs;
		org = Vector (self->s.origin) + Vector (self->view_ofs);

		gstate->TraceLine (org.vec (), end.vec (), TRUE, self, &trace);

		if (trace.fraction == 1.0 || trace.ent == targ)
		{
			org = org - Vector (trace.endpos);
			
			// return the distance to target
			return	org.vlen ();
		}
	}

	return	FALSE;
}
