local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local TUT = require("game.GlobalTutorials")
local DT = require("loot.DetailTables")
local thisObj, thisLevel, lootObject, player, son, reward, interactZone, isKeyOrb, isFatherSonScroll, isWaistLevelPickup, createInfoTable, interactionOnFinish
local framesToDefer = 2
local isLooted
local lostItemAmount = 0
local spawnerName
local isSpawnerEnemy = false
local SpawnOverrideCallback
IsLoot = true
local overrideReward
local lostItemFXScale = 4
local DEBUG_UseDiscreteAnims = false
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  lootObject = thisObj
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  reward = thisObj:GetLuaTableAttribute("Reward")
  if reward == "QUESTS_FATHERSON_SCROLL" then
    isFatherSonScroll = true
  end
  isKeyOrb = thisObj:FindLuaTableAttribute("IsKeyOrb")
  isWaistLevelPickup = thisObj:FindLuaTableAttribute("WaistLevelPickup")
  if isWaistLevelPickup then
    interactZone = LD.CreateInteractZone_Standard_360(lootObject, "zeroJoint")
    interactZone:SetTags("CarryActive")
  else
    interactZone = LD.CreateInteractZone_ConsumableStomp_360(lootObject, "zeroJoint")
    interactZone:SetTags("CarryActive")
    if isKeyOrb then
      interactZone:SetTags("NotInCombat")
    end
  end
  interactionOnFinish = thisObj:FindLuaTableAttribute("interactionOnFinish")
  if interactionOnFinish ~= nil then
    interactionOnFinish = LD.ExtractCallbacksForEvent(level, obj, interactionOnFinish)
  end
  game.SubObject.Sleep(thisObj)
  SoundInit()
  if SpawnOverrideCallback ~= nil then
    SpawnOverrideCallback()
  end
end
function OnStart(level, go)
  if isLooted then
    lootObject:Hide()
    interactZone:Disable()
    return
  end
  if overrideReward and DT.LostItemCleanUpTable[overrideReward] ~= nil then
    local fxObj = thisObj:FindSingleGOByName("PickupGlow_*")
    if fxObj then
      fxObj:SetScale(lostItemFXScale)
    end
  end
  SoundOnStart()
end
local syncDummy
function OnUseWorld(level, obj)
  if interactZone:PlayerCanInteract() then
    if isKeyOrb and isWaistLevelPickup then
      if syncDummy == nil then
        syncDummy = thisObj:FindSingleGOByName("OrbPickupSyncControl")
        syncDummy:Unparent()
      end
      LD.SetFacingTowardObject(syncDummy, player, {ignoreYAxis = true})
    end
    player:RequestInteract(obj, {InteractZone = interactZone, LerpObject = thisObj})
  end
end
function OnInteractStart()
  if isKeyOrb and isWaistLevelPickup and syncDummy ~= nil then
    LD.PlaySingleSynchMove_KratosObject(syncDummy, "synchJoint", "Waist Level KeyStone", "BRA_PickUpMidEnter", "", interactZone, true, "Bare", {completion_percentage = 0.6})
    PlaySoundPickup()
    return
  end
  local puppeteer = game.Puppeteer.NewForce(thisObj, "Prop Scoop", player)
  if isKeyOrb and isWaistLevelPickup then
    puppeteer:StartMove("MOV_PickUpMidEnter")
  else
    puppeteer:StartMove("MOV_LootScoop_Still")
  end
  puppeteer:OnComplete(function()
    puppeteer:Clear()
    puppeteer = nil
  end)
  PlaySoundPickup()
end
function PickUp(level, obj, creature)
  syncDummy = nil
  PlaySoundPickup_Anim()
  LD.HideFX(thisObj)
