/******************************************************************
  
  Module:  animcontroller.cpp
  
  Author: Sean Craig
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "animcontroller.h"
#include "gameobj.h"
#include "registry.h"
#include "animdefs.h"
#include "titan.h"

#include "graphic.h"
#include "SyScene.h"
#include "SyCSprite.h"
#include "tinyxml.h"
#include "nameid.h"
#include "stats.h"
#include "physics.h"
#include "tuning.h"
#include "netpacket.h"
#include "TitanPeeringNetwork.h"
#include "database.h"
#include "areaeffect.h"
//---------------------------------------------- Class Declarations

#define ANIM_HANDLE_NONE (-1)
     
class cAnimState;
class cAnimCharController : public cAnimCharControllerInterface
{
public:

  cAnimCharController();
  virtual ~cAnimCharController();
  virtual void            Init(cGameObject *owner);
  virtual void            Reset();
  virtual  void           Update(float time);
  virtual cGameObject *   GetOwner(){return mOwner;};

  void                    ChangeState(eAnimState state);
  cAnimControllerSetting *GetSetting();
  float                   GetStateTime(){return mStateTime;};

  void                    NetworkSendBroadcast(eAnimState state);
  virtual void            NetworkReceiveBroadcast(const char *state, int statelen);

  void                    PlayImpact(tGameObjectID id);
  void                    ExtractImpact(); // before and after a transition...
  void                    InsertImpact(); 
  void                    ClearImpact();
  int                     GetImpactIndex(){return mImpactIndex;};
  void                    UpdateImpactBlend();


  virtual float           GetDamageMultiplier(eComboType combo);
  virtual float           GetStrikethroughMultiplier(eComboType combo);
  virtual float           GetRange(eComboType combo);

  virtual eAnimState      GetAnimState() const {return mCurState;};
  virtual const char *    GetAnimStateName() const;

  virtual bool            IsBlocking() const;
  virtual bool            IsDodging() const {return false;};

protected:
  void                    CheckAnims();
  void                    CheckAnim(int32 id);
  const char *            GetAnimName(int32 id);

  cGameObject *           mOwner;
  cAnimState *            mStates[NUM_ANIM_STATES]; 
  eAnimState              mCurState;
  eAnimState              mPrevState;
  int32                   mPrevSlot; // current slot playing; used to detect if an anim is finished
  float                   mStateTime; // current amount of time the controller has been in this state
  int32                   mCurAbility; // cur ability being performed
  tAnimSetID              mAnimSetID; // action frame info, etc...

  int                     mImpactIndex;
  float                   mImpactTime;
  float32                 mImpactFrame;
  tAnimIDValues           mImpactAnimID; 

  static float            mImpactBlendMaxBlend; // max percentage of blending
  static float            mImpactBlendAttack;  // amount of time it takes to acheive max blend
  static float            mImpactBlendSustain; // amount of time we sustain max blend
  static float            mImpactBlendRelease; // amount of time it takes to go from max blend to no blend

};  


typedef enum
{
  AT_SINGLE,
  AT_ARC,
  AT_EXPLOSION,
  AT_CHARGE,
  NUM_ATTACK_TYPES
} eAttackType;


typedef enum
{
  AE_NONE,
  AE_KNOCKBACK,
  NUM_ATTACK_EFFECTS
} eAttackEffect;

class cAnimControllerSetting
{
public:

  cAnimControllerSetting();

  cNameID mAnimSet;
  eAnimState mAnim;

  float   mDamageMultiplier;
  float   mStrikethroughMultiplier;

  eAttackType   mAttackType;
  eAttackEffect mAttackEffect;

  float   mAttackStartAngle;
  float   mAttackEndAngle;

  float   mActionFrame;  // in secs
  float   mActionFrameEnd;  // in secs

  float   mSpeed;
  float   mRange;

  float   mComboWindowStart; // in secs
  float   mComboWindowEnd;
};

class cAnimState
{
public:
  cAnimState() ;
  virtual ~cAnimState(){};

  virtual void Init(cAnimCharController *owner){mOwner = owner;};

  virtual void Enter(){};
  virtual void Update(float time)=0;
  virtual void Exit(){};
  virtual bool Transitions(){return false;};

  virtual void CommonActions(); // take potions, pick up items
protected:

          void ActivateObject();
          int  GetTurnAnim(float delta);
          bool TurnAnimsCompatable(int playing_anim,int next_anim);

  cGameObject *GetGameObject();
  SyCSprite   *GetSprite();
  SyScene     *GetScene();
  cAnimCharControllerInput *GetInput();
  bool         PlayAnim(int32 animid,int32 blendtime = 250);
  float        TurnTo(float32 angle,float32 turnSpeed, float32 threshold);
  void         ClearInput();

  cAnimCharController *mOwner;
  int32           mPlaybackIndex; // for our animation...
  int32           mBlendIndex; 
  bool            mTurning;
  bool            mPlayingTurnAnim;
  float           mAnimDuration;

};


class cAS_Stand : public cAnimState
{
public:

  cAS_Stand();
  virtual ~cAS_Stand(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables();
public:
  void PlayTurnAnim();
  static float   mTurnThreshold; // if delta angle is greater than this, turn.
         float   mTurnSpeed;  
  static float   mTurnSpeedMultiplier;
         float   mTurnTo;

};

class cAS_Running : public cAnimState
{
public:
  cAS_Running();
  virtual ~cAS_Running(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables();


  static float   mTurnSpeed;

protected:
  int32   mWalkPlaybackIndex;
  float   mCurrentBlend;

  static float   mTurnThreshold; // if delta angle is greater than this, turn.
  static float   mStopThreshold; // if delta angle is greater than this, stop before turning.
  static float   mRunSpeed; // how fast to play the run animation at full tilt
  static float   mWalkSpeed; // how fast to play the run animation at full tilt
  static float   mBlendSpeed; // speed at which we change the blend factor


};

class cAS_Standard : public cAnimState  // standard animation
{
public:
  cAS_Standard(int32 animID) : mAnimID(animID){};
  virtual ~cAS_Standard(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables(){};

protected:
  int32   mAnimID;
};


class cAS_Land : public cAnimState  // standard animation
{
public:
  cAS_Land(int32 animID) : mAnimID(animID){};
  virtual ~cAS_Land(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables(){};

protected:
  int32   mAnimID;
};

class cAS_HitReact : public cAS_Standard  // standard animation
{
public:
  cAS_HitReact(int32 animID) : cAS_Standard(animID){};
  virtual ~cAS_HitReact(){};
  virtual void Update(float time);
  static void RegisterTuningVariables(){};
};

class cAS_Attack : public cAnimState  // standard animation
{
public:                        
  cAS_Attack(int32 animID,eComboType attackIndex) : 
        mAnimID(animID),
        mAttackIndex(attackIndex),
        mComboL(false),
        mComboS(false){};
  virtual ~cAS_Attack(){};
  virtual void Enter();
  virtual void Exit();
  virtual void Update(float time);
  void AttackFrame();
  static void RegisterTuningVariables(){};

protected:
  eAnimState GetNextCombo_Strong();
  eAnimState GetNextCombo_Light();
  bool  ShouldPlayImpact();
  int32   mAnimID;
  static float   mTurnSpeed;
  eComboType   mAttackIndex;
  tGameObjectID mTarget;
  float32 mTargetAngle;
  bool    mComboL;
  bool    mComboS;
};     

class cAS_Jump : public cAnimState  // standard animation
{
public:
  cAS_Jump(int32 animid) : mAnimID(animid){};
  virtual ~cAS_Jump(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables();

public:
  static  float      mJumpUp;
  static  float      mJumpSpeed;
  SyVect3            mJumpVel;
  int32              mAnimID;
};

class cAS_InAir : public cAnimState  // standard animation
{
public:
  cAS_InAir(int32 animid) : mAnimID(animid){};
  virtual ~cAS_InAir(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables();

protected:
  static  float      mJumpControl;
  static  float      mTurnSpeed;
  int32              mAnimID;
  
};

class cAS_Death : public cAnimState  // standard animation
{
public:
  virtual ~cAS_Death(){};
  virtual void Enter();
  virtual void Update(float time);

  virtual void CommonActions(){};
  static void RegisterTuningVariables(){};
};

class cAS_Block : public cAnimState  // standard animation
{
public:
  cAS_Block(){};
  virtual ~cAS_Block(){};
  virtual void Enter();
  virtual void Update(float time);
  bool IsActive();
  static void RegisterTuningVariables(){};
            
protected:
  static float   mTurnSpeed;  
};

class cAS_RangedAttack : public cAnimState  // standard animation
{
public:                        
  cAS_RangedAttack(){};
  virtual ~cAS_RangedAttack(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables(){};

protected:
  static float   mTurnSpeed;
  tGameObjectID mTarget;
  float32 mTargetAngle;
  float32 mTargetPitch;
  float32 mTargetRange;
};

class cAS_Override : public cAnimState  // standard animation
{
public:
  cAS_Override() {};
  virtual ~cAS_Override(){};
  virtual void Enter();
  virtual void Update(float time);
  static void RegisterTuningVariables(){};

protected:
};

class cAS_Dodge : public cAnimState  // standard animation
{
public:
  cAS_Dodge(){};
  virtual ~cAS_Dodge(){};
  virtual void Enter();
  virtual void Update(float time);
  bool IsActive();
};

class cAS_Knocked : public cAnimState  // standard animation
{
public:
  cAS_Knocked(int32 animid) : mAnimID(animid){};
  virtual ~cAS_Knocked(){};
  virtual void Enter();
  virtual void Update(float time);

public:
  SyVect3            mImpactVel;
  int32              mAnimID;
};

class cAS_KnockedInAir : public cAnimState  // standard animation
{
public:
  cAS_KnockedInAir(int32 animid) : mAnimID(animid){};
  virtual ~cAS_KnockedInAir(){};
  virtual void Enter();
  virtual void Update(float time);

protected:
  int32              mAnimID;
};

class cAS_KnockedLand : public cAnimState  // standard animation
{
public:
  cAS_KnockedLand(int32 animid) : mAnimID(animid){};
  virtual ~cAS_KnockedLand(){};
  virtual void Enter();
  virtual void Update(float time);

protected:
  int32              mAnimID;
};
//----------------------------------------- Functions Declarations

float GetAngleAdjustment(float delta_angle, float angle_speed);
static inline bool FloatsEqual(float a, float b);
static inline float FramesToSecs(int frame){return ((float)frame)/30.0f;};

//------------------------------------ Static Member Variables (For Tuning Only)

float cAS_Stand::mTurnThreshold   = SY_DEG_TO_RAD(10.0f);
float cAS_Stand::mTurnSpeedMultiplier = 2.5f;

float cAS_Running::mTurnThreshold = SY_DEG_TO_RAD(10.0f);
float cAS_Running::mTurnSpeed     = SY_DEG_TO_RAD(360 * 3); // all the way around in a third of a sec...
float cAS_Running::mStopThreshold = SY_DEG_TO_RAD(100); 
float cAS_Running::mWalkSpeed     =  0.5f; 
float cAS_Running::mRunSpeed      =  1.0f; 
float cAS_Running::mBlendSpeed      = 5.0f;

float cAS_Attack::mTurnSpeed      = SY_DEG_TO_RAD(360 * 3); // all the way around in a third of a sec...
float cAS_Jump::mJumpSpeed        = 5.0f; 
float cAS_Jump::mJumpUp           = 6.5f; 
float cAS_InAir::mJumpControl       = 2.0f; 
float cAS_InAir::mTurnSpeed      = SY_DEG_TO_RAD(360) ;

float cAS_Block::mTurnSpeed      = SY_DEG_TO_RAD(360 * 2) ;

float cAnimCharController::mImpactBlendMaxBlend = 0.5f; // max percentage of blending
float cAnimCharController::mImpactBlendAttack =   0.2f;  // amount of time it takes to acheive max blend
float cAnimCharController::mImpactBlendSustain =  0.2f; // amount of time we sustain max blend
float cAnimCharController::mImpactBlendRelease =  0.2f; // amount of time it takes to go from max blend to no blend

float cAS_RangedAttack::mTurnSpeed      = SY_DEG_TO_RAD(360 * 3); // all the way around in a third of a sec...

static const float EPSILON = 0.000001f; // used to see if two float angles or values are close enough to be equal


const char *l_AnimStatenames[] = 
{
  "Stand",
    "Run",
    "ComboL",
    "ComboH",
    "ComboLL",
    "ComboLH",                       
    "ComboHL",
    "ComboHH",
    "ComboLLL",
    "ComboLLH",
    "ComboLHL",
    "ComboLHH",
    "ComboHLL",
    "ComboHLH",
    "ComboHHL",
    "ComboHHH",
    "Jump",
    "InAir",
    "Land",
    "Death",
    "HitReact",
    "Block",
    "RangedAttack",
    "Override",
    "RunJump",
    "RunInAir",
    "RunLand",
    "Dodge",
    "EmoteAnger",
    "Knockback",
    "KnockbackInAir",
    "KnockbackLand",
    "KnockbackGetUp",
    "Knockforward",
    "KnockforwardInAir",
    "KnockforwardLand",
    "KnockforwardGetUp"
  
};
//------------------------------------ Functions Definitions

float AngleDifference(float start, float end )
{
  return AngleNormalize(start-end);
}

float AngleNormalize(float in)
{
  float result = fmod(in,SY_DEG_TO_RAD(360.0f));

  // result in range from -180 to +180
  if (result > SY_DEG_TO_RAD(180.0f))
  {
    result -= SY_DEG_TO_RAD(360.0f);
  } 
  else if (result < SY_DEG_TO_RAD(-180.0f))
  {
    result += SY_DEG_TO_RAD(360.0f);
  }

  return result;
}

float GetAngleAdjustment(float delta_angle, float angle_speed)
{
  if (delta_angle >  angle_speed)
  {
    return angle_speed;
  }
  else if (delta_angle < -angle_speed)
  {
    return -angle_speed;
  }
  else return delta_angle;
}

static inline bool FloatsEqual(float a, float b)
{
  float delta = a-b;
  if (delta > -EPSILON && delta < EPSILON)
  {
    return true;
  }
  return false;
}
//------------------------------------ Member Functions Definitions

//------------------------------------ cAnimControllerSys
cAnimControllerSys::cAnimControllerSys()
{
}

cAnimControllerSys::~cAnimControllerSys()
{
  for (int index = mSettings.Begin(); index != mSettings.End();index = mSettings.Next(index))
  {
    delete mSettings(index);
  }
  mSettings.Clear();
}

void
cAnimControllerSys::Init()
{
  Reload();
  RegisterTuningVariables();
}

void
cAnimControllerSys::Reload()
{

  static const int NUM_ANIM_STATENAMES = (sizeof(l_AnimStatenames) / sizeof (l_AnimStatenames[0]));

  const char *l_AttackTypenames[] = 
  {
    "Single",
    "Arc",
    "Explosion",
    "Charge"
  };
  static const int NUM_ATTACKTYPE_NAMES = (sizeof(l_AttackTypenames) / sizeof (l_AttackTypenames[0]));

  const char *l_AttackEffectnames[] = 
  {
    "None",
    "Knockback",
  };
  static const int NUM_ATTACKEFFECT_NAMES = (sizeof(l_AttackEffectnames) / sizeof (l_AttackEffectnames[0]));


  //static const float FPS = 30.0f;
  // clear out old settings
  for (int index = mSettings.Begin(); index != mSettings.End();index = mSettings.Next(index))
  {
    delete mSettings(index);
  }
  mSettings.Clear();

  // load up xml file 
	TiXmlDocument doc( "data/db/anim.xml" );
	bool loadOkay = doc.LoadFile();

	if ( !loadOkay )
	{
    SyAssertf(0,"Unable to open anim.xml file");
    return;
	}
  
	TiXmlNode* node = 0;

  node = doc.FirstChild( "dataroot" );
  SyAssertf(node!=NULL,"Unable to find dataroot in animation db file");
	node = node->FirstChild( "Anim" );
	SyAssertf( node!=NULL, "Unable to find Anim node in animation db file" );

  while (node != NULL)
  {
    cAnimControllerSetting *newsetting = new cAnimControllerSetting; 
    TiXmlNode* data_node = node->FirstChild("AnimSet");
    SyAssert(data_node);
    const char *name = data_node->FirstChild()->Value();
    newsetting->mAnimSet.SetName(name);

    int enumVal = 0;
    data_node = node->FirstChild("Anim");
    SyAssertf(data_node!=NULL,"Unable to find Anim in %s in anim db file",name);
    const char * state= data_node->FirstChild()->Value();
    SyAssertf(NUM_ANIM_STATENAMES == NUM_ANIM_STATES,"Forgot to add anim state name?");
    bool result = ChooseEnum(state,l_AnimStatenames,NUM_ANIM_STATENAMES, enumVal);
    SyAssertf(result == true,"Unknown anim state in record '%s' in item.xml",name);
    newsetting->mAnim = (eAnimState)enumVal;

    data_node = node->FirstChild("DamageMultiplier");
    if (data_node != NULL)
    {
      SyAssertf(data_node!=NULL,"Unable to find DamageMultiplier in %s in anim db file",name);
      newsetting->mDamageMultiplier = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("StrikethroughMultiplier");
    if (data_node != NULL)
    {
      newsetting->mStrikethroughMultiplier = (float) atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AttackType");
    if (data_node != NULL)
    {
      int enumVal = 0;
      const char * attack= data_node->FirstChild()->Value();
      SyAssertf(NUM_ATTACKTYPE_NAMES == NUM_ATTACK_TYPES,"Forgot to add attack type name?");
      bool result = ChooseEnum(attack,l_AttackTypenames,NUM_ATTACKTYPE_NAMES, enumVal);
      SyAssertf(result == true,"Unknown attack type in record '%s' in item.xml",name);
      newsetting->mAttackType = (eAttackType)enumVal;
    }

    data_node = node->FirstChild("AttackEffect");
    if (data_node != NULL)
    {
      int enumVal = 0;
      const char * effect= data_node->FirstChild()->Value();
      SyAssertf(NUM_ATTACKEFFECT_NAMES == NUM_ATTACK_EFFECTS,"Forgot to add attack effect name?");
      bool result = ChooseEnum(effect,l_AttackEffectnames,NUM_ATTACKEFFECT_NAMES, enumVal);
      SyAssertf(result == true,"Unknown attack effect in record '%s' in item.xml",name);
      newsetting->mAttackEffect = (eAttackEffect)enumVal;
    }

    data_node = node->FirstChild("AttackStartAngle");
    if (data_node != NULL)
    {
      newsetting->mAttackStartAngle = (float) atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("AttackEndAngle");
    if (data_node != NULL)
    {
      newsetting->mAttackEndAngle = (float) atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ActionFrame");
    if (data_node != NULL)
    {
      newsetting->mActionFrame = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ActionFrameEnd");
    if (data_node != NULL)
    {
      newsetting->mActionFrameEnd = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Speed");
    if (data_node != NULL)
    {
      newsetting->mSpeed = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("Range");
    if (data_node != NULL)
    {
      newsetting->mRange = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ComboWindowStart");
    if (data_node != NULL)
    {
      newsetting->mComboWindowStart = (float)atof(data_node->FirstChild()->Value());
    }

    data_node = node->FirstChild("ComboWindowEnd");
    if (data_node != NULL)
    {
      newsetting->mComboWindowEnd = (float)atof(data_node->FirstChild()->Value());
    }

    SyAssertf(newsetting->mComboWindowStart <= 1.0f,"Bad Parameter (ComboWindowStart > 1.0f) in anim setting file");
    SyAssertf(newsetting->mComboWindowStart >= 0.0f,"Bad Parameter (ComboWindowStart > 0.0f) in anim setting file");

    SyAssertf(newsetting->mComboWindowEnd <= 1.0f,"Bad Parameter (ComboWindowEnd > 1.0f) in anim setting file");
    SyAssertf(newsetting->mComboWindowEnd >= 0.0f,"Bad Parameter (ComboWindowEnd > 0.0f) in anim setting file");

    SyAssertf(newsetting->mActionFrame <= 1.0f,"Bad Parameter (ActionFrame > 1.0f) in anim setting file");
    SyAssertf(newsetting->mActionFrame >= 0.0f,"Bad Parameter (ActionFrame > 0.0f) in anim setting file");

    SyAssertf(newsetting->mActionFrameEnd <= 1.0f,"Bad Parameter (ActionFrameEnd > 1.0f) in anim setting file");
    SyAssertf(newsetting->mActionFrameEnd >= 0.0f,"Bad Parameter (ActionFrameEnd > 0.0f) in anim setting file");

    SyAssertf(newsetting->mActionFrame <= newsetting->mComboWindowEnd,"Bad Parameter (ActionFrameEnd > ComboWindowEnd) in anim setting file");
    SyAssertf(newsetting->mComboWindowStart <= newsetting->mComboWindowEnd,"Bad Parameter (CombowWindowStart > ComboWindowEnd) in anim setting file");

    mSettings.Add(newsetting);
    node = node->NextSibling();
  }
}

cAnimControllerSetting  *
cAnimControllerSys::Fetch(tAnimSetID id,int animstate)
{
  for (int index = 0;index < mSettings.Size();++index)
  {
    if (mSettings(index)->mAnimSet.GetID() == id && mSettings(index)->mAnim == animstate)
    {
      return mSettings(index);
    }
  }
  return NULL;
}

void
cAnimControllerSys::RegisterTuningVariables()
{
  cAS_Stand::RegisterTuningVariables();
  cAS_Running::RegisterTuningVariables();
  cAS_Attack::RegisterTuningVariables();
  cAS_Jump::RegisterTuningVariables();
  cAS_InAir::RegisterTuningVariables();
  cAS_Death::RegisterTuningVariables();
  cAS_Standard::RegisterTuningVariables();
  cAS_Land::RegisterTuningVariables();
}
//------------------------------------ cAnimControllerInput
cAnimCharControllerInput:: cAnimCharControllerInput() 
{
  Clear();
}

void 
cAnimCharControllerInput::Clear()
{
  mHeadingRequest = 0.0f;
  mSpeedRequest = 0.0f;
  mAttackRequestL = false;
  mAttackRequestS = false;
  mAttackRequestRanged = false;
  mBlockRequest = false;
  mJumpRequest = false;
  mHitReact = false;
  mAbilityRequest = false;
  mDeath = false;
  mHitReactTarget = ID_NONE;
  mHitReactAmount = 0.0f;
  mHeight  = 0.0f;
  mYVel = 0.0f;
  mFall = false;
  mActionRequest = false;
  mActionTarget = ID_NONE;
  mTargetAngle = 0.0f;
  mTargetRange = 20.0f;
  mAnimationOverride = ANIM_HANDLE_NONE; 
  mOverrideCancelEnd = false; 
  mOverrideCancelImmediate = false; 
  mDodging = false;
  mDodgeHigh = false;
  mEmoteAnger = false;
  mbKnockback = false;
  mbKnockforward = false;
  mKnockbackVect.X = 0.0f;
  mKnockbackVect.Y = 0.0f;
  mKnockbackVect.Z = 0.0f;
  mKnockbackHeading = 0.0f;
}

//------------------------------------ cAnimControllerSetting

cAnimControllerSetting::cAnimControllerSetting() :
mAnim(AS_STAND),
mDamageMultiplier(1.0f),
mStrikethroughMultiplier(1.0f),
mAttackType(AT_SINGLE),
mAttackEffect(AE_NONE),
mAttackStartAngle(0.0f),
mAttackEndAngle(0.0f),
mActionFrame(0.5f),
mActionFrameEnd(0.5f),
mSpeed(1.0f),
mRange(1.0f),
mComboWindowStart(0.2f),
mComboWindowEnd(0.8f)
{
}


//------------------------------------ cAnimControllerInterface

cAnimCharControllerInterface:: ~cAnimCharControllerInterface()
{
}

cAnimCharControllerInterface *cAnimCharControllerInterface::Allocate()
{
  return new cAnimCharController;
}

cAnimCharControllerInterface::cAnimCharControllerInterface() 
{
}

bool            
cAnimCharControllerInterface::IsAttacking() const
{
  switch(GetAnimState())
  {
    case AS_ATTACK_L:
    case AS_ATTACK_H:
    case AS_ATTACK_LL:
    case AS_ATTACK_LH:
    case AS_ATTACK_HL:
    case AS_ATTACK_HH:
    case AS_ATTACK_LLL:
    case AS_ATTACK_LLH:
    case AS_ATTACK_LHL:
    case AS_ATTACK_LHH:
    case AS_ATTACK_HLL:
    case AS_ATTACK_HLH:
    case AS_ATTACK_HHL:
    case AS_ATTACK_HHH:
    case AS_ATTACK_RANGED:
      return true;
    default:
      return false;
  }
}


//------------------------------------ cAnimCharController

cAnimCharController::cAnimCharController() :
  mOwner(NULL),
  mCurState(AS_STAND),
  mPrevState(AS_STAND),
  mPrevSlot(-1),
  mStateTime(0),
  mCurAbility(-1), // cur ability being performed
  mAnimSetID(NULL),
  mImpactIndex(-1)
{
  mStates[AS_STAND]   = new cAS_Stand;
  mStates[AS_RUN] = new cAS_Running;

  mStates[AS_ATTACK_L] = new cAS_Attack(ANIM_ATTACK_L ,COMBO_L);
  mStates[AS_ATTACK_H] = new cAS_Attack(ANIM_ATTACK_H ,COMBO_H);
  mStates[AS_ATTACK_LL] = new cAS_Attack(ANIM_ATTACK_LL ,COMBO_LL);
  mStates[AS_ATTACK_LH] = new cAS_Attack(ANIM_ATTACK_LH ,COMBO_LH);
  mStates[AS_ATTACK_HL] = new cAS_Attack(ANIM_ATTACK_HL ,COMBO_HL);
  mStates[AS_ATTACK_HH] = new cAS_Attack(ANIM_ATTACK_HH ,COMBO_HH);
  mStates[AS_ATTACK_LLL] = new cAS_Attack(ANIM_ATTACK_LLL ,COMBO_LLL);
  mStates[AS_ATTACK_LLH] = new cAS_Attack(ANIM_ATTACK_LLH ,COMBO_LLH);
  mStates[AS_ATTACK_LHL] = new cAS_Attack(ANIM_ATTACK_LHL ,COMBO_LHL);
  mStates[AS_ATTACK_LHH] = new cAS_Attack(ANIM_ATTACK_LLH ,COMBO_LHH);
  mStates[AS_ATTACK_HLL] = new cAS_Attack(ANIM_ATTACK_HLL ,COMBO_HLL);
  mStates[AS_ATTACK_HLH] = new cAS_Attack(ANIM_ATTACK_HLH ,COMBO_HLH);
  mStates[AS_ATTACK_HHL] = new cAS_Attack(ANIM_ATTACK_HHL ,COMBO_HHL);
  mStates[AS_ATTACK_HHH] = new cAS_Attack(ANIM_ATTACK_HHH,COMBO_HHH);


  mStates[AS_JUMP] = new cAS_Jump(ANIM_B_JUMP);
  mStates[AS_IN_AIR] = new cAS_InAir(ANIM_B_IN_AIR);
  mStates[AS_LAND] = new cAS_Land(ANIM_B_LAND);
  mStates[AS_DEATH] = new cAS_Death;
  mStates[AS_HITREACT] = new cAS_HitReact(ANIM_B_IMPACT_FRONT);
  mStates[AS_BLOCK] = new cAS_Block;
  mStates[AS_ATTACK_RANGED] = new cAS_RangedAttack;
  mStates[AS_OVERRIDE] = new cAS_Override();
  mStates[AS_RUN_JUMP] = new cAS_Jump(ANIM_B_RUN_JUMP);
  mStates[AS_RUN_IN_AIR] = new cAS_InAir(ANIM_B_RUN_IN_AIR);
  mStates[AS_RUN_LAND] = new cAS_Land(ANIM_B_RUN_LAND);
  mStates[AS_DODGE] = new cAS_Dodge();
  mStates[AS_EMOTE_ANGER] = new cAS_Standard(ANIM_B_EMOTE_ANGER);
  mStates[AS_KNOCKBACK] = new cAS_Knocked(ANIM_B_KNOCKBACK);
  mStates[AS_KNOCKBACK_IN_AIR] = new cAS_KnockedInAir(ANIM_B_KNOCKBACK_IN_AIR);
  mStates[AS_KNOCKBACK_LAND] = new cAS_KnockedLand(ANIM_B_KNOCKBACK_LAND);
  mStates[AS_KNOCKBACK_GETUP] = new cAS_Standard(ANIM_B_KNOCKBACK_GETUP);

  mStates[AS_KNOCKFORWARD] = new cAS_Knocked(ANIM_B_KNOCKFORWARD);
  mStates[AS_KNOCKFORWARD_IN_AIR] = new cAS_KnockedInAir(ANIM_B_KNOCKFORWARD_IN_AIR);
  mStates[AS_KNOCKFORWARD_LAND] = new cAS_KnockedLand(ANIM_B_KNOCKFORWARD_LAND);
  mStates[AS_KNOCKFORWARD_GETUP] = new cAS_Standard(ANIM_B_KNOCKFORWARD_GETUP);

  for (int ii=0;ii<NUM_ANIM_STATES;++ii)
  {
    mStates[ii]->Init(this);
  }
}

cAnimCharController::~cAnimCharController()
{
  for (int ii=0;ii<NUM_ANIM_STATES;++ii)
  {
    delete mStates[ii];
  }
}
           
void
cAnimCharController::Init(cGameObject *owner)
{
  mOwner = owner;
  mAnimSetID = owner->GetAnimSet();
  mStates[mCurState]->Enter();

  #if defined(_DEBUG)
  //CheckAnims();
  #endif
}


void            
cAnimCharController::Reset()
{
  mStateTime = 0.0f;
  mCurState = AS_STAND;
  mInput.Clear();
  mStates[mCurState]->Enter();
}

// checks to make sure all required animations are present
void
cAnimCharController::CheckAnims()
{
  if (!GetOwner()->GetTitan()->GetCheckArt())
  {
    return;
  }
  CheckAnim(ANIM_B_IDLE);
  CheckAnim(ANIM_B_MOVE_F_WALK);
  CheckAnim(ANIM_B_MOVE_F_RUN);
  CheckAnim(ANIM_B_DEATH);
  CheckAnim(ANIM_B_IMPACT_FRONT);
  CheckAnim(ANIM_B_IMPACT_BACK);
  CheckAnim(ANIM_B_BLOCK);
  CheckAnim(ANIM_ATTACK_L);

  if (mOwner->GetType()==cGameObject::OBJ_PLAYER)
  {
    CheckAnim(ANIM_B_KNOCKFORWARD);
    CheckAnim(ANIM_B_KNOCKFORWARD_IN_AIR);
    CheckAnim(ANIM_B_KNOCKFORWARD_LAND);
    CheckAnim(ANIM_B_KNOCKFORWARD_GETUP);
    CheckAnim(ANIM_B_KNOCKBACK);
    CheckAnim(ANIM_B_KNOCKBACK_IN_AIR);
    CheckAnim(ANIM_B_KNOCKBACK_LAND);
    CheckAnim(ANIM_B_KNOCKBACK_GETUP);

    CheckAnim(ANIM_B_JUMP);
    CheckAnim(ANIM_B_IN_AIR);
    CheckAnim(ANIM_B_LAND);

    CheckAnim(ANIM_B_RUN_JUMP);
    CheckAnim(ANIM_B_RUN_IN_AIR);
    CheckAnim(ANIM_B_RUN_LAND);

    CheckAnim(ANIM_B_CAST_A);
    CheckAnim(ANIM_ATTACK_H);
    CheckAnim(ANIM_ATTACK_LL);
    CheckAnim(ANIM_ATTACK_LH);
    CheckAnim(ANIM_ATTACK_HL);
    CheckAnim(ANIM_ATTACK_HH);
    CheckAnim(ANIM_ATTACK_LLL);
    CheckAnim(ANIM_ATTACK_LLH);
    CheckAnim(ANIM_ATTACK_LHL);
    CheckAnim(ANIM_ATTACK_LHH);
    CheckAnim(ANIM_ATTACK_HLL);
    CheckAnim(ANIM_ATTACK_HLH);
    CheckAnim(ANIM_ATTACK_HHL);
    CheckAnim(ANIM_ATTACK_HHH);
    CheckAnim(ANIM_ATTACK_JUMP);
    CheckAnim(ANIM_RANGED_ATTACK);

    CheckAnim(ANIM_B_TURN_90_L);
    CheckAnim(ANIM_B_TURN_90_R);
    CheckAnim(ANIM_B_TURN_180_L);
    CheckAnim(ANIM_B_TURN_180_R);
  }
};

void
cAnimCharController::CheckAnim(int32 animid)
{

  cGraphicActor *graphic = (cGraphicActor*) mOwner->GetGraphic();
  SyScene *scene = GetOwner()->GetTitan()->GetScene();
  SyCSprite* sprite = (SyCSprite*) scene->GetActorSpritePtr( graphic->GetActorHandle());

  SyAssertf(sprite->HasAnim(animid)!=0,"%s is missing required anim: %d(%s)",
            mOwner->GetName(),
            animid,
            GetAnimName(animid));
};

const char *
cAnimCharController::GetAnimName(int32 animID)
{
  switch (animID)
  {
    case ANIM_B_IDLE:
    {
      return "Idle";
    }
    case ANIM_B_MOVE_F_WALK:
    {
      return "Walk_Fwd";
    }
    case ANIM_B_MOVE_F_RUN:
    {
      return "Walk_Run";
    }
    case ANIM_B_DEATH:
    {
      return "Death";
    }

    case ANIM_B_IMPACT_FRONT:
    {
      return "Stand_TakeDmg_Fwd";
    }
    case ANIM_B_IMPACT_BACK:
    {
      return "Stand_TakeDmg_Bck";
    }
    case ANIM_B_IMPACT_FWD_EXTREME:
    {
      return "Stand_TakeDmg_Fwd_Extreme";
    }
    case ANIM_B_JUMP :
    {
      return "Jump";
    }
    case ANIM_B_IN_AIR:
    {
      return "Jump_In_Air";
    }
    case ANIM_B_LAND:
    {
      return "Jump_Land";
    }
    case ANIM_B_FALLING :
    {
      return "Fall";
    }
    case ANIM_B_LAND_HARD:
    {
      return "Land_Hard";
    }
    case ANIM_B_BLOCK:
    {
      return "Stand_Block";
    }
    case ANIM_B_KNOCKBACK:
    {
      return "Stand_KnockDown_Bck";
    }
    case ANIM_B_KNOCKBACK_IN_AIR :
    {
      return "Air_Knockdown_Bck";
    }
    case ANIM_B_KNOCKBACK_LAND:
    {
      return "Land_Knockdown_Bck";
    }
    case ANIM_B_KNOCKBACK_GETUP:
    {
      return "GetUp_Fwd";
    }

    case ANIM_B_KNOCKFORWARD:
    {
      return "Stand_KnockDown_Fwd";
    }
    case ANIM_B_KNOCKFORWARD_IN_AIR:
    {
      return "Air_KnockDown_Fwd";
    }
    case ANIM_B_KNOCKFORWARD_LAND:
    {
      return "Land_KnockDown_Fwd";
    }
    case ANIM_B_KNOCKFORWARD_GETUP:
    {
      return "GetUp_Fwd";
    }

    case ANIM_B_TURN_90_L :
    {
      return "Stand_Turn90_Lft";
    }
    case ANIM_B_TURN_90_R:
    {
      return "Stand_Turn90_Rgt";
    }
    case ANIM_B_TURN_180_L:
    {
      return "Stand_Turn180Left";
    }

    case ANIM_B_DODGE_F:
    {
      return "Dodge_Fwd";
    }
    case ANIM_B_DODGE_B:
    {
      return "Dodge_Bck";
    }
    case ANIM_B_DODGE_L:
    {
      return "Dodge_Lft";
    }
    case ANIM_B_DODGE_R:
    {
      return "Dodge_Rgt";
    }
    case ANIM_B_CAST_A:
    {
      return "CastA";
    }
    case ANIM_B_CAST_B:
    {
      return "CastB";
    }
    case ANIM_B_CAST_C:
    {
      return "CastC";
    }
  
    case ANIM_B_RUN_JUMP :
    {
      return "Run_Jump";
    }
    case ANIM_B_RUN_IN_AIR:
    {
      return "Run_Jump_In_Air";
    }
    case ANIM_B_RUN_LAND:
    {
      return "Run_Jump_Land";
    }

    case ANIM_B_DODGE_FWD_TAKEDAM:
    {
      return "Dodge_Fwd_Takedam";
    }

    case ANIM_B_COMBAT_IDLE_01:
    {
      return "Combat_Idle_01";
    }
    case ANIM_B_COMBAT_IDLE_02:
    {
      return "Combat_Idle_02";
    }
    case ANIM_B_TURN_180_R:
    {
      return "Stand_Turn180Right";
    }
    case ANIM_B_RUN_JUMP_R:
    {
      return "Run_JumpRight";
    }
    case ANIM_B_STAND_TAKEDAM_LEFT:
    {
      return "Stand_TakeDam_Left";
    }
    case ANIM_B_STAND_TAKEDAM_RIGHT:
    {
      return "Stand_Takedam_Right";
    }
    case ANIM_B_EMOTE_ANGER:
    {
      return "Emote_Anger";
    }
                      
    case ANIM_ATTACK_L :
    {
      return "Stand_Attack_L";
    }
    case ANIM_ATTACK_H:
    {
      return "Stand_Attack_H";
    }
    case ANIM_ATTACK_LL :
    {
      return "Stand_Attack_LL";
    }
    case ANIM_ATTACK_LH :
    {
      return "Stand_Attack_LH";
    }
    case ANIM_ATTACK_HL :
    {
      return "Stand_Attack_HL";
    }
    case ANIM_ATTACK_HH :
    {
      return "Stand_Attack_HH";
    }
    case ANIM_ATTACK_LLL :
    {
      return "Stand_Attack_LLL";
    }
    case ANIM_ATTACK_LLH :
    {
      return "Stand_Attack_LLH";
    }
    case ANIM_ATTACK_LHL :
    {
      return "Stand_Attack_LHL";
    }
    case ANIM_ATTACK_LHH :
    {
      return "Stand_Attack_LHH";
    }
    case ANIM_ATTACK_HLL :
    {
      return "Stand_Attack_HLL";
    }

    case ANIM_ATTACK_HLH :
    {
      return "Stand_Attack_HLH";
    }
    case ANIM_ATTACK_HHL:
    {
      return "Stand_Attack_HHL";
    }

    case ANIM_ATTACK_HHH :
    {
      return "Stand_Attack_HHH";
    }

    case ANIM_ATTACK_JUMP:
    {
      return "Stand_Attack_Jump";
    }

    case ANIM_RANGED_ATTACK :
    {
      return "Stand_Ranged_Attack";
    }
    case ANIM_RANGED_ATTACK_JUMP:
    {
      return "Jump_Ranged_Attack";
    }
    case ANIM_OVERRIDE :
    {
      return "Override";
    }
    default:
    {
      return "Unknown";
    }
  }
}


void
cAnimCharController::UpdateImpactBlend()
{
  if (mImpactIndex >= 0)
  {
    SyScene *scene = GetOwner()->GetRegistry()->GetTitan()->GetScene();
    cGraphicActor *graphic = (cGraphicActor*) GetOwner()->GetGraphic();
    SyCSprite * sprite = (SyCSprite*)scene->GetActorSpritePtr(graphic->GetActorHandle());
    float blend;
    if (mImpactTime < mImpactBlendAttack)
    {
      blend = (mImpactBlendMaxBlend/mImpactBlendAttack) * mImpactTime;
      sprite->SetPlaybackBlend(mImpactIndex,blend);
    }
    else if (mImpactTime < mImpactBlendAttack + mImpactBlendSustain)
    {
      blend = mImpactBlendMaxBlend;
      sprite->SetPlaybackBlend(mImpactIndex,blend);
    }
    else if (mImpactTime < mImpactBlendAttack + mImpactBlendSustain + mImpactBlendRelease)
    {
      float fadeout = mImpactTime - (mImpactBlendAttack + mImpactBlendSustain);
      blend = mImpactBlendMaxBlend - (mImpactBlendMaxBlend/mImpactBlendRelease) * fadeout;
      sprite->SetPlaybackBlend(mImpactIndex,blend);
    }
    else
    {
      sprite->EraseAnimPlay(mImpactIndex,*scene);
      mImpactIndex = -1;
    }
  }
}

void                    
cAnimCharController::Update(float time)
{
  cAnimCharControllerInput *input = GetInput();
  if (input->mAnimationOverride != ANIM_HANDLE_NONE)
  {
    ChangeState(AS_OVERRIDE);
  }

  mStates[mCurState]->CommonActions();
  mStates[mCurState]->Update(time);
  mStateTime += time;

  if (mImpactIndex >=0)
  {
    mImpactTime += time;

    UpdateImpactBlend();
  }
}

const char *            
cAnimCharController::GetAnimStateName() const
{
  return l_AnimStatenames[GetAnimState()];
}

void                    
cAnimCharController::ChangeState(eAnimState state)
{

  mStates[mCurState]->Exit();
  mPrevState = mCurState;
  mCurState = state;
  mStates[mCurState]->Enter();
  mStateTime = 0.0f;

  if (!mOwner->IsRemote())
  {
    NetworkSendBroadcast(mCurState);
  }
}

cAnimControllerSetting *
cAnimCharController::GetSetting()
{
  return mOwner->GetTitan()->GetAnimControllerSys()->Fetch(mAnimSetID,mCurState);
}

float           
cAnimCharController::GetDamageMultiplier(eComboType combo)
{
  int state = combo + AS_ATTACK_L; 
  cAnimControllerSetting * setting = mOwner->GetTitan()->GetAnimControllerSys()->Fetch(mAnimSetID,state);
  
  if (setting == NULL)
  {
    return 1.0f;
  }

  return setting->mDamageMultiplier;
}

float           
cAnimCharController::GetStrikethroughMultiplier(eComboType combo)
{
  int state = combo + AS_ATTACK_L; 
  cAnimControllerSetting * setting = mOwner->GetTitan()->GetAnimControllerSys()->Fetch(mAnimSetID,state);
  
  if (setting == NULL)
  {
    return 1.0f;
  }

  return setting->mStrikethroughMultiplier;
}

float           
cAnimCharController::GetRange(eComboType combo)
{
  int state = combo + AS_ATTACK_L; 
  cAnimControllerSetting * setting = mOwner->GetTitan()->GetAnimControllerSys()->Fetch(mAnimSetID,state);
  
  if (setting == NULL)
  {
    return 1.0f;
  }

  return setting->mRange;
}

void
cAnimCharController::NetworkSendBroadcast(eAnimState state)
{
  cNetAnimStatePacket packet;
  packet.mAnimState = (int)state;
  packet.mTarget = GetInput()->mTarget;
  packet.mActionTarget = GetInput()->mActionTarget;

  char buf[1024];
  int len = packet.PackBuffer(buf, sizeof(buf));
  mOwner->GetTitan()->GetPeeringNetwork()->ObjectBroadcast(mOwner->GetID(), buf, len);
}                         

void
cAnimCharController::NetworkReceiveBroadcast(const char *state, int statelen)
{
  cNetAnimStatePacket pPacket;
  pPacket.UnpackBuffer(state, statelen);

  SyAssertf(mOwner->IsRemote(), "Owner receiving network instructions?"); 
  ChangeState((eAnimState)pPacket.mAnimState);
  GetInput()->mTarget = pPacket.mTarget;
  GetInput()->mActionTarget = pPacket.mActionTarget;
}

void
cAnimCharController::PlayImpact(tGameObjectID attacker)
{
  SyScene *scene = GetOwner()->GetRegistry()->GetTitan()->GetScene();
  cGraphicActor *graphic = (cGraphicActor*) GetOwner()->GetGraphic();
  SyCSprite * sprite = (SyCSprite*)scene->GetActorSpritePtr(graphic->GetActorHandle());
  if (mImpactIndex >= 0)
  {
    sprite->EraseAnimPlay(mImpactIndex,*scene);
  }
  mImpactIndex = sprite->AppendAnimPlay(ANIM_B_IMPACT_FRONT,0,*scene);
  sprite->SetPlaybackBlend(mImpactIndex,0.0f);
  mImpactTime = 0.0f;
}

void
cAnimCharController::ExtractImpact()
{
}

void
cAnimCharController::InsertImpact()
{
  if (mImpactIndex < 0)
  {
    // no impact to resume
    return;
  }
  SyScene *scene = GetOwner()->GetRegistry()->GetTitan()->GetScene();
  cGraphicActor *graphic = (cGraphicActor*) GetOwner()->GetGraphic();
  SyCSprite * sprite = (SyCSprite*)scene->GetActorSpritePtr(graphic->GetActorHandle());
  
  mImpactIndex = sprite->AppendAnimPlay(ANIM_B_IMPACT_FRONT,0,*scene);
  sprite->SetPlaybackBlend(mImpactIndex,0.0f);

}

void
cAnimCharController::ClearImpact()
{
 mImpactIndex = -1;

}

bool            
cAnimCharController::IsBlocking() const
{
  if (mCurState == AS_BLOCK)
  {
    return ((cAS_Block*)mStates[AS_BLOCK])->IsActive();
  }
  return false;
}

//------------------------------------ cAnimState

cAnimState::cAnimState():
  mOwner(NULL),
  mTurning(false),
  mPlayingTurnAnim(false)
{
}

cGameObject *
cAnimState::GetGameObject()
{
  return mOwner->GetOwner();
}

SyScene *
cAnimState::GetScene()
{
  return mOwner->GetOwner()->GetRegistry()->GetTitan()->GetScene();
}

SyCSprite   *
cAnimState::GetSprite()
{
  cGameObject *obj = GetGameObject();
  cGraphicActor *graphic = (cGraphicActor*) obj->GetGraphic();

  return  (SyCSprite*) GetScene()->GetActorSpritePtr( graphic->GetActorHandle());
}


cAnimCharControllerInput *
cAnimState::GetInput()
{
  return mOwner->GetInput();
}

bool
cAnimState::PlayAnim(int32 animid, int32 blendtime)
{
  SyCSprite* sprite = GetSprite();
  SyScene *scene = GetScene();
  int curIndex;

  if (!sprite->HasAnim(animid))
  {
    //SyAssertf(0,"Missing animation %s for %s",mOwner->GetAnimStateName(),mOwner->GetOwner()->GetName());
    animid = ANIM_ATTACK_L;
    if (!sprite->HasAnim(animid))
    {
      animid = ANIM_B_IDLE;
      if (!sprite->HasAnim(animid))
      {
        // ok this is too much i give up
        mPlaybackIndex = 0;
        return false;
      }
    }

  }
  //mOwner->ClearImpact();
  mBlendIndex = -1;
  if (sprite->GetNumPlay() > 0)
  {
    int impactIndex = mOwner->GetImpactIndex();
    int numAnims = 1; // num anims to save from those currently playing
    // clear out impact anim for current pose
    if (impactIndex >= 0)
    {
      sprite->SetPlaybackBlend(impactIndex,0.0f);
      ++numAnims;
    }

    mBlendIndex = sprite->GetFirstPlay();
    sprite->SetPlaybackToCurrentPose(mBlendIndex,*scene); 

    while (sprite->GetNumPlay() > numAnims)
    {
      curIndex = sprite->GetFirstPlay();
      while (curIndex == mBlendIndex || curIndex==impactIndex)
      {
        curIndex = sprite->GetNextPlay(curIndex);
      }
      sprite->ErasePlay(curIndex,*scene);
    }

    mPlaybackIndex = sprite->PrependAnimPlay(animid,0,*scene);

    sprite->SetPlaybackBlendMode(mBlendIndex,SYANIMBLENDMODE_INVERT_TIMED_INTERP,blendtime,*scene);

    cAnimControllerSetting * setting = mOwner->GetSetting();

    float play_speed = 1.0f;
    if (setting != NULL)
    {
      play_speed = setting->mSpeed;
    }
    sprite->SetPlaySpeed(mPlaybackIndex,play_speed,*scene);
    mAnimDuration = sprite->GetPlayDuration(mPlaybackIndex,play_speed,*scene);

    if (impactIndex >= 0)
    {
      mOwner->UpdateImpactBlend();
    }
  }
  else
  {
    // sprite->Clear();
    sprite->EraseAllPlay(*scene);
    mPlaybackIndex = sprite->PrependAnimPlay(animid,0,*scene);
  }
  return true;

}



float       
cAnimState::TurnTo(float32 angle,float32 turnamount,float32 turnThreshold)
{
  cGameObject *obj = GetGameObject();

  // check for turn...
  float cur_heading = obj->GetHeading();
  float delta_ang = AngleDifference(angle,cur_heading);

  // add a little dead zone to the turn
  if (delta_ang > turnThreshold + EPSILON)
  {
    delta_ang -= turnThreshold;
  }
  else if (delta_ang < -turnThreshold - EPSILON)
  {
    delta_ang += turnThreshold;
  }
  else
  {
    mTurning = false;
    delta_ang = 0.0f;
  }

  if (!mTurning)
  {
    if (!FloatsEqual(delta_ang,0.0f))
    {
      // start turning
      mTurning = true;
    }
  }
  
  if (mTurning)
  {
    cur_heading += GetAngleAdjustment(delta_ang,turnamount);
    cur_heading = AngleNormalize(cur_heading);
    obj->SetHeading(cur_heading);
  }
  return delta_ang;
}


void 
cAnimState::CommonActions() // take potions, pick up items
{
  ActivateObject();
}

void
cAnimState::ActivateObject()
{
  cAnimCharControllerInput *input = GetInput();
  if (input->mActionRequest == true)
  {
    cGameObject *obj = GetGameObject();
    input->mActionRequest = false;
    ((cStatsCharacter *)obj->GetStats())->ActivateObject(input->mActionTarget);
    input->mActionTarget = ID_NONE;
  }
}

void
cAnimState::ClearInput()
{
  cAnimCharControllerInput *input = GetInput();
  float heading = input->mHeadingRequest;
  input->Clear();
  input->mHeadingRequest = heading;
}


bool
cAnimState::TurnAnimsCompatable(int playing_anim,int next_anim)
{
  switch (playing_anim)
  {
  case ANIM_B_TURN_180_L:
  case ANIM_B_TURN_90_L:
    if (next_anim == ANIM_B_TURN_180_L || next_anim == ANIM_B_TURN_90_L)
    {
      return true;
    }
  case ANIM_B_TURN_180_R:
  case ANIM_B_TURN_90_R:
    if (next_anim == ANIM_B_TURN_180_R || next_anim == ANIM_B_TURN_90_R)
    {
      return true;
    }

  }
  return false;
}
int 
cAnimState::GetTurnAnim(float delta)
{
  SyCSprite* sprite = GetSprite();

  if ( delta > SY_DEG_TO_RAD(135.0f )) 
  {
    if ( sprite->HasAnim(ANIM_B_TURN_180_L))
    {
      return ANIM_B_TURN_180_L;
    }
  }
  if ( delta < SY_DEG_TO_RAD(-135.0f ))
  {
    if ( sprite->HasAnim(ANIM_B_TURN_180_R))
    {
      return ANIM_B_TURN_180_R;
    }
  }
  else if (delta > SY_DEG_TO_RAD(1))
  {
    if ( sprite->HasAnim(ANIM_B_TURN_90_L))
    {
      return ANIM_B_TURN_90_L;
    }
  }
  else if (delta < SY_DEG_TO_RAD(-1))
  {
    if ( sprite->HasAnim(ANIM_B_TURN_90_R))
    {
      return ANIM_B_TURN_90_R;
    }
  }

  return -1;
}
//------------------------------------ cAS_Stand

cAS_Stand::cAS_Stand() :
mTurnSpeed(45.0f)
{
}

void 
cAS_Stand::Enter()
{
  // play standing anim...
  PlayAnim(ANIM_B_IDLE);

}

void 
cAS_Stand::Update(float time)
{
  cGameObject *obj = GetGameObject();
  
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }

  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }


  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  if (input->mDodging)
  {
    mOwner->ChangeState(AS_DODGE);
    return;
  }

  cStatsCharacter *stats = (cStatsCharacter *)obj->GetStats();
  if (input->mBlockRequest && stats->GetBlock() >= 0)
  {
    mOwner->ChangeState(AS_BLOCK);
    return;
  }

  if (input->mAttackRequestL)
  {                      
    mOwner->ChangeState(AS_ATTACK_L);
    return;
  }

  if (input->mAttackRequestS)
  {                      
    mOwner->ChangeState(AS_ATTACK_H);
    return;
  }

  if (input->mAttackRequestRanged)
  {                      
    mOwner->ChangeState(AS_ATTACK_RANGED);
    return;
  }

  if (input->mJumpRequest)
  {
    input->mJumpRequest = false;
    mOwner->ChangeState(AS_JUMP);
    return;
  }

  if (input->mFall)
  {
    input->mFall = false;
    mOwner->ChangeState(AS_IN_AIR);
    return;
  }

  if (input->mEmoteAnger)
  {
    input->mEmoteAnger = false;
    mOwner->ChangeState(AS_EMOTE_ANGER);
    return;
  }

  if (input->mSpeedRequest > EPSILON && !mTurning)  // start turning
  {
    mTurnTo = input->mHeadingRequest;
    PlayTurnAnim();
  }
  else if (input->mSpeedRequest < EPSILON && !mTurning)
  {
    TurnTo(input->mHeadingRequest,time*mTurnSpeed,mTurnThreshold);
  }

  if (mTurning)
  {

    PlayTurnAnim();

#if 0
    SyCSprite* sprite = GetSprite();

    if (sprite->IsPlaybackStopped(mPlaybackIndex))
    {
      PlayTurnAnim();
    }
    else if (input->mSpeedRequest > EPSILON )
    {
      float delta = AngleDifference(input->mHeadingRequest,mTurnTo);
      static const float RESTART_ANIM_ANGLE = SY_DEG_TO_RAD(45.0f);
      if (delta < -RESTART_ANIM_ANGLE || delta > RESTART_ANIM_ANGLE)
      {
        mTurnTo = input->mHeadingRequest;
        PlayTurnAnim();
      }
    }
#endif
    TurnTo(input->mHeadingRequest,time*mTurnSpeed,mTurnThreshold);


  }
  else if (mPlayingTurnAnim)
  {
    // stop playing turn anim
    PlayAnim(ANIM_B_IDLE);
    mPlayingTurnAnim = false;
  }

  if (!FloatsEqual(input->mSpeedRequest,0.0f))
  {
    bool start_running = false;

    if (!mTurning)
    {
      start_running = true;
    }
    else
    {
      float delta = AngleDifference(input->mHeadingRequest,obj->GetHeading());

      if (delta < SY_DEG_TO_RAD(30) && delta > -SY_DEG_TO_RAD(30))
      {
        start_running = true;
      }
    }
    if (start_running)
    {
      mOwner->ChangeState(AS_RUN);
    }
  }

}

void
cAS_Stand::PlayTurnAnim()
{
  float delta = TurnTo(mTurnTo,0,mTurnThreshold); // don't actually turn, but determine delta

  SyCSprite* sprite = GetSprite();


  int turn_anim = GetTurnAnim(delta);

  if (turn_anim != -1)
  {
      if (sprite->IsPlaybackStopped(mPlaybackIndex) || !TurnAnimsCompatable(sprite->GetPlayAnimID(mPlaybackIndex), turn_anim))
      {
        PlayAnim(turn_anim,100);
        

        SyScene *scene = GetScene();
        sprite->SetPlaySpeed(mPlaybackIndex,mTurnSpeedMultiplier,*scene);

        mAnimDuration /= mTurnSpeedMultiplier;
        if (delta < SY_DEG_TO_RAD(90) && delta > SY_DEG_TO_RAD(-90))
        {
          mTurnSpeed = SY_DEG_TO_RAD(90)/mAnimDuration;
        }
        else
        {
          mTurnSpeed = delta/mAnimDuration;
        }
        if (mTurnSpeed < 0.0f)
        {
          mTurnSpeed = -mTurnSpeed;
        }
      }
      mPlayingTurnAnim = true;
  }
}

void 
cAS_Stand::RegisterTuningVariables()
{
  gTuningSys.AddFloat(&mTurnSpeedMultiplier,"Stand_TurnSpeed_Multiplier");
}

//------------------------------------ cAS_Running

cAS_Running::cAS_Running() 
{
}

void 
cAS_Running::Enter()
{
  SyCSprite* sprite = GetSprite();
  SyScene *scene = GetScene();
  int curIndex;

  mBlendIndex = -1;
  mOwner->ClearImpact();
  if (sprite->GetNumPlay() > 0)
  {
    mBlendIndex = sprite->GetFirstPlay();
    sprite->SetPlaybackToCurrentPose(mBlendIndex,*scene); 

    while (sprite->GetNumPlay()>1)
    {
      curIndex = sprite->GetFirstPlay();
      if (curIndex == mBlendIndex)
      {
        curIndex = sprite->GetNextPlay(curIndex);
      }
      sprite->ErasePlay(curIndex,*scene);
    }

    if (sprite->HasAnim(ANIM_B_MOVE_F_WALK) && sprite->HasAnim(ANIM_B_MOVE_F_RUN))
    {
      mPlaybackIndex = sprite->PrependAnimPlay(ANIM_B_MOVE_F_RUN,0,*scene);
      mWalkPlaybackIndex     = sprite->PrependAnimPlayWithSync(ANIM_B_MOVE_F_WALK,0,mPlaybackIndex,*scene);
      cAnimCharControllerInput *input = GetInput();     
      float blendValue = input->mSpeedRequest;      

      cPhysicsAnimated *physics = (cPhysicsAnimated*) GetGameObject()->GetPhysics();

      if (physics->IsPushing())
      {
        blendValue = 0.0f; // can only walk while pushing
      }
      sprite->SetPlaybackBlend(mPlaybackIndex,blendValue);
      mCurrentBlend = blendValue;

    }
    else
    {
      SyAssertf(0,"Character Missing Animation");
    }

    sprite->SetPlaybackBlendMode(mBlendIndex,SYANIMBLENDMODE_INVERT_TIMED_INTERP,250,*scene);
  }
  else
  {
    if (sprite->HasAnim(ANIM_B_MOVE_F_WALK) && sprite->HasAnim(ANIM_B_MOVE_F_RUN))
    {
      mPlaybackIndex         = sprite->PrependAnimPlay(ANIM_B_MOVE_F_RUN,0,*scene);
      mWalkPlaybackIndex     = sprite->PrependAnimPlayWithSync(ANIM_B_MOVE_F_WALK,0,mPlaybackIndex,*scene);
      cAnimCharControllerInput *input = GetInput();
      float blendValue = input->mSpeedRequest;
      sprite->SetPlaybackBlend(mPlaybackIndex,blendValue);
    }
    else
    {
      SyAssertf(0,"Character Missing Animation");
    }
  }
}

void 
cAS_Running::Update(float time)
{
  // loop anim...  Is this necessary?
  cGameObject *obj = GetGameObject();
  SyCSprite* sprite = GetSprite();
  SyScene *scene = GetScene();

  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mFall)
  {
    input->mFall = false;
    mOwner->ChangeState(AS_IN_AIR);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  cStatsCharacter *stats = (cStatsCharacter *)obj->GetStats();
  if (input->mBlockRequest && stats->GetBlock() >= 0)
  {
    mOwner->ChangeState(AS_BLOCK);
    return;
  }


  // stopping?
  if (input->mSpeedRequest < EPSILON)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }

  if (input->mAttackRequestL)
  {                      
    mOwner->ChangeState(AS_ATTACK_L);
    return;
  }

  if (input->mAttackRequestS)
  {                      
    mOwner->ChangeState(AS_ATTACK_H);
    return;
  }

  if (input->mAttackRequestRanged)
  {                      
    mOwner->ChangeState(AS_ATTACK_RANGED);
    return;
  }

  if (input->mJumpRequest)
  {

    input->mJumpRequest = false;

    if (mOwner->GetStateTime() > 0.25f)
    {
      mOwner->ChangeState(AS_RUN_JUMP);
    }
    else
    {
      mOwner->ChangeState(AS_JUMP);
    }

    return;
  }

  float blendTarget = input->mSpeedRequest;


  cPhysicsAnimated *physics = (cPhysicsAnimated*) GetGameObject()->GetPhysics();

  if (physics->IsPushing())
  {
    blendTarget = 0.0f; // can only walk while pushing
  }

  float blendAmount = mBlendSpeed *time;

  if (mCurrentBlend + blendAmount < blendTarget  )
  {
    mCurrentBlend += blendAmount;
  }
  else if (mCurrentBlend - blendAmount > blendTarget)
  {
    mCurrentBlend -= blendAmount;
  }
  else
  {
    mCurrentBlend = blendTarget;
  }
  sprite->SetPlaybackBlend(mPlaybackIndex,mCurrentBlend);
  float playspeed = (mCurrentBlend * (mRunSpeed-mWalkSpeed)) + mWalkSpeed;
  if (mWalkPlaybackIndex >= 0)
  {
    sprite->SetPlaySpeed(mWalkPlaybackIndex,playspeed,*scene);
  }
  if (mPlaybackIndex >= 0)
  {
    sprite->SetPlaySpeed(mPlaybackIndex,playspeed,*scene);
  }

  // check for turn...
  float delta_ang = TurnTo(input->mHeadingRequest,time*mTurnSpeed,mTurnThreshold);
  if (delta_ang > mStopThreshold)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }
}

void 
cAS_Running::RegisterTuningVariables()
{
  gTuningSys.AddAngle(&mTurnThreshold,"Run_Turn_Threshold");
  gTuningSys.AddAngle(&mTurnSpeed,"Run_Turn_Speed");
  gTuningSys.AddAngle(&mStopThreshold,"Run_Stop_Threshold");
  gTuningSys.AddFloat(&mRunSpeed,"Run_Speed");
  gTuningSys.AddFloat(&mWalkSpeed,"Walk_Speed");
}

//------------------------------------ cAS_Standard

void 
cAS_Standard::Enter()
{
  
  PlayAnim(mAnimID);

}

void 
cAS_Standard::Update(float time)
{
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }

  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }

  float prev_time = mOwner->GetStateTime();
  float cur_time = prev_time + time;

  float combo_end = 0.8f * mAnimDuration;
  if (prev_time <= combo_end && cur_time  > combo_end)
  {
    ClearInput();
  } 
}

//------------------------------------ cAS_Land

void 
cAS_Land::Enter()
{

  PlayAnim(mAnimID);

}

void 
cAS_Land::Update(float time)
{
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }

  float prev_time = mOwner->GetStateTime();
  float cur_time = prev_time + time;

  float combo_end = 0.8f * mAnimDuration;
  if (prev_time <= combo_end && cur_time  > combo_end)
  {
    ClearInput();
  } 

  if (input->mSpeedRequest > EPSILON || mTurning)
  {
    TurnTo(input->mHeadingRequest,time*cAS_Running::mTurnSpeed,cAS_Stand::mTurnThreshold);
  }

}


//------------------------------------ cAS_HitReact
void 
cAS_HitReact::Update(float time)
{ 
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    mOwner->ChangeState(AS_HITREACT);
    return;
  }

  if (input->mAttackRequestL)
  {                      
    mOwner->ChangeState(AS_ATTACK_L);
    return;
  }

  if (input->mAttackRequestS)
  {                      
    mOwner->ChangeState(AS_ATTACK_H);
    return;
  }

  if (input->mAttackRequestRanged)
  {                      
    mOwner->ChangeState(AS_ATTACK_RANGED);
    return;
  }


  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }
}
//------------------------------------ cAS_Attack

void 
cAS_Attack::Enter()
{
  cAnimCharControllerInput *input = GetInput();

  mTarget = input->mTarget;
  mTargetAngle = input->mTargetAngle;

  input->mAttackRequestL = false;
  input->mAttackRequestS = false;
  mComboL = false;
  mComboS = false;
  PlayAnim(mAnimID);

}

void 
cAS_Attack::Exit()
{
  cAnimCharControllerInput *input = GetInput();
  input->mAttackRequestL = false;
  input->mAttackRequestS = false;
}

void 
cAS_Attack::Update(float time)
{
  SyCSprite* sprite = GetSprite();

  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    if (ShouldPlayImpact())
    {
      mOwner->PlayImpact(input->mHitReactTarget);
    }
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }


  if (mTarget != ID_NONE)
  {
    cGameObject *obj = GetGameObject();
    cGameObject *target = obj->GetRegistry()->Fetch(input->mTarget);
    if (target != NULL)
    {
      float heading = obj->GetHeadingTowards(target);
      TurnTo(heading,time*mTurnSpeed,0.0f);
    }
  }
  else
  {
    TurnTo(mTargetAngle,time*mTurnSpeed,0.0f);
  }

  float prev_time = mOwner->GetStateTime();
  float attack_time = 0.5f * mAnimDuration;
  float combo_start = 0.3f * mAnimDuration;
  float combo_end = 0.9f * mAnimDuration;
  cAnimControllerSetting * setting = mOwner->GetSetting();
  
  if (setting != NULL)
  {
    attack_time = setting->mActionFrame  * mAnimDuration;
    combo_start = setting->mComboWindowStart * mAnimDuration;
    combo_end = setting->mComboWindowEnd * mAnimDuration;
  }

  float cur_time = prev_time + time;


  if (prev_time < attack_time && cur_time >= attack_time )
  {
    // hit event occurs this time
    AttackFrame();
  }

  if (cur_time <  combo_start)
  {
    // ignore button presses before window
    input->mAttackRequestL = false;
    input->mAttackRequestS = false;
  }
  else if (cur_time  < combo_end)
  {
    if (input->mAttackRequestL)
    {
      mComboL = true;
      mComboS = false;
      input->mAttackRequestL = false;
    }
    else if (input->mAttackRequestS)
    {
      mComboS = true;
      mComboL = false;
      input->mAttackRequestS = false;
    }
  }
  else if (prev_time <= combo_end && cur_time  > combo_end)
  {
    eAnimState next_combo = NUM_ANIM_STATES;
    if (mComboL)
    {
      next_combo = GetNextCombo_Light();
    }
    else if (mComboS)
    {
      next_combo = GetNextCombo_Strong();
    }

    if (next_combo != NUM_ANIM_STATES)
    {
      mOwner->ChangeState(next_combo);
      return;
    }
    ClearInput();
   
  }   

}

bool
cAS_Attack::ShouldPlayImpact()
{
  switch (mAttackIndex)
  {
    case COMBO_LLH:
    case COMBO_LHL:
    case COMBO_LHH:
    case COMBO_HHL:
    case COMBO_HLH:
    case COMBO_HLL:
      return false;
  }
  return true;
}

void
cAS_Attack::AttackFrame()
{
  cAnimControllerSetting *setting = mOwner->GetSetting();
  cAnimCharControllerInput *input = GetInput();
  Titan *titan = mOwner->GetOwner()->GetTitan();
  cGameObject *obj = GetGameObject();
  static const float l_KNOCKBACK_XZ = 8.0f;
  static const float l_KNOCKBACK_Y = 3.5f;

  switch(setting->mAttackType)
  {
    case AT_SINGLE:
    {
      if (input->mTarget != ID_NONE)
      {
        ((cStatsCharacter *)obj->GetStats())->AttackFrame(input->mTarget,mAttackIndex);

        if (setting->mAttackEffect == AE_KNOCKBACK)
        {
          cGameEffect_Knockback knockback(titan);
          knockback.SetLocation(obj->GetLocation());
          knockback.SetAmount(l_KNOCKBACK_XZ, l_KNOCKBACK_Y);
          knockback.OnEnter(input->mTarget);
        }
      }
      return;
    }
    break;
    case AT_ARC:
    {
    }
    break;
    case AT_EXPLOSION:
    {
      cAreaEffect_Burst *radius = new cAreaEffect_Burst(titan);
      radius->SetSource(obj->GetID());

      radius->SetMaxRadius(setting->mRange);

      radius->SetSpeed(20.0f);

      radius->SetLocation(obj->GetLocation());

      cGameEffect_Attack *attack = new cGameEffect_Attack(titan);

      attack->SetSource(obj->GetID());
      attack->SetAttackIndex(mAttackIndex);

      radius->AddGameEffect(attack);

      if (setting->mAttackEffect == AE_KNOCKBACK)
      {
        cGameEffect_Knockback *knockback = new cGameEffect_Knockback(titan);
        knockback->SetLocation(obj->GetLocation());
        knockback->SetAmount(l_KNOCKBACK_XZ, l_KNOCKBACK_Y);
        radius->AddGameEffect(knockback);
      }

      titan->GetAreaEffectSys()->Add(radius);
    }
    break;
    case AT_CHARGE:
    {
    }
    break;
    default:
    SyAssert(0);
    break;
  }
};


eAnimState 
cAS_Attack::GetNextCombo_Strong()
{
  switch(mOwner->GetAnimState())
  {
    case AS_ATTACK_L:
    {
      return AS_ATTACK_LH;
    }
    case AS_ATTACK_H:
    case AS_ATTACK_HHH:
    {
      return AS_ATTACK_HH;
    }
    case AS_ATTACK_LL:
    {
      return AS_ATTACK_LLH;
    }
    case AS_ATTACK_LH:
    {
      return AS_ATTACK_LHH;
    }
    case AS_ATTACK_HL:
    {
      return AS_ATTACK_HLH;
    }
    case AS_ATTACK_HH:
    {
      return AS_ATTACK_HHH;
    }
    default:
    case AS_ATTACK_RANGED:
    case AS_ATTACK_LLL:
    case AS_ATTACK_LLH:
    case AS_ATTACK_LHL:
    case AS_ATTACK_LHH:
    case AS_ATTACK_HLL:
    case AS_ATTACK_HLH:
    case AS_ATTACK_HHL:
      return NUM_ANIM_STATES;
  }
}

eAnimState 
cAS_Attack::GetNextCombo_Light()
{
  switch(mOwner->GetAnimState())
  {
    case AS_ATTACK_L:
    case AS_ATTACK_LLL:
    {
      return AS_ATTACK_LL;
    }
    case AS_ATTACK_H:
    {
      return AS_ATTACK_HL;
    }
    case AS_ATTACK_LL:
    {
      return AS_ATTACK_LLL;
    }
    case AS_ATTACK_LH:
    {
      return AS_ATTACK_LHL;
    }
    case AS_ATTACK_HL:
    {
      return AS_ATTACK_HLL;
    }
    case AS_ATTACK_HH:
    {
      return AS_ATTACK_HHL;
    }
    default:
    case AS_ATTACK_RANGED:
    case AS_ATTACK_LLH:
    case AS_ATTACK_LHL:
    case AS_ATTACK_LHH:
    case AS_ATTACK_HLL:
    case AS_ATTACK_HLH:
    case AS_ATTACK_HHL:
    case AS_ATTACK_HHH:
      return NUM_ANIM_STATES;
  }
}

//------------------------------------ cAS_Jump

void 
cAS_Jump::Enter()
{
  PlayAnim(mAnimID);
  cAnimCharControllerInput *input = GetInput();

  mJumpVel.Y = mJumpUp;
  if (mOwner->GetAnimState()==AS_RUN_JUMP)
  {
    mJumpVel.X = SY_SIN(input->mHeadingRequest) * mJumpSpeed * input->mSpeedRequest;
    mJumpVel.Z = SY_COS(input->mHeadingRequest) * mJumpSpeed * input->mSpeedRequest;
  }
  else
  {
    mJumpVel.X = 0;
    mJumpVel.Z = 0;
  }
}

void 
cAS_Jump::Update(float time)
{
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();


  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK_IN_AIR);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {                              
    SyVect3 jump;

    cGameObject *obj = GetGameObject();
    ((cPhysicsAnimated *) obj->GetPhysics())->Jump(mJumpVel);

    if (mAnimID == ANIM_B_JUMP)
    {
      mOwner->ChangeState(AS_IN_AIR);
    }
    else
    {
      mOwner->ChangeState(AS_RUN_IN_AIR);
    }
    return;
  }

  cAnimControllerSetting * setting = mOwner->GetSetting();
  float combo_end = 0.8f * mAnimDuration;
  if (setting != NULL)
  {
    combo_end = setting->mComboWindowEnd * mAnimDuration;
  }
  float prev_time = mOwner->GetStateTime();
  float cur_time = prev_time + time;

  if (prev_time <= combo_end && cur_time  > combo_end)
  {
    ClearInput();
  }   
}

void 
cAS_Jump::RegisterTuningVariables()
{
  gTuningSys.AddFloat(&mJumpUp,"Jump_Up");
  gTuningSys.AddFloat(&mJumpSpeed,"Jump_Speed");
}


//------------------------------------ cAS_InAir

void 
cAS_InAir::Enter()
{
  if (GetSprite()->HasAnim(mAnimID))
  {
    // don't play for npcs, they're not meant to fall?
    PlayAnim(mAnimID);
  }
}

void 
cAS_InAir::Update(float time)
{
  static const float ON_GROUND_HEIGHT = 0.001f;
  static const float ON_GROUND_VEL = 0.001f;
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  // mario style jump
  if (input->mSpeedRequest > 0.0f)
  {
    SyVect3 impulse(0,0,0);

    impulse.X += SY_SIN(input->mHeadingRequest) * mJumpControl* input->mSpeedRequest;
    impulse.Y = 0.0f;
    impulse.Z += SY_COS(input->mHeadingRequest) * mJumpControl * input->mSpeedRequest;
    cGameObject *obj = GetGameObject();
    ((cPhysicsAnimated *) obj->GetPhysics())->Impulse(impulse);
  }
  
  // check for turn...
  TurnTo(input->mHeadingRequest,time*mTurnSpeed,0.0f);

  if (input->mHeight <= ON_GROUND_HEIGHT &&
      input->mYVel <= ON_GROUND_VEL)
  {
    if (mAnimID == ANIM_B_IN_AIR)
    {
      mOwner->ChangeState(AS_LAND);
    }
    else
    {
      mOwner->ChangeState(AS_RUN_LAND);
    }
    return;
  }
  ClearInput();
}

void 
cAS_InAir::RegisterTuningVariables()
{
  gTuningSys.AddAngle(&mTurnSpeed,"Jump_TurnSpeed");
  gTuningSys.AddFloat(&mJumpControl,"Jump_Control");
}


//------------------------------------ cAS_Death

void 
cAS_Death::Enter()
{
  PlayAnim(ANIM_B_DEATH);
}

void 
cAS_Death::Update(float time)
{
  // you dead man
}

//------------------------------------ cAS_Block

void 
cAS_Block::Enter()
{
  PlayAnim(ANIM_B_BLOCK); // Temp until i get a real anim
}

void 
cAS_Block::Update(float time)
{
  cAnimCharControllerInput *input = GetInput();
  cGameObject *obj = GetGameObject();
  cStatsCharacter *stats = (cStatsCharacter *)obj->GetStats();
  
  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }

  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (!input->mBlockRequest || stats->GetBlock() < 0)
  {
    input->mBlockRequest = false;
    mOwner->ChangeState(AS_STAND);
    return;
  }

  TurnTo(input->mHeadingRequest,time*mTurnSpeed,0.0f);
}

bool 
cAS_Block::IsActive()
{
  cAnimControllerSetting * setting = mOwner->GetSetting();
  
  float block_time = 0.5f;
  if (setting != NULL)
  {
    block_time = setting->mActionFrame;
  }

  return (mOwner->GetStateTime() >= block_time);
}
//------------------------------------ cAS_RangedAttack

void 
cAS_RangedAttack::Enter()
{
  cAnimCharControllerInput *input = GetInput();

  mTarget = input->mTarget;
  mTargetAngle = input->mTargetAngle;
  mTargetRange = input->mTargetRange;

  input->mAttackRequestRanged = false;
  //PlayAnim(ANIM_RANGED_ATTACK);
  PlayAnim(ANIM_ATTACK_L);

}

void 
cAS_RangedAttack::Update(float time)
{
  SyCSprite* sprite = GetSprite();

  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }


  if (mTarget != ID_NONE)
  {
    cGameObject *obj = GetGameObject();
    cGameObject *target = obj->GetRegistry()->Fetch(input->mTarget);
    if (target != NULL)
    {
      float heading = obj->GetHeadingTowards(target);
      TurnTo(heading,time*mTurnSpeed,0.0f);
    }
  }
  else
  {
    TurnTo(mTargetAngle,time*mTurnSpeed,0.0f);
  }

  float prev_time = mOwner->GetStateTime();
  float attack_time = 0.5f;
  
  cAnimControllerSetting * setting = mOwner->GetSetting();
  
  if (setting != NULL)
  {
    attack_time = setting->mActionFrame;
  }

  float cur_time = prev_time + time;

  if (prev_time < attack_time && cur_time >= attack_time )
  {
    // create projectile
    cGameObject *obj = GetGameObject();
    
    if (ID_NONE != input->mTarget)
    {
      cGameObject *target = obj->GetRegistry()->Fetch(input->mTarget);
      // note that target may be NULL (just shooting in current heading direction)
      if (target)
      {
        ((cStatsCharacter *)obj->GetStats())->Shoot(target);
      }
    }
    else
    {
      ((cStatsCharacter *)obj->GetStats())->Shoot(obj->GetLocation(), input->mTargetAngle, input->mTargetRange);
    }
  }

  float combo_end = 0.8f * mAnimDuration;
  if (setting != NULL)
  {
    combo_end = setting->mComboWindowEnd * mAnimDuration;
  }

  if (prev_time <= combo_end && cur_time  > combo_end)
  {
    ClearInput();
  }   
}
//------------------------------------ cAS_Override

void 
cAS_Override::Enter()
{
  SyCSprite* sprite = GetSprite();
  SyScene *scene = GetScene();
  cAnimCharControllerInput *input = GetInput();
  int curIndex;

  mOwner->ClearImpact();
  mBlendIndex = -1;
  if (sprite->GetNumPlay() > 0)
  {
    mBlendIndex = sprite->GetFirstPlay();
    sprite->SetPlaybackToCurrentPose(mBlendIndex,*scene); 

    while (sprite->GetNumPlay()>1)
    {
      curIndex = sprite->GetFirstPlay();
      if (curIndex == mBlendIndex)
      {
        curIndex = sprite->GetNextPlay(curIndex);
      }
      sprite->ErasePlay(curIndex,*scene);
    }

    //mPlaybackIndex = sprite->PrependAnimPlay(animid,0,*scene);
    mPlaybackIndex = sprite->PrependPlay(input->mAnimationOverride,ANIM_OVERRIDE,*scene);
    static const int blendtime = 250;
    sprite->SetPlaybackBlendMode(mBlendIndex,SYANIMBLENDMODE_INVERT_TIMED_INTERP,blendtime,*scene);
    input->mAnimationOverride = ANIM_HANDLE_NONE;
  }
  else
  {
    // sprite->Clear();
    sprite->EraseAllPlay(*scene);
    //mPlaybackIndex = sprite->PrependAnimPlay(animid,0,*scene);
    mPlaybackIndex = sprite->PrependPlay(input->mAnimationOverride,ANIM_OVERRIDE,*scene);
    input->mAnimationOverride = ANIM_HANDLE_NONE;
  }
}


void 
cAS_Override::Update(float time)
{
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();

  if (input->mOverrideCancelImmediate)
  {
    mOwner->ChangeState(AS_STAND);
    input->mOverrideCancelImmediate=false;
    input->mOverrideCancelEnd=false;
    return;
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0 && input->mOverrideCancelEnd)
  {
    cPhysicsAnimated *physics = (cPhysicsAnimated*) GetGameObject()->GetPhysics();
    physics->SetLocomotion(cPhysics::LOCO_ANIMATION);

    /*
     * Cut scene has ended - set the actor to the absolute last frame of the
     * cut scene animation.
     */
    SyScene *scene = GetScene();
    cGameObject* gameObject = GetGameObject();
    cGraphicCharacter* gameGraphicCharacter = prop_cast<cGraphicCharacter*>(gameObject->GetGraphic());
    SyActorHandle actorHandle = gameGraphicCharacter->GetActorHandle();
    SyCSprite* cSprite = (SyCSprite*) scene->GetActorSpritePtr (actorHandle);
    SyMatrix44 transform;
    SyVect3 Rotation,Scale, Translation;
    cSprite->CalcMotionAbsTransform(*scene,transform);
    transform.ConvertTo(Rotation, Scale, Translation);
    gameObject->SetLocation (Translation);
    gameObject->SetHPR (Rotation);

    mOwner->ChangeState(AS_STAND);
    input->mOverrideCancelImmediate=false;
    input->mOverrideCancelEnd=false;

 

    return;
  }
}

