// There is no custom link action - fall back on Link->Action.
const int CLA_NONE = 0;
// Link is pounding with a hammer.
const int CLA_POUND = 1;
// Link is stabbing.
const int CLA_STAB = 2;
// Link is throwing.
const int CLA_THROW = 3;
// Link is slashing.
const int CLA_SLASH = 4;
// Link is recovering.
const int CLA_RECOVER = 5;

//// Pound Constants
// Start of 8 tiles for link pounding. The animation is 2 tiles long.
const int CLA_POUND_TILE = 38442;
// Pound total duration.
const int CLA_POUND_DURATION = 16;
// Tick on which link switches to the next frame.
const int CLA_POUND_LINK_ADV1 = 6;
// Tick on which item switches to the next frame.
const int CLA_POUND_ITEM_ADV1 = 6;
const int CLA_POUND_ITEM_ADV2 = 7;
// Tick on which the strike hits.
const int CLA_POUND_HIT = 7;

//// Stab Constants
// Start of 4 tiles for link stabbing.
const int CLA_STAB_TILE = 38452;
// Stab total duration.
const int CLA_STAB_DURATION = 12;

//// Throw Constants
// Start of 4 tiles for Link throwing.
const int CLA_THROW_TILE = 38452;
// Throw total duration.
const int CLA_THROW_DURATION = 6;

//// Slash Constants
// Start of 12 tiles for link slashing.
const int CLA_SLASH_TILE = 38462;
// Tick on which link switches to the next frame.
const int CLA_SLASH_ADV1 = 4;
const int CLA_SLASH_ADV2 = 8;
// Slash total duration.
const int CLA_SLASH_DURATION = 12;


// The current custom link action.
int CLA_Action = CLA_NONE;
// The current animation id for link.
int CLA_LinkAnimationId = LWEAPON_ID_NULL;
// The number of frames the current action has been happening.
int CLA_Frames = 0;

// Timer for custom durations.
int CLA_Timer = 0;

//// Info for the item being used to read.
// The current item location.
int CLA_ItemX = 0;
int CLA_ItemY = 0;
// The current item frame offset.
int CLA_ItemFrame = 0;
// If the weapon is striking during this frame.
bool CLA_Strike = false;

// Test for standard actions that we can override.
bool CLA_CanAct() {
  return CLA_Action == CLA_NONE &&
    (Link->Action == LA_NONE ||
     Link->Action == LA_WALKING ||
     Link->Action == LA_GOTHURTLAND);}

// Make the animation base.
lweapon CLA_CreateAnimationBase() {
  lweapon base = Screen->CreateLWeapon(LW_ANIMATION2);
  base->DeadState = -2;
  base->CollDetection = false;
  base->Behind = true;
  base->Frame = 0;
  base->NumFrames = 9999;
  base->ASpeed = 3600;
  return base;}

// Update running custom link action. Just calls the respective update
// method for each action.
void CLA_Update() {
  if (CLA_Action == CLA_POUND) {CLA_UpdatePound();}
  else if (CLA_Action == CLA_STAB) {CLA_UpdateStab();}
  else if (CLA_Action == CLA_SLASH) {CLA_UpdateSlash();}
  else if (CLA_Action == CLA_THROW) {CLA_UpdateThrow();}
  else if (CLA_Action == CLA_RECOVER) {CLA_UpdateRecover();}}

// Pound with an item.
void CLA_Pound() {
  // Only works if we're not doing anything else.
  if (!CLA_CanAct()) {return;}

  CLA_Action = CLA_POUND;
  BlockInput(KEY_DIR, CLA_POUND_DURATION);

  // Setup Link's animation.
  Link->Invisible = true;
  lweapon animation = CLA_CreateAnimationBase();
  animation->OriginalTile = CLA_POUND_TILE;
  animation->CSet = 6;

  CLA_LinkAnimationId = GetId(animation);

  CLA_Frames = -1;
  CLA_UpdatePound();}

