local LD = require("design.LevelDesignLibrary")
local uiCalls, TUT, thisObj, thisLevel, player, son, onOpenedEvent, interactionOnFinish, interactZone, openFX, idleFX, parentObj, useLOSTest, chestType, rewardTier, rewardTierIndex, WADName, lootObjectName, reward, interactionName, branchName, animName, lootObject
local completion_percentage = 0.5
local typeSpecificData, typeSpecificCallbacks, trapChest, onInteractStartCallbacks, onInteractFinishCallbacks
local checkpointOnOpened = false
local checkpointOnOpened_Override
local checkpointRequirementDisabled = false
local state
local states = {
  ENABLED = 1,
  DISABLED = 2,
  LOCKED = 3,
  OPENED = 4
}
local uiCalls_LoadLibrary = function()
  if uiCalls == nil then
    uiCalls = require("ui.uicalls")
  end
end
local TUT_LoadLibrary = function()
  if TUT == nil then
    TUT = require("game.GlobalTutorials")
  end
end
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  parentObj = thisObj.Parent.Parent
  interactZone = LD.CreateInteractZone_Standard_180(parentObj, "promptJointFront")
  interactZone:SetHintXZRange(3)
  interactZone:SetPromptJoint("promptJointLoc")
  idleFX = parentObj:FindSingleGOByName("AmbientFX").Child
  lootObject = parentObj:FindSingleGOByName("lootObject")
  if lootObject then
    lootObject = lootObject.Child
  end
  onOpenedEvent = thisObj:FindLuaTableAttribute("onOpenedEvent")
  if onOpenedEvent ~= nil then
    onOpenedEvent = LD.ExtractCallbacksForEvent(thisLevel, thisObj, onOpenedEvent)
  end
  interactionOnFinish = thisObj:FindLuaTableAttribute("interactionOnFinish")
  if interactionOnFinish ~= nil then
    interactionOnFinish = LD.ExtractCallbacksForEvent(thisLevel, thisObj, interactionOnFinish)
  end
  chestType = thisObj:GetLuaTableAttribute("ChestType")
  rewardTier = thisObj:GetLuaTableAttribute("RewardTier")
  rewardTierIndex = thisObj:GetLuaTableAttribute("RewardTierIndex")
  WADName = thisObj:FindLuaTableAttribute("WADName")
  lootObjectName = thisObj:GetLuaTableAttribute("LootObject")
  checkpointOnOpened = thisObj:GetLuaTableAttribute("CheckpointOnOpened")
  checkpointOnOpened_Override = thisObj:GetLuaTableAttribute("CheckpointOnOpened_Override")
  if checkpointOnOpened_Override ~= nil and checkpointOnOpened_Override ~= "" then
    checkpointOnOpened_Override = GameObjects[checkpointOnOpened_Override]
  else
    checkpointOnOpened_Override = nil
  end
  useLOSTest = thisObj:GetLuaTableAttribute("UseLineOfSightTest")
  local startEnabled = thisObj:GetLuaTableAttribute("StartEnabled")
  local startLocked = thisObj:GetLuaTableAttribute("StartLocked")
  if useLOSTest and interactZone then
    interactZone:SetEnableLineOfSightTest(true)
  end
  if startEnabled then
    state = states.ENABLED
  else
    state = states.DISABLED
  end
  if startLocked then
    state = states.LOCKED
  end
  typeSpecificData = {}
  typeSpecificCallbacks = {}
  SoundInit()
  game.SubObject.Sleep(obj)
  local levelName = level.Name
  if string.find(levelName, "Nid") == nil and string.find(levelName, "Msp") == nil then
    idleFX:SetInhibitSave()
    parentObj:SetInhibitSave()
    thisObj:SetInhibitSave()
    if chestType == "Common" then
      parentObj:FindSingleGOByName("animatedLid"):SetInhibitSave()
    end
  end
end
function OnFirstStart(level, obj)
  if lootObject then
    lootObject:HideModel()
    LD.HideFX(lootObject)
  end
end
function OnStart(level, obj)
  if (onOpenedEvent or interactionOnFinish) and not checkpointOnOpened and not checkpointRequirementDisabled then
    engine.Warning("Chest Error - " .. LD.GetParentTrace(thisObj) .. " has a callback, but isn't set to save a checkpoint. Check the 'CheckpointOnOpened' option in the toolbox to ensure the chest saves correctly")
  end
  SetTypeData()
  if state == states.ENABLED then
    Enable()
  elseif state == states.DISABLED then
    Disable()
  elseif state == states.LOCKED then
    LockChest()
  elseif state == states.OPENED then
    TypeSpecific_SoftSaveRestore()
  end
