/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/*
==========================================================================

						 - SPLITMODELS -

==========================================================================
*/
// - Adding the Player models in split pieces
// by Jalisk0

#include "g_local.h"


void G_SetNewAnimUpper (edict_t *ent)
{
	pmanim_t	*pmanim = &ent->pmAnim;

	//CROUCHED
	if ((pmanim->anim_moveflags & ANIMMOVE_DUCK) )
	{
		if ((pmanim->anim_moveflags & ANIMMOVE_WALK) || (pmanim->anim_moveflags & ANIMMOVE_RUN))
		{
			pmanim->anim[UPPER] = TORSO_RUN;
			
		} else {

			pmanim->anim[UPPER] = TORSO_STAND;
		}
	}
	//SWIMMING
	else if (pmanim->anim_moveflags & ANIMMOVE_SWIM)
	{
		pmanim->anim[UPPER] = TORSO_SWIM;
		
	}
	//is JUMP
	else if (pmanim->anim_jump){

			pmanim->anim[UPPER] = TORSO_RUN;
	}
	// RUN
	else if (pmanim->anim_moveflags & ANIMMOVE_RUN)
	{
		pmanim->anim[UPPER] = TORSO_RUN;
	}
	//WALK
	else if (pmanim->anim_moveflags & ANIMMOVE_WALK)
	{
		pmanim->anim[UPPER] = TORSO_STAND;
	}
	// STAND
	else
	{	
		pmanim->anim[UPPER] = TORSO_STAND;
	}
}

void G_SetNewAnimLower (edict_t *ent)
{
	pmanim_t	*pmanim = &ent->pmAnim;

	if ((pmanim->anim_moveflags & ANIMMOVE_DUCK))
	{
		if ((pmanim->anim_moveflags & ANIMMOVE_WALK) || (pmanim->anim_moveflags & ANIMMOVE_RUN))
		{
			pmanim->anim[LOWER] = LEGS_CRWALK;
			
		} else {

			pmanim->anim[LOWER] = LEGS_CRSTAND;
		}
	}
	//SWIMMING
	else if (pmanim->anim_moveflags & ANIMMOVE_SWIM)
	{
		if (pmanim->anim_moveflags & ANIMMOVE_FRONT)
		{
			pmanim->anim[LOWER] = LEGS_SWIMFWD;
			
		} else 
			pmanim->anim[LOWER] = LEGS_SWIM;
	}
	//is JUMP
	else if (pmanim->anim_jump){
		
		if (pmanim->anim_jump_style == 1){
			pmanim->anim[LOWER] = LEGS_JUMP1;
		}
		else if (pmanim->anim_jump_style == 2){
			pmanim->anim[LOWER] = LEGS_JUMP2;
		}
		else 
			pmanim->anim[LOWER] = LEGS_JUMP3;
	}
	// RUN
	else if (pmanim->anim_moveflags & ANIMMOVE_RUN)
	{
		//front/backward has priority over side movements
		if (pmanim->anim_moveflags & ANIMMOVE_FRONT){
			pmanim->anim[LOWER] = LEGS_RUNFWD;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_BACK){
			pmanim->anim[LOWER] = LEGS_RUNBACK;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_RIGHT){
			pmanim->anim[LOWER] = LEGS_RUNRIGHT;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_LEFT){
			pmanim->anim[LOWER] = LEGS_RUNLEFT;
			
		} else 	//is moving by inertia
			pmanim->anim[LOWER] = LEGS_WALKFWD;	
	}
	//WALK
	else if (pmanim->anim_moveflags & ANIMMOVE_WALK)
	{
		//front/backward has priority over side movements
		if (pmanim->anim_moveflags & ANIMMOVE_FRONT){
			pmanim->anim[LOWER] = LEGS_WALKFWD;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_BACK){
			pmanim->anim[LOWER] = LEGS_WALKBACK;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_RIGHT){
			pmanim->anim[LOWER] = LEGS_WALKRIGHT;
			
		} else if (pmanim->anim_moveflags & ANIMMOVE_LEFT){
			pmanim->anim[LOWER] = LEGS_WALKLEFT;
			
		} else 	//is moving by inertia
			pmanim->anim[LOWER] = LEGS_WALKFWD;
	}
	else
	{	// STAND
		pmanim->anim[LOWER] = LEGS_STAND;
	}
}


/*
=================================
 G_SetNewAnim
=================================
*/
void G_SetNewAnim (edict_t *ent)
{
	int			part;
	pmanim_t	*pmanim = &ent->pmAnim;

	pmanim->anim_jump_thunk = qtrue;
	
	for( part = 0; part<PMODEL_PARTS; part++ )
	{
		if( pmanim->anim_priority[part] > ANIM_ATTACK )
			continue;
		
		switch (part)
		{
		case LOWER:
			G_SetNewAnimLower(ent);
			break;
			
		case UPPER:
			G_SetNewAnimUpper(ent);
			break;
			
		case HEAD:
		default:
			//there aren't any head animations
			pmanim->anim[part] = 0;
			break;
		}
	}
}


