includefilename = "asset\\composition\\AIScript\\LordAICommon.lua" 
includefilename = SYSTEM.RelativeToAbsolutePath(includefilename)
dofile(includefilename)

function Init ()
  startEscapingRange = 1000
  stopEscapingRange = 1800
  
  fMinEscapeRange = 1800
  fMaxEscapeRange = 2500
  fAngle = 45
  
  fMinWanderRange = 1000
  fMaxWanderRange = 3000
  fWanderAngle = 90
  
  
  walkDirectlyToEnemyRange = 800
  
  fMinPredictingRange = 800
  fMaxPredictingRange = 4000
  fPredictAngle = 90
  

  SPECIAL_ABILITY_WAIT_TIME_RANDOM_RANGE_START = 30.0
  SPECIAL_ABILITY_WAIT_TIME_RANDOM_RANGE_END = 60.0
  
  ENEMY_NEAR_ENOUGH_IN_SPECIAL_ABILITY_RANGE = 1200.0
  
  TIME_TO_CHASE_ENEMY_WHEN_WANT_TO_USE_SPECIAL_ABILITY = 2.0

  REACT_TIME_RANDOM_RANGE_START = 0.2
  REACT_TIME_RANDOM_RANGE_END = 1.0

  --initialize
  g_fNextSpecialAbilityTime = TIME.GetGameNowTime() + _RandomSpecialAbilityWaitTime()
  g_bHasDecidedWaitTime = true
  g_bShouldAttackLord = false
  g_fChangeToAttackLordTime = TIME.GetGameNowTime()
  g_bShouldEscape = false
  g_fChangeToEscapeTime = TIME.GetGameNowTime()

  Enter_Wander()
end


function _IsStrongerThan(iEnemyID)
  local myShape = ARMY.GetShape(myArmyID)
  local enemyShape = ARMY.GetShape(iEnemyID)
  return GAMERULE.IsShapeStronger(myShape, enemyShape)
end

function _IsOfSameShape(iEnemyID)
  local myShape = ARMY.GetShape(myArmyID)
  local enemyShape = ARMY.GetShape(iEnemyID)
  return GAMERULE.IsShapeSame(myShape, enemyShape)
end

function _IsWeakerThan(iEnemyID)
  local myShape = ARMY.GetShape(myArmyID)
  local enemyShape = ARMY.GetShape(iEnemyID)
  return GAMERULE.IsShapeWeaker(myShape, enemyShape)
end


function _RandomSpecialAbilityWaitTime()
  local fRandomEscapeWaitTime = SPECIAL_ABILITY_WAIT_TIME_RANDOM_RANGE_START + ( (SPECIAL_ABILITY_WAIT_TIME_RANDOM_RANGE_END - SPECIAL_ABILITY_WAIT_TIME_RANDOM_RANGE_START) * math.random() )
  return fRandomEscapeWaitTime
end

function _RandomDecideUseSpecialAbility()
  local bHasMP = ARMY.IsCanUseSpell(myArmyID)
  local fCurTime = TIME.GetGameNowTime()
  if bHasMP then
    if g_bHasDecidedWaitTime then
      if fCurTime > g_fNextSpecialAbilityTime then
        g_bHasDecidedWaitTime = false
        return true
      end
    else
      g_fNextSpecialAbilityTime = fCurTime + _RandomSpecialAbilityWaitTime()
      g_bHasDecidedWaitTime = true
    end
  end
  
  return false
end

--------------------------------------------------------------------------------------
----------------------------------- start Wonder state -------------------------------
--------------------------------------------------------------------------------------

function Enter_Wander ()
  STATE = "Wander"
  ARMY.SetBehavior(myArmyID, "WalkTo")
   
  local iNearestEnemyArmyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)
  local fCurEnemyPosX, fCurEnemyPosY = ARMY.GetPosition(iNearestEnemyArmyID)
  local fCurPosX, fCurPosY = ARMY.GetPosition(myArmyID)
  local fEscapingDirX, fEscapingDirY = fCurPosX-fCurEnemyPosX, fCurPosY-fCurEnemyPosY
  Wander_Goal_PositionX, Wander_Goal_PositionY = MAP.GetRandomPositionWithCondition(fCurEnemyPosX, fCurEnemyPosY, fEscapingDirX, fEscapingDirY, fMinWanderRange, fMaxWanderRange, fWanderAngle)
  
  ARMY.SetGoalPosition(myArmyID, Wander_Goal_PositionX, Wander_Goal_PositionY)
  
  Last_PositionX, Last_PositionY = ARMY.GetPosition(myArmyID)
  Now_PositionX, Now_PositionY = ARMY.GetPosition(myArmyID)
  samePosCount = 0

  g_bShouldAttackLord = false
  g_fChangeToAttackLordTime = TIME.GetGameNowTime()
