local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local picklelib = require("core.pickle")
local components = require("design.components")
local globalLuaHooks = require("design.GlobalLuaHooks")
local timer = require("creature.timer")
globalLuaHooks.InstallHooks()
local mpicon = require("ui.mpicon")
attachments = {}
Brain_Core = require("behavior.corebrain")
constants = {}
global = {}
global.currentType = nil
local valkyrieUniqueName = "Valkyrie"
local valkyrieIcon = "\198\150"
local summonPowerLevel = "5"
local bossHealthBarOn = false
local bossHealthBarInitialized = false
function LuaHook_ClearArrow(ai, data)
  game.Combat.RemoveArrows(ai)
end
function LuaHook_ForceTargetKratos(ai, data)
  local player = game.Player.FindPlayer()
  Brain_Core:SetTarget(game.Player.FindPlayer(), global, constants)
  _G.global.target = player
  ai:RequestHighPriorityUpdate()
  local facing = (player:GetWorldPosition() - ai:GetWorldPosition()):Normalized()
  ai:Warp(ai:GetWorldPosition(), facing)
end
function LuaHook_ShowHealth(ai)
  if bossHealthBarOn == false then
    mpicon.level.Create(ai:GetGameObject(), "BOSS_HEALTH_BAR_VALKYRIE")
    mpicon.level.SetTextObjectText(ai, "BOSS_HEALTH_BAR_VALKYRIE", "bossName", valkyrieUniqueName)
    mpicon.level.SetTextObjectText(ai, "BOSS_HEALTH_BAR_VALKYRIE", "icon", valkyrieIcon)
    if ai.SetOverrideStatusEffectIconParent ~= nil then
      ai:SetOverrideStatusEffectIconParent("BOSS_HEALTH_BAR_VALKYRIE")
    end
    bossHealthBarOn = true
    bossHealthBarInitialized = true
  end
end
function LuaHook_HideHealth(ai)
  if bossHealthBarOn == true then
    mpicon.level.Off(ai:GetGameObject(), "BOSS_HEALTH_BAR_VALKYRIE")
    bossHealthBarOn = false
  end
end
function LuaHook_CheckCanReact(ai)
  if ai:MeterGetValue("CD_ComboBreaker") >= 99 then
    return false
  else
    return true
  end
end
function LuaHook_ValkyrieSpawnLoot(ai)
  print("I should be making some dumb bags right now")
  print(ai)
  game.Loot.RollConditionSet("VALKYRIE_KILL_ROLL", "HERO", ai, ai:GetName())
end
function LuaHook_KillNearbyDraugr(ai)
  if ai:PickupIsAcquired("ValkyrieSummoner") or ai:PickupIsAcquired("ValkyrieQueen") then
    local nearbyCreatures = ai:FindFriendlies(100)
    for _, creature in ipairs(nearbyCreatures) do
      if creature ~= ai and creature:GetHitPoints() > 0 and creature:GetName() == "draugr00" then
        creature:MeterSetValue("Health", 0)
      end
    end
  end
end
function LuaHook_KillNearbyDraugrForced(ai)
  if ai:PickupIsAcquired("ValkyrieSummoner") or ai:PickupIsAcquired("ValkyrieQueen") then
    local nearbyCreatures = ai:FindFriendlies(100)
    for _, creature in ipairs(nearbyCreatures) do
      if creature:GetName() == "draugr00" then
        creature:Destroy()
      end
    end
  end
end
local CheckCollision = function(ai, direction, evadeDistance)
  local dirToCheck
  if direction == "Back" then
    dirToCheck = ai:GetWorldForward()
    dirToCheck = dirToCheck * -evadeDistance
  elseif direction == "Left" then
    dirToCheck = ai:GetWorldLeft()
    dirToCheck = dirToCheck * evadeDistance
  elseif direction == "Right" then
    dirToCheck = ai:GetWorldLeft()
    dirToCheck = dirToCheck * -evadeDistance
  elseif direction == "Front" then
    dirToCheck = ai:GetWorldForward()
    dirToCheck = dirToCheck * evadeDistance
  end
  local hit = game.World.RaycastCollision(ai:GetWorldPosition() + engine.Vector.New(0, 0.7, 0), ai:GetWorldPosition() + engine.Vector.New(0, 0.7, 0) + dirToCheck, {
    SourceGameObject = ai,
    EntityType = game.CollisionType.New("kEnvironment", "kPlayer")
  })
  if hit == nil then
    return false
  end
  return true
