local helper = require("son.helper")
local statemachine = require("ai.statemachine")
local pickle = require("core.pickle")
local DL = require("design.DesignerLibrary")
local FWP = require("behavior.followWayPoints")
local traverselink = require("creature.traverselink")
local uiCalls = require("ui.uicalls")
local bboardUtil = require("game.BlackboardUtil")
local lookAtConsts = require("game.lookAtConsts")
local son_input = require("son.input")
local thunk = require("core.thunk")
local environmentevent = require("creature.environmentevent")
local contextactionlibrary = require("level.contextactionlibrary")
local mpicon = require("ui.mpicon")
local TUT = require("game.GlobalTutorials")
traverselink.InstallHooks()
environmentevent.InstallHooks()
contextactionlibrary.InstallHooks()
engine.SetIncrementalGCTime(500)
local locomotion = require("creature.locomotion")
locomotion.Install()
local navbranchlogic = require("creature.navbranchlogic")
navbranchlogic.Install()
print("Free Bytes: " .. tostring(GetFreeMemory()))
print("Son main start")
gVFSDisableSonCommands = engine.VFSBool.New("Disable Son Commands")
gVFSDebug = engine.VFSBool.New("Enable Debug")
gVFSInfiniteMeter = engine.VFSBool.New("Enable Infinite Meter")
gVFSDebugAutonomousShoot = engine.VFSBool.New("Enable Debug Autonomous Shoot")
gVFSDebugBreakSonPOI = engine.VFSBool.New("Break Son POI")
gVFSDebugInputCommand = engine.VFSBool.New("Enable Debug Input")
gVFSDebugInputCommandMoveEvent = engine.VFSInt.New("Contexts 1 = 0, 2 = ARROW_NORMAL, 3 = ARROW_CRITICAL, 4 = ARROW_DEFAULT, 5 = STAGGER Arrow, 6 = STAGGER BOW, 7 = UNSHEATH BOW", 1, 300)
gVFSDebugNoBrainLogic = engine.VFSBool.New("Enable No Brain Logic")
gVFSDebugShowContextBehavior = engine.VFSBool.New("Show Context Behavior Debug")
gVFSDebugNoBrainLogic.value = false
gVFSDebugInputCommand.value = false
gVFSDebugInputCommandMoveEvent.value = 7
_G.thisLookAtEntry = nil
_G.lookAtPriorityOverrides = {}
constants = {}
global = {}
constants.stats = {}
constants.stats.advancedFollowUpEnabled = true
_G.stats = {}
_G.stats.VFSspecialShotChance = engine.VFSFloat.New("Son Crit Chance", 1, 100)
_G.stats.VFSspecialShotChance.value = 10
_G.stats.VFSshotFrequency = engine.VFSFloat.New("Son Shoot Frequency (sec)", 0.1, 100)
_G.stats.VFSshotFrequency.value = 8.5
_G.stats.VFSmaxShots = engine.VFSInt.New("Son max target count", 1, 100)
_G.stats.VFSmaxShots.value = 3
_G.stats.VFSclusterRadius = engine.VFSFloat.New("Son shot cluster radius", 1, 120)
_G.stats.VFSclusterRadius.value = 4
_G.stats.VFSreGrabRadius = engine.VFSFloat.New("Son enemy regrab radius", 1, 120)
_G.stats.VFSreGrabRadius.value = 5
_G.stats.VFSdiscardRadius = engine.VFSFloat.New("Son enemy discard radius", 1, 120)
_G.stats.VFSdiscardRadius.value = 6
_G.stats.VFSselfRegrabDistance = engine.VFSFloat.New("Son zone to grab enemy radius", 1, 120)
_G.stats.VFSselfRegrabDistance.value = 3
_G.stats.VFSselfRegrabAngle = engine.VFSFloat.New("Son zone to grab enemy angle", 1, 180)
_G.stats.VFSselfRegrabAngle.value = 75
_G.stats.VFSheroDiscardDistance = engine.VFSFloat.New("Hero zone to discard enemy radius", 1, 120)
_G.stats.VFSheroDiscardDistance.value = 6.5
_G.stats.VFSheroDiscardAngle = engine.VFSFloat.New("Hero zone to discard enemy angle", 1, 180)
_G.stats.VFSheroDiscardAngle.value = 30
_G.stats.VFSnoTargetHeroDiscardRadius = engine.VFSFloat.New("Hero discard enemy radius when no targets selected", 1, 120)
_G.stats.VFSnoTargetHeroDiscardRadius.value = 6
_G.stats.VFSnoTargetRegrabRadius = engine.VFSInt.New("Son grab enemy radius when no targets selected", 1, 300)
_G.stats.VFSnoTargetRegrabRadius.value = 20
_G.stats.VFSactionIndex = engine.VFSInt.New("Action type", 1, 9)
_G.stats.VFSactionIndex.value = 2
_G.stats.VFSsummonAction = engine.VFSInt.New("1 = fenrir, 2 = falcon, 3 = boar, 4 = rataoskr, 5 = birds, 6 = deer", 1, 6)
_G.stats.VFSsummonAction.value = 1
_G.currentState = nil
_G.previousState = nil
constants.theaterOfOperation = {}
constants.theaterOfOperation.params = {
  "TheaterOO_StayClose"
}
constants.theaterOfOperation.index = 1
constants.actionTypes = {
  {actionName = "Knife", cost = 0},
  {actionName = "Arrow", cost = 10},
  {actionName = "SuperMove", cost = 0},
  {
    actionName = "SummonFenrir",
    cost = 10
  },
  {actionName = "Disobey", cost = 10}
}
constants.POICollisionRadius = 0.05
constants.TeleportToSafetyTimer = 1
constants.InBadLocation = 1
constants.previousPathLength = 99
constants.InRunDuration = 0
constants.StuckInRun = false
constants.lockArrowType = false
constants.currentTargetList = {}
constants.MatchLevelTimer = 0
constants.resetTheaterPath = true
constants.extraPostCombatTime = 0
constants.postCombatIgnoreDistanceExit = false
constants.largeCreatureTarget = nil
constants.largeCreatureFinderTimer = 0
global.actionTypeIndex = 2
global.sonDisabledFromActions = false
global.actionMeterValue = 0
global.Player = game.Player.FindPlayer()
global.sonCSMoveAvailable = false
global.disableShooting = false
global.availabilityState = {}
global.availabilityState.LevelScript = nil
global.availabilityState.SonUnavailable = nil
constants.navCurveData = {}
global.overrides = {}
global.overrides.suppressCombat = false
constants.previousShotTarget = nil
constants.previousShotTargetTimer = 0
constants.previousShotTargetTimerReset = 0.25
constants.trollCounterCooldown = 5
global.jotunnGrabTimer = 5
global.allowReactionTimer = 0
constants.allowReactionTimerMax = 10
constants.reactionTimerEnabled = true
global.allowOccupyTimer = 0
constants.allowOccupyTimerMax = 10
constants.occupyTimerEnabled = true
global.allowIncapacitateTimer = 10
constants.allowIncapacitateTimerMax = 10
constants.incapacitateTimerEnabled = true
global.allowDeathTimer = 10
constants.allowDeathTimerMax = 10
constants.deathTimerEnabled = true
global.currentContextConfig = nil
global.currentOneOffTimer = -1
global.queuedContextConfigName = nil
global.currentContextName = nil
constants.oneOffTimer = 0
constants.nextOneOffTimerMax = -1
global.ContextBehaviorStageNames = {
  "Idle",
  "Enter",
  "Loop",
  "Exit",
  "OneOff",
  "EnvironmentEvent",
  "BanterMode"
}
constants.postUpTarget = nil
constants.doingPostUpAutonomousShot = false
global.deactivationTimer = 0
global.checkForCorrectSpeedAndPhaseStops = false
global.slowdownAllowed = true
global.stopMove = nil
global.nessecarySpeed = nil
global.contextBehaviorStage = 0
global.contextBehaviorNextOneOff = nil
global.contextBehaviorNextIndex = -1
global.ContextBehaviorCurrentOneOff = nil
global.markContextBehaviorForDelete = false
global.oneOffTimerActive = true
global.bowOut = false
global.usedSonInteractWhileOccupied = false
gVFSSetArrowType = engine.VFSInt.New("1 = default, 2 = shock, 3 = light, 4 = none", 1, 4)
gVFSSetArrowType.value = 1
constants.currentArrowType = 1
constants.isPotentiallyStuck = false
constants.isPotentiallyStuckTimer = 0
constants.isPotentiallyStuckTimerMax = 3
constants.deltaTime = 0
constants.overrideMovement = false
constants.disengageDistance = 15
constants.disengageDistance_Scripted = 15
constants.ambientTimer = 0
constants.ambientTimerMax = 8
constants.forcedBehavior = false
constants.allowedPOIAction = "NoAttack"
constants.inputDebugPressed = false
constants.urgentMovement = true
constants.maxAggro = 2
global.tutorial_Incapacitate = false
constants.heroInRage = false
local breathEffect
local fidgetCoolDownTime = 0
gVFSCloseCombatUpgraded = engine.VFSBool.New("Close Combat Upgrade")
gVFSLockDownUpgraded = engine.VFSBool.New("Lockdown Upgrade")
gVFSLockDownUpgraded.value = true
gVFSFollowupUpgraded = engine.VFSBool.New("Followup Upgrade")
gVFSFollowupUpgraded.value = true
gVFSEnableTeleportLogic = engine.VFSBool.New("Enable Teleport Logic")
gVFSEnableTeleportLogic.value = true
global.emotionState = {}
global.emotionState.value = 6
gVFSDebugHeroSyncSave = engine.VFSBool.New("Debug Hero Sync Save")
gVFSDebugHeroSyncSave.value = false
constants.skill_KratosSave = true
constants.skill_BlockBreak = true
constants.skill_MeleeFollowup = true
constants.skill_SonGrab = true
constants.skill_FollowupShot = true
constants.Steady = 1
constants.PostPOISteady = 2
constants.FollowPlayer = 3
constants.InchForward = 4
constants.Wait = 5
constants.NoticeHero = 6
constants.UrgentMoveAway = 7
constants.GoToPoint = 8
constants.ForcedGoToPoint = 9
constants.ObserveBeforeMoving = 10
constants.ObservedUrgentMoveAway = 11
constants.MoveAway = 12
constants.CombatSteady = 13
constants.MinorReposition = 14
constants.GenericReposition = 15
constants.SurvivalReposition = 16
constants.EvaluateSurvivalReposition = 17
constants.UrgentReposition = 18
constants.CloseCombat = 19
constants.PursueTarget = 20
constants.AggressiveFlank = 21
constants.Relaxed = 22
constants.InCombat = 23
constants.PostCombat = 24
constants.PreCombat = 25
global.StartLoadoutGiven = false
global.IsPlatinum = true
global.ArrowSetupHack = false
_G.incapacitated = false
local son_States = {
  require("son.GameState"),
  require("son.brain"),
  require("son.awarenessbrain"),
  require("son.targetbrain"),
  require("son.motionbrain")
}
translationDriver = nil
rotationDriver = nil
function AddRecipeToHero_NoNotification(recipeName)
  if not game.Wallets.HasRecipe("HERO", recipeName) then
    game.Wallets.AddRecipe("HERO", recipeName)
    game.UI.ClearNotification("Recipe", recipeName)
  end
end
function AddResourceToHero_NoNotification(resourceName)
  if game.Wallets.GetResourceValue("HERO", resourceName) <= 0 then
    game.Wallets.AddResource("HERO", resourceName, 1, "NO_TELEMETRY")
    game.UI.ClearNotification("Resource", resourceName)
  end
end
local GiveStartLoadout = function(creature)
  global.StartLoadout = {}
  global.StartLoadout.pickups = {
    "SonArmorBase",
    "SonArrow_Visual",
    "SonStrap_Visual",
    "SonKnife"
  }
  if game.Player.FindPlayer():PickupIsAcquired("SonKratosBow") == false or game.Level.GetVariable("CompletedCineNumber") == 25 then
    table.insert(global.StartLoadout.pickups, "SonBow")
  end
  if creature:PickupIsSlotUsed("SecondaryWeapon") == false then
    creature:PickupAcquire("SonQuiver")
  end
  for _, pickups in ipairs(global.StartLoadout.pickups) do
    local pickupId = game.Pickup.GetId(pickups)
    local slotName = game.Pickup.GetSlotName(pickupId)
    if creature:PickupIsSlotUsed(slotName) == false then
      creature:PickupAcquire(pickups)
    end
  end
end
function _G.OnAICreateLuaState(ai)
  statemachine.StartAll(ai, son_States, ai, global, constants)
  global.selfAI = ai
  global.StartLoadoutGiven = false
  GiveStartLoadout(ai)
  LoadAvailability(global, ai)
  AddResourceToHero_NoNotification("SonArmorBase")
  AddResourceToHero_NoNotification("SonKnifeUnlock")
  AddResourceToHero_NoNotification("ArrowUnlockStage1")
  ai:PickupSetStage("SonRuneArrow_Type", 2)
  global.actionTypeIndex = 2
  _G.OnAICreateLuaState = nil
  ai:SetBlackboardSize(64)
  _G.global.enemyBlackBoardIndex = 5000
  FWP.RegisterWaypointCallback(ai, global, constants)
  if ai.AddLookAtPriorityOverride then
    _G.thisLookAtEntry = game.AddGlobalLookAtTarget(ai, engine.Vector.New(0, 1, 0), lookAtConsts.TargetType.Kid)
    _G.lookAtPriorityOverrides.hero = ai:AddLookAtPriorityOverride(lookAtConsts.TargetType.Hero, lookAtConsts.Priority.Ignore)
    _G.lookAtPriorityOverrides.badGuy = ai:AddLookAtPriorityOverride(lookAtConsts.TargetType.BadGuy, lookAtConsts.Priority.High)
    _G.lookAtPriorityOverrides.combatTarget = ai:AddLookAtPriorityOverride(lookAtConsts.TargetType.CombatTarget, lookAtConsts.Priority.Highest)
    _G.lookAtPriorityOverrides.InterestingObject = ai:AddLookAtPriorityOverride(lookAtConsts.TargetType.InterestingObject, lookAtConsts.Priority.Highest)
  end
  local cineNumber = game.Level.GetVariable("CompletedCineNumber")
  if 365 <= cineNumber and cineNumber < 390 then
    LuaHook_SetSonSick()
  end
  son_input.InputSetup(ai, global, constants)
  if not _G.global.warpTargetRot then
    _G.global.warpTargetRot = ai:GetAnimDriver("WarpTargetRot")
  end
  constants.previousShotTarget = nil
  constants.WeaponSetupFinished = false
  constants.setCommandOnly = false
  constants.InSurvivalDriver = {}
  constants.targetROT = ai:GetAnimDriver("TargetROT")
  AddRecipeToHero_NoNotification("Recipe_SonPerk_FollowUp")
  AddRecipeToHero_NoNotification("Recipe_SonPerk_LockDownTarget")
  AddRecipeToHero_NoNotification("Recipe_SonPerk_CloseCombat")
  global.IsPlatinum = game.UI.IsPlatinum()
end
local FixupProfile = function(crt, pickupName)
  if crt:PickupIsAcquired(pickupName) then
    crt:PickupRelinquish(pickupName)
  end
  if game.Pickup.NGP_ClearProfile then
    game.Pickup.NGP_ClearProfile(pickupName)
  end
end
function OnNGPDataLoaded(sonCrt)
  assert(sonCrt ~= nil, "Why are you sending me a null creature?")
  FixupProfile(sonCrt, "SonArrow_Visual")
  AddResourceToHero_NoNotification("ArrowUnlockStage1")
  FixupProfile(sonCrt, "SonKnife")
  FixupProfile(sonCrt, "SonKnife2")
  AddResourceToHero_NoNotification("SonKnifeUnlock")
  if not sonCrt:PickupIsAcquired("SonBow") then
    sonCrt:PickupAcquire("SonBow", game.Pickup.GetProfileStage("SonBow"))
  end
  FixupProfile(sonCrt, "SonStrap_Visual")
  sonCrt:PickupAcquire("SonStrap_Visual", 0)
