local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local helper = require("son.helper")
local poilib = require("behavior.poi")
local son_poistates = require("son.POIFramework")
local mpicon = require("ui.mpicon")
local thunk = require("core.thunk")
local bboardUtil = require("game.BlackboardUtil")
local locomotion = require("creature.locomotion")
local color = require("core.color")
local Brain = statemachine.StateMachine.New("Son")
local Command_Action = Brain:State("Command_Action")
local AutoMode = Brain:State("AutoMode")
statemachine.AddTags(AutoMode, "DodgeAllowed")
local Dodge = Brain:Action("Dodge")
statemachine.AddTags(Dodge, "TagDoNotChangePath")
local BehaviorTransition = Brain:State("BehaviorTransition")
local CloseCombat = Brain:Action("CloseCombat")
statemachine.AddTags(CloseCombat, "InCloseCombat")
statemachine.AddTags(CloseCombat, "DodgeAllowed")
local Coop_Moves = Brain:Action("Coop_Moves")
local Idle = Brain:State("Idle")
statemachine.AddTags(Idle, "DodgeAllowed")
statemachine.AddTags(Idle, "InIdle")
local CustomIdle = Brain:Action("CustomIdle")
statemachine.AddTags(CustomIdle, "DodgeAllowed")
local WeaponHolsterLogic = Brain:Action("WeaponHolsterLogic")
local LockDownTargets = Brain:State("LockDownTargets")
statemachine.AddTags(LockDownTargets, "DodgeAllowed")
local Fire_AllTargets = Brain:State("Fire_AllTargets")
statemachine.AddTags(Fire_AllTargets, "DodgeAllowed")
local Incapacitated = Brain:State("Incapacitated")
local FollowUpAttack = Brain:Action("FollowUpAttack")
statemachine.AddTags(FollowUpAttack, "DoNotDodge")
local FollowUpAttackPriority = Brain:Action("FollowUpAttackPriority")
statemachine.AddTags(FollowUpAttackPriority, "DoNotDodge")
local BranchFromTweaksOnly = Brain:State("BranchFromTweaksOnly")
statemachine.AddTags(BranchFromTweaksOnly, "DoNotPerform")
local TweaksAndPOIAndRes = Brain:State("TweaksAndPOIAndRes")
statemachine.AddTags(TweaksAndPOIAndRes, "DoNotPerform")
local AssistMode = Brain:State("AssistMode")
statemachine.AddTags(AssistMode, "DodgeAllowed")
local PostUp = Brain:State("PostUp")
local UsePOI = poilib.NewPOIState(Brain, "UsePOI")
poilib.AllowPOIFromStates(Idle, Fire_AllTargets, CloseCombat, AssistMode, BehaviorTransition, TweaksAndPOIAndRes, AutoMode)
poilib.SetCharacterType(UsePOI, "SON")
son_poistates.SetupPOIStates(UsePOI)
statemachine.AddTags(CustomIdle, "CustomNav")
function Reset_Son_Actions(ai, global, constants)
  global.bCommand_Action = false
  FollowUpAttack.FollowUpAttackStart = false
end
function Brain:SelectNextState(ai, global, constants)
  local tags = statemachine.ActiveTags()
  if tags.GameState_Idle then
    return Brain:SelectIdleState(ai, global, constants, tags)
  elseif tags.GameState_PreCombat then
    return Brain:SelectPreCombatState(ai, global, constants, tags)
  elseif tags.GameState_EnterCombat then
    return Brain:SelectEnterCombatState(ai, global, constants, tags)
  elseif tags.GameState_InCombat then
    return Brain:SelectInCombatState(ai, global, constants, tags)
  elseif tags.GameState_PostCombat then
    return Brain:SelectPostCombatState(ai, global, constants, tags)
  else
    return Idle
  end
end
function Brain:SelectEnterCombatState(ai, global, constants, tags)
  if constants.setCommandOnly then
    if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
      return BranchFromTweaksOnly
    end
    if Incapacitated:IsAvailable(ai, global, constants) then
      Reset_Son_Actions(ai, global, constants)
      return Incapacitated
    end
    if poilib.IsPOIActive() then
      Reset_Son_Actions(ai, global, constants)
      return UsePOI
    end
    if Command_Action:IsAvailable(ai, global, constants) then
      return Command_Action
    end
  else
    if tags.SplineNav or tags.POI_Nav then
      Reset_Son_Actions(ai, global, constants)
    end
    if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
      return BranchFromTweaksOnly
    end
    if Incapacitated:IsAvailable(ai, global, constants) then
      Reset_Son_Actions(ai, global, constants)
      return Incapacitated
    end
    if poilib.IsPOIActive() or constants.forcedBehavior then
      if ai.OwnedPOI ~= nil then
        local canRevive = ai.OwnedPOI:FindLuaTableAttribute("AllowRevive")
        if canRevive == true and global.bStartCoopMove then
          return Coop_Moves
        end
        local attackType = ai.OwnedPOI:FindLuaTableAttribute("SonAttackType")
        if (attackType == "CommandShot" or attackType == "Both") and Command_Action:IsAvailable(ai, global, constants) then
          return Command_Action
        end
        if (attackType == "AutonomousShot" or attackType == "Both") and Fire_AllTargets:IsAvailable(ai, global, constants) then
          return Fire_AllTargets
        end
      end
      if poilib.IsPOIActive() == nil then
        return Idle
      end
      Reset_Son_Actions(ai, global, constants)
      return UsePOI
    end
    if ai:IsInVehicle() then
      return Idle
    end
    if global.bStartCoopMove then
      return Coop_Moves
    end
    if TweaksAndPOIAndRes:IsAvailable(ai, global, constants) then
      return TweaksAndPOIAndRes
    end
    if Command_Action:IsAvailable(ai, global, constants) then
      return Command_Action
    end
    if WeaponHolsterLogic:IsAvailable(ai, global, constants, "EnterCombat") then
      return WeaponHolsterLogic
    end
    if FollowUpAttackPriority:IsAvailable(ai, global, constants) then
      return FollowUpAttackPriority
    end
    if BehaviorTransition:IsAvailable(ai, global, constants) then
      return BehaviorTransition
    end
    if CloseCombat:IsAvailable(ai, global, constants, tags) then
      return CloseCombat
    end
    if Dodge:IsAvailable(ai, global, constants, tags) then
      return Dodge
    end
    if LockDownTargets:IsAvailable(ai, global, constants) then
      return LockDownTargets
    end
    if constants.awarenessState ~= "CautiousReposition" and constants.awarenessState ~= _G.constants.SurvivalReposition or constants.awarenessState == _G.constants.SurvivalReposition and ai:HasMarker("AllowThroughSurvivalReposition") then
      if FollowUpAttack:IsAvailable(ai, global, constants) then
        return FollowUpAttack
      end
      if Fire_AllTargets:IsAvailable(ai, global, constants) then
        return Fire_AllTargets
      end
    end
  end
  if AutoMode:IsAvailable(ai, global, constants) then
    return AutoMode
  end
  return Idle
end
function Brain:SelectInCombatState(ai, global, constants, tags)
  if constants.setCommandOnly then
    if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
      return BranchFromTweaksOnly
    end
    if Incapacitated:IsAvailable(ai, global, constants) then
      Reset_Son_Actions(ai, global, constants)
      return Incapacitated
    end
    if poilib.IsPOIActive() then
      Reset_Son_Actions(ai, global, constants)
      return UsePOI
    end
    if Command_Action:IsAvailable(ai, global, constants) then
      return Command_Action
    end
  else
    if tags.SplineNav or tags.POI_Nav then
      Reset_Son_Actions(ai, global, constants)
    end
    if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
      return BranchFromTweaksOnly
    end
    if Incapacitated:IsAvailable(ai, global, constants) then
      Reset_Son_Actions(ai, global, constants)
      return Incapacitated
    end
    if poilib.IsPOIActive() or constants.forcedBehavior then
      if ai.OwnedPOI ~= nil then
        local canRevive = ai.OwnedPOI:FindLuaTableAttribute("AllowRevive")
        if canRevive == true and global.bStartCoopMove then
          return Coop_Moves
        end
        local attackType = ai.OwnedPOI:FindLuaTableAttribute("SonAttackType")
        if (attackType == "CommandShot" or attackType == "Both") and not constants.doingPostUpAutonomousShot and Command_Action:IsAvailable(ai, global, constants) then
          return Command_Action
        end
        if (attackType == "AutonomousShot" or attackType == "Both") and not constants.doingPostUpAutonomousShot and Fire_AllTargets:IsAvailable(ai, global, constants) then
          return Fire_AllTargets
        end
      end
      if poilib.IsPOIActive() == nil then
        return Idle
      end
      Reset_Son_Actions(ai, global, constants)
      return UsePOI
    end
    if ai:IsInVehicle() then
      return Idle
    end
    if global.bStartCoopMove then
      return Coop_Moves
    end
    if TweaksAndPOIAndRes:IsAvailable(ai, global, constants) then
      return TweaksAndPOIAndRes
    end
    if Command_Action:IsAvailable(ai, global, constants) then
      return Command_Action
    end
    if WeaponHolsterLogic:IsAvailable(ai, global, constants, "InCombat") then
      return WeaponHolsterLogic
    end
    if FollowUpAttackPriority:IsAvailable(ai, global, constants) then
      return FollowUpAttackPriority
    end
    if BehaviorTransition:IsAvailable(ai, global, constants) then
      return BehaviorTransition
    end
    if CloseCombat:IsAvailable(ai, global, constants, tags) then
      return CloseCombat
    end
    if Dodge:IsAvailable(ai, global, constants, tags) then
      return Dodge
    end
    if LockDownTargets:IsAvailable(ai, global, constants) then
      return LockDownTargets
    end
    if constants.awarenessState ~= _G.constants.SurvivalReposition or constants.awarenessState == _G.constants.SurvivalReposition and ai:HasMarker("AllowThroughSurvivalReposition") then
      if FollowUpAttack:IsAvailable(ai, global, constants) then
        return FollowUpAttack
      end
      if Fire_AllTargets:IsAvailable(ai, global, constants) then
        return Fire_AllTargets
      end
    end
  end
  if AutoMode:IsAvailable(ai, global, constants) then
    return AutoMode
  end
  return Idle
end
function Brain:SelectIdleState(ai, global, constants, tags)
  if tags.SplineNav or tags.POI_Nav then
    Reset_Son_Actions(ai, global, constants)
  end
  if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
    return BranchFromTweaksOnly
  end
  if Incapacitated:IsAvailable(ai, global, constants) then
    Reset_Son_Actions(ai, global, constants)
    return Incapacitated
  end
  if poilib.IsPOIActive() or constants.forcedBehavior then
    if ai.OwnedPOI ~= nil then
      local canRevive = ai.OwnedPOI:FindLuaTableAttribute("AllowRevive")
      if canRevive == true and global.bStartCoopMove then
        return Coop_Moves
      end
      local attackType = ai.OwnedPOI:FindLuaTableAttribute("SonAttackType")
      if (attackType == "CommandShot" or attackType == "Both") and Command_Action:IsAvailable(ai, global, constants) then
        return Command_Action
      end
    end
    if poilib.IsPOIActive() == nil or ai.OwnedPOI ~= nil and ai.OwnedPOI:GetStageName() ~= "ApproachOrMovingBreakOut" then
      return Idle
    end
    Reset_Son_Actions(ai, global, constants)
    if Dodge:IsAvailable(ai, global, constants, tags) and TweaksAndPOIAndRes:IsAvailable(ai, global, constants) == false then
      return Dodge
    end
    return UsePOI
  end
  if poilib.IsPOIActive() then
    Reset_Son_Actions(ai, global, constants)
    if Dodge:IsAvailable(ai, global, constants, tags) and TweaksAndPOIAndRes:IsAvailable(ai, global, constants) == false then
      return Dodge
    end
    return UsePOI
  end
  if ai:IsInVehicle() then
    return Idle
  end
  if global.bStartCoopMove then
    return Coop_Moves
  end
  if TweaksAndPOIAndRes:IsAvailable(ai, global, constants) then
    return TweaksAndPOIAndRes
  end
  if Command_Action:IsAvailable(ai, global, constants) then
    return Command_Action
  end
  if WeaponHolsterLogic:IsAvailable(ai, global, constants, "Idle") then
    return WeaponHolsterLogic
  end
  if Dodge:IsAvailable(ai, global, constants, tags) then
    return Dodge
  end
  return Idle
end
function Brain:SelectPreCombatState(ai, global, constants, tags)
  if tags.SplineNav or tags.POI_Nav then
    Reset_Son_Actions(ai, global, constants)
  end
  if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
    return BranchFromTweaksOnly
  end
  if Incapacitated:IsAvailable(ai, global, constants) then
    Reset_Son_Actions(ai, global, constants)
    return Incapacitated
  end
  if poilib.IsPOIActive() or constants.forcedBehavior then
    if ai.OwnedPOI ~= nil then
      local canRevive = ai.OwnedPOI:FindLuaTableAttribute("AllowRevive")
      if canRevive == true and global.bStartCoopMove then
        return Coop_Moves
      end
      local attackType = ai.OwnedPOI:FindLuaTableAttribute("SonAttackType")
      if (attackType == "CommandShot" or attackType == "Both") and Command_Action:IsAvailable(ai, global, constants) then
        return Command_Action
      end
    end
    if poilib.IsPOIActive() == nil then
      return Idle
    end
    Reset_Son_Actions(ai, global, constants)
    return UsePOI
  end
  if ai:IsInVehicle() then
    return Idle
  end
  if global.bStartCoopMove then
    return Coop_Moves
  end
  if TweaksAndPOIAndRes:IsAvailable(ai, global, constants) then
    return TweaksAndPOIAndRes
  end
  if Command_Action:IsAvailable(ai, global, constants) then
    return Command_Action
  end
  if WeaponHolsterLogic:IsAvailable(ai, global, constants, "PreCombat") then
    return WeaponHolsterLogic
  end
  if Dodge:IsAvailable(ai, global, constants, tags) then
    return Dodge
  end
  return Idle
end
function Brain:SelectPostCombatState(ai, global, constants, tags)
  if tags.SplineNav or tags.POI_Nav then
    Reset_Son_Actions(ai, global, constants)
  end
  if BranchFromTweaksOnly:IsAvailable(ai, global, constants) then
    return BranchFromTweaksOnly
  end
  if Incapacitated:IsAvailable(ai, global, constants) then
    Reset_Son_Actions(ai, global, constants)
    return Incapacitated
  end
  if poilib.IsPOIActive() or constants.forcedBehavior then
    if ai.OwnedPOI ~= nil then
      if ai.OwnedPOI:FindLuaTableAttribute("AvailableDuringCombat") and WeaponHolsterLogic:IsAvailable(ai, global, constants, "Idle") then
        return WeaponHolsterLogic
      end
      local attackType = ai.OwnedPOI:FindLuaTableAttribute("SonAttackType")
      if (attackType == "CommandShot" or attackType == "Both") and Command_Action:IsAvailable(ai, global, constants) then
        return Command_Action
      end
    end
    if poilib.IsPOIActive() == nil then
      return Idle
    end
    Reset_Son_Actions(ai, global, constants)
    return UsePOI
  end
  if ai:IsInVehicle() then
    return Idle
  end
  if global.bStartCoopMove then
    return Coop_Moves
  end
  if TweaksAndPOIAndRes:IsAvailable(ai, global, constants) then
    return TweaksAndPOIAndRes
  end
  if Command_Action:IsAvailable(ai, global, constants) then
    return Command_Action
  end
  if WeaponHolsterLogic:IsAvailable(ai, global, constants, "PostCombat") then
    return WeaponHolsterLogic
  end
  if Dodge:IsAvailable(ai, global, constants, tags) then
    return Dodge
  end
  return Idle
end
function UsePOI:Enter(ai, global, constants)
  constants.strafe = false
end
function UsePOI:Exit(ai, global, constants)
end
function BranchFromTweaksOnly:IsAvailable(ai, global, constants)
  return ai:HasMarker("TweakLogicOnly") or _G.gVFSDebugNoBrainLogic.value == true
end
function TweaksAndPOIAndRes:IsAvailable(ai, global, constants)
  return ai:HasMarker("TweakPOIResOnly")
end
function Idle:Update(ai, global, constants)
end
function Idle:Exit(ai, global, constants)
end
function CustomIdle:IsAvailable(ai, global, constants)
  return global.customIdle.isAvailable
end
function CustomIdle:Enter(seq, ai, global, constants)
  CustomIdle:ResetState(ai, global, constants)
  seq:At(0, function()
    global.customIdle.CustomFrontPosition = false
  end)
  seq:At(5, function()
    global.CustomIdlePlayed = true
    global.customIdle.CustomIdlePlayed = true
    ai:ForceMove("BRA_CustomIdleEnter_Solo")
  end)
  seq:When(function()
    return global.Player:GetVelocity():Length() > 0
  end, function()
    global.customIdle.isAvailable = false
  end)
end
function CustomIdle:Update(ai, global, constants)
  if global.Player:GetVelocity():Length() > 0 then
    global.customIdle.isAvailable = false
  end
  global.sTargetMode = "TargetKratos"
end
function CustomIdle:Exit(ai, global, constants)
  global.customIdle.isAvailable = false
  CustomIdle:ResetState(ai, global, constants)
  global.sTargetMode = "RegularTarget"
  global.CustomIdlePlayed = false
  global.CustomFrontPosition = false
end
function CustomIdle:ResetState(ai, global, constants)
  global.customIdle.phaseTimer = 0
  global.customIdle.CustomFrontPosition = false
  global.customIdle.CustomIdlePlayed = false
end
function CustomIdle:OnBrainInit(ai, global, constants)
  global.customIdle = {}
  global.customIdle.isAvailable = false
  CustomIdle:ResetState(ai, global, constants)
end
function Incapacitated:Enter(ai, global, constants)
  self.SquareDown = false
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
  if enemyBBLenth ~= nil and 0 < enemyBBLenth then
    bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
  end
  global.previousTargetMode = global.sTargetMode
  if ai:HasMarker("Stunned") then
    mpicon.Create("WORLD_INTERACT_SON")
  end
  ai:SetNewAvailabilityRequest("SonInteraction", {
    AvailableForSync = false,
    AvailableForCombat = false,
    Unoccupied = false
  })
end
function Incapacitated:IsAvailable(ai, global, constants)
  return ai:HasMarker("Stunned")
end
function Incapacitated:Exit(ai, global, constants)
  global.sTargetMode = global.previousTargetMode
  ai:RemoveAvailabilityRequest("SonInteraction")
  mpicon.Off("WORLD_INTERACT_SON")
end
function Incapacitated:Update(ai, global, constants)
  local player = global.Player
  local distance = (player:GetWorldPosition() - ai:GetWorldPosition()).length
  if player.Pad.SquareDown == true and self.SquareDown == false and distance < 1.5 then
    local sonToHeroAngle = DL.FrontAngle(player, ai)
    if math.abs(sonToHeroAngle) < 90 then
      global.bCommand_Action = false
      global.sTargetMode = "TargetKratos"
      ai:SetCombatTarget(player)
      local heroToSonAngle = DL.FrontAngle(ai, player)
      if math.abs(heroToSonAngle) < 90 then
        ai:TriggerMoveEvent("SonRevive")
      else
        ai:TriggerMoveEvent("SonRevive")
      end
    end
  end
  self.SquareDown = player.Pad.SquareDown