// Stab with an item.
void CLA_Stab() {
  // Only works if we're not doing anything else.
  if (!CLA_CanAct()) {return;}

  CLA_Action = CLA_STAB;
  BlockInput(KEY_DIR, CLA_STAB_DURATION);

  // Steup Link's animation.
  Link->Invisible = true;
  lweapon animation = CLA_CreateAnimationBase();
  animation->OriginalTile = CLA_STAB_TILE;
  animation->CSet = 6;

  CLA_LinkAnimationId = GetId(animation);

  CLA_Frames = -1;
  CLA_UpdateStab();}

// Slash with an item.
void CLA_Slash() {
  // Only works if we're not doing anything else.
  if (!CLA_CanAct()) {return;}

  CLA_Action = CLA_SLASH;
  BlockInput(KEY_DIR, CLA_SLASH_DURATION);

  // Steup Link's animation.
  Link->Invisible = true;
  lweapon animation = CLA_CreateAnimationBase();
  animation->OriginalTile = CLA_SLASH_TILE;
  animation->CSet = 6;

  CLA_LinkAnimationId = GetId(animation);

  CLA_Frames = -1;
  CLA_UpdateSlash();}

void CLA_Throw() {
  // Only works if we're not doing anything else.
  if (!CLA_CanAct()) {return;}

  CLA_Action = CLA_THROW;
  BlockInput(KEY_DIR, CLA_THROW_DURATION);

  // Steup Link's animation.
  Link->Invisible = true;
  lweapon animation = CLA_CreateAnimationBase();
  animation->OriginalTile = CLA_THROW_TILE;
  animation->CSet = 6;

  CLA_LinkAnimationId = GetId(animation);

  CLA_Frames = -1;
  CLA_UpdateThrow();}

void CLA_Recover(int duration) {
  // Only works if we're not doing anything else.
  if (!CLA_CanAct()) {return;}

  CLA_Action = CLA_RECOVER;
  BlockInput(KEY_DIR, duration);

  CLA_Frames = -1;
  CLA_Timer = duration + 1;
  CLA_UpdateRecover();}

void CLA_UpdatePound() {
  Link->Action = LA_ATTACKING;
  CLA_Frames++;

  // Update link animation.
  lweapon animation = FindLWeapon(CLA_LinkAnimationId);
  animation->DrawXOffset = Link->X;
  animation->DrawYOffset = Link->Y;
  animation->Frame = Link->Dir * 2 +
    Cond(CLA_Frames >= CLA_POUND_LINK_ADV1, 1, 0);
  animation->Tile = animation->OriginalTile + animation->Frame;

  // Update item info.
  if (CLA_Frames >= CLA_POUND_ITEM_ADV2) {
    CLA_ItemX = DirX(Link->Dir) * 16 + Link->X;
    CLA_ItemY = DirY(Link->Dir) * 16 + Link->Y;
    CLA_ItemFrame = 2;}
  else if (CLA_Frames >= CLA_POUND_ITEM_ADV1) {
    if (Link->Dir >= DIR_LEFT) {
      CLA_ItemX = DirX(Link->Dir) * 12 + Link->X;
      CLA_ItemY = Link->Y - 4;}
    else {
      CLA_ItemX = Link->X;
      CLA_ItemY = 16 * (Link->Dir - 1) + Link->Y;}
    CLA_ItemFrame = 1;}
  else {
    CLA_ItemX = Link->X;
    CLA_ItemY = Link->Y - 14;
    CLA_ItemFrame = 0;}

  CLA_Strike = CLA_Frames == CLA_POUND_HIT;

  // Exit out if we need to.
  if (CLA_Frames >= CLA_POUND_DURATION) {
    Remove(animation);
    Link->Action = LA_NONE;
    CLA_Action = CLA_NONE;
    Link->Invisible = false;}}

void CLA_UpdateStab() {
  Link->Action = LA_ATTACKING;
  CLA_Frames++;

  lweapon animation = FindLWeapon(CLA_LinkAnimationId);
  animation->DrawXOffset = Link->X;
  animation->DrawYOffset = Link->Y;
  animation->Frame = Link->Dir;
  animation->Tile = animation->OriginalTile + animation->Frame;

  // Item Info.
  int offset = 14;
  if (CLA_Frames < 2) {offset = 10 + CLA_Frames * 2;}
  else if (CLA_Frames > CLA_STAB_DURATION - 2) {
    offset = 10 + (CLA_STAB_DURATION - CLA_Frames) * 2;}
  CLA_ItemX = offset * DirX(Link->Dir) + Link->X;
  CLA_ItemY = offset * DirY(Link->Dir) + Link->Y;

  // Exit if needed.
  if (CLA_Frames >= CLA_STAB_DURATION) {
    Remove(animation);
    Link->Action = LA_NONE;
    CLA_Action = CLA_NONE;
    Link->Invisible = false;}}

