local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local tablex = require("core.tablex")
local picklelib = require("core.pickle")
local components = require("design.components")
local globalLuaHooks = require("design.GlobalLuaHooks")
globalLuaHooks.InstallHooks()
local locomotion = require("creature.locomotion")
local timer = require("creature.timer")
local mpicon = require("ui.mpicon")
locomotion.Install()
attachments = {}
constants = {}
global = {}
global.target = {}
global.currentPOI = {}
global.navData = {}
global.navData.stopDistance = 0.5
global.navData.startDistance = 1
global.navData.isStrafing = true
global.navData.navSpeedJog = 0
global.navData.navSpeedWalk = 0
global.navBank = "NB_OffensiveBank"
global.awarenessBroadcastRange = 40
global.awarenessReceiveRange = 20
global.fightConstants = {}
global.fightConstants.AggressiveRange = 20
global.fightConstants.DesiredRadius = 1
constants.fightKnowledgeInputs = {}
constants.fightPosDataDefault = {}
constants.fightPosDataDefault.OccupancyRadius = 1.2
constants.fightPosDataDefault.OccupancyPriority = 10
constants.fightPosDataDefault.AggressiveMass = 50
constants.fightPosDataDefault.NonAggressiveMass = 20
constants.fightPosDataDefault.AggressiveMinDistance = 2
constants.fightPosDataDefault.AggressiveMaxDistance = 5
constants.fightPosDataDefault.NonAggressiveMinDistance = 10
constants.fightPosDataDefault.NonAggressiveMaxDistance = 15
constants.fightKnowledge_SM_Fire = {}
constants.fightPosData_SM_Fire = {}
constants.fightPosData_SM_Fire.OccupancyRadius = 1.2
constants.fightPosData_SM_Fire.OccupancyPriority = 10
constants.fightPosData_SM_Fire.AggressiveMass = 50
constants.fightPosData_SM_Fire.NonAggressiveMass = 20
constants.fightPosData_SM_Fire.AggressiveMinDistance = 8
constants.fightPosData_SM_Fire.AggressiveMaxDistance = 12
constants.fightPosData_SM_Fire.NonAggressiveMinDistance = 12
constants.fightPosData_SM_Fire.NonAggressiveMaxDistance = 15
constants.FightKnowledge_SM_Ph2 = {}
constants.fightPosData_SM_Ph2 = {}
constants.fightPosData_SM_Ph2.OccupancyRadius = 1.2
constants.fightPosData_SM_Ph2.OccupancyPriority = 10
constants.fightPosData_SM_Ph2.AggressiveMass = 50
constants.fightPosData_SM_Ph2.NonAggressiveMass = 20
constants.fightPosData_SM_Ph2.AggressiveMinDistance = 8
constants.fightPosData_SM_Ph2.AggressiveMaxDistance = 12
constants.fightPosData_SM_Ph2.NonAggressiveMinDistance = 12
constants.fightPosData_SM_Ph2.NonAggressiveMaxDistance = 15
constants.fightPosDataSonTarget = {}
constants.fightPosDataSonTarget.OccupancyRadius = 1.75
constants.fightPosDataSonTarget.OccupancyPriority = 10
constants.fightPosDataSonTarget.AggressiveMass = 20
constants.fightPosDataSonTarget.NonAggressiveMass = 10
constants.fightPosDataSonTarget.AggressiveMinDistance = 4.5
constants.fightPosDataSonTarget.AggressiveMaxDistance = 6.5
constants.fightPosDataSonTarget.NonAggressiveMinDistance = 4.5
constants.fightPosDataSonTarget.NonAggressiveMaxDistance = 6.5
constants.fightPosDataSonTarget.SonOnly = true
global.aggroState = ""
global.DTree = "DTR_BALDUR00_OFFENSIVE"
global.turretMode = false
global.turretModeBreak = false
global.turretModeBreakRange = 0
global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY"
global.powerArmEnchantAmmo = 0
global.allowDebug = false
global.stage = 0
global.firstElementalBurst = false
global.frostLevel = 0
global.maxFrostLevel = 100
global.heatLevel = 0
global.maxHeatLevel = 100
global.healthValue_SM_Phase0 = 0
dropCraftingResource = false
local timeUntilHint = 8
local blockHintTimer, player
function SetStage(ai, newStage)
  global.stage = newStage
end
function SetNonhostile(ai)
  global.aggroState = "NONHOSTILE"
  global.target = nil
end
function SetHostile(ai)
  global.aggroState = "INCOMBAT"
end
function OnWeaponApproaching(gameObject, attacker, weapon, side)
  local ai = gameObject:GetCreature():GetAI()
  if side == "front" and game.Level.GetVariable("DEBUG_CBT_Disable_Defense") == false then
    ai:TriggerMoveEvent("LE_DodgeAxe")
  end