//------------------------------------ cAS_Dodge

void 
cAS_Dodge::Enter()
{
  
  cGameObject* obj = GetGameObject();
  cAnimCharControllerInput *input = GetInput();
  SyCSprite* sprite = GetSprite();

  float delta = AngleDifference(obj->GetHeading(),input->mDodgeDirection);

  int anim_to_play = ANIM_B_DODGE_F;
  if (delta < SY_DEG_TO_RAD(-135.0f) || delta > SY_DEG_TO_RAD(135.0f))
  {
    anim_to_play = ANIM_B_DODGE_B;
  }
  else if (delta < SY_DEG_TO_RAD(-45.0f))
  {
    anim_to_play = ANIM_B_DODGE_L;
  }
  else if (delta > SY_DEG_TO_RAD(45.0f))
  {
    anim_to_play = ANIM_B_DODGE_R;
  }

  if (sprite->HasAnim(anim_to_play))
  {
    PlayAnim(anim_to_play);
  }
  else
  {
    mOwner->ChangeState(AS_STAND);
  }

  input->mDodging = false;
}

void 
cAS_Dodge::Update(float time)
{
  SyCSprite* sprite = GetSprite();
  cAnimCharControllerInput *input = GetInput();

  if (input->mbKnockback)
  {
    mOwner->ChangeState(AS_KNOCKBACK);
    return;
  }


  if (input->mbKnockforward)
  {
    mOwner->ChangeState(AS_KNOCKFORWARD);
    return;
  }

  if (input->mDeath)
  {
    input->mDeath = false;
    mOwner->ChangeState(AS_DEATH);
    return;
  }

  if (input->mHitReact)
  {
    input->mHitReact = false;
    //mOwner->ChangeState(AS_HITREACT);
    mOwner->PlayImpact(input->mHitReactTarget);
  }

  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {
    mOwner->ChangeState(AS_STAND);
    return;
  }

  float prev_time = mOwner->GetStateTime();
  float cur_time = prev_time + time;

  float combo_end = 0.8f * mAnimDuration;
  if (prev_time <= combo_end && cur_time  > combo_end)
  {
    ClearInput();
  } 
}

