local statemachine = require("ai.statemachine")
local tablex = require("core.tablex")
local picklelib = require("core.pickle")
local DL = require("design.DesignerLibrary")
local uiCalls = require("ui.uicalls")
local traverselink = require("creature.traverselink")
traverselink.InstallHooks()
local globalLuaHooks = require("design.GlobalLuaHooks")
globalLuaHooks.InstallHooks()
Brain_Core = require("behavior.corebrain")
constants = {}
global = {}
global.ai = nil
local powerLevel = "1"
local wulverType
local pLevelInt = 0
local breathEffect
local wiggleStickTextShow = false
local wulverFrenzyActive = false
local rageGrabTextShow = false
function OnAIPostSpawn(ai)
  global.ai = ai
  setupCharacter(ai)
end
function Brain_Core:OnSetPowerlevelOverrideCustom(ai, global, constants, level, isElite, forced)
  powerLevel = level
  ToggleCharacterConfig(ai, forced)
end
function setupCharacter(ai)
  local moveName = ""
  local aggroState = ""
  local spawnContext = ai:GetContext()
  ai:PickupAcquire("Claws_MFX_Pickup", 0)
  if not ai:PickupIsAcquired("WulverInit") then
    ai:PickupAcquire("WulverInit")
  end
  local player = game.Player.FindPlayer()
  if breathEffect == nil then
    breathEffect = game.FX.Spawn("Wulver_BreathFX", nil, {
      Joint = "JOJaw1Tip",
      GameObject = ai,
      DeleteWithCreature = true
    })
    breathEffect:SetAnimFloatChannelDriver(player, "sliderfloatchannels._breathfxintensity")
  end
  if StartConfig ~= nil then
    if StartConfig.WulverType ~= nil then
      wulverType = StartConfig.WulverType
    end
    if StartConfig.EliteDifficulty ~= nil and StartConfig.EliteDifficulty and not ai:PickupIsAcquired("Wulver00_Elite") then
      ai:PickupAcquire("Wulver00_Elite")
    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.SpawnMove ~= nil then
      moveName = StartConfig.SpawnMove
    end
    if ai.GroundLevel ~= nil and string.find(ai.GroundLevel.Name, "Msp100_") ~= nil and ai:GetWorldPosition().y < 30 then
      ai:PickupAcquire("MuspelheimPenalty")
      if ai:PickupIsAcquired("Wulver00_Elite") then
        ai:PickupRelinquish("Wulver00_Elite")
      end
    end
  end
  ToggleCharacterConfig(ai, not game.CHECK_FEATURE("PREEMPT_SPAWN_CONFIG"))
  if aggroState == "" then
    if DL.CheckCreatureContext(spawnContext, "UNAWARE") then
      aggroState = "UNAWARE"
    elseif DL.CheckCreatureContext(spawnContext, "NONHOSTILE") then
      aggroState = "NONHOSTILE"
    else
      aggroState = "INCOMBAT"
    end
  end
  global.aggroState = aggroState
  if moveName ~= "" then
    ai:ForceMove(moveName)
  end
  DL.NewGamePlusLevelBoost(ai, "Wulver00_PowerLevel")
end
function ToggleCharacterConfig(ai, override)
  if powerLevel == "1" then
    pLevelInt = 1
  elseif powerLevel == "2" then
    pLevelInt = 2
  elseif powerLevel == "3" then
    pLevelInt = 3
  elseif powerLevel == "4" then
    pLevelInt = 4
  elseif powerLevel == "5" then
    pLevelInt = 5
  elseif powerLevel == "6" then
    pLevelInt = 6
  elseif powerLevel == "7" then
    pLevelInt = 7
  elseif powerLevel == "8" then
    pLevelInt = 8
  else
    pLevelInt = powerLevel
  end
  if wulverType ~= nil then
    if wulverType == "Power" then
      if ai:PickupIsAcquired("WulverInit") then
        ai:PickupSetStage("WulverInit", 1)
      end
      if override ~= nil and override == true then
        if pLevelInt <= 2 then
          ai:SetCharacterConfig("power_easy")
        elseif 3 <= pLevelInt and pLevelInt <= 4 then
          ai:SetCharacterConfig("power_hard")
        elseif 5 <= pLevelInt then
          ai:SetCharacterConfig("power_veryhard")
        end
      end
    elseif override ~= nil and override == true then
      if pLevelInt <= 2 then
        ai:SetCharacterConfig("speed_easy")
      elseif 3 <= pLevelInt and pLevelInt <= 4 then
        ai:SetCharacterConfig("speed_hard")
      elseif 5 <= pLevelInt then
        ai:SetCharacterConfig("speed_veryhard")
      end
    end
  end
end
function OnAIUpdate(ai)
  traverselink.OnUpdate(ai)
  if wulverFrenzyActive and ai:PickupIsSlotUsed("Frost") then
    ai:PickupRelinquish("Debuff_Slow")
  end