end
function setupCharacter(ai)
  local moveName = ""
  local aggroState = ""
  local spawnContext = ai:GetContext()
  ai:PickupAcquire("Baldur_ComboCounter")
  ai:PickupAcquire("Baldur_Attributes")
  if StartConfig ~= nil then
    if StartConfig.SpawnMove ~= nil then
      moveName = StartConfig.SpawnMove
    end
    if StartConfig.AggroState ~= nil then
      DL.switch(StartConfig.AggroState):caseof({
        Unaware = function()
          aggroState = "UNAWARE"
        end,
        Passive = function()
          aggroState = "PASSIVE"
        end,
        NonHostile = function()
          aggroState = "NONHOSTILE"
        end,
        InCombat = function()
          aggroState = "INCOMBAT"
        end
      })
    end
    if StartConfig.Type == "Stage0" then
      global.stage = 0
    end
    if StartConfig.HealthBarStartOn == true then
      LuaHook_ShowHealth(ai)
    end
  end
  if aggroState == "" then
    aggroState = "INCOMBAT"
  end
  global.aggroState = aggroState
  if moveName ~= "" then
    ai:ForceMove(moveName)
  end
  ai:AddMarker("Baldur")
  LuaHook_MatAnim_FrostFistOff(ai, nil)
  LuaHook_MatAnim_TattooGlowOFF(ai, nil)
  if game.GetNewGamePlus() then
    ai:PickupAcquire("NewGameLevelBoost")
    local difficulty = ai:AttributeGetValue("Difficulty")
    local boostAmount = 3
    if difficulty == 4 then
      boostAmount = 6
    elseif difficulty == 3 then
      boostAmount = 5
    elseif difficulty == 2 then
      boostAmount = 4
    else
      boostAmount = 3
    end
    ai:PickupSetStage("NewGameLevelBoost", boostAmount)
  end
end
function OnAIPostSpawn(ai)
  player = game.Player.FindPlayer()
  global.navData.navSpeedWalk = ai:LookupFloatConstant("NAV_SPEED_WALK")
  global.navData.navSpeedJog = ai:LookupFloatConstant("NAV_SPEED_JOG")
  global.healthValue_SM_Phase0 = ai:LookupConstant("BALDUR_HEALTH_VALUE_STONEMASON_PH_0")
  setupCharacter(ai)
  ai:SetDirectAimingModeIsEnabled(true)
end
local tattooGlowOn = false
local breathEffect
function OnCreatureUpdate(ai)
  local debugTable = statemachine.GetDebugTable(Baldur_states, ai)
  if debugTable then
    drawDebugTable(debugTable, global, ai)
  end
  if breathEffect == nil then
    breathEffect = game.FX.Spawn("FX_kratos_Breath", nil, {
      Joint = "JOJaw1",
      GameObject = ai,
      DeleteWithCreature = true
    })
    breathEffect:SetAnimFloatChannelDriver(ai, "sliderfloatchannels._breathefxintensity")
  end
end
Baldur_states = {
  require("baldur00.leashBrain"),
  require("baldur00.brain")
}
local previousAggroState
function OnAIUpdate(ai)
  if previousAggroState ~= global.aggroState and ai.SetAggroState then
    ai:SetAggroState(global.aggroState)
  end
  previousAggroState = global.aggroState
  locomotion.CreateDrivers(ai)
  if ai:HasMarker("BaldurTattooFlare") == false and tattooGlowOn == true then
    LuaHook_MatAnim_TattooGlowOFF(ai, nil)
  end
  if ai:HasMarker("BaldurTattooFlare") == true and tattooGlowOn == false then
    LuaHook_MatAnim_TattooGlowON(ai, nil)
  end
  if ai ~= nil then
    if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
      global.DTree = "DTR_BALDUR00_STONEMASON"
    else
      global.DTree = "DTR_BALDUR00_OFFENSIVE"
    end
  end
  statemachine.UpdateAll(Baldur_states, ai, global, constants)
  components.UpdateComponents(ai, attachments)
  if ai ~= nil and ai:PickupIsAcquired("Debuff_Slow") then
    ai:PickupRelinquish("Debuff_Slow")
  end
end
function _G.OnAICreateLuaState(ai)
  statemachine.StartAll(ai, Baldur_states, ai, global, constants)
  components.StartComponents(ai, attachments)
  _G.OnAICreateLuaState = nil