end
function TypeSpecific_SoftSaveRestore()
  idleFX:Hide()
  Disable()
  if animName ~= nil and animName ~= "" then
    parentObj:StartAnim(animName)
    parentObj:JumpAnimToFrame(parentObj.AnimLengthFrames)
  end
  if chestType == "Common" then
    local breakableLid = parentObj:FindSingleGOByName("animatedLid")
    if breakableLid then
      breakableLid:JumpAnimToFrame(breakableLid.AnimLengthFrames)
    end
  elseif chestType == "Dispell" then
    typeSpecificData.DispellObject:StartAnim("envMaskDispelBreak")
    typeSpecificData.DispellObject:JumpAnimToFrame(typeSpecificData.DispellObject.AnimLengthFrames)
    typeSpecificData.DispellObject:FindSingleGOByName("Mesh"):Hide()
    typeSpecificData.DispellObject:FindSingleGOByName("FX"):Hide()
  elseif chestType == "Runic_Axe" or chestType == "Runic_Blades" then
    thisObj.Parent.Parent.LuaObjectScript.Disable()
    thisObj.Parent.Parent.LuaObjectScript.DimRunesImmediate()
    thisObj.Parent.Parent.LuaObjectScript.ResolveRunicSoftSave()
  end
end
function SetTypeData()
  interactionName = chestType .. " chest opening"
  reward = "CHEST_" .. string.upper(chestType) .. "_TIER"
  if chestType == "Common" then
    branchName = "BRA_EnvContainerCommonChestLeft_M"
    animName = "breaking_topAnim"
    local breakableLid = parentObj:FindSingleGOByName("animatedLid")
    if breakableLid then
      typeSpecificData.breakableObject = breakableLid
      LD.HideFX(breakableLid)
    end
    function typeSpecificCallbacks.OnOpened()
      CommonChestBroken()
    end
  elseif chestType == "Coffin" then
    if parentObj then
      trapChest = parentObj:FindLuaTableAttribute("TrapChest_BanditSpawn")
    end
    if trapChest then
      branchName = "BRA_EnvContainerCoffinTrap_M"
      animName = "envContainerCoffinTrap"
    else
      branchName = "BRA_EnvContainerCoffin_M"
      animName = "envContainerCoffin"
    end
    reward = "COFFIN_TIER"
    openFX = "Coffin_Open_Effect_Root"
  elseif chestType == "Legendary" then
    branchName = "BRA_EnvContainerLegendaryChest_M"
    animName = "envContainerLegendary"
    openFX = "Chest_Legendary_Open_Effect_Root"
  elseif chestType == "Runic_Axe" or chestType == "Runic_Blades" then
    completion_percentage = 0
    branchName = "BRA_EnvContainerRunicChest_UnlockedShort_M"
    animName = "envTripleChestUnlocked"
    reward = "CHEST_RUNIC_TIER"
    openFX = "Chest_Locked_Open_Effect_Root"
  elseif chestType == "Dispell" then
    completion_percentage = 0
    interactZone:SetTags("NotWhileSonInteracting")
    interactZone:SetRequiresSonUnoccupied(true)
    branchName = "BRA_EnvContainerDispellChest_M"
    animName = "envLegendaryChestUnlockedShortKratos"
    openFX = "Chest_Legendary_Open_Effect_Root"
    typeSpecificData.DispellObject = parentObj:FindSingleGOByName("sondispell_chest").Child
    if typeSpecificData.DispellObject then
      typeSpecificData.DispellObject:StartAnim("maskLoopFloating")
      typeSpecificData.DispellObject:PlayAnimCycle()
    end
    if state ~= states.OPENED then
      if typeSpecificData.DispellObject and not typeSpecificData.FX_Idle then
        typeSpecificData.FX_Idle = typeSpecificData.DispellObject:FindSingleGOByName("idleFX")
        typeSpecificData.FX_Death = typeSpecificData.DispellObject:FindSingleGOByName("deathFX")
      end
      typeSpecificData.FX_Idle:SetWorldPosition(typeSpecificData.DispellObject:FindSingleGOByName("Mesh"):GetWorldPosition())
      typeSpecificData.FX_Mesh = typeSpecificData.DispellObject:FindSingleGOByName("fx_mesh")
      typeSpecificData.FX_Mesh:JumpAnimToFrame(0)
      typeSpecificData.FX_Mesh:PlayAnimToFrame(650)
      typeSpecificData.FX_Mesh:OnAnimationDone(thisObj, "OnFXMeshDone", {Force = true})
      typeSpecificData.FX_Inner = typeSpecificData.DispellObject:FindSingleGOByName("fx_meshInner")
      typeSpecificData.FX_Inner:JumpAnimToFrame(0)
      typeSpecificData.FX_Inner:PlayAnimToFrame(650)
      typeSpecificData.FX_Inner:OnAnimationDone(thisObj, "OnFXMeshInnerDone", {Force = true})
    end
    function typeSpecificCallbacks.CustomHook()
      if typeSpecificData.FX_Idle then
        LD.HideFX(typeSpecificData.FX_Idle)
      end
      if typeSpecificData.FX_Death then
        typeSpecificData.FX_Death:SetWorldPosition(typeSpecificData.DispellObject:FindSingleGOByName("Mesh"):GetWorldPosition())
        LD.ShowFX(typeSpecificData.FX_Death)
      end
      typeSpecificData.FX_Mesh:ClearAllAnimCallbacks()
      typeSpecificData.FX_Mesh:JumpAnimToFrame(650)
      typeSpecificData.FX_Mesh:PlayAnimToFrame(700)
      typeSpecificData.FX_Inner:ClearAllAnimCallbacks()
      typeSpecificData.FX_Inner:JumpAnimToFrame(650)
      typeSpecificData.FX_Inner:PlayAnimToFrame(700)
    end
    function typeSpecificCallbacks.OnOpened()
      typeSpecificData.PreAwardMuspCount = game.Wallets.GetResourceValue("HERO", "MuspelheimCipherPiece")
      typeSpecificData.PreAwardNifCount = game.Wallets.GetResourceValue("HERO", "NiflheimCipherPiece")
    end
    function typeSpecificCallbacks.AwardLoot()
      local IncrementQuest = function(parent, child, pieces)
        local questState = game.QuestManager.GetQuestState(parent)
        if questState == "Inactive" then
          game.QuestManager.StartQuest(parent)
        end
        local _, progress = game.QuestManager.GetQuestProgressAndGoal(child)
        game.QuestManager.IncrementQuestProgress(child, pieces - progress)
      end
      if game.Wallets.GetResourceValue("HERO", "MuspelheimCipherPiece") > typeSpecificData.PreAwardMuspCount then
        IncrementQuest("Quest_SonLanguage_Muspelheim_Parent", "Quest_SonLanguage_Muspelheim", game.Wallets.GetResourceValue("HERO", "MuspelheimCipherPiece"))
        if game.Wallets.GetResourceValue("HERO", "MuspelheimCipherPiece") == 4 then
          game.Audio.PlayBanterNonCritical("Musp_Disc_Complete")
        else
          game.Audio.PlayBanterNonCritical("Musp_Disc", nil, nil, false)
        end
        TrophyCheck()
      end
      if game.Wallets.GetResourceValue("HERO", "NiflheimCipherPiece") > typeSpecificData.PreAwardNifCount then
        IncrementQuest("Quest_SonLanguage_Niflheim_Parent", "Quest_SonLanguage_Niflheim", game.Wallets.GetResourceValue("HERO", "NiflheimCipherPiece"))
        local _, progress = game.QuestManager.GetQuestProgressAndGoal("Quest_SonLanguage_Niflheim")
        if game.Wallets.GetResourceValue("HERO", "NiflheimCipherPiece") == 4 then
          game.Audio.PlayBanterNonCritical("Nif_Disc_Complete")
        else
          game.Audio.PlayBanterNonCritical("Nif_Disc")
        end
        TrophyCheck()
      end
    end
  elseif chestType == "Custom_HammerQuest" then
    branchName = "BRA_EnvContainerCustomHammerQuest"
    animName = "envContainerLegendary"
    reward = ""
    openFX = "Chest_Legendary_Open_Effect_Root"
  elseif chestType == "Custom_MimirsEye" then
    branchName = "BRA_MimirsEye"
    animName = "poiMimirEye010Scene"
    reward = ""
    openFX = "Chest_Legendary_Open_Effect_Root"
  end