end
function pickSpawnMove(ai)
  local spawnContext = ai:GetContext()
  if DL.CheckCreatureContext(spawnContext, "UNAWARE") then
    global.aggroState = "UNAWARE"
  elseif DL.CheckCreatureContext(spawnContext, "NONHOSTILE") then
    global.aggroState = "NONHOSTILE"
  else
    global.aggroState = "INCOMBAT"
  end
end
local setupValkyrie = function(ai, valkyrie_type)
  global.DTree = string.format(constants.defaultDTree, string.upper(valkyrie_type))
  local sonDtreeOverride = {
    dTree = global.DTree
  }
  ai:CallScript("AggroChangeAttribute", sonDtreeOverride)
  ai:PickupRelinquishAll()
  ai:PickupAcquire("HealthThreshold")
  local pickup = "Valkyrie" .. valkyrie_type
  ai:PickupAcquire(pickup)
  if not ai:PickupIsAcquired("ValkyriePhase") then
    ai:PickupAcquire("ValkyriePhase")
  end
  if not game.CHECK_FEATURE("PREEMPT_SPAWN_CONFIG") then
    DL.switch(valkyrie_type):caseof({
      Slugger = function()
        ai:SetCharacterConfig("raven")
      end,
      Evader = function()
        ai:SetCharacterConfig("eagle")
      end,
      Defender = function()
        ai:SetCharacterConfig("bear")
      end,
      Fire = function()
        ai:SetCharacterConfig("vulture")
      end,
      Ice = function()
        ai:SetCharacterConfig("boar")
      end,
      Shooter = function()
        ai:SetCharacterConfig("owl")
      end,
      Summoner = function()
        ai:SetCharacterConfig("ram")
      end,
      Grappler = function()
        ai:SetCharacterConfig("stag")
      end,
      Queen = function()
        ai:SetCharacterConfig("queen")
      end
    })
  end
  DL.switch(valkyrie_type):caseof({
    Slugger = function()
      valkyrieIcon = "\198\150"
      valkyrieUniqueName = "[MSG:" .. 39425 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_gnr")
    end,
    Evader = function()
      valkyrieIcon = "\198\153"
      valkyrieUniqueName = "[MSG:" .. 39432 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_olr")
    end,
    Defender = function()
      valkyrieIcon = "\198\154"
      valkyrieUniqueName = "[MSG:" .. 39430 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_eir")
    end,
    Fire = function()
      valkyrieIcon = "\198\148"
      valkyrieUniqueName = "[MSG:" .. 39426 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_gnd")
    end,
    Ice = function()
      valkyrieIcon = "\198\147"
      valkyrieUniqueName = "[MSG:" .. 39431 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_hld")
    end,
    Shooter = function()
      valkyrieIcon = "\198\152"
      valkyrieUniqueName = "[MSG:" .. 39427 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_gei")
    end,
    Summoner = function()
      valkyrieIcon = "\198\151"
      valkyrieUniqueName = "[MSG:" .. 39428 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_kaa")
    end,
    Grappler = function()
      valkyrieIcon = "\198\149"
      valkyrieUniqueName = "[MSG:" .. 39429 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_rot")
    end,
    Queen = function()
      valkyrieIcon = "\198\145"
      valkyrieUniqueName = "[MSG:" .. 39424 .. "]"
      game.Audio.SetWwiseSwitch(ai, "CHR_Valkyrie", "CHR_Valkyrie_sig")
    end
  })
  local combatModule = "valkyrie.%sCombat"
  if global.currentType ~= nil then
    local Module = string.format(combatModule, global.currentType)
    package.loaded[Module] = nil
    _G[Module] = nil
  end
  global.currentType = valkyrie_type
  combatModule = string.format(combatModule, valkyrie_type)
  if package.preload[combatModule] then
    require(combatModule)
  end
  ai:HideJoint(ai:GetJointIndex("phaseJoint"))
  local valkyriesDefeated = game.Level.GetVariable("ValkyriesDefeated")
  if valkyriesDefeated ~= nil and valkyriesDefeated < 3 and game.GetNewGamePlus() == false then
    local scaleAmount = 3 - valkyriesDefeated
    local maxScaleDown = 0
    if valkyrie_type == "Slugger" then
      maxScaleDown = 3
    elseif valkyrie_type == "Shooter" then
      maxScaleDown = 2
    elseif valkyrie_type == "Summoner" then
      maxScaleDown = 2
    end
    if scaleAmount > maxScaleDown then
      scaleAmount = maxScaleDown
    end
    if scaleAmount == 3 then
      ai:PickupAcquire("ValkyrieTrainingWheels")
      ai:PickupSetStage("ValkyrieTrainingWheels", 0)
      summonPowerLevel = "4"
    elseif scaleAmount == 2 then
      ai:PickupAcquire("ValkyrieTrainingWheels")
      ai:PickupSetStage("ValkyrieTrainingWheels", 1)
      summonPowerLevel = "4"
    elseif scaleAmount == 1 then
      ai:PickupAcquire("ValkyrieTrainingWheels")
      ai:PickupSetStage("ValkyrieTrainingWheels", 2)
      summonPowerLevel = "5"
    end
  end