end
function drawDebugTable(debugTable, global, ai)
  global.currentPosition = ai:GetWorldPosition()
  local fireballMeter = ai:MeterGetValue("FireballAmmo")
  local pummelMeter = ai:MeterGetValue("BeatdownPummel")
  table.insert(debugTable, {
    "Current Pos: ",
    global.currentPosition
  })
  table.insert(debugTable, {
    "Distance to Player: ",
    global.distToPlayer
  })
  table.insert(debugTable, {
    "Target Params: ",
    global.targetParams
  })
  table.insert(debugTable, {
    "Current Target: ",
    global.target
  })
  table.insert(debugTable, {"-----", "-----"})
  table.insert(debugTable, {
    "Frost Level: ",
    global.frostLevel
  })
  table.insert(debugTable, {
    "Max Frost Level: ",
    global.maxFrostLevel
  })
  table.insert(debugTable, {
    "Heat Level: ",
    global.heatLevel
  })
  table.insert(debugTable, {
    "Max Heat Level: ",
    global.maxHeatLevel
  })
  table.insert(debugTable, {
    "First Element Burst: ",
    global.firstElementalBurst
  })
  table.insert(debugTable, {
    "Fireball Meter: ",
    fireballMeter
  })
  table.insert(debugTable, {
    "Pummel Meter: ",
    pummelMeter
  })
  table.insert(debugTable, {
    "Reaction Breaker Meter: ",
    DL.ComboBreakerGetDebugString()
  })
  table.insert(debugTable, {
    "Phase 0 Health: ",
    global.healthValue_SM_Phase0
  })
  engine.DrawDebugTable(debugTable)
end
function GetBaldurEvadeDirection(ai, allowLeft, allowBack, allowRight)
  local move = "EvadeRight"
  if allowRight == false then
    move = "EvadeLeft"
  end
  if allowBack and DL.CheckSpherecastCollision(ai, "Back", 4) == false then
    move = "EvadeBack"
  elseif allowLeft and DL.CheckSpherecastCollision(ai, "Left", 4) == false then
    move = "EvadeLeft"
  elseif allowRight and DL.CheckSpherecastCollision(ai, "Right", 4) == false then
    move = "EvadeRight"
  elseif allowLeft and DL.CheckSpherecastCollision(ai, "LeftFront", 4) == false then
    move = "EvadeLeft"
  elseif allowRight and DL.CheckSpherecastCollision(ai, "RightFront", 4) == false then
    move = "EvadeRight"
  elseif allowLeft and DL.CheckSpherecastCollision(ai, "LeftBack", 4) == false then
    move = "EvadeLeft"
  elseif allowRight and DL.CheckSpherecastCollision(ai, "RightBack", 4) == false then
    move = "EvadeRight"
  end
  return move
end
function LuaHook_ClearArrow(ai, data)
  game.Combat.RemoveArrows(ai)
end
function LuaHook_BaldurEvade_CollisionCheck_LeftToWall(ai, data)
  local move = "None"
  if DL.CheckSpherecastCollision(ai, "Left", 2) == true then
    move = GetBaldurEvadeDirection(ai, false, true, true)
  end
  return data:FindOutcomeBranchesEntry(move)
end
function LuaHook_BaldurEvade_CollisionCheck_RightToWall(ai, data)
  local move = "None"
  if DL.CheckSpherecastCollision(ai, "Right", 2) == true then
    move = GetBaldurEvadeDirection(ai, true, true, false)
  end
  return data:FindOutcomeBranchesEntry(move)
end
function LuaHook_BaldurEvade_CollisionCheck_BackToWall(ai, data)
  local move = "None"
  if DL.CheckSpherecastCollision(ai, "Back", 2) == true then
    move = GetBaldurEvadeDirection(ai, true, false, true)
  end
  return data:FindOutcomeBranchesEntry(move)
end
function LuaHook_BaldurEvade_CollisionCheck(ai, data)
  local move = GetBaldurEvadeDirection(ai, true, true, true)
  return data:FindOutcomeBranchesEntry(move)
end
global.quickRecoverCooldown = 0
function LuaHook_WarpToPlayer(ai, data)
  local playerCreature = game.Player.FindPlayer()
  local playerForward = playerCreature:GetWorldForward()
  ai:Warp(playerCreature:GetWorldPosition() + playerForward * 0.5, engine.Vector.New(playerForward.x * -1, playerForward.y, playerForward.z * -1))
end
function LuaHookDecision_PlayerAlive(ai, data)
  local playerCreature = game.Player.FindPlayer()
  if playerCreature ~= nil and playerCreature:MeterGetValue("Health") > 0 then
    return true
  end
  return false
end
function LuaHookDecision_Baldur_QuickRecoverCheck(ai, data)
  if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
    global.quickRecoverCooldown = 0
  end
  if global.quickRecoverCooldown > 0 then
    global.quickRecoverCooldown = 0
    return data:FindOutcomeBranchesEntry("Slow")
  else
    return data:FindOutcomeBranchesEntry("Quick")
  end