end
function AwardLoot(level, obj, creature)
  isLooted = true
  local preTravelerLootTest = game.Wallets.GetResourceValue("HERO", "EnhancedTravelerLoot") == -1
  local preGolemLootTest = game.Wallets.GetResourceValue("HERO", "EnhancedGolemLoot") == -1
  local lootName
  if isSpawnerEnemy then
    lootName = spawnerName
  else
    lootName = thisObj:GetName()
  end
  if overrideReward ~= nil and overrideReward ~= "" then
    CleanUpLostItems(overrideReward)
  end
  local newReward = overrideReward ~= nil and overrideReward or reward
  if isKeyOrb then
    if overrideReward ~= nil then
      if overrideReward == "VIKING_TROLL_KEY_REWARD" then
        game.Wallets.AddResource("HERO", "EntryStoneVikingFuneral", 1, "NO_TELEMETRY", "", true)
      end
      if not game.Loot.RollCondition(newReward, "HERO_SAVEONLY", nil, lootName, isSpawnerEnemy) then
        game.Loot.RollConditionSet(newReward, "HERO_SAVEONLY", nil, lootName, isSpawnerEnemy)
      end
    else
      game.Loot.GrantAward(reward, "HERO_SAVEONLY", nil, lootName, isSpawnerEnemy)
    end
  elseif not game.Loot.RollCondition(newReward, "HERO", nil, lootName, isSpawnerEnemy) then
    game.Loot.RollConditionSet(newReward, "HERO", nil, lootName, isSpawnerEnemy)
  end
  if overrideReward ~= nil and overrideReward ~= "" and DT.LostItemCleanUpTable[overrideReward] and DT.LostItemCleanUpTable.GetName(overrideReward) == "AxeReinforcement" then
    player:CallScript("LuaHook_IncrementAxeQuest")
  end
  if overrideReward ~= nil and overrideReward ~= "" and DT.LostItemCleanUpTable[overrideReward] and DT.LostItemCleanUpTable.GetName(overrideReward) == "BladesReinforcement" then
    player:CallScript("LuaHook_IncrementBladesQuest")
  end
  local postTravelerLootTest = game.Wallets.GetResourceValue("HERO", "EnhancedTravelerLoot") > -1
  local postGolemLootTest = -1 < game.Wallets.GetResourceValue("HERO", "EnhancedGolemLoot")
  if preTravelerLootTest and postTravelerLootTest then
    CheckForSonSick()
    PlayNewMaterialBanter(StartTravelerArmorQuest)
  end
  if preGolemLootTest and postGolemLootTest then
    CheckForSonSick()
    PlayNewMaterialBanter(StartGolemArmorQuest)
  end
  local currentRealmIndex = game.Level.GetVariable("_GBL_CurrentRealm")
  local isInNifleheim = currentRealmIndex == 2 and true or false
  if isInNifleheim then
    local isSoftSaveDisabled = game.World.IsSoftSaveDisabled()
    local NifSpecificChest, NifSpecificWC, NifSpecificEnchantmentRunes
    if game.GetNewGamePlus() then
      NifSpecificChest = -1 < game.Wallets.GetResourceValue("HERO", "KratosArmorChest_ValkyrieSet_Tier06_NGP")
      NifSpecificWC = -1 < game.Wallets.GetResourceValue("HERO", "WeaponComponent_Axe_Valkyrie02_NGP")
      NifSpecificEnchantmentRunes = game.Wallets.GetRunesWithFlags("HERO", {
        "RuneCreator_Unique_Valkyrie01"
      })
    else
      NifSpecificChest = -1 < game.Wallets.GetResourceValue("HERO", "KratosArmorChest_ValkyrieSet_Tier06")
      NifSpecificWC = -1 < game.Wallets.GetResourceValue("HERO", "WeaponComponent_Axe_Valkyrie02")
      NifSpecificEnchantmentRunes = game.Wallets.GetRunesWithFlags("HERO", {
        "RuneCreator_Unique_Valkyrie01"
      })
    end
    local NifSpecificEnchantment = 0 < #NifSpecificEnchantmentRunes
    if NifSpecificChest and LD.GetEntityVariable("NIF_ValkyrieChest") == false then
      LD.SetEntityVariable("NIF_ValkyrieChest", true)
      game.World.EnableSoftSave()
      LD.CallFunctionAfterDelay(function()
        SoftSave()
      end, 0.25)
      if isSoftSaveDisabled then
        LD.CallFunctionAfterDelay(function()
          game.World.DisableSoftSave()
        end, 1.5)
      end
    end
    if NifSpecificWC and LD.GetEntityVariable("NIF_ValkyrieWeapon") == false then
      LD.SetEntityVariable("NIF_ValkyrieWeapon", true)
      game.World.EnableSoftSave()
      LD.CallFunctionAfterDelay(function()
        SoftSave()
      end, 0.25)
      if isSoftSaveDisabled then
        LD.CallFunctionAfterDelay(function()
          game.World.DisableSoftSave()
        end, 1.5)
      end
    end
    if NifSpecificEnchantment and LD.GetEntityVariable("NIF_ValkyrieEnchantment") == false then
      LD.SetEntityVariable("NIF_ValkyrieEnchantment", true)
      game.World.EnableSoftSave()
      LD.CallFunctionAfterDelay(function()
        SoftSave()
      end, 0.25)
      if isSoftSaveDisabled then
        LD.CallFunctionAfterDelay(function()
          game.World.DisableSoftSave()
        end, 1.5)
      end
    end
  end
  if reward == "QUESTS_BROTHERS_SCROLL" then
    timers.StartLevelTimer(3, CheckSecretBusinessQuest)
  end
  if reward == "QUESTS_FATHERSON_SCROLL" then
    timers.StartLevelTimer(2, CheckSecretBusinessQuest02)
  end
  if reward == "QUESTS_ENCHANTER_RING" then
    timers.StartLevelTimer(1, CheckBrok01Quest)
    LD.CompleteQuest("Quest_Brok_Epic_01_Objective_04")
  end
  if reward == "HELBOSS_FLAME_REWARD" then
    game.Wallets.RemoveResource("LOST_ITEMS", "BladesReinforcement", 1)
    game.Wallets.RemoveResource("LOST_ITEMS", "BladesReinforcement_Tracker3", 1)
    player:CallScript("LuaHook_IncrementBladesQuest")
  end