end
function OnCreatureUpdate(crt)
  locomotion.AssignNavCurve(global, constants, crt)
  if global.StartLoadoutGiven == false then
    GiveStartLoadout(crt)
    global.StartLoadoutGiven = true
  end
  if global.ArrowSetupHack == false then
    HideBaseArrow(crt)
  end
  if not global.IsPlatinum then
    local completedCine = game.Level.GetVariable("CompletedCineNumber")
    if 60 <= completedCine and crt:PickupIsSlotUsed("PrimaryWeapon") == false then
      assert(false, "Son is missing Bow after deer cine! Last completed cine is : " .. completedCine)
      crt:PickupAcquire("SonBow")
    end
  end
end
local isUIHackApplied = false
function OnAIUpdate(ai)
  if not isUIHackApplied then
    local uiSon = game.UI.FindCreatureByGOName("goSon00")
    if uiSon then
      local uiSonArrow = uiSon:GetAttachmentGO("goArrow00")
      if uiSonArrow then
        uiSonArrow:HideJoint(0)
        isUIHackApplied = true
      end
    end
  end
  if ai.OwnedPOI then
    global.POIInfo.POIStageName = ai.OwnedPOI:GetStageName()
  end
  if _G.incapacitated == true and ai:HasMarker("Incapacitated") == false then
    LuaHook_IncapacitateExit()
  end
  if constants.WeaponSetupFinished == false then
    local pickupId = game.Pickup.GetId("SonKnife")
    local slotName = game.Pickup.GetSlotName("SonKnife")
    if ai:PickupIsSlotUsed(slotName) == false then
      ai:PickupAcquire("SonKnife")
      if not game.Wallets.HasResource("HERO", "SonKnifeUnlock") then
        game.Wallets.AddResource("HERO", "SonKnifeUnlock", 1, "NO_TELEMETRY")
      end
    end
    constants.WeaponSetupFinished = true
  end
  if global.emotionState.value == 3 then
    local cineNumber = game.Level.GetVariable("CompletedCineNumber")
    if 470 <= cineNumber then
      global.emotionState.value = 6
      game.Wallets.RemoveResource("HERO", "Dummy_Son_EnragedMarker", 1)
    end
  end
  locomotion.CreateDrivers(ai)
  traverselink.OnUpdate(ai)
  GetDrivingVariable(ai, global)
  SetGameState(ai, global, constants)
  UpdateSonDisabledUI(ai, global, constants)
  Son_VO_Logic(ai, global, constants)
  MatchHeroPowerLevel(ai, global, constants)
  UpdateSonIncapacitateTimer(ai, global, constants)
  SetWorldParameters(ai, global, constants)
  DebugArrowStuff(ai, global, constants)
  SetBlackBoardVariables(ai, global, constants)
  RemoveDebuffs(ai, global, constants)
  CheckForStopping(ai)
  statemachine.UpdateAll(son_States, ai, global, constants)
  local statepointer = son_States
  if global.currentContextConfig == nil then
    if game.Boat.GetPlayerBoat() then
      EnterBehaviorContext(ai, "BOAT_CONTEXT_CONFIG_NORMAL")
    else
      EnterBehaviorContext(ai, "NORMAL_BEHAVIOR_CONTEXT_CONFIG")
    end
  end
  if global.currentContextConfig ~= nil and 0 < global.contextBehaviorStage and (not ai:IsDoingSyncMove() or game.Boat.GetPlayerBoat() ~= nil or ai:IsPlayingMove("MOV_CA_Idle")) then
    RunSonContextBehavior(ai, global, constants)
  end
  BreathEffect(ai, global, constants)
  ShowDebugInfo(ai, global, constants, statepointer)
  constants.previousShotTargetTimer = constants.previousShotTargetTimer + ai:GetFrameTime()
  if 0 < fidgetCoolDownTime then
    fidgetCoolDownTime = fidgetCoolDownTime - ai:GetFrameTime()
  end
  if 0 < constants.focusDuration then
    constants.focusDuration = constants.focusDuration - ai:GetFrameTime()
    if 0 >= constants.focusDuration and ai.OwnedPOI == nil then
      ai:ClearFocus()
    end
  end
  if ai:DebugIsSelectedCreature() then
    engine.DrawTextInWorld(ai:GetWorldPosition(), ai:GetMovementPriority(), 0)
  end
  if gVFSDebugBreakSonPOI.value then
    if ai.OwnedPOI ~= nil then
      ai.OwnedPOI:SendEvent("BreakOut")
    end
    gVFSDebugBreakSonPOI.value = false
  end
  TeleportToSafetyCheck(ai, global, constants)
end
function SetGameState(ai, global, constants)
  global.bInCombat, constants.numEnemies, global.potentialEnemyList = helper.inCombat(ai)
  constants.resetPathDelay = constants.resetPathDelay + 1
end
function SetBlackBoardVariables(ai, global, constants)
  gVFSCloseCombatUpgraded.value = game.Wallets.GetResourceValue("HERO", "SonPerk_CloseCombat") > 0
  constants.followUpSkill = 0 < game.Wallets.GetResourceValue("HERO", "SonPerk_Followup")
  constants.lockDownTargetSkill = 0 < game.Wallets.GetResourceValue("HERO", "SonPerk_LockDownTarget")
  if global.Player:PickupIsAcquired("RageMode") and global.Player:PickupGetStage("RageMode") == 1 then
    constants.heroInRage = true
  else
    constants.heroInRage = false
  end
  local sonbb = ai:GetPrivateBlackboard()
  if sonbb ~= nil then
    if global.bInCombat then
      sonbb:Set("InCombat", true)
    else
      sonbb:Set("InCombat", false)
    end
    if 0 < game.Wallets.GetResourceValue("HERO", "Dummy_Son_EnragedMarker") then
      global.emotionState.value = 3
      sonbb:Set("Enraged", true)
    else
      global.emotionState.value = 6
      sonbb:Set("Enraged", false)
    end
  end
end
function BreathEffect(ai, global, constants)
  if breathEffect == nil then
    local son = game.AI.FindSon()
    breathEffect = game.FX.Spawn("FX_son_Breath", nil, {
      Joint = "JOJaw1",
      GameObject = son,
      DeleteWithCreature = true
    })
    breathEffect:SetAnimFloatChannelDriver(son, "sliderfloatchannels._breathfxintensity")
  end
end
global.stupidcounter = 0
function ShowDebugInfo(ai, global, constants, statepointer)
  if ai:DebugIsSelectedCreature() == false then
    return
  end
  local debugTable = statemachine.GetDebugTable(statepointer, ai)
  if debugTable then
    if _G.previousState then
      table.insert(debugTable, {
        "Previous State",
        _G.previousState.__identity
      })
    end
    if constants.motionType ~= nil then
      table.insert(debugTable, {
        "Motion Type",
        constants.motionType
      })
    end
    table.insert(debugTable, {
      "Current behavior",
      ReturnStringStateText(constants.awarenessState)
    })
    table.insert(debugTable, {
      "Behavior Stack",
      ":"
    })
    if constants.behaviorStack ~= nil then
      for _, i in ipairs(constants.behaviorStack) do
        table.insert(debugTable, {
          "Behavior",
          ReturnStringStateText(i.behavior) .. " time = " .. round(i.duration, 2)
        })
      end
    end
    table.insert(debugTable, {
      "Stupid counter ",
      global.stupidcounter
    })
    if constants.focusDuration > 0 then
      table.insert(debugTable, {
        "Focus Time ",
        constants.focusDuration
      })
    end
    engine.DrawDebugTable(debugTable)
  end
end
function ReturnStringStateText(id)
  if id == constants.Steady then
    return "Steady"
  elseif id == constants.PostPOISteady then
    return "PostPOISteady"
  elseif id == constants.FollowPlayer then
    return "FollowPlayer"
  elseif id == constants.InchForward then
    return "InchForward"
  elseif id == constants.Wait then
    return "Wait"
  elseif id == constants.NoticeHero then
    return "NoticeHero"
  elseif id == constants.UrgentMoveAway then
    return "UrgentMoveAway"
  elseif id == constants.GoToPoint then
    return "GoToPoint"
  elseif id == constants.ForcedGoToPoint then
    return "ForcedGoToPoint"
  elseif id == constants.ObserveBeforeMoving then
    return "ObserveBeforeMoving"
  elseif id == constants.ObservedUrgentMoveAway then
    return "ObservedUrgentMoveAway"
  elseif id == constants.MoveAway then
    return "MoveAway"
  elseif id == constants.CombatSteady then
    return "CombatSteady"
  elseif id == constants.MinorReposition then
    return "MinorReposition"
  elseif id == constants.GenericReposition then
    return "GenericReposition"
  elseif id == constants.SurvivalReposition then
    return "SurvivalReposition"
  elseif id == constants.EvaluateSurvivalReposition then
    return "EvaluateSurvivalReposition"
  elseif id == constants.UrgentReposition then
    return "UrgentReposition"
  elseif id == constants.CloseCombat then
    return "CloseCombat"
  elseif id == constants.PursueTarget then
    return "PursueTarget"
  elseif id == constants.AggressiveFlank then
    return "AggressiveFlank"
  elseif id == constants.Relaxed then
    return "Relaxed"
  elseif id == constants.InCombat then
    return "InCombat"
  elseif id == constants.PostCombat then
    return "PostCombat"
  elseif id == constants.PreCombat then
    return "PreCombat"
  end
  if id == "POI" then
    return "POI"
  end
  return id
end
function round(num, idp)
  return string.format("%." .. (idp or 0) .. "f", num)
end
function SetReticleCreature(ai, global, constants)
  constants.reticleCreature, constants.hitLoc = DL.Reticle_GetCreature()
  if constants.reticleCreature ~= nil and constants.reticleCreature:GetCreature() == nil then
    constants.reticleCreature = nil
  end
end
function UpdateSonDisabledUI(ai, global, constants)
  local actionCost = constants.actionTypes[global.actionTypeIndex].cost
  local meterAmount = _G.global.selfAI:MeterGetValue("Son_ArrowCount")
  local isSonDisabled = ai:IsDoingSyncMove() and not ai:HasMarker("AllowSyncShots")
  isSonDisabled = isSonDisabled or ai:HasMarker("DisableCommandShot")
  isSonDisabled = isSonDisabled or ai:HasMarker("TweakPOIResOnly")
  isSonDisabled = isSonDisabled or DL.CheckCreatureContext(ai:GetContext(), "INCAPACITATED")
  isSonDisabled = isSonDisabled or actionCost > meterAmount
  isSonDisabled = isSonDisabled or not ai:IsAvailableForCombat()
  isSonDisabled = isSonDisabled and not gVFSInfiniteMeter.value
  if isSonDisabled ~= global.sonDisabledFromActions then
    uiCalls.UI_Refresh_HUD()
  end
  global.sonDisabledFromActions = isSonDisabled
end
function ToggleReactionTimer(ai, reactionType, enable)
  if reactionType == "Reaction" then
    _G.constants.reactionTimerEnabled = enable
  elseif reactionType == "Incapacitate" then
    _G.constants.incapacitateTimerEnabled = enable
  elseif reactionType == "Occupy" then
    _G.constants.occupyTimerEnabled = enable
  elseif reactionType == "Death" then
    _G.constants.deathTimerEnabled = enable
  end
end
function MatchHeroPowerLevel(ai, global, constants)
  if constants.MatchLevelTimer >= 5 then
    local heroPowerLevel = global.Player:AttributeGetValue("PowerLevel")
    heroPowerLevel = math.floor(heroPowerLevel)
    if 15 < heroPowerLevel then
      heroPowerLevel = 15
    end
    if ai:PickupIsAcquired("Son_PowerLevel") == false then
      ai:PickupAcquire("Son_PowerLevel")
    end
    ai:PickupSetStage("Son_PowerLevel", heroPowerLevel)
    constants.MatchLevelTimer = 0
  end
  constants.MatchLevelTimer = constants.MatchLevelTimer + ai:GetUnitTime()
end
function UpdateSonIncapacitateTimer(ai, global, constants)
  local deltaTime = ai:GetFrameTime()
  constants.trollCounterCooldown = constants.trollCounterCooldown - deltaTime
  if constants.trollCounterCooldown < 0 then
    constants.trollCounterCooldown = 0
  end
  constants.largeCreatureFinderTimer = constants.largeCreatureFinderTimer + deltaTime
  global.allowReactionTimer = global.allowReactionTimer + deltaTime
  global.allowOccupyTimer = global.allowOccupyTimer + deltaTime
  global.allowIncapacitateTimer = global.allowIncapacitateTimer + deltaTime
  global.allowDeathTimer = global.allowDeathTimer + deltaTime
  global.jotunnGrabTimer = global.jotunnGrabTimer - deltaTime
  if 0 > global.jotunnGrabTimer then
    global.jotunnGrabTimer = 0
  end
  if constants.reactionTimerEnabled == false then
    global.allowReactionTimer = 0
  elseif constants.incapacitateTimerEnabled == false then
    global.allowIncapacitateTimer = 0
  elseif constants.occupyTimerEnabled == false then
    global.allowOccupyTimer = 0
  elseif constants.deathTimerEnabled == false then
    global.allowDeathTimer = 0
  end
  if global.allowReactionTimer >= constants.allowReactionTimerMax then
    global.allowReactionTimer = constants.allowReactionTimerMax
    if not ai:PickupIsAcquired("Son_AllowReaction") then
      ai:PickupAcquire("Son_AllowReaction")
    end
    ai:PickupSetStage("Son_AllowReaction", 1)
  else
    if not ai:PickupIsAcquired("Son_AllowReaction") then
      ai:PickupAcquire("Son_AllowReaction")
    end
    ai:PickupSetStage("Son_AllowReaction", 0)
  end
  if global.allowOccupyTimer >= constants.allowOccupyTimerMax then
    global.allowOccupyTimer = constants.allowOccupyTimerMax
    if not ai:PickupIsAcquired("Son_AllowOccupy") then
      ai:PickupAcquire("Son_AllowOccupy")
    end
    ai:PickupSetStage("Son_AllowOccupy", 1)
  else
    if not ai:PickupIsAcquired("Son_AllowOccupy") then
      ai:PickupAcquire("Son_AllowOccupy")
    end
    ai:PickupSetStage("Son_AllowOccupy", 0)
  end
  if global.allowIncapacitateTimer >= constants.allowIncapacitateTimerMax then
    global.allowIncapacitateTimer = constants.allowIncapacitateTimerMax
    if not ai:PickupIsAcquired("Son_AllowIncapacitate") then
      ai:PickupAcquire("Son_AllowIncapacitate")
    end
    ai:PickupSetStage("Son_AllowIncapacitate", 1)
  else
    if not ai:PickupIsAcquired("Son_AllowIncapacitate") then
      ai:PickupAcquire("Son_AllowIncapacitate")
    end
    ai:PickupSetStage("Son_AllowIncapacitate", 0)
  end
  if global.allowDeathTimer >= constants.allowDeathTimerMax then
    global.allowDeathTimer = constants.allowDeathTimerMax
    if not ai:PickupIsAcquired("Son_AllowDeath") then
      ai:PickupAcquire("Son_AllowDeath")
    end
    ai:PickupSetStage("Son_AllowDeath", 1)
  else
    if not ai:PickupIsAcquired("Son_AllowDeath") then
      ai:PickupAcquire("Son_AllowDeath")
    end
    ai:PickupSetStage("Son_AllowDeath", 0)
  end
