local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local helper = require("son.helper")
local bboardUtil = require("game.BlackboardUtil")
local poilib = require("behavior.poi")
AwarenessBrain = statemachine.StateMachine.New("AwarenessBrain")
local Relaxed = AwarenessBrain:State("Relaxed")
local InCombat = AwarenessBrain:State("InCombat")
local PostCombat = AwarenessBrain:State("PostCombat")
local PreCombat = AwarenessBrain:State("PreCombat")
local Steady = AwarenessBrain:State("Steady")
local PostPOISteady = AwarenessBrain:State("PostPOISteady")
local FollowPlayer = AwarenessBrain:State("FollowPlayer")
local InchForward = AwarenessBrain:State("InchForward")
local Wait = AwarenessBrain:State("Wait")
local NoticeHero = AwarenessBrain:State("NoticeHero")
local UrgentMoveAway = AwarenessBrain:State("UrgentMoveAway")
local GoToPoint = AwarenessBrain:State("GoToPoint")
local ForcedGoToPoint = AwarenessBrain:State("ForcedGoToPoint")
local ObserveBeforeMoving = AwarenessBrain:State("ObserveBeforeMoving")
local ObservedUrgentMoveAway = AwarenessBrain:State("ObservedUrgentMoveAway")
local MoveAway = AwarenessBrain:State("MoveAway")
local CombatSteady = AwarenessBrain:State("CombatSteady")
local MinorReposition = AwarenessBrain:State("MinorReposition")
local GenericReposition = AwarenessBrain:State("GenericReposition")
local SurvivalReposition = AwarenessBrain:State("SurvivalReposition")
local EvaluateSurvivalReposition = AwarenessBrain:State("EvaluateSurvivalReposition")
local UrgentReposition = AwarenessBrain:State("UrgentReposition")
local CloseCombat = AwarenessBrain:State("CloseCombat")
local PursueTarget = AwarenessBrain:State("PursueTarget")
local AggressiveFlank = AwarenessBrain:State("AggressiveFlank")
local UseForcedPath = AwarenessBrain:State("UseForcedPath")
local POI = AwarenessBrain:State("POI")
local Idle = AwarenessBrain:State("Idle")
function AwarenessBrain:SelectNextState(ai, global, constants)
  constants.tags = statemachine.ActiveTags()
  if ai:IsInVehicle() then
    return Idle
  end
  UpdateBehaviorStack(ai, global, constants)
  UpdateHeroState(ai, global, constants)
  if _G.gVFSDebugNoBrainLogic.value == true or ai:HasMarker("TweakLogicOnly") then
    return Steady
  end
  if global.currentState == "InCombat" then
    return GetCombatState(ai, global, constants)
  end
  if global.currentState == "PostCombat" then
    return GetPostCombatState(ai, global, constants)
  end
  if global.currentState == "EnterCombat" then
    return GetCombatState(ai, global, constants)
  end
  if global.currentState == "PreCombat" then
    return GetPreCombatState(ai, global, constants)
  end
  return GetRelaxedState(ai, global, constants)
end
function AwarenessBrain:OnBrainInit(ai, global, constants)
  constants.awarenessState = _G.constants.Steady
  constants.previousAwarenessState = _G.constants.Steady
  constants.panicNow = false
  constants.behaviorStack = {
    {
      behavior = _G.constants.Steady,
      duration = 0
    }
  }
  constants.intersectionRadius = 1.25
  constants.moveToPointActuator = {}
  constants.moveToPointActuator.Destination = ai.WorldPosition
  constants.moveToPointActuator.Speed = 2
  constants.moveToPointActuator.StopDistance = 0.5
  constants.moveToPointActuator.StartDistance = 0.1
  constants.allowResponseBehavior = true
  constants.randomAttackInput = true
end
function GetCombatState(ai, global, constants)
  if _G.gVFSDebugNoBrainLogic.value == true then
    return CombatSteady
  end
  if global.POIInfo.useThisPOI ~= nil and global.POIInfo.useThisPOI:GetStageName() ~= "ApproachOrMovingBreakOut" then
    return POI
  end
  SetRegenRate(ai, global, constants)
  UpdateBB(ai, global, constants)
  if constants.awarenessState == _G.constants.CombatSteady then
    return CombatSteady
  elseif constants.awarenessState == _G.constants.MinorReposition then
    return MinorReposition
  elseif constants.awarenessState == _G.constants.AggressiveFlank then
    return AggressiveFlank
  elseif constants.awarenessState == _G.constants.GenericReposition then
    return GenericReposition
  elseif constants.awarenessState == _G.constants.SurvivalReposition then
    return SurvivalReposition
  elseif constants.awarenessState == _G.constants.EvaluateSurvivalReposition then
    return EvaluateSurvivalReposition
  elseif constants.awarenessState == _G.constants.UrgentReposition then
    return UrgentReposition
  elseif constants.awarenessState == _G.constants.CloseCombat then
    return CloseCombat
  elseif constants.awarenessState == _G.constants.UrgentMoveAway then
    return UrgentMoveAway
  elseif constants.awarenessState == _G.constants.GoToPoint then
    return GoToPoint
  elseif constants.awarenessState == _G.constants.ForcedGoToPoint then
    return ForcedGoToPoint
  elseif constants.awarenessState == _G.constants.ObserveBeforeMoving then
    return ObserveBeforeMoving
  elseif constants.awarenessState == _G.constants.MoveAway then
    return MoveAway
  elseif constants.awarenessState == _G.constants.Wait then
    return Wait
  end
  constants.awarenessState = _G.constants.CombatSteady
  return CombatSteady
end
function GetPreCombatState(ai, global, constants)
  SetRegenRate(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil and global.POIInfo.useThisPOI:GetStageName() ~= "ApproachOrMovingBreakOut" then
    return POI
  end
  if constants.awarenessState == _G.constants.UrgentMoveAway then
    return UrgentMoveAway
  elseif constants.awarenessState == _G.constants.GoToPoint then
    return GoToPoint
  elseif constants.awarenessState == _G.constants.ForcedGoToPoint then
    return ForcedGoToPoint
  elseif constants.awarenessState == _G.constants.ObserveBeforeMoving then
    return ObserveBeforeMoving
  elseif constants.awarenessState == _G.constants.MoveAway then
    return MoveAway
  elseif constants.awarenessState == _G.constants.Wait then
    return Wait
  end
  return FollowPlayer
end
function GetPostCombatState(ai, global, constants)
  SetRegenRate(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil and global.POIInfo.useThisPOI:GetStageName() ~= "ApproachOrMovingBreakOut" then
    return POI
  end
  if constants.awarenessState == _G.constants.UrgentMoveAway then
    return UrgentMoveAway
  elseif constants.awarenessState == _G.constants.GoToPoint then
    return GoToPoint
  elseif constants.awarenessState == _G.constants.ForcedGoToPoint then
    return ForcedGoToPoint
  elseif constants.awarenessState == _G.constants.ObserveBeforeMoving then
    return ObserveBeforeMoving
  elseif constants.awarenessState == _G.constants.MoveAway then
    return MoveAway
  elseif constants.awarenessState == _G.constants.Wait then
    return Wait
  end
  return FollowPlayer
end
function GetRelaxedState(ai, global, constants)
  SetRegenRate(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil and global.POIInfo.useThisPOI:GetStageName() ~= "ApproachOrMovingBreakOut" then
    return POI
  end
  if constants.awarenessState == _G.constants.Steady then
    return Steady
  elseif constants.awarenessState == "UseForcedPath" then
    return UseForcedPath
  elseif constants.awarenessState == _G.constants.PostPOISteady then
    return PostPOISteady
  elseif constants.awarenessState == _G.constants.FollowPlayer then
    return FollowPlayer
  elseif constants.awarenessState == _G.constants.InchForward then
    return InchForward
  elseif constants.awarenessState == _G.constants.Wait then
    return Wait
  elseif constants.awarenessState == _G.constants.NoticeHero then
    return NoticeHero
  elseif constants.awarenessState == _G.constants.UrgentMoveAway then
    return UrgentMoveAway
  elseif constants.awarenessState == _G.constants.GoToPoint then
    return GoToPoint
  elseif constants.awarenessState == _G.constants.ForcedGoToPoint then
    return ForcedGoToPoint
  elseif constants.awarenessState == _G.constants.ObserveBeforeMoving then
    return ObserveBeforeMoving
  elseif constants.awarenessState == _G.constants.MoveAway then
    return MoveAway
  end
  return FollowPlayer
end
function CombatSteady.Events:OnBroadcastReaction(event, ai, global, constants)
  if event.source == nil then
    return
  end
  if DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_DEFAULT") or DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_STAGGER") then
    if constants.enemiesInReactionList[event.source] == nil then
      local stringID = string.upper(event.source:GetName())
      constants.enemiesInReactionList[event.source] = {}
      constants.enemiesInReactionList[event.source].id = stringID
      constants.enemiesInReactionList[event.source].perform = false
      constants.enemiesInReactionList[event.source].distCheck = 12
      if ShouldPerformAction(event.source) and event.source:GetLastAttacker() == global.Player then
        if helper.IsMountable(stringID) and helper.CanPerformSkill(constants, "skill_SonGrab") and constants.GrabTimerAvailable then
          constants.enemiesInReactionList[event.source].action = "GrabEnemy"
          constants.enemiesInReactionList[event.source].response = "NoResponse"
          constants.enemiesInReactionList[event.source].perform = true
          constants.enemiesInReactionList[event.source].playerDist = 6
        else
          constants.enemiesInReactionList[event.source].action = "ShootArrow"
          constants.enemiesInReactionList[event.source].response = "NoResponse"
          constants.enemiesInReactionList[event.source].perform = true
        end
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_BLOCKING") or DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_SPIN") or DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_STUMBLE") then
    if constants.enemiesInReactionList[event.source] == nil and constants.heroInRage == false then
      local stringID = string.upper(event.source:GetName())
      constants.enemiesInReactionList[event.source] = {}
      constants.enemiesInReactionList[event.source].id = stringID
      constants.enemiesInReactionList[event.source].perform = false
      constants.enemiesInReactionList[event.source].distCheck = 12
      if ShouldPerformAction(event.source) then
        local response, action = GetMeleeAbility(ai, global, constants, "BowSwingTrip", stringID)
        constants.enemiesInReactionList[event.source].action = action
        constants.enemiesInReactionList[event.source].response = response
        constants.enemiesInReactionList[event.source].perform = true
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_FLYBACK") or DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_SUPERFLYBACK") or DL.CheckCreatureContext(event.broadcastContext, "REGISTER_REACTION_GROUNDSLIDE") then
    if constants.enemiesInReactionList[event.source] == nil and constants.heroInRage == false then
      local stringID = string.upper(event.source:GetName())
      constants.enemiesInReactionList[event.source] = {}
      constants.enemiesInReactionList[event.source].id = stringID
      constants.enemiesInReactionList[event.source].perform = false
      constants.enemiesInReactionList[event.source].distCheck = 12
      if ShouldPerformAction(event.source) and gVFSCloseCombatUpgraded.value == true then
        constants.enemiesInReactionList[event.source].action = "BowSwingLaunchTry"
        constants.enemiesInReactionList[event.source].response = "NoResponse"
        constants.enemiesInReactionList[event.source].perform = true
      end
    end
  elseif DL.CheckCreatureContext(event.broadcastContext, "UNREGISTER_REACTION") and constants.enemiesInReactionList[event.source] ~= nil then
    constants.enemiesInReactionList[event.source] = nil
  end