end
function LuaHookDecision_StageGreaterThanEqual3(ai, data)
  if global.stage >= 3 then
    return true
  else
    return false
  end
end
function LuaHookDecision_StageGreaterThanEqual2(ai, data)
  if global.stage >= 2 then
    return true
  else
    return false
  end
end
function LuaHookDecision_StageLessThanEqual1(ai, data)
  if global.stage <= 1 then
    return true
  else
    return false
  end
end
function LuaHookDecision_StageLessThanEqual2(ai, data)
  if global.stage <= 2 then
    return true
  else
    return false
  end
end
function LuaHookDecision_CanBanterPlay(ai, data)
  if game.Audio.CanBanterConversationPlay("355_Phase1Taunts") or game.Audio.CanBanterConversationPlay("415_Phase2Taunts") or game.Audio.CanBanterConversationPlay("444_Phase3Taunts") then
    return true
  else
    return false
  end
end
function LuaHookDecision_StoneMasonAttacking(ai, data)
  for c in game.Creature.IterateAllCreatures() do
    local creatureName = c:GetName()
    if creatureName == "stonemason00" then
      local stoneMason = c
      if stoneMason:HasMarker("StoneMason_Attack") then
        return true
      else
        return false
      end
    end
  end
end
function LuaHookDecision_SM_Attack_VinesActive(ai, data)
  for c in game.Creature.IterateAllCreatures() do
    local creatureName = c:GetName()
    if creatureName == "stonemason00" then
      local stoneMason = c
      if stoneMason:HasMarker("StoneMason_Vines") then
        return true
      else
        return false
      end
    end
  end
end
function LuaHookDecision_SM_DisablePhaseTransition(ai, data)
  for c in game.Creature.IterateAllCreatures() do
    local creatureName = c:GetName()
    if creatureName == "stonemason00" then
      local stoneMason = c
      if stoneMason:HasMarker("StoneMason_DisablePhaseTransition") then
        return true
      else
        return false
      end
    end
  end
end
function LuaHook_MatAnim_IceModeOn(ai, data)
  ai:StartMaterialAnim("ice_state_on")
end
local iceMatOn = false
function LuaHook_MatAnim_FrostFistOn(ai, data)
  if iceMatOn == false then
    ai:StartMaterialAnim("ice_on")
    iceMatOn = true
  end
end
function LuaHook_MatAnim_FrostFistOff(ai, data)
  if iceMatOn == true then
    ai:StartMaterialAnim("ice_off")
    iceMatOn = false
  end
end
function LuaHook_MatAnim_TattooGlowON(ai, data)
  if ai:PickupIsAcquired("Baldur_FireMode") == false and ai:PickupIsAcquired("Baldur_IceMode") == false and tattooGlowOn == false then
    ai:StartMaterialAnim("baldur00_TattooON")
    tattooGlowOn = true
  end
end
function LuaHook_MatAnim_TattooGlowOFF(ai, data)
  if ai:PickupIsAcquired("Baldur_FireMode") == false and ai:PickupIsAcquired("Baldur_IceMode") == false and tattooGlowOn == true then
    ai:StartMaterialAnim("baldur00_TattooOFF")
    tattooGlowOn = false
  end
end
local throwableSpawnID, baldurCreature
function LuaHook_SpawnBoulder(C, data)
  print("spawn grenade")
  if throwableSpawnID == nil then
    local levelName = game.FindLevel("TB_Baldur")
    throwableSpawnID = game.FX.Spawn("throwableVibrateCrystal", levelName)
    throwableSpawnID:SetWorldPosition(C:GetWorldPosition())
    timer.StartCreatureTimer(0.1, BoulderSetThrowable)
    baldurCreature = C
  end
end
function BoulderSetThrowable(C)
  print(baldurCreature)
end
function onBoulderCreated(go, grenadeThrowable)
  print("Grenade created")
  local creature = go:GetCreature()
end
function LuaHook_StoneMasonBoss_ActivateIceMode(ai)
  if ai:PickupIsAcquired("Baldur_FireMode") then
    ai:PickupRelinquish("Baldur_FireMode")
  end
  if not ai:PickupIsAcquired("Baldur_IceMode") then
    ai:PickupAcquire("Baldur_IceMode")
    ai:StartMaterialAnim("ice_state_on")
  end
  if global.firstElementalBurst == false then
    global.firstElementalBurst = true
  end
  global.heatLevel = 0
  ai:MeterSetValue("HeatLevel", global.heatLevel)
  global.frostLevel = 0
  ai:MeterSetValue("FrostLevel", global.frostLevel)
  ai:SetStance("AIStance_Offensive_IceMode")