end
function TrophyCheck()
  if game.QuestManager.GetQuestState("Quest_SonLanguage_Muspelheim") == "Complete" and game.QuestManager.GetQuestState("Quest_SonLanguage_Niflheim") == "Complete" then
    AwardTrophy()
  end
  if game.Wallets.GetResourceValue("HERO", "MuspelheimCipherPiece") == 4 and game.Wallets.GetResourceValue("HERO", "NiflheimCipherPiece") == 4 then
    AwardTrophy()
  end
end
function OnUseWorld(level, obj)
  if interactZone:PlayerCanInteract() then
    player:RequestInteract(thisObj, {InteractZone = interactZone, LerpObject = lootObject})
  end
end
function ForceInteract()
  player:RequestInteract(thisObj, {InteractZone = interactZone, LerpObject = lootObject})
end
function GetInteractZone()
  if interactZone then
    return interactZone
  end
  return nil
end
function OnInteractStart(level, obj, creature)
  if chestType == "Dispell" then
    local syncTable = {
      {player, branchName},
      {son, branchName},
      {
        typeSpecificData.DispellObject,
        "envLegendaryChestUnlockedShortKratos"
      }
    }
    LD.PlayApproach_GroupSync(parentObj, syncTable, animName, true, "synchJoint", {completion_percentage = completion_percentage})
  elseif chestType == "Coffin" and trapChest then
    game.Audio.SetBanterFact("CreatureSpawnCooldown", true, 30)
    game.Audio.SetBanterFact("FightStartCD", true, 10)
    local banditSpawner = parentObj:FindSingleGOByName("banditCoffinSpawn")
    local bandit = banditSpawner.LuaObjectScript.GetLastEnemy()
    if bandit == nil then
      bandit = banditSpawner.LuaObjectScript.SpawnEnemy()
      bandit:SetAIDeathCallback(thisObj, "TrapChestSoftSave")
    end
    local slaveTable = {
      {player, branchName},
      {bandit, branchName}
    }
    LD.PlayApproach_GroupSync(parentObj, slaveTable, "envContainerCoffinTrap", true, "synchJoint", {completion_percentage = completion_percentage})
  else
    local weaponMode
    if chestType == "Common" and player:GetCurrentWeapon() == "Axe" then
      weaponMode = "Axe"
    end
    LD.PlaySingleSynchMove_KratosObject(parentObj, "synchJoint", interactionName, branchName, animName, interactZone, true, weaponMode, {completion_percentage = completion_percentage})
  end
  if chestType == "Common" then
    game.Camera.Recenter({
      TimeStart = 0,
      TimeDuration = 2,
      LockRecenter = 0,
      YawRange = 0,
      TriggerLeft = 0,
      TriggerRight = 0,
      ReturnLeft = 0,
      ReturnRight = 0,
      PitchRange = 0,
      ReturnUp = -8,
      ReturnDown = -8
    })
  else
    game.Camera.Recenter({
      TimeStart = 0,
      TimeDuration = 2,
      LockRecenter = 1,
      YawRange = 0,
      TriggerLeft = 0,
      TriggerRight = 0,
      ReturnLeft = 0,
      ReturnRight = 0,
      PitchRange = 0,
      ReturnUp = -8,
      ReturnDown = -8
    })
  end
  if game.Loot.AlertChestOpened then
    game.Loot.AlertChestOpened()
  end
  FireOnInteractStartCallbacks()