end
function DebugArrowStuff(ai, global, constants)
  if gVFSSetArrowType.value ~= constants.currentArrowType then
    if gVFSSetArrowType.value == 2 then
      LuaHook_ChangeArrowType(ai, "Shock")
    elseif gVFSSetArrowType.value == 3 then
      LuaHook_ChangeArrowType(ai, "Light")
    elseif gVFSSetArrowType.value == 4 then
      LuaHook_ChangeArrowType(ai, "none")
    end
    constants.currentArrowType = gVFSSetArrowType.value
  end
end
function SetWorldParameters(ai, global, constants)
  constants.onCamera = ai:CheckDecision("tweak_Decision_OnCamera")
end
function RemoveDebuffs(ai, global, constants)
  RemovePickup(ai, "Debuff_Hero_Poison_Persist")
  RemovePickup(ai, "Debuff_Hero_Poison_Short")
  RemovePickup(ai, "Debuff_Hero_Poison")
  RemovePickup(ai, "Debuff_Hero_Burn")
  RemovePickup(ai, "Debuff_Hero_Blind")
  RemovePickup(ai, "Debuff_Hero_Poison_Persist")
end
function RemovePickup(ai, pickupname)
  if ai:PickupIsAcquired(pickupname) then
    ai:PickupRelinquish(pickupname)
  end
end
function InBossFight(ai, inBossFight)
  _G.constants.inBossFight = inBossFight
end
function ShowPouch()
  local son = game.AI.FindSon()
  local pouchIndex = son:GetJointIndex("pouchMesh")
  son:ShowJoint(pouchIndex)
  local pullStringIndex = son:GetJointIndex("pullStringMesh")
  son:ShowJoint(pullStringIndex)
end
function HideBaseArrow(creature)
  if creature == nil then
    creature = game.AI.FindSon()
  end
  local arrowGO = creature:GetAttachmentGO("goArrow00")
  if arrowGO ~= nil then
    if game.Level.GetVariable("CompletedCineNumber") >= 290 or game.Wallets.GetResourceValue("HERO", "ArrowUnlockStage2") > 0 then
      local baseVersionIndex = arrowGO:GetJointIndex("arrow00_stream")
      arrowGO:HideJoint(baseVersionIndex)
    end
    global.ArrowSetupHack = true
  end
end
global.VO = {
  enemyBehindAttackingPlayer = {currentTimer = 500, cooldownTimer = 0.1}
}
global.VOStats = {}
global.VOStats.ReadyToShoot = {meterValue = 0}
global.VOStats.inCombat = false
global.VOStats.currentFightEnemyCount = 0
function Son_VO_Logic(ai, global, constants)
  VO_ReadyToShootRunicArrows(ai, global, constants)
end
function VO_ReadyToShootRunicArrows(ai, global, constants)
  local currentvalue = ai:MeterGetValue("Son_ArrowCount")
  if currentvalue > global.VOStats.ReadyToShoot.meterValue then
    local percentage = currentvalue / ai:MeterGetMax("Son_ArrowCount")
    if 0.9999 <= percentage then
      game.Audio.PlayBanterNonCritical("cbt_son_ready_to_shoot")
    end
  end
  global.VOStats.ReadyToShoot.meterValue = currentvalue
end
function LuaHookDecision_AttackType(ai, data)
  if _G.constants.heroInRage then
    return data:FindOutcomeBranchesEntry("Shoot")
  end
  if gVFSCloseCombatUpgraded.value then
    local chance = math.random()
    if chance < 0.05 then
      return data:FindOutcomeBranchesEntry("Dropkick")
    elseif chance < 0.1 then
      return data:FindOutcomeBranchesEntry("Trip")
    else
      return data:FindOutcomeBranchesEntry("Combo")
    end
  end
  return data:FindOutcomeBranchesEntry("None")
end
function LuaHookDecision_WeaponActivate(ai, data)
  for weapon in game.Player.FindPlayer():IterateActiveWeapons() do
    if weapon ~= nil and weapon.Weapon ~= nil and (weapon.Weapon:GetName() == "explosive00" or weapon.Weapon:GetName() == "axe00") then
      local weaponObject = weapon.Weapon
      local name = weapon.Weapon:GetName()
      if (name == "axe00" and (weaponObject.ThrowOutStatus == tweaks.tThrowOutStatus.eThrownWeaponStatus.kTOSInFlightOut or weaponObject.ThrowOutStatus == tweaks.tThrowOutStatus.eThrownWeaponStatus.kTOSInFlightReturn) or name == "explosive00" and weaponObject.ThrowOutStatus == tweaks.tThrowOutStatus.eThrownWeaponStatus.kTOSInHand) and (weaponObject.WorldPosition - ai.WorldPosition):Length() < 2.5 then
        if name == "explosive00" then
          return data:FindOutcomeBranchesEntry("FireExplosion")
        else
          return data:FindOutcomeBranchesEntry("IceExplosion")
        end
      end
    end
  end
end
function LuaHookDecision_NotInSurvival(ai, data)
  return _G.constants.awarenessState ~= _G.constants.SurvivalReposition
end
function LuaHook_WarpToKratos()
  local ad_sonRotation = _G.global.selfAI:GetAnimDriver("sonRotation_00")
  local ad_sonWarpLocation = _G.global.selfAI:GetAnimDriver("sonWarpLocation_00")
  local player = game.Player.FindPlayer()
  ad_sonWarpLocation.ValueVec = player:GetWorldPosition() + player:GetWorldForward() * 2
  ad_sonRotation.ValueVec = player:GetWorldForward()
end
function SonSetBBValue(ai, entry, value)
  local sonbb = ai:GetPrivateBlackboard()
  if value == nil then
    sonbb:Set(entry, 1)
  else
    sonbb:Set(entry, value)
  end
end
function SonAddChainLightningTarget(ai, target)
  local sonBB = ai:GetPrivateBlackboard()
  if sonBB ~= nil then
    local bbLength = bboardUtil.GetListLength(sonBB, 8000)
    if bbLength ~= nil and 0 < bbLength then
      local enemyList = bboardUtil.GetList(sonBB, 8000)
      if enemyList == nil then
        enemyList = {}
      end
      for _, i in ipairs(enemyList) do
        if i:GetCreature() == target then
          return
        end
      end
      table.insert(enemyList, target)
      bboardUtil.EraseList(sonBB, 8000)
      bboardUtil.SetList(sonBB, 8000, enemyList)
    else
      bboardUtil.SetList(sonBB, 8000, {target})
    end
  end
end
function SonRemoveChainLightningTarget(ai, target)
  local sonBB = ai:GetPrivateBlackboard()
  if sonBB ~= nil then
    local bbLength = bboardUtil.GetListLength(sonBB, 8000)
    if bbLength ~= nil and 0 < bbLength then
      local enemyList = bboardUtil.GetList(sonBB, 8000)
      local newEnemyList = {}
      if enemyList == nil then
        enemyList = {}
      end
      for _, i in ipairs(enemyList) do
        if not i:GetCreature() == target then
          table.insert(newEnemyList, target)
        end
      end
      bboardUtil.EraseList(sonBB, 8000)
      bboardUtil.SetList(sonBB, 8000, newEnemyList)
    end
  end
end
function SonRemoveChainLightningList(ai)
  local sonBB = ai:GetPrivateBlackboard()
  if sonBB ~= nil and bboardUtil.GetListLength(sonBB, 8000) ~= nil then
    bboardUtil.EraseList(sonBB, 8000)
  end
end
function LuaHook_InCommandShotContextAction(ai)
  if ai.OwnedPOI and ai.OwnedPOI.Type == "ContextAction" and (ai.OwnedPOI:FindLuaTableAttribute("SonAttackType") == "CommandShot" or ai.OwnedPOI:FindLuaTableAttribute("SonAttackType") == "Both") then
    return true
  else
    return false
  end
end
function LuaHook_InCombatState(ai)
  if game.Combat.GetCombatStatus() then
    return true
  else
    return false
  end
end
function LuaHook_EnablePostUpBehaviorContext(ai)
  EnterBehaviorContext(ai, "POST_UP_BEHAVIOR_CONTEXT_CONFIG")
end
function LuaHook_DisablePostUpBehaviorContext(ai)
  ClearThisBehaviorContextIfActive(ai, "POST_UP_BEHAVIOR_CONTEXT_CONFIG")
end
function LuaHook_DispelToTrue(ai)
  local sonbb = ai:GetPrivateBlackboard()
  sonbb:Set("Dispelled", true)
end
function LuaHook_RotateAwayFromTarget()
  if not _G.global.WarpTargetRot_v2 then
    _G.global.WarpTargetRot_v2 = game.AI.FindSon():GetAnimDriver("WarpTargetRot_v2")
  end
  if _G.global.WarpTargetRot_v2 ~= nil and _G.global.FollowUpTarget ~= nil then
    _G.global.WarpTargetRot_v2.ValueVec = game.AI.FindSon().WorldPosition - _G.global.FollowUpTarget.WorldPosition
  end
end
function LuaHook_RotateTowardsTarget()
  if not _G.global.WarpTargetRot_v2 then
    _G.global.WarpTargetRot_v2 = game.AI.FindSon():GetAnimDriver("WarpTargetRot_v2")
  end
  if _G.global.WarpTargetRot_v2 ~= nil then
    if global.FollowUpTarget ~= nil then
      _G.global.WarpTargetRot_v2.ValueVec = _G.global.FollowUpTarget.WorldPosition - game.AI.FindSon().WorldPosition
    else
      _G.global.WarpTargetRot_v2.ValueVec = game.Player.FindPlayer().WorldPosition - game.AI.FindSon().WorldPosition
    end
  end
end
function LuaHook_RotateTowardCombatTarget()
  if _G.global.combatTarget == nil then
    return
  end
  local son = game.AI.FindSon()
  if not _G.global.warpTargetRot then
    _G.global.warpTargetRot = son:GetAnimDriver("WarpTargetRot")
  end
  _G.global.warpTargetRot.ValueVec = _G.global.combatTarget.WorldPosition - son.WorldPosition
end
function LuaHook_RotateTowardsCenter()
  if not _G.global.warpTargetRot then
    _G.global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
  end
  local camForward = game.Camera.GetOrbitForward()
  camForward.y = 0
  local camPos = game.Camera.GetOrbitPosition() + camForward * 7
  if _G.global.warpTargetRot ~= nil then
    _G.global.warpTargetRot.ValueVec = camPos - game.AI.FindSon().WorldPosition
  end
end
function SaveAvailability(global, crt)
  if crt.IsAvailabilitySourceRequesting == nil then
    return
  end
  if crt:IsAvailabilitySourceRequesting("LevelDesignScript") then
    global.availabilityState.LevelScript = crt:GetAvailabilityStateRequestByName("LevelDesignScript")
    if game.AI.FindSon():HasMarker("InVendor") then
      if global.availabilityState.LevelScript.AvailableForSync == true and global.availabilityState.LevelScript.AvailableForBanter == true and global.availabilityState.LevelScript.AvailableInLevel == true and global.availabilityState.LevelScript.AvailableForCombat == true then
        global.availabilityState.LevelScript = nil
      else
        global.availabilityState.LevelScript.Unoccupied = true
      end
    end
  else
    global.availabilityState.LevelScript = nil
  end
  if crt:IsAvailabilitySourceRequesting("SonUnavailable") then
    global.availabilityState.SonUnavailable = crt:GetAvailabilityStateRequestByName("SonUnavailable")
  else
    global.availabilityState.SonUnavailable = nil
  end
end
function LoadAvailability(global, ai)
  if ai.IsAvailabilitySourceRequesting == nil then
    return
  end
  if global.availabilityState.LevelScript ~= nil then
    local availabilityState = {
      AvailableForSync = global.availabilityState.LevelScript.AvailableForSync,
      AvailableForBanter = global.availabilityState.LevelScript.AvailableForBanter,
      AvailableInLevel = global.availabilityState.LevelScript.AvailableInLevel,
      AvailableForCombat = global.availabilityState.LevelScript.AvailableForCombat,
      Unoccupied = global.availabilityState.LevelScript.Unoccupied
    }
    ai:SetNewAvailabilityRequest("LevelDesignScript", availabilityState)
  end
  if global.availabilityState.SonUnavailable ~= nil then
    local availabilityState = {
      AvailableForSync = global.availabilityState.SonUnavailable.AvailableForSync,
      AvailableForBanter = global.availabilityState.SonUnavailable.AvailableForBanter,
      AvailableInLevel = global.availabilityState.SonUnavailable.AvailableInLevel,
      AvailableForCombat = global.availabilityState.SonUnavailable.AvailableForCombat,
      Unoccupied = global.availabilityState.SonUnavailable.Unoccupied
    }
    ai:SetNewAvailabilityRequest("SonUnavailable", availabilityState)
  end
end
pickle.Install()
function Pickle(GO)
  local creature = GO:GetCreature()
  SaveAvailability(global, creature)
  global.combatTarget = nil
  global.FollowUpTarget = nil
  global.reactionTarget = nil
  global.LTW_Destinations = nil
  global.LTW_Spline = nil
  if global.leadTheWay ~= nil then
    global.leadTheWay.spline = nil
  end
  return global
end
function Unpickle(GO, tab)
  global = tab
  local player = game.Player.FindPlayer()
  player:CallScript("ResetCineMixStates")
end
function GetDrivingVariable(ai, global)
  if not global.locationDriver then
    global.locationDriver = ai:GetAnimDriver("WarpLocation")
  end
  if not global.directionDriver then
    global.directionDriver = ai:GetAnimDriver("WarpDirection")
  end
end
function OnSetWarpLocation(go, warpLocation)
  if not _G.global.locationDriver then
    _G.global.locationDriver = game.AI.FindSon():GetAnimDriver("WarpLocation")
  end
  if not _G.global.directionDriver then
    _G.global.directionDriver = game.AI.FindSon():GetAnimDriver("WarpDirection")
  end
  _G.global.warpLocation = warpLocation
  _G.global.warpDirection = (warpLocation - go.WorldPosition):Normalized()
  _G.global.locationDriver.ValueVec = _G.global.warpLocation
  _G.global.directionDriver.ValueVec = _G.global.warpDirection
end
function LuaHook_Boost_TraversalLink(go)
  if _G.global.warpLocation and _G.global.locationDriver and _G.global.directionDriver then
    _G.global.locationDriver.ValueVec = _G.global.warpLocation
    _G.global.directionDriver.ValueVec = (_G.global.warpLocation - go.WorldPosition):Normalized()
  end
end
function LuaHook_DeepSnowMat_Start(ai)
  ai:StartMaterialAnim("son00_deepSnow_reveal")
end
function LuaHook_DeepSnowMat_Stop(ai)
  ai:StartMaterialAnim("son00_deepSnow_conceal")
end
function LuaHook_SetBossFightBrain(ai, toggle)
  _G.constants.setCommandOnly = toggle
end
function LuaHook_FirstHumanKill(ai)
  local son = game.AI.FindSon()
  son:SetMaterialSwap("cineFHK")
  son:StartMaterialAnim("cineFHK")
end
function LuaHook_SetSonSick()
  _G.global.selfAI:SetMaterialSwap("sick")
end
function WarpAndReset(ai, location, rotation)
  if ai.OwnedPOI ~= nil and (ai.OwnedPOI.Type == "ContextAction" or ai.OwnedPOI.Type == "CubbyHole") then
    ai.OwnedPOI:SendEvent("BreakOut")
  end
  ai:Warp(location, rotation, true)
end
function TriggerBehaviorContextEnd(ai)
  if global.currentContextConfig == nil then
    return
  end
  if global.currentContextConfig.ExitAnimEvent ~= "" then
    ai:TriggerMoveEvent(global.currentContextConfig.ExitAnimEvent)
  else
    global.currentContextConfig = nil
    global.currentContextName = nil
    if global.queuedContextConfigName ~= nil then
      EnterBehaviorContext(ai, global.queuedContextConfigName)
      global.queuedContextConfigName = nil
    end
  end
