/*
================================================================
GOLIATH
================================================================

Copyright (C) 1998 by 2015, Inc.
All rights reserved.

This source is may not be distributed and/or modified without
expressly written permission by 2015, Inc.
*/

#include "goliath.h"
#include "object.h"
#include "player.h"

#define GOLIATH_MAX_OBJECTS 20

//===============================================================
// GOLIATH
//===============================================================

Event EV_Goliath_InitThrowTime( "throwtime" );
Event EV_Goliath_MeleeForce( "meleeforce" );
Event EV_Goliath_SetLeftHand( "setlefthand" );
Event EV_Goliath_SetRightHand( "setrighthand" );
Event EV_Goliath_SetBothHands( "setbothhands" );
Event EV_Goliath_SetAttackStage( "attackstage" );

Event EV_Goliath_IfThrowHigh( "ifthrowhigh" );
Event EV_Goliath_IfThrowLow( "ifthrowlow" );

CLASS_DECLARATION( Actor, Goliath, "monster_goliath" );

ResponseDef Goliath::Responses[] =
{
	{&EV_Goliath_InitThrowTime, (Response)&Goliath::InitThrowTime},
	{&EV_Goliath_MeleeForce, (Response)&Goliath::SetMeleeForce},
	{&EV_Goliath_SetLeftHand, (Response)&Goliath::SetLeftHand},
	{&EV_Goliath_SetRightHand, (Response)&Goliath::SetRightHand},
	{&EV_Goliath_SetBothHands, (Response)&Goliath::SetBothHands},
	{&EV_Goliath_SetAttackStage, (Response)&Goliath::SetAttackStage},

	{&EV_Goliath_IfThrowHigh, (Response)&Goliath::IfThrowHighEvent},
	{&EV_Goliath_IfThrowLow, (Response)&Goliath::IfThrowLowEvent},

	{NULL, NULL}
};

Goliath::Goliath()
{
	randomthrowtime = 0;
	rubbletime = 0;
	throwhigh = false;
	leftdamage = 0;
	leftforce = 0;
	rightdamage = 0;
	rightforce = 0;
	hitsentient = false;
	if(attackstage < 1 || attackstage > 2)
	{
		attackstage = 1;
	}

	if ( actorthread )
	{
		actorthread->Vars()->SetVariable( "attackstage", attackstage );
	}

	flags |= FL_POSTTHINK;
}