end
function OnAIPostSpawn(ai)
  if StartConfig ~= nil then
    setupValkyrie(ai, StartConfig.Type)
  end
  global.navData.navSpeedWalk = ai:LookupFloatConstant("NAV_SPEED_WALK")
  global.navData.navSpeedJog = ai:LookupFloatConstant("NAV_SPEED_WALK")
  ai:SetDirectAimingModeIsEnabled(true)
end
function OnAIUpdate(ai)
  components.UpdateComponents(ai, attachments)
  if Brain_Core:GetDebugTable(ai) then
    local debugTable = {}
    Brain_Core:AddCustomDebugTable(debugTable)
  end
end
function _G.OnAICreateLuaState(ai)
  components.StartComponents(ai, attachments)
  _G.OnAICreateLuaState = nil
end
function Brain_Core:OnUpdateMotion(ai, global, constants, actuatorData, avoidanceArgs)
end
local valkHitFXTimer = 0
local vkHealth_SpawnMax = 7
local vkHealth_SpawnCooldown = 15
local vkHealth_SpawnThresholdHigh = 0.7
local vkHealth_SpawnThresholdLow = 0.5
local vkHealth_SpawnDamageReq = 0.1
local valkHealthSpawnTimer = 0
local valkHealthSpawnCount = 0
local valkHealthPercentReqToSpawn = 1
function Brain_Core:OnHitReaction(event, ai, global, constants)
  if valkHealthSpawnTimer < game.GetGameTime() and valkHealthSpawnCount < vkHealth_SpawnMax and ai ~= nil and ai.GroundLevel ~= nil then
    local player = game.Player.FindPlayer()
    local healthPerc = player:MeterGetValue("Health") / player:MeterGetMax("Health")
    local valkPerc = ai:MeterGetValue("Health") / ai:MeterGetMax("Health")
    local spawnAllowed = false
    if valkPerc < valkHealthPercentReqToSpawn and (healthPerc < vkHealth_SpawnThresholdLow or healthPerc < vkHealth_SpawnThresholdHigh and math.random(0, 100) <= 12) then
      spawnAllowed = true
    end
    if spawnAllowed then
      local spawnArgs1 = {
        SpawnObject = "golootgenerichealthrune",
        OffsetPosition = ai:GetWorldPosition(),
        Level = game.Level.GetPermLevel()
      }
      ai:SpawnGameObject(spawnArgs1)
      valkHealthSpawnTimer = game.GetGameTime() + vkHealth_SpawnCooldown
      valkHealthSpawnCount = valkHealthSpawnCount + 1
      valkHealthPercentReqToSpawn = valkPerc - vkHealth_SpawnDamageReq
    end
  end
  if valkHitFXTimer < game.GetGameTime() then
    valkHitFXTimer = game.GetGameTime() + 0.2
  end
  if 0 >= ai:PickupGetStage("ValkyriePhase") and ai.PercentageOfHealth < 75 then
    ai:PickupSetStage("ValkyriePhase", 1)
  elseif 1 >= ai:PickupGetStage("ValkyriePhase") and ai.PercentageOfHealth < 50 then
    ai:PickupSetStage("ValkyriePhase", 2)
  elseif ai:PickupGetStage("ValkyriePhase") <= 2 and ai.PercentageOfHealth < 25 then
    ai:PickupSetStage("ValkyriePhase", 3)
  end
end
function Brain_Core:OnEnterCombat(ai, global, constants)
  if bossHealthBarInitialized == true then
    LuaHook_ShowHealth(ai, nil)
  end
end
function Brain_Core:OnExitCombat(ai, global, constants)
  LuaHook_HideHealth(ai, nil)