end
function LuaHook_originalMaterial(ai)
  local son = game.AI.FindSon()
  local sonOriginalMaterial = "sonHead_M"
  if son.ReapplyExistingMaterialSwaps then
    son:ReapplyExistingMaterialSwaps()
  end
  son:SetMaterialSwap(sonOriginalMaterial)
end
function LuaHook_BehaviorContextAtIdle(ai)
  global.contextBehaviorStage = 1
  global.oneOffTimer = 0
end
function LuaHook_BehaviorContextTransition(ai)
  if global.currentContextConfig and global.currentContextConfig.ComplimentaryConfigName then
    EnterBehaviorContext(ai, global.currentContextConfig.ComplimentaryConfigName)
  end
end
function LuaHook_BehaviorContextEnter(ai)
  global.contextBehaviorStage = 2
  if global.currentContextConfig == nil then
    return
  end
  if global.currentContextConfig.EnterBanter ~= "" then
    game.Audio.PlayBanterNonCritical(global.currentContextConfig.EnterBanter)
  end
end
function LuaHook_BehaviorContextLoop(ai)
  global.contextBehaviorStage = 3
  global.ContextBehaviorCurrentOneOff = false
  global.ContextBehaviorCurrentIndex = -1
  if global.currentContextConfig == nil then
    return
  end
  if global.currentContextConfig.LoopBanter[0] ~= "" and global.currentContextConfig.LoopBanter[0] ~= nil then
    game.Audio.PlayBanterNonCritical(global.currentContextConfig.LoopBanter[0])
  end
end
function LuaHook_BehaviorContextExit(ai)
  global.contextBehaviorStage = 4
  if global.currentContextConfig == nil then
    return
  end
  if global.currentContextConfig.ExitBanter ~= "" and global.currentContextConfig.ExitBanter ~= nil then
    game.Audio.PlayBanterNonCritical(global.currentContextConfig.ExitBanter)
  end
end
function LuaHook_BehaviorContextOneOff(ai)
  global.contextBehaviorStage = 5
  if global.currentContextConfig == nil then
    return
  end
  if global.ContextBehaviorCurrentOneOff ~= nil and global.currentContextConfig.OneOffBanter[global.ContextBehaviorCurrentIndex] ~= "" and global.currentContextConfig.OneOffBanter[global.ContextBehaviorCurrentIndex] ~= nil and global.ContextBehaviorCurrentIndex ~= -1 then
    game.Audio.PlayBanterNonCritical(global.currentContextConfig.OneOffBanter[global.ContextBehaviorCurrentIndex])
  end
end
function LuaHook_BehaviorContextBanterMode(ai)
  global.contextBehaviorStage = 7
end
function LuaHook_BehaviorContextEnvEvent(ai)
  global.contextBehaviorStage = 6
end
function LuaHook_BehaviorContextExitMoveOver(ai)
  global.currentContextConfig = nil
  global.currentContextName = nil
  if global.queuedContextConfigName ~= nil then
    EnterBehaviorContext(ai, global.queuedContextConfigName)
    global.queuedContextConfigName = nil
  end
end
function LuaHook_SetToKillTarget()
  _G.global.sTargetMode = "TargetSpecificEnemy"
end
function LuaHook_LockCombatStatus()
  _G.global.sTargetMode = "TargetSpecificEnemy"
  game.AI.FindSon():SendBroadcastReaction({
    FilterData = {FilterRadius = 40},
    BroadcastContext = "GRUNT_CRY"
  })
end
function LuaHook_UnlockCombatStatus()
  _G.global.sTargetMode = "RegularTarget"
end
function LuaHook_SetToKratosTarget()
  _G.global.sTargetMode = "KratosTarget"
end
function LuaHook_SetToTargetKratos()
  _G.global.sTargetMode = "TargetKratos"
end
function LuaHook_SetToRegularTarget()
  _G.global.sTargetMode = "RegularTarget"
end
function LuaHook_HealthCreated(go, spawnedObject)
  print("Health created")
end
function LuaHook_SpawnHealth()
  local son = game.AI.FindSon()
  local jointIndex = son:GetJointIndex("JORightIndexA1")
  local jointPos = son:GetWorldJointPosition(jointIndex)
  local player = game.Player.FindPlayer()
  local distToPlayer = (player.WorldPosition + player:GetWorldForward() * 1.5 - son.WorldPosition):Length()
  local time = 1.7857143
  local velocity = distToPlayer / time
  local direction = engine.Vector.New(0, 5, -velocity)
  local healthTemplate = {
    SpawnObject = "golootGenericHealthRune",
    HookName = "LuaHook_HealthCreated",
    OffsetPosition = jointPos,
    Velocity = direction
  }
  son:SpawnGameObject(healthTemplate)
end
function LuaHook_ThrowHealth()
  local fakearrow = game.FX.Spawn("HealthRuneThrow", game.Level.GetPermLevel())
end
function LuaHook_ShootArrowBanterLogic()
  print("DEBUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG")
  if _G.global.selfAI:PickupIsAcquired("SonArrow_Light") then
    game.Audio.PlayBanterNonCritical("cbt_son_LightArrowShot")
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    game.Audio.PlayBanterNonCritical("cbt_son_ShockArrowShot")
    print("DEBUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG______________________________SHOCK CHECK")
  else
    game.Audio.PlayBanterNonCritical("cbt_son_NormalArrowShot")
  end
end
function LuaHook_ShootPostupShot()
  local target = _G.constants.postUpTarget
  local position
  if target == nil then
    if _G.constants.shootTargetInfo ~= nil and _G.constants.shootTargetInfo.target ~= nil then
      if _G.constants.shootTargetInfo.isCrystal == true then
        position = _G.constants.shootTargetInfo.position
      else
        target = _G.constants.shootTargetInfo.target
      end
    elseif _G.constants.shootTargetInfo ~= nil and _G.constants.shootTargetInfo.position ~= nil then
      position = _G.constants.shootTargetInfo.position
    else
      return
    end
  end
  local son = game.AI.FindSon()
  if target ~= nil then
    son:ForceLookAtToObject(target)
  end
  local arrowData = {}
  arrowData.Tweak = "ARR_ARROW_LUA"
  if _G.global.selfAI:PickupIsAcquired("SonArrow_Light") then
    arrowData.Tweak = "ARR_ARROW_LIGHT_LUA"
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    arrowData.Tweak = "ARR_ARROW_SHOCK_LUA"
  end
  if position ~= nil then
    arrowData.TargetLocation = _G.constants.shootTargetInfo.position
  else
    arrowData.Target = target
  end
  arrowData.Creator = son
  arrowData.CreatorEmitJoint = "JORightIndexA1"
  arrowData.EmitOffset = nil
  arrowData.PlayerCommand = false
  game.Combat.EmitArrow(arrowData)
  ArrowMeterAdjust(-10)
end
function LuaHook_ShootArrowLogic(fromPlayerCommand)
  uiCalls.UI_Event_Son_ShootArrow()
  local target
  local pierce = false
  if _G.constants.doingPostUpAutonomousShot then
    target = _G.constants.postUpTarget
  else
    if constants.shootTargetInfo == nil then
      return
    end
    target = constants.shootTargetInfo.target
    pierce = _G.constants.shootTargetInfo.isCrystal
  end
  local isEnemyCreature = pierce == false and target ~= nil
  local son = game.AI.FindSon()
  if son.OwnedPOI and son.OwnedPOI:GetLuaTableAttribute("PostUp") then
    if target ~= nil then
      son:ForceLookAtToObject(target)
    else
      son:ClearForcedLookAtToObject()
    end
  end
  local arrowData = {}
  arrowData.Tweak = "ARR_ARROW_LUA"
  if _G.global.selfAI:PickupIsAcquired("SonArrow_Light") then
    arrowData.Tweak = "ARR_ARROW_LIGHT_LUA"
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    arrowData.Tweak = "ARR_ARROW_SHOCK_LUA"
  end
  if pierce then
    arrowData.Tweak = arrowData.Tweak .. "_PIERCE_ENVIRONMENT"
  elseif isEnemyCreature then
    local creature = target
    if (creature:HasMarker("React_Flip") or creature:HasMarker("React_DefaultAir")) and (son:PickupIsAcquired("SonArmorSpeed01") or son:PickupIsAcquired("SonArmorSpeed02") or son:PickupIsAcquired("SonArmorSpeed03") or son:PickupIsAcquired("SonArmorBonus02") or son:PickupIsAcquired("SonArmorBonus03") or son:PickupIsAcquired("SonArmorBonus04")) then
      son:PickupAcquire("Perk_WeaknessExploitShot")
    end
  else
    arrowData.Tweak = arrowData.Tweak .. "_NOTARGET"
  end
  if _G.constants.doingPostUpAutonomousShot then
    arrowData.Target = _G.constants.postUpTarget
    arrowData.Tweak = "ARR_ARROW_LUA"
  elseif target ~= nil then
    arrowData.Target = target
    if _G.constants.shootTargetInfo.targetJoint ~= nil then
      arrowData.TargetJointId = _G.constants.shootTargetInfo.targetJoint
      arrowData.JointSpaceTargetPosition = _G.constants.shootTargetInfo.target:GetWorldJointPosition(_G.constants.shootTargetInfo.targetJoint)
    else
      arrowData.JointSpaceTargetPosition = _G.constants.shootTargetInfo.position
    end
  else
    arrowData.TargetLocation = _G.constants.shootTargetInfo.position
  end
  arrowData.Creator = son
  arrowData.CreatorEmitJoint = "JORightIndexA1"
  arrowData.EmitOffset = nil
  arrowData.PlayerCommand = fromPlayerCommand
  game.Combat.EmitArrow(arrowData)
  ArrowMeterAdjust(-10)
end
function LuaHook_ShootAtReticle(reticleHitData, fromPlayerCommand)
  local reticleHitPosition = reticleHitData.HitPosition
  local reticleHitGameObject = reticleHitData.HitGameObject
  local reticleHitJointId = reticleHitData.HitJointId
  local isEnemyCreature = false
  local arrowData = {}
  local pierce = false
  if global.Player.ReticalTargetGameObjectAndHitPosition ~= nil then
    arrowData.TargetLocation = reticleHitPosition
    if reticleHitGameObject ~= nil and reticleHitGameObject:HasMarker("SonTarget") then
      pierce = true
    end
  elseif reticleHitGameObject ~= nil then
    arrowData.Target = reticleHitGameObject
    arrowData.TargetJointId = reticleHitJointId
    arrowData.JointSpaceTargetPosition = reticleHitPosition
    local reticleCreature = reticleHitGameObject:GetCreature()
    if reticleCreature ~= nil then
      local son = game.AI.FindSon()
      if reticleCreature ~= son then
        isEnemyCreature = true
        if (reticleCreature:HasMarker("React_Flip") or reticleCreature:HasMarker("React_DefaultAir")) and (son:PickupIsAcquired("SonArmorSpeed01") or son:PickupIsAcquired("SonArmorSpeed02") or son:PickupIsAcquired("SonArmorSpeed03") or son:PickupIsAcquired("SonArmorBonus02") or son:PickupIsAcquired("SonArmorBonus03") or son:PickupIsAcquired("SonArmorBonus04")) then
          son:PickupAcquire("Perk_WeaknessExploitShot")
        end
      end
    end
    if reticleHitGameObject:HasMarker("SonTarget") then
      pierce = true
    end
  else
    arrowData.TargetLocation = reticleHitPosition
  end
  if _G.global.selfAI:PickupIsAcquired("SonArrow_Light") then
    if pierce then
      arrowData.Tweak = "ARR_ARROW_LIGHT_LUA_PIERCE_ENVIRONMENT"
    else
      if isEnemyCreature then
        arrowData.Tweak = "ARR_ARROW_LIGHT_LUA"
      else
        arrowData.Tweak = "ARR_ARROW_LIGHT_LUA_NOTARGET"
      end
      if game.Wallets.HasResource("HERO", "SonPerk_LightArrow_WeakenDefense") and isEnemyCreature then
        arrowData.Tweak = "ARR_ARROW_LIGHT_LUA_WEAKEN"
      end
    end
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    if pierce then
      arrowData.Tweak = "ARR_ARROW_SHOCK_LUA_PIERCE_ENVIRONMENT"
    elseif isEnemyCreature then
      arrowData.Tweak = "ARR_ARROW_SHOCK_LUA"
    else
      arrowData.Tweak = "ARR_ARROW_SHOCK_LUA_NOTARGET"
    end
  elseif pierce then
    arrowData.Tweak = "ARR_ARROW_LUA_PIERCE_ENVIRONMENT"
  elseif isEnemyCreature then
    arrowData.Tweak = "ARR_ARROW_LUA"
  else
    arrowData.Tweak = "ARR_ARROW_LUA_NOTARGET"
  end
  if _G.global.selfAI:HasMarker("SonCrystalThrowMode") then
    if pierce then
      arrowData.Tweak = "ARR_ARROW_VIBRATECRYSTAL_LUA_PIERCE_ENVIRONMENT"
    else
      arrowData.Tweak = "ARR_ARROW_VIBRATECRYSTAL_LUA_NOTARGET"
    end
  end
  arrowData.Creator = _G.global.selfAI
  arrowData.CreatorEmitJoint = "JORightIndexA1"
  if global.Player.ReticalTargetGameObjectAndHitPosition ~= nil then
    arrowData.Target = nil
  end
  arrowData.EmitOffset = nil
  arrowData.PlayerCommand = fromPlayerCommand
  game.Combat.EmitArrow(arrowData)
  ArrowMeterAdjust(-10)
end
function ArrowMeterAdjust(value)
  local meterValue = _G.global.selfAI:MeterGetValue("Son_ArrowCount")
  meterValue = meterValue + value
  if meterValue < 0 then
    meterValue = 0
  end
  _G.global.selfAI:MeterSetValue("Son_ArrowCount", meterValue)
end
function LuaHook_ShootAtCombatTarget(fromPlayerCommand)
  local totalShots = 1
  local son = game.AI.FindSon()
  if _G.constants.targetOfPlayer ~= nil and (_G.constants.targetOfPlayer:HasMarker("React_Flip") or _G.constants.targetOfPlayer:HasMarker("React_Air")) and (son:PickupIsAcquired("SonArmorSpeed01") or son:PickupIsAcquired("SonArmorSpeed02") or son:PickupIsAcquired("SonArmorSpeed03") or son:PickupIsAcquired("SonArmorBonus02") or son:PickupIsAcquired("SonArmorBonus03") or son:PickupIsAcquired("SonArmorBonus04")) then
    son:PickupAcquire("Perk_WeaknessExploitShot")
  end
  if _G.global.selfAI:HasMarker("SonCrystalThrowMode") then
    local arrowData = {}
    arrowData.Target = _G.constants.targetOfPlayer
    arrowData.Tweak = "ARR_ARROW_VIBRATECRYSTAL_LUA"
    arrowData.Creator = _G.global.selfAI
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.EmitOffset = nil
    arrowData.PlayerCommand = fromPlayerCommand
    game.Combat.EmitArrow(arrowData)
    if not _G.global.warpTargetRot then
      _G.global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
    end
    _G.global.warpTargetRot.ValueVec = _G.constants.targetOfPlayer.WorldPosition - _G.global.selfAI.WorldPosition
    ArrowMeterAdjust(-10)
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Light") then
    if totalShots == 1 then
      local arrowData = {}
      arrowData.Target = _G.constants.targetOfPlayer
      arrowData.Tweak = "ARR_ARROW_LIGHT_LUA"
      if game.Wallets.HasResource("HERO", "SonPerk_LightArrow_WeakenDefense") then
        arrowData.Tweak = "ARR_ARROW_LIGHT_LUA_WEAKEN"
      end
      arrowData.Creator = _G.global.selfAI
      arrowData.CreatorEmitJoint = "JORightIndexA1"
      arrowData.EmitOffset = nil
      arrowData.PlayerCommand = fromPlayerCommand
      game.Combat.EmitArrow(arrowData)
      if not _G.global.warpTargetRot then
        _G.global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
      end
      _G.global.warpTargetRot.ValueVec = _G.constants.targetOfPlayer.WorldPosition - _G.global.selfAI.WorldPosition
      ArrowMeterAdjust(-10)
    end
  elseif _G.global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    if totalShots == 1 then
      local arrowData = {}
      arrowData.Tweak = "ARR_ARROW_SHOCK_LUA"
      arrowData.Target = _G.constants.targetOfPlayer
      arrowData.Creator = _G.global.selfAI
      arrowData.CreatorEmitJoint = "JORightIndexA1"
      arrowData.EmitOffset = nil
      arrowData.PlayerCommand = fromPlayerCommand
      game.Combat.EmitArrow(arrowData)
      if not _G.global.warpTargetRot then
        _G.global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
      end
      _G.global.warpTargetRot.ValueVec = _G.constants.targetOfPlayer.WorldPosition - _G.global.selfAI.WorldPosition
      ArrowMeterAdjust(-10)
    end
  else
    local arrowData = {}
    arrowData.Tweak = "ARR_ARROW_LUA"
    arrowData.Target = _G.constants.targetOfPlayer
    arrowData.Creator = _G.global.selfAI
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.EmitOffset = nil
    arrowData.PlayerCommand = fromPlayerCommand
    game.Combat.EmitArrow(arrowData)
    ArrowMeterAdjust(-10)
    if not _G.global.warpTargetRot then
      _G.global.warpTargetRot = game.AI.FindSon():GetAnimDriver("WarpTargetRot")
    end
    _G.global.warpTargetRot.ValueVec = _G.constants.targetOfPlayer.WorldPosition - _G.global.selfAI.WorldPosition
  end