end
function PlayNewMaterialBanter(callbackFunc)
  if game.Audio.CanBanterConversationPlay("Son_New_Mat_Found") then
    game.Audio.PlayBanterNonCritical("Son_New_Mat_Found", callbackFunc)
  else
    callbackFunc()
  end
end
function CheckForSonSick()
  if son:IsAvailableInLevel() then
    game.Audio.SetBanterFact("SonIsSick", "False")
  else
    game.Audio.SetBanterFact("SonIsSick", "True")
  end
end
function PutAway(level, obj, creature)
  if interactionOnFinish ~= nil then
    LD.ExecuteCallbacksForEvent(level, obj, interactionOnFinish, "Interaction Event Finish")
  end
  lootObject:Hide()
  interactZone:Disable()
  if not isKeyOrb then
    LD.CallFunctionAfterDelay(function()
      game.UI.SoftSavePlayerState()
    end, 0.1)
  end
end
function DisableInteraction()
  interactZone:Disable()
end
function EnableInteraction()
  interactZone:Enable()
end
function GetInteractZone()
  return interactZone
end
function StartTravelerArmorQuest()
  LD.ActivateQuest("Quest_TravelerArmor_Parent")
  timers.StartLevelTimer(1, SoftSave)
end
function StartGolemArmorQuest()
  LD.ActivateQuest("Quest_GolemArmor_Parent")
  timers.StartLevelTimer(1, SoftSave)
end
function CheckSecretBusinessQuest()
  local questState = game.QuestManager.GetQuestState("Quest_SecretBusiness01_Parent")
  if questState == "Inactive" then
    game.Audio.PlayBanter("UB1_SecretEnding", StartSecretBusiness)
    timers.StartLevelTimer(1, SoftSave)
  end
end
function CheckSecretBusinessQuest02()
  local questState = game.QuestManager.GetQuestState("Quest_SecretBusiness02_Parent")
  if questState == "Inactive" then
    game.Audio.PlayBanter("Sindri02_FoundDiary", StartSecretBusiness02)
  end
end
function CheckBrok01Quest()
  thisLevel:CallScript("PlayBanterObjective04")