end
function Command_Action:OnBrainInit(ai, global, constants)
  global.combatstate_commandaction = {}
  global.bCommand_Action = false
  global.combatstate_commandaction.waitForContext = false
  global.combatstate_commandaction.runTimer = 0
  global.combatstate_commandaction.shootTimer = 0
  global.combatstate_commandaction.moveName = ""
  global.combatstate_commandaction.arrowTweakName = ""
  global.ChargedOrRegular = "none"
  global.shootTargetType = "reticle"
end
function Command_Action:Enter(ai, global, constants)
  ai:AddMarker("DoNotInterrupt")
  if ai.OwnedPOI ~= nil then
    ai.OwnedPOI:ResetApproachTimer()
  end
end
function Command_Action:IsAvailable(ai, global, constants)
  return global.bCommand_Action and global.bStartCoopMove == false and (ai:IsDoingSyncMove() == false or ai:IsDoingSyncMove() and ai:HasMarker("AllowSyncShots"))
end
function Command_Action:BowAction_Rapid_FirstShot(ai, global, constants)
  local targetOfPlayer = helper.returnBestPlayerTargetCreature(global, true)
  global.sTargetMode = "SpecialAim"
  constants.targetOfPlayer = targetOfPlayer
  ai:SetCombatTarget(constants.targetOfPlayer)
  global.combatstate_commandaction.shootTimer = 0
  local arrowData = {}
  arrowData.Tweak = global.combatstate_commandaction.arrowTweakName
  arrowData.Creator = ai
  arrowData.CreatorEmitJoint = "JORightWrist1Tip"
  if targetOfPlayer == nil then
    arrowData.Target = nil
    arrowData.TargetLocation = global.rayCastHitLocation
    arrowData.Tweak = global.combatstate_commandaction.arrowTweakName .. "_NOTARGET"
  else
    arrowData.Target = targetOfPlayer
  end
  game.Combat.EmitArrow(arrowData)
  global.combatstate_commandaction.potentialTargets = helper.getOnScreenEnemySorted()
  if targetOfPlayer ~= nil then
    for k, v in ipairs(global.combatstate_commandaction.potentialTargets) do
      if v == targetOfPlayer then
        table.remove(global.combatstate_commandaction.potentialTargets, k)
        break
      end
    end
    table.insert(global.combatstate_commandaction.potentialTargets, 1, targetOfPlayer)
  end
  global.combatstate_commandaction.potentialTargetsIndex = 1
end
function Command_Action:BowAction(ai, global, constants)
  DL.switch(global.combatstate_commandaction.Command_ActionPhase):caseof({
    [0] = function()
      if global.Player:GetReticleState().InvalidTarget then
        global.combatstate_commandaction.Command_ActionPhase = 2
        return
      end
      global.combatstate_commandaction.Command_ActionPhase = 1
      global.combatstate_commandaction.shootTimer = 0
      constants.targetOfPlayer = nil
      global.bDodging = false
      global.previousTargetMode = global.sTargetMode
      global.sTargetMode = "SpecialAim"
      local arrowstage = not ai:PickupIsAcquired("SonArrow_Regular")
      local sonBB = game.AI.FindSon():GetPrivateBlackboard()
      local potentialTrackObject
      if sonBB:IsObject("PotentialArrowTrackObject") then
        potentialTrackObject = sonBB:GetObject("PotentialArrowTrackObject")
      end
      if potentialTrackObject ~= nil and arrowstage then
        local penAmount = game.Camera.GetViewPenetration(potentialTrackObject.WorldPosition, 0, 0.158)
        if 0.5 < penAmount then
          sonBB:Set("ArrowTrackObject", potentialTrackObject)
          sonBB:Set("ArrowTrackConcussion", 1)
          global.shootTargetType = "trackObject"
          return
        end
      end
      local amplifierPOIs = game.POI.FindAdvertising(global.Player:GetWorldPosition(), 50, {
        "SON",
        "AMPLIFIER",
        MatchAll = true
      })
      local bestPOIPen = 0.5
      local thisAmpPOI
      for _, poi in ipairs(amplifierPOIs) do
        local checkPos = poi:GetWorldPosition()
        local penAmount = game.Camera.GetViewPenetration(checkPos, 0, 0.158)
        if bestPOIPen < penAmount then
          bestPOIPen = penAmount
          thisAmpPOI = poi
        end
      end
      if thisAmpPOI ~= nil and arrowstage then
        sonBB:Set("ArrowTrackObject", thisAmpPOI)
        sonBB:Set("ArrowTrackConcussion", 1)
        global.shootTargetType = "trackObject"
        thisAmpPOI:SetEvent("CoolDown")
        return
      end
      helper.ReturnBestTargetObject(global, constants)
      local targetOfPlayer = constants.shootTargetInfo.target
      constants.targetOfPlayer = targetOfPlayer
      ai:SetCombatTarget(constants.targetOfPlayer)
      if targetOfPlayer ~= nil then
        Fire_AllTargets.ManualSetTarget(Fire_AllTargets, ai, global, targetOfPlayer)
      end
      if targetOfPlayer == nil then
        global.shootTargetType = "reticle"
      else
        global.shootTargetType = "combatTarget"
      end
      local actionName = global.combatstate_commandaction.moveName
      helper.CallTurnShootEvent(ai, global, constants, constants.shootTargetInfo.position, true)
      local shouldBreakOut = false
      global.combatstate_commandaction.Command_ActionPhase = 2
    end,
    [1] = function()
      if ai:MeterGetValue("Son_ArrowCount") < constants.actionTypes[global.actionTypeIndex].cost and not gVFSInfiniteMeter.value then
        global.combatstate_commandaction.Command_ActionPhase = 2
      else
        local shootNow = true
        local charged = false
        if shootNow == true then
          local actionName = global.combatstate_commandaction.moveName
          if global.E3_SpecialArrow then
            ai:TriggerMoveEvent("kLE_E3TrollShootArrow")
          elseif constants.targetOfPlayer == nil then
            if global.rayCastHitLocation ~= nil then
              helper.CallTurnShootEvent(ai, global, constants, global.rayCastHitLocation, true)
            end
          else
            helper.CallTurnShootEvent(ai, global, constants, constants.targetOfPlayer.WorldPosition, true)
          end
          global.combatstate_commandaction.Command_ActionPhase = 5
        end
      end
    end,
    [2] = function()
      if DL.CheckCreatureContext(ai:GetContext(), "SON_COOLDOWN") == false then
        global.bCommand_Action = false
      end
      global.combatstate_commandaction.shootTimer = global.combatstate_commandaction.shootTimer + ai:GetFrameTime()
    end,
    [5] = function()
      if global.ChargedOrRegular ~= "none" then
        global.combatstate_commandaction.Command_ActionPhase = 2
        global.ChargedOrRegular = "none"
      end
    end
  })
end
function Command_Action:KnifeAction(ai, global, constants)
  DL.switch(global.combatstate_commandaction.Command_ActionPhase):caseof({
    [0] = function()
      global.combatstate_commandaction.ShootNow = false
      global.combatstate_commandaction.holdTimer = 0
      global.combatstate_commandaction.runTimer = 0
      local targetOfPlayer = helper.returnBestPlayerTargetCreature(_G.global, true)
      global.sTargetMode = "SpecialAim"
      constants.targetOfPlayer = targetOfPlayer
      ai:SetCombatTarget(constants.targetOfPlayer)
      if targetOfPlayer ~= nil then
        ai:TriggerMoveEvent("kLE_DashAttack")
        global.combatstate_commandaction.Command_ActionPhase = 1
        self.targetOfPlayer = targetOfPlayer
      else
        targetOfPlayer = nil
        global.combatstate_commandaction.Command_ActionPhase = 2
      end
    end,
    [1] = function()
      if ai:IsInNavigationMove() then
        global.combatstate_commandaction.Command_ActionPhase = 2
      end
    end,
    [2] = function()
      global.combatstate_commandaction.runTimer = global.combatstate_commandaction.runTimer + ai:GetFrameTime()
      global.bCommand_Action = false
    end
  })
end
function Command_Action:SuperMove(ai, global, constants)
  DL.switch(global.combatstate_commandaction.Command_ActionPhase):caseof({
    [0] = function()
      if ai:HasMarker("HoldEnemy") then
        ai:TriggerMoveEvent("kLEForceSummon")
        global.combatstate_commandaction.Command_ActionPhase = 1
      else
        global.combatstate_commandaction.Command_ActionPhase = 2
      end
    end,
    [1] = function()
      local pickupname = global.Player:PickupGetPickupNameInSlot("SonSpecial")
      if pickupname ~= nil and pickupname ~= "" and global.Player:PickupIsAcquired(pickupname) then
        if pickupname == "SummonBird" or pickupname == "SummonSouls" or pickupname == "SummonFenrir" then
          if ai:IsInNavigationMove() or ai:HasMarker("SummonInAirFlag") then
            global.combatstate_commandaction.Command_ActionPhase = 2
          end
        elseif ai:IsInNavigationMove() then
          global.combatstate_commandaction.Command_ActionPhase = 2
        end
      else
        global.combatstate_commandaction.Command_ActionPhase = 3
      end
    end,
    [2] = function()
      local targetOfPlayer = helper.returnBestPlayerTargetCreature(_G.global, true)
      constants.targetOfPlayer = targetOfPlayer
      ai:SetCombatTarget(constants.targetOfPlayer)
      global.previousTargetMode = global.sTargetMode
      global.sTargetMode = "SpecialAim"
      local pickupname = global.Player:PickupGetPickupNameInSlot("SonSpecial")
      local rotPos = global.rayCastHitLocation
      if constants.targetOfPlayer ~= nil then
        rotPos = constants.targetOfPlayer.WorldPosition
      else
        local ray = game.World.GetReticleRay("ret_aim")
        local pos = ray.Position
        local dir = ray.Direction
        local collisionParams = {
          SourceGameObject = _G.global.selfAI,
          EntityType = game.CollisionType.New("kEnvironment"),
          RejectMaterialFlags = game.GroundType.New("kGT_CosmeticIK", "kGT_Death", "kGT_InstantDeath", "kGT_NoTargetThru")
        }
        rotPos = pos + dir * 30
        local hit = game.World.RaycastCollision(pos, rotPos, collisionParams)
        if hit ~= nil then
          rotPos = hit.Position
        end
      end
      if pickupname ~= nil and pickupname == "SummonDeer" then
        rotPos = global.Player.WorldPosition
      end
      if rotPos ~= nil then
        if not global.warpTargetRot then
          global.warpTargetRot = ai:GetAnimDriver("WarpTargetRot")
        end
        global.warpTargetRot.ValueVec = rotPos - ai.WorldPosition
      end
      if pickupname ~= nil and pickupname ~= "" and global.Player:PickupIsAcquired(pickupname) then
        local activatePickup = true
        local sonPOI = ai.OwnedPOI
        if sonPOI then
          sonPOI:SendEvent("BreakOut")
        end
        if pickupname == "SummonFenrir" then
          if _G.stats.VFSsummonAction.value == 1 then
            ai:TriggerMoveEvent("kLE_SummonFenrir")
          elseif _G.stats.VFSsummonAction.value == 2 then
            ai:TriggerMoveEvent("kLE_SummonSouls")
          elseif _G.stats.VFSsummonAction.value == 3 then
            ai:TriggerMoveEvent("kLE_SummonBoar")
          elseif _G.stats.VFSsummonAction.value == 4 then
            ai:TriggerMoveEvent("kLE_SummonRatatoskr")
          elseif _G.stats.VFSsummonAction.value == 5 then
            ai:TriggerMoveEvent("kLE_SummonBird")
          elseif _G.stats.VFSsummonAction.value == 6 then
            ai:TriggerMoveEvent("kLE_SummonDeer")
          end
        elseif pickupname == "SummonBird" then
          ai:TriggerMoveEvent("kLE_SummonBird")
        elseif pickupname == "SummonDeer" then
          ai:TriggerMoveEvent("kLE_SummonDeer")
        elseif pickupname == "SummonRatatoskr" then
          if game.Combat.GetCombatStatus() then
            ai:TriggerMoveEvent("kLE_SummonRatatoskr_Combat")
          else
            ai:TriggerMoveEvent("kLE_SummonRatatoskr")
          end
        elseif pickupname == "SummonBoar" then
          ai:TriggerMoveEvent("kLE_SummonBoar")
        elseif pickupname == "SummonSouls" then
          ai:TriggerMoveEvent("kLE_SummonSouls")
        elseif pickupname == "SummonDragon" then
          ai:TriggerMoveEvent("kLE_SummonDragon")
        elseif pickupname == "SonSpecial_PowerShot" then
          ai:TriggerMoveEvent("kLE_PowerShot")
        elseif pickupname == "SonSpecial_SpreadShot" then
          ai:TriggerMoveEvent("kLE_SpreadShot")
        elseif pickupname == "SonSpecial_ArcShot" then
          ai:TriggerMoveEvent("kLE_ArcShot")
        elseif pickupname == "SonSpecial_ScatterShot" then
          ai:TriggerMoveEvent("kLE_ScatterShot")
        elseif pickupname == "SonSpecial_RainShot" then
          ai:TriggerMoveEvent("kLE_RainShot")
        elseif pickupname == "SonSpecial_Grab" then
          local targetOfPlayer = helper.returnBestPlayerTargetCreature(global, true)
          global.previousTargetMode = global.sTargetMode
          global.sTargetMode = "SpecialAim"
          constants.targetOfPlayer = targetOfPlayer
          ai:SetCombatTarget(constants.targetOfPlayer)
          if targetOfPlayer ~= nil then
            ai:AddMarker("DoNotInterrupt")
            ai:TriggerMoveEvent("kLE_ForceDashAttack")
          else
            activatePickup = false
          end
        elseif pickupname == "SonSpecial_Lockdown" then
          ai:TriggerMoveEvent("kLE_RainShot")
        elseif pickupname == "SonSpecial_CloseCombatLaunch" then
          ai:TriggerMoveEvent("kLE_RainShot")
        end
        if activatePickup then
          global.Player:PickupActivate(pickupname)
        end
        if ai:PickupIsAcquired("SonBow") and 5 <= ai:PickupGetStage("SonBow") then
          ai:MeterSetValue("Son_ArrowCount", 40)
        end
      end
      global.combatstate_commandaction.Command_ActionPhase = 3
    end,
    [3] = function()
      global.combatstate_commandaction.Command_ActionPhase = 4
    end,
    [4] = function()
      global.combatstate_commandaction.runTimer = global.combatstate_commandaction.runTimer + ai:GetFrameTime()
      if DL.CheckCreatureContext(ai:GetContext(), "SON_COOLDOWN") == false then
        global.bCommand_Action = false
      end
    end
  })
end
function Command_Action:Disobey(ai, global, constants)
  DL.switch(global.combatstate_commandaction.Command_ActionPhase):caseof({
    [0] = function()
      global.combatstate_commandaction.runTimer = 0
      game.Audio.PlayBanterNonCritical("FireJerkArrow")
      global.combatstate_commandaction.Command_ActionPhase = 1
    end,
    [1] = function()
      global.combatstate_commandaction.Command_ActionPhase = 2
    end,
    [2] = function()
      global.combatstate_commandaction.runTimer = global.combatstate_commandaction.runTimer + ai:GetFrameTime()
      if DL.CheckCreatureContext(ai:GetContext(), "SON_COOLDOWN") == false or global.combatstate_commandaction.runTimer > 3 then
        global.bCommand_Action = false
      end
    end
  })
end
function Command_Action:Update(ai, global, constants)
  if global.combatstate_commandaction.moveName == "kLECommandNonCombat" then
    ai:TriggerMoveEvent("kLECommandNonCombat")
    global.bCommand_Action = false
    return
  end
  if constants.actionTypes[global.actionTypeIndex].actionName == "Knife" then
    Command_Action:KnifeAction(ai, global, constants)
  elseif constants.actionTypes[global.actionTypeIndex].actionName == "SuperMove" then
    Command_Action:SuperMove(ai, global, constants)
  elseif constants.actionTypes[global.actionTypeIndex].actionName == "Disobey" then
    Command_Action:Disobey(ai, global, constants)
  else
    Command_Action:BowAction(ai, global, constants)
  end
end
function Command_Action:Exit(ai, global, constants)
  global.bCommand_Action = false
  ai:RemoveMarker("DoNotInterrupt")
  if ai:PickupIsAcquired("SonChargeRune") then
    ai:PickupRelinquish("SonChargeRune")
  end
  FollowUpAttack.FollowUpAttackStart = false
  global.sTargetMode = global.previousTargetMode
  ai:ClearForcedPath()
  global.UseForcedPath = false
end
function LockDownTargets:OnBrainInit(ai, global, constants)
  self.lockDownTimer = 120
  self.lockDownTimerMax = 120
  self.isAvailable = false
  self.ShootState = "Init"
  constants.inLockDown = false
  constants.lockDownRanged = false
  self.allTargets = {}
  self.side = "none"
  self.isItAllTargets = false
  self.lockDownLength = 7
  self.lockDownLengthMax = 7
  constants.lockDownPursue = false
  constants.boostSpeed = false
end
function LockDownTargets:FireBanter(ai, global, constants)
  local numTargets = #self.allTargets
  if self.isItAllTargets then
    if self.side == "front" then
      if numTargets == 1 then
        game.Audio.PlayBanterNonCritical("DivideTheField_InfrontCamera_Single")
      else
        game.Audio.PlayBanterNonCritical("DivideTheField_InfrontCamera_Multiple")
      end
    elseif self.side == "back" then
      if numTargets == 1 then
        game.Audio.PlayBanterNonCritical("DivideTheField_BehindCamera_Single")
      else
        game.Audio.PlayBanterNonCritical("DivideTheField_BehindCamera_Multiple")
      end
    elseif self.side == "left" then
      if numTargets == 1 then
        game.Audio.PlayBanterNonCritical("DivideTheField_LeftOfCamera_Single")
      else
        game.Audio.PlayBanterNonCritical("DivideTheField_LeftOfCamera_Multiple")
      end
    elseif self.side == "right" then
      if numTargets == 1 then
        game.Audio.PlayBanterNonCritical("DivideTheField_RightOfCamera_Single")
      else
        game.Audio.PlayBanterNonCritical("DivideTheField_RightOfCamera_Multiple")
      end
    end
  elseif numTargets == 1 then
    game.Audio.PlayBanterNonCritical("cbt_son_pull_aggro_single")
  else
    game.Audio.PlayBanterNonCritical("cbt_son_pull_aggro_multiple")
  end
end
function LockDownTargets:Enter(ai, global, constants)
  constants.inLockDown = true
  ai:AddMarker("DoNotFollowup")
  for i = #self.allTargets, 1, -1 do
    if self.allTargets[i] == nil then
      table.remove(self.allTargets, i)
    end
  end
  local numTargets = #self.allTargets
  if 0 < numTargets then
    constants.NumTargetShot = 0
    global.targetOverride = true
    self.retargetNewPeople = false
    constants.currentEnemyIndex = 1
  else
    self.isAvailable = false
  end
  self.Color = math.random(256, 16777216)