end
function LuaHook_ShootAtTrackedObject()
  local sonBB
  local son = game.AI.FindSon()
  sonBB = son:GetPrivateBlackboard()
  sonBB:Set("ArrowType", 0)
  local fakearrow = game.FX.Spawn("LightningArrowToAxe", son.GroundLevel)
  fakearrow:SetWorldPosition(son.WorldPosition + engine.Vector.New(0, 1, 0))
end
function LuaHook_ShootChargedArrow()
  _G.global.ChargedOrRegular = "Charged"
  LuaHook_ShootArrowLogic(false)
end
function LuaHook_ShootNonChargedArrow()
  _G.global.ChargedOrRegular = "Regular"
  LuaHook_ShootArrowLogic(true)
end
function LuaHook_ShootAtCurrentTarget()
  local son = game.AI.FindSon()
  if son == nil then
    return
  end
  local sonTarget = son:GetTargetCreature()
  if sonTarget ~= nil then
    local arrowData = {}
    arrowData.Tweak = "ARR_ARROW_LUA_VERSION2"
    arrowData.Target = sonTarget
    arrowData.Creator = _G.global.selfAI
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.EmitOffset = nil
    arrowData.PlayerCommand = false
    game.Combat.EmitArrow(arrowData)
  else
    LuaHook_ShootArrow(false)
  end
end
function LuaHook_ShootArrow(fromPlayerCommand)
  local targetOfPlayer = helper.returnBestPlayerTargetCreature(_G.global, true)
  _G.constants.targetOfPlayer = targetOfPlayer
  _G.global.selfAI:SetCombatTarget(_G.constants.targetOfPlayer)
  local reticleHitPosition, reticleHitGameObject, reticleHitJointId
  reticleHitPosition = _G.constants.shootTargetInfo.position
  reticleHitGameObject = _G.constants.shootTargetInfo.target
  reticleHitJointId = _G.constants.shootTargetInfo.targetJoint
  local hitCrystal = reticleHitGameObject ~= nil and reticleHitGameObject:HasMarker("SonTarget")
  local reticleHitCreature
  if reticleHitGameObject ~= nil then
    reticleHitCreature = reticleHitGameObject:GetCreature()
  end
  _G.global.shootTargetType = "reticle"
  if reticleHitCreature ~= nil and reticleHitCreature ~= game.AI.FindSon() or hitCrystal or targetOfPlayer == nil then
    _G.global.shootTargetType = "reticle"
  elseif targetOfPlayer ~= nil then
    _G.global.shootTargetType = "combatTarget"
  end
  if _G.global.shootTargetType == "reticle" then
    local reticleTable = {}
    reticleTable.HitPosition = reticleHitPosition
    reticleTable.HitGameObject = reticleHitGameObject
    reticleTable.HitJointId = reticleHitJointId
    LuaHook_ShootAtReticle(reticleTable, fromPlayerCommand)
  elseif _G.global.shootTargetType == "combatTarget" then
    LuaHook_ShootAtCombatTarget(fromPlayerCommand)
  else
    LuaHook_ShootAtTrackedObject(fromPlayerCommand)
  end
  uiCalls.UI_Event_Son_ShootArrow()
end
function LuaHook_SetTrackingObject(ai, thisObject)
  local sonBB
  local son = game.AI.FindSon()
  sonBB = son:GetPrivateBlackboard()
  if thisObject ~= nil then
    sonBB:Set("PotentialArrowTrackObject", thisObject)
  end
end
function LuaHook_ShootWithAppropriateArrow(ai)
  local sonTarget = ai:GetTargetCreature()
  if sonTarget == nil then
    return
  end
  if sonTarget == game.Player.FindPlayer() then
    return
  end
  local arrowData = {}
  arrowData.Tweak = "ARR_ARROW_LUA_NORMAL"
  arrowData.Target = sonTarget
  arrowData.Creator = _G.global.selfAI
  arrowData.CreatorEmitJoint = "JORightIndexA1"
  arrowData.EmitOffset = nil
  if sonTarget ~= nil then
    local player = game.Player.FindPlayer()
    local distance = (player.WorldPosition - sonTarget.WorldPosition):Length()
    local sondist = (ai.WorldPosition - sonTarget.WorldPosition):Length()
    if distance < 4.5 or 5 < sondist then
      arrowData.Tweak = "ARR_PEPPER_ARROW_JUGGLE"
    end
  end
  if global.emotionState.value == 3 and math.random() > 0.25 then
    if math.random() > 0.5 then
      arrowData.Tweak = "ARR_ARROW_SHOCK_LUA"
    else
      arrowData.Tweak = "ARR_ARROW_LIGHT_LUA"
    end
  end
  arrowData.PlayerCommand = false
  game.Combat.EmitArrow(arrowData)
end
function LuaHook_RemoveTrackingObject()
  local sonBB
  local son = game.AI.FindSon()
  sonBB = son:GetPrivateBlackboard()
  sonBB:Erase("PotentialArrowTrackObject")
end
function GetBehaviorContextTimerMax(global, constants)
  if global.currentContextConfig.OneOffTimerDeviation then
    return global.currentContextConfig.OneOffTimer + math.random(-global.currentContextConfig.OneOffTimerDeviation, global.currentContextConfig.OneOffTimerDeviation)
  else
    return global.currentContextConfig.OneOffTimer
  end
end
function ClearBehaviorContextConfig(ai, delayUntilExit)
  if global.currentContextConfig == nil then
    return
  end
  if ai.ClearContextBehaviorName then
    ai:ClearContextBehaviorName()
  end
  if delayUntilExit and global.currentContextConfig.ExitAnimEvent ~= "" and global.contextBehaviorStage ~= 1 then
    ai:TriggerMoveEvent(global.currentContextConfig.ExitAnimEvent)
    global.markContextBehaviorForDelete = true
  else
    ai:TriggerMoveEvent("kDefaultBehaviorToNav")
    global.currentContextConfig = nil
    global.currentContextName = nil
    if global.queuedContextConfigName ~= nil then
      EnterBehaviorContext(ai, global.queuedContextConfigName)
      global.queuedContextConfigName = nil
    end
  end
end
function EnterBehaviorContextAtIdle(ai, contextName)
  EnterBehaviorContext(ai, contextName)
  if global.currentContextConfig.LoopAnimEvents[0] then
    ai:TriggerMoveEvent(global.currentContextConfig.LoopAnimEvents[0])
  end
end
function EnterBehaviorContext(ai, contextName)
  if not global.currentContextConfig or game.Boat.GetPlayerBoat() ~= nil then
    if ai.GetContextConfigByName then
      global.currentContextConfig = ai:GetContextConfigByName(contextName)
      global.contextBehaviorStage = 1
      global.currentContextName = contextName
      if ai.SetContextBehaviorName then
        ai:SetContextBehaviorName(contextName)
      end
    end
  else
    global.queuedContextConfigName = contextName
    ClearBehaviorContext(ai, true)
  end
end
function TransitionBehaviorContext(ai, fromLeftSide)
  if not global.currentContextConfig then
    return
  end
  if global.currentContextConfig and global.currentContextConfig.TransitionToComplimentEvent then
    ai:TriggerMoveEvent(global.currentContextConfig.TransitionToComplimentEvent)
  end
end
function OnContextBehaviorBanterEnded()
  if global.currentContextConfig and global.currentContextConfig.ExitBanterModeEvent then
    local son = game.AI.FindSon()
    son:TriggerMoveEvent(global.currentContextConfig.ExitBanterModeEvent)
  end
end
function TriggerBehaviorContextBanterMode(ai, banterName)
  game.Audio.PlayBanter(banterName, OnContextBehaviorBanterEnded)
end
function ClearBehaviorContext(ai)
  ClearBehaviorContextConfig(ai, true)
end
function ClearThisBehaviorContextIfActive(ai, contextName)
  if global.currentContextConfig and global.currentContextConfig.BehaviorContextName == contextName then
    ClearBehaviorContextConfig(ai, true)
  end
end
function SetOneOffTimerActivation(ai, timerOn)
  global.oneOffTimerActive = timerOn
end
function BehaviorContextDebug(ai, global, constants)
  local debugtable = {}
  local tableColor = engine.Vector.New(1, 0.12, 0.1)
  local pausedString = ""
  if not DL.PositionVisibleOnCamera(ai:GetWorldPosition(), 0) and not global.currentContextConfig.OneOffsPlayOffScreen then
    pausedString = "[TIMER PAUSED - SON OUT OF VIEW]"
  elseif global.contextBehaviorStage ~= 3 then
    pausedString = "[TIMER PAUSED - NOT IN LOOP]"
  elseif not global.oneOffTimerActive then
    pausedString = "[TIMER PAUSED - SET_FROM_SCRIPT]"
  end
  table.insert(debugtable, {"--", "--"})
  table.insert(debugtable, {
    "Current Behavior Context Config:",
    global.currentContextConfig.BehaviorContextName
  })
  table.insert(debugtable, {
    "Current Behavior Context Stage:",
    global.ContextBehaviorStageNames[global.contextBehaviorStage]
  })
  table.insert(debugtable, {
    "Remaining Timer to Next One Off",
    pausedString .. constants.nextOneOffTimerMax - constants.oneOffTimer
  })
  table.insert(debugtable, {
    "Current Anim Progress Percent",
    ai:GetActiveMovePercent() * 100
  })
  table.insert(debugtable, {
    "Next One-Off Animation",
    global.contextBehaviorNextOneOff
  })
  table.insert(debugtable, {"--", "--"})
  local titleName = "Behavior Context Debug Table"
  debugtable.Title = titleName
  debugtable.TitleColor = tableColor
  debugtable.TitleAlpha = 255
  debugtable.X = 100
  debugtable.Y = 10
  engine.DrawDebugTable(debugtable)
end
local SetupAnimDrivers, DetermineDirection, DetermineDistance
function SetupAnimDrivers()
  local referenceTarget = game.Player.FindPlayer()
  DetermineDirection(referenceTarget)
  DetermineDistance(referenceTarget)
end
function DetermineDirection(referenceTarget)
  local puppet = global.selfAI
  local animDriver = puppet:GetAnimDriver("ContextAction_Direction")
  local animDriver6way = puppet:GetAnimDriver("ContextAction_SixDirection")
  local directionIndex = 1
  local directionSixWayIndex = 1
  if referenceTarget ~= nil then
    local forward = puppet:GetWorldForward()
    local to = referenceTarget:GetWorldPosition() - puppet:GetWorldPosition()
    local angle = game.AIUtil.AngleBetweenXZ(forward, to)
    if animDriver then
      if -60 <= angle and angle <= 60 then
        directionIndex = 1
      elseif angle <= -60 and -120 <= angle then
        directionIndex = 4
      elseif 60 <= angle and angle <= 120 then
        directionIndex = 2
      else
        directionIndex = 3
      end
      animDriver.Value = directionIndex
    end
    if animDriver6way then
      if 0 <= angle and angle < 60 then
        directionSixWayIndex = 1
      elseif 60 <= angle and angle < 100 then
        directionSixWayIndex = 2
      elseif 100 <= angle and angle <= 180 then
        directionSixWayIndex = 3
      elseif -180 <= angle and angle < -100 then
        directionSixWayIndex = 4
      elseif -100 <= angle and angle < -60 then
        directionSixWayIndex = 5
      else
        directionSixWayIndex = 1
      end
      animDriver6way.Value = directionSixWayIndex
    end
    print("Angle: ", angle, " Direction: ", directionIndex, " Direction 6 way: ", directionSixWayIndex)
  end
end
function DetermineDistance(referenceTarget)
  local puppet = global.selfAI
  local animDriver = puppet:GetAnimDriver("ContextAction_Distance")
  local distance = (referenceTarget:GetWorldPosition() - puppet:GetWorldPosition()):Length()
  local distanceIndex = 1
  if animDriver then
    if 0 <= distance and distance < 10 then
      distanceIndex = 1
    elseif 10 <= distance and distance < 25 then
      distanceIndex = 2
    else
      distanceIndex = 3
    end
    animDriver.Value = distanceIndex
  end
  print("Distance: ", distance, " CA_Distance: ", distanceIndex)
end
function DestroyQueuedContextBehavior(ai)
  global.markContextBehaviorForDelete = false
  global.currentContextConfig = nil
  global.currentContextName = nil
  if global.queuedContextConfigName ~= nil then
    EnterBehaviorContext(ai, global.queuedContextConfigName)
    global.queuedContextConfigName = nil
  end