end
function IsTargetSpecialActive(targetCreature)
  if targetCreature:PickupIsSlotActive("WeaponSpecial_Heavy") == true or targetCreature:PickupIsSlotActive("WeaponSpecial_Light") == true then
    return true
  else
    return false
  end
end
function ValkyriePlayConcussion(ai, concussionName, position)
  local player = game.Player.FindPlayer()
  local concussionParams = {
    Tweak = concussionName,
    WorldLocation = position,
    GameObject = ai,
    EnemyId = player:GetID()
  }
  game.Combat.PlayConcussion(concussionParams)
end
function ValkyrieFireProjectile_Shared(ai, projectileName, position)
  local arrowData = {}
  arrowData.Tweak = projectileName
  arrowData.Creator = ai
  arrowData.CreatorEmitJoint = "zeroJoint"
  arrowData.TargetLocation = position
  arrowData.EmitOffset = engine.Vector.New(0, 15, 0)
  game.Combat.EmitArrow(arrowData)
end
function ValkyrieFireProjectile(ai, projectileName, position)
  ValkyrieFireProjectile_Shared(ai, projectileName, position)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", position)
end
function ValkyrieFireProjectile_NoDuck(ai, projectileName, position)
  ValkyrieFireProjectile_Shared(ai, projectileName, position)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD_NODUCK", position)
end
function ValkyrieIceProjectile(ai, projectileName, position)
  local arrowData = {}
  arrowData.Tweak = projectileName
  arrowData.Creator = ai
  arrowData.CreatorEmitJoint = "zeroJoint"
  arrowData.TargetLocation = position
  arrowData.EmitOffset = engine.Vector.New(0, 1.5, 0)
  game.Combat.EmitArrow(arrowData)
end
function GetPositionPlayerVelocity(ai, speedLimit)
  local kratosVelocity = global.target:GetVelocity()
  if kratosVelocity:Length() > 0.5 then
    kratosVelocity = kratosVelocity:Normalized() * speedLimit
  else
    kratosVelocity = engine.Vector.New(math.random(), 0, math.random()):Normalized() * speedLimit
  end
  return global.target:GetWorldPosition() + kratosVelocity
end
function GetPositionAIForward(ai, offset)
  local faceVec = (game.Player:FindPlayer():GetWorldPosition() - ai:GetWorldPosition()):Normalized()
  return ai:GetWorldPosition() + faceVec * offset
end
function GetPositionPlayerForward(ai, offset, randomSide)
  local faceVec = game.Player:FindPlayer():GetWorldForward()
  local leftVec = game.Player:FindPlayer():GetWorldLeft()
  local halfRandomSide = randomSide / 2
  return game.Player:FindPlayer():GetWorldPosition() + faceVec * offset + (math.random() * randomSide - halfRandomSide) * leftVec
end
function LuaHook_ValkyrieProjectile1(ai, data)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", GetPositionPlayerForward(ai, 13, 1.5))
end
function LuaHook_ValkyrieProjectile2(ai, data)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", GetPositionPlayerForward(ai, 8, 1.5))
end
function LuaHook_ValkyrieProjectile3(ai, data)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", GetPositionPlayerForward(ai, 3, 0.5))
end
function LuaHook_ValkyrieProjectile4(ai, data)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", GetPositionPlayerForward(ai, -2, 0.5))
end
function LuaHook_ValkyrieProjectileStomp(ai, data)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", GetPositionPlayerForward(ai, 1, 0))
end
function LuaHook_ValkyrieProjectileStompLoop(ai, data)
  ValkyrieFireProjectile_NoDuck(ai, "ARR_VALKYRIE_FIRE_PROJECTILE_NODUCK", GetPositionPlayerForward(ai, 1, 0))
end
function LuaHook_ValkyrieProjectileHail(ai, data)
  local randX = math.random() * 1 - 0.5
  local randZ = math.random() * 1 - 0.5
  local pos = GetPositionAIForward(ai, 8) + engine.Vector.New(randX, 1.5, randZ)
  ValkyrieIceProjectile(ai, "ARR_VALKYRIE_ICE_PROJECTILE", pos)
end
function LuaHook_ValkyrieProjectileHailHoming(ai, data)
  local pos = GetPositionAIForward(ai, 8) + engine.Vector.New(0, 1.5, 0)
  local arrowData = {}
  arrowData.Tweak = "ARR_VALKYRIE_ICE_PROJECTILE_HOMING"
  arrowData.Creator = ai
  arrowData.CreatorEmitJoint = "zeroJoint"
  arrowData.Target = global.target
  arrowData.TargetLocation = pos
  arrowData.EmitOffset = engine.Vector.New(0, 1.5, 0)
  game.Combat.EmitArrow(arrowData)