end
function TrapChestSoftSave()
  game.SubObject.SoftSave(thisObj)
end
function OnInteractAbort(level, obj, creature)
end
function OnInteractFinish(level, obj, creature)
  StopSoundChestIdle()
end
function CustomHook()
  if typeSpecificCallbacks.CustomHook then
    typeSpecificCallbacks.CustomHook()
  end
end
function OnInteractDone()
  uiCalls_LoadLibrary()
  uiCalls.UI_Global_Menu_Clear_Exclusive_Access("GlobalInWorldMenu")
end
function LuaHook_HideIdleFX()
  idleFX:Hide()
  if chestType == "Dispell" then
    StopSoundChestIdle()
  end
end
function OnOpened()
  Disable()
  if lootObject and not trapChest then
    lootObject:ShowModel()
    LD.ShowFX(lootObject)
  end
  SpawnOpenFX()
  state = states.OPENED
  if onOpenedEvent ~= nil then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onOpenedEvent, "onOpenedEvent")
  end
  if typeSpecificCallbacks.OnOpened then
    typeSpecificCallbacks.OnOpened()
  end
  if chestType == "Legendary" then
    local found, currentRegion = game.Map.FindPlayerRegion()
    local comparisonString = LD.RegionInfo_GetName(currentRegion)
    if found and comparisonString ~= "TyrsVault" and comparisonString ~= "TheHallofTyr" then
      UpdateRegionSummary(currentRegion, "LegendaryChest")
    elseif found and comparisonString == "TyrsVault" then
      LD.ActivateAndIncrementQuest("RegionSummary_LegendaryChest_Parent_TyrsVault")
    elseif found and comparisonString == "TheHallofTyr" then
      LD.ActivateAndIncrementQuest("RegionSummary_LegendaryChest_Parent_TheHallofTyr")
    else
      print("RegionSummary Check: no region found for player")
    end
  end
  if chestType == "Runic_Axe" or chestType == "Runic_Blades" then
    local found, currentRegion = game.Map.FindPlayerRegion()
    local comparisonString = LD.RegionInfo_GetName(currentRegion)
    if found and comparisonString ~= "TyrsVault" then
      UpdateRegionSummary(currentRegion, "RunicChest")
    elseif found and comparisonString == "TyrsVault" then
      LD.ActivateAndIncrementQuest("RegionSummary_RunicChest_Parent_TyrsVault")
    else
      print("RegionSummary Check: no region found for player")
    end
    if game.Loot.AlertTripleChestOpened ~= nil then
      game.Loot.AlertTripleChestOpened()
    end
  end
  if chestType == "Coffin" and not trapChest then
    game.Audio.PlayBanterNonCritical("ca_first_coffin", nil, nil, false)
  end
  if not trapChest then
    PlayLootIdleSound()
  end