end
function LuaHook_StoneMasonBoss_ActivateFireMode(ai)
  if ai:PickupIsAcquired("Baldur_IceMode") then
    ai:PickupRelinquish("Baldur_IceMode")
  end
  if not ai:PickupIsAcquired("Baldur_FireMode") then
    ai:PickupAcquire("Baldur_FireMode")
    ai:StartMaterialAnim("fire_state_on")
  end
  if global.firstElementalBurst == false then
    global.firstElementalBurst = true
  end
  global.frostLevel = 0
  ai:MeterSetValue("FrostLevel", global.frostLevel)
  global.heatLevel = 0
  ai:MeterSetValue("HeatLevel", global.heatLevel)
  ai:SetStance("AIStance_Offensive_FireMode")
end
function LuaHook_StoneMasonBoss_DeactivateElements(ai)
  if ai:PickupIsAcquired("Baldur_IceMode") then
    ai:PickupRelinquish("Baldur_IceMode")
  end
  if ai:PickupIsAcquired("Baldur_FireMode") then
    ai:PickupRelinquish("Baldur_FireMode")
  end
  ai:StartMaterialAnim("fire_ice_state_off")
  global.frostLevel = 0
  ai:MeterSetValue("FrostLevel", global.frostLevel)
  global.heatLevel = 0
  ai:MeterSetValue("HeatLevel", global.heatLevel)
  ai:SetStance("AIStance_Offensive")
end
function LuaHook_TargetSon()
  if global.targetParams ~= "FIND_TARGET_PARAMETERS_COMBAT_SON_ONLY" then
    global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_SON_ONLY"
  end
end
function LuaHook_TargetKratos()
  if global.targetParams ~= "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY" then
    global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY"
  end
end
function LuaHook_TargetProjection()
  if global.targetParams ~= "FIND_TARGET_PARAMETERS_COMBAT_PROJECTION20_ONLY" then
    global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_PROJECTION20_ONLY"
  end
end
function LuaHook_ClearSonSpecialAttacks()
  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_RemoveBloodFromBaldur(ai)
  game.Combat.ForceBloodDecayInCombat(true)
end
function LuaHook_AddBlood_Baldur_Beatdown(ai)
  ai:AddBlood(game.GameObject.eRegionId.kHead, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightChest, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.1)
  ai:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.1)
end
function LuaHook_FireMode_ProximityMine(ai)
  if ai:PickupGetStage("Baldur_StoneMasonBoss") >= 3 then
    LuaHook_FireMode_ProximityMine_Left(ai)
    LuaHook_FireMode_ProximityMine_Right(ai)
  else
    LuaHook_FireMode_ProximityMine_Center(ai)
  end