end
function LockDownTargets:Update(ai, global, constants)
  if #constants.currentTargetList ~= #self.allTargets then
    local difference = #self.allTargets - #constants.currentTargetList
    if constants.currentEnemyIndex > 1 then
      constants.currentEnemyIndex = constants.currentEnemyIndex - difference
    end
    if constants.currentEnemyIndex < 1 then
      constants.currentEnemyIndex = 1
    end
    self.allTargets = constants.currentTargetList
  end
  if #self.allTargets < 1 then
    self.ShootState = "Done"
  end
  if self.ShootState == "Init" then
    self.ShootState = "PursueTarget"
    self.PursueTargetTimer = 1
    global.sTargetMode = "TargetList"
    local nearestTarget
    local nearestDistance = 9999
    for _, i in ipairs(self.allTargets) do
      local disttoenemy = (ai.WorldPosition - i.WorldPosition):Length()
      if nearestDistance > disttoenemy then
        nearestTarget = i
        nearestDistance = disttoenemy
      end
    end
    if nearestTarget ~= nil then
      ai:SetCombatTarget(nearestTarget)
      global.FollowUpTarget = nearestTarget
      if 7 < nearestDistance then
        ai:TriggerMoveEvent("kLE_PanicSurrounded")
      end
    end
  elseif self.ShootState == "PursueTarget" then
    local pursueTarget = ai:GetTargetCreature()
    constants.boostSpeed = true
    constants.lockDownPursue = true
    self.PursueTargetTimer = self.PursueTargetTimer - ai:GetFrameTime()
    if pursueTarget ~= nil then
      global.bGoToTarget = true
      if (pursueTarget.WorldPosition - ai.WorldPosition):Length() < 5 or self.PursueTargetTimer <= 0 then
        self.ShootState = "SetTarget"
        self.allTargets, self.side, self.isItAllTargets = ReturnClusterQuadrant(ai, global, constants, self.allTargets)
        LockDownTargets:FireBanter(ai, global, constants)
      end
    else
      self.ShootState = "SetTarget"
      self.allTargets, self.side, self.isItAllTargets = ReturnClusterQuadrant(ai, global, constants, self.allTargets)
      LockDownTargets:FireBanter(ai, global, constants)
    end
  elseif self.ShootState == "SetTarget" then
    constants.boostSpeed = false
    global.bGoToTarget = false
    local thisTarget = self.allTargets[constants.currentEnemyIndex]
    if constants.currentEnemyIndex <= #self.allTargets and thisTarget ~= nil then
      ai:SetCombatTarget(thisTarget)
      ai:SetFocus(thisTarget)
      global.combatTarget = thisTarget
      constants.NumTargetShot = constants.NumTargetShot + 1
      self.targetSelectionTimer = 0
      self.ShootState = "AcquireTarget"
    end
  elseif self.ShootState == "AcquireTarget" then
    self.targetSelectionTimer = self.targetSelectionTimer + ai:GetFrameTime()
    if self.allTargets[constants.currentEnemyIndex] == ai:GetTargetCreature() then
      self.ShootState = "ShootTarget"
      self.targetSelectionTimer = 0
    elseif self.targetSelectionTimer > 2 then
      self.ShootState = "Done"
    end
  elseif self.ShootState == "ShootTarget" then
    if self.allTargets[constants.currentEnemyIndex] ~= nil then
      helper.CallTurnShootEvent(ai, global, constants, self.allTargets[constants.currentEnemyIndex].WorldPosition, false, false)
    end
    constants.currentEnemyIndex = constants.currentEnemyIndex + 1
    self.ShootState = "PickReloadType"
  elseif self.ShootState == "PickReloadType" then
    if constants.currentEnemyIndex > #Fire_AllTargets.allTargets then
      ai:TriggerMoveEvent("kLE_ReloadFeather")
    else
      ai:TriggerMoveEvent("kLE_Reload")
    end
    self.ShootState = "WaitForShotDone"
  elseif self.ShootState == "WaitForShotDone" then
    if DL.CheckCreatureContext(ai:GetContext(), "ARROWSHOT_DONE") == true or ai:IsInNavigationMove() then
      if constants.currentEnemyIndex > #self.allTargets then
        constants.currentEnemyIndex = 1
        self.ShootState = "Reposition"
        constants.fightPositionCurrent = constants.fightPositionNew
        if #self.allTargets == 1 then
          self.repositionTime = math.random(3, 5)
        else
          self.repositionTime = math.random(3, 6)
        end
      else
        self.ShootState = "SetTarget"
      end
    end
  elseif self.ShootState == "Reposition" then
    local thisTarget = self.allTargets[constants.currentEnemyIndex]
    if thisTarget == nil and self.allTargets[1] ~= nil then
      ai:SetCombatTarget(self.allTargets[1])
    end
    self.repositionTime = self.repositionTime - ai:GetFrameTime()
    if 0 > self.repositionTime and global.nearDestination then
      self.ShootState = "SetTarget"
    end
    if 0 >= self.lockDownLength then
      self.ShootState = "Done"
    end
  elseif self.ShootState == "Done" then
    self.ShootState = "Init"
    self.isAvailable = false
    game.Audio.PlayBanterNonCritical("DivideTheField_Disengage")
  end
  self.lockDownLength = self.lockDownLength - ai:GetFrameTime()
  if gVFSDebug.value then
    for _, i in ipairs(self.allTargets) do
      engine.DrawFillSphere(i.WorldPosition + engine.Vector.New(0, 1.5, 0), 0.5, self.Color)
    end
    engine.DrawTextInWorld(global.Player.WorldPosition + engine.Vector.New(0, 1.5, 0), "FATHER, I'll cover your : " .. self.side, self.Color)
    engine.DrawTextInWorld(global.Player.WorldPosition + engine.Vector.New(0, 1.3, 0), "Im in  : " .. self.ShootState, self.Color)
  end
end
function LockDownTargets:UpdateData_Old(ai, global, constants)
  if self.isAvailable == false and self.ShootState ~= "Done" then
    self.lockDownTimer = self.lockDownTimer - ai:GetFrameTime()
    if self.lockDownTimer <= 0 and constants.awarenessState == _G.constants.CombatSteady and ai:IsInNavigationMove() then
      self.allTargets, self.side, self.isItAllTargets = ReturnClusterQuadrant(ai, global, constants)
      if self.side ~= "none" then
        local targetcount = #self.allTargets
        local maxCount = 3
        local allEnemyCount = #DL.FindLivingEnemies(ai, 50) / 2
        if maxCount > allEnemyCount then
          maxCount = allEnemyCount
        end
        if maxCount <= 0 then
          self.lockDownTimer = 1
          return
        end
        if targetcount > maxCount then
          for i = targetcount, 1, -1 do
            table.remove(self.allTargets, i)
          end
        end
        self.isAvailable = true
        self.lockDownLength = self.lockDownLengthMax
        constants.lockDownRanged = false
        local sonBB = ai:GetPrivateBlackboard()
        local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
        if enemyBBLenth ~= nil and 0 < enemyBBLenth then
          bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
        end
        bboardUtil.SetList(sonBB, global.enemyBlackBoardIndex, self.allTargets)
        constants.currentTargetList = self.allTargets
        self.lockDownTimer = self.lockDownTimerMax
      else
        self.lockDownTimer = 1
      end
    end
  end
end
function LockDownTargets.Events:OnBroadcastReaction(event, ai, global, constants)
end
function LockDownTargets.Events:LockDownRanged(ai, global, constants)
  if LockDownTargets.isAvailable == false then
    LockDownTargets.allTargets = {}
    table.insert(self.allTargets, constants.lockdownTarget)
    LockDownTargets.isAvailable = true
    LockDownTargets.ShootState = "Init"
    LockDownTargets.isItAllTargets = false
    constants.lockDownRanged = true
    LockDownTargets.lockDownLength = 12
    local sonBB = ai:GetPrivateBlackboard()
    local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
    if enemyBBLenth ~= nil and 0 < enemyBBLenth then
      bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
    end
    bboardUtil.SetList(sonBB, global.enemyBlackBoardIndex, LockDownTargets.allTargets)
    constants.currentTargetList = LockDownTargets.allTargets
    LockDownTargets.lockDownTimer = 12
    FollowUpAttackPriority:Reset("ProtectProjectile")
  end
end
function LockDownTargets:IsAvailable(ai, global, constants)
  return gVFSLockDownUpgraded.value and self.isAvailable and constants.awarenessState ~= _G.constants.SurvivalReposition and not ai:HasMarker("DisableLockdown")
end
function LockDownTargets:Exit(ai, global, constants)
  constants.inLockDown = false
  ai:RemoveMarker("DoNotFollowup")
  constants.lockDownPursue = false
  constants.boostSpeed = false
  global.targetOverride = false
  global.bGoToTarget = false
  if constants.awarenessState == _G.constants.SurvivalReposition then
    game.Audio.PlayBanterNonCritical("DivideTheField_Overwhelmed")
  end
end
function LuaHook_InLockdown(ai, data)
  return _G.constants.inLockDown
end
function AreAllInCluster(enemyTable, radius)
  for _, i in ipairs(enemyTable) do
    for _, j in ipairs(enemyTable) do
      if i ~= j and radius < (i.WorldPosition - j.WorldPosition):Length() then
        return false
      end
    end
  end
  return true
end
function ReturnClusterQuadrant(ai, global, constants, potentialCluster)
  local allEnemies = helper.ReturnAllValidAggroableCreatures(ai, global, constants, 50)
  local player = game.Player.FindPlayer()
  local playerTarget = player:GetTargetCreature()
  local frontQuad = {}
  local frontQuadRejects = {}
  local backQuad = {}
  local backQuadRejects = {}
  local leftQuad = {}
  local leftQuadRejects = {}
  local rightQuad = {}
  local rightQuadRejects = {}
  if #allEnemies < 2 then
    return {}, "none"
  end
  local myQuadrant = "none"
  local myAngle = DL.FrontAngleFromPoint(ai, global.Player.WorldPosition, game.Camera:GetOrbitForward())
  if -75 <= myAngle and myAngle < 75 then
    myQuadrant = "front"
  elseif -155 <= myAngle and myAngle < -75 then
    myQuadrant = "left"
  elseif 75 <= myAngle and myAngle < 155 then
    myQuadrant = "right"
  elseif myAngle < -155 and myAngle <= -180 or 155 <= myAngle and myAngle <= 180 then
    myQuadrant = "back"
  end
  for _, i in ipairs(allEnemies) do
    if playerTarget ~= i then
      local dist = (player.WorldPosition - i.WorldPosition):Length()
      local distToPlayer = (player.WorldPosition - ai.WorldPosition):Length()
      if dist > distToPlayer then
        local angle = DL.FrontAngleFromPoint(i, global.Player.WorldPosition, game.Camera:GetOrbitForward())
        if -75 <= angle and angle < 75 and myQuadrant ~= "back" then
          if 12 < dist and i:IsAggressive() == false then
            table.insert(frontQuad, i)
          else
            table.insert(frontQuadRejects, i)
          end
        elseif -155 <= angle and angle < -75 and myQuadrant ~= "right" then
          if 2 < dist then
            table.insert(leftQuad, i)
          else
            table.insert(leftQuadRejects, i)
          end
        elseif 75 <= angle and angle < 155 and myQuadrant ~= "left" then
          if 2 < dist then
            table.insert(rightQuad, i)
          else
            table.insert(rightQuadRejects, i)
          end
        elseif angle < -155 and angle <= -180 or 155 <= angle and angle <= 180 and myQuadrant ~= "front" then
          if 1 < dist then
            table.insert(backQuad, i)
          else
            table.insert(backQuadRejects, i)
          end
        end
      end
    end
  end
  if potentialCluster ~= nil then
    local all = false
    local quad
    local theMatch = true
    if LockDownTargets.side == "right" and #rightQuadRejects <= 0 and #potentialCluster == #rightQuad then
      for _, i in ipairs(potentialCluster) do
        local isMatch = false
        for _, j in ipairs(rightQuad) do
          if i == j then
            isMatch = true
          end
        end
        if isMatch == false then
          theMatch = false
          break
        end
      end
      if theMatch then
        return potentialCluster, "right", true
      end
    end
    theMatch = true
    if LockDownTargets.side == "left" and #leftQuadRejects <= 0 and #potentialCluster == #leftQuad then
      for _, i in ipairs(potentialCluster) do
        local isMatch = false
        for _, j in ipairs(leftQuad) do
          if i == j then
            isMatch = true
          end
        end
        if isMatch == false then
          theMatch = false
          break
        end
      end
      if theMatch then
        return potentialCluster, "left", true
      end
    end
    theMatch = true
    if LockDownTargets.side == "front" and #frontQuadRejects <= 0 and #potentialCluster == #frontQuad then
      for _, i in ipairs(potentialCluster) do
        local isMatch = false
        for _, j in ipairs(frontQuad) do
          if i == j then
            isMatch = true
          end
        end
        if isMatch == false then
          theMatch = false
          break
        end
      end
      if theMatch then
        return potentialCluster, "front", true
      end
    end
    theMatch = true
    if LockDownTargets.side == "back" and #backQuadRejects <= 0 and #potentialCluster == #backQuad then
      for _, i in ipairs(potentialCluster) do
        local isMatch = false
        for _, j in ipairs(backQuad) do
          if i == j then
            isMatch = true
          end
        end
        if isMatch == false then
          theMatch = false
          break
        end
      end
      if theMatch then
        return potentialCluster, "back", true
      end
    end
    return potentialCluster, "none", false
  end
  local frontQuadNum = #frontQuad
  local backQuadNum = #backQuad
  local leftQuadNum = #leftQuad
  local rightQuadNum = #rightQuad
  local clusterRadius = 5
  if 1 <= rightQuadNum and frontQuadNum <= rightQuadNum and leftQuadNum <= rightQuadNum and backQuadNum <= rightQuadNum then
    if AreAllInCluster(rightQuad, clusterRadius) then
      local all = false
      if #rightQuadRejects == 0 and rightQuadNum <= 3 then
        all = true
      end
      return rightQuad, "right", all
    end
  elseif 1 <= leftQuadNum and frontQuadNum <= leftQuadNum and leftQuadNum >= rightQuadNum and backQuadNum <= leftQuadNum then
    if AreAllInCluster(leftQuad, clusterRadius) then
      local all = false
      if #leftQuadRejects == 0 and leftQuadNum <= 3 then
        all = true
      end
      return leftQuad, "left", all
    end
  elseif 1 <= frontQuadNum and frontQuadNum >= leftQuadNum and frontQuadNum >= rightQuadNum and frontQuadNum >= backQuadNum then
    if AreAllInCluster(frontQuad, clusterRadius) then
      local all = false
      if #frontQuadRejects == 0 and frontQuadNum <= 3 then
        all = true
      end
      return frontQuad, "front", all
    end
  elseif 1 <= backQuadNum and frontQuadNum <= backQuadNum and backQuadNum >= rightQuadNum and backQuadNum >= leftQuadNum and AreAllInCluster(backQuad, clusterRadius) then
    local all = false
    if #backQuadRejects == 0 and backQuadNum <= 3 then
      all = true
    end
    return backQuad, "back", all
  end
  return {}, "none", false
end
local Fire_AllTargets_Timer = Brain:Timer("Fire_AllTargets_Timer") .. {
  period = _G.stats.VFSshotFrequency.value,
  autostart = true
}
function Fire_AllTargets_Timer.Events:OnStartShootBow_NormalCooldown(timer, ai, global, constants)
  global.fireAtAllTargets = false
  timer:Restart()
end
function Fire_AllTargets_Timer:OnTimerComplete(timer, ai, global, constants)
  global.fireAtAllTargets = true
  if ai:CheckDecision("tweak_Decision_LevelVariable_SetSonBehind") then
    timer.period = 15
  else
    timer.period = _G.stats.VFSshotFrequency.value
  end
end
function Fire_AllTargets:ManualSetTarget(ai, global, target)
  self.allTargets = {}
  if target ~= nil then
    table.insert(self.allTargets, target)
  end
end
function Fire_AllTargets:OnBrainInit(ai, global, constants)
  global.fireAtAllTargets = false
  self.numTargets = 1
  self.allTargets = {}
  constants.NumTargetShot = 0
  self.targetSelectionTimer = 0
  constants.currentTargets = {}
  constants.numCurrentTargets = 0
  constants.currentEnemyIndex = 1
end
function Fire_AllTargets:Enter(ai, global, constants)
  self.ShootState = "CalculateTargets"
  self.Timer1 = 0
  constants.NumTargetShot = 0
  global.targetOverride = true
  self.retargetNewPeople = false
  constants.currentEnemyIndex = 1
  self.responseAfterShot = "normal"
end
function LuaHook_IsStrafing(ai, data)
  return data:FindOutcomeBranchesEntry("TRUE")
end
function Fire_AllTargets:Update(ai, global, constants)
  if self.ShootState == "CalculateTargets" then
    if gVFSDebugAutonomousShoot.value == true then
      self.allTargets = DL.FindLivingEnemies(ai, 50)
    else
      Fire_AllTargets:ClusterCalculation(ai, global, constants)
    end
    self.ShootState = "SetTarget"
  elseif self.ShootState == "SetTarget" then
    local thisTarget = self.allTargets[constants.currentEnemyIndex]
    if constants.currentEnemyIndex <= #self.allTargets and thisTarget ~= nil and ValidEnemyCheck(ai, global, constants, thisTarget) then
      ai:SetCombatTarget(thisTarget)
      ai:SetFocus(thisTarget)
      global.sTargetMode = "TargetList"
      global.combatTarget = thisTarget
      constants.NumTargetShot = constants.NumTargetShot + 1
      self.targetSelectionTimer = 0
      self.ShootState = "AcquireTarget"
    else
      self.ShootState = "Done"
    end
    if ai:PickupIsAcquired("SonChargeRune") then
      ai:PickupRelinquish("SonChargeRune")
    end
  elseif self.ShootState == "AcquireTarget" then
    self.targetSelectionTimer = self.targetSelectionTimer + ai:GetFrameTime()
    if self.allTargets[constants.currentEnemyIndex] == ai:GetTargetCreature() then
      self.ShootState = "ShootTarget"
      self.targetSelectionTimer = 0
    elseif self.targetSelectionTimer > 2 then
      self.ShootState = "Done"
    end
  elseif self.ShootState == "ShootTarget" then
    local crit = false
    if math.random(1, 101) <= _G.stats.VFSspecialShotChance.value then
      crit = true
      self.responseAfterShot = "crit"
    end
    local luaeventstring = "SHOOT_ALL"
    if constants.currentEnemyIndex == self.numTargets then
      luaeventstring = luaeventstring .. "_LAST"
    end
    if self.numTargets == 1 then
      luaeventstring = "SHOOT_ONE"
    end
    if self.allTargets[constants.currentEnemyIndex] ~= nil then
      helper.CallTurnShootEvent(ai, global, constants, self.allTargets[constants.currentEnemyIndex].WorldPosition, false, false)
      if #self.allTargets >= 1 then
        if crit == true then
          if not ai:PickupIsAcquired("SonChargeRune") then
            ai:PickupAcquire("SonChargeRune")
          end
        elseif self.retargetNewPeople == true and self.shotOnce == false then
          self.shotOnce = true
        end
      end
    end
    constants.currentEnemyIndex = constants.currentEnemyIndex + 1
    self.ShootState = "PickReloadType"
  elseif self.ShootState == "PickReloadType" then
    if constants.currentEnemyIndex > #self.allTargets then
      ai:TriggerMoveEvent("kLE_ReloadFeather")
    else
      ai:TriggerMoveEvent("kLE_Reload")
    end
    self.ShootState = "WaitForShotDone"
  elseif self.ShootState == "WaitForShotDone" then
    if DL.CheckCreatureContext(ai:GetContext(), "ARROWSHOT_DONE") == true or ai:IsInNavigationMove() then
      self.ShootState = "SetTarget"
    end
  elseif self.ShootState == "Done" then
    if self.responseAfterShot == "crit" then
      ai:TriggerMoveEvent("kLE_CritShotHappened")
    end
    global.fireAtAllTargets = false
  end