end
function LuaHook_ValkyrieProjectileHailLarge(ai, data)
  local pos = GetPositionAIForward(ai, 8) + engine.Vector.New(0, 1.5, 0)
  ValkyrieIceProjectile(ai, "ARR_VALKYRIE_ICE_PROJECTILE_LARGE", pos)
end
function LuaHook_ValkyrieProjectileStraight1(ai, data)
  local firePos = GetPositionAIForward(ai, 3)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", firePos)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", firePos)
end
function LuaHook_ValkyrieProjectileStraight2(ai, data)
  local firePos = GetPositionAIForward(ai, 7)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", firePos)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", firePos)
end
function LuaHook_ValkyrieProjectileStraight3(ai, data)
  local firePos = GetPositionAIForward(ai, 11)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", firePos)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", firePos)
end
function LuaHook_ValkyrieProjectileStraight4(ai, data)
  local firePos = GetPositionAIForward(ai, 15)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", firePos)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", firePos)
end
function LuaHook_ValkyrieProjectileStraight5(ai, data)
  local firePos = GetPositionAIForward(ai, 19)
  ValkyrieFireProjectile(ai, "ARR_VALKYRIE_FIRE_PROJECTILE", firePos)
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL_NO_PAYLOAD", firePos)
end
function LuaHook_ValkyrieFireFromValk(ai, data)
  local faceVec = (game.Player:FindPlayer():GetWorldPosition() - ai:GetWorldPosition()):Normalized()
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL", GetPositionAIForward(ai, 3))
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL", GetPositionAIForward(ai, 7))
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL", GetPositionAIForward(ai, 11))
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL", GetPositionAIForward(ai, 15))
  ValkyriePlayConcussion(ai, "CNC_VALKYRIE_FIRE_GROUND_TELL", GetPositionAIForward(ai, 19))
end
function LuaHook_ValkyrieWingBlind(ai, data)
  if ai:CheckDecision("tweak_Decision_OnCamera") then
    local concussionParams = {
      Tweak = "CNC_VALKYRIE_WINGGUST_BLIND",
      WorldLocation = ai:GetWorldPosition(),
      GameObject = ai,
      EnemyId = game.Player.FindPlayer():GetID()
    }
    game.Combat.PlayConcussion(concussionParams)
  end
end
local grabRepeatNum = 0
function LuaHook_ValkyrieGrabReset(ai, data)
  grabRepeatNum = 3
  if game.GetNewGamePlus() then
    grabRepeatNum = 5
  end
end
local multiStompCooldown = 0
function LuaHook_ValkyrieGrabResetNGPlus(ai, data)
  if multiStompCooldown < game.GetGameTime() then
    multiStompCooldown = game.GetGameTime() + 8
    grabRepeatNum = 3
  end
end
function LuaHookDecision_ValkyrieGrabRepeat(ai, data)
  if game.GetNewGamePlus() and ai:IsPlayingMove("MOV_SwoopLoop") then
    grabRepeatNum = grabRepeatNum - math.random(1, 2)
  end
  local retVal = true
  grabRepeatNum = grabRepeatNum - 1
  if grabRepeatNum <= 0 then
    retVal = false
  end
  return retVal
end
local maxEnemies = 6
local sumPos = 0
local sumWaveType = -1
function LuaHook_SpawnGhost(C, data)
  local spawnPos = C:GetWorldPosition()
  if game.CHECK_FEATURE("PREEMPT_SPAWN_CONFIG") then
    local charConfig = "raven"
    if global.currentType == "Slugger" then
      charConfig = "raven"
    elseif global.currentType == "Evader" then
      charConfig = "eagle"
    elseif global.currentType == "Defender" then
      charConfig = "bear"
    elseif global.currentType == "Fire" then
      charConfig = "vulture"
    elseif global.currentType == "Ice" then
      charConfig = "boar"
    elseif global.currentType == "Shooter" then
      charConfig = "owl"
    elseif global.currentType == "Summoner" then
      charConfig = "ram"
    elseif global.currentType == "Grappler" then
      charConfig = "stag"
    elseif global.currentType == "Queen" then
      charConfig = "queen"
    end
    game.AI.Spawn(C.Level, spawnPos, C:GetWorldForward(), "CRT_ValkyrieGhost00", "BRA_Spawn", {
      Type = global.CurrentType
    }, charConfig)
  else
    local ghost = game.AI.Spawn(C.Level, spawnPos, C:GetWorldForward(), "CRT_ValkyrieGhost00", "BRA_Spawn")
    DL.switch(global.currentType):caseof({
      Slugger = function()
        ghost:SetCharacterConfig("raven")
      end,
      Evader = function()
        ghost:SetCharacterConfig("eagle")
      end,
      Defender = function()
        ghost:SetCharacterConfig("bear")
      end,
      Fire = function()
        ghost:SetCharacterConfig("vulture")
      end,
      Ice = function()
        ghost:SetCharacterConfig("boar")
      end,
      Shooter = function()
        ghost:SetCharacterConfig("owl")
      end,
      Summoner = function()
        ghost:SetCharacterConfig("ram")
      end,
      Grappler = function()
        ghost:SetCharacterConfig("stag")
      end,
      Queen = function()
        ghost:SetCharacterConfig("queen")
      end
    })
  end