end
function StartSecretBusiness()
  LD.ActivateQuest("Quest_SecretBusiness01_Parent")
  timers.StartLevelTimer(1, SoftSave)
end
function StartSecretBusiness02()
  LD.ActivateQuest("Quest_SecretBusiness02_Parent")
  timers.StartLevelTimer(1, SoftSave)
end
function DestroyLostItem()
  EmptyUniqueRunes()
  if thisObj ~= nil then
    thisObj:Destroy()
  end
end
function OnSpawnedObjectDestroyed()
  EmptyUniqueRunes()
end
function EmptyUniqueRunes()
  if overrideReward == nil or overrideReward == "" or DT.LostItemCleanUpTable[overrideReward] == nil or not DT.LostItemCleanUpTable[overrideReward].IsRune then
    return
  end
  local result = game.Wallets.GetRunesWithFlags("LOST_ITEMS", {
    DT.LostItemCleanUpTable.GetName(overrideReward)
  })
  if #result <= 1 then
    return
  end
  for i = 2, #result do
    game.Wallets.ExchangeRune("HERO", "LOST_ITEMS", result[i])
  end
end
function OnSaveCheckpoint(level, obj)
  return {isLooted = isLooted}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  isLooted = savedInfo.isLooted
end
function SoftSave(level, go)
  game.SubObject.SoftSave(thisObj)
end
function LostItemAwardInWallet(wallet)
  if DT.LostItemCleanUpTable[overrideReward].IsRune then
    local resultList = game.Wallets.GetRunesWithFlags(wallet, {
      DT.LostItemCleanUpTable.GetName(overrideReward)
    })
    return 0 < #resultList
  end
  if DT.LostItemCleanUpTable[overrideReward].TrackerResource ~= nil then
    return 0 < game.Wallets.GetResourceValue(wallet, DT.LostItemCleanUpTable[overrideReward].TrackerResource)
  end
  if DT.LostItemCleanUpTable[overrideReward].IgnoreUniquenessCheck then
    return false
  end
  return 0 < game.Wallets.GetResourceValue(wallet, DT.LostItemCleanUpTable.GetName(overrideReward))
end
function OverrideReward(level, go, newReward)
  if newReward == nil or overrideReward ~= nil then
    return
  end
  overrideReward = newReward
  if DT.LostItemCleanUpTable[overrideReward] == nil then
    if thisObj:HasMarker("LostItem") then
      thisObj:RemoveMarker("LostItem")
    end
    return
  end
  if (LostItemAwardInWallet("HERO") or LostItemAwardInWallet("LOST_ITEMS") or LostItemAwardInWallet("HERO_SAVEONLY")) and not DT.EnchantmentDrops[overrideReward] then
    thisObj:Destroy()
    return
  end
  local beforeRoll = game.Wallets.GetResourceValue("LOST_ITEMS", DT.LostItemCleanUpTable.GetName(overrideReward))
  game.Loot.RollCondition(overrideReward, "LOST_ITEMS", nil, "Lost Item Roll")
  local afterRoll = game.Wallets.GetResourceValue("LOST_ITEMS", DT.LostItemCleanUpTable.GetName(overrideReward))
  lostItemAmount = afterRoll - beforeRoll
end
function LuaHook_SpawnLootObject(level, go, variant, spawnedObjectName)
  function SpawnOverrideCallback()
    if spawnedObjectName ~= "" then
      spawnerName = spawnedObjectName
      isSpawnerEnemy = true
      if LD.GetEntityVariable("TUT_ShowEnemyPickupTutorial") == true then
        TUT.EnemyDrop_Tutorial()
        LD.SetEntityVariable("TUT_ShowEnemyPickupTutorial", false)
      end
    end
    OverrideReward(level, go, DT.Variants[variant])
  end
  if thisObj ~= nil then
    SpawnOverrideCallback()
  end