void Goliath::Prethink( void )
{
	trace_t trace;
	Vector tmpvec;
	qboolean nowthrowing = false;
	Entity *ent;

	if(attackstage == 2)
	{
		if(randomthrowtime && (randomthrowtime < level.time))
		{
			// we want to throw something...

			Entity *bestent;
			float bestdist;
			float dist;
			float distance;
			Vector delta;

			distance = 300;
			bestent = NULL;
			bestdist = distance * distance;

			ent = NULL;
			while((ent = findradius( ent, worldorigin.vec3(), distance ))!=0)
			{
				if(ent->isSubclassOf( ThrowObject ))
				{
					delta = centroid - ent->centroid;
					dist = delta * delta;
					if(dist <= bestdist)
					{
						bestent = ent;
						bestdist = dist;
					}
				}
			}

			if(bestent)
			{
				bestent = CheckObjectsAbove( bestent );
				SetVariable( "other", bestent );
				if(DoAction( "throwthing", false ))
				{
					randomthrowtime = 0;
					nowthrowing = true;

					// easy his rumbling tendancies a bit
					if(rubbletime)
						rubbletime += 4 + G_Random( 2 ) - skill->value;
				}
			}
		}

		// rumble timmer for when he goes to long wanting to
		// throw something without anything around
		if(randomthrowtime && !nowthrowing)
		{
			if(!rubbletime)
			{
				rubbletime = level.time + 5 + G_Random( 2 ) - (skill->value * 1.5);
			}
			else if(rubbletime < level.time)
			{
				ScriptVariable *var;
				int numobjects;

				// check max objects at one time limit
				var = levelVars.GetVariable( "goliathobject_count" );
				if(var)
					numobjects = var->intValue();
				else
					numobjects = 0;

				if(numobjects < GOLIATH_MAX_OBJECTS)
				{
					// time to make more stuff to throw >)
					if(DoAction( "rumbleattack", false ))
					{
						randomthrowtime = 0;
						nowthrowing = true;

						// ah, feel better now
						rubbletime = 0;
					}
				}
				else
				{
					// ease off his rubble making tendacies for a bit
					rubbletime = level.time + 1 + G_Random( 2 );
				}
			}
		}

	}
	// check for having a hissy hit and going into stage two
	else
	{
		if(health < (max_health*0.5))
		{
			if(DoAction( "pissedoff", false ))
			{
				// yup, he's pissed
				attackstage = 2;
				nowthrowing = true;

				// make sure rubble attack timmer is inited
				rubbletime = 0;
			}
		}

		// stage one throwing occures less often then stage two throwing
		if(!nowthrowing && randomthrowtime && ((randomthrowtime + 1) < level.time))
		{
			// we want to throw something...

			Entity *bestent;
			float bestdist;
			float dist;
			float distance;
			Vector delta;

			distance = 320;
			bestent = NULL;
			bestdist = distance * distance;

			ent = NULL;
			while((ent = findradius( ent, worldorigin.vec3(), distance ))!=0)
			{
				if(ent->isSubclassOf( ThrowObject ))
				{
					delta = centroid - ent->centroid;
					dist = delta * delta;
					if(dist <= bestdist)
					{
						bestent = ent;
						bestdist = dist;
					}
				}
			}

			if(bestent)
			{
				bestent = CheckObjectsAbove( bestent );
				if(bestent)
				{
					SetVariable( "other", bestent );
					if(DoAction( "throwthing", false ))
					{
						randomthrowtime = 0;
						nowthrowing = true;
					}
				}
			}
		}
	}

	if(!nowthrowing && randomthrowtime)
	{
		// if there's ever a throwable thing in our
		// way, then get it out of our way.
		tmpvec = worldorigin + move;
		trace = G_Trace( worldorigin, mins, maxs, tmpvec, this, edict->clipmask, "Goliath::Prethink" );
		if((trace.fraction != 1) && (trace.ent))
		{
			// check if we ran into something we can throw
			ent = trace.ent->entity;
			if(ent->isSubclassOf( ThrowObject ))
			{
				ent = CheckObjectsAbove( ent );
				if(ent)
				{
					SetVariable( "other", ent );
					// if in attackstage 2, there's a chance we'll just smash it
					if((attackstage == 2) && (G_Random() < 0.6))
					{
						DoAction( "destroyobstruction", false );
					}
					else
					{
						DoAction( "throwobstruction", false );
					}
				}
			}
		}
	}

	//
	// call our superclass
	//
	Actor::Prethink();
}

#define HAND_SIZE 40