end
function LuaHookDecision_ValkyrieNotAlone(ai, data)
  local retVal = false
  local player = game.Player.FindPlayer()
  local enemies = DL.FindLivingEnemies(player, 200)
  if 1 < #enemies then
    retVal = true
  end
  return retVal
end
function LuaHookDecision_ValkyrieAlone(ai, data)
  local retVal = true
  local player = game.Player.FindPlayer()
  local enemies = DL.FindLivingEnemies(player, 200)
  if 1 < #enemies then
    retVal = false
  end
  return retVal
end
function LuaHookDecision_ValkyrieAlmostAlone(ai, data)
  local retVal = true
  local player = game.Player.FindPlayer()
  local enemies = DL.FindLivingEnemies(player, 200)
  if 2 < #enemies then
    retVal = false
  end
  return retVal
end
local enemyRecord = 0
local ngRespawnTimer = 0
function LuaHookDecision_ValkyrieSummonCheck(ai, data)
  local retVal = true
  local player = game.Player.FindPlayer()
  local enemies = DL.FindLivingEnemies(player, 200)
  if #enemies > maxEnemies then
    retVal = false
  end
  if 2 < #enemies then
    if game.GetNewGamePlus() == false then
      ai:MeterSetValue("CD_Summon", 0)
      retVal = false
    elseif ngRespawnTimer > game.GetGameTime() or 4 < #enemies then
      ai:MeterSetValue("CD_Summon", 0)
      retVal = false
    elseif #enemies < enemyRecord then
      ngRespawnTimer = game.GetGameTime() + 25
      enemyRecord = #enemies
      ai:MeterSetValue("CD_Summon", 0)
      retVal = false
    end
  end
  if ai:MeterGetValue("CD_Summon") < 100 then
    retVal = false
  end
  return retVal
end
function LuaHook_SummonAlliesInitialize(C, data)
  enemyRecord = maxEnemies
  sumPos = 0
  sumWaveType = sumWaveType + 1
  if 3 < sumWaveType then
    sumWaveType = 1
  end
end
function GetSummonAllyConfigString(powerLevelInt, currentCharConfig, currentWeaponType)
  local charConfig
  if currentCharConfig == nil then
    return charConfig
  end
  local hasShield = false
  if currentWeaponType == "TwoHand" then
    currentCharConfig = "heavy"
  elseif currentWeaponType == "DualWield" then
    currentCharConfig = "light"
  elseif currentWeaponType == "Shield" then
    hasShield = true
    currentCharConfig = "medium"
  end
  local shieldString = ""
  if hasShield then
    shieldString = "_ArmShield"
  end
  local difficulty = "_veryeasy"
  if powerLevelInt == 1 then
    difficulty = "_veryeasy"
  elseif 1 < powerLevelInt and powerLevelInt < 4 then
    difficulty = "_easy"
  elseif 4 <= powerLevelInt and powerLevelInt < 6 then
    difficulty = "_moderate"
  elseif 6 <= powerLevelInt and powerLevelInt < 8 then
    difficulty = "_hard"
  else
    difficulty = "_veryhard"
  end
  charConfig = "d_" .. currentCharConfig .. difficulty .. shieldString
  return charConfig