end

function Update_Wander ()

  if ARMY.GetHasLord(myArmyID) then
    if _RandomDecideUseSpecialAbility() then
      Exit_Wander()
      Enter_UseSpecialAbility()
      return
    end
  end  

  local iNearestEnemyArmyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)
  
  if not g_bShouldAttackLord then
    if _IsStrongerThan(iNearestEnemyArmyID) or _IsOfSameShape(iNearestEnemyArmyID)  then
      g_bShouldAttackLord = true
      g_fChangeToAttackLordTime = TIME.GetGameNowTime() + REACT_TIME_RANDOM_RANGE_START + ( (REACT_TIME_RANDOM_RANGE_END - REACT_TIME_RANDOM_RANGE_START) * math.random() )
    end
  end  

  if g_bShouldAttackLord and TIME.GetGameNowTime() > g_fChangeToAttackLordTime then
    g_bShouldAttackLord = false
    g_fChangeToAttackLordTime = TIME.GetGameNowTime()
    Exit_Wander()
    Enter_AttackLord()
    return
  end  
  
  if _IsWeakerThan(iNearestEnemyArmyID) then    
    local fCurEnemyPosX, fCurEnemyPosY = ARMY.GetPosition(iNearestEnemyArmyID)
    local fCurPosX, fCurPosY = ARMY.GetPosition(myArmyID)  
    local fDistanceEnemyAndThisArmy = MAP.GetDistanceBetweenTwoPosition(fCurPosX, fCurPosY, fCurEnemyPosX, fCurEnemyPosY)  
    if fDistanceEnemyAndThisArmy < startEscapingRange then  
      Exit_Wander()
      Enter_Escape()
      return
    end
  end
    
  Last_PositionX, Last_PositionY = Now_PositionX, Now_PositionY
  Now_PositionX, Now_PositionY = ARMY.GetPosition(myArmyID)

  if (Last_PositionX == Now_PositionX) and (Last_PositionY == Now_PositionY) then
    samePosCount = samePosCount + 1
  else
    samePosCount = 0
  end

  if samePosCount >= 5 then
    Exit_Wander()
    Enter_Wander()
  end 
  
end

function Exit_Wander()
end
--------------------------------------------------------------------------------------
------------------------------------ end Wonder state --------------------------------
--------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------
---------------------------------- start Escape state --------------------------------
--------------------------------------------------------------------------------------

function Enter_Escape ()
  STATE = "Escape"
  ARMY.SetBehavior(myArmyID, "WalkTo")
  
  fNextDecideEscapeTime = TIME.GetGameNowTime()
  fNextDecideEscapePosTime = TIME.GetGameNowTime()
  fEscapePosX, fEscapePosY = ARMY.GetPosition(myArmyID)
  ARMY.SetGoalPosition(myArmyID, fEscapePosX, fEscapePosY)
  fAtPosAccuracyRange = 200

  g_bShouldAttackLord = false
  g_fChangeToAttackLordTime = TIME.GetGameNowTime()
end