end
function CombatSteady:OnBrainInit(ai, global, constants)
  self.genericTimer = 0
  self.steadyTimer = 1.75
  self.steadyWeightInflation = 0.3
  self.steadyWeightInflationMaxValue = 0.3
  self.minDistanceToMove = 2.25
  self.currentLocationWeightLimit = 0.6
  constants.enemiesInReactionList = {}
end
function CombatSteady:Enter(ai, global, constants)
  self.genericTimer = 0
  self.steadyTimer = 15
  self.steadyWeightInflation = self.steadyWeightInflationMaxValue
  AddBehaviorStack(ai, global, constants, _G.constants.CombatSteady)
  game.Audio.PlayBanterNonCritical("Son Behavior Steady")
  global.combatnav_StayPut = true
  constants.motionType = "none"
  constants.strafe = false
end
function CombatSteady:Update(ai, global, constants)
  constants.InSurvivalDriver.Value = 0
  if ShouldPanic(ai, global, constants) then
    return
  end
  local responseAction, closeCombatAction, target = ReturnResponse(ai, global, constants, false)
  if (responseAction ~= "NoResponse" or closeCombatAction ~= "NoAction") and ai:HasMarker("DoNotDodge") == false and ai:HasMarker("DoNotInterrupt") == false then
    constants.GO_dodgeObject = target
    global.responseAction = responseAction
    constants.closeCombatAction = closeCombatAction
    statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
    ChangeState(ai, global, constants, _G.constants.CloseCombat)
    return
  end
  self.genericTimer = self.genericTimer + ai:GetFrameTime()
  local distToHero = ai.WorldPosition:Distance(global.Player.WorldPosition)
  local tooCloseToHero = distToHero < 2
  local locFarAway = false
  local dist = 0
  if constants.fightPositionCurrent ~= nil then
    dist = (ai.WorldPosition - constants.fightPositionNew):Length()
    if 5 < dist then
      locFarAway = true
    end
  end
  local locomotionInfo = ai:GetLocomotionInfo()
  if locomotionInfo.PathDestination ~= nil and constants.fightPositionNew ~= nil and locFarAway == false then
    dist = (locomotionInfo.PathDestination - constants.fightPositionNew):Length()
    if 10 < dist then
      locFarAway = true
    end
  end
  if not constants.tags.InCloseCombat and (constants.onCamera == false or self.genericTimer >= self.steadyTimer or global.theaterValues.CurrentValue + self.steadyWeightInflation < self.currentLocationWeightLimit or tooCloseToHero or locFarAway) then
    constants.delayTheater = false
    if constants.fightPositionCurrent ~= nil or 15 < distToHero and ai.OwnedPOI == nil then
      if 6 < dist or 15 < distToHero then
        ChangeState(ai, global, constants, _G.constants.GenericReposition)
        self.behaviorChanged = true
        self.genericTimer = 0
        global.nearDestination = false
        constants.strafe = false
        constants.fightPositionCurrent = constants.fightPositionNew
        ResetPath(true)
      elseif ai:IsInNavigationMove() and ai.OwnedPOI == nil and 1.5 < dist then
        global.nearDestination = false
        global.smallRepositionLoc = constants.fightPositionNew
        self.genericTimer = 0
        self.steadyWeightInflation = self.steadyWeightInflationMaxValue
        constants.fightPositionCurrent = constants.fightPositionNew
        constants.strafe = true
        ChangeState(ai, global, constants, _G.constants.MinorReposition)
        ResetPath(true)
      end
    end
    return
  end
  self.steadyWeightInflation = self.steadyWeightInflation - ai:GetFrameTime() * 0.1818
end
function CombatSteady:Exit(ai, global, constants)
end
function MinorReposition:OnBrainInit(ai, global, constants)
  constants.motionType = "none"
  self.resetPathTimer = 0.25
  self.resetPathTimerMax = 0.25
  self.offscreenResetPathTimer = 0
  self.offscreenResetPathTimerMax = 0.15
  constants.doNotUsePath = false
end
function MinorReposition:Enter(ai, global, constants)
  global.combatnav_StayPut = false
  AddBehaviorStack(ai, global, constants, _G.constants.MinorReposition)
  ResetPath(true)
  global.nearDestination = false
  constants.fightPositionCurrent = constants.fightPositionNew
  constants.strafe = true
  game.Audio.PlayBanterNonCritical("CBT_Son_MinorReposition")
  constants.doNotUsePath = true
end
function MinorReposition:Update(ai, global, constants)
  constants.InSurvivalDriver.Value = 0
  local responseAction, closeCombatAction, target = ReturnResponse(ai, global, constants, false)
  if (responseAction ~= "NoResponse" or closeCombatAction ~= "NoAction") and ai:HasMarker("DoNotDodge") == false and ai:HasMarker("DoNotInterrupt") == false then
    constants.GO_dodgeObject = target
    global.responseAction = responseAction
    constants.closeCombatAction = closeCombatAction
    statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
    ChangeState(ai, global, constants, _G.constants.CloseCombat)
    return
  end
  local destinationTooLarge = false
  local locomotionInfo = ai:GetLocomotionInfo()
  if locomotionInfo.PathDestination ~= nil and constants.fightPositionNew ~= nil and (locomotionInfo.PathDestination - constants.fightPositionNew):Length() > 10 then
    destinationTooLarge = true
  end
  if locomotionInfo.PathLength < 0.5 or destinationTooLarge then
    ChangeState(ai, global, constants, _G.constants.CombatSteady)
    return
  end
  if 0 >= self.offscreenResetPathTimer then
    if constants.onCamera == false then
      ResetPath(false)
    end
    self.offscreenResetPathTimer = self.offscreenResetPathTimerMax
  end
  local unitTime = ai:GetUnitTime()
  self.resetPathTimer = self.resetPathTimer - unitTime
  self.offscreenResetPathTimer = self.offscreenResetPathTimer - unitTime
end
function MinorReposition:Exit(ai, global, constants)
  constants.strafe = false
  constants.aggressiveFlank = false
  constants.doNotUsePath = false
end
function AggressiveFlank:OnBrainInit(ai, global, constants)
  constants.motionType = "none"
  self.resetPathTimer = 0.25
  self.resetPathTimerMax = 0.25
  self.aggressiveTime = 0
  self.aggressiveTimeMax = 1000
  constants.aggressiveFlank = false
end
function AggressiveFlank:Enter(ai, global, constants)
  global.combatnav_StayPut = false
  AddBehaviorStack(ai, global, constants, _G.constants.AggressiveFlank)
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  global.nearDestination = false
  constants.fightPositionCurrent = constants.fightPositionNew
  self.aggressiveTime = self.aggressiveTimeMax
  constants.aggressiveFlank = true