//------------------------------------ cAS_Knocked

void 
cAS_Knocked::Enter()
{
  PlayAnim(mAnimID);
  cGameObject *obj = GetGameObject();
  cAnimCharControllerInput *input = GetInput();
  obj->SetHeading(input->mKnockbackHeading);

  mImpactVel = input->mKnockbackVect;
  ClearInput();
}

void 
cAS_Knocked::Update(float time)
{
  SyCSprite* sprite = GetSprite();


  if (sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {                              

    cGameObject *obj = GetGameObject();
    ((cPhysicsAnimated *) obj->GetPhysics())->Jump(mImpactVel);

    if (mAnimID == ANIM_B_KNOCKBACK)
    {
      mOwner->ChangeState(AS_KNOCKBACK_IN_AIR);
    }
    else
    {
      mOwner->ChangeState(AS_KNOCKFORWARD_IN_AIR);
    }
    return;
  }

}


//------------------------------------ cAS_KnockedInAir

void 
cAS_KnockedInAir::Enter()
{
  if (GetSprite()->HasAnim(mAnimID))
  {
    PlayAnim(mAnimID);
  }
}

void 
cAS_KnockedInAir::Update(float time)
{
  static const float ON_GROUND_HEIGHT = 0.001f;
  static const float ON_GROUND_VEL = 0.001f;
  cAnimCharControllerInput *input = GetInput();

  if (input->mHeight <= ON_GROUND_HEIGHT &&
      input->mYVel <= ON_GROUND_VEL)
  {
    if (mAnimID == ANIM_B_KNOCKBACK_IN_AIR)
    {
      mOwner->ChangeState(AS_KNOCKBACK_LAND);
    }
    else
    {
      mOwner->ChangeState(AS_KNOCKFORWARD_LAND);
    }
    return;
  }
}

//------------------------------------ cAS_KnockedLand

void 
cAS_KnockedLand::Enter()
{
  if (GetSprite()->HasAnim(mAnimID))
  {
    PlayAnim(mAnimID);
  }
  else
  {
    mPlaybackIndex =-1;
  }
}

void 
cAS_KnockedLand::Update(float time)
{

  SyCSprite* sprite = GetSprite();

  if (mPlaybackIndex == -1 || sprite->IsPlaybackStopped(mPlaybackIndex)!=0)
  {                              
    cAnimCharControllerInput *input = GetInput();

    if (!input->mDeath)
    {
      ClearInput();
      if (mAnimID == ANIM_B_KNOCKBACK_LAND)
      {

        mOwner->ChangeState(AS_KNOCKBACK_GETUP);
      }
      else
      {
        mOwner->ChangeState(AS_KNOCKFORWARD_GETUP);
      }
      return;
    }
  }
}

//---------------------------------------------------------------------
// Prop Animation
//---------------------------------------------------------------------

const char *l_PropAnimStatenames[] = 
{
  "Base",
  "Destroy",
  "Activate",
  "Deactivate",
  "Misc"
};

class cAnimPropController : public cAnimPropControllerInterface
{
public:
  cAnimPropController();
  virtual ~cAnimPropController();

  virtual void            Init(cGameObject *owner);
  virtual void            Reset();
  virtual void            Update(float time);
  virtual cGameObject*    GetOwner();
  virtual void            NetworkReceiveBroadcast(const char *state, int statelen);

  virtual PropAnimState   GetAnimState() const;
  virtual const char *    GetAnimStateName() const;

  virtual void            ChangeAnimState(PropAnimState newState);

private:
  bool                    PlayAnim(int32 animid);
  void                    NetworkSendBroadcast(PropAnimState state);

  cGameObject *           mOwner;
  PropAnimState           mCurState;
  tAnimSetID              mAnimSetID; // action frame info, etc...
  int32                   mPlaybackIndex; // for our animation...
};  

cAnimPropController::cAnimPropController()
: mOwner(NULL),
  mCurState(PAS_BASE),
  mAnimSetID(0),
  mPlaybackIndex(0)
{
}

cAnimPropController::~cAnimPropController()
{

}

void cAnimPropController::Init(cGameObject *owner)
{
  mOwner = owner;
  mAnimSetID = owner->GetAnimSet();
}

void cAnimPropController::Reset()
{
  // just change state w/o network messages
  mCurState = PAS_BASE;
  PlayAnim(PAS_BASE);
}

void cAnimPropController::Update(float time)
{
}

void cAnimPropController::ChangeAnimState(PropAnimState newState)
{
  mCurState = newState;
  PlayAnim(newState);

  if (!mOwner->IsRemote())
  {
    NetworkSendBroadcast(mCurState);
  }
}

cGameObject* cAnimPropController::GetOwner()
{
  return mOwner;
}

void cAnimPropController::NetworkReceiveBroadcast(const char *state, int statelen)
{
  cNetAnimStatePacket pPacket;
  pPacket.UnpackBuffer(state, statelen);

  SyAssertf(mOwner->IsRemote(), "Owner receiving network instructions?"); 
  ChangeAnimState((PropAnimState)pPacket.mAnimState);
}

void cAnimPropController::NetworkSendBroadcast(PropAnimState state)
{
  cNetAnimStatePacket packet;
  packet.mAnimState = (int)state;

  char buf[1024];
  int len = packet.PackBuffer(buf, sizeof(buf));
  mOwner->GetTitan()->GetPeeringNetwork()->ObjectBroadcast(mOwner->GetID(), buf, len);
}                         

cAnimPropController::PropAnimState cAnimPropController::GetAnimState() const
{
  return mCurState;
}

const char* cAnimPropController::GetAnimStateName() const
{
  return l_PropAnimStatenames[mCurState];
}

cAnimPropControllerInterface *cAnimPropControllerInterface::Allocate()
{
  return new cAnimPropController;
}

bool cAnimPropController::PlayAnim(int32 animid)
{
  cGraphicActor *graphic = (cGraphicActor*) mOwner->GetGraphic();
  SyScene *scene = mOwner->GetRegistry()->GetTitan()->GetScene();  
  
  SyAssert(scene->GetActorSpriteType(graphic->GetActorHandle()) == SYSPRITETYPE_CSPRITE);

  SyCSprite* sprite = (SyCSprite*)(scene->GetActorSpritePtr(graphic->GetActorHandle()));

  if (!sprite->HasAnim(animid))
  {
    mPlaybackIndex = 0;
    return false;
  }

  // sprite->Clear();
  sprite->EraseAllPlay(*scene);
  mPlaybackIndex = sprite->PrependAnimPlay(animid,0,*scene);

  return true;
}

// EOF              