end
function RunSonContextBehavior(ai, global, constants)
  if global.contextBehaviorStage == 4 then
    global.markContextBehaviorForDelete = false
  end
  if global.markContextBehaviorForDelete == true then
    if not ai:CheckDecision("tweak_Decision_OnCamera") then
      DestroyQueuedContextBehavior(ai)
      ai:TriggerMoveEvent("kDefaultBehaviorToNav")
    end
    return
  end
  if global.contextBehaviorNextOneOff == nil then
    local index = 0
    if global.currentContextConfig.OneOffWeights then
      local sum = 0
      for x = 0, global.currentContextConfig.OneOffWeightCount - 1 do
        local i = global.currentContextConfig.OneOffWeights[x]
        sum = sum + i
      end
      local randomValue = math.random(0, sum)
      for x = 0, global.currentContextConfig.OneOffWeightCount - 1 do
        local i = global.currentContextConfig.OneOffWeights[x]
        randomValue = randomValue - i
        index = x
        if randomValue <= 0 then
          break
        end
      end
    else
      index = math.random(0, global.currentContextConfig.OneOffAnimEventCount)
    end
    global.contextBehaviorNextOneOff = global.currentContextConfig.OneOffAnimEvents[index]
    global.contextBehaviorNextIndex = index
  end
  if 0 < global.currentContextConfig.OneOffAnimEventCount and 0 > constants.nextOneOffTimerMax then
    constants.nextOneOffTimerMax = GetBehaviorContextTimerMax(global, constants)
  end
  if global.contextBehaviorStage == 1 then
    if global.currentContextConfig.EnterAnimEvent ~= "" then
      ai:TriggerMoveEvent(global.currentContextConfig.EnterAnimEvent)
      global.contextBehaviorStage = 2
    elseif 0 < global.currentContextConfig.LoopAnimEventCount then
      ai:TriggerMoveEvent(global.currentContextConfig.LoopAnimEvents[0])
      global.contextBehaviorStage = 3
    end
  end
  if gVFSDebugShowContextBehavior.value then
    BehaviorContextDebug(ai, global, constants)
  end
  if global.contextBehaviorStage == 3 and 0 < global.currentContextConfig.OneOffAnimEventCount and global.oneOffTimerActive then
    local sonOnScreen = DL.PositionVisibleOnCamera(ai:GetWorldPosition(), 0)
    if sonOnScreen or global.currentContextConfig.OneOffsPlayOffScreen then
      if constants.oneOffTimer >= constants.nextOneOffTimerMax then
        SetupAnimDrivers()
        ai:TriggerMoveEvent(global.contextBehaviorNextOneOff)
        constants.oneOffTimer = 0
        constants.nextOneOffTimerMax = GetBehaviorContextTimerMax(global, constants)
        global.ContextBehaviorCurrentOneOff = global.contextBehaviorNextOneOff
        global.ContextBehaviorCurrentIndex = global.contextBehaviorNextIndex
        global.contextBehaviorNextOneOff = nil
        global.contextBehaviorNextIndex = -1
      else
        constants.oneOffTimer = constants.oneOffTimer + ai:GetUnitTime()
      end
    end
  end
end
function LuaHook_ShootArcShot()
  local sonBB
  local son = game.AI.FindSon()
  sonBB = son:GetPrivateBlackboard()
  local targetOfPlayer = helper.returnBestPlayerTargetCreature(_G.global, true)
  _G.constants.targetOfPlayer = targetOfPlayer
  _G.global.selfAI:SetCombatTarget(_G.constants.targetOfPlayer)
  if targetOfPlayer == nil then
    sonBB:Erase("ArrowTrackObject")
    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")
    }
    local raycastFinalPos = pos + dir * 20
    local hit = game.World.RaycastCollision(pos, raycastFinalPos, collisionParams)
    if hit ~= nil then
      raycastFinalPos = hit.Position
    end
    sonBB:Set("ArrowTrackLocation", raycastFinalPos)
  else
    sonBB:Set("ArrowTrackObject", targetOfPlayer)
  end
  local fakearrow = game.FX.Spawn("ArcShot_Light_Shot", son.GroundLevel)
  fakearrow:SetWorldPosition(son:GetWorldJointPosition(son:GetJointIndex("JORightIndexA1")))
end
function LuaHook_ShootSpreadShot()
  local son = game.AI.FindSon()
  local enemiesOnScreen = DL.GetAllEnemiesOnScreen(true)
  local arrowtype = "ARR_ARROW_LIGHT_LUA"
  if global.selfAI:PickupIsAcquired("SonArrow_Shock") then
    arrowtype = "ARR_ARROW_SHOCK_LUA"
  end
  for _, i in ipairs(enemiesOnScreen) do
    local arrowData = {}
    arrowData.Tweak = arrowtype
    arrowData.Creator = son
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.Target = i
    game.Combat.EmitArrow(arrowData)
  end
end
function LuaHook_ShootPowerShot()
  local targetOfPlayer = helper.returnBestPlayerTargetCreature(_G.global, true)
  local son = game.AI.FindSon()
  if targetOfPlayer ~= nil then
    local arrowData = {}
    arrowData.Tweak = "ARR_ARROW_POWER_SHOT_LUA"
    arrowData.Creator = son
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.Target = targetOfPlayer
    game.Combat.EmitArrow(arrowData)
  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")
    }
    local raycastFinalPos = pos + dir * 30
    local hit = game.World.RaycastCollision(pos, raycastFinalPos, collisionParams)
    if hit ~= nil then
      raycastFinalPos = hit.Position
    end
    local arrowData = {}
    arrowData.Tweak = "ARR_ARROW_POWER_SHOT_LUA_NOTARGET"
    arrowData.Creator = son
    arrowData.CreatorEmitJoint = "JORightIndexA1"
    arrowData.Target = nil
    arrowData.TargetLocation = raycastFinalPos
    game.Combat.EmitArrow(arrowData)
  end
end
function LuaHook_ShootRainShot()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("RainShot_Shock", sonAI.GroundLevel)
  fakearrow:SetWorldPosition(sonAI:GetWorldJointPosition(sonAI:GetJointIndex("JORightIndexA1")))
end
function LuaHook_SummonFenrir()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonFen", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI:GetWorldJointPosition(sonAI:GetJointIndex("JOArrowWeapon1")))
end
function LuaHook_SummonRata()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonRata", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition)
end
function LuaHook_SummonDragon()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonDragon", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition - engine.Vector.New(0, 100, 0))
end
function LuaHook_SummonSouls()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonSouls", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition - engine.Vector.New(0, 100, 0))
end
function LuaHook_SummonDeer()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonDeer", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition)
end
function LuaHook_SummonBear()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonBear", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition)
end
function LuaHook_SummonBoar()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonBoar", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition)
end
function LuaHook_SummonBird()
  local sonAI = game.AI.FindSon()
  local fakearrow = game.FX.Spawn("SummonBird", game.Level.GetPermLevel())
  fakearrow:SetWorldPosition(sonAI.WorldPosition)
end
function LuaHookDecision_IsInMagicElevator(ai, data)
  local levelName = ""
  if ai.GroundLevel ~= nil then
    levelName = ai.GroundLevel.Name
  end
  return levelName == "WAD_Stn430_HammerClimb"
end
local CheckWarpPositionAgainstCurrentPosition = function(warpPosition, currentPosition, targetPosition)
  local player = game.Player.FindPlayer()
  local targetToPlayer = (player.WorldPosition - targetPosition):Normalized()
  local targetToCurrent = (currentPosition - targetPosition):Normalized()
  local targetToWarp = (warpPosition - targetPosition):Normalized()
  local currentTest = targetToPlayer:Dot(targetToCurrent)
  local warpTest = targetToPlayer:Dot(targetToWarp)
  local raycastHit = game.World.RaycastCollision(targetPosition + engine.Vector.New(0, 0.5, 0), warpPosition + engine.Vector.New(0, 0.5, 0), {
    SourceGameObject = game.AI.FindSon(),
    EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
  })
  if raycastHit ~= nil then
    return false
  end
  local pathToHeroAvailable = false
  local son = game.AI.FindSon()
  local playerLocation = game.NavMesh.ClosestLocation(global.Player.WorldPosition, son)
  if playerLocation ~= nil and 0.5 > global.Player.WorldPosition:Distance(playerLocation.Position) then
    local bestLocation = game.NavMesh.ClosestLocation(warpPosition, son)
    if bestLocation ~= nil then
      local pathToPlayer = game.NavMesh.FindPath({StartLocation = bestLocation, EndLocation = playerLocation})
      if pathToPlayer ~= nil and 0.5 > pathToPlayer.PathArray[pathToPlayer.PathCount]:Distance(playerLocation.Position) then
        raycastHit = nil
        raycastHit = game.World.SpherecastCollision(global.Player.WorldPosition + engine.Vector.New(0, 0.5, 0), bestLocation.Position + engine.Vector.New(0, 0.5, 0), 0.3, {
          SourceGameObject = global.Player,
          EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
        })
        if raycastHit == nil then
          pathToHeroAvailable = true
        end
      end
    end
  end
  if currentTest > warpTest and pathToHeroAvailable then
    return true
  end
  return false
end
function TeleportToSafetyCheck(ai, global, constants)
  local unitTime = ai:GetUnitTime()
  if ai:IsPlayingMove("MOV_Run") then
    local locomotionInfo = ai:GetLocomotionInfo()
    if locomotionInfo.TargetSpeed > 2 and ai:GetVelocity():Length() <= 0.75 then
      constants.InRunDuration = constants.InRunDuration + unitTime
    else
      constants.InRunDuration = 0
      constants.StuckInRun = false
    end
    if constants.InRunDuration > 10 then
      constants.StuckInRun = true
    end
  else
    constants.InRunDuration = 0
    constants.StuckInRun = false
  end
  constants.TeleportToSafetyTimer = constants.TeleportToSafetyTimer - unitTime
  if 0 >= constants.TeleportToSafetyTimer then
    local levelName = ""
    if global.Player.GroundLevel ~= nil then
      levelName = global.Player.GroundLevel.Name
    end
    if levelName ~= "WAD_Stn430_HammerClimb" and levelName ~= "WAD_Peak380_BigElevator" and levelName ~= "WAD_HelR300_HelShip" then
      if gVFSEnableTeleportLogic.value then
        TeleportToSafety_GlobalCase(ai, global, constants)
      end
    else
      ElevatorTeleportLogic(ai, global, constants)
    end
  end
end
function ElevatorTeleportLogic(ai, global, constants)
  local teleportNow = false
  if math.abs(global.Player.WorldPosition.y - ai.WorldPosition.y) > 8 then
    teleportNow = true
  end
  if teleportNow then
    if ai:IsDoingSyncMove() == false and ai.OwnedPOI == nil and not ai:CheckDecision("tweak_Decision_OnCamera") then
      local bestLocation = game.NavMesh.FindClosestReachableLocation(global.Player.WorldPosition, ai.WorldPosition)
      if bestLocation ~= nil and game.Camera.GetViewPenetration(bestLocation.Position, 0, 0) <= 0 then
        ai:Warp(bestLocation.Position, (global.Player.WorldPosition - bestLocation.Position):Normalized())
        constants.TeleportToSafetyTimer = 1
        return
      end
    end
  else
    constants.TeleportToSafetyTimer = 1
  end
end
function TeleportToSafety_GlobalCase(ai, global, constants)
  local unitTime = ai:GetUnitTime()
  local onNavMesh = game.NavMesh.IsOnNavMesh(ai.WorldPosition, ai)
  local distanceToHero = ai.WorldPosition:Distance(global.Player.WorldPosition)
  local teleportNow = false
  if (not (not (80 < distanceToHero) or constants.StandGround) or onNavMesh == false and 20 < distanceToHero or constants.StuckInRun) and not ai:HasMarker("DisableCommandShot") and not ai:HasMarker("TweakPOIResOnly") then
    constants.InBadLocation = constants.InBadLocation - unitTime
    if 80 < distanceToHero or constants.StuckInRun then
      teleportNow = true
    else
      local locomotionInfo = ai:GetLocomotionInfo()
      if locomotionInfo.PathLength == nil or locomotionInfo.PathLength >= constants.previousPathLength then
        teleportNow = true
      else
        constants.previousPathLength = locomotionInfo.PathLength
        if constants.InBadLocation <= 0 then
          teleportNow = true
        end
      end
    end
    if teleportNow and ai:IsDoingSyncMove() == false and ai.OwnedPOI == nil and ai:IsAvailableForSync() and ai:IsAvailableInLevel() and (constants.awarenessState == constants.FollowPlayer or constants.awarenessState == constants.UrgentMoveAway or constants.awarenessState == constants.MoveAway) then
      local playerLocation = game.NavMesh.ClosestLocation(global.Player.WorldPosition, ai)
      if playerLocation ~= nil and global.Player.WorldPosition:Distance(playerLocation.Position) < 0.5 and not ai:CheckDecision("tweak_Decision_OnCamera") then
        local compassLocation = game.Compass.GetNearbyMarkerBiasBehindPlayer()
        if compassLocation ~= nil then
          local bestLocation = game.NavMesh.ClosestLocation(compassLocation, ai)
          if bestLocation ~= nil then
            local pathToPlayer = game.NavMesh.FindPath({StartLocation = bestLocation, EndLocation = playerLocation})
            if pathToPlayer ~= nil and 0.5 > pathToPlayer.PathArray[pathToPlayer.PathCount]:Distance(playerLocation.Position) then
              local lengthOfPath = pathToPlayer.PathLength
              local warpDistanceFromPlayer = 8
              if lengthOfPath <= warpDistanceFromPlayer then
                lengthOfPath = warpDistanceFromPlayer + 1
              end
              local percentageToWarp = 1 - warpDistanceFromPlayer / lengthOfPath
              local pointOnPath = game.NavMesh.PointOnPath(pathToPlayer, percentageToWarp)
              if pointOnPath ~= nil and 0 >= game.Camera.GetViewPenetration(pointOnPath.Position, 0, 0) then
                local raycastHit = game.World.SpherecastCollision(global.Player.WorldPosition + engine.Vector.New(0, 0.5, 0), pointOnPath.Position + engine.Vector.New(0, 0.5, 0), 0.3, {
                  SourceGameObject = global.Player,
                  EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
                })
                if raycastHit == nil then
                  ai:Warp(pointOnPath.Position, (global.Player.WorldPosition - pointOnPath.Position):Normalized(), true)
                end
                constants.TeleportToSafetyTimer = 1
                return
              end
            end
          end
        end
      end
    end
    constants.TeleportToSafetyTimer = 1
  else
    constants.previousPathLength = 999
    constants.TeleportToSafetyTimer = 1
  end
end
function LuaHook_WarpToPosition()
  local son = game.AI.FindSon()
  if son.GroundLevel ~= nil and (son.GroundLevel.Name == "WAD_HelR300_HelShip" or son.GroundLevel.Name == "WAD_Stn430_HammerClimb" or son.GroundLevel.Name == "WAD_Peak380_BigElevator") then
    return
  end
  if son.GetCloseOffscreenPosition == nil then
    return
  end
  if son:CheckDecision("tweak_Decision_OnCamera") then
    return
  end
  local sonTarget = _G.constants.meleeTarget
  if sonTarget == nil then
    return
  end
  local rotation = (sonTarget.WorldPosition - son.WorldPosition):Normalized()
  local params = {
    targetPosition = sonTarget.WorldPosition,
    distanceFromCamera = 5,
    fallbackPosition = son.WorldPosition,
    useAIPositionForTesting = true,
    cameraOffset = engine.Vector.New(0, 0, 0)
  }
  local position = son:GetCloseOffscreenPosition(params)
  local shouldWarp = CheckWarpPositionAgainstCurrentPosition(position, son.WorldPosition, sonTarget.WorldPosition)
  if shouldWarp then
    son:Warp(position, rotation, false)
  end
end
function LuaHookDecision_TargetInSync()
  local sonAI = game.AI.FindSon()
  if sonAI == nil then
    return false
  end
  local sonTarget = sonAI:GetTargetCreature()
  if sonTarget ~= nil then
    if sonTarget:IsDoingSyncMove() then
      return true
    end
    local sonPos = sonAI.WorldPosition
    local targetPos = sonTarget.WorldPosition
    if (sonPos - targetPos):Length() < 4 then
      local offset = engine.Vector.New(0, 0.5, 0)
      local endLocation = targetPos + engine.Vector.New(0, 0.5, 0)
      local raycastHit = game.World.RaycastCollision(sonPos + offset, endLocation, {
        SourceGameObject = sonAI,
        EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
      })
      if raycastHit ~= nil then
        return true
      end
    end
  end
  return false
end
function LuaHook_SetBowOutStatus(ai, bowOutStatus)
  global.bowOut = bowOutStatus
end
function LuaHook_PostUpExitCanFire(ai)
  constants.doingPostUpAutonomousShot = false
end
function LuaHook_ShouldTackle(ai, data)
  local target = ai:GetTargetCreature()
  if target == nil then
    return data:FindOutcomeBranchesEntry("DoNothing")
  end
  if (target.WorldPosition - ai.WorldPosition):Length() > 2.5 then
    return data:FindOutcomeBranchesEntry("DoNothing")
  end
  local inCone = game.AIUtil.IntersectPointCone(ai.WorldPosition, target.WorldPosition, (target.WorldPosition - _G.global.Player.WorldPosition):Normalized(), 40, 15)
  if inCone and helper.IsMountable(DL.ReturnStringID(target)) then
    return data:FindOutcomeBranchesEntry("Grab")
  end
  return data:FindOutcomeBranchesEntry("Tackle")