end
function CleanUpLostItems(tableName)
  if DT.LostItemCleanUpTable[tableName] == nil then
    return
  end
  local resourceName = DT.LostItemCleanUpTable.GetName(tableName)
  local recipeName = DT.LostItemCleanUpTable.GetRecipe(tableName)
  local isRune = DT.LostItemCleanUpTable[tableName].IsRune
  if isRune then
    local result = game.Wallets.GetRunesWithFlags("LOST_ITEMS", {resourceName})
    for _, runeId in ipairs(result) do
      game.Wallets.ExchangeRune("HERO", "LOST_ITEMS", runeId)
    end
    return
  end
  local removedResource = game.Wallets.RemoveResource("LOST_ITEMS", resourceName, lostItemAmount)
  assert(removedResource, "Could not remove resource " .. resourceName .. " from LOST_ITEMS wallet")
  if recipeName ~= nil then
    local removedRecipe = game.Wallets.RemoveRecipe("LOST_ITEMS", recipeName)
    assert(removedRecipe, "Could not remove recipe " .. recipeName .. " from LOST_ITEMS wallet")
  end
end
local soundEmitter
local soundEvents = {
  Generic_Pickup = "SND_CHR_Kratos_Foley_Item_Pickup_Generic",
  Blue_Idle = "SND_LOOT_Shard_Blue_Idle_LP",
  Blue_Pickup = "SND_LOOT_Shard_Blue_Pickup",
  Orange_Idle = "SND_LOOT_Shard_Orange_LP",
  Orange_Pickup = "SND_LOOT_Shard_Orange_Pickup",
  Purple_Idle = "SND_LOOT_Shard_Purple_Idle_LP",
  Purple_Pickup = "SND_LOOT_Shard_Purple_Pickup",
  White_Idle = "SND_LOOT_Shard_White_LP",
  White_Pickup = "SND_LOOT_Shard_White_Pickup",
  KeyOrb_Idle = "",
  KeyOrb_IsPickupSFXAnimTimed = true,
  KeyOrb_Pickup = "SND_MECH_Keystone_Key_Pickup",
  QUESTS_FATHERSON_SCROLL_Idle = "",
  QUESTS_FATHERSON_SCROLL_IsPickupSFXAnimTimed = true,
  QUESTS_FATHERSON_SCROLL_Pickup = "SND_LOOT_Scroll_Pickup"
}
local specialCases = {
  "KeyOrb",
  "QUESTS_FATHERSON_SCROLL"
}
local rarityColor, specialCaseTag
function SoundInit()
  soundEmitter = FindLootSoundEmitter()
end
function SoundOnStart()
  if rarityColor ~= nil then
    StopSoundIdleLoop()
  end
  CheckForSpecialCases()
  DetermineRarityColor()
  PlaySoundIdleLoop()
end
function PlaySoundIdleLoop()
  soundEmitter = FindLootSoundEmitter()
  if specialCaseTag ~= nil then
    LD.PlayRestartableSoundLoop(soundEmitter, soundEvents[specialCaseTag .. "_Idle"])
  elseif rarityColor ~= nil then
    LD.PlayRestartableSoundLoop(soundEmitter, soundEvents[rarityColor .. "_Idle"])
  end
end
function StopSoundIdleLoop()
  if specialCaseTag ~= nil then
    LD.StopRestartableSoundLoop(soundEmitter, soundEvents[specialCaseTag .. "_Idle"])
  elseif rarityColor ~= nil then
    LD.StopRestartableSoundLoop(soundEmitter, soundEvents[rarityColor .. "_Idle"])
  end
end
function PlaySoundPickup()
  StopSoundIdleLoop()
  if specialCaseTag ~= nil then
    if not soundEvents[specialCaseTag .. "_IsPickupSFXAnimTimed"] then
      LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), soundEvents[specialCaseTag .. "_Pickup"])
    end
  elseif rarityColor ~= nil then
    LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), soundEvents[rarityColor .. "_Pickup"])
  end