end
function LuaHook_SummonAllies(C, data)
  C:MeterSetValue("CD_Summon", 0)
  local enemies = DL.FindLivingEnemies(game.Player.FindPlayer(), 100)
  local numEnemies = #enemies
  local spawnJointIndex = C:GetJointIndex("zerojoint")
  local spawnPos
  if sumPos == 0 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) + C:GetWorldLeft() * 2 + C:GetWorldForward() * 2)
  elseif sumPos == 1 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) - C:GetWorldLeft() * 2 + C:GetWorldForward() * 2)
  elseif sumPos == 2 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) + C:GetWorldLeft() * 4 - C:GetWorldForward() * 2)
  elseif sumPos == 3 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) - C:GetWorldLeft() * 4 - C:GetWorldForward() * 2)
  elseif sumPos == 4 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) + C:GetWorldLeft() * 6 - C:GetWorldForward() * 4)
  elseif sumPos == 5 then
    spawnPos = game.NavMesh.ClosestPoint(C:GetWorldJointPosition(spawnJointIndex) - C:GetWorldLeft() * 6 - C:GetWorldForward() * 4)
  end
  if spawnPos ~= nil and numEnemies < maxEnemies then
    local cancelSpawn = false
    if spawnPos.y < 13 or spawnPos.z < 131 then
      cancelSpawn = true
    end
    if cancelSpawn then
      return
    end
    local enemySummon
    if game.CHECK_FEATURE("PREEMPT_SPAWN_CONFIG") then
      local spawnWeaponType = "OneHand"
      local spawnBodyType = "medium"
      local spawnArmL = false
      local spawnLegs = false
      local specialSpawnThreshold = 0
      if C:PickupIsAcquired("ValkyriePhase") then
        specialSpawnThreshold = specialSpawnThreshold + C:PickupGetStage("ValkyriePhase")
      end
      if specialSpawnThreshold >= sumPos then
        if sumWaveType == 1 then
          spawnBodyType = "light"
          spawnWeaponType = "DualWield"
        elseif sumWaveType == 2 then
          spawnWeaponType = "Shield"
        elseif sumWaveType == 3 then
          spawnLegs = true
        end
      end
      if sumPos == 4 and 2 <= specialSpawnThreshold then
        spawnArmL = true
      end
      local charConfig = GetSummonAllyConfigString(3, spawnBodyType, spawnWeaponType)
      enemySummon = game.AI.Spawn(C.Level, spawnPos, C:GetWorldForward(), "CRT_Draugr00", "BRA_SpawnFromGroundValkyrie", {
        PowerLevel = summonPowerLevel,
        PowerLegs = spawnLegs,
        PowerArmL = spawnArmL,
        WeaponType = spawnWeaponType,
        BodyType = spawnBodyType
      }, charConfig)
    else
      enemySummon = game.AI.Spawn(C.Level, spawnPos, C:GetWorldForward(), "CRT_Draugr00", "BRA_SpawnFromGroundValkyrie", {
        PowerLevel = summonPowerLevel,
        WeaponType = "OneHand",
        BodyType = "medium"
      })
    end
    if enemySummon ~= nil then
      game.Encounters.AddSpawnToEncounter(C, enemySummon)
      sumPos = sumPos + 1
      if 5 < sumPos then
        sumPos = 0
      end
    end
  end
end
function LuaHook_MaceQuake(ai, data)
  local startPos = ai.WorldPosition + engine.Vector.New(0, 0.5, 0)
  local endPos = ai.WorldPosition + engine.Vector.New(0, -10, 0)
  local hit = game.World.RaycastCollision(startPos, endPos, {
    SourceGameObject = ai,
    FindAnything = true,
    EntityType = game.CollisionType.New("kEnvironment")
  }, false)
  if hit then
    local concussionParams = {
      Tweak = "CNC_VALKYRIE_QUAKE",
      WorldLocation = hit.Position,
      GameObject = ai,
      EnemyId = game.Player.FindPlayer():GetID()
    }
    game.Combat.PlayConcussion(concussionParams)
  end
end
function LuaHook_WarpToPlayer(ai, data)
  local playerCreature = game.Player.FindPlayer()
  ai:Warp(playerCreature:GetWorldPosition(), playerCreature:GetWorldPosition())
end
function LuaHookDecision_ValkyrieBackToWall(ai, data)
  local retVal = false
  if CheckCollision(ai, "Back", 8) then
    retVal = true
  end
  return retVal
end
function LuaHookDecision_ValkyrieRunningIntoWall(ai, data)
  local retVal = false
  if CheckCollision(ai, "Front", 3) then
    retVal = true
  end
  return retVal