end
function UpdateRegionSummary(regionID, categoryName)
  local regionStringName = LD.RegionInfo_GetName(regionID)
  local regionSummaryInfo = game.Map.GetRegionSummaryInfo(regionID)
  for _, summaryInfo in ipairs(regionSummaryInfo) do
    local categoryID = summaryInfo.CategoryStr
    if categoryID ~= nil then
      if categoryID == categoryName then
        local regionQuest = tostring("RegionSummary_" .. categoryName .. "_Parent_" .. regionStringName)
        print("DOING LEGENDARY QUEST....", regionQuest)
        LD.ActivateAndIncrementQuest(regionQuest)
      end
    else
      print("string value not present")
    end
  end
end
function PickUp()
  if lootObject then
    LD.HideFX(lootObject)
  end
  if typeSpecificCallbacks.PickUp then
    typeSpecificCallbacks.PickUp()
  end
  PlayLootPickupSound()
end
function AwardLoot()
  uiCalls_LoadLibrary()
  uiCalls.UI_Global_Menu_Set_Exclusive_Access("GlobalInWorldMenu")
  local resourceTestTable = PreAwardResourceTest()
  if lootObjectName == "Custom_MimirsEye" then
    if typeSpecificCallbacks.AwardLoot then
      typeSpecificCallbacks.AwardLoot()
    end
    PostAwardResourceTest(resourceTestTable)
    return
  end
  if lootObjectName == "Custom_HammerQuest" then
    game.Wallets.AddResource("HERO", "AndvarisHammer", 1, "Quest")
    return
  end
  LD.RollContainerConditionLoot(reward, rewardTier, WADName, rewardTierIndex)
  if typeSpecificCallbacks.AwardLoot then
    typeSpecificCallbacks.AwardLoot()
  end
  PostAwardResourceTest(resourceTestTable)
end
function PutAway()
  uiCalls_LoadLibrary()
  uiCalls.UI_Global_Menu_Clear_Exclusive_Access("GlobalInWorldMenu")
  if typeSpecificCallbacks.PutAway then
    typeSpecificCallbacks.PutAway()
  end
  CheckTutorialHint()
  if lootObject then
    StopLootIdleSound()
    lootObject:HideAudio()
    LD.HideFX(lootObject)
    lootObject:HideModel()
  end
  FireOnInteractFinishCallbacks()
  if interactionOnFinish ~= nil then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, interactionOnFinish, "Interaction Event Finish")
  end
  if checkpointOnOpened then
    if checkpointOnOpened_Override ~= nil then
      if checkpointOnOpened_Override.IsRefNode then
        checkpointOnOpened_Override = checkpointOnOpened_Override.Child
      end
      game.World.StoreCheckpoint({OverrideObject = checkpointOnOpened_Override})
    else
      game.World.StoreCheckpoint()
    end
  else
    if game.SubObject.SoftSave then
      game.SubObject.SoftSave(thisObj)
    end
    if chestType == "Runic_Axe" or chestType == "Runic_Blades" then
      thisObj.Parent.Parent.LuaObjectScript.RunicSoftSave()
    end
  end
end
function DisableCheckpointRequirement()
  checkpointRequirementDisabled = true
end
function OnSaveCheckpoint(level, obj)
  return {state = state}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  state = savedInfo.state
end
function AwardTrophy()
  print("Trophy Awarded: Trilingual")
  game.UnlockTrophy(14)
end
function Enable()
  if state ~= states.OPENED then
    state = states.ENABLED
    interactZone:Enable()
    LD.ShowFX(idleFX)
    PlaySoundChestIdle()
  end
end
function Disable()
  if state ~= states.OPENED then
    state = states.DISABLED
  end
  interactZone:Disable()
  LD.HideFX(idleFX)
end
function LockChest()
  state = states.LOCKED
  interactZone:Lock()
  LD.HideFX(idleFX)
  StopSoundChestIdle()
end
function UnlockChest()
  state = states.ENABLED
  interactZone:Unlock()
  LD.ShowFX(idleFX)
  PlaySoundChestIdle()
end
function ForceStateDisabled()
  state = states.DISABLED
end
function Reset()
  state = states.ENABLED
  interactZone:Unlock()
  interactZone:Enable()
  LD.ShowFX(idleFX)
  ResetLootObjectLocation()
  LD.HideFX(lootObject)
  if chestType == "Common" then
    local breakableLid = parentObj:FindSingleGOByName("animatedLid")
    if breakableLid then
      breakableLid:JumpAnimToFrame(0)
      breakableLid:PauseAnim()
      LD.HideFX(breakableLid)
    end
  end
  if animName ~= nil and animName ~= "" then
    parentObj:StartAnim(animName)
  end
  parentObj:JumpAnimToFrame(0)
  parentObj:PauseAnim()
end
function HideChest()
  Disable()
  parentObj:HideModel()
  parentObj:HideCollision()
  parentObj:HideNavObstacle()
  idleFX:Hide()
  lootObject:HideModel()
  LD.HideFX(lootObject)
  if chestType == "Common" then
    local breakableLid = parentObj:FindSingleGOByName("animatedLid")
    if breakableLid then
      breakableLid:HideModel()
      breakableLid:HideCollision()
    end
  end