end
function Fire_AllTargets:GrabUnaggressiveEnemies(ai, global, constants)
  constants.NumTargetShot = 0
  global.targetOverride = true
  self.retargetNewPeople = false
  local player = global.Player
  local newEnemies = DL.FindLivingEnemies(ai, 50)
  local maxEnemyTargetable = math.floor(#newEnemies / 2)
  if maxEnemyTargetable > _G.stats.VFSmaxShots.value then
    maxEnemyTargetable = _G.stats.VFSmaxShots.value
  end
  if #self.allTargets ~= 0 then
    local validTargets = {}
    local atleastEnemyIsClose = false
    local atleastOneGuy
    for _, thisEnemy in ipairs(self.allTargets) do
      if thisEnemy ~= nil then
        local distanceToEnemy = thisEnemy.WorldPosition:Distance(player.WorldPosition)
        if distanceToEnemy < 30 and 7 < distanceToEnemy and ValidEnemyCheck(ai, global, constants, thisEnemy) and thisEnemy:IsAggressive() == false then
          table.insert(validTargets, thisEnemy)
        end
      end
    end
    self.allTargets = {}
    for i, _ in ipairs(validTargets) do
      self.allTargets[i] = validTargets[i]
    end
  end
  if #self.allTargets == 0 then
    self.retargetNewPeople = true
    local testTargets = DL.FindLivingEnemies(ai, 50)
    maxEnemyTargetable = #testTargets / 2
    for _, thisEnemy in ipairs(testTargets) do
      local distanceToEnemy = thisEnemy.WorldPosition:Distance(player.WorldPosition)
      if helper.getPlayerTarget() ~= thisEnemy and 0 < thisEnemy:GetHitPoints() and distanceToEnemy < 30 and not Fire_AllTargets:InsideCone(ai, global, constants, player, thisEnemy.WorldPosition, 4) and ValidEnemyCheck(ai, global, constants, thisEnemy) and thisEnemy:IsAggressive() == false and ValidEnemyState(ai, global, constants, thisEnemy) then
        table.insert(self.allTargets, thisEnemy)
      end
    end
  else
    local newTargets = {}
    local distanceToReGrab = _G.stats.VFSreGrabRadius.value
    for e = 1, #newEnemies do
      if #newTargets + #self.allTargets >= _G.stats.VFSmaxShots.value then
        break
      end
      local newTargetInList = false
      for k = 1, #self.allTargets do
        if newEnemies[e] == self.allTargets[k] then
          newTargetInList = true
          break
        end
      end
      local thisEnemy = newEnemies[e]
      if newTargetInList == false then
        local distanceToEnemy = thisEnemy.WorldPosition:Distance(player.WorldPosition)
        if helper.getPlayerTarget() ~= thisEnemy and 0 < thisEnemy:GetHitPoints() and distanceToEnemy < 30 and not Fire_AllTargets:InsideCone(ai, global, constants, player, thisEnemy.WorldPosition, 4) and ValidEnemyCheck(ai, global, constants, thisEnemy) and thisEnemy:IsAggressive() == false and ValidEnemyState(ai, global, constants, thisEnemy) then
          table.insert(newTargets, thisEnemy)
        end
      end
    end
    for i, _ in ipairs(newTargets) do
      table.insert(self.allTargets, newTargets[i])
    end
  end
  self.numTargets = #self.allTargets
  if maxEnemyTargetable < #self.allTargets then
    local excess = self.numTargets - maxEnemyTargetable
    for _ = excess, 1, -1 do
      table.remove(self.allTargets)
    end
  end
  self.numTargets = #self.allTargets
  constants.currentTargets = self.allTargets
  constants.numCurrentTargets = self.numTargets
  self.shotOnce = false
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
  if enemyBBLenth ~= nil and 0 < enemyBBLenth then
    bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
  end
  bboardUtil.SetList(sonBB, global.enemyBlackBoardIndex, self.allTargets)
end
function Fire_AllTargets:InsideCone(ai, global, constants, thiscreature, thispoint)
  local forward = thiscreature:GetWorldForward()
  return game.AIUtil.IntersectPointCone(thispoint, thiscreature.WorldPosition, forward, 45, 4)
end
function Fire_AllTargets:ClusterCalculation(ai, global, constants)
  constants.NumTargetShot = 0
  global.targetOverride = true
  self.retargetNewPeople = false
  local player = global.Player
  if #self.allTargets ~= 0 then
    local validTargets = {}
    local atleastEnemyIsClose = false
    local atleastOneGuy
    for _, thisEnemy in ipairs(self.allTargets) do
      if thisEnemy ~= nil then
        atleastEnemyIsClose = true
        if atleastOneGuy == nil then
          atleastOneGuy = thisEnemy
        end
        local atleastCloseToOneEnemy = false
        local sizeToConsiderClose = _G.stats.VFSdiscardRadius.value
        for _, nextEnemy in ipairs(self.allTargets) do
          if nextEnemy ~= nil and nextEnemy ~= thisEnemy and sizeToConsiderClose >= nextEnemy.WorldPosition:Distance(thisEnemy.WorldPosition) and not IsTargetInFront(global.Player, thisEnemy, _G.stats.VFSheroDiscardDistance.value, _G.stats.VFSheroDiscardAngle.value) then
            atleastCloseToOneEnemy = true
            break
          end
        end
        if atleastCloseToOneEnemy == true and ValidEnemyCheck(ai, global, constants, thisEnemy) then
          table.insert(validTargets, thisEnemy)
        end
      end
    end
    if atleastEnemyIsClose == true and #validTargets == 0 then
      table.insert(validTargets, atleastOneGuy)
    end
    self.allTargets = {}
    for i, _ in ipairs(validTargets) do
      self.allTargets[i] = validTargets[i]
    end
  end
  if #self.allTargets == 0 then
    self.retargetNewPeople = true
    local testTargets = DL.FindLivingEnemies(ai, 50)
    for _, thisEnemy in ipairs(testTargets) do
      if helper.getPlayerTarget() ~= thisEnemy and 0 < thisEnemy:GetHitPoints() and ValidTargetCriteria(ai, global, constants, thisEnemy) then
        table.insert(self.allTargets, thisEnemy)
      end
    end
    local clusterRadius = _G.stats.VFSclusterRadius.value
    local bestCluster = {}
    for _, thisEnemy in ipairs(self.allTargets) do
      local potentialCluster = {}
      table.insert(potentialCluster, thisEnemy)
      for _, otherEnemy in ipairs(self.allTargets) do
        if otherEnemy ~= thisEnemy and clusterRadius > (otherEnemy:GetWorldPosition() - thisEnemy:GetWorldPosition()):Length() then
          table.insert(potentialCluster, otherEnemy)
        end
      end
      if #potentialCluster > #bestCluster then
        bestCluster = {}
        for i, _ in ipairs(potentialCluster) do
          bestCluster[i] = potentialCluster[i]
        end
      end
    end
    self.allTargets = {}
    for i, _ in ipairs(bestCluster) do
      self.allTargets[i] = bestCluster[i]
    end
  else
    local newTargets = {}
    local newEnemies = DL.FindLivingEnemies(ai, 50)
    local distanceToReGrab = _G.stats.VFSreGrabRadius.value
    for e = 1, #newEnemies do
      if #newTargets + #self.allTargets >= _G.stats.VFSmaxShots.value then
        break
      end
      local newTargetInList = false
      for k = 1, #self.allTargets do
        if newEnemies[e] == self.allTargets[k] then
          newTargetInList = true
          break
        end
      end
      if newTargetInList == false then
        for k = 1, #self.allTargets do
          if self.allTargets[k] ~= nil and self.allTargets[k].GetHitPoints ~= nil and 0 < self.allTargets[k]:GetHitPoints() and (IsTargetInFront(ai, newEnemies[e], _G.stats.VFSselfRegrabDistance.value, _G.stats.VFSselfRegrabAngle.value) or distanceToReGrab > (self.allTargets[k].WorldPosition - newEnemies[e].WorldPosition):Length()) and SonTargetCrossingPlayerIntent(ai, global, newEnemies[e]) == false and not IsTargetInFront(global.Player, newEnemies[e], _G.stats.VFSheroDiscardDistance.value, _G.stats.VFSheroDiscardAngle.value) then
            table.insert(newTargets, newEnemies[e])
            break
          end
        end
      end
    end
    for i, _ in ipairs(newTargets) do
      table.insert(self.allTargets, newTargets[i])
    end
  end
  self.numTargets = #self.allTargets
  if #self.allTargets > _G.stats.VFSmaxShots.value then
    local excess = self.numTargets - _G.stats.VFSmaxShots.value
    for _ = excess, 1, -1 do
      table.remove(self.allTargets)
    end
  end
  self.numTargets = #self.allTargets
  constants.currentTargets = self.allTargets
  constants.numCurrentTargets = self.numTargets
  self.shotOnce = false
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
  if enemyBBLenth ~= nil and 0 < enemyBBLenth then
    bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
  end
  bboardUtil.SetList(sonBB, global.enemyBlackBoardIndex, self.allTargets)
  constants.currentTargetList = self.allTargets
end
function DrawTriangleFromCreature(thisCreature, angle, length, color)
  local x1, z1, x2, z2
  local sonDirection = thisCreature:GetWorldForward()
  local hyp = length
  x1 = sonDirection.x * math.cos(math.rad(angle)) - sonDirection.z * math.sin(math.rad(angle))
  z1 = sonDirection.x * math.sin(math.rad(angle)) + sonDirection.z * math.cos(math.rad(angle))
  x2 = sonDirection.x * math.cos(math.rad(-angle)) - sonDirection.z * math.sin(math.rad(-angle))
  z2 = sonDirection.x * math.sin(math.rad(-angle)) + sonDirection.z * math.cos(math.rad(-angle))
  local vec1 = engine.Vector.New(x1, 0, z1):Normalized() * hyp + engine.Vector.New(0, 0.1, 0)
  local vec2 = engine.Vector.New(x2, 0, z2):Normalized() * hyp + engine.Vector.New(0, 0.1, 0)
  local vec3 = sonDirection:Normalized() * hyp + engine.Vector.New(0, 0.1, 0)
  engine.DrawLine(thisCreature.WorldPosition + engine.Vector.New(0, 0.1, 0), thisCreature.WorldPosition + vec1, color)
  engine.DrawLine(thisCreature.WorldPosition + engine.Vector.New(0, 0.1, 0), thisCreature.WorldPosition + vec2, color)
  engine.DrawLine(thisCreature.WorldPosition + vec3, thisCreature.WorldPosition + vec2, color)
  engine.DrawLine(thisCreature.WorldPosition + vec3, thisCreature.WorldPosition + vec1, color)
end
function Fire_AllTargets:Exit(ai, global, constants)
  global.fireAtAllTargets = false
  global.targetOverride = false
  statemachine.DispatchEvent("OnStartShootBow_NormalCooldown")
  if ai:PickupIsAcquired("SonOffScreenProtection") then
    ai:PickupRelinquish("SonOffScreenProtection")
  end
end
function Fire_AllTargets:IsAvailable(ai, global, constants)
  local test1 = global.bInCombat and constants.onCamera
  local test2 = not ai:IsDoingSyncMove() and (constants.awarenessState == _G.constants.CombatSteady or constants.awarenessState == _G.constants.GenericReposition or constants.awarenessState == _G.constants.UrgentReposition)
  local test4 = not ai:IsPlayingMove("MOV_UnsheatheBow") and not ai:IsPlayingMove("MOV_ThreatLowReactionCombat0") and not ai:IsPlayingMove("MOV_ThreatLowReactionCombat120L") and not ai:IsPlayingMove("MOV_ThreatLowReactionCombat120R") and not ai:IsPlayingMove("MOV_CombatEnter")
  local test5 = not ai:HasMarker("DoNotAutonomousShoot") and (global.Player.WorldPosition - ai.WorldPosition):Length() < 10
  if global.navSpeed == nil then
    global.navSpeed = 0
  end
  return global.fireAtAllTargets and test1 and test2 and global.navSpeed < 6 and test4 and test5
end
function ShootingTargetFilter(ai, global, target)
  if not target:IsInNavigationMove() and target:GetTargetCreature() == global.Player then
    return false
  end
  return true
end
function SonTargetCrossingPlayerIntent(ai, global, target)
  local aiPos = ai.WorldPosition
  local targetPos = target.WorldPosition
  local playerPos = global.Player.WorldPosition
  local playerEndPos = playerPos + global.Player:GetWorldForward() * 5
  if global.Player:GetTargetCreature() ~= nil then
    local playerTarget = global.Player:GetTargetCreature()
    playerEndPos = playerTarget.WorldPosition
  end
  return DL.LinesIntersect(aiPos.x, aiPos.z, targetPos.x, targetPos.z, playerPos.x, playerPos.z, playerEndPos.x, playerEndPos.z)
end
function IsTargetInFront(from, thisTarget, dist, angleConstraint)
  if dist >= from.WorldPosition:Distance(thisTarget.WorldPosition) then
    local angle = DL.FrontAngle(from, thisTarget)
    if angleConstraint > angle and angle > -angleConstraint then
      return true
    end
  end
  return false
end
function ValidEnemyCheck(ai, global, constants, thisEnemy)
  if thisEnemy ~= nil and (thisEnemy:HasMarker("DoNotEvaluate") or thisEnemy:GetCreature() ~= nil and thisEnemy.PickupIsAcquired ~= nil and thisEnemy:PickupIsAcquired("HealthThreshold") and thisEnemy:PickupGetStage("HealthThreshold") == 2) then
    return false
  end
  return true
end
function ValidEnemyState(ai, global, constants, thisEnemy)
  if thisEnemy:HasMarker("Dormant") or DL.TargetObstructed(ai, thisEnemy) == true or SonTargetCrossingPlayerIntent(ai, global, thisEnemy) == true or thisEnemy:IsDoingSyncMove() then
    return false
  end
  return true
end
function ValidTargetCriteria(ai, global, constants, thisEnemy)
  if thisEnemy:HasMarker("Dormant") or DL.TargetObstructed(ai, thisEnemy) == true or SonTargetCrossingPlayerIntent(ai, global, thisEnemy) == true or thisEnemy:IsDoingSyncMove() or thisEnemy:PickupIsAcquired("HealthThreshold") and thisEnemy:PickupGetStage("HealthThreshold") == 2 then
    return false
  end
  local distToTargetFromPlayer = (thisEnemy:GetWorldPosition() - global.Player:GetWorldPosition()):Length()
  local distToTargetFromSon = (thisEnemy:GetWorldPosition() - ai:GetWorldPosition()):Length()
  local sonToHeroAngle = DL.FrontAngle(global.Player, ai)
  local targetToHeroAngle = DL.FrontAngle(global.Player, thisEnemy)
  if (sonToHeroAngle < 0 and targetToHeroAngle < 0 and -90 < sonToHeroAngle and -90 < targetToHeroAngle or 0 < sonToHeroAngle and 0 < targetToHeroAngle and sonToHeroAngle < 90 and targetToHeroAngle < 90) and distToTargetFromSon < _G.stats.VFSnoTargetRegrabRadius.value and distToTargetFromPlayer > _G.stats.VFSnoTargetHeroDiscardRadius.value then
    return true
  elseif targetToHeroAngle < -90 or 90 < targetToHeroAngle then
    return true
  end
  return false
end
function BehaviorTransition:IsAvailable(ai, global, constants)
  return self.startBehaviorTransition
end
function BehaviorTransition:Enter(ai, global, constants)
end
function BehaviorTransition:Update(ai, global, constants)
  if self.transitionPhase == "Init" then
    if constants.behaviorTransitionType == "Panic" then
      local locomotionInfo = ai:GetLocomotionInfo()
      local pathAngle = locomotionInfo.PathAngle
      if pathAngle == nil then
        ai:TriggerMoveEvent("kLESurvialRepositionStart0")
      else
        if -60 < pathAngle and pathAngle < 60 then
          ai:TriggerMoveEvent("kLESurvialRepositionStart0")
        end
        if pathAngle <= -60 then
          ai:TriggerMoveEvent("kLESurvialRepositionStart120L")
        end
        if 60 <= pathAngle then
          ai:TriggerMoveEvent("kLESurvialRepositionStart120R")
        end
        game.AI.FindSon():GetAnimDriver("WarpRotation").ValueVec = locomotionInfo.PathDirection
        engine.DrawLine(ai.WorldPosition, ai.WorldPosition + locomotionInfo.PathDirection * 5, 16711935, 5)
      end
    elseif constants.behaviorTransitionType == "PanicEnter" then
      ai:TriggerMoveEvent("kLESurvivalRepositionEnter")
    end
    self.transitionPhase = "Loop"
  elseif self.transitionPhase == "Loop" then
    if constants.behaviorTransitionType == "Panic" then
      if constants.awarenessState ~= _G.constants.SurvivalReposition then
        self.transitionPhase = "End"
      end
    elseif constants.behaviorTransitionType == "PanicEnter" and constants.evaluateSurvivalReposition == false then
      self.transitionPhase = "End"
    end
  else
    self.startBehaviorTransition = false
  end
end
function BehaviorTransition:Exit(ai, global, constants)
end
function BehaviorTransition:OnBrainInit(ai, global, constants)
  constants.behaviorTransitionType = "Panic"
  self.transitionPhase = "Init"
  self.startBehaviorTransition = false
end
function BehaviorTransition.Events:StartBehaviorTransition(ai, global, constants, test)
  if ai:IsDoingSyncMove() or ai:HasMarker("SonCommand_WorldInteraction") or ai:GetNavCurve() ~= nil or poilib.IsPOIActive() or ai:HasMarker("DoNotInterrupt") then
    return
  end
  BehaviorTransition.transitionPhase = "Init"
  self.startBehaviorTransition = true
end
function CloseCombat:Enter(seq, ai, global, constants)
  global.previousTargetMode = global.sTargetMode
  global.sTargetMode = "CloseCombatTarget"
  if constants.GO_closeCombatObject ~= nil then
    ai:SetCombatTarget(constants.GO_closeCombatObject)
  else
    return
  end
  if constants.closeCombatAction == "BowSwingLaunch" then
    seq:At(0, function()
      if constants.GO_closeCombatObject == nil then
        return
      end
      local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, constants.GO_closeCombatObject.WorldPosition)
      if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
        ai:TriggerMoveEvent("kLEResponseTurn0")
      elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
        ai:TriggerMoveEvent("kLEResponseTurn120R")
      elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
        ai:TriggerMoveEvent("kLEResponseTurn120L")
      end
    end)
    seq:At(0.4, function()
      ai:TriggerMoveEvent("kLE_BowSwingLaunchTry")
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  elseif constants.closeCombatAction == "BowSwingLaunchTry" then
    seq:At(0, function()
      if constants.GO_closeCombatObject == nil then
        return
      end
      local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, constants.GO_closeCombatObject.WorldPosition)
      if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
        ai:TriggerMoveEvent("kLEResponseTurn0")
      elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
        ai:TriggerMoveEvent("kLEResponseTurn120R")
      elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
        ai:TriggerMoveEvent("kLEResponseTurn120L")
      end
    end)
    seq:At(0.4, function()
      ai:TriggerMoveEvent("kLE_BowSwingLaunchTry")
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  elseif constants.closeCombatAction == "KickOff" then
    seq:At(0, function()
      if CloseCombat:SkillAvailable("KickOff") then
        ai:TriggerMoveEvent("kLE_KickOff")
        CloseCombat:Reset("KickOff")
      else
        ai:TriggerMoveEvent("BRA_EvadeShootB")
      end
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  elseif constants.closeCombatAction == "BowCombo" then
    seq:At(0, function()
      ai:TriggerMoveEvent("kLE_BowCombo")
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  else
    seq:At(0, function()
      if constants.closeCombatAction == "BowSwing" then
        ai:TriggerMoveEvent("kLE_BowSwingFlyback")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "BowSwingTrip" then
        ai:TriggerMoveEvent("kLE_BowSwingTrip")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "BowSwingLaunch" then
        ai:TriggerMoveEvent("kLE_BowSwingLaunchTry")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "BowSwingCheapShot" then
        ai:TriggerMoveEvent("kLE_BowSwingCheapShot")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "BowSwingBounce" then
        ai:TriggerMoveEvent("kLE_BowSwingBounce")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "BackupScared" then
        ai:TriggerMoveEvent("kLE_ResponseBackupScared")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "GrabEnemy" then
        ai:TriggerMoveEvent("kLE_DashAttack")
        self.grabTimer = self.grabTimerMax
        constants.flankEnemy = false
        FollowUpAttackPriority:Reset("GrabEnemy")
      elseif constants.closeCombatAction == "FlybackReturn" then
        ai:TriggerMoveEvent("kLE_FlybackReturn")
      elseif constants.closeCombatAction == "FlybackInAwe" then
        ai:TriggerMoveEvent("kLE_CombatFeatLookSurprised")
      elseif constants.closeCombatAction == "PanicDodge" then
        if constants.GO_closeCombatObject ~= nil then
          if not global.warpTargetRot then
            global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
          end
          global.warpTargetRot.ValueVec = ai.WorldPosition - constants.GO_closeCombatObject.WorldPosition
          if constants.GO_closeCombatObject ~= nil then
            ai:TriggerMoveEvent("kLEPanicDodge")
          end
        end
      elseif constants.closeCombatAction == "ShootArrow" then
        if constants.GO_closeCombatObject ~= nil then
          helper.CallTurnShootEvent(ai, global, constants, constants.GO_closeCombatObject.WorldPosition, false, false)
          statemachine.DispatchGlobalEvent("ResetTheaterPath")
        end
      elseif constants.closeCombatAction == "BodyCheck" then
        if math.random(0, 10) > 5 then
          ai:TriggerMoveEvent("kLEBodyCheck")
        else
          ai:TriggerMoveEvent("kLEShoulderCheck")
        end
      elseif constants.closeCombatAction == "EvadeShoot" then
        ai:TriggerMoveEvent("BRA_EvadeShootB")
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      elseif constants.closeCombatAction == "TauntEnemy" then
        ai:TriggerMoveEvent("kLE_CombatTaunt03")
      elseif constants.closeCombatAction == "RuneDash" then
        ai:TriggerMoveEvent("kLE_RuneDash")
      elseif constants.closeCombatAction == "SmallReposition" and global.smallRepositionLoc ~= nil and (ai.WorldPosition - global.smallRepositionLoc):Length() < 2.25 then
        local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, global.smallRepositionLoc)
        if not global.warpPos then
          global.warpPos = game.AI.FindSon():GetAnimDriver("WarpTargetPOS")
        end
        global.warpPos.ValueVec = global.smallRepositionLoc
        if not global.warpTargetRot then
          global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
        end
        global.warpTargetRot.ValueVec = global.smallRepositionLoc - ai.WorldPosition
        if -45 <= desiredDirectionDelta and desiredDirectionDelta <= 45 then
          ai:TriggerMoveEvent("kLERespositionForward")
        elseif -135 <= desiredDirectionDelta and desiredDirectionDelta < -45 then
          ai:TriggerMoveEvent("kLERespositionLeft")
        elseif -180 <= desiredDirectionDelta and desiredDirectionDelta < -135 or desiredDirectionDelta <= 180 and 135 < desiredDirectionDelta then
          ai:TriggerMoveEvent("kLERespositionBackward")
        elseif 45 < desiredDirectionDelta and desiredDirectionDelta <= 135 then
          ai:TriggerMoveEvent("kLERespositionRight")
        end
      end
    end)
  end
  seq:When(function()
    return ai:IsInNavigationMove()
  end, function()
  end)
end
function CloseCombat:UpdateTimers(ai, global, constants)
  for _, i in pairs(self.actionTimers) do
    i.CurrentTime = i.CurrentTime - ai:GetFrameTime()
    if i.CurrentTime <= 0 then
      i.CurrentTime = 0
      i.Available = true
    end
  end
end
function CloseCombat:Reset(skillname)
  if self.actionTimers[skillname] ~= nil then
    self.actionTimers[skillname].CurrentTime = self.actionTimers[skillname].CooldownTime
    self.actionTimers[skillname].Available = false
  end
end
function CloseCombat:SkillAvailable(skillname)
  if self.actionTimers[skillname] ~= nil then
    return self.actionTimers[skillname].Available
  end
  return false
end
function CloseCombat:OnBrainInit(ai, global, constants)
  global.closeCombatStart = false
  constants.closeCombatAction = "none"
  self.scatterShotTimer = 0
  self.grabTimer = 10
  self.grabTimerMax = 10
  constants.flankEnemy = false
  self.actionTimers = {
    KickOff = {
      CurrentTime = 0,
      CooldownTime = 180,
      Available = false
    }
  }
end
function CloseCombat:IsAvailable(ai, global, constants)
  return global.closeCombatStart
end
function CloseCombat:Exit(ai, global, constants)
  global.closeCombatStart = false
  global.targetOverride = false
  constants.closeCombatAction = "none"
  global.sTargetMode = global.previousTargetMode
  if constants.GO_closeCombatObject ~= nil and constants.enemiesInReactionList[constants.GO_closeCombatObject] ~= nil then
    constants.enemiesInReactionList[constants.GO_closeCombatObject] = nil
  end
end
function CloseCombat:UpdateData(ai, global, constants)
  self.grabTimer = self.grabTimer - ai:GetFrameTime()
  if self.grabTimer <= 0 then
    constants.flankEnemy = true
  end
  CloseCombat:UpdateTimers(ai, global, constants)
end
function CloseCombat:Update(ai, global, constants)
  self.scatterShotTimer = self.scatterShotTimer + ai:GetFrameTime()
  if ai:HasMarker("ForceExit") or ai:HasMarker("DoNotInterrupt") then
    global.closeCombatStart = false
  end
end
function LuaHook_DismountLogicFlyback(ai)
  LuaHook_DismountLogic(ai, {
    -115,
    115,
    -115,
    115,
    -165,
    165,
    90,
    -90,
    180
  })
end
function LuaHook_DismountLogicLaunch(ai)
  LuaHook_DismountLogic(ai, {
    50,
    90,
    25,
    45,
    -25,
    -35,
    90,
    -60,
    -135,
    135,
    180
  })
end
function LuaHook_DismountLogicExit(ai)
  LuaHook_DismountLogic(ai, {
    25,
    45,
    -25,
    -35,
    90,
    -60,
    -135,
    135,
    180
  })
end
function LuaHook_DismountLogic(ai, dismountLocations)
  local player = game.Player.FindPlayer()
  local subject, forward, allEnemies, position, bestLocation, jumpLocations
  jumpLocations = dismountLocations
  forward = player:GetWorldForward()
  allEnemies = helper.FindLivingEnemiesExludeMarked(player, 4, "DoNotEvaluate")
  position = ai.WorldPosition
  table.insert(allEnemies, player)
  for _, i in ipairs(jumpLocations) do
    local newPoint = forward:RotateXZ(i) * 3 + position
    local enemyFound = false
    for _, enemy in ipairs(allEnemies) do
      if (enemy.WorldPosition - newPoint):Length() < 1.5 then
        enemyFound = true
        break
      end
    end
    if enemyFound == false then
      local collisionParams = {
        SourceGameObject = ai,
        EntityType = game.CollisionType.New("kEnvironment"),
        RejectMaterialFlags = game.GroundType.New("kGT_CosmeticIK", "kGT_Death", "kGT_InstantDeath", "kGT_NoTargetThru")
      }
      local raycastFinalPos = newPoint - engine.Vector.New(0, 1.5, 0)
      local hit = game.World.RaycastCollision(newPoint, raycastFinalPos, collisionParams)
      if hit ~= nil then
        engine.DrawTextInWorld(newPoint + engine.Vector.New(0, 2, 0), "Value = " .. i, 255, 2, 10)
        bestLocation = newPoint
        break
      end
    end
  end
  if bestLocation == nil then
    bestLocation = player:GetWorldForward():RotateXZ(-45) * 0.15 + player.WorldPosition
  end
  if _G.constants.targetROT == nil then
    _G.constants.targetROT = game.AI.FindSon():GetAnimDriver("TargetROT")
  end
  _G.constants.targetROT.ValueVec = ai.WorldPosition - bestLocation
end
function Dodge:IsAvailable(ai, global, constants, tags)
  return global.bDodgeNow
end
function Dodge:OnBrainInit(ai, global, constants)
  global.bDodgeNow = false
  global.bDodging = false
  constants.GO_dodgeObject = nil
  constants.GO_closeCombatObject = nil
  global.dodgeAwarenessFlag = false
  global.DodgeType = "none"
  constants.meleeTarget = nil
end
function Dodge:Enter(seq, ai, global, constants)
  if ai:IsDoingSyncMove() then
    return
  end
  global.dodgeAwarenessFlag = true
  global.DodgeType = global.responseAction
  constants.GO_closeCombatObject = constants.GO_dodgeObject
  constants.meleeTarget = constants.GO_dodgeObject
  global.previousTargetMode = global.sTargetMode
  self.ExitTimer = 3
  global.dodgeCurrentQuadrant = constants.currentIntersectionQuadrant
  global.dodgeFutureQuadrant = constants.futureIntersectionQuadrant
  self.potentialCurrentQuadrant = constants.potentialCurrentQuadrant
  self.potentialFutureQuadrant = constants.potentialFutureQuadrant
  global.sTargetMode = "DodgeTarget"
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  if global.DodgeType == "ResponseTurn" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
        local sonspeed = ai:GetVelocity():Length()
        if 2.5 < sonspeed then
          if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
            ai:TriggerMoveEvent("kLEResponseCombatStopTurn0")
          elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
            ai:TriggerMoveEvent("kLEResponseCombatStopTurn120R")
          elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
            ai:TriggerMoveEvent("kLEResponseCombatStopTurn120L")
          end
        elseif -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
          ai:TriggerMoveEvent("kLEResponseTurn0")
        elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
          ai:TriggerMoveEvent("kLEResponseTurn120R")
        elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
          ai:TriggerMoveEvent("kLEResponseTurn120L")
        end
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "ResponseTurnRun" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
        local locomotionInfo = ai:GetLocomotionInfo()
        if locomotionInfo.PathStraightDistance ~= nil and locomotionInfo.PathStraightDistance > 4.25 and ai:HasMarker("Moving") then
          if 0 < desiredDirectionDelta and desiredDirectionDelta <= 90 then
            ai:TriggerMoveEvent("kLEResponseTurnRunL")
          elseif desiredDirectionDelta < 0 and -90 <= desiredDirectionDelta then
            ai:TriggerMoveEvent("kLEResponseTurnRunR")
          end
          global.bDodging = true
          global.sTargetMode = "DodgeTarget"
        end
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "StopTurn" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        ai:TriggerMoveEvent("kLEStopTurn")
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "UnwareBackup" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        ai:TriggerMoveEvent("kLEPanicReposition")
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "PanicDodge" then
    seq:At(0.5, function()
      global.bDodging = true
      global.sTargetMode = "DodgeTarget"
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "MeleeAndDodge" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
        if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
          ai:TriggerMoveEvent("kLEBowSwingTurn0")
        elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
          ai:TriggerMoveEvent("kLEBowSwingTurn120R")
        elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
          ai:TriggerMoveEvent("kLEBowSwingTurn120L")
        end
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "DodgeAndShoot" then
    seq:At(0, function()
      global.bDodgeNow = global.s_DodgeMoveName ~= "none"
      global.bDodging = true
      global.sTargetMode = "DodgeTarget"
      ai:TriggerMoveEvent("kLE_DodgeAndShoot")
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
    seq:When(function()
      return constants.GO_dodgeObject ~= nil and (constants.GO_dodgeObject.WorldPosition - ai.WorldPosition):Length() < 4.5, constants.GO_dodgeObject == nil or self.ExitTimer < 0
    end, function()
      ai:TriggerMoveEvent("kLE_DodgeNow")
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "DodgeHero" and global.dodgeFutureQuadrant ~= nil then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        local inCombat = ""
        if global.bInCombat or global.currentState == "PreCombat" then
          inCombat = "Combat"
        end
        if global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 1 or global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 0 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeBack")
        elseif global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 2 or global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 1 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeRight")
        elseif global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 3 or global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 2 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeForward")
        elseif global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 0 or global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 3 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeLeft")
        elseif global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 3 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeBack")
        elseif global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 1 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeForward")
        elseif global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 0 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeRight")
        elseif global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 2 then
          ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeLeft")
        elseif global.dodgeCurrentQuadrant == 0 then
          local dodgeObjectLocation = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
          if 50 < dodgeObjectLocation then
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeBack")
          else
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeLeft")
          end
        elseif global.dodgeCurrentQuadrant == 1 then
          local dodgeObjectLocation = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
          if dodgeObjectLocation < -50 then
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeBack")
          else
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeRight")
          end
        elseif global.dodgeCurrentQuadrant == 2 then
          local dodgeObjectLocation = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
          if -135 < dodgeObjectLocation then
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeForward")
          else
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeRight")
          end
        else
          local dodgeObjectLocation = DL.FrontAngleFromPoint(ai, constants.GO_dodgeObject.WorldPosition)
          if 135 < dodgeObjectLocation then
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeLeft")
          else
            ai:TriggerMoveEvent("BRA_" .. inCombat .. "EvadeBack")
          end
        end
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
        game.Audio.PlayBanterNonCritical("Son Avoid Hero")
      end
    end)
    seq:WaitForMoveEnd()
  elseif global.DodgeType == "DodgeEnemy" then
    seq:At(0, function()
      if constants.GO_dodgeObject ~= nil then
        local shouldFaceTarget = ""
        local combatEvade = "Combat"
        shouldFaceTarget = ""
        combatEvade = "Combat"
        local timeToDodge
        constants.potentialCurrentQuadrant, timeToDodge, constants.potentialFutureQuadrant = game.AIUtil.DetectCreatureIntersection(ai, constants.GO_dodgeObject, 6)
        if timeToDodge <= 1 then
          global.dodgeCurrentQuadrant = constants.potentialCurrentQuadrant
          global.dodgeFutureQuadrant = constants.potentialFutureQuadrant
          if global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 1 or global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 0 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeBack" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 2 or global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 1 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeRight" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 3 or global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 2 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeForward" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 0 or global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 3 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeLeft" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 1 and global.dodgeFutureQuadrant == 3 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeBack" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 3 and global.dodgeFutureQuadrant == 1 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeForward" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 2 and global.dodgeFutureQuadrant == 0 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeRight" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 0 and global.dodgeFutureQuadrant == 2 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeLeft" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 0 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeBack" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 1 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeBack" .. shouldFaceTarget)
          elseif global.dodgeCurrentQuadrant == 2 then
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeForward" .. shouldFaceTarget)
          else
            ai:TriggerMoveEvent("BRA_" .. combatEvade .. "EvadeForward" .. shouldFaceTarget)
          end
        end
        global.bDodgeNow = true
        global.bDodging = true
        global.sTargetMode = "DodgeTarget"
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
      end
    end)
    seq:When(function()
      return constants.GO_dodgeObject ~= nil and (constants.GO_dodgeObject.WorldPosition - ai.WorldPosition):Length() < 4.5 or constants.GO_dodgeObject == game.Player.FindPlayer(), constants.GO_dodgeObject == nil or self.ExitTimer < -1.5
    end, function()
      ai:TriggerMoveEvent("kLE_DodgeNow")
    end)
    seq:WaitForMoveEnd()
  end
end
function Dodge:Update(ai, global, constants)
  self.ExitTimer = self.ExitTimer - ai:GetFrameTime()
  if ai:IsDoingSyncMove() then
    global.bDodgeNow = false
  end
end
function Dodge:Exit(ai, global, constants)
  global.bDodging = false
  global.bDodgeNow = false
  global.DodgeType = "none"
  global.responseAction = "NoResponse"
  global.sTargetMode = global.previousTargetMode
  FollowUpAttack.FollowUpAttackStart = false
  if constants.closeCombatAction ~= "NoAction" and ai:HasMarker("DoNotDodge") == false and ai:HasMarker("IgnoreDodge") == false and ai:HasMarker("DoNotInterrupt") == false and not ai:HasMarker("DoNotDodge") then
    global.closeCombatStart = true
  end
end
function Dodge.Events:RespondNow(ai, global, constants)
  if ai:IsDoingSyncMove() or ai:HasMarker("DoNotDodge") and ai:HasMarker("IgnoreDodge") == false and ai:HasMarker("TweakLogicOnly") or ai:HasMarker("SonCommand_WorldInteraction") or ai:HasMarker("DoNotInterrupt") or ai:GetNavCurve() ~= nil and not global.leadTheWayParams.Role == "relaxedFollower" then
    return
  end
  local tags = statemachine.ActiveTags()
  if not tags.DoNotDodge or poilib.IsPOIActive() and constants.awarenessState == _G.constants.ObservedUrgentMoveAway then
    if global.DodgeType ~= "DodgeHero" and ai:IsAvailableForCombat() == false and game.Combat.GetCombatStatus() then
      return
    end
    global.bDodgeNow = true
  else
    constants.closeCombatAction = "NoAction"
    global.responseAction = "NoResponse"
  end
end
function FollowUpAttack:Enter(seq, ai, global, constants)
  constants.sPreviousTargetMode = global.sTargetMode
  self.timer = 0
  if global.FollowUpTarget == nil then
    return
  end
  constants.meleeTarget = global.FollowUpTarget
  if self.FollowUpType == "Tackle" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttack:Reset(self.FollowUpType)
    end)
    seq:When(function()
      return ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 16
    end, function()
      ai:ForceMove("BRA_RuneDash")
    end)
  elseif self.FollowUpType == "FastCrit" then
    seq:At(0, function()
      ai:ForceMove("BRA_ShootBowFastCritical_00")
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttack:Reset(self.FollowUpType)
    end)
    seq:At(0.6, function()
    end)
  elseif self.FollowUpType == "CoverHero" then
    seq:At(0, function()
      if constants.onCamera == false then
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = false
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttack:Reset(self.FollowUpType)
        ai:TriggerMoveEvent("kLE_PortalAssist")
      end
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread"), ai:IsInNavigationMove()
    end, function()
      local enemiesNotOnScreen = helper.getEnemiesBehind(ai)
      local arrowtype = "ARR_ARROW_LUA_NORMAL"
      for _, i in ipairs(enemiesNotOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
      end
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread") and ai:HasMarker("SecondRound"), ai:IsInNavigationMove()
    end, function()
      local enemiesNotOnScreen = helper.getEnemiesBehind(ai)
      local arrowtype = "ARR_ARROW_LUA_NORMAL"
      for _, i in ipairs(enemiesNotOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
      end
    end)
  elseif self.FollowUpType == "JumpOffEnemyBack" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttack:Reset(self.FollowUpType)
    end)
    seq:When(function()
      return ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 4, not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_LIE_STOMACH") and not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_EXPOSE_BACK") and not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_CRUMPLE")
    end, function()
      ai:TriggerMoveEvent("kLE_JumpOffEnemyBack")
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread"), ai:IsInNavigationMove()
    end, function()
      local enemiesOnScreen = DL.GetAllEnemiesOnScreen(true)
      local arrowtype = "ARR_ARROW_LUA"
      if global.heroInput then
        if global.selfAI:PickupIsAcquired("SonArrow_Light") then
          arrowtype = "ARR_ARROW_LIGHT_LUA"
        elseif global.selfAI:PickupIsAcquired("SonArrow_Shock") then
          arrowtype = "ARR_ARROW_SHOCK_LUA"
        end
      end
      for _, i in ipairs(enemiesOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
      end
    end)
  elseif self.FollowUpType == "EnemyHold" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttack:Reset(self.FollowUpType)
      ai:TriggerMoveEvent("kLE_JumpOffEnemyBack")
    end)
    seq:When(function()
      return ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 4, not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_EXPOSE_BACK") and not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_CRUMPLE")
    end, function()
      ai:TriggerMoveEvent("kLE_JumpOffEnemyBack")
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread"), ai:IsInNavigationMove()
    end, function()
      local enemiesOnScreen = DL.GetAllEnemiesOnScreen(true)
      local arrowtype = "ARR_ARROW_LUA"
      if global.heroInput then
        if global.selfAI:PickupIsAcquired("SonArrow_Light") then
          arrowtype = "ARR_ARROW_LIGHT_LUA"
        elseif global.selfAI:PickupIsAcquired("SonArrow_Shock") then
          arrowtype = "ARR_ARROW_SHOCK_LUA"
        end
      end
      for _, i in ipairs(enemiesOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
      end
    end)
  elseif self.FollowUpType == "PepperTarget" then
    ai:AddMarker("AllowDodgeOverride")
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        if (ai.WorldPosition - global.FollowUpTarget.WorldPosition):Length() > 6 then
          ai:TriggerMoveEvent("kLE_PanicSurrounded")
        end
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttack:Reset(self.FollowUpType)
      end
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ((ai.WorldPosition - global.FollowUpTarget.WorldPosition):Length() < 5 or ai:IsInNavigationMove()), global.FollowUpTarget == nil
    end, function()
      ai:TriggerMoveEvent("kLEPepperTarget")
      game.Audio.PlayBanterNonCritical("Followup_WallPin")
    end)
    seq:At(1, function()
      ai:RemoveMarker("AllowDodgeOverride")
    end)
  elseif self.FollowUpType == "FlybackShot" then
    seq:At(0, function()
      ai:ForceMove("BRA_ShootBowFlybackArrow_00")
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttack:Reset(self.FollowUpType)
    end)
    seq:At(0.6, function()
    end)
  else
    seq:When(function()
      if not global.goPlayerTarget then
        return false, true
      else
        local advance = global.goPlayerTarget:GetWorldPosition() and (global.goPlayerTarget:GetWorldPosition() - ai:GetWorldPosition()):Length() < 2
        local cancel = global.goPlayerTarget:GetAI() == nil or global.goPlayerTarget:IsInNavigationMove() or global.goPlayerTarget:IsDoingSyncMove()
        return advance, cancel
      end
    end)
    seq:Always(function()
      ai:ForceMove("BRA_CSAttKnifeFollowUpEnter")
      global.sTargetMode = "AcquireKratosTarget"
      ai:SetCombatTarget(global.goPlayerTarget)
    end)
  end
  seq:When(function()
    return not ai:HasMarker("InFollowup")
  end)
  seq:WaitForMoveEnd()
end
function FollowUpAttack.Events:OnBroadcastReaction(event, ai, global, constants)
  if self.FollowUpAttackStart or game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") >= 1 then
    return
  end
  if event.source == nil then
    return
  end
  if not constants.followUpSkill then
    return
  end
  if global.bInCombat == false or ai:IsDoingSyncMove() or constants.StandGround then
    return
  end
  if event.source:PickupIsAcquired("HealthThreshold") and event.source:PickupGetStage("HealthThreshold") == 2 then
    return
  end
  if ai:HasMarker("DoNotFollowup") or ai:HasMarker("DoNotFollowupPriority") then
    return
  end
  local onScreen = constants.onCamera
  local canPerformHigherTierSkill = gVFSFollowupUpgraded.value
  if DL.CheckCreatureContext(event.broadcastContext, "SONSPECIAL_COVERHERO") and canPerformHigherTierSkill then
    if FollowUpAttack:SkillAvailable("CoverHero") and constants.onCamera == false then
      if (ai.WorldPosition - global.Player.WorldPosition):Length() > 4 and self.offScreenTimer < 3 then
        return
      end
      if not constants.onCamera then
        local enemylist = helper.getEnemiesBehind(global.Player)
        if 1 < #enemylist then
          local collisionParams = {
            SourceGameObject = global.Player,
            ExcludeGameObject = global.Player,
            EntityType = game.CollisionType.New("kInvisibleBarrier", "kEnvironment")
          }
          local origin = global.Player.WorldPosition + engine.Vector.New(0, 0.5, 0)
          local hit1 = game.World.RaycastCollision(origin, origin + global.Player:GetWorldForward() * 3, collisionParams)
          local hit2 = game.World.RaycastCollision(origin, origin + global.Player:GetWorldLeft() * -3, collisionParams)
          if hit1 == nil and hit2 == nil then
            self.FollowUpAttackStart = true
            self.FollowUpType = "CoverHero"
            ai:SetCombatTarget(global.Player)
            self.FollowUpAttackStart = true
          end
        end
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_FOLLOWUP_DROPKICK") and canPerformHigherTierSkill then
    if not global.Player:HasMarker("CommandFollowup") then
      return
    end
    if 1 < helper.getNumberOfOnScreenEnemy() and FollowUpAttack:SkillAvailable("JumpOffBack_Finisher") and self.FollowUpAttackStart == false and onScreen == false then
      self.FollowUpAttackStart = true
      self.FollowUpType = "JumpOffBack_Finisher"
      global.FollowUpTarget = global.Player
      ai:SetCombatTarget(global.Player)
      self.FollowUpAttackStart = true
      self.KratosDropKicked = true
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_ENEMY_HOLD") then
    local playerDistance = (event.source.WorldPosition - global.Player.WorldPosition):Length()
    local distanceToTarget = (event.source.WorldPosition - ai.WorldPosition):Length()
    if FollowUpAttack:SkillAvailable("EnemyHold") and 4 < playerDistance and playerDistance < 10 and distanceToTarget < 6 then
      self.FollowUpAttackStart = true
      self.FollowUpType = "EnemyHold"
      global.FollowUpTarget = event.source
      return
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_PEPPER_TARGET") and FollowUpAttack:SkillAvailable("PepperTarget") and event.source:GetLastAttacker() ~= ai then
    self.FollowUpAttackStart = true
    self.FollowUpType = "PepperTarget"
    global.FollowUpTarget = event.source
    return
  end
end
function FollowUpAttack:Update(ai, global, constants)
  if (self.FollowUpType == "Tackle" or self.FollowUpType == "JumpOffEnemyBack" or self.FollowUpType == "EnemyHold") and (global.FollowUpTarget == nil or global.FollowUpTarget:GetHitPoints() <= 0 or global.FollowUpTarget == global.Player:GetTargetCreature() and (global.Player.WorldPosition - global.FollowUpTarget.WorldPosition):Length() < (ai.WorldPosition - global.FollowUpTarget.WorldPosition):Length()) then
    self.FollowUpAttackStart = false
    ai:TriggerMoveEvent("kLE_StopDash")
  end
end
function FollowUpAttack:Exit(ai, global, constants)
  global.sTargetMode = constants.sPreviousTargetMode
  global.bGoToTarget = false
  self.FollowUpAttackStart = false
  self.FollowUpType = "none"
  ai:RemoveMarker("AllowDodgeOverride")
  if ai:PickupIsAcquired("SonSpeedNavMod") then
    ai:PickupRelinquish("SonSpeedNavMod")
  end
  global.heroInput = false
end
function FollowUpAttack:IsAvailable(ai, global, constants)
  return self.FollowUpAttackStart and constants.stats.advancedFollowUpEnabled
end
function FollowUpAttack:UpdateData(ai, global, constants)
  if ai:HasMarker("DoNotFollowup") then
    return
  end
  if global.bInCombat == false then
    return
  end
  FollowUpAttack:UpdateTimers(ai, global, constants)
  if self.FollowUpAttackStart then
    return
  end
end
function FollowUpAttack:UpdateTimers(ai, global, constants)
  local frametime = ai:GetFrameTime()
  for _, i in pairs(self.actionTimers) do
    i.CurrentTime = i.CurrentTime - frametime
    if i.CurrentTime <= 0 then
      i.CurrentTime = 0
      i.Available = true
    end
  end
end
function FollowUpAttack:Reset(skillname)
  if self.actionTimers[skillname] ~= nil then
    self.actionTimers[skillname].CurrentTime = self.actionTimers[skillname].CooldownTime
    self.actionTimers[skillname].Available = false
  end
end
function FollowUpAttack:SkillAvailable(skillname)
  if self.actionTimers[skillname] ~= nil then
    return self.actionTimers[skillname].Available
  end
  return false
end
function FollowUpAttack:OnBrainInit(ai, global, constants)
  self.FollowUpAttackStart = false
  self.FollowUpType = "none"
  global.bGoToTarget = false
  constants.sPreviousTargetMode = "RegularTarget"
  self.timer = 3
  global.heroInput = false
  self.offScreenTimer = 0
  self.actionTimers = {
    JumpOffBack_Finisher = {
      CurrentTime = 0,
      CooldownTime = 2,
      Available = false
    },
    Tackle = {
      CurrentTime = 0,
      CooldownTime = 12,
      Available = false
    },
    CriticalShot = {
      CurrentTime = 0,
      CooldownTime = 3,
      Available = false
    },
    FastCrit = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    JumpOffBack_Takedown = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    PepperTarget = {
      CurrentTime = 0,
      CooldownTime = 30,
      Available = false
    },
    FlybackShot = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    InterruptHeavyAttack = {
      CurrentTime = 0,
      CooldownTime = 2,
      Available = false
    },
    JumpOffEnemyBack = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    EnemyHold = {
      CurrentTime = 0,
      CooldownTime = 25,
      Available = false
    },
    CoverHero = {
      CurrentTime = 0,
      CooldownTime = 120,
      Available = false
    },
    MultiFocusShot = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    EnemyLieAttack = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    }
  }
  thunk.Install("OnCommandSonStart", function(go, inputType)
    if ai:HasMarker("HeroInput") then
      global.heroInput = true
      ai:TriggerMoveEvent("kLESuper")
    end
  end)
