//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/animate.cpp                               $
// $Revision:: 23                                                             $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:35p                                                  $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
// DESCRIPTION: Animate Class
//

#include "_pch_cpp.h"
#include "animate.h"
#include "player.h"
#include <qcommon/qfiles.h>

// Leg Animation events
Event EV_Anim
(
	"anim",
	EV_DEFAULT,
	"s",
	"animName",
	"set the legs animation to animName."
);
Event EV_SetFrame
(
	"setframe",
	EV_CODEONLY,
	"iS",
	"frameNumber animName",
	"Set the frame on the legs, if anim is not specified, current is assumed."
);
Event EV_AnimDone
(
	"animdone",
	EV_CODEONLY,
	NULL,
	NULL,
	"Legs animation has finished, not for script use."
);
Event EV_FrameDelta
(
	"setmovedelta",
	EV_CODEONLY,
	"v",
	"moveDelta",
	"movement from animation, not for script use."
);
Event EV_StopAnimating
(
	"stopanimating",
	EV_CODEONLY,
	NULL,
	NULL,
	"stop the legs from animating.  Animation will end at the end of current cycle."
);

// Torso Animation events
Event EV_Torso_Anim
(
	"torso_anim",
	EV_CODEONLY,
	"s",
	"animName",
	"set the torso animation to animName."
);
Event EV_Torso_SetFrame
(
	"torso_setframe",
	EV_CODEONLY,
	"iS",
	"frameNumber animName",
	"Set the frame on the torso, if anim is not specified, current is assumed."
);
Event EV_Torso_AnimDone
(
	"torso_animdone",
	EV_CODEONLY,
	NULL,
	NULL,
	"Torso animation has finished, not for script use."
);
Event EV_Torso_StopAnimating
(
	"torso_stopanimating",
	EV_CODEONLY,
	NULL,
	NULL,
	"stop the torso from animating.  Animation will end at the end of current cycle."
);
Event EV_NewAnim
(
	"animate_newanim",
	EV_CODEONLY,
	"ii",
	"animNum bodyPart",
	"Start a new animation, not for script use."
);

CLASS_DECLARATION( Listener, Animate, "animate" )
{
	{ &EV_Anim,						&Animate::Legs_AnimEvent },
	{ &EV_SetFrame,					&Animate::Legs_SetFrameEvent },
	{ &EV_AnimDone,					&Animate::Legs_AnimDoneEvent },
	{ &EV_StopAnimating,			&Animate::Legs_StopAnimating },
	{ &EV_FrameDelta,				&Animate::FrameDeltaEvent },
	{ &EV_NewAnim,					&Animate::NewAnimEvent },	
	{ &EV_Torso_Anim,				&Animate::Torso_AnimEvent },
	{ &EV_Torso_SetFrame,			&Animate::Torso_SetFrameEvent },
	{ &EV_Torso_AnimDone,			&Animate::Torso_AnimDoneEvent },
	{ &EV_Torso_StopAnimating,		&Animate::Torso_StopAnimating },
	
	{ NULL, NULL }
};

Animate::Animate()
{
	// Should always use other constructor
	
	assert( 0 );
}

Animate::Animate( Entity *ent )
{
	// Animation variables
	frame_delta    = Vector(0, 0, 0);
	animDoneEvent	= NULL;
	torso_animDoneEvent	= NULL;
	
	legs_animtime = 0;
	torso_animtime = 0;
	
	legs_starttime = 0;
	torso_starttime = 0;
	
	legs_numframes = 0;
	torso_numframes = 0;
	
	legs_frametime = 0;
	torso_frametime = 0;
	
	self = ent;
	
	if ( self )
	{
		self->edict->s.animationRate = 1.0f;
		self->edict->s.anim        |= (ANIM_SERVER_EXITCOMMANDS_PROCESSED);
		self->edict->s.torso_anim  |= (ANIM_SERVER_EXITCOMMANDS_PROCESSED);
		self->edict->s.effectsAnims[ 0 ] = -1 ;
		self->edict->s.effectsAnims[ 1 ] = -1 ;
		self->edict->s.effectsAnims[ 2 ] = -1 ;
		self->edict->s.effectsAnims[ 3 ] = -1 ;
	}
}