void CLA_UpdateThrow() {
  Link->Action = LA_ATTACKING;
  CLA_Frames++;

  lweapon animation = FindLWeapon(CLA_LinkAnimationId);
  animation->DrawXOffset = Link->X;
  animation->DrawYOffset = Link->Y;
  animation->Frame = Link->Dir;
  animation->Tile = animation->OriginalTile + animation->Frame;

  // Item Info.
  int offset = 8;
  CLA_ItemX = offset * DirX(Link->Dir) + Link->X;
  CLA_ItemY = offset * DirY(Link->Dir) + Link->Y;

  // Exit if needed.
  if (CLA_Frames >= CLA_THROW_DURATION) {
    Remove(animation);
    Link->Action = LA_NONE;
    CLA_Action = CLA_NONE;
    Link->Invisible = false;}}

void CLA_UpdateSlash() {
  Link->Action = LA_ATTACKING;
  CLA_Frames++;

  // Update link animation.
  lweapon animation = FindLWeapon(CLA_LinkAnimationId);
  animation->DrawXOffset = Link->X;
  animation->DrawYOffset = Link->Y;
  animation->Frame = Link->Dir * 3 +
    Cond(CLA_Frames >= CLA_SLASH_ADV1, 1, 0) +
    Cond(CLA_Frames >= CLA_SLASH_ADV2, 1, 0);
  animation->Tile = animation->OriginalTile + animation->Frame;

  // Update item info.
  if (CLA_Frames >= CLA_SLASH_ADV2) {
    CLA_ItemFrame = 2;
    if (Link->Dir == DIR_UP) {
      CLA_ItemX = Link->X - 8;
      CLA_ItemY = Link->Y - 8;}
    else if (Link->Dir == DIR_DOWN) {
      CLA_ItemX = Link->X + 8;
      CLA_ItemY = Link->Y + 8;}
    else if (Link->Dir == DIR_LEFT) {
      CLA_ItemX = Link->X - 8;
      CLA_ItemY = Link->Y + 8;}
    else if (Link->Dir == DIR_RIGHT) {
      CLA_ItemX = Link->X + 8;
      CLA_ItemY = Link->Y + 8;}}
  else if (CLA_Frames >= CLA_SLASH_ADV1) {
    CLA_ItemX = DirX(Link->Dir) * 14 + Link->X;
    CLA_ItemY = DirY(Link->Dir) * 14 + Link->Y;
    CLA_ItemFrame = 1;}
  else {
    CLA_ItemFrame = 0;
    if (Link->Dir == DIR_UP) {
      CLA_ItemX = Link->X + 8;
      CLA_ItemY = Link->Y - 8;}
    else if (Link->Dir == DIR_DOWN) {
      CLA_ItemX = Link->X - 8;
      CLA_ItemY = Link->Y + 8;}
    else if (Link->Dir == DIR_LEFT) {
      CLA_ItemX = Link->X - 8;
      CLA_ItemY = Link->Y - 8;}
    else if (Link->Dir == DIR_RIGHT) {
      CLA_ItemX = Link->X + 8;
      CLA_ItemY = Link->Y - 8;}}

  CLA_Strike = true;

  // Exit out if we need to.
  if (CLA_Frames >= CLA_SLASH_DURATION) {
    Remove(animation);
    Link->Action = LA_NONE;
    CLA_Action = CLA_NONE;
    Link->Invisible = false;}}

void CLA_UpdateRecover() {
  CLA_Timer--;
  if (CLA_Timer <= 0) {
    CLA_Action = CLA_NONE;}}

// Make sure we don't get permanently stuck somehow.
void CLA_Continue() {
  CLA_Action = CLA_NONE;}