end
function AggressiveFlank:Update(ai, global, constants)
  constants.InSurvivalDriver.Value = 0
  constants.strafe = true
  local responseAction, closeCombatAction, target = ReturnResponse(ai, global, constants, false)
  if responseAction ~= "NoResponse" or closeCombatAction ~= "NoAction" then
    constants.GO_dodgeObject = target
    global.responseAction = responseAction
    constants.closeCombatAction = closeCombatAction
    statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
    ChangeState(ai, global, constants, _G.constants.CloseCombat)
    return
  end
  if 0 >= self.resetPathTimer then
    ResetPath(false)
    global.nearDestination = false
    constants.fightPositionCurrent = constants.fightPositionNew
    self.resetPathTimer = self.resetPathTimerMax
  end
  if 0 >= self.aggressiveTime then
    ChangeState(ai, global, constants, _G.constants.CombatSteady)
    return
  end
  local unitTime = ai:GetUnitTime()
  self.resetPathTimer = self.resetPathTimer - unitTime
  self.aggressiveTime = self.aggressiveTime - unitTime
end
function AggressiveFlank:Exit(ai, global, constants)
  constants.strafe = false
  constants.aggressiveFlank = false
end
function GenericReposition:OnBrainInit(ai, global, constants)
  constants.motionType = "none"
  self.pathResetTimer = 0
  self.pathResetTimerMax = 0.5
  self.pathRealignmentTimer = 0
  self.pathRealignmentTimerMax = 0.75
  self.urgentRepositionWeightInflation = 0.2
  self.urgentRepositionWeightInflationMaxValue = 0.2
  self.urgentRepositionWeightLimit = 0.65
  self.urgentRepositionToSurvivalWeightLimit = 0.25
  self.finalLocationWeightLimit = 0.7
  constants.motionType = "none"
  self.offscreenResetPathTimer = 0
  self.offscreenResetPathTimerMax = 0.15
  constants.resetPathDelay = 0
  self.delayCounter = 5
end
function GenericReposition:Enter(ai, global, constants)
  global.combatnav_StayPut = false
  self.pathResetTimer = self.pathResetTimerMax
  AddBehaviorStack(ai, global, constants, _G.constants.GenericReposition)
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  global.nearDestination = false
  constants.fightPositionCurrent = constants.fightPositionNew
  self.delayCounter = 5
  game.Audio.PlayBanterNonCritical("CBT_Son_GenericReposition")
end
function GenericReposition:Update(ai, global, constants)
  constants.InSurvivalDriver.Value = 0
  self.delayCounter = self.delayCounter - 1
  local responseAction, closeCombatAction, target = ReturnResponse(ai, global, constants, false)
  if (responseAction ~= "NoResponse" or closeCombatAction ~= "NoAction") and ai:HasMarker("DoNotDodge") == false and ai:HasMarker("DoNotInterrupt") == false then
    constants.GO_dodgeObject = target
    global.responseAction = responseAction
    constants.closeCombatAction = closeCombatAction
    statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
    ChangeState(ai, global, constants, _G.constants.CloseCombat)
    return
  end
  if ShouldPanic(ai, global, constants) then
    return
  end
  local locomotionInfo = ai:GetLocomotionInfo()
  if 0 >= self.delayCounter and (locomotionInfo.PathLength < 0.5 or IsBlocked(ai, global, constants)) then
    ChangeState(ai, global, constants, _G.constants.CombatSteady)
    return
  end
  if global.nearDestination == true or 0 >= self.pathResetTimer or constants.onCamera == false then
    if constants.fightPositionCurrent ~= nil then
      local currentFightPosVisible = DL.PositionVisibleOnCamera(constants.fightPositionCurrent, 0)
      if (constants.fightPositionCurrent - constants.fightPositionNew):Length() > 5 or constants.onCamera == false and currentFightPosVisible == false then
        ResetPath(false)
        global.nearDestination = false
        constants.fightPositionCurrent = constants.fightPositionNew
        self.pathResetTimer = self.pathResetTimerMax
      end
      if global.theaterValues.FinalValue ~= nil and global.theaterValues.FinalValue < self.finalLocationWeightLimit then
        ResetPath(false)
        global.nearDestination = false
        constants.fightPositionCurrent = constants.fightPositionNew
        self.pathResetTimer = self.pathResetTimerMax
      end
    elseif 0 >= self.pathResetTimer and (global.theaterValues.CurrentValue + self.urgentRepositionWeightInflation < self.urgentRepositionWeightLimit or global.theaterValues.FinalValue ~= nil and global.theaterValues.FinalValue < self.finalLocationWeightLimit) and not ai:HasMarker("DoNotChangePath") then
      ResetPath(false)
      global.nearDestination = false
      self.pathResetTimer = 2.5
    end
  end
  if 0 >= self.pathRealignmentTimer then
    if locomotionInfo.PathDestination ~= nil and (constants.fightPositionNew ~= nil and 5 < (locomotionInfo.PathDestination - constants.fightPositionNew):Length() or 2.5 > (locomotionInfo.PathDestination - global.Player.WorldPosition):Length()) or 2.5 > (global.Player.WorldPosition - ai.WorldPosition):Length() then
      ResetPath(false)
    end
    self.pathRealignmentTimer = self.pathRealignmentTimerMax
  end
  if 0 >= self.offscreenResetPathTimer then
    if constants.onCamera == false then
      ResetPath(false)
    end
    self.offscreenResetPathTimer = self.offscreenResetPathTimerMax
  end
  self.pathResetTimer = self.pathResetTimer - ai:GetUnitTime()
  self.pathRealignmentTimer = self.pathRealignmentTimer - ai:GetUnitTime()
  self.offscreenResetPathTimer = self.offscreenResetPathTimer - ai:GetUnitTime()
  self.urgentRepositionWeightInflation = self.urgentRepositionWeightInflation - ai:GetFrameTime() * 0.075
  if 0 > self.urgentRepositionWeightInflation then
    self.urgentRepositionWeightInflation = 0
  end
end
function GenericReposition:Exit(ai, global, constants)
end
function EvaluateSurvivalReposition:OnBrainInit(ai, global, constants)
  self.timer = 0
  constants.evaluateSurvivalReposition = false
end
function EvaluateSurvivalReposition:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.EvaluateSurvivalReposition)
  constants.behaviorTransitionType = "PanicEnter"
  statemachine.DispatchGlobalEvent("StartBehaviorTransition", ai, global, constants, "PANICNOW")
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  self.timer = 0.4
  constants.evaluateSurvivalReposition = true
end
function EvaluateSurvivalReposition:Update(ai, global, constants)
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  self.timer = self.timer - ai:GetUnitTime()
  if self.timer <= 0 then
    ChangeState(ai, global, constants, _G.constants.SurvivalReposition)
  end
end
function EvaluateSurvivalReposition:Exit(ai, global, constants)
  constants.evaluateSurvivalReposition = false
end
function SurvivalReposition:OnBrainInit(ai, global, constants)
  self.survivalRepositionWeightInflation = 0.5
  self.survivalRepositionWeightInflationMaxValue = 0.25
  self.survivalRepositionWeightLimit = 0.15
  self.finalLocationWeightLimit = 0.35
  self.refreshPathTimer = 0
  self.refreshPathTimerMax = 3
  self.timeout = 5
end
function SurvivalReposition:Enter(ai, global, constants)
  global.combatnav_StayPut = false
  self.timeout = 5
  self.refreshPathTimer = self.refreshPathTimerMax
  statemachine.DispatchGlobalEvent("ResetTheaterPath")
  AddBehaviorStack(ai, global, constants, _G.constants.SurvivalReposition)
  self.survivalRepositionWeightInflation = self.survivalRepositionWeightInflationMaxValue
  game.Audio.PlayBanterNonCritical("Son Behavior Survival Reposition")
  constants.motionType = "none"
  constants.behaviorTransitionType = "Panic"
  statemachine.DispatchGlobalEvent("StartBehaviorTransition", ai, global, constants, "PANICNOW")
  self.survivalRepositionWeightInflation = self.survivalRepositionWeightInflationMaxValue
  constants.InSurvivalDriver.Value = 1
  game.Audio.PlayBanterNonCritical("CBT_Son_SurvivalReposition")
end
function SurvivalReposition:Update(ai, global, constants)
  if self.refreshPathTimer <= 0 and (global.theaterValues.CurrentValue ~= nil and global.theaterValues.CurrentValue + self.survivalRepositionWeightInflation < self.survivalRepositionWeightLimit or global.theaterValues.FinalValue ~= nil and global.theaterValues.FinalValue < self.finalLocationWeightLimit) then
    ResetPath(false)
    self.survivalRepositionWeightInflation = self.survivalRepositionWeightInflationMaxValue
    self.refreshPathTimer = self.refreshPathTimerMax
    return
  end
  local locomotionInfo = ai:GetLocomotionInfo()
  if 0 >= self.timeout or (locomotionInfo.PathLength < 0.5 or IsBlocked(ai, global, constants)) and ShouldPanicNoAction(ai, global, constants) == false and ai:HasMarker("InSurvivalReposition") == false then
    ChangeState(ai, global, constants, _G.constants.CombatSteady)
    return
  end
  self.survivalRepositionWeightInflation = self.survivalRepositionWeightInflation - ai:GetFrameTime() * 0.1
  if 0 > self.survivalRepositionWeightInflation then
    self.survivalRepositionWeightInflation = 0
  end
  self.timeout = self.timeout - ai:GetFrameTime()