end
function Brain_Core:OnDeath(ai, attacker, deathByDespawn)
  if ai:PickupIsAcquired("Wulver00_Elite") then
    ai:PickupRelinquish("Wulver00_Elite")
  end
  if breathEffect ~= nil then
    breathEffect:Destroy()
    breathEffect = nil
  end
end
function LuaHook_CheckCanReact(ai)
  if ai:MeterGetValue("CD_ComboBreaker") >= 99 then
    return false
  end
  return true
end
function _G.OnAICreateLuaState(ai)
  _G.OnAICreateLuaState = nil
end
picklelib.Install()
function Pickle(GO)
  return {
    global = global
  }
end
function Unpickle(GO, t)
  global = t.global
end
function OnWeaponApproaching(gameObject, attacker, weapon, side)
  local ai = gameObject:GetCreature():GetAI()
  if ai:HasMarker("AxeTossEvade") and ai.HitPoints > 0 then
    ai:ForceMove("BRA_DefEvadeAxeThrow")
  end
end
local RaycastCheck = function(creature, offsetZJoint, offsetDir)
  local raycastHit = game.World.RaycastCollision(offsetZJoint, offsetDir, {
    SourceGameObject = creature,
    EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
  })
  if raycastHit then
    return false
  else
    return true
  end
end
local CheckForClearEvadePath = function(C, direction, dist)
  local ai = C:GetAI()
  local jointIndex = C:GetJointIndex("zeroJoint")
  local offset = engine.Vector.New(0, 1.5, 0)
  local offsetZeroJoint = C.WorldPosition + offset
  if direction == "backR" then
    local zeroJointFacingFwd = C:GetWorldJointForward(jointIndex)
    local zeroJointFacingLeft = C:GetWorldJointLeft(jointIndex)
    local toRightBack = zeroJointFacingLeft * -dist + zeroJointFacingFwd * -dist
    local offsetRightBack = offsetZeroJoint + toRightBack
    offsetRightBack.y = offsetZeroJoint.y
    return RaycastCheck(ai, offsetZeroJoint, offsetRightBack)
  elseif direction == "backL" then
    local zeroJointFacingFwd = C:GetWorldJointForward(jointIndex)
    local zeroJointFacingLeft = C:GetWorldJointLeft(jointIndex)
    local toLeftBack = zeroJointFacingLeft * dist + zeroJointFacingFwd * -dist
    local offsetLeftBack = offsetZeroJoint + toLeftBack
    offsetLeftBack.y = offsetZeroJoint.y
    return RaycastCheck(ai, offsetZeroJoint, offsetLeftBack)
  elseif direction == "left" then
    local zeroJointFacingLeft = C:GetWorldJointLeft(jointIndex)
    local toLeft = zeroJointFacingLeft * dist
    local offsetLeft = offsetZeroJoint + toLeft
    offsetLeft.y = offsetZeroJoint.y
    return RaycastCheck(ai, offsetZeroJoint, offsetLeft)
  elseif direction == "right" then
    local zeroJointFacingLeft = C:GetWorldJointLeft(jointIndex)
    local toRight = zeroJointFacingLeft * (dist * -1)
    local offsetRight = offsetZeroJoint + toRight
    offsetRight.y = offsetZeroJoint.y
    return RaycastCheck(ai, offsetZeroJoint, offsetRight)
  elseif direction == "back" then
    local zeroJointFacingFwd = C:GetWorldJointForward(jointIndex)
    local toBack = zeroJointFacingFwd * (dist * -1)
    local offsetBack = offsetZeroJoint + toBack
    offsetBack.y = offsetZeroJoint.y
    return RaycastCheck(ai, offsetZeroJoint, offsetBack)
  else
    return false
  end
end
local EvadeBackCheck = function(C, dist)
  local random = math.random(1, 2)
  local evadeDir
  local clearPath = false
  if random == 1 then
    evadeDir = "backL"
  else
    evadeDir = "backR"
  end
  if evadeDir == "backL" then
    clearPath = CheckForClearEvadePath(C, evadeDir, dist)
    if not clearPath then
      evadeDir = "backR"
      clearPath = CheckForClearEvadePath(C, evadeDir, dist)
    end
  elseif evadeDir == "backR" then
    clearPath = CheckForClearEvadePath(C, evadeDir, dist)
    if not clearPath then
      evadeDir = "backL"
      clearPath = CheckForClearEvadePath(C, evadeDir, dist)
    end
  end
  return evadeDir
end
function LuaHook_RageGrabTextShow(C, data)
  if not rageGrabTextShow then
    rageGrabTextShow = true
    uiCalls.SendSidebarDesignerMessage({
      Text = 40867,
      AdvanceType = uiCalls.msgParam.INFINITE_TIMER
    })
  end
end
function LuaHook_RageGrabTextHide(C, data)
  if rageGrabTextShow then
    uiCalls.UI_Event_TurnOffDesignerMessage()
    rageGrabTextShow = false
  end