// here's where the hand collision detection is done
void Goliath::Postthink( void )
{
	vec3_t trans[3];
	vec3_t orient;
	int groupindex;
	int tri_num;
	Vector pos, forward;
	Vector offset;
	Vector tmpvec;
	Vector handmins, handmaxs;
	Vector min, max;
	float handdiff;
	int num;
	edict_t *touch[MAX_EDICTS];

	handmins = Vector( -HAND_SIZE, -HAND_SIZE, -HAND_SIZE );
	handmaxs = Vector( HAND_SIZE, HAND_SIZE, HAND_SIZE );

	if(rightdamage || rightforce)
	{
		if(!gi.GetBoneInfo( edict->s.modelindex, "gun", &groupindex, &tri_num, orient ))
		{
			// couldn't get proper bone info
			gi.dprintf( "Couldn't find Goliath's gun bone\n" );
			return;
		}

		AngleVectors( orient, forward.vec3(), NULL, NULL );

		// get hand position
		offset = vec_zero;
		gi.GetBoneTransform( edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim,
		                     edict->s.frame, edict->s.scale, trans, offset.vec3());
		MatrixTransformVector( offset.vec3(), orientation, pos.vec3());
		pos += worldorigin;

		// check at an interpolated position if hand moved enough from last frame
		if(lastrightpos != vec_zero)
		{
			tmpvec = lastrightpos - pos;
			handdiff = tmpvec.length();
			if(handdiff > (HAND_SIZE*3))
			{
				tmpvec = (lastrightpos + pos)*0.5;

				min = tmpvec + handmins;
				max = tmpvec + handmaxs;

				num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );
				if(num)
					DoHandHits( tmpvec, forward, rightdamage, rightforce, touch, num );

				num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
				if(num)
					DoHandHits( tmpvec, forward, rightdamage, rightforce, touch, num );
			}
		}

		// check current hand position
		min = pos + handmins;
		max = pos + handmaxs;

		num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );
		if(num)
			DoHandHits( pos, forward, rightdamage, rightforce, touch, num );

		num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
		if(num)
			DoHandHits( pos, forward, rightdamage, rightforce, touch, num );

		// save current hand position
		lastrightpos = pos;
	}
	else
	{
		// clear out last hand position
		lastrightpos = vec_zero;
	}


	if(leftdamage || leftforce)
	{
		if(!gi.GetBoneInfo( edict->s.modelindex, "leftgun", &groupindex, &tri_num, orient ))
		{
			// couldn't get proper bone info
			gi.dprintf( "Couldn't find Goliath's leftgun bone\n" );
			return;
		}

		AngleVectors( orient, forward.vec3(), NULL, NULL );

		// get hand position
		offset = vec_zero;
		gi.GetBoneTransform( edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim,
		                     edict->s.frame, edict->s.scale, trans, offset.vec3());
		MatrixTransformVector( offset.vec3(), orientation, pos.vec3());
		pos += worldorigin;

		// check at an interpolated position if hand moved enough from last frame
		if(lastleftpos != vec_zero)
		{
			tmpvec = lastleftpos - pos;
			handdiff = tmpvec.length();
			if(handdiff > (HAND_SIZE*3))
			{
				tmpvec = (lastleftpos + pos)*0.5;

				min = tmpvec + handmins;
				max = tmpvec + handmaxs;

				num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );
				if(num)
					DoHandHits( tmpvec, forward, leftdamage, leftforce, touch, num );

				num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
				if(num)
					DoHandHits( tmpvec, forward, leftdamage, leftforce, touch, num );
			}
		}

		// check current hand position
		min = pos + handmins;
		max = pos + handmaxs;

		num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );
		if(num)
			DoHandHits( pos, forward, leftdamage, leftforce, touch, num );

		num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
		if(num)
			DoHandHits( pos, forward, leftdamage, leftforce, touch, num );

		// save current hand position
		lastleftpos = pos;
	}
	else
	{
		// clear out last hand position
		lastleftpos = vec_zero;
	}
}

void Goliath::InitThrowTime( Event *ev )
{
	float mintime, maxtime, timediff;

	// if fewer than 2 numbers are given, clear timmer
	if(ev->NumArgs() < 2)
	{
		randomthrowtime = 0;
		return;
	}

	mintime = ev->GetFloat( 1 );
	maxtime = ev->GetFloat( 2 );
	timediff = maxtime - mintime;

	randomthrowtime = level.time + mintime + G_Random( timediff );
	randomthrowtime = level.time + G_Random( timediff );
}