end
function SurvivalReposition:Exit(ai, global, constants)
end
function CloseCombat:OnBrainInit(ai, global, constants)
  self.FollowUpResponded = false
end
function CloseCombat:Enter(ai, global, constants)
  global.combatnav_StayPut = false
  if constants.closeCombatAction == "SmallReposition" then
    global.combatnav_StayPut = true
  end
  AddBehaviorStack(ai, global, constants, _G.constants.CloseCombat)
  game.Audio.PlayBanterNonCritical("Son Behavior Close Combat")
  self.FollowUpResponded = false
end
function CloseCombat:Update(ai, global, constants)
  constants.InSurvivalDriver.Value = 0
  if self.FollowUpResponded == false then
    self.FollowUpResponded = FollowUpResponse(ai, global, constants, false)
  end
  if global.closeCombatStart == false and global.bDodging == false then
    if constants.urgentMovement then
      ChangeState(ai, global, constants, _G.constants.GenericReposition)
    else
      ChangeState(ai, global, constants, _G.constants.CombatSteady)
    end
    return
  end
end
function CloseCombat:Exit(ai, global, constants)
end
function PursueTarget:OnBrainInit(ai, global, constants)
  self.cautiousRepositionWeightInflation = 0.5
  self.cautiousRepositionWeightInflationMaxValue = 0.5
  self.cautiousRepositionWeightLimit = 0.25
end
function PursueTarget:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, "Pursue")
  global.combatnav_StayPut = false
end
function PursueTarget:Update(ai, global, constants)
  local locomotionInfo = ai:GetLocomotionInfo()
  if locomotionInfo.PathLength < 0.5 then
    ChangeState(ai, global, constants, _G.constants.CombatSteady)
    return
  end
end
function PursueTarget:Exit(ai, global, constants)
end
function UseForcedPath:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, "UseForcedPath")
  constants.motionType = "UseForcedPath"
end
function UseForcedPath:Update(ai, global, constants)
  if not global.UseForcedPath then
    if ai:GetNavCurve() then
      ChangeState(ai, global, constants, _G.constants.FollowPlayer)
    else
      ChangeState(ai, global, constants, _G.constants.Steady)
    end
  end
end
function UseForcedPath:Exit(ai, global, constants)
end
function Steady:OnBrainInit(ai, global, constants)
  self.moveAwayTimer = 0
  self.moveAwayTimerMax = 2
  self.backToSteadyTimer = 0
  self.doNotReset = false
end
function Steady:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.Steady)
  constants.motionType = "Idle"
  constants.intersectionRadius = 1.25
  if self.doNotReset == false then
    self.backToSteadyTimer = 0
  else
    self.doNotReset = false
  end
end
function Steady:Update(ai, global, constants)
  self.backToSteadyTimer = self.backToSteadyTimer + ai:GetFrameTime()
  local curve
  if ai.GetNavCurve then
    curve = ai:GetNavCurve()
  else
    curve = ai:GetActuator().Curve
  end
  if _G.gVFSDebugNoBrainLogic.value == true or ai:HasMarker("TweakLogicOnly") then
    return
  end
  if global.UseForcedPath then
    ChangeState(ai, global, constants, "UseForcedPath")
    return
  end
  if IsBumpingIntoHero(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.UrgentMoveAway)
    return
  end
  if curve ~= nil or constants.distanceToHero > 7 and self.backToSteadyTimer > 2 then
    ChangeState(ai, global, constants, _G.constants.FollowPlayer)
    return
  end
end
function Steady:Exit(ai, global, constants)
end
function PostPOISteady:OnBrainInit(ai, global, constants)
  self.moveAwayTimer = 0
  self.moveAwayTimerMax = 2
  self.backToSteadyTimer = 0
  self.doNotReset = false
end
function PostPOISteady:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.PostPOISteady)
  constants.motionType = "Idle"
  self.backToSteadyTimer = 0
end
function PostPOISteady:Update(ai, global, constants)
  self.backToSteadyTimer = self.backToSteadyTimer + ai:GetFrameTime()
  if self.backToSteadyTimer > 0.5 or global.POIInfo.useThisPOI == nil then
    ChangeState(ai, global, constants, _G.constants.Steady)
  end
end
function PostPOISteady:Exit(ai, global, constants)
end
function PostPOISteady.Events:OnDisengagePOI(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil and global.POIInfo.useThisPOI:FindLuaTableAttribute("SteadyForPOITransition") then
    constants.awarenessState = _G.constants.PostPOISteady
  else
    constants.awarenessState = _G.constants.FollowPlayer
  end
end
function FollowPlayer:OnBrainInit(ai, global, constants)
  constants.focusDuration = 0
end
function FollowPlayer:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.FollowPlayer)
  constants.motionType = "UseFollowPoint"
  constants.intersectionRadius = 1.1
end
function FollowPlayer:Update(ai, global, constants)
  local curve
  if ai.GetNavCurve then
    curve = ai:GetNavCurve()
  else
    curve = ai:GetActuator().Curve
  end
  if global.UseForcedPath then
    ChangeState(ai, global, constants, "UseForcedPath")
    return
  end
  if ShouldUrgentMoveAway(ai, global, constants) or IsBumpingIntoHero(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.UrgentMoveAway)
    return
  end
  if ShouldMoveAway(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.NoticeHero)
    return
  end
  if constants.distanceToHero < 7 and curve == nil then
    ChangeState(ai, global, constants, _G.constants.Steady)
    return
  end
end
function FollowPlayer:Exit(ai, global, constants)
end
function InchForward:OnBrainInit(ai, global, constants)
end
function InchForward:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.InchForward)
  constants.intersectionRadius = 2
  constants.motionType = "UseFollowPoint"
end
function InchForward:Update(ai, global, constants)
  if constants.heroSpeed > 0.5 then
    ChangeState(ai, global, constants, _G.constants.Steady)
    return
  elseif IsBumpingIntoHero(ai, global, constants) or ShouldMoveAway(ai, global, constants) then
    constants.motionType = "Idle"
  end
  if ShouldUrgentMoveAway(ai, global, constants) or IsBumpingIntoHero(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.UrgentMoveAway)
    return
  end
  if ShouldMoveAway(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.NoticeHero)
    return
  end
  if ai.GetPathDestination then
    constants.motionType = "UseFollowPoint"
    local path_destination = ai:GetPathDestination()
    if 0.5 >= (path_destination - ai.WorldPosition):Length() or constants.distanceToHero > 10 then
      ChangeState(ai, global, constants, _G.constants.Steady)
      return
    end
  end
end
function InchForward:Exit(ai, global, constants)
end
function Wait:OnBrainInit(ai, global, constants)
end
function Wait:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.Wait)
  constants.motionType = "Idle"
  constants.forcedBehavior = true
end
function Wait:Update(ai, global, constants)
  if constants.distanceToHero > constants.disengageDistance_Scripted then
    ChangeState(ai, global, constants, _G.constants.FollowPlayer)
    return
  end
  if engine.IsDebug() then
    engine.DrawFillSphere(ai:GetWorldPosition() + ai:GetWorldUp() * 1.3, 0.085, 16711680)
  end
end
function Wait:Exit(ai, global, constants)
  ai:TriggerMoveEvent("kLE_Disengage")
  constants.disengageDistance_Scripted = 15
  constants.forcedBehavior = false
  constants.allowedPOIAction = "NoAttack"
end
function NoticeHero:OnBrainInit(ai, global, constants)
end
function NoticeHero:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.NoticeHero)
  constants.motionType = "GoToPoint"
  helper.FocusKratos(ai, global, constants, 4)
end
function NoticeHero:Update(ai, global, constants)
  local dx = ai.WorldPosition.x - global.Player.WorldPosition.x
  local dz = ai.WorldPosition.z - global.Player.WorldPosition.z
  local perpVector1 = engine.Vector.New(-dz, 0, dx):Normalized()
  local perpVector2 = engine.Vector.New(dz, 0, -dx):Normalized()
  local vectorTable = {perpVector2, perpVector1}
  local desiredDirectionDelta = DL.FrontAngleFromPoint(global.Player, ai.WorldPosition, global.Player:GetVelocity():Normalized())
  if 0 <= desiredDirectionDelta and desiredDirectionDelta <= 180 then
    vectorTable = {perpVector1, perpVector2}
  end
  local potentialWalkLocation = game.NavMesh.ClosestPoint(ai.WorldPosition + 3 * vectorTable[1])
  if potentialWalkLocation ~= nil and (ai.WorldPosition - potentialWalkLocation):Length() < 2 then
    potentialWalkLocation = nil
  end
  if potentialWalkLocation == nil then
    local pervVector2 = engine.Vector.New(dz, 0, -dx):Normalized()
    potentialWalkLocation = game.NavMesh.ClosestPoint(ai.WorldPosition + 3 * vectorTable[2])
    if potentialWalkLocation ~= nil and (ai.WorldPosition - potentialWalkLocation):Length() < 2 then
      potentialWalkLocation = nil
    end
  end
  if potentialWalkLocation ~= nil then
    constants.moveToPointActuator.Destination = potentialWalkLocation
    constants.moveToPointActuator.Speed = 4
    constants.moveToPointActuator.StopDistance = 0.5
    ChangeState(ai, global, constants, _G.constants.MoveAway)
    return
  else
    ChangeState(ai, global, constants, _G.constants.UrgentMoveAway)
    return
  end