function Update_Escape ()

  if ARMY.GetHasLord(myArmyID) then
    if _RandomDecideUseSpecialAbility() then
      Exit_Escape()
      Enter_UseSpecialAbility()
      return
    end
  end  

  local iNearestEnemyArmyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)
  local fCurEnemyPosX, fCurEnemyPosY = ARMY.GetPosition(iNearestEnemyArmyID)
  local fCurPosX, fCurPosY = ARMY.GetPosition(myArmyID)  
  local fDistanceEnemyAndThisArmy = MAP.GetDistanceBetweenTwoPosition(fCurPosX, fCurPosY, fCurEnemyPosX, fCurEnemyPosY)
  
  if fDistanceEnemyAndThisArmy > stopEscapingRange then
    Exit_Escape()
    Enter_Wander()
    return
  end
    
  if not g_bShouldAttackLord then
    if _IsStrongerThan(iNearestEnemyArmyID) or _IsOfSameShape(iNearestEnemyArmyID) then
     g_bShouldAttackLord = true
      g_fChangeToAttackLordTime = TIME.GetGameNowTime() + REACT_TIME_RANDOM_RANGE_START + ( (REACT_TIME_RANDOM_RANGE_END - REACT_TIME_RANDOM_RANGE_START) * math.random() )
    end  
  end

  if g_bShouldAttackLord and TIME.GetGameNowTime() > g_fChangeToAttackLordTime then
    g_bShouldAttackLord = false
    g_fChangeToAttackLordTime = TIME.GetGameNowTime()
    Exit_Escape()
    Enter_AttackLord()
    return
  end
  
  -- check if need to redecide escape position
  local fCurTime = TIME.GetGameNowTime()
  local bNeedToDecideEscapePosAgain = false
  if fCurTime >= fNextDecideEscapeTime then
    bNeedToDecideEscapePosAgain = true
  else
    local fDistanceFromEscapePos = MAP.GetDistanceBetweenTwoPosition(fCurPosX, fCurPosY, fEscapePosX, fEscapePosY)
    if fDistanceFromEscapePos < fAtPosAccuracyRange then
      --already at the position, so need to re-decide
      bNeedToDecideEscapePosAgain = true
    end
  end
 
  if bNeedToDecideEscapePosAgain then
    -- set new escape position
    local fEscapingDirX, fEscapingDirY = fCurPosX-fCurEnemyPosX, fCurPosY-fCurEnemyPosY
    fEscapePosX, fEscapePosY = MAP.GetRandomPositionWithCondition(fCurPosX, fCurPosY, fEscapingDirX, fEscapingDirY, fMinEscapeRange, fMaxEscapeRange, fAngle)
    ARMY.SetGoalPosition(myArmyID, fEscapePosX, fEscapePosY)
    local decideEscapeWaitTime = math.random(500,1000) / 1000.0
    fNextDecideEscapeTime = fCurTime + decideEscapeWaitTime
  end    
  
end

function Exit_Escape()
end

--------------------------------------------------------------------------------------
----------------------------------- end Escape state ---------------------------------
--------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------
------------------------------ start AttackLord state --------------------------------
--------------------------------------------------------------------------------------

function Enter_AttackLord ()
  STATE = "AttackLord"
  
  local EnemyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)
  AttackLord_Goal_Army = EnemyID
  hasStoppedApproachingEnemy = false

  fNextPredictTime = TIME.GetGameNowTime()

  ARMY.SetBehavior(myArmyID, "WalkTo")
  fEnemyLastPosX, fEnemyLastPosY = ARMY.GetPosition(AttackLord_Goal_Army)
  fPredictedPosX, fPredictedPosY = fEnemyLastPosX, fEnemyLastPosY
  ARMY.SetGoalPosition(myArmyID, fPredictedPosX, fPredictedPosY)
    
  fAtPredictedPosAccuracyRange = 200
  
  stopApproachingRange = 400

  g_bShouldEscape = false
  g_fChangeToEscapeTime = TIME.GetGameNowTime()
end

function Update_AttackLord ()

  if (not IsAttackable(AttackLord_Goal_Army)) then
    Exit_AttackLord ()
    Enter_Wander()
	return 1
  end

  if ARMY.GetHasLord(myArmyID) then
    if _RandomDecideUseSpecialAbility() then
      Exit_AttackLord()
      Enter_UseSpecialAbility()
      return
    end
  end  

  local iNearestEnemyArmyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)

  if not g_bShouldEscape then
    if _IsWeakerThan(iNearestEnemyArmyID) then
      g_bShouldEscape = true
      g_fChangeToEscapeTime = TIME.GetGameNowTime() + REACT_TIME_RANDOM_RANGE_START + ( (REACT_TIME_RANDOM_RANGE_END - REACT_TIME_RANDOM_RANGE_START) * math.random() )
    end
  end

  if g_bShouldEscape and TIME.GetGameNowTime() > g_fChangeToEscapeTime then
    g_bShouldEscape = false
    g_fChangeToEscapeTime = TIME.GetGameNowTime()
    Exit_AttackLord()
    Enter_Wander()
    return
  end  

  local Enemy_PositionX, Enemy_PositionY = ARMY.GetPosition(AttackLord_Goal_Army)
  local Now_PositionX, Now_PositionY = ARMY.GetPosition(myArmyID)
      
  local fDistanceEnemyHasMovedLastFrame = MAP.GetDistanceBetweenTwoPosition(Enemy_PositionX, Enemy_PositionY, fEnemyLastPosX, fEnemyLastPosY)
  fEnemyLastPosX, fEnemyLastPosY = Enemy_PositionX, Enemy_PositionY

  local DistanceFromEnemy = MAP.GetDistanceBetweenTwoPosition(Now_PositionX, Now_PositionY, Enemy_PositionX, Enemy_PositionY)
  
  if DistanceFromEnemy < stopApproachingRange then
    if not hasStoppedApproachingEnemy then
      --already very close, stop approaching
      ARMY.SetGoalPosition(myArmyID, Now_PositionX, Now_PositionY)
      hasStoppedApproachingEnemy = true
    end
    
  elseif fDistanceEnemyHasMovedLastFrame < 3.0 or DistanceFromEnemy < walkDirectlyToEnemyRange then  
    ARMY.SetGoalPosition(myArmyID, Enemy_PositionX, Enemy_PositionY)
    hasStoppedApproachingEnemy = false
    
  else
    
    local fCurTime = TIME.GetGameNowTime()
    local bNeedToPredictAgain = false
    if fCurTime >= fNextPredictTime then
      bNeedToPredictAgain = true
    else
      local fDistanceFromPredictedPos = MAP.GetDistanceBetweenTwoPosition(Now_PositionX, Now_PositionY, fPredictedPosX, fPredictedPosY)
      if  fDistanceFromPredictedPos < fAtPredictedPosAccuracyRange then
        --already at the predicted position, so need to re-predict
        bNeedToPredictAgain = true
      end
    end
      
    if bNeedToPredictAgain then      
      --predict
      fPredictedPosX, fPredictedPosY = MAP.GetRandomPositionPredictingEnemy(myArmyID, AttackLord_Goal_Army, fMinPredictingRange, fMaxPredictingRange, fPredictAngle)      
      ARMY.SetGoalPosition(myArmyID, fPredictedPosX, fPredictedPosY)
      local predictWaitTime = math.random(1000,5000) / 1000.0
      fNextPredictTime = fCurTime + predictWaitTime
    end
    
    hasStoppedApproachingEnemy = false
  end