end
function ShowChest(hideIdleFx)
  parentObj:ShowModel()
  parentObj:ShowCollision()
  parentObj:ShowNavObstacle()
  ResetLootObjectLocation()
  LD.HideFX(lootObject)
  if state ~= states.OPENED and not hideIdleFx then
    idleFX:Show()
  end
  if chestType == "Common" then
    local breakableLid = parentObj:FindSingleGOByName("animatedLid")
    if breakableLid then
      breakableLid:ShowModel()
      breakableLid:ShowCollision()
    end
  end
end
function ResetLootObjectLocation()
  local lootObjJoint = parentObj:FindJointIndex("lootObjLocation")
  if lootObjJoint then
    lootObject:SetWorldPosition(parentObj:GetWorldJointPosition(lootObjJoint))
    lootObject:SetWorldFacing(parentObj:GetWorldJointForward(lootObjJoint))
  end
end
function IsOpen()
  return state == states.OPENED
end
function GetState()
  return state
end
function GetPossibleStates()
  return states
end
function CheckTutorialHint()
  if reward == "StartingTrinketReward" then
    local showHint = LD.GetEntityVariable("TUT_ShowTrinketTutorialHint")
    if showHint == true then
      TUT_LoadLibrary()
      TUT.ArmorTrinkets_Tutorial()
      LD.SetEntityVariable("TUT_ShowTrinketTutorialHint", false)
    end
  elseif reward == "StartingWristArmorReward" then
    local showHint = LD.GetEntityVariable("TUT_ShowWristArmorTutorialHint")
    if showHint == true then
      TUT_LoadLibrary()
      TUT.WristArmor_Tutorial()
      LD.SetEntityVariable("TUT_ShowWristArmorTutorialHint", false)
    end
  elseif reward == "StartingWaistArmorReward" then
    local showHint = LD.GetEntityVariable("TUT_ShowWaistArmorTutorialHint")
    if showHint == true then
      TUT_LoadLibrary()
      TUT.WaistArmor_Tutorial()
      LD.SetEntityVariable("TUT_ShowWaistArmorTutorialHint", false)
    end
  elseif reward == "XP_Reward_VerySmall" or reward == "XP_Reward_Small" or reward == "XP_Reward_Medium" or reward == "XP_Reward_Large" or reward == "XP_Reward_VeryLarge" then
    local showHint = LD.GetEntityVariable("TUT_ShowXPTutorialHint")
    if showHint == true then
      TUT_LoadLibrary()
      TUT.WaistArmor_Tutorial()
      LD.SetEntityVariable("TUT_ShowXPTutorialHint", false)
    end
  elseif reward == "FrostSpecialAxeFlurryChopReward" then
    local showHint = LD.GetEntityVariable("TUT_ShowWeaponGemTutorialHint")
    if showHint == true then
      LD.SetEntityVariable("TUT_ShowWeaponGemTutorialHint", false)
    end
  else
    local showHint = LD.GetEntityVariable("TUT_ShowResourceTutorialHint")
    if showHint == true then
      TUT_LoadLibrary()
      TUT.CollectResources_Tutorial()
      LD.SetEntityVariable("TUT_ShowResourceTutorialHint", false)
    end
  end
end
function CommonChestBroken()
  if typeSpecificData.breakableObject then
    typeSpecificData.breakableObject:PlayAnimToEnd()
    LD.ShowFX(typeSpecificData.breakableObject)
  end
  LD.HideFX(idleFX)
  SpawnOpenFX()
  PlaySoundBreakCommonChest()
end
function SpawnOpenFX()
  if openFX then
    game.FX.Spawn(openFX, nil, {
      AutoDelete = true,
      GameObject = parentObj,
      Joint = "synchJoint"
    })
  end
end
function OnFXMeshDone()
  typeSpecificData.FX_Mesh:JumpAnimToFrame(50)
  typeSpecificData.FX_Mesh:PlayAnimToFrame(650)
  typeSpecificData.FX_Mesh:OnAnimDone(thisObj, "OnFXMeshDone")
end
function OnFXMeshInnerDone()
  typeSpecificData.FX_Inner:JumpAnimToFrame(50)
  typeSpecificData.FX_Inner:PlayAnimToFrame(650)
  typeSpecificData.FX_Inner:OnAnimDone(thisObj, "OnFXMeshInnerDone")