end
function NoticeHero:Exit(ai, global, constants)
end
function UrgentMoveAway:OnBrainInit(ai, global, constants)
end
function UrgentMoveAway:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.UrgentMoveAway)
  constants.motionType = "Idle"
  constants.GO_dodgeObject = global.Player
  global.responseAction = "DodgeHero"
  constants.closeCombatAction = "NoAction"
  global.dodgeCurrentQuadrant = constants.currentIntersectionQuadrant
  global.dodgeFutureQuadrant = constants.futureIntersectionQuadrant
  statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
end
function UrgentMoveAway:Update(ai, global, constants)
  if ai:IsDoingSyncMove() then
    ChangeState(ai, global, constants, _G.constants.Steady)
    return
  end
  if global.bDodgeNow == false then
    if constants.distanceToHero > 3 or ai:GetNavCurve() ~= nil and global.leadTheWayParams.Role == "relaxedFollower" then
      ChangeState(ai, global, constants, _G.constants.Steady)
      return
    else
      ChangeState(ai, global, constants, _G.constants.NoticeHero)
      return
    end
  end
end
function UrgentMoveAway:Exit(ai, global, constants)
end
function GoToPoint:OnBrainInit(ai, global, constants)
end
function GoToPoint:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.GoToPoint)
  constants.motionType = "GoToPoint"
end
function GoToPoint:Update(ai, global, constants)
  if IsBumpingIntoHero(ai, global, constants) or IsHeroCrossingPath(ai, global, constants, 3) then
    ChangeState(ai, global, constants, _G.constants.ObserveBeforeMoving)
    return
  end
  if engine.IsDebug() then
    engine.DrawFillSphere(ai:GetWorldPosition() + ai:GetWorldUp() * 1.3, 0.085, 16711680)
  end
end
function GoToPoint:Exit(ai, global, constants)
end
function ForcedGoToPoint:OnBrainInit(ai, global, constants)
end
function ForcedGoToPoint:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.ForcedGoToPoint)
  constants.motionType = "GoToPoint"
  ai:ModifyCreatureMotion({MovementPriority = 1000})
  constants.forcedBehavior = true
end
function ForcedGoToPoint:Update(ai, global, constants)
  if engine.IsDebug() then
    engine.DrawFillSphere(ai:GetWorldPosition() + ai:GetWorldUp() * 1.3, 0.085, 16711680)
  end
end
function ForcedGoToPoint:Exit(ai, global, constants)
  constants.forcedBehavior = false
  constants.allowedPOIAction = "NoAttack"
  ai:ModifyCreatureMotion({})
end
function ObserveBeforeMoving:OnBrainInit(ai, global, constants)
end
function ObserveBeforeMoving:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.ObserveBeforeMoving)
  constants.motionType = "Idle"
  ai:ModifyCreatureMotion({})
end
function ObserveBeforeMoving:Update(ai, global, constants)
  if not IsHeroCrossingPath(ai, global, constants, 3) and not IsBumpingIntoHero(ai, global, constants) then
    ChangeState(ai, global, constants, constants.previousAwarenessState)
    return
  end
  if ShouldUrgentMoveAwayAggressivePOI(ai, global, constants) then
    ChangeState(ai, global, constants, _G.constants.ObservedUrgentMoveAway)
    return
  end
end
function ObserveBeforeMoving:Exit(ai, global, constants)
end
function ObservedUrgentMoveAway:OnBrainInit(ai, global, constants)
end
function ObservedUrgentMoveAway:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.ObservedUrgentMoveAway)
  constants.motionType = "Idle"
  ai:ModifyCreatureMotion({})
  constants.GO_dodgeObject = global.Player
  global.responseAction = "DodgeHero"
  constants.closeCombatAction = "NoAction"
  statemachine.DispatchGlobalEvent("RespondNow", ai, global, constants)
end
function ObservedUrgentMoveAway:Update(ai, global, constants)
  if global.bDodgeNow == false then
    ChangeState(ai, global, constants, _G.constants.ObserveBeforeMoving)
    return
  end
end
function ObservedUrgentMoveAway:Exit(ai, global, constants)
end
function MoveAway:OnBrainInit(ai, global, constants)
  self.moveAwayTimer = 0
  self.moveAwayTimerMax = 0.5
  self.backToSteadyTimer = 0
  self.doNotReset = false
end
function MoveAway:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, _G.constants.MoveAway)
  constants.motionType = "GoToPoint"
  ai:ModifyCreatureMotion({})
  constants.intersectionRadius = 1.2
  if self.doNotReset == false then
    self.moveAwayTimer = 0
  else
    self.doNotReset = false
  end
end
function MoveAway:Update(ai, global, constants)
  self.moveAwayTimer = self.moveAwayTimer + ai:GetFrameTime()
  if IsBumpingIntoHero(ai, global, constants) or ShouldMoveAwayAggressive(ai, global, constants) and self.moveAwayTimer > self.moveAwayTimerMax or constants.distanceToHero < 1 then
    ChangeState(ai, global, constants, _G.constants.NoticeHero)
    return
  end
  if (constants.moveToPointActuator.Destination - ai.WorldPosition):Length() <= 0.5 or constants.distanceToHero > 10 then
    ChangeState(ai, global, constants, _G.constants.Steady)
    return
  end
end
function MoveAway:Exit(ai, global, constants)
end
function POI:OnBrainInit(ai, global, constants)
end
function POI:Enter(ai, global, constants)
  AddBehaviorStack(ai, global, constants, "POI")
end
function POI:Update(ai, global, constants)
end
function POI:Exit(ai, global, constants)
end
function ShouldPanic(ai, global, constants)
  if not ai:HasMarker("DoNotDodge") and ai.OwnedPOI == nil and constants.onCamera == true then
    local enemies = helper.FindLivingEnemiesExludeMarked(ai, 2.5, "DoNotEvaluate")
    if 3 < #enemies then
      constants.GO_dodgeObject = enemies[1]
      ChangeState(ai, global, constants, _G.constants.EvaluateSurvivalReposition)
      statemachine.DispatchGlobalEvent("ResetTheaterPath")
      return true
    end
  end
  return false
end
function ShouldPanicNoAction(ai, global, constants)
  if not ai:HasMarker("DoNotDodge") and ai.OwnedPOI == nil and constants.onCamera == true then
    local enemies = helper.FindLivingEnemiesExludeMarked(ai, 2.5, "DoNotEvaluate")
    if 3 < #enemies then
      return true
    end
  end
  return false
end
function ChangeState(ai, global, constants, awarenessState)
  constants.previousAwarenessState = constants.awarenessState
  constants.awarenessState = awarenessState
end
function UpdateBehaviorStack(ai, global, constants)
  local sizeofarray = #constants.behaviorStack
  if 0 < sizeofarray then
    constants.behaviorStack[sizeofarray].duration = constants.behaviorStack[sizeofarray].duration + ai:GetFrameTime()
  end
end
function AddBehaviorStack(ai, global, constants, newBehavior)
  if constants.behaviorStack == nil then
    return
  end
  if #constants.behaviorStack > 10 then
    table.remove(constants.behaviorStack, 1)
  end
  table.insert(constants.behaviorStack, {behavior = newBehavior, duration = 0})
end
function UpdateBB(ai, global, constants)
  local bb = ai:GetPrivateBlackboard()
  if bb ~= nil then
    bb:Set("IsInSurvival", constants.awarenessState == _G.constants.SurvivalReposition or constants.awarenessState == _G.constants.EvaluateSurvivalReposition)
  end
end
function SetRelaxedBehavior(ai, behaviorname, arg1)
  _G.constants.disengageDistance_Scripted = arg1
  _G.constants.awarenessState = behaviorname
end
function SetDisengageDistance(ai, arg1)
  _G.constants.disengageDistance_Scripted = arg1
