//---------------------------------------------------------------------------
// NIGHTMARE
//---------------------------------------------------------------------------
#include "weapons.h"
#include "nightmare.h"

// model defs
#define MDL_REAPER         MDL_SPECIAL+0
#define MDL_PENTAGRAM      MDL_SPECIAL+1

// sound defs
#define SND_APPEAR         0
#define SND_ATTACK         1
#define SND_CHANT1         2
#define SND_CHANT2         3
#define SND_CHANT3         4
#define SND_CHANT4         5
#define SND_CHANT5         6
#define SND_WIND           7

// frame defs
#define ANIM_AMBIENT2      ANIM_SPECIAL+0
#define ANIM_AMBIENT3      ANIM_SPECIAL+1
#define ANIM_REAPER        ANIM_SPECIAL+2
#define ANIM_PENTAGRAM     ANIM_SPECIAL+3

// weapon info def
weaponInfo_t nightmareWeaponInfo = {
      // names, net names, icon
      "weapon_nightmare",
      "nharres nightmare",
      "ammo_ionpack",
      "shock-sphere",
      "iconname",

      // model files
      {"models/nmare/nmare.md2",
       "models/reaper/reaper.md2",
       "models/pentmodel/pentmodel.md2",
       "models/reaper/reaper.md2",
       NULL},

      // sound files
      {"weapons/nightmare/reaperappear2.wav",
       "weapons/nightmare/reaperattack2.wav",
       "weapons/nightmare/chant1.wav",
       "weapons/nightmare/chant2.wav",
       "weapons/nightmare/chant3.wav",
       "weapons/nightmare/chant4.wav",
       "weapons/nightmare/chant5.wav",
       "weapons/nightmare/nharrewind.wav",
       NULL},

      // frame names
      {MDL_WEAPON_STD,"ready",0,0,
               -1,"away",0,0,
               -1,"shoot",0,0,
               -1,"amba",0,0,
               -1,"ambb",0,0,
               -1,"ambc",0,0,
       MDL_REAPER,"ataka",0,0,
       MDL_PENTAGRAM,"pent",0,0,
       0,NULL},

      // commands
      {"weapon_nightmare",(void (*)())give,
       "select_nightmare",(void (*)())select,
       NULL},

      use,
      command,
      sizeof(nightmare_t),

      150,
};

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

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

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

//---------------------------------------------------------------------------
// command()
//---------------------------------------------------------------------------
static void *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];

		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"))
   {
      short animList[]={ANIM_AMBIENT_STD,ANIM_AMBIENT2,ANIM_AMBIENT3},animnum;

      animnum=animList[(short)(rnd()*2.9)];

      winfoAnim((userEntity_t *)data,animnum,.05);
      phook->ambient_time=gstate->time+(winfoNumFrames(animnum)*.05)+2.0+(rnd()*6);
   }
   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;
   nightmare_t *nightmare=(nightmare_t *)self->curWeapon;

   // may want to just set phook->attack_finished...
   if (nightmare->count)
      return;

   winfoAnimDelay(self,winfo,ANIM_SHOOT_STD,0.05);

   winfoPlay(SND_CHANT1+(rnd()*4.9),255);
   winfoPlay(SND_WIND,255);
   tracePentagram(self);

   phook->fxFrameNum=winfoLastFrame(ANIM_SHOOT_STD)-8;
   phook->fxFrameFunc=timeToDie;
}