end
function PreAwardResourceTest()
  local result = {}
  result.NifTankArmor_Chest_Pattern = game.Wallets.GetResourceValue("HERO", "NifTankArmor_Chest_Pattern")
  result.MaxHealthUpgradePiece = game.Wallets.GetResourceValue("HERO", "MaxHealthUpgradePiece")
  result.MaxRageUpgradePiece = game.Wallets.GetResourceValue("HERO", "MaxRageUpgradePiece")
  result.MaxHealthUpgrade = game.Wallets.GetResourceValue("HERO", "MaxHealthUpgrade")
  result.MaxRageUpgrade = game.Wallets.GetResourceValue("HERO", "MaxRageUpgrade")
  return result
end
function PostAwardResourceTest(resourceTestTable)
  local nifArmorPattern_Chest = game.Wallets.GetResourceValue("HERO", "NifTankArmor_Chest_Pattern") > resourceTestTable.NifTankArmor_Chest_Pattern
  local MaxHealthUpgradePiece = game.Wallets.GetResourceValue("HERO", "MaxHealthUpgradePiece") > resourceTestTable.MaxHealthUpgradePiece
  local MaxRageUpgradePiece = game.Wallets.GetResourceValue("HERO", "MaxRageUpgradePiece") > resourceTestTable.MaxRageUpgradePiece
  local MaxHealthUpgrade = game.Wallets.GetResourceValue("HERO", "MaxHealthUpgrade") > resourceTestTable.MaxHealthUpgrade
  local MaxRageUpgrade = game.Wallets.GetResourceValue("HERO", "MaxRageUpgrade") > resourceTestTable.MaxRageUpgrade
  if nifArmorPattern_Chest then
    game.Audio.PlayBanter("Niflheim_Armor_Quest")
    LD.ActivateQuest("Quest_NiflheimArmorChest_Parent")
    LD.CompleteQuest("Quest_NiflheimArmorChest_01")
    game.SubObject.SoftSave(thisObj)
  end
  if MaxHealthUpgradePiece or MaxHealthUpgrade then
    player:MeterSetValue("Health", player:MeterGetMax("Health"))
  end
  if MaxRageUpgradePiece or MaxRageUpgrade then
    player:MeterSetValue("Blood", player:MeterGetMax("Blood"))
  end
end
function RefreshRageMeter()
  uiCalls_LoadLibrary()
  uiCalls.UI_Refresh_HUD()
  print(player:MeterGetMax("Blood"))
  print("Rage meter refreshed.")
end
function RegisterOnInteractStartCallback(fn)
  if not checkpointOnOpened and not checkpointRequirementDisabled then
    engine.Warning("Chest Error - " .. LD.GetParentTrace(thisObj) .. " has a callback, but isn't set to save a checkpoint. Check the 'CheckpointOnOpened' option in the toolbox to ensure the chest saves correctly")
  end
  if onInteractStartCallbacks == nil then
    onInteractStartCallbacks = {}
  end
  onInteractStartCallbacks[#onInteractStartCallbacks + 1] = fn
end
function FireOnInteractStartCallbacks()
  if onInteractStartCallbacks ~= nil then
    for _, fn in pairs(onInteractStartCallbacks) do
      fn()
    end
  end
end
function RegisterOnInteractFinishCallback(fn)
  if not checkpointOnOpened and not checkpointRequirementDisabled then
    engine.Warning("Chest Error - " .. LD.GetParentTrace(thisObj) .. " has a callback, but isn't set to save a checkpoint. Check the 'CheckpointOnOpened' option in the toolbox to ensure the chest saves correctly")
  end
  if onInteractFinishCallbacks == nil then
    onInteractFinishCallbacks = {}
  end
  onInteractFinishCallbacks[#onInteractFinishCallbacks + 1] = fn
end
function FireOnInteractFinishCallbacks()
  if onInteractFinishCallbacks ~= nil then
    for _, fn in pairs(onInteractFinishCallbacks) do
      fn()
    end
  end
end
local chestSoundEmitter
local chestSoundEvents = {
  Common_IdleLoop = "SND_LOOT_Chest_Wood_Idle_LP",
  Common_OnBreak = "SND_LOOT_Chest_Wood_Open",
  Coffin_IdleLoop = "SND_LOOT_Chest_Wood_Idle_LP",
  Legendary_Gold_IdleLoop = "SND_LOOT_Chest_Gold_Idle_LP",
  Legendary_Runic_IdleLoop = "SND_LOOT_Chest_Legendary_Idle_LP",
  Dispell_IdleLoop = "SND_MAG_Dispel_Barrier_Small_LP",
  Hive_IdleLoop = "SND_LOOT_Chest_Hive_Idle_LP",
  Rune_Appear = "SND_LOOT_Chest_Legendary_Letters",
  Rune_Disappear = "SND_LOOT_Chest_Legendary_Letters"
}
local lootSoundEvents = {
  OnPickUp = "SND_CHR_Kratos_Foley_Item_Pickup_Generic",
  Common_Idle = "SND_LOOT_Shard_White_LP",
  Rare_Idle = "SND_LOOT_Shard_Blue_Idle_LP",
  Legendary_Idle = "SND_LOOT_Shard_Purple_Idle_LP",
  Custom_HammerQuest_Idle = "SND_LOOT_Shard_Orange_LP",
  Custom_MimirsEye_Idle = "SND_LOOT_Shard_Orange_LP"
}
function SoundInit()
  chestSoundEmitter = thisObj:FindSingleSoundEmitterByName("SNDChestSoundEmitter")
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      chestSoundEmitter = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitter)
    end
    for key in pairs(chestSoundEvents) do
      for newKey, newValue in pairs(sounds) do
        if newKey == key and newValue ~= nil and newValue ~= "" then
          chestSoundEvents[key] = newValue
        end
      end
      LD.SoundDebug(tostring(key) .. ": " .. tostring(chestSoundEvents[key]))
    end
  end
end
function PlaySoundChestIdle()
  if chestType == "Common" then
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Common_IdleLoop)
  elseif chestType == "Coffin" then
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Coffin_IdleLoop)
  elseif chestType == "Legendary" or chestType == "Custom_HammerQuest" or chestType == "Custom_MimirsEye" then
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Legendary_Gold_IdleLoop)
  elseif chestType == "Runic_Axe" or chestType == "Runic_Blades" then
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Legendary_Runic_IdleLoop)
  elseif chestType == "Dispell" then
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Dispell_IdleLoop)
  else
    LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Common_IdleLoop)
  end