end
function ShouldMoveAway(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge > 0.3 and constants.timeToDodge < 2 and constants.heroSpeed > 1.5 and CanRespond(ai, global, constants)
end
function ShouldUrgentMoveAway(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge >= 0 and constants.timeToDodge <= 0.3 and constants.heroSpeed > 1 and CanRespond(ai, global, constants)
end
function ShouldMoveAwayAggressive(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge >= 0 and constants.timeToDodge < 2 and constants.heroSpeed > 1 and CanRespond(ai, global, constants)
end
function ShouldUrgentMoveAwayAggressive(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge >= 0 and constants.timeToDodge <= 0.01 and constants.heroSpeed > 1 and CanRespond(ai, global, constants)
end
function ShouldUrgentMoveAwayAggressivePOI(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge >= 0 and constants.timeToDodge <= 0.01 and constants.heroSpeed > 1
end
function IsBumpingIntoHero(ai, global, constants)
  return constants.timeToDodge ~= nil and constants.timeToDodge >= 0 and constants.timeToDodge <= 0.01 and constants.heroSpeed > 1 and CanRespond(ai, global, constants)
end
function IsHeroCrossingPath(ai, global, constants, distanceInfront)
  local aiPos = ai.WorldPosition
  local targetPos = ai.WorldPosition
  if constants.motionType == "UseFollowPoint" then
    targetPos = constants.followPointPosition
  else
    targetPos = constants.moveToPointActuator.Destination
  end
  if distanceInfront < constants.distanceToHero then
    return false
  end
  local playerPos = global.Player.WorldPosition
  local playerPrediction = global.Player:GetVelocity()
  if playerPrediction:Length() <= 0.5 then
    playerPrediction = global.Player:GetWorldForward() * 4
  end
  local playerEndPos = playerPos + playerPrediction
  local pathIntersect = DL.LinesIntersect(aiPos.x, aiPos.z, targetPos.x, targetPos.z, playerPos.x, playerPos.z, playerEndPos.x, playerEndPos.z)
  if pathIntersect == true then
    return true
  end
  local d = aiPos - targetPos
  local f = aiPos - playerPos
  local r = 1.5
  local a = d:Dot(d)
  local b = 2 * f:Dot(d)
  local c = f:Dot(f) - r * r
  local discriminant = b * b - 4 * a * c
  if 0 <= discriminant then
    return true
  end
  return false
end
function UpdateHeroState(ai, global, constants)
  constants.distanceToHero = (global.Player.WorldPosition - ai.WorldPosition):Length()
  constants.heroSpeed = global.Player:GetVelocity():Length()
  local collideWithHero = ai:FindCollisionCreatures(3, 0.1, true)
  if collideWithHero ~= nil and 0 < #collideWithHero then
    constants.currentIntersectionQuadrant, constants.timeToDodge, constants.futureIntersectionQuadrant = collideWithHero[1].CurrentQuadrant, 0.005, collideWithHero[1].FutureQuadrant
  else
    constants.timeToDodge = -1
  end
end
function IsBlocked(ai, global, constants)
  if not ai:IsInNavigationMove() then
    return false
  end
  local currentVelocity = ai:GetVelocity():Length()
  if 0.5 < currentVelocity then
    constants.isPotentiallyStuck = false
    constants.isPotentiallyStuckTimer = 0
  else
    local ailoco = ai:GetLocomotionInfo()
    if 0 < ailoco.NextSpeed then
      constants.isPotentiallyStuckTimer = constants.isPotentiallyStuckTimer + ai:GetFrameTime()
      if constants.isPotentiallyStuckTimer >= constants.isPotentiallyStuckTimerMax then
        constants.isPotentiallyStuckTimer = 0
        return true
      end
    end
  end
  return false
end
function ShouldPerformAction(thisEnemy)
  if thisEnemy:CheckDecision("tweak_Decision_OnCamera") == false then
    return false
  end
  local enemyBB = thisEnemy:GetBlackboard()
  if enemyBB == nil then
    return false
  end
  if enemyBB:Exists("ClassType") then
    local enemyValue = enemyBB:GetNumber("ClassType")
    local enemyType = DL.ReturnClassType(enemyValue)
    if enemyType == "eFodder" or enemyType == "dGrunts" or enemyType == "cMajorGrunts" or enemyType == "aBosses" then
      return true
    else
      return false
    end
  end
end
function ResetStates(ai, global, constants)
  global.InCombat_behavior = _G.constants.Steady
  InCombat.behaviorChanged = true
  PostCombat.behaviorChanged = true
  ai:TriggerMoveEvent("kLEStopBackPedal")
  for k in pairs(constants.enemiesInReactionList) do
    constants.enemiesInReactionList[k] = nil
  end
  global.bDodgeNow = false
end
function ReturnCreatureRadius(i)
  local enemyBB = i:GetBlackboard()
  local creatureRadius
  if enemyBB ~= nil and enemyBB:Exists("CreatureRadius") then
    creatureRadius = enemyBB:GetNumber("CreatureRadius")
  end
  return creatureRadius
end
function InDangerZone(ai, global, constants, i)
  local enemyBB = i:GetBlackboard()
  local coneLength = -1
  local coneAngle = -1
  local classType = -1
  local canCheckCone = true
  if enemyBB ~= nil and enemyBB:Exists("ConeLength") then
    coneLength = enemyBB:GetNumber("ConeLength")
  else
    canCheckCone = false
  end
  if enemyBB ~= nil and enemyBB:Exists("ConeAngle") then
    coneAngle = enemyBB:GetNumber("ConeAngle")
  else
    canCheckCone = false
  end
  if enemyBB ~= nil and enemyBB:Exists("ClassType") then
    classType = enemyBB:GetNumber("ClassType")
  else
    canCheckCone = false
  end
  if canCheckCone == true and 3 < classType then
    return game.AIUtil.IntersectPointCone(ai.WorldPosition, i.WorldPosition, i:GetWorldForward(), coneAngle, coneLength)
  end
  return false
end
function CanRespond(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil or ai.OwnedPOI then
    return false
  end
  if not constants.allowResponseBehavior then
    return false
  end
  local curve
  if ai.GetNavCurve then
    curve = ai:GetNavCurve()
  else
    curve = ai:GetActuator().Curve
  end
  if not ai:HasMarker("AllowDodgeOverride") and (not (not ai:IsDoingSyncMove() and (not ai:HasMarker("DoNotDodge") or ai:HasMarker("IgnoreDodge") ~= false) and not ai:HasMarker("SonCommand_WorldInteraction") and global.DodgeType == "none" and constants.tags.DodgeAllowed) or constants.tags.DoNotPerform or curve ~= nil and not global.leadTheWayParams.Role == "relaxedFollower" or DL.CheckCreatureContext(ai:GetContext(), "RUNESPELL") or DL.CheckCreatureContext(ai:GetContext(), "INCAPACITATE_AVAILABLE") or DL.CheckCreatureContext(ai:GetContext(), "SON_COOLDOWN") and ai:HasMarker("IgnoreCooldown") == false or ai:IsInVehicle() or global.bCommand_Action or ai:HasMarker("TweakLogicOnly")) then
    return false
  end
  return true
end
function CanFollowup(ai, global, constants)
  if global.POIInfo.useThisPOI ~= nil then
    return false
  end
  if constants.onCamera == false then
    return false
  end
  if not constants.allowResponseBehavior then
    return false
  end
  local curve
  if ai.GetNavCurve then
    curve = ai:GetNavCurve()
  else
    curve = ai:GetActuator().Curve
  end
  if not ai:HasMarker("AllowDodgeOverride") and (ai:IsDoingSyncMove() or ai:HasMarker("SonCommand_WorldInteraction") or curve ~= nil or DL.CheckCreatureContext(ai:GetContext(), "RUNESPELL") or DL.CheckCreatureContext(ai:GetContext(), "INCAPACITATE_AVAILABLE") or DL.CheckCreatureContext(ai:GetContext(), "SON_COOLDOWN") and ai:HasMarker("IgnoreCooldown") == false or ai:IsInVehicle() or ai:HasMarker("NoCloseCombatInterrupt") or global.bCommand_Action) then
    return false
  end
  return true
end
function ReturnResponse(ai, global, constants, ignoretarget)
  local responseAction = "NoResponse"
  local combatAction = "NoAction"
  if not CanRespond(ai, global, constants) then
    return responseAction, combatAction, nil
  end
  local enemyDodge = true
  local ignoreCombatTarget = ignoretarget
  local inCombat = false
  if global.bInCombat then
    inCombat = true
  end
  local collideWithHero = ai:FindCollisionCreatures(1.15, 0.25, true)
  if collideWithHero ~= nil and 0 < #collideWithHero and constants.onCamera then
    statemachine.DispatchGlobalEvent("ResetTheaterPath")
    return "DodgeHero", "NoAction", global.Player
  end
  local playerPos = global.Player.WorldPosition
  for index, enemyReactionInfo in pairs(constants.enemiesInReactionList) do
    if index == nil and enemyReactionInfo ~= nil then
      constants.enemiesInReactionList[index] = nil
      enemyReactionInfo = nil
    elseif enemyReactionInfo.perform and 0 >= game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") then
      local enemyLoc = index.WorldPosition
      if enemyReactionInfo.distCheck >= (ai.WorldPosition - enemyLoc):Length() and (enemyReactionInfo.playerDist == nil or enemyReactionInfo.playerDist ~= nil and (playerPos - enemyLoc):Length() < enemyReactionInfo.playerDist) then
        return enemyReactionInfo.response, enemyReactionInfo.action, index
      end
    end
  end
  if inCombat == true and enemyDodge then
    local enemiesNearby = ai:FindCollisionCreatures(5, 0.1)
    local sonTarget = constants.GO_closeCombatObject
    if enemiesNearby ~= nil and 0 < #enemiesNearby then
      for _, t in ipairs(enemiesNearby) do
        local i = t.Creature
        if i:PickupIsAcquired("Crawler_BurrowState") == false and (ignoreCombatTarget and sonTarget ~= i or ignoreCombatTarget == false) then
          local thisCreatureRadius = ReturnCreatureRadius(i)
          local stringID = DL.ReturnStringID(i)
          constants.potentialCurrentQuadrant, _, constants.potentialFutureQuadrant = t.CurrentQuadrant, 0.1, t.FutureQuadrant
          local dodgeEnemy = true
          local onScreen = i:CheckDecision("tweak_Decision_OnCamera")
          local isAttacking = i:HasMarker("Attacking")
          local angle = 0
          if isAttacking and onScreen and i:GetTargetCreature() == ai then
            if gVFSCloseCombatUpgraded.value == true then
              responseAction, combatAction = SelectResponse_EnemyTargetSon(ai, global, constants, isAttacking, angle, i, stringID)
            else
              responseAction, combatAction = SelectResponse_EnemyTargetSon_LowRank(ai, global, constants, isAttacking, angle, i)
            end
            if responseAction ~= "NoResponse" or combatAction ~= "NoAction" then
              return responseAction, combatAction, i
            end
          end
          if dodgeEnemy then
            responseAction, combatAction = SelectResponse_Generic(ai, global, constants, isAttacking, angle, i, stringID)
            if responseAction ~= "NoResponse" or combatAction ~= "NoAction" then
              return responseAction, combatAction, i
            end
          end
        end
      end
    end
  end
  return "NoResponse", "NoAction", nil
end
function FollowUpResponse(ai, global, constants, ignoretarget)
  local responseAction = "NoResponse"
  local combatAction = "NoAction"
  local enemyDodge = true
  local ignoreCombatTarget = ignoretarget
  if not CanFollowup(ai, global, constants) or constants.heroInRage == true then
    return false
  end
  local sonTarget = constants.GO_closeCombatObject
  for index, enemyReactionInfo in pairs(constants.enemiesInReactionList) do
    if ignoreCombatTarget and sonTarget ~= index or ignoreCombatTarget == false then
      if index == nil and enemyReactionInfo ~= nil then
        constants.enemiesInReactionList[index] = nil
        enemyReactionInfo = nil
      elseif enemyReactionInfo.perform and game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") <= 0 and enemyReactionInfo.distCheck >= (ai.WorldPosition - index.WorldPosition):Length() then
        if enemyReactionInfo.action == "GrabEnemy" then
          if (global.Player.WorldPosition - index.WorldPosition):Length() < 5 then
            statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "GrabEnemy", index)
            break
          end
        elseif enemyReactionInfo.action == "BowSwingLaunchTry" then
          statemachine.DispatchGlobalEvent("FollowUpNow", ai, global, constants, "Launch", index)
          break
        end
      end
    end
  end
  return false
end
function SelectResponse_Generic(ai, global, constants, enemyAttacking, enemyAngle, thisEnemy, stringID)
  if stringID == "modi00" then
    return "DodgeEnemy", "NoAction"
  end
  if thisEnemy:HasMarker("DoNotGrab") then
    return "DodgeEnemy", "NoAction"
  end
  if not ShouldPerformAction(thisEnemy) then
    return "NoResponse", "NoAction"
  end
  if thisEnemy:IsDoingSyncMove() then
    return "NoResponse", "NoAction"
  end
  if not constants.onCamera then
    return "NoResponse", "NoAction"
  end
  if thisEnemy:PickupIsAcquired("HealthThreshold") and thisEnemy:PickupGetStage("HealthThreshold") == 2 then
    return "DodgeEnemy", "NoAction"
  end
  local enemyInFront = -90 < enemyAngle and enemyAngle < 90
  local distance = (ai.WorldPosition - thisEnemy.WorldPosition):Length()
  local distanceToPlayer = (global.Player.WorldPosition - thisEnemy.WorldPosition):Length()
  if distanceToPlayer < 4 and distance < 2.5 then
    return "ResponseTurn", "EvadeShoot"
  end
  if enemyInFront then
    if distance < 2.5 then
      if constants.randomAttackInput == true then
        constants.randomAttackInput = false
        return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
      else
        constants.randomAttackInput = true
        return GetMeleeAbility(ai, global, constants, "BowCombo", stringID)
      end
    end
  elseif distance < 2.5 then
    return "ResponseTurn", "EvadeShoot"
  end
  return "NoResponse", "NoAction"
end
function SelectResponse_EnemyTargetSon_LowRank(ai, global, constants, enemyAttacking, enemyAngle, thisEnemy)
  if not ShouldPerformAction(thisEnemy) then
    return "NoResponse", "NoAction"
  end
  if not constants.onCamera then
    return "NoResponse", "NoAction"
  end
  if constants.urgentMovement then
    return "NoResponse", "NoAction"
  end
  if thisEnemy:HasMarker("DoNotGrab") then
    return "DodgeEnemy", "NoAction"
  end
  if thisEnemy:IsDoingSyncMove() then
    return "NoResponse", "NoAction"
  end
  local enemyInFront = -90 < enemyAngle and enemyAngle < 90
  local distance = (ai.WorldPosition - thisEnemy.WorldPosition):Length()
  if enemyAttacking then
    if distance < 6 then
      if enemyInFront then
        return "DodgeEnemy", "ShootArrow"
      else
        return "DodgeEnemy", "ShootArrow"
      end
    else
      return "NoResponse", "ShootArrow"
    end
  elseif constants.awarenessState == _G.constants.SurvivalReposition or constants.awarenessState == _G.constants.GenericReposition or constants.awarenessState == _G.constants.UrgentReposition then
    return "ResponseTurnRun", "NoAction"
  else
    return "ResponseTurnRun", "NoAction"
  end
end
function SelectResponse_EnemyTargetSon(ai, global, constants, enemyAttacking, enemyAngle, thisEnemy, stringID)
  if not ShouldPerformAction(thisEnemy) then
    return "NoResponse", "NoAction"
  end
  if not constants.onCamera then
    return "NoResponse", "NoAction"
  end
  if constants.urgentMovement or (global.Player.WorldPosition - ai.WorldPosition):Length() > 12 then
    return "NoResponse", "NoAction"
  end
  if thisEnemy:HasMarker("DoNotGrab") then
    return "DodgeEnemy", "NoAction"
  end
  if thisEnemy:IsDoingSyncMove() then
    return "NoResponse", "NoAction"
  end
  local enemyInFront = -90 < enemyAngle and enemyAngle < 90
  local distance = (ai.WorldPosition - thisEnemy.WorldPosition):Length()
  if constants.awarenessState == _G.constants.CombatSteady then
    if not constants.inLockDown and 4 < distance and distance < 8 then
      return "DodgeAndShoot", "NoAction"
    end
    if enemyInFront then
      if distance < 4 then
        if constants.randomAttackInput == true then
          constants.randomAttackInput = false
          return GetMeleeAbility(ai, global, constants, "BowCombo", stringID)
        else
          constants.randomAttackInput = true
          if math.random(0, 1) > 0.5 then
            return "NoResponse", "EvadeShoot"
          else
            return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
          end
        end
      elseif constants.randomAttackInput == true then
        constants.randomAttackInput = false
        return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
      else
        constants.randomAttackInput = true
        return "NoResponse", "EvadeShoot"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      return "ResponseTurn", "ShootArrow"
    end
  elseif constants.awarenessState == _G.constants.MinorReposition then
    if not constants.inLockDown and 4 < distance then
      return "DodgeAndShoot", "NoAction"
    end
    if enemyInFront then
      if enemyAttacking and distance < 5 then
        if constants.randomAttackInput == true then
          constants.randomAttackInput = false
          return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
        else
          constants.randomAttackInput = true
          return GetMeleeAbility(ai, global, constants, "BowCombo", stringID)
        end
      else
        return "NoResponse", "ShootArrow"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      else
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == _G.constants.GenericReposition or constants.awarenessState == _G.constants.UrgentReposition then
    if enemyInFront then
      if enemyAttacking and distance < 3 then
        if constants.randomAttackInput == true then
          constants.randomAttackInput = false
          return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
        else
          constants.randomAttackInput = true
          return GetMeleeAbility(ai, global, constants, "BowCombo", stringID)
        end
      else
        return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      elseif distance < 3 then
        if constants.randomAttackInput == true then
          constants.randomAttackInput = false
          return GetMeleeAbility(ai, global, constants, "BowCombo", stringID)
        else
          constants.randomAttackInput = true
          return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
        end
      elseif constants.randomAttackInput == true then
        constants.randomAttackInput = false
        return GetMeleeAbility(ai, global, constants, "KickOff", stringID)
      else
        constants.randomAttackInput = true
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == _G.constants.SurvivalReposition then
    if enemyInFront then
      if enemyAttacking then
        return "ResponseTurnRun", "NoAction"
      else
        return "ResponseTurnRun", "NoAction"
      end
    elseif enemyAttacking then
      return "NoResponse", "NoAction"
    else
      return "NoResponse", "NoAction"
    end
  elseif constants.awarenessState == "CautiousReposition" then
    if enemyInFront then
      if enemyAttacking then
        return "NoResponse", "EvadeShoot"
      else
        return "NoResponse", "ShootArrow"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      elseif math.random(0, 1) > 0.5 then
        return "NoResponse", "EvadeShoot"
      else
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == "Backpedal" then
    if enemyInFront then
      if enemyAttacking then
        return "ResponseTurnRun", "NoAction"
      else
        return "ResponseTurnRun", "NoAction"
      end
    elseif enemyAttacking then
      return "NoResponse", "NoAction"
    else
      return "NoResponse", "NoAction"
    end
  elseif constants.awarenessState == _G.constants.CloseCombat then
    return "ResponseTurn", "PanicDodge"
  end
  return "ResponseTurn", "NoAction"
end
function SelectResponse_InDangerZone(ai, global, constants, enemyAttacking, enemyAngle, thisEnemy)
  if not ShouldPerformAction(thisEnemy) then
    return "NoResponse", "NoAction"
  end
  if constants.urgentMovement and not constants.onCamera then
    return "NoResponse", "NoAction"
  end
  local enemyInFront = -90 < enemyAngle and enemyAngle < 90
  if constants.awarenessState == _G.constants.CombatSteady then
    return "ResponseTurn", "NoAction"
  elseif constants.awarenessState == _G.constants.MinorReposition then
    return "ResponseTurn", "NoAction"
  elseif constants.awarenessState == _G.constants.GenericReposition then
    return "ResponseTurn", "NoAction"
  elseif constants.awarenessState == _G.constants.SurvivalReposition then
    return "NoResponse", "NoAction"
  elseif constants.awarenessState == "CautiousReposition" then
    return "NoResponse", "NoAction"
  elseif constants.awarenessState == "Backpedal" then
    return "ResponseTurn", "NoAction"
  elseif constants.awarenessState == _G.constants.CloseCombat then
    return "ResponseTurn", "NoAction"
  end
  return "ResponseTurn", "NoAction"
end
function SelectResponse_EnemyTargetHero(ai, global, constants, enemyAttacking, enemyAngle, thisEnemy)
  if not ShouldPerformAction(thisEnemy) then
    return "NoResponse", "NoAction"
  end
  if constants.urgentMovement and not constants.onCamera then
    return "NoResponse", "NoAction"
  end
  local distance = (thisEnemy.WorldPosition - global.Player.WorldPosition):Length()
  local enemyDistance = (thisEnemy.WorldPosition - ai.WorldPosition):Length()
  local bowaction = "ShootArrow"
  if enemyDistance < 2.75 then
    if thisEnemy:PickupIsAcquired("Son_FollowUpAttackCounter") or constants.heroInRage then
      bowaction = "EvadeShoot"
    else
      bowaction = "BowSwingCheapShot"
    end
  end
  local enemyInFront = -90 < enemyAngle and enemyAngle < 90
  local isTargetedPlayer = global.Player:GetTargetCreature() == thisEnemy
  if constants.awarenessState == _G.constants.CombatSteady then
    if isTargetedPlayer then
      return "NoResponse", bowaction
    elseif enemyInFront then
      if enemyAttacking then
        return "NoResponse", bowaction
      else
        return "NoResponse", bowaction
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      return "ResponseTurn", "ShootArrow"
    end
  elseif constants.awarenessState == _G.constants.MinorReposition then
    if isTargetedPlayer then
      return "NoResponse", bowaction
    elseif enemyInFront then
      if enemyAttacking then
        return "NoResponse", "EvadeShoot"
      else
        return "NoResponse", "ShootArrow"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      elseif math.random(0, 1) > 0.5 then
        return "NoResponse", bowaction
      else
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == _G.constants.GenericReposition or constants.awarenessState == _G.constants.UrgentReposition then
    if isTargetedPlayer then
      return "NoResponse", bowaction
    elseif enemyInFront then
      if enemyAttacking or enemyDistance < 5 then
        return "NoResponse", bowaction
      else
        return "NoResponse", "ShootArrow"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      elseif math.random(0, 1) > 0.5 then
        return "NoResponse", bowaction
      else
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == _G.constants.SurvivalReposition then
    if enemyInFront then
      if enemyAttacking then
        return "ResponseTurnRun", "NoAction"
      else
        return "ResponseTurnRun", "NoAction"
      end
    elseif enemyAttacking then
      return "DodgeEnemy", "NoAction"
    else
      return "DodgeEnemy", "NoAction"
    end
  elseif constants.awarenessState == "CautiousReposition" then
    if isTargetedPlayer then
      return "DodgeEnemy", "NoAction"
    elseif enemyInFront then
      if enemyAttacking then
        return "NoResponse", "EvadeShoot"
      else
        return "NoResponse", "ShootArrow"
      end
    elseif enemyAttacking then
      return "ResponseTurn", "ShootArrow"
    else
      local son = game.AI.FindSon()
      if son:IsInNavigationMove() then
        return "ResponseTurn", "ShootArrow"
      elseif math.random(0, 1) > 0.5 then
        return "NoResponse", bowaction
      else
        return "NoResponse", "EvadeShoot"
      end
    end
  elseif constants.awarenessState == "Backpedal" then
    if enemyInFront then
      if enemyAttacking then
        return "ResponseTurnRun", "NoAction"
      else
        return "ResponseTurnRun", "NoAction"
      end
    elseif enemyAttacking then
      return "NoResponse", "NoAction"
    else
      return "NoResponse", "NoAction"
    end
  elseif constants.awarenessState == _G.constants.CloseCombat then
    return "ResponseTurn", "NoAction"
  end
  return "ResponseTurn", "NoAction"
end
function ResetPath(urgent)
  if urgent then
    _G.constants.resetTheaterPath = true
  elseif _G.constants.resetPathDelay >= 10 then
    _G.constants.resetTheaterPath = true
  end
end
function SetRegenRate(ai, global, constants)
  if global.currentState == "InCombat" or global.currentState == "EnterCombat" then
    local regen = ai:AttributeGetValue("Recovery")
    if regen == nil or regen == 0 then
      ai:MeterSetRegenRate("Son_ArrowCount", 3)
    elseif regen == 1 then
      ai:MeterSetRegenRate("Son_ArrowCount", 3.5)
    elseif regen == 2 then
      ai:MeterSetRegenRate("Son_ArrowCount", 4)
    elseif regen == 3 then
      ai:MeterSetRegenRate("Son_ArrowCount", 4.5)
    elseif regen == 4 then
      ai:MeterSetRegenRate("Son_ArrowCount", 5)
    elseif regen == 5 then
      ai:MeterSetRegenRate("Son_ArrowCount", 5.5)
    elseif 5 < regen then
      ai:MeterSetRegenRate("Son_ArrowCount", 6)
    else
      ai:MeterSetRegenRate("Son_ArrowCount", 3)
    end
  else
    ai:MeterSetRegenRate("Son_ArrowCount", 10)
  end
end
function GetMeleeAbility(ai, global, constants, potentialAttack, stringID)
  if stringID == "traveler00" or stringID == "wulver00" or stringID == "wulver10" or stringID == "jotunn00" or stringID == "jotunn10" or stringID == "jotunn20" or stringID == "troll00" or stringID == "troll10" or stringID == "troll20" or stringID == "valkryie00" or stringID == "golem00" or stringID == "golem10" or stringID == "golem20" or stringID == "golem30" then
    return "NoResponse", "EvadeShoot"
  end
  if constants.heroInRage == true then
    return "NoResponse", "EvadeShoot"
  end
  if potentialAttack == "BowCombo" then
    if gVFSCloseCombatUpgraded.value == true then
      return "NoResponse", "BowCombo"
    end
  elseif potentialAttack == "BowSwingTrip" then
    return "NoResponse", "BowSwingTrip"
  elseif potentialAttack == "KickOff" and gVFSCloseCombatUpgraded.value == true then
    if stringID ~= "flyer00" and stringID ~= "flyer10" and stringID ~= "witch00" and stringID ~= "witch10" and stringID ~= "witch20" then
      return "NoResponse", "KickOff"
    else
      return "NoResponse", "EvadeShoot"
    end
  end
  return "NoResponse", "BowSwing"
end
function LuaHook_SetSurvivalReposition(ai)
  _G.constants.previousAwarenessState = _G.constants.awarenessState
  _G.constants.awarenessState = _G.constants.SurvivalReposition
  _G.global.InCombat = true
  _G.global.overrides.suppressCombat = false
  _G.global.previousState = "PreCombat"
  _G.global.currentState = "InCombat"
  InCombat.overrideResetState = true
end
return AwarenessBrain