end
function WeaponHolsterLogic:Enter(seq, ai, global, constants)
  seq:When(function()
    return (ai:IsInNavigationMove() or ai:HasMarker("InOneOff")) and not ai:IsInAir()
  end, function()
  end)
  seq:At(0, function()
    if global.currentState == "EnterCombat" or global.currentState == "InCombat" or global.currentState == "PreCombat" then
      if ai:IsContextBehaviorNameEqualTo("ALERT_BEHAVIOR_CONTEXT_CONFIG") then
        ai:CallScript("ClearBehaviorContext")
        ai:TriggerMoveEvent("kLE_EnterCombat")
      else
        ai:TriggerMoveEvent("kLE_PreCombat")
      end
    elseif global.currentState == "Idle" or global.currentState == "PostCombat" then
      if (global.Player.WorldPosition - ai.WorldPosition):Length() < 10 then
        ai:TriggerMoveEvent("kLE_CombatExit")
      else
        ai:TriggerMoveEvent("kLE_CombatExitQuick")
      end
    end
    local sonBB = game.AI.FindSon():GetPrivateBlackboard()
    local enemyBBLenth = bboardUtil.GetListLength(sonBB, global.enemyBlackBoardIndex)
    if enemyBBLenth ~= nil and 0 < enemyBBLenth then
      bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
    end
  end)
  seq:When(function()
    return (ai:IsInNavigationMove())
  end, function()
  end)