end
function LuaHook_ShouldGrabJotunn(ai, data)
  local target = ai:GetTargetCreature()
  if target == nil then
    return data:FindOutcomeBranchesEntry("CancelOut")
  end
  if target:HasMarker("DoNotGrab") then
    return data:FindOutcomeBranchesEntry("CancelOut")
  end
  if target:PickupIsAcquired("HealthThreshold") and target:PickupGetStage("HealthThreshold") >= 2 then
    return data:FindOutcomeBranchesEntry("CancelOut")
  end
  if DL.CheckCreatureContext(target:GetContext(), "REACT_CRUMPLE") then
    return data:FindOutcomeBranchesEntry("CancelOut")
  end
  if (target.WorldPosition - ai.WorldPosition):Length() > 2.5 then
    return data:FindOutcomeBranchesEntry("DoNothing")
  end
  local inFront = game.AIUtil.IntersectPointCone(ai.WorldPosition, target.WorldPosition, target:GetWorldForward(), 90, 15)
  if inFront then
    return data:FindOutcomeBranchesEntry("CancelOut")
  end
  return data:FindOutcomeBranchesEntry("Grab")
end
function LuaHook_InRageMode(ai, data)
  if _G.global.emotionState.value == 3 then
    return data:FindOutcomeBranchesEntry("InRageMode")
  end
  return data:FindOutcomeBranchesEntry("NotInRageMode")
end
function LuaHook_BeatdownBranch(ai, data)
  local target = ai:GetTargetCreature()
  if target == nil then
    return data:FindOutcomeBranchesEntry("none")
  end
  local player = game.Player.FindPlayer()
  if ai:CheckDecision("tweak_Decision_OnCamera") == false or (player.WorldPosition - ai.WorldPosition):Length() > 10 then
    return data:FindOutcomeBranchesEntry("none")
  end
  if target:HasMeter("StunState") and target:MeterGetValue("StunState") > 30 or _G.global.emotionState.value == 3 then
    return data:FindOutcomeBranchesEntry("Beatdown")
  end
  return data:FindOutcomeBranchesEntry("none")
end
function LuaHook_TauntAppropriate(ai, data)
  local target = ai:GetTargetCreature()
  if target == nil then
    return data:FindOutcomeBranchesEntry("No")
  end
  local enemiesNearby = helper.FindLivingEnemiesExludeMarked(ai, 20, "DoNotEvaluate")
  if target:HasMeter("StunState") and target:MeterGetValue("StunState") >= 100 and #enemiesNearby == 1 or _G.global.emotionState.value == 3 then
    return data:FindOutcomeBranchesEntry("Yes")
  end
  return data:FindOutcomeBranchesEntry("No")
end
function LuaHook_InStrafe(ai, data)
  if _G.constants.strafe == true then
    return data:FindOutcomeBranchesEntry("InStrafe")
  end
  return data:FindOutcomeBranchesEntry("NotInStrafe")
end
function OnRequestTagTeamHook(go)
  local targetOfPlayer = helper.getPlayerTarget()
  local canTagTeam = false
  if _G.global.combatTarget ~= nil and targetOfPlayer ~= nil and targetOfPlayer == _G.global.combatTarget then
    local result = DL.GetObjectARelativePositionToObjectB(go, game.Player.FindPlayer())
    if result == "objBack" then
      canTagTeam = true
    end
  end
  engine.SendHook("OnResponseTagTeamHook", game.Player.FindPlayer(), canTagTeam)
end
function LuaHook_TargetTooCloseToHero(ai, data)
  local target = ai:GetTargetCreature()
  local player = game.Player.FindPlayer()
  local heroTargetCreature = player:GetTargetCreature()
  if target ~= nil then
    local distance = (target.WorldPosition - player.WorldPosition):Length()
    if distance < 5.5 or heroTargetCreature == target then
      return data:FindOutcomeBranchesEntry("TooClose")
    end
  end
end
function LuaHook_TooCloseToHero(ai, data)
  local distance = (ai.WorldPosition - game.Player.FindPlayer().WorldPosition):Length()
  if distance < 4.5 then
    return data:FindOutcomeBranchesEntry("TooClose")
  end
end
function LuaHook_JumpToNextTarget(ai, data)
  local enemies = DL.FindLivingEnemies(ai, 3.5, true)
  local sonTarget = ai:GetTargetCreature()
  for _, i in ipairs(enemies) do
    if sonTarget ~= i and i:CheckDecision("tweak_Decision_OnCamera") and not i:HasMarker("React_DefaultAir") and not i:HasMarker("React_Flip") and not i:HasMarker("React_Crumple") and not i:HasMarker("React_WallBounce") and not i:HasMarker("React_Wall") and not i:HasMarker("React_WallPin") and not i:HasMarker("React_Frozen") and not i:HasMarker("React_Flyback") and not i:HasMarker("React_GroundSlide") and not i:HasMarker("React_SuperFlyback") and not i:HasMarker("React_Lie_Stomach") and helper.IsMountable(DL.ReturnStringID(i)) then
      _G.constants.reactionTarget = i
      _G.global.sTargetMode = "TransferTarget"
      return data:FindOutcomeBranchesEntry("ValidTarget")
    end
  end
  return data:FindOutcomeBranchesEntry("NoValidTarget")
end
function LuaHookDecision_TargetCloseEnough(ai, data)
  local i = ai:GetTargetCreature()
  if i ~= nil then
    return not i:HasMarker("React_DefaultAir") and not i:HasMarker("React_Flip") and not i:HasMarker("React_Crumple") and not i:HasMarker("React_WallBounce") and not i:HasMarker("React_Wall") and not i:HasMarker("React_WallPin") and not i:HasMarker("React_Frozen") and not i:HasMarker("React_Flyback") and not i:HasMarker("React_GroundSlide") and not i:HasMarker("React_SuperFlyback") and not i:HasMarker("React_Lie_Stomach") and i:IsDoingSyncMove() == false
  end
  return false
end
function LuaHookWhichTargetQuadrantAmIIn(ai, data)
  if _G.global.combatTarget then
    return data:FindOutcomeBranchesEntry(DL.GetObjectARelativePositionToObjectB(ai, _G.global.combatTarget))
  end
  return data:FindOutcomeBranchesEntry("none")
end
function LuaHookDecision_IsUnavailableForCombat(ai, data)
  if ai:IsAvailableForCombat() then
    return data:FindOutcomeBranchesEntry("Nothing")
  end
  return data:FindOutcomeBranchesEntry("NoReaction")
end
function LuaHookDecision_RunStopExploreIdle(ai, data)
  if global.currentContextConfig and ai:IsContextBehaviorNameEqualTo("WAIT_AND_EXPLORE_BEHAVIOR_CONTEXT_CONFIG") then
    return data:FindOutcomeBranchesEntry("MOV_ExploreIdle")
  else
    return data:FindOutcomeBranchesEntry("MOV_Stand")
  end
end
function LuaHookIsWorldInCombat(ai, data)
  if game.Combat.GetCombatStatus() then
    return data:FindOutcomeBranchesEntry("InCombat")
  end
  return data:FindOutcomeBranchesEntry("NotInCombat")
end
function LuaHookInCombatCheck(ai, data)
  if not global.bowSheathed then
    return data:FindOutcomeBranchesEntry("InCombat")
  end
  return data:FindOutcomeBranchesEntry("NotInCombat")
end
function LuaHook_DirectionOfClosestEnemy(ai, branch)
  local closestEnemy = helper.FindClosestLivingEnemyExcludeMarker(ai, 40, "DoNotEvaluate")
  if closestEnemy == nil then
    return branch:FindOutcomeBranchesEntry("Forward")
  else
    local forward = ai:GetWorldForward()
    local sonToEnemy = closestEnemy:GetWorldPosition() - ai:GetWorldPosition()
    local angle = game.AIUtil.AngleBetweenXZ(forward, sonToEnemy)
    if -60 <= angle and angle <= 60 then
      return branch:FindOutcomeBranchesEntry("Forward")
    elseif angle <= -60 and -180 <= angle then
      return branch:FindOutcomeBranchesEntry("Left")
    else
      return branch:FindOutcomeBranchesEntry("Right")
    end
  end
end
function LuaHookInCombat(ai, data)
  if ai:HasMarker("NavigationButCombat") then
    return data:FindOutcomeBranchesEntry("none")
  end
  local speed = ai:GetVelocity():Length()
  local nav = not _G.global.bInCombat and not _G.global.bForceStrafe and not _G.global.bowOut or not ai:IsAvailableForCombat()
  local stand = speed < 0.2 or _G.global.combatnav_StayPut
  local entry = ""
  if nav then
    if stand then
      entry = "NavStand"
    else
      entry = "NavRun"
    end
  elseif stand then
    entry = "CombatStand"
  else
    entry = "CombatRun"
  end
  return data:FindOutcomeBranchesEntry(entry)
end
function LuaHook_MarkBowSheathedInCutscene(ai, data)
  if global.previousState == "InCombat" or global.previousState == "PreCombat" then
    global.previousState = "Idle"
    global.bowSheathed = true
  end
end
function LuaHook_MarkBowUnsheathedInCutscene(ai, data)
  if global.previousState == "Idle" then
    global.previousState = "InCombat"
    global.bowSheathed = false
  end
end
function LuaHook_AssureBowSheathed(ai, data)
  global.bowSheathed = true
end
function LuaHook_AssureBowUnsheathed(ai, data)
  global.bowSheathed = false
end
function LuaHook_KratosAttackSonBanterCheck()
  print("KratosAttackSon | InCombat: ", _G.global.bInCombat, " Player: ", global.Player, " Attacking: ", global.Player:HasMarker("Attacking"))
  if not _G.global.bInCombat and global.Player and global.Player:HasMarker("Attacking") then
    game.Audio.PlayBanterNonCritical("ca_player_ridiculous")
  else
    game.Audio.PlayBanterNonCritical("cbt_son_EvadesEffort")
  end
end
function LuaHook_DisableAutonomousSon(ai)
  if game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") <= 0 then
    game.Wallets.AddResource("HERO", "Dummy_DisableAutonomous", 1, "NO_TELEMETRY")
  end
end
function LuaHook_EnableAutonomousSon(ai)
  if game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") >= 1 then
    game.Wallets.RemoveResource("HERO", "Dummy_DisableAutonomous", 1)
  end
end
function LuaHook_SendLevelEvent_FirstTroll(ai)
  if constants.trollCounterCooldown <= 0 then
    local level = ai.GroundLevel
    if level ~= nil then
      level:CallScript("Btr_SonHit")
    end
    constants.trollCounterCooldown = 5
  end
end
function LuaHookDecision_HasFirstTrollMarker(ai)
  return ai:HasMarker("FirstTrollBehavior")
end
function LuaHookDecision_SendBroadcastToTroll00(ai)
  if not ai:HasMarker("FirstTrollBehavior") then
    return false
  end
  local troll = helper.LargeCreatureExists(ai, _G.global, _G.constants, 40)
  if troll ~= nil and troll:IsInNavigationMove() then
    return true
  end
  return false
end
function LuaHookDecision_SendBroadcastToAttackingTroll00(ai)
  if not ai:HasMarker("FirstTrollBehavior") then
    return false
  end
  local troll = helper.LargeCreatureExists(ai, _G.global, _G.constants, 40)
  if troll ~= nil and troll:HasMarker("Attacking") then
    return true
  end
  return false
end
function LuaHook_SetNextPostCombatTimerLength(ai, time)
  constants.extraPostCombatTime = time
end
function LuaHook_IgnoreDistanceExitNextPostCombat(ai)
  constants.postCombatIgnoreDistanceExit = true
end
function LuaHookPlayerMoved(ai, data)
  local player = game.Player.FindPlayer()
  local playerSpeed = player:GetVelocity():Length()
  if 0 < playerSpeed then
    return data:FindOutcomeBranchesEntry("PlayerMoved")
  end
  return data:FindOutcomeBranchesEntry("nothing")
end
function LuaHookDoIHaveValidTarget(ai, data)
  if _G.constants.targetOfPlayer ~= nil then
    return data:FindOutcomeBranchesEntry("ValidTarget")
  end
  return data:FindOutcomeBranchesEntry("NoTarget")
end
function LuaHook_POI_WaitingForAction()
  if _G.global.POIInfo.useThisPOI ~= nil then
    _G.global.POIInfo.useThisPOI:SendEvent("StartBrainLogic")
  end
end
function OnStandGround(ai, standGround)
  _G.constants.StandGround = standGround
end
function LuaHook_ResetJotunnGrabCounter(ai)
  _G.global.jotunnGrabTimer = 600
end
function SetSonForcedPath(ai, path, speed, forcedType)
  local pathObj = ai.GroundLevel:FindSingleGameObject(path).Children
  local navPath = game.NavPath.New(pathObj)
  ai:SetForcedPath(forcedType or 0, speed or 1.25, navPath)
  global.UseForcedPath = true
end
function ClearSonForcedPath(ai)
  ai:ClearForcedPath()
  global.UseForcedPath = false
end
function CheckForStopping(ai)
  if global.checkForCorrectSpeedAndPhaseStops then
    if ai:IsDoingSyncMove() or ai.OwnedPOI then
      ClearSlowdownToStop(ai)
      return
    end
    local locomotionInfo = ai:GetLocomotionInfo()
    local rightfoot = false
    local leftfoot = false
    if locomotionInfo.RightFootPhase > 0.15 and locomotionInfo.RightFootPhase < 0.4 then
      leftfoot = true
    elseif 0.15 < locomotionInfo.LeftFootPhase and 0.4 > locomotionInfo.LeftFootPhase then
      rightfoot = true
    end
    local velocity = ai:GetVelocity()
    local speed = velocity:Length()
    local speedTolerance = global.nessecarySpeed + 0.3
    if speed <= speedTolerance then
      if global.stopMove == nil then
        if rightfoot then
          ai:ForceMove("BRA_WalkStopTurn0_RightFoot")
          ClearSlowdownToStop(ai)
        elseif leftfoot then
          ai:ForceMove("BRA_WalkStopTurn0_LeftFoot")
          ClearSlowdownToStop(ai)
        end
      elseif rightfoot then
        ai:ForceMove(global.stopMove)
        ClearSlowdownToStop(ai)
      end
    end
  end
end
function SetupSlowdownToStop(ai, stopMove, nessecarySpeed, destination)
  if not global.slowdownAllowed then
    return
  end
  local slowdownPathTable = {destination}
  local navPath = game.NavPath.New(slowdownPathTable)
  ai:SetForcedPath(1, nessecarySpeed, navPath)
  global.UseForcedPath = true
  ai:SetDecelerationOverride(1.25)
  global.stopMove = stopMove
  global.nessecarySpeed = nessecarySpeed
  global.checkForCorrectSpeedAndPhaseStops = true
end
function ClearSlowdownToStop(ai)
  ai:ClearForcedPath()
  global.UseForcedPath = false
  ai:ClearDecelerationOverride()
  global.checkForCorrectSpeedAndPhaseStops = false
end
function LuaHook_InterruptAndClearSlowdown(ai)
  if global.checkForCorrectSpeedAndPhaseStops then
    ai:ForceMove("BRA_WalkStopTurn0_RightFoot")
    ClearSlowdownToStop(global.stopMove)
  end
end
function LuaHook_ToggleSlowdownStops(ai, slowdownActive)
  global.slowdownAllowed = slowdownActive
end
function LuaHook_FollowupToggle(ai, togglestate)
  constants.stats.advancedFollowUpEnabled = togglestate