end
function LuaHook_ValkyrieEvade_CollisionCheckSide(ai, data)
  local dir_table = {"Left", "Right"}
  local dir
  while 0 < #dir_table do
    dir = math.random(1, #dir_table)
    if CheckCollision(ai, dir_table[dir], 8) then
      table.remove(dir_table, dir)
    else
      break
    end
  end
  local move = "Evade"
  if dir ~= nil and 0 < #dir_table then
    move = move .. dir_table[dir]
  else
    move = move .. "Failed"
  end
  return data:FindOutcomeBranchesEntry(move)
end
function LuaHook_ValkyrieEvade_CollisionCheck(ai, data)
  local move = "EvadeRight"
  if CheckCollision(ai, "Back", 8) == false then
    move = "EvadeBack"
  elseif CheckCollision(ai, "Left", 8) == false then
    move = "EvadeLeft"
  end
  return data:FindOutcomeBranchesEntry(move)
end
function LuaHook_ValkyrieEvadeCancel_Check(ai, data)
  local targetCreature = ai:GetTargetCreature()
  local playerCreature = game.Player.FindPlayer()
  if targetCreature == nil or playerCreature == nil then
    return
  elseif targetCreature ~= playerCreature then
    return
  end
  local distToTarget = (targetCreature:GetWorldPosition() - ai:GetWorldPosition()):Length()
  local relativePosHero = DL.GetObjectARelativePositionToObjectB(targetCreature, ai)
  local relativePosSelf = DL.GetObjectARelativePositionToObjectB(ai, targetCreature)
  local defRadiusMax = 6
  if relativePosSelf == "objFront" and relativePosHero == "objFront" and distToTarget <= defRadiusMax and IsTargetSpecialActive(targetCreature) then
    return data:FindOutcomeBranchesEntry("Evade")
  else
    return
  end
end
function LuaHook_ValkyrieIsPlayerTooClose_Check(ai, data)
  local playerCreature = game.Player.FindPlayer()
  if playerCreature == nil then
    return
  end
  local distToPlayer = (playerCreature:GetWorldPosition() - ai:GetWorldPosition()):Length()
  local defRadiusMax = 6
  if distToPlayer <= defRadiusMax then
    return data:FindOutcomeBranchesEntry("true")
  end
end
function LuaHook_BackAttackOpportunity(ai, data)
  if not _G.global.target then
    return
  end
  local relativePos = DL.GetObjectARelativePositionToObjectB(_G.global.target, ai)
  if relativePos == "objBack" then
    ai:TriggerMoveEvent("kLEBackAttackOpportunity")
  else
    return
  end
end
function LuaHook_AddBlood_Valkyrie_RageFinisher(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 1)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 1)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 1)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.2)
end
function LuaHook_AddBlood_Valkyrie_Stomp(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.15)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.15)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.15)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.1)
end
function LuaHook_AddBlood_Valkyrie_Swoop(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.2)
end
function LuaHook_Fight()
  local player = game.Player.FindPlayer()
  local level = player.GroundLevel
  local valkyrieBanter = level:FindSingleGameObject("ValkyrieBanterController")
  if valkyrieBanter ~= nil then
    valkyrieBanter.Child:CallScript("ValkyrieFightStart")
  end
end
function LuaHook_SpawnExit()
  if game.FindLevel("Stn110_ChiselArena") then
    game.Audio.StartMusic("SND_MX_STN_valkyrie_fight_in")
  elseif game.FindLevel("Alf355_ChiselDungeon") then
    game.Audio.StartMusic("SND_MX_ALF_valkyrie_fight_in")
  elseif game.FindLevel("Foot250_ChiselArena") then
    game.Audio.StartMusic("SND_MX_FOOT_valkyrie_fight_in")
  elseif game.FindLevel("Hel350_ChiselArena") then
    game.Audio.StartMusic("SND_MX_HEL_valkyrie_fight_in")
  elseif game.FindLevel("Peak205_ChiselArena") then
    game.Audio.StartMusic("SND_MX_PEAK_valkyrie_fight_in")
  elseif game.FindLevel("Riv975_ChiselArena") then
    game.Audio.StartMusic("SND_MX_RIV_valkyrie_fight_in")
  elseif game.FindLevel("Nid310_NWRoom") then
    game.Audio.StartMusic("SND_MX_NIF_valkyrie_fight_in")
  end
end
function SetValkyrieType(ai, valkyrie_type)
  setupValkyrie(ai, valkyrie_type)
end
picklelib.Install()
function Pickle(GO)
  return {
    global = global
  }
end
function Unpickle(GO, t)
  global = t.global
end