end
function WeaponHolsterLogic:OnBrainInit(ai, global, constants)
  global.bowSheathed = true
end
function WeaponHolsterLogic:IsAvailable(ai, global, constants, currentState)
  if not ai:IsDoingSyncMove() then
    if global.bowSheathed == true then
      if global.currentState == "EnterCombat" or global.currentState == "PreCombat" or global.currentState == "InCombat" then
        return true
      end
    elseif global.currentState == "Idle" or global.currentState == "PostCombat" then
      return true
    end
  end
end
function WeaponHolsterLogic:Exit(ai, global, constants)
  if global.currentState == "PreCombat" or global.currentState == "InCombat" or global.currentState == "EnterCombat" then
    global.bowSheathed = false
  else
    global.bowSheathed = true
  end
  FollowUpAttack.FollowUpAttackStart = false
end
function OnRequestDisengageHook(ai, other_ai, weight)
  for _, i in ipairs(_G.constants.currentTargetList) do
    if i == other_ai or i == nil then
      table.remove(_G.constants.currentTargetList, _)
      break
    end
  end
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLenth = bboardUtil.GetListLength(sonBB, _G.global.enemyBlackBoardIndex)
  if enemyBBLenth ~= nil and 0 < enemyBBLenth then
    bboardUtil.EraseList(sonBB, _G.global.enemyBlackBoardIndex)
  end
  if _G.constants.currentTargetList ~= nil and #_G.constants.currentTargetList > 0 then
    bboardUtil.SetList(sonBB, _G.global.enemyBlackBoardIndex, _G.constants.currentTargetList)
  end
end
function Coop_Moves.Events:OnBroadcastReaction(event, ai, global, constants)
  if DL.CheckCreatureContext(event.broadcastContext, "COMMAND_SAVE") then
    self.sCoopMoveType = "COMMAND_SAVE"
    global.bStartCoopMove = true
    global.sTargetMode = "TargetKratos"
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_DISPEL") then
    self.sCoopMoveType = "COMMAND_DISPEL"
    global.bStartCoopMove = true
    global.sTargetMode = "TargetKratos"
  end
end
function Coop_Moves:Enter(seq, ai, global, constants)
  if self.sCoopMoveType == "COMMAND_SAVE" then
    seq:At(0, function()
      global.sTargetMode = "TargetKratos"
      ai:SetCombatTarget(global.Player)
      ai:ForceMove("BRA_DeathSave_Respond")
    end)
    seq:WaitForMoveEnd()
    seq:When(function()
      return ai:IsInNavigationMove()
    end)
  elseif self.sCoopMoveType == "COMMAND_DISPEL" then
    seq:At(0, function()
      global.sTargetMode = "TargetKratos"
      ai:ForceMove("BRA_PickUpDispelObjectEnter")
    end)
    seq:WaitForMoveEnd()
    seq:When(function()
      return ai:IsInNavigationMove()
    end)
  end
end
function Coop_Moves:IsAvailable(ai, global, constants)
  return global.bStartCoopMove
end
function Coop_Moves:Exit(ai, global, constants)
  global.sTargetMode = "RegularTarget"
  global.bStartCoopMove = false
  FollowUpAttack.FollowUpAttackStart = false
end
function Coop_Moves:OnBrainInit(ai, global, constants)
  global.bStartCoopMove = false