/*
===================
  Anim_CheckJump
  Manages the jumping animation phases. First phase is the player going up.

  Second phase is what I called PRESTEP. It is not made for Q3 animations,
  but for my own. Q3 would launch the landing animations at touching ground.
  I check the floor one step below the player, and launch a "ready to land"
  animation just before touching ground. Removing prestepping will produce
  better results for Q3 ppms.
===================
*/
void Anim_CheckJump( edict_t *ent )
{
	vec3_t		point;
	trace_t		trace;
	pmanim_t	*pmanim = &ent->pmAnim;

	//not jumping anymore?
	if (ent->groundentity)
	{	
		pmanim->anim_jump_thunk = qfalse;
		pmanim->anim_jump = qfalse;
		return;
	}

	//don't change the animation on high anim_priorities
	if ((pmanim->anim_priority[LOWER] > ANIM_ATTACK))
		return;

	//if not falling we don't need to continue
	if (ent->velocity[2] > -80)
		return;

	//PRE-STEP TO LAND
	if (pmanim->anim_jump_prestep)	//I already did.
		return;

	//determine a point below scaled by velocity
	point[0] = ent->s.origin[0];
	point[1] = ent->s.origin[1];
	point[2] = ent->s.origin[2] + (0.025 * ent->velocity[2]);

	//trace to point
	trap_Trace (&trace, ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID);

	if (trace.plane.normal[2] < 0.7 && !trace.startsolid){
		return;
	}
	else
	{
		//found solid. Get ready to land
		if (pmanim->anim_jump_style == 1){
			pmanim->anim[LOWER] = LEGS_JUMP1ST;

		} else if (pmanim->anim_jump_style == 2){
			pmanim->anim[LOWER] = LEGS_JUMP2ST;

		} else 
			pmanim->anim[LOWER] = LEGS_JUMP3ST;

		//set as done
		pmanim->anim_jump_prestep = qtrue;
	}
}

/*
===================
  AnimIsStep
  Checks the floor one step below the player. Used to detect
  if the player is really falling or just walking down a stair.
===================
*/
qboolean Anim_IsStep( edict_t *ent )
{
	vec3_t		point;
	trace_t		trace;
	
	//determine a point below
	point[0] = ent->s.origin[0];
	point[1] = ent->s.origin[1];
	point[2] = ent->s.origin[2] - (1.6*STEPSIZE);
	
	//trace to point
	trap_Trace (&trace, ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID);
	
	if (trace.plane.normal[2] < 0.7 && !trace.startsolid)
		return qfalse;
	
	//found solid.
	return qtrue;
}
/*
===================
  AnimIsSwim
  Categorize waterlevel for swim animation
===================
*/
qboolean AnimIsSwim( edict_t *ent )
{

	if (ent->waterlevel > 2)
		return qtrue;

	if (ent->waterlevel && !ent->groundentity){

		if (!Anim_IsStep(ent))
			return qtrue;
	}

	return qfalse;
}

/*
=================
G_SetPModelFrame
=================
*/
void G_SetPModelFrame( edict_t *ent )
{
	pmanim_t	*pmanim = &ent->pmAnim;
	qboolean	updateanims = qfalse;

	if( ent->deadflag )
		return;

	//Finish updating the moveflags

	//swim
	if (AnimIsSwim(ent))
		pmanim->anim_moveflags |= ANIMMOVE_SWIM;

	//jump is special
	if (pmanim->anim_jump && (ent->groundentity || pmanim->anim_moveflags & ANIMMOVE_SWIM))//JUMP stop
	{
		pmanim->anim_jump = qfalse;
		pmanim->anim_jump_thunk = qfalse;
		
	} else if (!ent->groundentity && !pmanim->anim_jump && !(pmanim->anim_moveflags & ANIMMOVE_SWIM)){ //falling
		
		if (!Anim_IsStep(ent)){
			pmanim->anim_jump_style = 0;
			pmanim->anim_jump = qtrue;
			updateanims = qtrue;//activate update
		}
	}

#ifdef _ANIM_PRESTEP

	//prestep thinking
	if (pmanim->anim_jump)
		Anim_CheckJump(ent);

#endif

	//--See if we need to update based on the new moveflags--
	
	//jump is ordered to refresh by setting anim_jump_thunk as false
	if (pmanim->anim_jump_thunk == qfalse && !(pmanim->anim_moveflags & ANIMMOVE_SWIM)){
		updateanims = qtrue;
		pmanim->anim_jump_prestep = qfalse;
	}
	else if (pmanim->anim_moveflags != pmanim->anim_oldmoveflags)
	{
		updateanims = qtrue;
	}

	//Call update
	if (updateanims)
		G_SetNewAnim (ent);

	//set animations
	ent->s.frame = ((pmanim->anim[LOWER] &0x3F)|(pmanim->anim[UPPER] &0x3F)<<6|(pmanim->anim[HEAD] &0xF)<<12);

	//store oldmoveflags for next round.
	pmanim->anim_oldmoveflags = pmanim->anim_moveflags;
}



/*
=================
G_SetClientFrame
=================
*/
void G_SetClientFrame( edict_t *ent )
{
	if( (!ent->r.client) || (ent->s.type != ET_PLAYER) || (ent->r.svflags & SVF_NOCLIENT) )
		return;

	G_SetPModelFrame(ent);
}