Entity *Goliath::CheckObjectsAbove( Entity *ent )
{
	Vector tmpvec, tmpvec2;
	trace_t trace;
	int mask, i;
	Entity *newent;
	float tmpflt;

	// make sure we're passed a good thing

	if(!ent)
		return NULL;

	if((ent->health <= 0) || (!ent->isSubclassOf( ThrowObject )))
		return NULL;

	// check for a better thing on the thing

	mask = MASK_PROJECTILE;

	// first try a full size trace
	tmpvec = ent->worldorigin + Vector( 0, 0, 16 );
	trace = G_Trace( ent->worldorigin, ent->mins, ent->maxs, tmpvec, ent, mask, "Goliath::CheckObjectsAbove" );

	// check for something good
	if(trace.ent)
	{
		newent = trace.ent->entity;

		// check for a stackable object that's above us
		if((newent->spawnflags & 8) && (newent->worldorigin.z > ent->worldorigin.z))
		{
			throwhigh = true;
			return newent;
		}
	}

	// try several different spots
	for(i = 0; i < 13; i++)
	{
		tmpvec = ent->worldorigin;
		switch(i)
		{
		// corners
		case 1:
			tmpvec.x += ent->mins.x + 8;
			tmpvec.y += ent->mins.y + 8;
			break;
		case 2:
			tmpvec.x += ent->mins.x + 8;
			tmpvec.y += ent->maxs.y - 8;
			break;
		case 3:
			tmpvec.x += ent->maxs.x - 8;
			tmpvec.y += ent->mins.y + 8;
			break;
		case 4:
			tmpvec.x += ent->maxs.x - 8;
			tmpvec.y += ent->maxs.y - 8;
			break;
		// edge middles
		case 5:
			tmpvec.x += ent->mins.x + 8;
			break;
		case 6:
			tmpvec.x += ent->maxs.x - 8;
			break;
		case 7:
			tmpvec.y += ent->mins.y + 8;
			break;
		case 8:
			tmpvec.y += ent->maxs.y - 8;
			break;
		}

		tmpvec2 = tmpvec + Vector( 0, 0, 16 );
		trace = G_Trace( tmpvec, Vector( -4, -4, 0 ), Vector( 4, 4, ent->maxs.z - 1 ), tmpvec2, ent, mask, "Goliath::CheckObjectsAbove" );

		// check for something good
		if(trace.ent)
		{
			newent = trace.ent->entity;

			// check for a stackable object that's above us
			if(newent->worldorigin.z > ent->worldorigin.z)
			{
				throwhigh = true;
				return newent;
			}
		}
	}

	// couldn't find anything, so stick with that we've got
	// check our object's pichup height
	tmpflt = ent->worldorigin.z - worldorigin.z;
	if(tmpflt > 32)
		throwhigh = true;
	else
		throwhigh = false;
	return ent;
}

//===============================================================
// melee attack stuff

void Goliath::SetMeleeForce( Event *ev )
{
	melee_force = ev->GetFloat( 1 );
	melee_force *= edict->s.scale;
}

void Goliath::SetLeftHand( Event *ev )
{
	leftdamage = ev->GetFloat( 1 );
	leftforce = ev->GetFloat( 2 );
}

void Goliath::SetRightHand( Event *ev )
{
	rightdamage = ev->GetFloat( 1 );
	rightforce = ev->GetFloat( 2 );
}

void Goliath::SetBothHands( Event *ev )
{
	rightdamage = ev->GetFloat( 1 );
	rightforce = ev->GetFloat( 2 );

	leftdamage = rightdamage;
	leftforce = rightforce;
}