end
function LuaHook_ForceUpdate(ai)
  ai:RequestHighPriorityUpdate()
end
function LuaHook_DisableShooting(ai, disable)
  _G.global.disableShooting = disable
end
function LuaHook_SuppressCombat(ai, suppress)
  _G.global.overrides.suppressCombat = suppress
end
function LuaHook_LockArrowType(ai, lock)
  _G.constants.lockArrowType = lock
end
function LuaHook_ChangeArrowType(ai, arrowType)
  if arrowType == "Shock" then
    if not ai:PickupIsAcquired("SonArrow_Shock") then
      ai:PickupAcquire("SonArrow_Shock")
    end
  elseif arrowType == "Light" then
    if not ai:PickupIsAcquired("SonArrow_Light") then
      ai:PickupAcquire("SonArrow_Light")
    end
  elseif not ai:PickupIsAcquired("SonArrow_Regular") then
    ai:PickupAcquire("SonArrow_Regular")
  end
end
function LuaHook_ChangeStrapToThisType(ai, strapType)
  for weapon in ai:IterateActiveWeapons() do
    if weapon ~= nil and weapon.Weapon ~= nil and weapon.Weapon:GetName() == "quiver00" then
      local wep = weapon.Weapon
      wep:SetCharacterConfig(strapType)
      _G.constants.WeaponSetupFinished = true
    end
  end
end
function LuaHook_ChangeSashToDamagedStrap(ai)
end
function LuaHook_ChangeSashToDefault(ai)
end
function LuaHook_ChangeSashToPrestineStrap(ai)
end
function LuaHook_ChangeSashToFixedStrap(ai)
end
function LuaHook_ChangeSashToRepairStrap(ai)
end
function LuaHook_ChangeSashToWrapHide(ai)
end
function LuaHook_SetToEnraged(ai, value)
  _G.global.emotionState.value = 3
  game.Wallets.AddResource("HERO", "Dummy_Son_EnragedMarker", 1, "NO_TELEMETRY")
end
function LuaHook_ResetEnraged(ai, value)
  _G.global.emotionState.value = 6
  game.Wallets.RemoveResource("HERO", "Dummy_Son_EnragedMarker", 1)
end
function LuaHook_SetEmotionalState(ai, value)
  _G.global.emotionState.value = value
end
function LuaHook_IdleTurn_OverrideSpeed(ai, branch)
  local locomotionInfo = ai:GetLocomotionInfo()
  local focusAngle = locomotionInfo.FocusAngle
  if ai.OwnedPOI ~= nil and locomotionInfo.IsCloseRangeApproach and locomotionInfo.PathLength < 1 then
    if focusAngle == 0 or -30 < focusAngle and focusAngle < 30 then
      return branch:FindOutcomeBranchesEntry("MOV_IdlePOI")
    end
    if 60 < focusAngle then
      return branch:FindOutcomeBranchesEntry("MOV_IdleTurn120RWarp")
    elseif focusAngle < -60 then
      return branch:FindOutcomeBranchesEntry("MOV_IdleTurn120LWarp")
    elseif -60 <= focusAngle and focusAngle <= 60 then
      return branch:FindOutcomeBranchesEntry("MOV_IdleTurn0Warp")
    end
  end
end
function LuaHook_ClearSummons()
  local permLevel = game.Level.GetPermLevel()
  local summonScript1 = permLevel:FindSingleGameObject("*summonfen*")
  local summonScript2 = permLevel:FindSingleGameObject("*summondeer*")
  local summonScript3 = permLevel:FindSingleGameObject("*summonboar*")
  local summonScript4 = permLevel:FindSingleGameObject("*summonsouls*")
  local summonScript5 = permLevel:FindSingleGameObject("*summonbird*")
  if summonScript1 ~= nil then
    summonScript1:CallScript("DestroySummons")
  end
  if summonScript2 ~= nil then
    summonScript2:CallScript("DestroySummons")
  end
  if summonScript3 ~= nil then
    summonScript3:CallScript("DestroySummons")
  end
  if summonScript4 ~= nil then
    summonScript4:CallScript("DestroySummons")
  end
  if summonScript5 ~= nil then
    summonScript5:CallScript("DestroySummons")
  end
end
function LuaHook_ReactionReset(ai)
  if not ai:PickupIsAcquired("Son_AllowReaction") then
    ai:PickupAcquire("Son_AllowReaction")
  end
  ai:PickupSetStage("Son_AllowReaction", 0)
  _G.global.allowReactionTimer = 0
end
function LuaHook_OccupyReset(ai)
  if not ai:PickupIsAcquired("Son_AllowOccupy") then
    ai:PickupAcquire("Son_AllowOccupy")
  end
  ai:PickupSetStage("Son_AllowOccupy", 0)
  _G.global.allowOccupyTimer = 0
end
function LuaHook_IncapacitateReset(ai)
  if not ai:PickupIsAcquired("Son_AllowIncapacitate") then
    ai:PickupAcquire("Son_AllowIncapacitate")
  end
  ai:PickupSetStage("Son_AllowIncapacitate", 0)
  _G.global.allowIncapacitateTimer = 0
end
function LuaHook_DeathReset(ai)
  if not ai:PickupIsAcquired("Son_AllowDeath") then
    ai:PickupAcquire("Son_AllowDeath")
  end
  ai:PickupSetStage("Son_AllowDeath", 0)
  _G.global.allowDeathTimer = 0
end
function LuaHook_InSurvival(ai, data)
  return _G.constants.awarenessState == _G.constants.SurvivalReposition
end
function LuaHook_CombatRunDecision(ai, branch)
  if _G.constants.InSurvivalDriver.Value == 0 then
    return branch:FindOutcomeBranchesEntry("MOV_CombatRun")
  else
    return branch:FindOutcomeBranchesEntry("MOV_CombatSurvivalRepositionRunF")
  end
end
function LuaHook_ForceAggro(ai, minEnemies)
  if minEnemies == nil then
    minEnemies = 3
  end
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLength = bboardUtil.GetListLength(sonBB, _G.global.enemyBlackBoardIndex)
  if enemyBBLength ~= nil then
    bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
  end
  local livingenemies = DL.FindLivingEnemies(ai, 40, true)
  local enemyList = {}
  if 0 < #livingenemies then
    for x = 1, minEnemies do
      if x <= #livingenemies then
        table.insert(enemyList, livingenemies[x])
      end
    end
    bboardUtil.SetList(sonBB, global.enemyBlackBoardIndex, enemyList)
  end
  for _, i in ipairs(enemyList) do
    engine.SendHook("LuaHook_NewProximityTargetSon", i)
  end
end
function LuaHook_RemoveAggro(ai)
  local sonBB = ai:GetPrivateBlackboard()
  local enemyBBLength = bboardUtil.GetListLength(sonBB, _G.global.enemyBlackBoardIndex)
  if enemyBBLength ~= nil then
    bboardUtil.EraseList(sonBB, global.enemyBlackBoardIndex)
  end
  local livingenemies = DL.FindLivingEnemies(ai, 40, true)
  local enemyList = {}
  for _, i in ipairs(enemyList) do
    engine.SendHook("LuaHook_SoftResetAggro", i)
  end
end
function LuaHook_IsInCombat(ai)
  return _G.global.bInCombat
end
function LuaHook_ShootShield(ai)
  local arrowData = {}
  local fakearrow = game.FX.Spawn("LightningToShield", game.AI.FindSon().GroundLevel)
  fakearrow:SetWorldPosition(game.AI.FindSon().WorldPosition + engine.Vector.New(0, 1, 0))
  arrowData.Target = nil
  arrowData.TargetLocation = game.AI.FindSon().WorldPosition + engine.Vector.New(0, 100, 0)
  arrowData.Tweak = "ARR_ARROW_SHOCK_LUA_CHARGED"
  arrowData.Creator = _G.global.selfAI
  arrowData.CreatorEmitJoint = "JORightIndexA1"
  arrowData.EmitOffset = nil
  arrowData.PlayerCommand = false
  game.Combat.EmitArrow(arrowData)
  ArrowMeterAdjust(-30)
end
function LuaHook_SonDeath(ai)
  uiCalls.UI_Event_SendTutorialMessage("Son has died. Game over", 0)
  game.Player.FindPlayer():TriggerMoveEvent("kLE_ForceDeath")
end
function LuaHook_SonInTrouble(ai)
end
function LuaHook_Peak550_WarpSonToClampPOI()
  local level = game.FindLevel("Peak550_DragonArena")
  level:CallScript("LuaHook_Peak550_WarpSonToClampPOI")
end
function LuaHook_SonIncapacitateTutorial(ai)
  if _G.global.tutorial_Incapacitate == false then
    _G.global.tutorial_Incapacitate = true
    TUT.Son_Incapacitated_Tutorial()
  end
end
function LuaHookDecision_SonHasFerocity_lv2(ai, data)
  if ai:PickupIsAcquired("SonArmorOffense02") or ai:PickupIsAcquired("SonArmorOffense03") or ai:PickupIsAcquired("SonArmorBonus01") or ai:PickupIsAcquired("SonArmorBonus03") or ai:PickupIsAcquired("SonArmorBonus04") then
    return true
  end
  return false
end
function LuaHook_RefreshArrows(ai)
  if ai:PickupIsAcquired("SonArmorDefense03") or ai:PickupIsAcquired("SonArmorBonus01") or ai:PickupIsAcquired("SonArmorBonus02") or ai:PickupIsAcquired("SonArmorBonus04") then
    ai:PickupAcquire("SonPerk_AllStatus_Potency")
  end
end
function LuaHook_CanDoKratosBackFidget(ai)
  if 0 < fidgetCoolDownTime then
    return
  end
  local marker
  if ai:HasMarker("excited") then
    marker = "LE_excited"
  elseif ai:HasMarker("fiddle") then
    marker = "LE_fiddle"
  end
  ai:TriggerMoveEvent(marker)
  fidgetCoolDownTime = 16
end
function OnSonInteractButtonPressed(ai, obj)
  if ai.OwnedPOI ~= nil and global.POIInfo ~= nil and global.POIInfo.useThisPOI ~= nil then
    if obj == global.POIInfo.useThisPOI then
      if global.POIInfo.useThisPOI.Type == "SonSandBowl" then
        global.POIInfo.useThisPOI:SendEvent("kESonInteractBowl")
      end
    else
      global.POIInfo.useThisPOI:SendEvent("kESonAbort")
    end
  end
  local son_interacts = game.POI.FindAdvertising(ai:GetWorldPosition(), 120, {
    "INTERACT_SON"
  })
  for _, poi in ipairs(son_interacts) do
    if poi:GetGameObject() == obj then
      poi:Engage(ai)
      break
    end
  end
end
function OnSonInteractButtonPressedWhileOccupied(ai, obj)
  global.usedSonInteractWhileOccupied = true
end
function LuaDecision_CheckSonSick(ai, branch)
  local cineNumber = game.Level.GetVariable("CompletedCineNumber")
  local cineNumber_SonSick_WithKratos = 365
  local cineNumber_SonSick_WithFreya = 370
  if cineNumber >= cineNumber_SonSick_WithKratos and cineNumber < cineNumber_SonSick_WithFreya then
    ai:CallScript("EnterBehaviorContext", "BOAT_SICK_CONTEXT_CONFIG")
    return branch:FindOutcomeBranchesEntry("SonSick")
  else
    return branch:FindOutcomeBranchesEntry("Default")
  end
end
local pluckModule
function LuaHook_BoatPluck_StartInteract()
  local pluckModules = game.World.FindGameObjectsByMarker("PluckLoot", global.selfAI:GetWorldPosition(), 5)
  if #pluckModules < 1 then
    assert(false, "No pluck modules found increase search radius.")
  end
  if 1 < #pluckModules then
    assert(false, "Found too many modules, decrease search radius.")
  end
  pluckModule = pluckModules[1].Parent
  print(pluckModule)
  pluckModule:CallScript("LuaHook_StartInteract")
end
function LuaHook_BoatPluck_AwardLoot()
  assert(pluckModule ~= nil)
  pluckModule:CallScript("LuaHook_AwardLoot")
end
function LuaHook_BoatPluck_EndInteract()
  assert(pluckModule ~= nil)
  pluckModule:CallScript("LuaHook_EndInteract")
end
function LuaHook_DeerMissCineFor()
  game.Audio.StartMusic("SND_MX_PRO_deer_miss")
end
function LuaHook_PastHauntsSon()
  game.Audio.StartMusic("SND_MX_HEL_son_haunted_moment")
end
function LuaHook_BridgeFallEnter88()
  game.Audio.StartMusic("SND_MX_RIV_bridge_hang")
end
function LuaHook_PlayHitSound(ai)
  local emitter
  emitter = ai:FindSingleSoundEmitterByName("SNDSon")
  emitter:Start("SND_WPN_Bow_Melee_Hit")
end
function GetFreyaReference()
  local objArray = game.World.FindGameObjectsByMarker("freya00")
  for _, obj in ipairs(objArray) do
    if obj:GetCreature() ~= nil and obj:GetCreature():GetAI() ~= nil then
      return obj:GetCreature():GetAI()
    end
  end
  return nil
end
function LuaHook_HeadTrack_SetForcedTarget_Kratos()
  _G.global.selfAI:ForceLookAtToObject(game.Player.FindPlayer())
end
function LuaHook_HeadTrack_SetForcedTarget_Freya()
  local freya = GetFreyaReference()
  if freya then
    _G.global.selfAI:ForceLookAtToObject(freya)
  end
end
function LuaHook_HeadTrack_ClearForcedTarget()
  _G.global.selfAI:ClearForcedLookAtToObject()
end
function LuaHook_HeadTrack_PrioritizeKratos()
  _G.lookAtPriorityOverrides.hero = _G.global.selfAI:AddLookAtPriorityOverride(lookAtConsts.TargetType.Hero, lookAtConsts.Priority.Highest)
end
function LuaHook_HeadTrack_DeprioritizeKratos()
  _G.lookAtPriorityOverrides.hero = _G.global.selfAI:AddLookAtPriorityOverride(lookAtConsts.TargetType.Hero, lookAtConsts.Priority.Ignore)
end
function LuaHook_HeadTrack_PrioritizeFriendlyAI()
  _G.lookAtPriorityOverrides.FriendlyAI = _G.global.selfAI:AddLookAtPriorityOverride(lookAtConsts.TargetType.FriendlyAI, lookAtConsts.Priority.High)
end
function LuaHook_HeadTrack_DeprioritizeFriendlyAI()
  _G.lookAtPriorityOverrides.FriendlyAI = _G.global.selfAI:AddLookAtPriorityOverride(lookAtConsts.TargetType.FriendlyAI, lookAtConsts.Priority.Ignore)
end
function LuaHook_SetSonScuff(ai)
  ai:PickupAcquire("Scuff_MFX_Pickup")
end
function LuaHook_UnsetSonScuff(ai)
  ai:PickupRelinquish("Scuff_MFX_Pickup")
end
function LuaHook_Effort_Struggle_Loop()
  local son = game.AI.FindSon()
  local emitter = son:FindSingleSoundEmitterByName("SNDMouth")
  if not emitter:IsPlaying("SND_vo_glb_cbt_exrt_fearful_struggle_000_son") then
    emitter:Start("SND_vo_glb_cbt_exrt_fearful_struggle_000_son")
  end
end
function LuaHook_Effort_Struggle_Loop_Stop()
  local son = game.AI.FindSon()
  son:FindSingleSoundEmitterByName("SNDMouth"):Stop("SND_vo_glb_cbt_exrt_fearful_struggle_000_son")
end
function LuaHook_IncapacitateEnter()
  _G.incapacitated = true
end
function LuaHook_IncapacitateExit()
  _G.incapacitated = false
  mpicon.Off("SON_INCAPACITATE")
  mpicon.Off("SON_INCAPACITATE_MUSP")
end