end
function FollowUpAttackPriority:Enter(seq, ai, global, constants)
  constants.sPreviousTargetMode = global.sTargetMode
  self.timer = 0
  constants.strafe = false
  constants.meleeTarget = global.FollowUpTarget
  if self.FollowUpType == "CriticalShot" then
    seq:At(0, function()
      ai:ForceMove("BRA_ShootBowCritical_00")
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
    end)
    seq:At(0.3, function()
      game.Audio.PlayBanterNonCritical("cbt_son_ArrowSuccess")
    end)
    seq:At(0.6, function()
    end)
  elseif self.FollowUpType == "JumpOffBack_Takedown" then
    local enemy = global.Player:GetLastAttacker()
    if enemy ~= nil and enemy:HasMarker("SonVictim") then
      seq:At(0, function()
        global.sTargetMode = "TargetKratos"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.Player)
        local warppos = global.Player.WorldPosition
        ai:ForceMove("BRA_PerformWiff_Takedown")
        FollowUpAttackPriority:Reset(self.FollowUpType)
        self.JumpOffBack_Target = enemy
      end)
      seq:When(function()
        return ai:HasMarker("TargetSonVictim"), ai:IsInNavigationMove()
      end, function()
        if self.JumpOffBack_Target ~= nil then
          global.sTargetMode = "FollowUpTarget"
          global.bGoToTarget = false
          global.FollowUpTarget = self.JumpOffBack_Target
          ai:SetCombatTarget(self.JumpOffBack_Target)
        end
      end)
      seq:At(1, function()
      end)
    end
  elseif self.FollowUpType == "Protect" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        ai:TriggerMoveEvent("kLE_ShoulderRamSave")
      end
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 2.5, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil
    end, function()
      ai:AddMarker("DoNotInterrupt")
    end)
  elseif self.FollowUpType == "Protect_PID" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        self.TrackingDangerTarget = true
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        ai:TriggerMoveEvent("kLE_ShoulderRamSave")
      end
    end)
    seq:When(function()
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 2.5, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil
    end, function()
      ai:AddMarker("DoNotInterrupt")
    end)
  elseif self.FollowUpType == "CoverBack" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        ai:TriggerMoveEvent("kLE_ShoulderRamSave")
        ai:AddMarker("AttackOffscreen")
        if global.FollowUpTarget ~= nil then
          global.FollowUpTarget:PickupAcquire("SonKnifeAttackSetup")
        end
        game.Audio.PlayBanterNonCritical("SonCoverBack")
      end
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 2.5, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil
    end, function()
      ai:AddMarker("DoNotInterrupt")
    end)
  elseif self.FollowUpType == "HoldCleaveEnemy" then
    seq:At(0, function()
      constants.boostSpeed = true
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
      if global.FollowUpTarget ~= nil or (ai.WorldPosition - global.FollowUpTarget.WorldPosition):Length() > 2 then
        ai:TriggerMoveEvent("kLE_PanicSurrounded")
      end
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 2, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil
    end, function()
      ai:TriggerMoveEvent("kLE_GrabEnemy")
    end)
  elseif self.FollowUpType == "StunCleaveEnemy" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = false
        ai:SetCombatTarget(global.FollowUpTarget)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        FollowUpAttackPriority:Reset(self.FollowUpType)
        local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, global.FollowUpTarget.WorldPosition)
        local sonspeed = ai:GetVelocity():Length()
        if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
          ai:TriggerMoveEvent("kLEResponseTurnAndSpreadShoot0")
        elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
          ai:TriggerMoveEvent("kLEResponseTurnAndSpreadShoot120R")
        elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
          ai:TriggerMoveEvent("kLEResponseTurnAndSpreadShoot120L")
        end
      end
    end)
  elseif self.FollowUpType == "ProtectPullAggro" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        ai:TriggerMoveEvent("kLEPepperTarget")
        engine.SendHook("OnForceTempAggro", global.FollowUpTarget, 5)
        game.Audio.PlayBanterNonCritical("cbt_son_ArrowSuccess")
      end
    end)
    seq:At(2, function()
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  elseif self.FollowUpType == "ProtectWeak" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        constants.boostSpeed = true
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        statemachine.DispatchGlobalEvent("ResetTheaterPath")
        ai:TriggerMoveEvent("kLE_ShoulderRamSave")
      end
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 2.5, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil
    end, function()
      ai:AddMarker("DoNotInterrupt")
    end)
  elseif self.FollowUpType == "ProtectOffScreen" then
    seq:At(0, function()
      constants.boostSpeed = true
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      ai:Warp(global.Player.WorldPosition + global.Player:GetWorldLeft() * -1.5 + global.Player:GetWorldForward() * 1, global.Player:GetWorldForward())
    end)
    seq:At(0.15, function()
      ai:TriggerMoveEvent("kLE_DodgeLAndShoot")
    end)
  elseif self.FollowUpType == "DodgeAwayFromBroadcast" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        ai:TriggerMoveEvent("kLE_EvadeFromBroadcaster")
      end
    end)
    seq:At(0.3, function()
    end)
  elseif self.FollowUpType == "Taunt" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      ai:TriggerMoveEvent("kLE_CombatTaunt03")
      ai:SetCombatTarget(global.FollowUpTarget)
    end)
  elseif self.FollowUpType == "GrabEnemy" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      ai:TriggerMoveEvent("kLE_DashAttack")
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
    end)
  elseif self.FollowUpType == "Launch" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      ai:TriggerMoveEvent("kLE_BowSwingLaunchTry")
    end)
    seq:At(0.4, function()
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
    end)
  elseif self.FollowUpType == "JumpOffBack_Finisher" then
    seq:At(0, function()
      if not global.Player:HasMarker("CommandFollowup") then
        return
      end
      global.sTargetMode = "TargetKratos"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.Player)
      local warppos = global.Player.WorldPosition
      if self.KratosDropKicked == false then
        ai:ForceMove("BRA_PerformWiff")
      else
        ai:ForceMove("BRA_PerformWiff_DropKick")
      end
      FollowUpAttackPriority:Reset(self.FollowUpType)
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread"), ai:IsInNavigationMove()
    end, function()
      local enemiesOnScreen = DL.GetAllEnemiesOnScreen(true)
      local arrowtype = "ARR_ARROW_LUA"
      if global.heroInput then
        if global.selfAI:PickupIsAcquired("SonArrow_Light") then
          arrowtype = "ARR_ARROW_LIGHT_LUA"
        elseif global.selfAI:PickupIsAcquired("SonArrow_Shock") then
          arrowtype = "ARR_ARROW_SHOCK_LUA"
        end
      end
      for _, i in ipairs(enemiesOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
      end
    end)
    seq:At(2, function()
      global.heroInput = false
    end)
  elseif self.FollowUpType == "HeroSpreadshot_PID" then
    seq:At(0, function()
      FollowUpAttackPriority:Reset(self.FollowUpType)
      global.sTargetMode = "FollowUpTarget"
      ai:SetCombatTarget(global.FollowUpTarget)
      game.Audio.PlayBanterNonCritical("cbt_son_save")
      ai:TriggerMoveEvent("kLE_SpreadShot_PID")
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread"), ai:IsInNavigationMove()
    end, function()
      local enemiesOnScreen = helper.getOnScreenEnemySorted()
      local arrowtype = "ARR_ARROW_LUA_SPREAD"
      for _, i in ipairs(enemiesOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
        if _ == 3 then
          break
        end
      end
    end)
    seq:When(function()
      return ai:HasMarker("ShootSpread2"), ai:IsInNavigationMove()
    end, function()
      local enemiesOnScreen = helper.getOnScreenEnemySorted()
      local arrowtype = "ARR_ARROW_LUA_SPREAD"
      for _, i in ipairs(enemiesOnScreen) do
        local arrowData = {}
        arrowData.Tweak = arrowtype
        arrowData.Creator = ai
        arrowData.CreatorEmitJoint = "JORightWrist1Tip"
        arrowData.Target = i
        game.Combat.EmitArrow(arrowData)
        if _ == 3 then
          break
        end
      end
    end)
    seq:At(2, function()
      global.heroInput = false
    end)
  elseif self.FollowUpType == "EnemyLieAttack" then
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = true
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      ai:TriggerMoveEvent("kLE_BowSwingSlamDownEnter")
    end)
    seq:When(function()
      return global.FollowUpTarget ~= nil and ai:GetTargetCreature() == global.FollowUpTarget and (global.FollowUpTarget.WorldPosition - ai.WorldPosition):Length() < 3.25, global.FollowUpTarget == nil or global.FollowUpTarget.WorldPosition == nil or not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_LIE_STOMACH") and not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_EXPOSE_BACK") and not DL.CheckCreatureContext(global.FollowUpTarget:GetContext(), "REACT_CRUMPLE")
    end, function()
    end)
  elseif self.FollowUpType == "KillEnemy" then
    if global.FollowUpTarget ~= nil then
      local enemyID = DL.ReturnStringID(global.FollowUpTarget)
      if enemyID == "draugr00" then
        seq:At(0, function()
          global.sTargetMode = "FollowUpTarget"
          global.bGoToTarget = true
          ai:SetCombatTarget(global.FollowUpTarget)
          FollowUpAttackPriority:Reset(self.FollowUpType)
          ai:TriggerMoveEvent("kLE_ForceDashStandKill")
        end)
      end
    end
  elseif self.FollowUpType == "GrabLargeEnemy" then
    if global.FollowUpTarget ~= nil then
      seq:At(0, function()
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        ai:TriggerMoveEvent("kLE_GrabJotunn")
      end)
    end
  elseif self.FollowUpType == "JumpSlamDownAttack" then
    seq:At(0, function()
      if global.FollowUpTarget ~= nil then
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        local attackerID = DL.ReturnStringID(global.FollowUpTarget)
        if attackerID == "traveler00" or attackerID == "traveler10" or attackerID == "traveler30" then
          ai:TriggerMoveEvent("kLE_TravelerJumpAttack")
        else
          ai:TriggerMoveEvent("kLE_WulverJumpAttack")
        end
      end
    end)
  elseif self.FollowUpType == "JumpSlamDownAttackSmall" then
    if global.FollowUpTarget ~= nil then
      seq:At(0, function()
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        ai:TriggerMoveEvent("kLE_WulverJumpAttack")
      end)
    end
  elseif self.FollowUpType == "Trip" then
    if global.FollowUpTarget ~= nil then
      seq:At(0, function()
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        ai:TriggerMoveEvent("kLE_TrollBeatDownFail")
      end)
    end
  elseif self.FollowUpType == "CombatPostUp" then
    if global.FollowUpTarget ~= nil then
      seq:At(0, function()
        global.sTargetMode = "FollowUpTarget"
        global.bGoToTarget = true
        ai:SetCombatTarget(global.FollowUpTarget)
        FollowUpAttackPriority:Reset(self.FollowUpType)
        ai:TriggerMoveEvent("kLE_TrollPostUp")
        if ai:HasMarker("ArrowTutorial") then
          ai:RemoveMarker("ArrowTutorial")
        end
      end)
    end
  elseif self.FollowUpType == "HealPlayer" then
    seq:At(0, function()
      global.sTargetMode = "TargetKratos"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.Player)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      ai:TriggerMoveEvent("kLE_ThrowHealth")
    end)
  elseif self.FollowUpType == "SonSurprised" then
    seq:At(0, function()
      global.sTargetMode = "TargetKratos"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.Player)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      ai:TriggerMoveEvent("kLE_SonSurprised")
    end)
    seq:At(1, function()
      global.sTargetMode = "TargetKratos"
      ai:SetCombatTarget(global.Player)
    end)
  elseif self.FollowUpType == "MultiFocusShot" then
    ai:AddMarker("AllowDodgeOverride")
    seq:At(0, function()
      global.sTargetMode = "FollowUpTarget"
      global.bGoToTarget = false
      ai:SetCombatTarget(global.FollowUpTarget)
      FollowUpAttackPriority:Reset(self.FollowUpType)
      local target = global.FollowUpTarget
      if target == nil then
        target = game.Player.FindPlayer()
      end
      local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, target.WorldPosition)
      local sonspeed = ai:GetVelocity():Length()
      if -60 <= desiredDirectionDelta and desiredDirectionDelta <= 60 then
        ai:TriggerMoveEvent("kLEResponseTurnAndShoot0")
      elseif 60 < desiredDirectionDelta and desiredDirectionDelta <= 180 then
        ai:TriggerMoveEvent("kLEResponseTurnAndShoot120R")
      elseif desiredDirectionDelta < -60 and -180 <= desiredDirectionDelta then
        ai:TriggerMoveEvent("kLEResponseTurnAndShoot120L")
      end
    end)
    seq:At(0.3, function()
      game.Audio.PlayBanterNonCritical("Followup_Air")
    end)
  else
    seq:When(function()
      if not global.goPlayerTarget then
        return false, true
      else
        local advance = global.goPlayerTarget:GetWorldPosition() and (global.goPlayerTarget:GetWorldPosition() - ai:GetWorldPosition()):Length() < 2
        local cancel = global.goPlayerTarget:GetAI() == nil or global.goPlayerTarget:IsInNavigationMove() or global.goPlayerTarget:IsDoingSyncMove()
        return advance, cancel
      end
    end)
    seq:Always(function()
      ai:ForceMove("BRA_CSAttKnifeFollowUpEnter")
      global.sTargetMode = "AcquireKratosTarget"
      ai:SetCombatTarget(global.goPlayerTarget)
    end)
  end
  seq:When(function()
    return not ai:HasMarker("InFollowup")
  end)
end
function FollowUpAttackPriority.Events:OnBroadcastReaction(event, ai, global, constants)
  if event.source == nil then
    return
  end
  if self.FollowUpAttackStart or game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") >= 1 then
    return
  end
  if not constants.followUpSkill and not DL.CheckCreatureContext(event.broadcastContext, "ESCAPE_AWAY") then
    return
  end
  if global.bInCombat == false or ai:IsDoingSyncMove() or constants.StandGround or ai.OwnedPOI ~= nil then
    return
  end
  if ai:HasMarker("DoNotFollowupPriority") or ai:HasMarker("DoNotFollowup") then
    return
  end
  if event.source:PickupIsAcquired("HealthThreshold") and event.source:PickupGetStage("HealthThreshold") == 2 then
    return
  end
  local onScreen = constants.onCamera
  local canPerformHigherTierSkill = gVFSFollowupUpgraded.value
  if DL.CheckCreatureContext(event.broadcastContext, "COMMAND_FOLLOWUP") and canPerformHigherTierSkill then
    if not global.Player:HasMarker("CommandFollowup") then
      return
    end
    if helper.getNumberOfOnScreenEnemy() >= 0 and FollowUpAttackPriority:SkillAvailable("JumpOffBack_Finisher") and self.FollowUpAttackStart == false and onScreen == false then
      self.FollowUpAttackStart = true
      self.FollowUpType = "JumpOffBack_Finisher"
      global.FollowUpTarget = global.Player
      ai:SetCombatTarget(global.Player)
      self.FollowUpAttackStart = true
      self.KratosDropKicked = false
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_SURPRISED") then
    if global.Player:HasMarker("LastEnemyCS") and FollowUpAttackPriority:SkillAvailable("SonSurprised") and self.FollowUpAttackStart == false then
      self.FollowUpAttackStart = true
      self.FollowUpType = "SonSurprised"
      global.FollowUpTarget = global.Player
      ai:SetCombatTarget(global.Player)
      self.FollowUpAttackStart = true
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_PROTECT") and canPerformHigherTierSkill then
    if global.emotionState.value == 3 then
      return
    end
    local lastattacker = global.Player:GetLastAttacker()
    local attackerID = DL.ReturnStringID(lastattacker)
    if lastattacker ~= nil and helper.CanMeleeAgainst(DL.ReturnStringID(lastattacker)) then
      if lastattacker:PickupIsAcquired("DraugrPowerArmsL") or (ai.WorldPosition - global.Player.WorldPosition):Length() > 4 then
        return
      elseif (ai.WorldPosition - lastattacker.WorldPosition):Length() < 12 and FollowUpAttackPriority:SkillAvailable("Protect") and attackerID ~= "flyer00" and attackerID ~= "flyer10" then
        self.FollowUpAttackStart = true
        self.FollowUpType = "Protect"
        global.FollowUpTarget = lastattacker
        ai:SetCombatTarget(global.FollowUpTarget)
        self.FollowUpAttackStart = true
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_ENEMY_HOLD") and canPerformHigherTierSkill then
    local playerTarget = global.Player:GetTargetCreature()
    if playerTarget == nil then
      return
    end
    local distToPlayer = (playerTarget.WorldPosition - global.Player.WorldPosition):Length()
    local distToSon = (playerTarget.WorldPosition - ai.WorldPosition):Length()
    if distToPlayer < 4 then
      if distToSon < 2.5 then
        if helper.IsMountable(DL.ReturnStringID(playerTarget)) and FollowUpAttackPriority:SkillAvailable("HoldCleaveEnemy") then
          self.FollowUpAttackStart = true
          self.FollowUpType = "HoldCleaveEnemy"
          global.FollowUpTarget = playerTarget
          ai:SetCombatTarget(global.FollowUpTarget)
          self.FollowUpAttackStart = true
        end
      elseif FollowUpAttackPriority:SkillAvailable("StunCleaveEnemy") then
        self.FollowUpAttackStart = true
        self.FollowUpType = "StunCleaveEnemy"
        global.FollowUpTarget = playerTarget
        ai:SetCombatTarget(global.FollowUpTarget)
        self.FollowUpAttackStart = true
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "ENEMY_ATTACK") and canPerformHigherTierSkill then
    if event.source:HasMarker("React") then
      return
    end
    if constants.heroInRage then
      return
    end
    if event.source:CheckDecision("tweak_Decision_OnCamera") == false then
      return
    end
    local yDiff = event.source.WorldPosition.y - ai.WorldPosition.y
    if 2 < yDiff then
      return
    end
    local attackerID = DL.ReturnStringID(event.source)
    if attackerID == "JOTUNN00" or attackerID == "JOTUNN10" then
      if 0 < global.jotunnGrabTimer then
        return
      end
      local inCone = game.AIUtil.IntersectPointCone(ai.WorldPosition, event.source.WorldPosition, event.source:GetWorldForward(), 190, 30)
      if inCone then
        return
      end
      if FollowUpAttackPriority:SkillAvailable("GrabLargeEnemy") then
        self.FollowUpAttackStart = true
        self.FollowUpType = "GrabLargeEnemy"
        global.FollowUpTarget = event.source
        return
      end
    elseif attackerID == "TRAVELER00" or attackerID == "WULVER00" or attackerID == "WULVER10" then
      if FollowUpAttackPriority:SkillAvailable("JumpSlamDownAttack") then
        self.FollowUpAttackStart = true
        self.FollowUpType = "JumpSlamDownAttack"
        global.FollowUpTarget = event.source
        return
      end
    elseif attackerID ~= "FLYER00" and attackerID ~= "FLYER10" and attackerID ~= "FLYER20" and (event.source.WorldPosition - global.Player.WorldPosition):Length() < 8 and FollowUpAttackPriority:SkillAvailable("JumpSlamDownAttackSmall") then
      self.FollowUpAttackStart = true
      self.FollowUpType = "JumpSlamDownAttackSmall"
      global.FollowUpTarget = event.source
      return
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_PROTECT_WEAK") and canPerformHigherTierSkill then
    if global.Player:PickupIsAcquired("Debuff_Hero_Frost") == false then
      return
    end
    if global.emotionState.value == 3 then
      return
    end
    local lastattacker = global.Player:GetLastAttacker()
    if lastattacker ~= nil and lastattacker:HasMarker("Barrage") and FollowUpAttackPriority:SkillAvailable("ProtectWeak") and (ai.WorldPosition - lastattacker.WorldPosition):Length() < 15 then
      self.FollowUpAttackStart = true
      self.FollowUpType = "ProtectWeak"
      global.FollowUpTarget = lastattacker
      ai:SetCombatTarget(global.FollowUpTarget)
      self.FollowUpAttackStart = true
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_FOLLOWUP_TAKEDOWN") and canPerformHigherTierSkill then
    if not global.Player:HasMarker("CommandFollowup") then
      return
    end
    if FollowUpAttackPriority:SkillAvailable("JumpOffBack_Takedown") and self.FollowUpAttackStart == false and gVFSDebugHeroSyncSave.value == true then
      local enemy = global.Player:GetLastAttacker()
      if enemy ~= nil and enemy:HasMarker("SonVictim") then
        self.FollowUpAttackStart = true
        self.FollowUpType = "JumpOffBack_Takedown"
        global.FollowUpTarget = global.Player
        ai:SetCombatTarget(global.Player)
        self.FollowUpAttackStart = true
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_COVER_KRATOS") then
    if global.emotionState.value == 3 then
      return
    end
    if not helper.CanPerformSkill(constants, "skill_KratosSave") then
      return
    end
    local lastattacker = event.source
    local attackerID = DL.ReturnStringID(lastattacker)
    if lastattacker ~= nil and helper.CanMeleeAgainst(DL.ReturnStringID(lastattacker)) then
      if lastattacker:PickupIsAcquired("DraugrPowerArmsL") then
        return
      else
        if lastattacker:GetTargetCreature() ~= global.Player then
          return
        end
        if (ai.WorldPosition - lastattacker.WorldPosition):Length() < 10 and FollowUpAttackPriority:SkillAvailable("CoverBack") and attackerID ~= "flyer00" and attackerID ~= "flyer10" and lastattacker:CheckDecision("tweak_Decision_OnCamera") == false then
          self.FollowUpAttackStart = true
          self.FollowUpType = "CoverBack"
          global.FollowUpTarget = lastattacker
          ai:SetCombatTarget(global.FollowUpTarget)
          self.FollowUpAttackStart = true
        end
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "ESCAPE_AWAY") then
    if FollowUpAttackPriority:SkillAvailable("DodgeAwayFromBroadcast") then
      self.FollowUpAttackStart = true
      self.FollowUpType = "DodgeAwayFromBroadcast"
      global.FollowUpTarget = event.source
      return
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_SHOOTMULTIPLE") and canPerformHigherTierSkill then
    local dist = (ai.WorldPosition - event.source.WorldPosition):Length()
    local numEnemies = #ai:FindEnemies(30)
    if (constants.onCamera and dist < 10 or numEnemies == 1 or event.source:PickupIsAcquired("Son_FollowUpBypassRules")) and FollowUpAttackPriority:SkillAvailable("MultiFocusShot") then
      self.FollowUpAttackStart = true
      self.FollowUpType = "MultiFocusShot"
      global.FollowUpTarget = event.source
      return
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "COMMAND_FLIP") and canPerformHigherTierSkill then
    if event.source:CheckDecision("tweak_Decision_OnCamera") == false or constants.heroInRage == true then
      return
    end
    local distanceToTarget = (event.source.WorldPosition - ai.WorldPosition):Length()
    if FollowUpAttackPriority:SkillAvailable("Launch") and distanceToTarget < 12 then
      self.FollowUpAttackStart = true
      self.FollowUpType = "Launch"
      global.FollowUpTarget = event.source
      return
    end
  elseif (DL.CheckCreatureContext(event.broadcastContext, "COMMAND_TACKLE") or DL.CheckCreatureContext(event.broadcastContext, "COMMAND_JUMP_OFF_ENEMY")) and canPerformHigherTierSkill and event.source:GetLastAttacker() == global.Player then
    if event.source:CheckDecision("tweak_Decision_OnCamera") == false or constants.heroInRage == true then
      return
    end
    local playerDistance = (event.source.WorldPosition - global.Player.WorldPosition):Length()
    local distanceToTarget = (event.source.WorldPosition - ai.WorldPosition):Length()
    if FollowUpAttackPriority:SkillAvailable("EnemyLieAttack") and distanceToTarget < 9 and 2 < playerDistance then
      self.FollowUpAttackStart = true
      self.FollowUpType = "EnemyLieAttack"
      global.FollowUpTarget = event.source
      return
    end
  end