void Goliath::DoHandHits( Vector pos, Vector dir, float handdamage, float handforce, edict_t *touch[MAX_EDICTS], int num )
{
	edict_t *hit;
	Entity *ent;
	int i;
	float damage;

	// horizontalize the direction
	dir.z = 0;
	dir.normalize();

	for(i = 0; i < num; i++)
	{
		hit = touch[i];
		if(!hit->inuse)
			continue;

		assert( hit->entity );

		ent = hit->entity;

		if(ent == this)
			continue;

		if(ent->getSolidType() == SOLID_BSP)
			continue;

		// so goliaths don't hurt each other
		if(ent->isSubclassOf( Goliath ))
			continue;

		if(ent->isSubclassOf( Sentient ) && hitsentient)
			continue;

		if(ent->takedamage)
		{
			if(ent->isSubclassOf( Sentient ))
			{
				damage = melee_damage*handdamage;
				hitsentient = true;
			}
			else
			{
				damage = ent->max_health * 10;

				// make a hit sound
				ent->RandomGlobalSound( "impact_smallexplosion", 0.5, CHAN_BODY );
			}

			ent->Damage( this, this, (int)damage, pos,
			             vec_zero, vec_zero, 0, DAMAGE_NO_KNOCKBACK, MOD_MUTANTHANDS, -1, -1, 1.0f );

			ent->velocity -= dir * (melee_force * handforce);
			if(ent->velocity.z < 50)
				ent->velocity.z = 50;
			ent->groundentity = NULL;
		}
	}
}

//===============================================================

void Goliath::SetAttackStage( Event *ev )
{
	attackstage = ev->GetInteger( 1 );

	if(attackstage < 1)
		attackstage = 1;
	else if(attackstage > 2)
		attackstage = 2;

	if ( actorthread )
	{
		actorthread->Vars()->SetVariable( "attackstage", attackstage );
	}
}

void Goliath::IfThrowHighEvent( Event *ev )
{
	ScriptThread *thread;

	thread = ev->GetThread();
	assert( thread );
	if(!thread)
	{
		return;
	}

	if(throwhigh)
	{
		thread->ProcessCommandFromEvent( ev, 1 );
	}
}

void Goliath::IfThrowLowEvent( Event *ev )
{
	ScriptThread *thread;

	thread = ev->GetThread();
	assert( thread );
	if(!thread)
	{
		return;
	}

	if(!throwhigh)
	{
		thread->ProcessCommandFromEvent( ev, 1 );
	}
}

//===============================================================

/****************************************************************************

   RumbleAttack Class Definition

   Goliath does an attack that causes the player to get jittered around
   and also causes rubble to fall from the ceiling

****************************************************************************/

CLASS_DECLARATION( Behavior, RumbleAttack, NULL );

Event EV_RumbleAttack_DoRumble( "dorumble" );

ResponseDef RumbleAttack::Responses[] =
{
	{&EV_Behavior_Args, (Response)&RumbleAttack::SetArgs},
	{&EV_Behavior_AnimDone, (Response)&RumbleAttack::AnimDone},
	{&EV_RumbleAttack_DoRumble,(Response)&RumbleAttack::DoRumble},
	{NULL, NULL}
};

void RumbleAttack::ShowInfo( Actor &self )
{
	Behavior::ShowInfo( self );

	gi.printf( "\nanim: %s\n", anim.c_str());
	gi.printf( "animdone: %d\n", animdone );
	gi.printf( "\nrubbletarget: %s\n", rubbletarget.c_str());
	gi.printf( "rubblenums: %d\n", rubblenums );
}

void RumbleAttack::Begin( Actor &self )
{
	if(!anim.length())
	{
		anim = "rumble";
	}
	animdone = false;

	// start off the rumbling animation
	if(self.HasAnim( anim.c_str()))
	{
		self.SetAnim( anim.c_str(), EV_Actor_NotifyBehavior );
	}
	else
	{
		// no animation, so don't do it
		self.SetAnim( "idle" );
		animdone = true;
	}
}

void RumbleAttack::SetArgs( Event *ev )
{
	anim = ev->GetString( 2 );
	rubbletarget = ev->GetString( 3 );
}

void RumbleAttack::AnimDone( Event *ev )
{
	animdone = true;
}

qboolean RumbleAttack::Evaluate( Actor &self )
{
	// when the animation is finished, the behaivior is finished
	if(animdone)
		return false;

	return true;
}