end
function StopSoundChestIdle()
  if chestType == "Common" then
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Common_IdleLoop)
  elseif chestType == "Coffin" then
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Coffin_IdleLoop)
  elseif chestType == "Legendary" or chestType == "Custom_HammerQuest" or chestType == "Custom_MimirsEye" then
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Legendary_Gold_IdleLoop)
  elseif chestType == "Runic_Axe" or chestType == "Runic_Blades" then
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Legendary_Runic_IdleLoop)
  elseif chestType == "Dispell" then
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Dispell_IdleLoop)
  else
    LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Common_IdleLoop)
  end
end
function PlaySoundBreakCommonChest()
  LD.PlaySound(chestSoundEmitter, chestSoundEvents.Common_OnBreak)
end
function PlaySoundChestIdle_Roots()
  StopSoundChestIdle()
  LD.PlayRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Hive_IdleLoop)
end
function StopSoundChestIdle_Roots()
  LD.StopRestartableSoundLoop(chestSoundEmitter, chestSoundEvents.Hive_IdleLoop)
end
function PlaySoundRuneAppear()
  LD.PlaySound(chestSoundEmitter, chestSoundEvents.Rune_Appear)
end
function PlaySoundRuneDisappear()
  LD.PlaySound(chestSoundEmitter, chestSoundEvents.Rune_Disappear)
end
function PlayLootIdleSound()
  if lootObject then
    local lootSoundEmitter = lootObject.SoundEmitters[1]
    if lootObjectName ~= nil then
      local lootObjectNameForTagging = string.upper(lootObjectName)
      if string.find(lootObjectNameForTagging, "COMMON") then
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Common_Idle)
      elseif string.find(lootObjectNameForTagging, "RARE") then
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Rare_Idle)
      elseif string.find(lootObjectNameForTagging, "LEGENDARY") then
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Legendary_Idle)
      elseif string.find(lootObjectNameForTagging, "CUSTOM_HAMMER") then
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Custom_HammerQuest_Idle)
      elseif string.find(lootObjectNameForTagging, "CUSTOM_MIMIR") then
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Custom_MimirsEye_Idle)
      else
        LD.PlaySound(lootSoundEmitter, lootSoundEvents.Common_Idle)
      end
    else
      LD.PlaySound(lootSoundEmitter, lootSoundEvents.Common_Idle)
    end
  end
end
function StopLootIdleSound()
  if lootObject then
    local lootSoundEmitter = lootObject.SoundEmitters[1]
    if lootObjectName ~= nil then
      local lootObjectNameForTagging = string.upper(lootObjectName)
      if string.find(lootObjectNameForTagging, "COMMON") then
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Common_Idle)
      elseif string.find(lootObjectNameForTagging, "RARE") then
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Rare_Idle)
      elseif string.find(lootObjectNameForTagging, "LEGENDARY") then
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Legendary_Idle)
      elseif string.find(lootObjectNameForTagging, "CUSTOM_HAMMER") then
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Custom_HammerQuest_Idle)
      elseif string.find(lootObjectNameForTagging, "CUSTOM_MIMIR") then
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Custom_MimirsEye_Idle)
      else
        LD.StopSound(lootSoundEmitter, lootSoundEvents.Common_Idle)
      end
    else
      LD.StopSound(lootSoundEmitter, lootSoundEvents.Common_Idle)
    end
  end
end
function PlayLootPickupSound()
  LD.PlaySound(player:FindSingleSoundEmitterByName("SNDLeftHand"), lootSoundEvents.OnPickUp)
end