Animate::~Animate()
{
	if ( animDoneEvent )
	{
		delete animDoneEvent;
		animDoneEvent = NULL;
	}

	if ( torso_animDoneEvent )
	{
		delete torso_animDoneEvent;
		torso_animDoneEvent = NULL;
	}
}

void Animate::NewAnim( int animnum, bodypart_t part )
{
	//
	// this is an animating model, we set this here so that non-animating models
	// don't have to go through the animating code path
	//
	if ( self->edict->s.eType == ET_GENERAL )
	{
		self->edict->s.eType = ET_MODELANIM;
	}
	
	if ( LoadingSavegame )
	{
		// if we are loading a game, we don't need to start animating, that will be automatic
		// all these events would mess everything up anyway
		return;
	}
	
	
	int   *anim;
	int   flags;
	float *animtime;
	float *starttime;
	float *frametime;
	int   *numframes;
	switch( part )
	{
	case legs:
		anim = &self->edict->s.anim;
		self->edict->s.frame &= ~FRAME_EXPLICIT;
		flags = EVENT_LEGS_ANIM;
		animtime = &legs_animtime;
		starttime = &legs_starttime;
		frametime = &legs_frametime;
		numframes = &legs_numframes;
		break;
	case torso:
		anim = &self->edict->s.torso_anim;
		self->edict->s.torso_frame &= ~FRAME_EXPLICIT;
		flags = EVENT_TORSO_ANIM;
		animtime = &torso_animtime;
		starttime = &torso_starttime;
		frametime = &torso_frametime;
		numframes = &torso_numframes;
		break;
	default:
		warning( "NewAnim", "Unknown body part %d", part );
		return;
		break;
	}
	
	int last_anim = *anim & ANIM_MASK;
	int last_anim_flags = *anim;
	
	float lastStartTime = *starttime;
	
	//
	// if the animations were different we need to process the entry and exit events
	//
	if ( ( last_anim != animnum ) && !( last_anim_flags & ANIM_SERVER_EXITCOMMANDS_PROCESSED ) )
	{
		// exit the previous animation
		tiki_cmd_t cmds;
		if ( gi.Frame_Commands( self->edict->s.modelindex, last_anim, TIKI_FRAME_CMD_EXIT, &cmds ) )
		{
			for( int ii = 0; ii < cmds.num_cmds; ii++ )
            {
				Event *ev = new Event( cmds.cmds[ ii ].args[ 0 ] );
				
				ev->SetSource( EV_FROM_ANIMATION );
				ev->SetAnimationNumber( last_anim );
				ev->SetAnimationFrame( 0 );
				
				for( int j = 1; j < cmds.cmds[ ii ].num_args; j++ )
				{
					ev->AddToken( cmds.cmds[ ii ].args[ j ] );
				}
				self->ProcessEvent( ev );
            }
		}
	}
	
	if ( ( animnum >= 0 ) && ( animnum < gi.NumAnims( self->edict->s.modelindex ) ) )
	{
		if ( *starttime == level.time )
		{
			// don't toggle the togglebit if we've already had an animation set this frame
			*anim = ( *anim & ANIM_TOGGLEBIT ) | animnum | ANIM_BLEND;
		}
		else
		{
			*anim = ( ( *anim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | animnum | ANIM_BLEND;
		}
	}
	else
	{
		// bad value
		return;
	}
	
	// get rid of old anim events
	CancelFlaggedEvents( flags );
	self->CancelFlaggedEvents( flags );
	
	// get total time of animation
	float totaltime = gi.Anim_Time( self->edict->s.modelindex, animnum );
	totaltime /= self->edict->s.animationRate;
	
	// set the total time for the animation
	*animtime = totaltime;
	
	// set the start time of the animation
	*starttime = level.time;
	
	// set the number of frames for the animation
	*numframes = gi.Anim_NumFrames( self->edict->s.modelindex, animnum );
	
	// set the time for each animation frame
	*frametime = gi.Frame_Time( self->edict->s.modelindex, animnum, 0 );
	*frametime /= self->edict->s.animationRate;
	
	bool has_commands = (gi.Anim_HasCommands( self->edict->s.modelindex, animnum ) != 0);
	
	if ( self->edict->s.eFlags & EF_DONT_PROCESS_COMMANDS )
		has_commands = false;
	
	if ( ( ( last_anim != animnum ) || ( lastStartTime == 0 ) ) && ( has_commands ) )
	{
		ClearAllEffectAnims();
		// enter this animation
		tiki_cmd_t cmds;
		if ( gi.Frame_Commands( self->edict->s.modelindex, animnum, TIKI_FRAME_CMD_ENTRY, &cmds ) )
		{
			for( int ii = 0; ii < cmds.num_cmds; ii++ )
            {
				Event *ev = new Event( cmds.cmds[ ii ].args[ 0 ] );
				ev->SetSource( EV_FROM_ANIMATION );
				ev->SetAnimationNumber( animnum );
				ev->SetAnimationFrame( 0 );
				
				for( int j = 1; j < cmds.cmds[ ii ].num_args; j++ )
				{
					ev->AddToken( cmds.cmds[ ii ].args[ j ] );
				}
				self->ProcessEvent( ev );
            }
		}
	}
	
	
	// initially don't post move delta stuff
	bool dodelta = false;
	
	// if it is the legs, find out if there is a delta and if it is a delta_driven animation
	if ( part == legs )
	{
		Vector totaldelta;
		gi.Anim_AbsoluteDelta( self->edict->s.modelindex, animnum, totaldelta );
		
		float length = totaldelta.length();
		if ( length > MINIMUM_DELTA_MOVEMENT )
		{
			int flags = gi.Anim_Flags( self->edict->s.modelindex, animnum );
			if ( !( flags & MDL_ANIM_DELTA_DRIVEN ) )
            {
				length /= (float)*numframes;
				if ( length > MINIMUM_DELTA_MOVEMENT_PER_FRAME )
				{
					dodelta = true;
				}
            }
		}
	}
	
	if ( has_commands || dodelta )
	{
		float time = 0;
		
		for( int i = 0; i < *numframes; i++ )
		{
			if ( has_commands )
            {
				// we want normal frame commands to occur right on the frame
				tiki_cmd_t cmds;
				if ( gi.Frame_Commands( self->edict->s.modelindex, animnum, i, &cmds ) )
				{
					for( int ii = 0; ii < cmds.num_cmds; ii++ )
					{
						Event *ev = new Event( cmds.cmds[ ii ].args[ 0 ] );
						
						ev->SetSource( EV_FROM_ANIMATION );
						ev->SetAnimationNumber( animnum );
						ev->SetAnimationFrame( i );
						
						for( int j = 1; j < cmds.cmds[ ii ].num_args; j++ )
						{
							ev->AddToken( cmds.cmds[ ii ].args[ j ] );
						}
						self->PostEvent( ev, time, flags );
					}
				}
            }
			
			// add to time
			time += *frametime;
			
			// we want deltas to occur at the end of the frame
			// only add deltas on the legs
			if ( dodelta )
            {
				Vector delta;
				
				// get the current frame delta
				gi.Frame_Delta( self->edict->s.modelindex, animnum, i, delta );
				
				if ( *frametime > FRAMETIME )
				{
					float time_offset;
					
					VectorScale( delta, ( FRAMETIME / *frametime ), delta );
					
					for ( time_offset = 0; time_offset < *frametime; time_offset += FRAMETIME )
					{
						Event *ev = new Event( EV_FrameDelta );
						ev->AddVector( delta );
						PostEvent( ev, time + time_offset, flags );
					}
				}
				else
				{
					Event *ev = new Event( EV_FrameDelta );
					ev->AddVector( delta );
					ev->AddFloat( *frametime );
					PostEvent( ev, time, flags );
				}
            }
			
		}
	}
	
	//
	// if we have a 1 frame animation, which has no commands,
	// we aren't a subclass of Sentient and our animation time is the same as frametime
	// there is no reason for us to constantly animate, since nothing will change
	// lets get the hell out of dodge!
	if (
		( *numframes == 1 ) &&
		!self->isSubclassOf( Player ) &&
		!has_commands &&
		( *frametime <= FRAMETIME )
		)
	{
		switch( part )
		{
		case legs:
			if ( animDoneEvent )
			{
				self->PostEvent( *animDoneEvent, 0.0f );
			}
            break;
		case torso:
			if ( torso_animDoneEvent )
			{
				self->PostEvent( *torso_animDoneEvent, 0.0f );
			}
            break;
		default:
            break;
		}
		return;
	}
	
	// Note: Below we post the ANIMDONE on the last frame of the animation if we're switching 
	// animations.  If we're playing a looped anim, the ANIMDONE event is posted one frame early.
	// No idea why it makes a difference, but it removes hitching on the last frame of a looped anim.
	switch( part )
	{
	case legs:
		if ( totaltime > *frametime && last_anim == animnum )
            PostEvent( EV_AnimDone, totaltime - *frametime, flags );
		else
            PostEvent( EV_AnimDone, totaltime, flags );
		break;
	case torso:
		if ( totaltime > *frametime && last_anim == animnum )
            PostEvent( EV_Torso_AnimDone, totaltime - *frametime, flags );
		else
            PostEvent( EV_Torso_AnimDone, totaltime, flags );
		break;
	default:
		warning( "NewAnim", "Unknown body part %d", part );
		return;
		break;
	}
}
   
void Animate::NewAnim( int animnum, Event &newevent, bodypart_t part )
{
	SetAnimDoneEvent( newevent, part );
	NewAnim( animnum, part );
}

void Animate::NewAnim( int animnum, Event *newevent, bodypart_t part )
{
	SetAnimDoneEvent( newevent, part );
	NewAnim( animnum, part );
}

void Animate::FrameDeltaEvent( Event *ev )
{
	frame_delta = ev->GetVector( 1 );
	self->total_delta += frame_delta * self->edict->s.scale ;
}

void Animate::EndAnim( bodypart_t part )
{
	Event * ev;
	
	switch( part )
	{
	case legs:
		if ( animDoneEvent )
		      {
			self->PostEvent( *animDoneEvent, 0.0f );
		      }
		ev = new Event( EV_NewAnim );
		ev->AddInteger( self->edict->s.anim );
		ev->AddInteger( part );
		
		if ( legs_animtime > legs_frametime )
            PostEvent( ev, legs_frametime, EVENT_LEGS_ANIM );
		else
            PostEvent( ev, 0.0f, EVENT_LEGS_ANIM );
		break;
	case torso:
		if ( torso_animDoneEvent )
		      {
			self->PostEvent( *torso_animDoneEvent, 0.0f );
		      }
		ev = new Event( EV_NewAnim );
		ev->AddInteger( self->edict->s.torso_anim );
		ev->AddInteger( part );
		
		if ( torso_animtime > torso_frametime )
            PostEvent( ev, torso_frametime, EVENT_TORSO_ANIM );
		else
            PostEvent( ev, 0.0f, EVENT_TORSO_ANIM );
		break;
	default:
		warning( "EndAnim", "Unknown body part %d", part );
		return;
		break;
	}
}

void Animate::SetAnimDoneEvent( Event *event, bodypart_t part )
{
	Event **doneevent;
	
	switch( part )
	{
	case legs:
		doneevent = &animDoneEvent;
		break;
	case torso:
		doneevent = &torso_animDoneEvent;
		break;
	default:
		warning( "SetAnimDoneEvent", "Unknown body part %d", part );
		return;
		break;
	}
	if ( *doneevent )
	{
		delete *doneevent;
	}
	
	*doneevent = event;
}

void Animate::SetAnimDoneEvent( const Event &event, bodypart_t part )
{
	SetAnimDoneEvent( new Event( event ), part );
}

str Animate::GetName()
{
	return currentAnim;
}

//===============================================================
// Name:		AddEffectAnim
// Class:		Animate
//
// Description: Add an effects anim to be layered about a normal
//				anim.
// 
// Parameters:	const char* -- the anim to whose effects you want to add.
//
// Returns:		None
// 
//===============================================================
void Animate::AddEffectAnim( const char *animname )
{
	int num = gi.Anim_Random( self->edict->s.modelindex, animname );
	if ( num != -1 )
	{
		for ( int effectAnimIdx = 0; effectAnimIdx < NUM_EFFECTS_ANIMS; ++effectAnimIdx )
		{
			int effectAnimNum = self->edict->s.effectsAnims[ effectAnimIdx ] ;
			if ( effectAnimNum == num ) return ;
			if ( effectAnimNum == -1 )
			{
				self->edict->s.effectsAnims[ effectAnimIdx ] = num ;
				return ;
			}
		}
	}
}


//===============================================================
// Name:		RemoveEffectAnim
// Class:		Animate
//
// Description: Removes an anim effect
// 
// Parameters:	const char *animname
//
// Returns:		None
// 
//===============================================================
void Animate::RemoveEffectAnim( const char *animname )
{
	int num = gi.Anim_Random( self->edict->s.modelindex, animname );
	if ( num != -1 )
	{
		for ( int effectAnimIdx = 0; effectAnimIdx < NUM_EFFECTS_ANIMS; ++effectAnimIdx )
		{
			if ( self->edict->s.effectsAnims[ effectAnimIdx ] == num )
			{
				self->edict->s.effectsAnims[ effectAnimIdx ] = -1 ;
			}
		}
	}
}

//===============================================================
// Name:		ClearAllEffectAnims
// Class:		Animate
//
// Description: Clears the list of effect anims.
// 
// Parameters:	None
//
// Returns:		None
// 
//===============================================================
void Animate::ClearAllEffectAnims( void )
{
	for ( int effectAnimIdx = 0; effectAnimIdx < NUM_EFFECTS_ANIMS; ++effectAnimIdx )
	{
		self->edict->s.effectsAnims[ effectAnimIdx ] = -1 ;
	}
}

void Animate::RandomAnimate( const char *animname, Event *endevent, bodypart_t part )
{
	Event *event_to_post;
	int   num;
	int   flags;
	qboolean allparts;
	
	assert( animname );
	if ( !animname )
	{
		return;
	}
	
	allparts = false;
	if ( part == all )
	{
		allparts = true;
	}
	
	do {
		if ( allparts )
		{
			switch( part )
            {
            case all:
				part = legs;
				break;
            case legs:
				part = torso;
				break;
            case torso:
				return;
				break;
            }
		}
		switch( part )
		{
		case legs:
            flags = EVENT_LEGS_ANIM;
            break;
		case torso:
            flags = EVENT_TORSO_ANIM;
            break;
		default:
            warning( "RandomAnimate", "Unknown body part %d", part );
            return;
            break;
		}
		num = gi.Anim_Random( self->edict->s.modelindex, animname );
		
		currentAnim = animname;
		
		//
		// this is here to ensure that multiple distinct events are posted for each body part
		//
		if ( allparts && ( part != legs ) )
		{
			if ( endevent )
            {
				event_to_post = new Event( endevent );
            }
			else
            {
				event_to_post = NULL;
            }
		}
		else
		{
			event_to_post = endevent;
		}
		
		//
		// see if we even have a valid animation at all
		//
		if ( num == -1 )
		{
			if ( event_to_post )
			{
				self->PostEvent( event_to_post, FRAMETIME, flags );
			}
		}
		else
		{
			SetAnimDoneEvent( event_to_post, part );
			NewAnim( num, part );
		}
	} while( allparts );
}

void Animate::SetAnimationRate( const float animationRate ) 
{ 
	oldAnimationRate = self->edict->s.animationRate; 
	self->edict->s.animationRate = animationRate;
}

void Animate::RestoreAnimationRate( void ) 
{ 
	self->edict->s.animationRate = oldAnimationRate; 
}

void Animate::SetFrame( int framenum, bodypart_t part, int anim )
{
	int   flags;
	int   numframes;
	int   *panim;
	int   *frame;
	
	switch( part )
	{
	case legs:
		frame = &self->edict->s.frame;
		flags = EVENT_LEGS_ANIM;
		numframes = legs_numframes;
		panim = &self->edict->s.anim;
		break;
	case torso:
		frame = &self->edict->s.torso_frame;
		flags = EVENT_TORSO_ANIM;
		numframes = torso_numframes;
		panim = &self->edict->s.torso_anim;
		break;
	default:
		warning( "SetFrame", "Unknown body part %d", part );
		return;
		break;
	}
	
	if ( anim >= 0 )
	{
		numframes = gi.Anim_NumFrames( self->edict->s.modelindex, anim );
	}
	
	if ( framenum < 0 || ( framenum >= numframes ) )
	{
		warning( "SetFrame","Frame %d is out of range on model %s", framenum, self->model.c_str() );
		return;
	}
	
	// get rid of old anim events so we don't animate
	CancelFlaggedEvents( flags );
	self->CancelFlaggedEvents( flags );
	
	*frame = framenum | FRAME_EXPLICIT;
	
	// if we have a frame override, make sure to set the animation as well
	if ( anim >= 0 )
	{
		*panim = anim | ANIM_BLEND;
	}
}

qboolean Animate::HasAnim( const char *animname )
{
	int num;
	
	num = gi.Anim_Random( self->edict->s.modelindex, animname );
	return ( num >= 0 );
}

void Animate::NewAnimEvent( Event *ev )
{
	NewAnim( ev->GetInteger( 1 ) & ANIM_MASK, (bodypart_t) ev->GetInteger( 2 )  );
}

void Animate::StopAnimating( bodypart_t part )
{
	int frame;
	int anim;
	
	if ( part == all )
	{
		// legs
		frame = CurrentFrame( legs );
		anim = CurrentAnim( legs );
		SetFrame( frame, legs, anim );
		// torso
		frame = CurrentFrame( torso );
		anim = CurrentAnim( torso );
		SetFrame( frame, torso, anim );
	}
	else
	{
		frame = CurrentFrame( part );
		anim = CurrentAnim( part );
		SetFrame( frame, part, anim );
	}
}

void Animate::StopAnimatingAtEnd( bodypart_t part )
{
	int anim;
	
	if ( part == all )
	{
		StopAnimatingAtEnd( legs );
		StopAnimatingAtEnd( torso );
	}
	else if ( part == legs )
	{
		anim = CurrentAnim( part );
		SetFrame( legs_numframes - 1, part, anim );
	}
	else if ( part == torso )
	{
		anim = CurrentAnim( part );
		SetFrame( torso_numframes - 1, part, anim );
	}
}

////////////////////////////
//
// BODY PART SPECIFIC EVENTS
//
////////////////////////////

// Legs

void Animate::Legs_AnimDoneEvent( Event *ev )
{
	EndAnim( legs );
}

void Animate::Legs_AnimEvent( Event *ev )
{
	RandomAnimate( ev->GetString( 1 ), NULL, legs );
}

void Animate::Legs_SetFrameEvent( Event *ev )
{
	int framenum;
	int animnum;
	
	framenum = ev->GetInteger( 1 );
	if ( ev->NumArgs() > 1 )
	{
		animnum = gi.Anim_NumForName( self->edict->s.modelindex, ev->GetString( 2 ) );
	}
	else
	{
		animnum = -1;
	}
	
	SetFrame( framenum, legs, animnum );
}

// HACK HACK HACK
void Animate::Legs_StopAnimating( Event *ev )
{
	CancelFlaggedEvents( EVENT_LEGS_ANIM );
	self->CancelFlaggedEvents( EVENT_LEGS_ANIM );
}

// Torso

void Animate::Torso_AnimDoneEvent( Event *ev )
{
	EndAnim( torso );
}

void Animate::Torso_AnimEvent( Event *ev )
{
	RandomAnimate( ev->GetString( 1 ), NULL, torso );
}

void Animate::Torso_SetFrameEvent( Event *ev )
{
	int framenum;
	int animnum;
	
	framenum = ev->GetInteger( 1 );
	if ( ev->NumArgs() > 1 )
	{
		animnum = gi.Anim_NumForName( self->edict->s.modelindex, ev->GetString( 2 ) );
	}
	else
	{
		animnum = -1;
	}
	
	SetFrame( framenum, torso, animnum );
}

// HACK HACK HACK
void Animate::Torso_StopAnimating( Event *ev )
{
	CancelFlaggedEvents( EVENT_TORSO_ANIM );
	self->CancelFlaggedEvents( EVENT_TORSO_ANIM );
}

void Animate::ClearTorsoAnim( void )
{
	tiki_cmd_t  cmds;
	int         last_anim;
	static qboolean clearing = false;
	
	last_anim = self->edict->s.torso_anim & ANIM_MASK;
	
	if ( ( self->edict->s.torso_anim & ANIM_BLEND ) && !( self->edict->s.torso_anim & ANIM_SERVER_EXITCOMMANDS_PROCESSED ) && !clearing )
	{
		if ( gi.Frame_Commands( self->edict->s.modelindex, last_anim, TIKI_FRAME_CMD_EXIT, &cmds ) )
		{
			int ii, j;
			
			clearing = true;
			for( ii = 0; ii < cmds.num_cmds; ii++ )
            {
				Event *ev = new Event( cmds.cmds[ ii ].args[ 0 ] );
				
				ev->SetSource( EV_FROM_ANIMATION );
				ev->SetAnimationNumber( last_anim );
				ev->SetAnimationFrame( 0 );
				
				for( j = 1; j < cmds.cmds[ ii ].num_args; j++ )
				{
					ev->AddToken( cmds.cmds[ ii ].args[ j ] );
				}
				self->ProcessEvent( ev );
            }
			clearing = false;
		}
		self->edict->s.torso_anim |= ANIM_SERVER_EXITCOMMANDS_PROCESSED;
	}
	
	CancelFlaggedEvents( EVENT_TORSO_ANIM );
	self->CancelFlaggedEvents( EVENT_TORSO_ANIM );
	self->edict->s.torso_anim &= ~ANIM_BLEND;
}

void Animate::ClearLegsAnim( void )
{
	tiki_cmd_t        cmds;
	int               last_anim;
	//static qboolean   clearing = false;
	
	if ( self->edict->s.anim & ANIM_SERVER_EXITCOMMANDS_PROCESSED )
	{
		last_anim = self->edict->s.anim & ANIM_MASK;
		
		if ( gi.Frame_Commands( self->edict->s.modelindex, last_anim, TIKI_FRAME_CMD_EXIT, &cmds ) )
		{
			int ii, j;
			
			//clearing = true;
			for( ii = 0; ii < cmds.num_cmds; ii++ )
            {
				Event *ev = new Event( cmds.cmds[ ii ].args[ 0 ] );
				
				ev->SetSource( EV_FROM_ANIMATION );
				ev->SetAnimationNumber( last_anim );
				ev->SetAnimationFrame( 0 );
				
				for( j = 1; j < cmds.cmds[ ii ].num_args; j++ )
				{
					ev->AddToken( cmds.cmds[ ii ].args[ j ] );
				}
				self->ProcessEvent( ev );
            }
			//clearing = false;
		}
		self->edict->s.anim |= ANIM_SERVER_EXITCOMMANDS_PROCESSED;
	}
	
	CancelFlaggedEvents( EVENT_LEGS_ANIM );
	self->CancelFlaggedEvents( EVENT_LEGS_ANIM );
	
	self->edict->s.anim &= ~ANIM_BLEND;
}