void RumbleAttack::End( Actor &self )
{
	self.SetAnim( "idle" );
}

void RumbleAttack::DoRumble( Event *ev )
{
	str targ;
	int num, i;
	Entity *ent;
	Player *player;
	Event *event;

	targ = rubbletarget;

	if(targ.length())
	{
		num = 0;
		do
		{
			num = G_FindTarget( num, targ.c_str());
			if(!num)
			{
				break;
			}

			ent = G_GetEntity( num );

			event = new Event( EV_Activate );
			event->AddEntity( ev->GetEntity( 1 ));
			ent->PostEvent( event, 0 );
		}
		while(1);
	}

	// jitter the player around
	for(i = 1; i <= maxclients->value; i++)
	{
		if(!g_edicts[i].inuse || !g_edicts[i].entity)
		{
			continue;
		}

		player = (Player *)(g_edicts[i].entity);

		player->SetAngleJitter( 12, 5, 1 );
		player->SetOffsetJitter( 10, 2.5, 1 );
	}
}

/****************************************************************************

   GoliathMelee Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, GoliathMelee, NULL );

ResponseDef GoliathMelee::Responses[] =
{
	{&EV_Behavior_AnimDone, (Response)&GoliathMelee::AnimDone},
	{NULL, NULL}
};

GoliathMelee::GoliathMelee()
{}

void GoliathMelee::ShowInfo( Actor &self )
{
	Behavior::ShowInfo( self );

	gi.printf( "\naim:\n" );
	turnto.ShowInfo( self );

	gi.printf( "animdone: %d\n", animdone );
}

void GoliathMelee::Begin( Actor &self )
{
	mode = 0;
	animdone = false;

	// clear Goliaths hitsentient value
	((Goliath *)&self)->hitsentient = false;
}

void GoliathMelee::SetArgs( Event *ev )
{}

void GoliathMelee::AnimDone( Event *ev )
{
	animdone = true;
}

qboolean GoliathMelee::Evaluate( Actor &self )
{
	if(animdone)
	{
		return false;
	}

	if(!self.has_melee || !self.currentEnemy)
	{
		return false;
	}

	if(mode == 0)
	{
		float r;
		Vector delta;

		delta = self.currentEnemy->centroid - self.centroid;
		r = delta.length();
		if(r > self.melee_range)
		{
			return false;
		}

		r = delta.toYaw();
		turnto.SetDirection( r );
		turnto.SetTolerance( 10 );

		if(turnto.Evaluate( self ))
		{
			return true;
		}

		// melee
		self.SetAnim( "melee", EV_Actor_NotifyBehavior );
		self.Chatter( "snd_attacktaunt", 5 );

		mode = 1;
	}

	return true;
}

void GoliathMelee::End( Actor &self )
{
	turnto.End( self );
	self.SetAnim( "idle" );
}

/****************************************************************************

   Goliath Pickup Behavior Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, GoliathPickupAndThrow, NULL );

extern Event EV_PickupAndThrow_Pickup;
extern Event EV_PickupAndThrow_Throw;

ResponseDef GoliathPickupAndThrow::Responses[] =
{
	{&EV_Behavior_Args, (Response)&GoliathPickupAndThrow::SetArgs},
	{&EV_Behavior_AnimDone, (Response)&GoliathPickupAndThrow::AnimDone},
	{&EV_PickupAndThrow_Pickup, (Response)&GoliathPickupAndThrow::Pickup},
	{&EV_PickupAndThrow_Throw, (Response)&GoliathPickupAndThrow::Throw},
	{NULL, NULL}
};

GoliathPickupAndThrow::GoliathPickupAndThrow()
{}

void GoliathPickupAndThrow::ShowInfo( Actor &self )
{
	Behavior::ShowInfo( self );

	gi.printf( "\naim:\n" );
	aim.ShowInfo( self );

	gi.printf( "\nmode: %d\n", mode );
	gi.printf( "\nanim: %s\n", anim.c_str() );
	gi.printf( "animdone: %d\n", animdone );

	if ( pickup_target )
	{
		gi.printf( "\npickup_target: #%d '%s'\n", pickup_target->entnum, pickup_target->targetname.c_str() );
	}
	else
	{
		gi.printf( "\npickup_target: NULL\n" );
	}
}

void GoliathPickupAndThrow::Begin( Actor &self )
{
	mode = 0;
	animdone = false;

	if(!anim.length())
	{
		anim = "pickup";
	}

	// turn towards the object
	if(pickup_target)
	{
		turnto.SetTarget( pickup_target );
		turnto.SetTolerance( 20 );
		turnto.Begin( self );
	}
}

void GoliathPickupAndThrow::SetArgs( Event *ev )
{
	pickup_target = ev->GetEntity( 2 );

	if(ev->NumArgs() > 2)
	{
		anim = ev->GetString( 3 );
	}
}

void GoliathPickupAndThrow::AnimDone( Event *ev )
{
	animdone = true;
}

void GoliathPickupAndThrow::Pickup( Event *ev )
{
	Entity * ent;
	Event * e;

	ent = ev->GetEntity( 1 );
	if ( pickup_target )
	{
		e = new Event( EV_ThrowObject_Pickup );
		e->AddEntity( ent );
		e->AddString( ev->GetString( 2 ) );
		pickup_target->ProcessEvent( e );
		turnto.End( *((Actor *)ent));
	}
}

void GoliathPickupAndThrow::Throw( Event *ev )
{
	Actor * act;
	Event * e;

	act = (Actor *)ev->GetEntity( 1 );
	if ( pickup_target )
	{
		if ( !act->currentEnemy )
			return;
		e = new Event( EV_ThrowObject_Throw );
		e->AddEntity( act );
		e->AddFloat( 800 );
		e->AddEntity( act->currentEnemy );
		e->AddFloat( 1 );
		pickup_target->ProcessEvent( e );
	}
}

qboolean GoliathPickupAndThrow::Evaluate( Actor &self )
{
	Event * ev;

	if ( !self.currentEnemy || !pickup_target )
		return false;

	switch( mode )
	{
	case 0:
		if ( self.HasAnim( anim.c_str() ) )
		{
			animdone = false;
			self.SetAnim( anim.c_str(), EV_Actor_NotifyBehavior );
			mode = 1;
		}
		else
		{
			// skip the pickup animation
			ev = new Event( EV_PickupAndThrow_Pickup );
			ev->AddEntity( &self );
			ev->AddString( "gun" );
			ProcessEvent( ev );
			animdone = true;
		}

	case 1:
		if ( !animdone )
			break;
		aim.Begin( self );
		mode = 2;
		if ( self.HasAnim( "throw_aim" ) )
		{
			self.SetAnim( "throw_aim", EV_Actor_NotifyBehavior );
		}
		else
		{
			self.SetAnim( "idle" );
		}

	case 2:
		// aim towards our target
		aim.SetTarget( self.currentEnemy );
		if ( aim.Evaluate( self ) )
		{
			break;
		}
		mode = 3;

	case 3:
		// Throwing
		mode = 4;
		if ( self.HasAnim( "throw" ) )
		{
			animdone = false;
			self.SetAnim( "throw", EV_Actor_NotifyBehavior );
			mode = 4;
		}
		else
		{
			// skip the pickup animation
			ev = new Event( EV_PickupAndThrow_Throw );
			ev->AddEntity( &self );
			ProcessEvent( ev );
			animdone = true;
		}
		break;

	case 4:
		if ( !animdone )
			break;
		return false;
		break;
	}

	return true;
}

void GoliathPickupAndThrow::End( Actor &self )
{
	aim.End( self );
	turnto.End( self );
	self.SetAnim( "idle" );
}