end
function LuaHook_WulverDropHealthConsumable(ai, data)
  local chance = ai:LookupFloatConstant("WULVER_RAGE_PUNCH_LOOT_CHANCE")
  if chance < math.random() then
    return
  end
  local index = ai:GetJointIndex("JONeck1")
  local pos = ai:GetWorldJointPosition(index)
  local dir = ai:GetWorldForward()
  pos = pos + dir * math.random(0.15, 0.5)
  local spawnArgs = {
    SpawnObject = "golootGenericHealthRune",
    OffsetPosition = pos,
    Level = game.Level.GetPermLevel()
  }
  game.Player.FindPlayer():CallScript("LuaHook_SpawnGameObject", spawnArgs)
end
function LuaHook_WiggleStickTextShow(C, data)
  if not wiggleStickTextShow then
    wiggleStickTextShow = true
    uiCalls.UI_Event_SendDesignerMessage("[MSG:" .. 40869 .. "]", 0)
  end
end
function LuaHook_WiggleStickTextHide(C, data)
  if wiggleStickTextShow then
    uiCalls.UI_Event_TurnOffDesignerMessage()
    wiggleStickTextShow = false
  end
end
function LuaHook_DestroyBreathFX(C, data)
  if breathEffect ~= nil then
    breathEffect:Destroy()
    breathEffect = nil
  end
end
function LuaHook_WulverFrenzyCharge(ai, data)
  ai:StartMaterialAnim("RageCHARGE")
end
function LuaHook_WulverFrenzyEnable(ai, data)
  wulverFrenzyActive = true
  ai:StartMaterialAnim("RageON")
end
function LuaHook_WulverFrenzyDisable(ai, data)
  wulverFrenzyActive = false
  ai:StartMaterialAnim("RageOFF")
end
local spawnedBanditCarcass
function LuaHook_SpawnEatingBandit_SpawnBandit(ai)
  if spawnedBanditCarcass == nil then
    spawnedBanditCarcass = game.AI.Spawn(ai.Level, ai.WorldPosition, ai:GetWorldForward(), "CRT_Bandit00", "BRA_SpawnWulverEatingBandit")
  end
end
function LuaHook_EvadeIfPossible(C, data)
  if constants.canEvade and C.HitPoints > 0 then
    local ai = C:GetAI()
    local distToTarget = global.target:GetWorldPosition():Distance(C:GetWorldPosition())
    if distToTarget <= 4.5 then
      ai:ForceMove("BRA_DefEvadeToLowB")
      constants.canEvade = false
    end
  end
end
function LuaHook_EvadeToLowCheck(C, data)
  local evadeDirection = EvadeBackCheck(C, 4)
  if evadeDirection ~= nil then
    if evadeDirection == "backL" then
      return data:FindOutcomeBranchesEntry("EvadeBackL")
    else
      return data:FindOutcomeBranchesEntry("EvadeBackR")
    end
  else
    return data:FindOutcomeBranchesEntry("Nothing")
  end
end
function LuaHook_EvadeToUprightCheck(C, data)
  local evadeDirection = EvadeBackCheck(C, 5)
  if evadeDirection ~= nil then
    if evadeDirection == "backL" then
      return data:FindOutcomeBranchesEntry("EvadeBackL")
    else
      return data:FindOutcomeBranchesEntry("EvadeBackR")
    end
  else
    return data:FindOutcomeBranchesEntry("Nothing")
  end
end
function LuaHook_EvadeToSideCheck(C, data)
  local random = math.random(1, 2)
  local evadeDir
  local clearPath = false
  if random == 1 then
    evadeDir = "left"
  else
    evadeDir = "right"
  end
  if evadeDir == "left" then
    clearPath = CheckForClearEvadePath(C, evadeDir, 3)
    if not clearPath then
      evadeDir = "right"
      clearPath = CheckForClearEvadePath(C, evadeDir, 3)
    end
  elseif evadeDir == "right" then
    clearPath = CheckForClearEvadePath(C, evadeDir, 3)
    if not clearPath then
      evadeDir = "left"
      clearPath = CheckForClearEvadePath(C, evadeDir, 3)
    end
  end
  if clearPath then
    if evadeDir == "left" then
      return data:FindOutcomeBranchesEntry("EvadeLeft")
    else
      return data:FindOutcomeBranchesEntry("EvadeRight")
    end
  else
    return data:FindOutcomeBranchesEntry("Nothing")
  end
end
function LuaHook_AddBlood_Cleave_Impact(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.1)
end
function LuaHook_AddBlood_Wulver_RageEnter(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.4)
end
function LuaHook_AddBlood_Wulver_RagePunch(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.4)
end
function LuaHook_AddBlood_Wulver_RageKill(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.6)
  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.8)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 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.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.2)
end
function LuaHook_AddBlood_Wulver_BiteEnter(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.4)
end
function LuaHook_AddBlood_Wulver_BiteLoop(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.075)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.075)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.05)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.075)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.1)
end
function LuaHook_AddBlood_Wulver_BiteDeath(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, 1)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 1)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.4)
end