end

function Exit_AttackLord ()
  if ARMY.IsBeenAttacked(myArmyID) then
    --do nothing, calling here is just to clear the flag
  end 
end
--------------------------------------------------------------------------------------
------------------------------- end AttackLord state ---------------------------------
--------------------------------------------------------------------------------------


--------------------------------------------------------------------------------------
----------------------------- start UseSpecialAbility state --------------------------
--------------------------------------------------------------------------------------

function Enter_UseSpecialAbility ()
  STATE = "UseSpecialAbility"
  
  local EnemyID = ARMY.GetInRangeID(myArmyID, "VISIBLE_CAN_ATTACK_ARMY", 100000)
  AttackLord_Goal_Army = EnemyID
  hasStoppedApproachingEnemy = false

  ARMY.SetBehavior(myArmyID, "WalkTo")
  local Enemy_PositionX, Enemy_PositionY = ARMY.GetPosition(AttackLord_Goal_Army)
  ARMY.SetGoalPosition(myArmyID, Enemy_PositionX, Enemy_PositionY)
  
  stopApproachingRange = 400
  g_fTimeShouldLeaveUseSpecialAbilityState = TIME.GetGameNowTime() + TIME_TO_CHASE_ENEMY_WHEN_WANT_TO_USE_SPECIAL_ABILITY
end

function Update_UseSpecialAbility()
  local Enemy_PositionX, Enemy_PositionY = ARMY.GetPosition(AttackLord_Goal_Army)
  local Now_PositionX, Now_PositionY = ARMY.GetPosition(myArmyID)      

  local DistanceFromEnemy = MAP.GetDistanceBetweenTwoPosition(Now_PositionX, Now_PositionY, Enemy_PositionX, Enemy_PositionY)
  if DistanceFromEnemy < ENEMY_NEAR_ENOUGH_IN_SPECIAL_ABILITY_RANGE then
    ARMY.UseSpecialAbility(myArmyID)
    Exit_UseSpecialAbility()
    Enter_Wander()
  elseif TIME.GetGameNowTime() > g_fTimeShouldLeaveUseSpecialAbilityState then
    Exit_UseSpecialAbility()
    Enter_Wander()
  else
    ARMY.SetGoalPosition(myArmyID, Enemy_PositionX, Enemy_PositionY)
  end  
  
end

function Exit_UseSpecialAbility()
end

--------------------------------------------------------------------------------------
---------------------------- end UseSpecialAbility state -----------------------------
--------------------------------------------------------------------------------------

function Update ()
  if STATE == "Wander" then
    Update_Wander()
  elseif STATE == "Escape" then
    Update_Escape()
  elseif STATE == "AttackLord" then
    Update_AttackLord()
  elseif STATE == "UseSpecialAbility" then
    Update_UseSpecialAbility()
  end
end