end
function FollowUpAttackPriority:Update(ai, global, constants)
  if (self.FollowUpType == "Tackle" or self.FollowUpType == "JumpOffEnemyBack" or self.FollowUpType == "EnemyHold") and (global.FollowUpTarget == nil or global.FollowUpTarget:GetHitPoints() <= 0 or global.FollowUpTarget == global.Player:GetTargetCreature() and (global.Player.WorldPosition - global.FollowUpTarget.WorldPosition):Length() < (ai.WorldPosition - global.FollowUpTarget.WorldPosition):Length()) then
    self.FollowUpAttackStart = false
    ai:TriggerMoveEvent("kLE_StopDash")
  end
  if ai:HasMarker("ForceExit") then
    self.FollowUpAttackStart = false
  end
end
function FollowUpAttackPriority:Exit(ai, global, constants)
  global.sTargetMode = constants.sPreviousTargetMode
  global.bGoToTarget = false
  self.FollowUpAttackStart = false
  self.FollowUpType = "none"
  ai:RemoveMarker("AllowDodgeOverride")
  ai:RemoveMarker("DoNotInterrupt")
  ai:RemoveMarker("AttackOffscreen")
  if ai:PickupIsAcquired("SonSpeedNavMod") then
    ai:PickupRelinquish("SonSpeedNavMod")
  end
  constants.boostSpeed = false
  global.heroInput = false
end
function FollowUpAttackPriority:IsAvailable(ai, global, constants)
  return self.FollowUpAttackStart and constants.stats.advancedFollowUpEnabled
end
function FollowUpAttackPriority:UpdateData(ai, global, constants)
  constants.GrabTimerAvailable = self.actionTimers.GrabEnemy.Available
  if ai:HasMarker("DoNotFollowupPriority") then
    self.FollowUpAttackStart = false
  end
  if global.bInCombat == false then
    return
  end
  local BlockingFearAttritionPerUpdate = 0.06
  local DodgingFearAttritionPerUpdate = 0.1
  local HitReactFearAttritionPerUpdate = 0.02
  local MaxFear = 100
  local PlayerFearAttritionCooldownMax = 1
  local BlockingFearIncreasePerFrame = 0.7
  local DodgingFearIncreasePerFrame = 0.7
  local HitReactFearIncreasePerFrame = 0.35
  local FearIncreasePerAttackPerFrame = 0.3
  local FearIncreasePerDodge = 10
  local FearIncreasePerBlockedHit = 5
  local LowOnHealthThreshold = 60
  local LowestOnHealthThreshold = 35
  local MaxEnemiesBeforeIncreasingFear = 0
  local FearPerAddedEnemy = 3
  local MaxFearFromEnemies = 30
  local MaxFearFromLowOnHealth = 30
  local MaxFearFromLowestOnHealth = 40
  local MaxFearFromDodging = 25
  local MaxFearFromBlocking = 25
  local MaxFearFromHitReact = 20
  local player = game.Player.FindPlayer()
  local numEnemies = #player:FindEnemies(10)
  local isBlocking = player:HasMarker("Blocking")
  local playerHealth = player.PercentageOfHealth
  if LowOnHealthThreshold > playerHealth then
    self.PlayerFearTrackingData.PointsFromHealth = (LowOnHealthThreshold + 10 - playerHealth) / LowOnHealthThreshold * MaxFearFromLowOnHealth
  end
  if MaxEnemiesBeforeIncreasingFear < numEnemies then
    self.PlayerFearTrackingData.PointsFromEnemies = FearPerAddedEnemy * (numEnemies - MaxEnemiesBeforeIncreasingFear)
  end
  if player:HasMarker("Evading") and 0 < #player:FindEnemies(5) then
    self.PlayerFearTrackingData.PointsFromDodging = self.PlayerFearTrackingData.PointsFromDodging + DodgingFearIncreasePerFrame
  end
  if player:HasMarker("BlockedAttack") then
    self.PlayerFearTrackingData.PointsFromBlocking = self.PlayerFearTrackingData.PointsFromBlocking + BlockingFearIncreasePerFrame
  end
  if player:HasMarker("React") then
    self.PlayerFearTrackingData.PointsFromHitReact = self.PlayerFearTrackingData.PointsFromHitReact + HitReactFearIncreasePerFrame
  end
  if MaxFearFromBlocking < self.PlayerFearTrackingData.PointsFromBlocking then
    self.PlayerFearTrackingData.PointsFromBlocking = MaxFearFromBlocking
  end
  if MaxFearFromLowOnHealth < self.PlayerFearTrackingData.PointsFromHealth then
    self.PlayerFearTrackingData.PointsFromHealth = MaxFearFromLowOnHealth
  end
  if LowestOnHealthThreshold > playerHealth then
    self.PlayerFearTrackingData.PointsFromHealth = MaxFearFromLowestOnHealth
  end
  if MaxFearFromDodging < self.PlayerFearTrackingData.PointsFromDodging then
    self.PlayerFearTrackingData.PointsFromDodging = MaxFearFromDodging
  end
  if MaxFearFromEnemies < self.PlayerFearTrackingData.PointsFromEnemies then
    self.PlayerFearTrackingData.PointsFromEnemies = MaxFearFromEnemies
  end
  if MaxFearFromHitReact < self.PlayerFearTrackingData.PointsFromHitReact then
    self.PlayerFearTrackingData.PointsFromHitReact = MaxFearFromHitReact
  end
  local attackingAttritionBonus = 0
  if player:HasMarker("Attacking") then
    attackingAttritionBonus = FearIncreasePerAttackPerFrame
  end
  if 0 < self.PlayerFearTrackingData.PointsFromBlocking then
    self.PlayerFearTrackingData.PointsFromBlocking = self.PlayerFearTrackingData.PointsFromBlocking - (BlockingFearAttritionPerUpdate + attackingAttritionBonus)
  end
  if 0 < self.PlayerFearTrackingData.PointsFromDodging then
    self.PlayerFearTrackingData.PointsFromDodging = self.PlayerFearTrackingData.PointsFromDodging - (DodgingFearAttritionPerUpdate + attackingAttritionBonus)
  end
  if 0 < self.PlayerFearTrackingData.PointsFromHitReact then
    self.PlayerFearTrackingData.PointsFromHitReact = self.PlayerFearTrackingData.PointsFromHitReact - (HitReactFearAttritionPerUpdate + attackingAttritionBonus)
  end
  local playerFearMeter = self.PlayerFearTrackingData.PointsFromBlocking + self.PlayerFearTrackingData.PointsFromDodging + self.PlayerFearTrackingData.PointsFromEnemies + self.PlayerFearTrackingData.PointsFromHealth + self.PlayerFearTrackingData.PointsFromHitReact
  if MaxFear < playerFearMeter then
    playerFearMeter = MaxFear
  end
  if _G.gVFSDebug.value then
    engine.DrawText2D("PLAYER FEAR METER", 1500, 100, color.black)
    engine.DrawRect2D(1500, 150, 50, 500, color.white, 50)
  end
  local remainingPercent = 100
  local barOffset = (remainingPercent - self.PlayerFearTrackingData.PointsFromHealth) * 0.01 * 500
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, self.PlayerFearTrackingData.PointsFromHealth * 0.01 * 500, color.red, 80)
  end
  remainingPercent = remainingPercent - self.PlayerFearTrackingData.PointsFromHealth
  barOffset = (remainingPercent - self.PlayerFearTrackingData.PointsFromEnemies) * 0.01 * 500
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, self.PlayerFearTrackingData.PointsFromEnemies * 0.01 * 500, color.blue, 80)
  end
  remainingPercent = remainingPercent - self.PlayerFearTrackingData.PointsFromEnemies
  barOffset = (remainingPercent - self.PlayerFearTrackingData.PointsFromDodging) * 0.01 * 500
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, self.PlayerFearTrackingData.PointsFromDodging * 0.01 * 500, color.yellow, 80)
  end
  remainingPercent = remainingPercent - self.PlayerFearTrackingData.PointsFromDodging
  barOffset = (remainingPercent - self.PlayerFearTrackingData.PointsFromBlocking) * 0.01 * 500
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, self.PlayerFearTrackingData.PointsFromBlocking * 0.01 * 500, color.purple, 80)
  end
  remainingPercent = remainingPercent - self.PlayerFearTrackingData.PointsFromBlocking
  barOffset = (remainingPercent - self.PlayerFearTrackingData.PointsFromHitReact) * 0.01 * 500
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, self.PlayerFearTrackingData.PointsFromHitReact * 0.01 * 500, color.orange, 80)
  end
  remainingPercent = remainingPercent - self.PlayerFearTrackingData.PointsFromHitReact
  barOffset = 149.99998
  if _G.gVFSDebug.value then
    engine.DrawRect2D(1500, 150 + barOffset, 50, 5, color.green, 80)
  end
  FollowUpAttackPriority:UpdateTimers(ai, global, constants)
  if self.FollowUpAttackStart or ai:IsDoingSyncMove() or constants.StandGround or ai:HasMarker("DoNotFollowupPriority") or ai.OwnedPOI ~= nil then
    return
  end
  if self.TrackingDangerTarget and constants.followUpSkill then
    local sortedEnemies = helper.getOnScreenEnemySorted()
    local closestOnScreenEnemy
    if sortedEnemies then
      closestOnScreenEnemy = sortedEnemies[1]
    end
    if closestOnScreenEnemy then
      global.FollowUpTarget = closestOnScreenEnemy
    end
  end
  if 70 <= playerFearMeter then
    local nearestTarget
    for _, i in ipairs(player:FindEnemies(30)) do
      if 0 < i:GetHitPoints() then
        nearestTarget = i
        break
      end
    end
    self.PlayerFearTrackingData.PointsFromDodging = 0
    self.PlayerFearTrackingData.PointsFromBlocking = 0
    self.PlayerFearTrackingData.PointsFromHitReact = 0
    if nearestTarget then
      global.FollowUpTarget = nearestTarget
      if FollowUpAttackPriority:SkillAvailable("HeroSpreadshot_PID") then
        self.FollowUpType = "HeroSpreadshot_PID"
        self.FollowUpAttackStart = true
      end
    end
    return
  end
  if global.Player:HasMarker("React") and constants.followUpSkill and global.Player:HasMarker("CannotDodgeOrEvade") then
    local lastAttacker = global.Player:GetLastAttacker()
    local targets = DL.FindLivingEnemies(global.Player, 10)
    for _, i in ipairs(targets) do
      if i:HasMarker("Attacking") and i ~= lastAttacker and i:GetTargetCreature() == global.Player then
        self.FollowUpAttackStart = true
        self.FollowUpType = "CriticalShot"
        global.FollowUpTarget = i
        return
      end
    end
  end
  if (ai:PickupIsAcquired("SonArmorDefense01") or ai:PickupIsAcquired("SonArmorDefense02") or ai:PickupIsAcquired("SonArmorDefense03") or ai:PickupIsAcquired("SonArmorBonus01") or ai:PickupIsAcquired("SonArmorBonus02") or ai:PickupIsAcquired("SonArmorBonus04")) and FollowUpAttackPriority:SkillAvailable("HealPlayer") then
    local maxMeter = global.Player:MeterGetMax("Health")
    if 0 < maxMeter then
      local percent = global.Player:MeterGetValue("Health") / maxMeter
      if percent < 0.4 then
        self.FollowUpAttackStart = true
        self.FollowUpType = "HealPlayer"
        global.FollowUpTarget = global.Player
        return
      end
    end
  end
end
function FollowUpAttackPriority.Events:FollowUpNow(ai, global, constants, followupType, followupTarget)
  if ai:IsDoingSyncMove() or ai:HasMarker("SonCommand_WorldInteraction") or ai:GetNavCurve() ~= nil or poilib.IsPOIActive() then
    return
  end
  if followupTarget:PickupIsAcquired("HealthThreshold") and followupTarget:PickupGetStage("HealthThreshold") == 2 then
    return
  end
  if global.closeCombatStart and (constants.closeCombatAction == "BowSwingLaunch" or constants.closeCombatAction == "GrabEnemy" or constants.closeCombatAction == "BowSwingTrip") then
    return
  end
  if FollowUpAttackPriority:SkillAvailable(followupType) then
    self.FollowUpAttackStart = true
    self.FollowUpType = followupType
    global.FollowUpTarget = followupTarget
    return
  end
end
function FollowUpAttackPriority:UpdateTimers(ai, global, constants)
  self.timerPast = self.timerPast + ai:GetFrameTime()
  if self.timerPast > 1 then
    local frametime = self.timerPast
    for _, i in pairs(self.actionTimers) do
      i.CurrentTime = i.CurrentTime - frametime
      if i.CurrentTime <= 0 then
        i.CurrentTime = 0
        i.Available = true
      end
    end
    self.timerPast = 0
  end
end
function FollowUpAttackPriority:Reset(skillname)
  if self.actionTimers[skillname] ~= nil then
    self.actionTimers[skillname].CurrentTime = self.actionTimers[skillname].CooldownTime
    self.actionTimers[skillname].Available = false
  end
end
function FollowUpAttackPriority:SkillAvailable(skillname)
  if self.actionTimers[skillname] ~= nil then
    return self.actionTimers[skillname].Available
  end
  return false
end
function FollowUpAttackPriority:OnBrainInit(ai, global, constants)
  constants.GrabTimerAvailable = false
  self.FollowUpAttackStart = false
  self.FollowUpType = "none"
  self.PlayerFearMeter = 0
  self.PlayerFearTrackingData = {
    FearDecreasing = false,
    PointsFromDodging = 0,
    PointsFromHealth = 0,
    PointsFromEnemies = 0,
    PointsFromBlocking = 0,
    PointsFromHitReact = 0
  }
  self.PlayerFearAttritionCooldown = 0
  self.TrackingDangerTarget = false
  global.bGoToTarget = false
  constants.sPreviousTargetMode = "RegularTarget"
  self.timer = 3
  self.timerPast = 0
  global.heroInput = false
  self.offScreenTimer = 0
  self.actionTimers = {
    JumpOffBack_Finisher = {
      CurrentTime = 0,
      CooldownTime = 20,
      Available = false
    },
    JumpOffBack_Takedown = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    CriticalShot = {
      CurrentTime = 0,
      CooldownTime = 3,
      Available = false
    },
    FastCrit = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    MultiFocusShot = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    Protect = {
      CurrentTime = 0,
      CooldownTime = 3,
      Available = false
    },
    ProtectPullAggro = {
      CurrentTime = 0,
      CooldownTime = 45,
      Available = false
    },
    ProtectWeak = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    ProtectOffScreen = {
      CurrentTime = 0,
      CooldownTime = 5,
      Available = false
    },
    ProtectProjectile = {
      CurrentTime = 0,
      CooldownTime = 12,
      Available = false
    },
    Launch = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    Trip = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    GrabEnemy = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    DodgeAwayFromBroadcast = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    CoverBack = {
      CurrentTime = 0,
      CooldownTime = 30,
      Available = false
    },
    EnemyLieAttack = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    GrabLargeEnemy = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    KillEnemy = {
      CurrentTime = 0,
      CooldownTime = 15,
      Available = false
    },
    JumpSlamDownAttack = {
      CurrentTime = 0,
      CooldownTime = 60,
      Available = false
    },
    JumpSlamDownAttackSmall = {
      CurrentTime = 60,
      CooldownTime = 60,
      Available = false
    },
    HealPlayer = {
      CurrentTime = 150,
      CooldownTime = 150,
      Available = false
    },
    SonSurprised = {
      CurrentTime = 0,
      CooldownTime = 2,
      Available = false
    },
    CombatPostUp = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    Taunt = {
      CurrentTime = 0,
      CooldownTime = 1,
      Available = false
    },
    Protect_PID = {
      CurrentTime = 0,
      CooldownTime = 60,
      Available = false
    },
    HeroSpreadshot_PID = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    }
  }
  local difficulty = global.Player:AttributeGetValue("Difficulty")
  if difficulty ~= nil and difficulty == 4 then
    self.actionTimers.HealPlayer.CooldownTime = 300
    self.actionTimers.HealPlayer.CurrentTime = 300
    self.actionTimers.GrabEnemy.CooldownTime = 10
    self.actionTimers.GrabEnemy.CurrentTime = 10
  end
end
function AutoMode:OnBrainInit(ai, global, constants)
  self.actionTimers = {
    UseSummon = {
      CurrentTime = 0,
      CooldownTime = 30,
      Available = false
    },
    Taunt = {
      CurrentTime = 0,
      CooldownTime = 120,
      Available = false
    },
    KillSteal = {
      CurrentTime = 0,
      CooldownTime = 10,
      Available = false
    },
    AggressiveAttack = {
      CurrentTime = 0,
      CooldownTime = 15,
      Available = false
    },
    CombatPostUp = {
      CurrentTime = 0,
      CooldownTime = 5,
      Available = false
    }
  }
end
function AutoMode:Enter(ai, global, constants)
end
function AutoMode:Exit(ai, global, constants)
end
function AutoMode:UpdateData(ai, global, constants)
  if global.emotionState.value ~= 3 and not ai:HasMarker("FirstTrollBehavior") then
    return
  end
  AutoMode:UpdateTimers(ai, global, constants)
end
function AutoMode:Update(ai, global, constants)
  local hasMarker = ai:HasMarker("FirstTrollBehavior")
  if AutoMode:SkillAvailable("UseSummon") and not hasMarker then
    AutoMode:Reset("UseSummon")
    engine.SendHook("OnCommandSonStart", ai, "Charged_Jerk")
  elseif AutoMode:SkillAvailable("KillSteal") and not hasMarker then
    local currentTarget = ai:GetTargetCreature()
    if currentTarget ~= nil and currentTarget ~= global.Player then
      AutoMode:Reset("KillSteal")
      statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "KillEnemy", currentTarget)
    end
  elseif AutoMode:SkillAvailable("Taunt") and not hasMarker then
    local currentTarget = ai:GetTargetCreature()
    if currentTarget ~= nil then
      AutoMode:Reset("Taunt")
      statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "Taunt", currentTarget)
    end
  elseif AutoMode:SkillAvailable("CombatPostUp") and ai:HasMarker("ArrowTutorial") then
    local currentTarget = helper.LargeCreatureExists(ai, global, constants, 40)
    if currentTarget ~= nil then
      AutoMode:Reset("CombatPostUp")
      statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "CombatPostUp", currentTarget)
    end
  elseif AutoMode:SkillAvailable("AggressiveAttack") and hasMarker and ai:HasMarker("RushTroll") then
    local currentTarget = helper.LargeCreatureExists(ai, global, constants, 40)
    if currentTarget ~= nil then
      AutoMode:Reset("AggressiveAttack")
      statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "Trip", currentTarget)
    end
  end
end
function AutoMode:IsAvailable(ai, global, constants)
  return global.emotionState.value == 3 or ai:HasMarker("FirstTrollBehavior")
end
function AutoMode:UpdateTimers(ai, global, constants)
  for _, i in pairs(self.actionTimers) do
    i.CurrentTime = i.CurrentTime - ai:GetFrameTime()
    if i.CurrentTime <= 0 then
      i.CurrentTime = 0
      i.Available = true
    end
  end
end
function AutoMode:Reset(skillname)
  if self.actionTimers[skillname] ~= nil then
    self.actionTimers[skillname].CurrentTime = self.actionTimers[skillname].CooldownTime
    self.actionTimers[skillname].Available = false
  end
end
function AutoMode:SkillAvailable(skillname)
  if self.actionTimers[skillname] ~= nil then
    return self.actionTimers[skillname].Available
  end
  return false
end
function LuaHook_CombatBackpedal(ai, branch)
  return nil
end
function LuaHook_InteractionSyncStarted(ai)
  if ai.OwnedPOI ~= nil then
    ai.OwnedPOI:SendEvent("Disengage")
  end
end
return Brain