//---------------------------------------------------------------------------
// tracePentagram()
//---------------------------------------------------------------------------
static void tracePentagram(userEntity_t *self)
{
   nightmare_t *nightmare=(nightmare_t *)self->curWeapon;
   trackInfo_t tinfo;
   userEntity_t *pent;

   // setup weapon entity
   pent=gstate->SpawnEntity();
   pent->owner=self;
   pent->movetype=MOVETYPE_FLY;
   pent->solid=SOLID_NOT;
   pent->className="nightmare_pentagram";
   pent->s.effects=256;
   pent->s.renderfx=RF_TRACKENT|RF_TRANSLUCENT|RF_NODRAW;
   pent->s.alpha=.8;

   nightmare->transadd=-.1;
   nightmare->starttrack=true;

   winfoSetModel(pent,MDL_PENTAGRAM);
   pent->s.frame=winfoFirstFrame(ANIM_PENTAGRAM);
   pent->think=doPentagram;
   pent->nextthink=gstate->time+.1;
   com->SetVector(pent->s.render_scale,1,1,1);
   com->ZeroVector(pent->s.angles);
   gstate->LinkEntity(pent);
}

//---------------------------------------------------------------------------
// doPentagram()
//---------------------------------------------------------------------------
static void doPentagram(userEntity_t *self)
{
   nightmare_t *nightmare=(nightmare_t *)self->owner->curWeapon;
   Vector pos=Vector(self->owner->s.origin);

   if (nightmare->starttrack)
   {
      trackInfo_t tinfo;

      self->s.renderfx &= ~RF_NODRAW;

      // send 'track entity' msg to client
      tinfo.ent=self;
      tinfo.srcent=self->owner;
      tinfo.fru[0]=13;
      tinfo.fru[1]=0;
      tinfo.fru[2]=0;
      tinfo.dstent=0;
      tinfo.flags=RF_MINLIGHT|RF_WEAPONMODEL|TEF_SRCINDEX|TEF_FRU;
      trackEntity(&tinfo,MULTICAST_ALL);
      nightmare->starttrack=false;
   }

   self->s.alpha += nightmare->transadd;
   if (self->s.alpha > .8)
   {
      nightmare->transadd = -nightmare->transadd;
      self->s.alpha=.8;
   }
   else if (self->s.alpha < .3)
   {
      nightmare->transadd = -nightmare->transadd;
      self->s.alpha=.3;
   }

   pos.set(self->s.origin);

   self->s.frame++;
   if (self->s.frame > winfoLastFrame(ANIM_PENTAGRAM))
   {
      untrackEntity(self,NULL,MULTICAST_ALL);
      gstate->RemoveEntity(self);
      return;
   }

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

//---------------------------------------------------------------------------
// buildDeathList()
//---------------------------------------------------------------------------
static short buildDeathList(userEntity_t *self)
{
   userEntity_t *ent=gstate->FirstEntity();
   nightmare_t *nightmare=(nightmare_t *)self->curWeapon;

   nightmare->count=0;
   nightmare->deathnum=0;
   while (ent && nightmare->count<MAX_REAPER_DEATH)
   {
      if (((ent->flags & FL_CLIENT) || (ent->flags & FL_BOT) || (ent->flags & FL_MONSTER)) && 
          (ent->deadflag==DEAD_NO) && !(ent->flags & RF_TRACKENT))
      {
         if (ai_visible(self,ent))
            nightmare->deathList[nightmare->count++]=ent;
      }

      ent=gstate->NextEntity(ent);
   }

   // just for testing
   if (!nightmare->count)
   {
      nightmare->count=1;
      nightmare->deathList[0]=self;
   }

   return(nightmare->count);
}

//---------------------------------------------------------------------------
// timeToDie()
//---------------------------------------------------------------------------
static void timeToDie(userEntity_t *self)
{
   playerHook_t *phook=(playerHook_t *)self->userHook;
   userEntity_t *ent;
   Vector pos=Vector(self->s.origin);

   phook->fxFrameFunc=NULL;

   if (!buildDeathList(self))
      return;

   ent=gstate->SpawnEntity();
   ent->owner=self;
   ent->movetype=MOVETYPE_NONE;
   ent->solid=SOLID_BBOX;
   ent->className="reaper";
   ent->s.modelindex=gstate->ModelIndex(winfoModel(MDL_REAPER));
   ent->s.renderfx=RF_TRANSLUCENT;
   ent->s.alpha=.6;

   initReaperAttack(ent);
}

//---------------------------------------------------------------------------
// initReaperAttack()
//---------------------------------------------------------------------------
static void initReaperAttack(userEntity_t *self)
{
   nightmare_t *nightmare=(nightmare_t *)self->owner->curWeapon;
   userEntity_t *victim=nightmare->deathList[nightmare->deathnum];
   Vector pos,angles;

   self->s.frame=winfoFirstFrame(ANIM_REAPER);
   self->think=death;
   self->nextthink=gstate->time+.1;

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

   pos=Vector(victim->s.origin)+forward*120;
   pos.set(self->s.origin);

   angles.y = angles.y + 180;
   angles.set(self->s.angles);

   gstate->LinkEntity(self);
   pos=pos-up*300;             // lower him a little AFTER THE LINK!!

   freezeEntity(self->owner,victim,nightmare);
   winfoPlay(SND_APPEAR,255);
}

//---------------------------------------------------------------------------
// death()
//---------------------------------------------------------------------------
static void death(userEntity_t *self)
{
   nightmare_t *nightmare=(nightmare_t *)self->owner->curWeapon;
   userEntity_t *victim=nightmare->deathList[nightmare->deathnum];
   short lastframe=winfoLastFrame(ANIM_REAPER);
   Vector	dir;

   self->s.frame++;
   if (self->s.frame == lastframe-1)
   {
      winfoPlay(SND_ATTACK,255);
      // for now, just do massive death!!!
      unfreezeEntity(victim,nightmare);
	  //	Nelno:	calculate vector from reaper to victim for good gibbage spread
	  //	0, 0, -24 moves origin of reaper down a bit so gibs fly upwards
	  dir = Vector (victim->s.origin) - (Vector (self->s.origin) + Vector (0, 0, -24));
	  dir.Normalize ();
      com->Damage(victim,self,self,victim->s.origin, dir.vec (),200,DAMAGE_EXPLOSION);
   }
   else if (self->s.frame == lastframe)
   {
      // another victim?
      if (--nightmare->count)
      {
         nightmare->deathnum++;
         victim=nightmare->deathList[nightmare->deathnum];
         initReaperAttack(self);
      }
      else
      {
         gstate->RemoveEntity(self);
         return;
      }
   }

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

//---------------------------------------------------------------------------
// freezeEntity()
//---------------------------------------------------------------------------
static void freezeEntity(userEntity_t *owner,userEntity_t *ent,nightmare_t *nightmare)
{
   // save state
   nightmare->old_prethink=ent->prethink;
   nightmare->old_think=ent->think;
   nightmare->old_pain=ent->pain;
   nightmare->old_die=ent->die;
   nightmare->old_movetype=ent->movetype;
   VectorCopy(ent->velocity,nightmare->old_velocity);
   VectorCopy(ent->avelocity,nightmare->old_avelocity);

   // set new stuff
   ent->pain=NULL;
   ent->die=NULL;
   ent->prethink=NULL;
   ent->think=NULL;
   ent->velocity[0]=ent->velocity[1]=0;
   ent->avelocity[0]=ent->avelocity[1]=0;
   ent->movetype=MOVETYPE_TOSS;
   ent->owner=owner;
}

//---------------------------------------------------------------------------
// unfreezeEntity()
//---------------------------------------------------------------------------
static void unfreezeEntity(userEntity_t *ent,nightmare_t *nightmare)
{
   playerHook_t *phook=(playerHook_t *)ent->userHook;

   ent->prethink=nightmare->old_prethink;
   ent->think=nightmare->old_think;
   ent->pain=nightmare->old_pain;
   ent->die=nightmare->old_die;
   ent->movetype=nightmare->old_movetype;
   phook->tempgoal_search_time=-1;
   phook->goal_search_time=-1;
//   VectorCopy(nightmare->old_velocity,ent->velocity);
//   VectorCopy(nightmare->old_avelocity,ent->avelocity);
   ent->owner=NULL;
   ent=NULL;
}