end
function PlaySoundPickup_Anim()
  if specialCaseTag ~= nil then
    if soundEvents[specialCaseTag .. "_IsPickupSFXAnimTimed"] then
      LD.StopSound(soundEmitter, soundEvents[specialCaseTag .. "_Idle"])
      LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), soundEvents[specialCaseTag .. "_Pickup"])
    else
      LD.PlaySound(player:FindSingleSoundEmitterByName("SNDLeftHand"), soundEvents.Generic_Pickup)
    end
  else
    LD.PlaySound(player:FindSingleSoundEmitterByName("SNDLeftHand"), soundEvents.Generic_Pickup)
  end
end
function DetermineRarityColor()
  local rarity, debugPrintObject
  if thisObj then
    debugPrintObject = thisObj:GetName()
  else
    debugPrintObject = "nil LootObject"
  end
  if specialCaseTag == nil then
    if overrideReward ~= nil then
      local rarityTag = DT.RarityTable[overrideReward]
      if rarityTag then
        if string.upper(rarityTag) == "COMMON" then
          rarity = "COMMON"
        elseif string.upper(rarityTag) == "RARE" then
          rarity = "RARE"
        elseif string.upper(rarityTag) == "LEGENDARY" then
          rarity = "LEGENDARY"
        elseif string.upper(rarityTag) == "EPIC" then
          rarity = "EPIC"
        elseif string.upper(rarityTag) == "PERFECT" then
          rarity = "EPIC"
        else
          print("[" .. debugPrintObject .. " -> DetermineRarityColor (using OVERRIDE REWARD)] : Loot's rarity tag - " .. rarity .. " - does not contain an implemented rarirty level! Is there a new rarity level? ... Using default rarity (COMMON).")
          rarity = "COMMON"
        end
      else
        print("[" .. debugPrintObject .. " -> DetermineRarityColor (using OVERRIDE REWARD)] : Loot's OverrideReward string - " .. overrideReward .. " - was not found in rarity table! Is this loot a new variant? ... Using default rarity (COMMON).")
        rarity = "COMMON"
      end
    elseif reward ~= nil then
      local rewardTag = string.upper(reward)
      if string.find(rewardTag, "COMMON") ~= nil then
        rarity = "COMMON"
      elseif string.find(rewardTag, "RARE") ~= nil then
        rarity = "RARE"
      elseif string.find(rewardTag, "LEGENDARY") ~= nil then
        rarity = "LEGENDARY"
      elseif string.find(rewardTag, "EPIC") ~= nil then
        rarity = "EPIC"
      else
        print("[" .. debugPrintObject .. " -> DetermineRarityColor (using REWARD ATTR)] : Loot's Reward attribute string - " .. rewardTag .. " - does not contain a rarity level! Does the attribute have use a new rarity level? Is it using an abbreviation? ... Using default rarity (COMMON).")
        rarity = "COMMON"
      end
    else
      print("[" .. debugPrintObject .. " -> DetermineRarityColor (FAILED OVERRIDE REWARD & REWARD ATTR NIL CHECKS)] : Loot does not have an overrideReward or a reward value! ... Using default rarity (Common).")
      rarity = "COMMON"
    end
    if rarity then
      if rarity == "COMMON" then
        rarityColor = "White"
      elseif rarity == "RARE" then
        rarityColor = "Blue"
      elseif rarity == "LEGENDARY" then
        rarityColor = "Purple"
      elseif rarity == "EPIC" then
        rarityColor = "Orange"
      else
        print("[" .. debugPrintObject .. " -> DetermineRarityColor (NEW RARITY LEVEL?)] : Rarity determined for loot is unknown! Was a new rarity level added but not accounted for in sound? ... Using default color (White).")
        rarityColor = "White"
      end
    else
      print("[" .. debugPrintObject .. " -> DetermineRarityColor (CRITICAL FAILURE - nil rarity)] : Loot's rarity is nil! This should never happen. ... Using default color (White).")
      rarityColor = "White"
    end
  end
end
function CheckForSpecialCases()
  if isKeyOrb then
    specialCaseTag = specialCases[1]
  end
  if isFatherSonScroll then
    specialCaseTag = specialCases[2]
  end
end
function FindLootSoundEmitter()
  local targetSoundEmitter = thisObj.SoundEmitters[1]
  return targetSoundEmitter
end