end
function LuaHook_FireMode_ProximityMine_Left(ai)
  local targetPosition = global.target:GetWorldPosition()
  local targetForward = global.target:GetWorldForward()
  local targetLeft = global.target:GetWorldLeft()
  local distanceFrontOfTarget = 4
  local distanceLeftOfTarget = 4
  local targetLocation = targetPosition + targetForward * distanceFrontOfTarget + targetLeft * distanceLeftOfTarget
  local navMeshPoint = game.NavMesh.ClosestPoint(targetLocation)
  engine.DrawFillSphere(targetPosition, 0.25, 65280, 3)
  engine.DrawFillSphere(targetLocation, 0.25, 16711680, 3)
  local concussionParams = {
    Tweak = "CNC_FIREMODE_PROXIMITY_MINE",
    WorldLocation = navMeshPoint,
    GameObject = ai,
    EnemyId = ai:GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
end
function LuaHook_FireMode_ProximityMine_Center(ai)
  local targetPosition = global.target:GetWorldPosition()
  local targetForward = global.target:GetWorldForward()
  local targetLeft = global.target:GetWorldLeft()
  local distanceFrontOfTarget = 4
  local distanceLeftOfTarget = 0
  local targetLocation = targetPosition + targetForward * distanceFrontOfTarget + targetLeft * distanceLeftOfTarget
  local navMeshPoint = game.NavMesh.ClosestPoint(targetLocation)
  engine.DrawFillSphere(targetPosition, 0.25, 65280, 3)
  engine.DrawFillSphere(targetLocation, 0.25, 16711680, 3)
  local concussionParams = {
    Tweak = "CNC_FIREMODE_PROXIMITY_MINE",
    WorldLocation = navMeshPoint,
    GameObject = ai,
    EnemyId = ai:GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
end
function LuaHook_FireMode_ProximityMine_Right(ai)
  local targetPosition = global.target:GetWorldPosition()
  local targetForward = global.target:GetWorldForward()
  local targetLeft = global.target:GetWorldLeft()
  local distanceFrontOfTarget = 4
  local distanceLeftOfTarget = -4
  local targetLocation = targetPosition + targetForward * distanceFrontOfTarget + targetLeft * distanceLeftOfTarget
  local navMeshPoint = game.NavMesh.ClosestPoint(targetLocation)
  local concussionParams = {
    Tweak = "CNC_FIREMODE_PROXIMITY_MINE",
    WorldLocation = navMeshPoint,
    GameObject = ai,
    EnemyId = ai:GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
end
mortarsRemaining = 0
global.mortarsActive = false
function LuaHook_FireMode_LeapStompMortar(ai, data)
  local targetPosition = ai:GetWorldPosition()
  local targetForward = ai:GetWorldForward()
  local targetLeft = ai:GetWorldLeft()
  local difficulty = ai:AttributeGetValue("Difficulty")
  global.mortarsActive = true
  mortarsRemaining = 15
  if difficulty == 1 then
    mortarsRemaining = 0
  elseif difficulty == 2 then
    mortarsRemaining = 10
  elseif difficulty == 3 then
    mortarsRemaining = 15
  elseif difficulty == 4 then
    mortarsRemaining = 20
  end
  SpawnMortars(0, ai, targetPosition, targetForward, targetLeft, 0, 0)
end
function SpawnMortars(delayTimer, ai, targetPosition, targetForward, targetLeft, oldDistanceFrontOfTarget, oldDistanceLeftOfTarget)
  local directionFront = math.random(1, 2) * 2 - 3
  local directionLeft = math.random(1, 2) * 2 - 3
  local distanceFrontOfTarget = math.random(10, 15) / 10 * directionFront
  if distanceFrontOfTarget == oldDistanceFrontOfTarget then
    distanceFrontOfTarget = distanceFrontOfTarget * -1
  end
  local distanceLeftOfTarget = math.random(10, 15) / 10 * directionLeft
  if distanceLeftOfTarget == oldDistanceLeftOfTarget then
    distanceLeftOfTarget = oldDistanceLeftOfTarget * -1
  end
  print("(" .. distanceLeftOfTarget .. ", " .. distanceFrontOfTarget .. ")")
  local targetLocation = targetPosition + targetForward * distanceFrontOfTarget + targetLeft * distanceLeftOfTarget
  local navMeshPoint = game.NavMesh.ClosestPoint(targetLocation)
  local spawnTimer = 0.125
  local concussionParams
  concussionParams = {
    Tweak = "CNC_FIREMODE_LEAPSTOMP_MORTAR",
    WorldLocation = navMeshPoint,
    GameObject = ai,
    EnemyId = ai:GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
  mortarsRemaining = mortarsRemaining - 1
  if global.mortarsActive == true then
    if mortarsRemaining > 0 then
      timer.StartCreatureTimer(spawnTimer, SpawnMortars, ai, targetPosition, targetForward, targetLeft, distanceFrontOfTarget, distanceLeftOfTarget)
    else
      global.mortarsActive = false
      return
    end
  else
    global.mortarsActive = false
    return
  end
end
function LuaHook_CreateIceHazardBreakable(ai)
  local spawnArgs = {
    SpawnObject = "goBaldur_IceMode_LeapStomp_Hazard_Brk",
    HookName = "OnHazardCreated",
    OffsetPosition = ai:GetWorldPosition()
  }
  ai:SpawnGameObject(spawnArgs)
end
function OnHazardCreated(ai, go, spawnedObject)
end
function LuaHook_AttIceBomb_LeftHand(ai)
  LuaHook_AttIceBomb_Center(ai, "LEFT")
end
function LuaHook_AttIceBomb_RightHand(ai)
  LuaHook_AttIceBomb_Center(ai, "RIGHT")
end
function LuaHook_AttIceBomb_Center(ai, hand)
  if global.target == nil then
    global.target = player
  end
  local targetPosition = global.target:GetWorldPosition()
  local targetLocation = global.target:GetWorldPosition()
  local targetForward = global.target:GetWorldForward()
  local targetLeft = global.target:GetWorldLeft()
  local distanceFrontOfTarget = 1
  local distanceLeftOfTarget = 0
  local velocityMultiplier = 0.5
  local emitJoint
  if hand == "LEFT" then
    emitJoint = "JOLeftMiddleA1"
  elseif hand == "RIGHT" then
    emitJoint = "JORightMiddleA1"
  end
  local kratosVelocity = global.target:GetVelocity()
  kratosVelocity = kratosVelocity * velocityMultiplier
  local angleFwd = DL.GetAngleBetweenVector(targetForward, kratosVelocity)
  local angleLeft = DL.GetAngleBetweenVector(targetLeft, kratosVelocity)
  if 0.5 < kratosVelocity:Length() then
    kratosVelocity = kratosVelocity:Normalized() * velocityMultiplier
    if -45 <= angleFwd and angleFwd <= 45 then
      targetForward = targetForward * distanceFrontOfTarget + kratosVelocity
    else
      targetForward = targetForward * distanceFrontOfTarget
    end
    if -45 <= angleLeft and angleLeft <= 45 then
      targetLeft = targetLeft * distanceLeftOfTarget + kratosVelocity
    elseif angleLeft <= -135 or 135 <= angleLeft then
      targetLeft = targetLeft * distanceLeftOfTarget + kratosVelocity
    else
      targetLeft = targetLeft * distanceLeftOfTarget
    end
  else
    targetForward = targetForward * distanceFrontOfTarget
    targetLeft = targetLeft * distanceLeftOfTarget
  end
  targetLocation = targetPosition + targetForward + targetLeft
  local arrowData = {}
  engine.DrawFillSphere(targetPosition, 0.25, 65280, 3)
  engine.DrawFillSphere(targetLocation, 0.25, 16711680, 3)
  arrowData.Tweak = "ARR_ICEMODE_ICEBOMB_LUA"
  arrowData.Creator = ai
  arrowData.CreatorEmitJoint = emitJoint
  arrowData.TargetLocation = targetLocation
  arrowData.EmitOffset = engine.Vector.New(0, 0, 0)
  game.Combat.EmitArrow(arrowData)
end
function LuaHook_FindThrowableProjection(ai)
  local NearbyCreatures = ai:FindEnemies(100)
  local closestDist = 100
  local closestCreature
  for _, creature in ipairs(NearbyCreatures) do
    if creature ~= ai and creature:GetHitPoints() > 0 and not ai:IsDoingSyncMove() and creature:GetName() == "projection20" then
      local distance = game.AIUtil.Distance(ai, creature)
      if closestDist > distance then
        closestCreature = creature
        closestDist = distance
        break
      end
    end
  end
  if closestCreature ~= nil then
    global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_PROJECTION20_ONLY"
    ai:SetCombatTarget(closestCreature)
    ai:TriggerMoveEvent("LE_AttChaseProjection")
  else
    global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY"
  end
end
function LuaHookDecision_IsInNavigationMove(ai, data)
  if ai:IsInNavigationMove() then
    return data:FindOutcomeBranchesEntry("True")
  else
    return data:FindOutcomeBranchesEntry("False")
  end
end
function LuaHookDecision_IsPlayerInSynchMove()
  if player:IsDoingSyncMove() then
    return true
  else
    return false
  end
end
function LuaHookDecision_OppositeWeaponEquipped(ai)
  if player:GetCurrentWeapon() == "Axe" and ai:PickupIsAcquired("Baldur_FireMode") then
    return true
  elseif player:GetCurrentWeapon() == "Blades" and ai:PickupIsAcquired("Baldur_IceMode") then
    return true
  else
    return false
  end
end
function LuaHook_BaldurComboIncrement(ai)
  if ai:PickupIsAcquired("Baldur_Invulnerable") then
    return
  end
  if ai:PickupIsAcquired("Baldur_ComboCounter") then
    if ai:PickupGetStage("Baldur_ComboCounter") < 3 then
      ai:PickupIncrementStage("Baldur_ComboCounter")
    else
      ai:PickupSetStage("Baldur_ComboCounter", 0)
    end
  end
end
function LuaHook_BaldurComboResetIfMaxed(ai)
  if ai:PickupIsAcquired("Baldur_ComboCounter") and ai:PickupGetStage("Baldur_ComboCounter") >= 3 then
    ai:PickupSetStage("Baldur_ComboCounter", 0)
  end
end
function LuaHook_BaldurComboReset(ai)
  if ai:PickupIsAcquired("Baldur_ComboCounter") and ai:PickupGetStage("Baldur_ComboCounter") > 0 then
    ai:PickupSetStage("Baldur_ComboCounter", 0)
  end
end
local bossHealthBarOn = false
function LuaHook_ShowHealth(ai)
  if bossHealthBarOn == false then
    if ai:PickupIsAcquired("Baldur_StoneMasonBoss") == false then
      mpicon.level.Create(ai:GetGameObject(), "BOSS_HEALTH_BAR")
      mpicon.level.SetTextObjectText(ai:GetGameObject(), "BOSS_HEALTH_BAR", "bossName", "[MSG:" .. 38182 .. "]")
      mpicon.level.SetTextObjectText(ai, "BOSS_HEALTH_BAR", "icon", "\198\162")
    end
    if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
      if ai:PickupGetStage("Baldur_StoneMasonBoss") == 0 then
        mpicon.level.Create(ai:GetGameObject(), "BOSS_HEALTH_BAR_SM_BALDUR_PH_0")
        mpicon.level.SetTextObjectText(ai:GetGameObject(), "BOSS_HEALTH_BAR_SM_BALDUR_PH_0", "bossName", "[MSG:" .. 38185 .. "]")
        mpicon.level.SetTextObjectText(ai, "BOSS_HEALTH_BAR_SM_BALDUR_PH_0", "icon", "\198\162")
      else
        mpicon.level.Create(ai:GetGameObject(), "BOSS_HEALTH_BAR")
        mpicon.level.SetTextObjectText(ai:GetGameObject(), "BOSS_HEALTH_BAR", "bossName", "[MSG:" .. 38185 .. "]")
        mpicon.level.SetTextObjectText(ai, "BOSS_HEALTH_BAR", "icon", "\198\162")
      end
    end
    bossHealthBarOn = true
  end
end
function LuaHook_HideHealth(ai)
  if bossHealthBarOn == true then
    if ai:PickupIsAcquired("Baldur_StoneMasonBoss") == false then
      mpicon.level.Off(ai:GetGameObject(), "BOSS_HEALTH_BAR")
    end
    if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
      if ai:PickupGetStage("Baldur_StoneMasonBoss") == 0 then
        mpicon.level.Off(ai:GetGameObject(), "BOSS_HEALTH_BAR_SM_BALDUR_PH_0")
      else
        mpicon.level.Off(ai:GetGameObject(), "BOSS_HEALTH_BAR")
      end
    end
    bossHealthBarOn = false
  end
end
function LuaHook_SM_Phase0_ResetHealth(ai)
  global.healthValue_SM_Phase0 = ai:LookupConstant("BALDUR_HEALTH_VALUE_STONEMASON_PH_0")
end
function LuaHook_ShowWound_AxeFirstElement(ai)
  ai:ShowJoint(ai:GetJointIndex("StoneMasonFight_Axe01"))
end
function LuaHook_HideWound_AxeFirstElement(ai)
  ai:HideJoint(ai:GetJointIndex("StoneMasonFight_Axe01"))
end
function LuaHook_ShowWound_BladeSlingshot(ai)
  ai:ShowJoint(ai:GetJointIndex("StoneMasonFight_Blades"))
end
function LuaHook_HideWound_BladeSlingshot(ai)
  ai:HideJoint(ai:GetJointIndex("StoneMasonFight_Blades"))
end
function LuaHook_ShowWound_AxeTomahawk(ai)
  ai:ShowJoint(ai:GetJointIndex("StoneMasonFight_Axe02"))
end
function LuaHook_HideWound_AxeTomahawk(ai)
  ai:HideJoint(ai:GetJointIndex("StoneMasonFight_Axe02"))
end
function LuaHook_ShowWound_ArrowSkydive_01(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow01"))
end
function LuaHook_ShowWound_ArrowSkydive_02(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow02"))
end
function LuaHook_ShowWound_ArrowSkydive_03(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow03"))
end
function LuaHook_ShowWound_ArrowSkydive_04(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow04"))
end
function LuaHook_ShowWound_ArrowSkydive_05(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow05"))
end
function LuaHook_ShowWound_ArrowSkydive_All(ai)
  ai:ShowJoint(ai:GetJointIndex("arrow01"))
  ai:ShowJoint(ai:GetJointIndex("arrow02"))
  ai:ShowJoint(ai:GetJointIndex("arrow03"))
  ai:ShowJoint(ai:GetJointIndex("arrow04"))
  ai:ShowJoint(ai:GetJointIndex("arrow05"))
end
function LuaHook_HideWound_ArrowSkydive_All(ai)
  ai:HideJoint(ai:GetJointIndex("arrow01"))
  ai:HideJoint(ai:GetJointIndex("arrow02"))
  ai:HideJoint(ai:GetJointIndex("arrow03"))
  ai:HideJoint(ai:GetJointIndex("arrow04"))
  ai:HideJoint(ai:GetJointIndex("arrow05"))
end
function LuaHook_HideWound_All(ai)
  LuaHook_HideWound_AxeFirstElement(ai)
  LuaHook_HideWound_BladeSlingshot(ai)
  LuaHook_HideWound_AxeTomahawk(ai)
  LuaHook_HideWound_ArrowSkydive_All(ai)
end
function LuaHook_PastHauntsBaldurCine()
  game.Audio.StartMusic("SND_MX_HEL_past_haunts_baldur")
  game.Audio.SetWwiseState("Scene_494_HelheimReturn_PastHauntsBaldur", "in")
end
function LuaHook_PastHauntsBaldurCineEND()
  game.Audio.SetWwiseState("Scene_494_HelheimReturn_PastHauntsBaldur", "out")
end
picklelib.Install()
function Pickle(GO)
  return {
    global = global
  }
end
function Unpickle(GO, t)
  global = t.global
end
