local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local ND = require("design.NarrativeDesignLibrary")
local uiCalls = require("ui.uicalls")
local actor = require("narrative.actor")
local DL = require("design.DesignerLibrary")
local player, son, bboard, thisLevel, warpDoor, warpRequested, finalDestinationMarkerID, destinationFound
local fastTravelWadsLoaded = false
local PreludeBanterStates = {}
local KeyBanterStates = {}
local currentPreludeBanter, currentKeyBanter, lastPreludeBanterPlayed, lastKeyBanterPlayed
local onRealmEnterBanterPlaying = false
local reCheckTimer
local yOffset = engine.Vector.New(0, 1.8, 0)
local spawnPoints = {
  AtBridge = {
    player = "PlayerDeathWarpLocator",
    son = "SonDeathWarpLocator"
  },
  FarAway = {
    player = "PlayerWarpLocator",
    son = "SonWarpLocator"
  }
}
local closestDoor, doorPositions
local doorWarped = false
function OnScriptLoaded(level)
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  thisLevel = level
  warpDoor = level:FindSingleGameObject("ft_RealmVersionController").Child
  doorPositions = thisLevel:FindGameObjects("DoorPosition*")
  InitializeBanterStates()
  reCheckTimer = timers.StartLevelTimer(0.25, EnableFTDoor)
  reCheckTimer:Stop()
end
function OnPreStart(level)
  if LD.GetCurrentRealm() ~= "Muspelheim" then
    DeterminePlayStates()
  end
end
function OnStart(level)
  local delay = 6
  if SkipBanter() == true then
    delay = 0
  end
  warpDoor.LuaObjectScript.Disable()
  _G.StartLevelTimer(delay, RestartCheckForLoad())
  if SkipBanter() == false and GetIsFinale() then
    GameObjects.FastTravel_WarpDetector:HideEntityVolume()
    GameObjects.Cal410_LoadCheck:ShowEntityVolume()
    DisableInstantDeath()
    EnableMightAsWellJump()
    LD.CompleteQuest("Quest_Caldera_Objective736")
    _G.StartLevelTimer(3, function()
      game.World.StoreCheckpoint()
    end)
  else
    GameObjects.Cal410_LoadCheck:HideEntityVolume()
    GameObjects.FastTravel_WarpDetector:ShowEntityVolume()
    if not game.Level.GetVariable("_GBL_JotTowerRevealed") then
      EnableInstantDeath()
    else
      DisableInstantDeath()
      GameObjects.RT:Hide()
    end
    DisableMightAsWellJump()
  end
end
function RestartCheckForLoad()
  if finalDestinationMarkerID ~= nil and finalDestinationMarkerID ~= "nil" then
    bboard = player:GetPrivateBlackboard()
    if bboard and not bboard:Exists("FastTravelLoadRequested") then
      destinationFound = true
      if bboard ~= nil then
        bboard:Erase("FinalDestinationMarker")
      else
        bboard = player:GetPrivateBlackboard()
        bboard:Erase("FinalDestinationMarker")
      end
      if game.Map.SpecialFastTravelRestart then
        game.Map.SpecialFastTravelRestart(thisLevel)
      end
      warpDoor.LuaObjectScript.SetDestination(finalDestinationMarkerID)
      bboard:Set("PlayerIsFastTraveling", true)
      if not game.Map.SpecialFastTravelRestart then
        OnFastTravelWadsLoaded()
      end
    end
  end
end
function SkipBanter()
  if LD.GetCurrentRealm() == "Muspelheim" then
    return true
  end
  if currentPreludeBanter == nil and currentKeyBanter == nil and GameObjects.Btr_RoadLore.LuaObjectScript.AllRoadLorePlayed() == true then
    return true
  end
  return false
end
function GetSpawnPoint()
  if SkipBanter() then
    return "FarAway"
  end
  local skipPreludeSpawn = false
  if currentPreludeBanter and currentKeyBanter and PreludeBanterStates[currentPreludeBanter].Priority ~= KeyBanterStates[currentKeyBanter].Priority then
    skipPreludeSpawn = true
  end
  if currentPreludeBanter and not skipPreludeSpawn then
    DisableSonLeadTheWay()
    GameObjects.ContextAction_Bridge.LuaObjectScript.Enable()
    return PreludeBanterStates[currentPreludeBanter].SpawnPoint or "AtBridge"
  elseif currentKeyBanter then
    EnableSonLeadTheWay()
    return KeyBanterStates[currentKeyBanter].SpawnPoint or "FarAway"
  else
    return "FarAway"
  end
end
function WarpIntoFastTravel()
  uiCalls.UI_Journal_Log_Unpause()
  doorWarped = false
  local spawn = spawnPoints[GetSpawnPoint()]
  local playerWarp = thisLevel:FindSingleGameObject(spawn.player)
  local sonWarp = thisLevel:FindSingleGameObject(spawn.son)
  if playerWarp ~= nil then
    player:Warp(playerWarp.WorldPosition, playerWarp:GetWorldForward())
    if son ~= nil and GetIsSonAvailable() then
      son:Warp(sonWarp.WorldPosition, sonWarp:GetWorldForward(), true)
    end
  end
end
function PlayerEnteredWarpZone()
  if not warpRequested then
    game.Compass.OnWarp()
    local delay = 2
    if SkipBanter() == true then
      delay = 0
    end
    _G.StartLevelTimer(delay, GetDestination)
    game.Compass.SetDesignerForcedHide(true)
    warpRequested = true
  end
end
function GetDestination()
  if not destinationFound then
    bboard = player:GetPrivateBlackboard()
    if bboard and bboard:Exists("FinalDestinationMarker") then
      finalDestinationMarkerID = nil
      finalDestinationMarkerID = bboard:GetLargeInteger("FinalDestinationMarker")
      StartLoadRequests()
      destinationFound = true
    elseif finalDestinationMarkerID ~= nil then
      StartLoadRequests()
    end
  end
  game.World.StoreCheckpoint()
  _G.StartLevelTimer(1.5, TryToPlayBanter)
end
function StartLoadRequests()
  if bboard ~= nil then
    bboard:Erase("FinalDestinationMarker")
  else
    bboard = player:GetPrivateBlackboard()
    bboard:Erase("FinalDestinationMarker")
  end
  game.Map.LoadFastTravelWads(finalDestinationMarkerID, thisLevel)
  warpDoor.LuaObjectScript.SetDestination(finalDestinationMarkerID)
  bboard:Set("PlayerIsFastTraveling", true)
end
function OnFastTravelWadsLoaded(level)
  fastTravelWadsLoaded = true
  local delay = 6
  if SkipBanter() == true then
    delay = 0
  end
  _G.StartLevelTimer(delay, function()
    if destinationFound and not GetIsFinale() and not onRealmEnterBanterPlaying then
      LD.AddControllerRumble({EffectName = "FFB_Medium", Duration = 1})
      EnableFTDoor()
    end
  end)
end
function ClearWarpData()
  destinationFound = false
  warpRequested = false
  fastTravelWadsLoaded = false
  finalDestinationMarkerID = nil
  warpDoor.LuaObjectScript.Disable()
end
function EnableFTDoor()
  if doorWarped == true then
    return
  end
  reCheckTimer:Reset()
  local door = FindValidDoor()
  if door ~= nil then
    doorWarped = true
    WarpDoorAndEnable(door)
  else
    reCheckTimer:Start()
  end
end
function CleanupContextAction(contextAction)
  contextAction.LuaObjectScript.Disable()
end
function UpdateCompassAndSonAvailability()
  game.Compass.SetDesignerForcedHide(false)
  if GetIsSonAvailable() then
    son:RemoveAvailabilityRequest("LevelDesignScript")
  end
end
function WarpDoorAndEnable(door)
  reCheckTimer:Stop()
  local doorWarpPos = door:GetWorldPosition()
  local doorWarpFacing = door:GetWorldForward()
  warpDoor:SetWorldPosition(doorWarpPos)
  warpDoor:SetWorldFacing(doorWarpFacing)
  warpDoor.LuaObjectScript.Enable()
  local reactionStim = door:FindSingleGOByName("Reaction_Interest")
  if reactionStim then
    reactionStim.LuaObjectScript.Trigger()
  end
  local contextAction = door:FindSingleGOByName("CA_Door")
  if contextAction then
    contextAction.LuaObjectScript.Enable()
    warpDoor.LuaObjectScript.RegisterCallbackOnInteractStart(function()
      CleanupContextAction(contextAction)
    end)
  end
  local contextAction_2 = door:FindSingleGOByName("CA_Door_2")
  if contextAction_2 then
    contextAction_2.LuaObjectScript.Enable()
    warpDoor.LuaObjectScript.RegisterCallbackOnInteractStart(function()
      CleanupContextAction(contextAction_2)
    end)
  end
  warpDoor.LuaObjectScript.RegisterCallbackOnInteractStart(UpdateCompassAndSonAvailability)
end
function FindValidDoor()
  local CompareDistanceToPlayer = function(loc_01, loc_02)
    local dist1 = game.AIUtil.Distance(player, loc_01)
    local dist2 = game.AIUtil.Distance(player, loc_02)
    return dist1 < dist2
  end
  table.sort(doorPositions, CompareDistanceToPlayer)
  local closestTwo = {}
  for i = 1, #doorPositions do
    if game.AIUtil.Distance(doorPositions[i], player) > 2 then
      closestTwo[1] = doorPositions[i]
      break
    end
  end
  for i = 1, #doorPositions do
    if doorPositions[i] ~= closestTwo[1] then
      local angleBetweenDoors = DL.GetAngleBetweenVector(closestTwo[1].WorldPosition - player.WorldPosition, doorPositions[i].WorldPosition - player.WorldPosition)
      angleBetweenDoors = math.abs(angleBetweenDoors)
      if 45 < angleBetweenDoors and game.AIUtil.Distance(doorPositions[i], player) > 2 then
        closestTwo[2] = doorPositions[i]
        break
      end
    end
  end
  for i = 1, #closestTwo do
    local sphereHit = game.World.SpherecastCollision(player.WorldPosition + yOffset, closestTwo[i].WorldPosition + yOffset, 0.1, {
      SourceGameObject = player,
      ExcludeGameObject = closestTwo[i],
      EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier"),
      CollidesWith = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
    }, true)
    if game.World.IsSphereOnScreen(closestTwo[i].WorldPosition, 0.25) and sphereHit == nil then
      if engine.IsDebug() then
        local green = 65280
        local yellow = 16776960
        local obj_offset = engine.Vector.New(0, 2, 0)
        local plyr_offset = engine.Vector.New(0, 1, 0)
        engine.DrawSphere(closestTwo[i].WorldPosition + obj_offset, 0.5, 16711680)
        local chosenColor
        if i == 1 then
          chosenColor = green
        else
          chosenColor = yellow
        end
        engine.DrawFillSphere(closestTwo[i].WorldPosition + obj_offset, 0.25, chosenColor)
        engine.DrawLine(player.WorldPosition + plyr_offset, closestTwo[i].WorldPosition + obj_offset, 65280)
      end
      return closestTwo[i]
    end
  end
  return nil
end
function GetAngleToDoor(target)
  local forward_vector = player:GetWorldForward()
  local dot = forward_vector:Dot(GetDirectionToDoor(target))
  local degree = math.acos(dot)
  degree = math.deg(degree)
  return degree
end
function GetDirectionToDoor(target)
  local playerPos = player:GetWorldPosition()
  local doorPos = closestDoor:GetWorldPosition()
  local dir = doorPos - playerPos
  return dir:Normalized()
end
function WarpInFSE()
  local fastTravelFog_FSE = {
    EffectName = "FSE_FAST_TRAVEL_VOLUME_FOG",
    TweenInTime = 0.5,
    TweenOutTime = 0.5,
    Duration = 1.5
  }
  local fastTravelExposure_FSE = {
    EffectName = "FSE_FAST_TRAVEL_EXPOSURE",
    TweenInTime = 0.7,
    TweenOutTime = 2,
    Duration = 0.8
  }
  game.FX.SubmitEffect(fastTravelFog_FSE)
  game.FX.SubmitEffect(fastTravelExposure_FSE)
end
function OnSaveCheckpoint(level)
  return {
    finalDestinationMarkerID = tostring(finalDestinationMarkerID),
    lastPreludeBanterPlayed = lastPreludeBanterPlayed
  }
end
function OnRestoreCheckpoint(level, savedInfo)
  if savedInfo.finalDestinationMarkerID ~= nil and savedInfo.finalDestinationMarkerID ~= "nil" then
    finalDestinationMarkerID = game.LargeInteger.FromString(savedInfo.finalDestinationMarkerID)
  end
  lastPreludeBanterPlayed = savedInfo.lastPreludeBanterPlayed or nil
end
function InitializeBanterStates()
  PreludeBanterStates = {
    FirstFastTravel = {
      SpawnPoint = "AtBridge",
      Banter = "010_FirstFastTravel",
      MinCine = 0,
      MaxCine = 330,
      Priority = 1
    },
    GetAround = {
      SpawnPoint = "FarAway",
      Banter = "080_GetAround",
      MinCine = 330,
      MaxCine = 515,
      Priority = 2
    },
    ICanFly = {
      SpawnPoint = "AtBridge",
      Banter = "110_ICanFly",
      MinCine = 330,
      MaxCine = 515,
      Priority = 3,
      Requirement = function()
        return lastPreludeBanterPlayed == nil
      end
    },
    WayWayDown = {
      SpawnPoint = "AtBridge",
      Banter = "130_WayWayDown",
      MinCine = 330,
      MaxCine = 515,
      Priority = 4,
      Requirement = function()
        return lastPreludeBanterPlayed == nil
      end
    },
    BloodyOblivion = {
      SpawnPoint = "AtBridge",
      Banter = "170_BloodyOblivion",
      MinCine = 330,
      MaxCine = 515,
      Priority = 5,
      Requirement = function()
        return lastPreludeBanterPlayed == nil
      end
    },
    CanGodsFly = {
      SpawnPoint = "AtBridge",
      Banter = "180_CanGodsFly",
      MinCine = 400,
      MaxCine = 515,
      Priority = 2
    },
    TalismanGone = {
      SpawnPoint = "AtBridge",
      Banter = "230_TalismanGone",
      MinCine = 512,
      MaxCine = 999,
      Priority = 2,
      Requirement = function()
        return game.Level.GetVariable("_GBL_JotTowerRevealed")
      end
    },
    BoatLoreOnly = {
      SpawnPoint = "FarAway",
      Banter = "270_BoatLoreOnly",
      MinCine = 330,
      MaxCine = 999,
      Priority = 10,
      Requirement = function()
        return GameObjects.Btr_RoadLore.LuaObjectScript.AllRoadLorePlayed()
      end
    }
  }
  KeyBanterStates = {
    LongRideDown = {
      SpawnPoint = "FarAway",
      Banter = "050_LongRideDown",
      MinCine = 310,
      MaxCine = 329,
      Priority = 1
    },
    MimirTalksBaldur = {
      SpawnPoint = "FarAway",
      Banter = "002_MimirTalksBaldur",
      MinCine = 330,
      MaxCine = 339,
      Priority = 1
    },
    TyrsVaultRedirect = {
      SpawnPoint = "FarAway",
      Banter = "GBL_010_TyrsVaultRedirect",
      MinCine = 360,
      MaxCine = 361,
      Priority = 1
    },
    MimirHeardThat = {
      SpawnPoint = "FarAway",
      Banter = "150_MimirHeardThat",
      MinCine = 380,
      MaxCine = 390,
      Priority = 1
    },
    DontTellFreya = {
      SpawnPoint = "FarAway",
      Banter = "DontTellFreya",
      MinCine = 330,
      MaxCine = 9999,
      Priority = 1
    }
  }
end
function DeterminePlayStates()
  currentPreludeBanter, currentKeyBanter = GetCurrentBanters()
end
function GetCurrentBanters()
  local chosenPreludeBanter = GetBanterState(PreludeBanterStates)
  local chosenKeyBanter = GetBanterState(KeyBanterStates)
  return chosenPreludeBanter, chosenKeyBanter
end
function GetBanterState(banterStateTable)
  local BanterToPlay
  local cineNum = game.Level.GetVariable("CompletedCineNumber")
  for k, v in pairs(banterStateTable) do
    local newProposedBanterToPlay
    if cineNum >= v.MinCine and cineNum < v.MaxCine then
      if v.Requirement ~= nil then
        if v.Requirement() then
          newProposedBanterToPlay = k
        end
      else
        newProposedBanterToPlay = k
      end
      if newProposedBanterToPlay and game.Audio.CanBanterConversationPlay(banterStateTable[newProposedBanterToPlay].Banter) then
        if BanterToPlay then
          if banterStateTable[newProposedBanterToPlay].Priority < banterStateTable[BanterToPlay].Priority then
            BanterToPlay = newProposedBanterToPlay
          end
        else
          BanterToPlay = newProposedBanterToPlay
        end
      end
    end
  end
  return BanterToPlay
end
function TryToPlayBanter()
  if currentPreludeBanter and currentKeyBanter and PreludeBanterStates[currentPreludeBanter].Priority == KeyBanterStates[currentKeyBanter].Priority then
    lastPreludeBanterPlayed = currentPreludeBanter
    game.Audio.PlayBanter(PreludeBanterStates[currentPreludeBanter].Banter, OnPreludeBanterComplete, nil, false, OnRealmEnterBanterFired)
  elseif currentKeyBanter then
    game.Audio.PlayBanter(KeyBanterStates[currentKeyBanter].Banter, OnRealmEnterBanterFinished, nil, false, OnRealmEnterBanterFired)
  elseif currentPreludeBanter then
    lastPreludeBanterPlayed = currentPreludeBanter
    game.Audio.PlayBanter(PreludeBanterStates[currentPreludeBanter].Banter, function()
      timers.StartLevelTimer(0.5, TryToPlayRoadLore)
    end, nil, false, OnRealmEnterBanterFired)
  else
    TryToPlayRoadLore()
  end
end
function TryToPlayRoadLore()
  GameObjects.Btr_RoadLore.LuaObjectScript.TryToPlayRoadLore("OnRealmEnterBanterFinished", "OnRealmEnterBanterFired")
end
function OnPreludeBanterComplete()
  if not GetIsFinale() then
    GameObjects.ContextAction_Bridge.LuaObjectScript.Disable()
    EnableSonLeadTheWay()
  end
  timers.StartLevelTimer(2, function()
    game.Audio.PlayBanter(KeyBanterStates[currentKeyBanter].Banter, OnRealmEnterBanterFinished)
  end)
end
function BanterStateDebug()
  local debugTable = {}
  debugTable.X = 10
  debugTable.Y = 22
  table.insert(debugTable, {
    "PreludeBanterStates"
  })
  for k, v in pairs(PreludeBanterStates) do
    print(k, v)
    local insertTable = {}
    table.insert(insertTable, k)
    for l, w in pairs(v) do
      table.insert(insertTable, l)
      table.insert(insertTable, w)
    end
    table.insert(debugTable, insertTable)
  end
  table.insert(debugTable, {
    "KeyBanterStates"
  })
  for k, v in pairs(KeyBanterStates) do
    print(k, v)
    local insertTable = {}
    table.insert(insertTable, k)
    for l, w in pairs(v) do
      table.insert(insertTable, l)
      table.insert(insertTable, w)
    end
    table.insert(debugTable, insertTable)
  end
  engine.DrawDebugTable(debugTable)
end
function EnableSonLeadTheWay()
  if GetIsSonAvailable() then
    GameObjects.RBR_Son_LTW.LuaObjectScript.Enable()
  end
end
function DisableSonLeadTheWay()
  if GetIsSonAvailable() then
    GameObjects.RBR_Son_LTW.LuaObjectScript.Disable()
  end
end
function GetIsSonAvailable()
  local cineNum = game.Level.GetVariable("CompletedCineNumber")
  return cineNum < 365 or 390 <= cineNum or son ~= nil and son:IsAvailableInLevel()
end
function GetIsFinale()
  bboard = player:GetPrivateBlackboard()
  if bboard:GetBoolean("FinaleTravel") then
    if game.Wallets.GetResourceValue("HERO_SAVEONLY", "TyrAmuletUnlock") > 0 and game.Level.GetVariable("CompletedCineNumber") < 520 then
      bboard:Erase("FinaleTravel")
      return true
    else
      return false
    end
  end
end
function LoadCheck_410()
  game.UI.LoadCheck("Cal410_FinaleTowerFight")
end
function PlayMightAsWellJumpBanter()
  local cal410 = game.FindLevel("Cal410_FinaleTowerFight")
  if cal410 then
    cal410:CallScript("StartPlungeCine")
  else
    EnableMightAsWellJump()
  end
end
function EnableMightAsWellJump()
  GameObjects.Interact_MightAsWellJump.LuaObjectScript.Enable()
end
function DisableMightAsWellJump()
  GameObjects.Interact_MightAsWellJump.LuaObjectScript.Disable()
end
function EnableInstantDeath()
  if GetIsSonAvailable() then
    GameObjects.Interact_InstantDeath.LuaObjectScript.Enable()
    GameObjects.Interact_InstantDeath.LuaObjectScript.GetInteractZone():SetRequiresSonUnoccupied(true)
  end
end
function DisableInstantDeath()
  GameObjects.Interact_InstantDeath.LuaObjectScript.Disable()
end
local kratosActor, sonActor
local actorTable = {}
function DeathJump()
  if not GetIsSonAvailable() then
    return
  end
  kratosActor = actor.Actor.New("Kratos", game.Player.FindPlayer)
  sonActor = actor.Actor.New("Son", game.AI.FindSon)
  actorTable = {
    {Actor = sonActor}
  }
  local cineSeq = ND.CreateCineSequence(thisLevel, GameObjects.DeathJumpSync, "DeathJump")
  cineSeq:RequestCineModeAndWait(kratosActor, GameObjects.DeathJumpSync)
  cineSeq:Do(function()
    local recenterTable = {
      TimeStart = 0,
      TimeDuration = 4,
      LockRecenter = 1,
      PitchRange = -1,
      YawRange = 180,
      TriggerUp = 0,
      TriggerDown = 0,
      ReturnUp = 90,
      ReturnDown = -90
    }
    game.Camera.Recenter(recenterTable)
  end)
  cineSeq:ActorForceApproachAndWait(kratosActor, {
    Branch = "BRA_RealmFallFail",
    ReferenceJoint = "JODeathJumpSync",
    speed = ND.KraWalkSpeed,
    stop = false,
    foot = game.Creature.Foot.kLeft,
    radius = ND.ArrivalRadius,
    weapon_state = "bare",
    completion_percentage = 0.7
  })
  cineSeq:Do(function()
    game.Audio.PlayBanter("020_FastTravelSuicide")
  end)
  cineSeq:ActorSync(kratosActor, {
    Slaves = actorTable,
    Branch = "BRA_RealmFallFail",
    ReferenceJoint = "JODeathJumpSync"
  })
  cineSeq:WaitUntilActorAnimPastPercentage(kratosActor, 0.8)
  cineSeq:Do(function()
    uiCalls.UI_Event_Queue_Death_Tip(43009)
    game.UI.ShowFakeDeathScreen()
  end)
  cineSeq:WaitUntilActorAnimPastPercentage(kratosActor, 0.85)
  cineSeq:Do(function()
    uiCalls.UI_Event_Fade_To_Black(0.5)
    game.Audio.PlaySound("SND_UX_Health_Kratos_Low_Health_LP")
    game.Audio.PlaySound("SND_UX_Health_Kratos_Low_Health_STNG")
    game.Audio.PlaySound("SND_UX_Health_Kratos_Death_STNG")
    game.Audio.PlaySound("SND_UX_RealmFallFail_FakeDeath")
  end)
  cineSeq:WaitForAndCompleteCineMode(kratosActor, GameObjects.DeathJumpSync)
  cineSeq:StopPuppetingActor(sonActor)
  cineSeq:StopPuppetingActor(kratosActor)
  cineSeq:StartSequence()
end
function OnShowFakeDeathScreen(level, obj)
  timers.StartLevelTimer(0.05, WarpAfterDeath)
end
function OnHideFakeDeathScreen(level, obj)
  uiCalls.UI_Event_Fade_In(0)
  game.Audio.StopSound("SND_UX_Health_Kratos_Low_Health_LP")
  game.Audio.PlaySound("SND_UX_Health_Kratos_Death_Revived_STNG")
  game.Audio.PlaySound("SND_UX_RealmFallFail_FakeDeath_Reset")
end
function WarpAfterDeath()
  EnableInstantDeath()
  if son ~= nil and GetIsSonAvailable() then
    son:Warp(GameObjects.SonDeathWarpLocator:GetWorldPosition(), GameObjects.SonDeathWarpLocator:GetWorldForward())
  end
  player:Warp(GameObjects.PlayerDeathWarpLocator:GetWorldPosition(), GameObjects.PlayerDeathWarpLocator:GetWorldForward())
  timers.StartLevelTimer(0.2, function()
    local recenterTable = {
      TimeStart = 0,
      TimeDuration = 0,
      LockRecenter = 1,
      PitchRange = -1,
      YawRange = 180,
      TriggerUp = 0,
      TriggerDown = 0,
      ReturnUp = -8,
      ReturnDown = -8,
      ReturnLeft = 0,
      ReturnRight = 0
    }
    game.Camera.Recenter(recenterTable)
  end)
end
function OnRealmEnterBanterFired()
  onRealmEnterBanterPlaying = true
  if not GetIsFinale() and GetIsSonAvailable() then
    local availabilityState = {Unoccupied = false}
    son:SetNewAvailabilityRequest("LevelDesignScript", availabilityState)
  end
  if currentKeyBanter == "MimirTalksBaldur" then
    LD.EnableBoatLore()
  end
end
function OnRealmEnterBanterFinished()
  onRealmEnterBanterPlaying = false
  if not GetIsFinale() then
    if son ~= nil and GetIsSonAvailable() and son.RemoveAvailabilityRequest then
      son:RemoveAvailabilityRequest("LevelDesignScript")
    end
    GameObjects.ContextAction_Bridge.LuaObjectScript.Disable()
    EnableSonLeadTheWay()
    if not warpDoor.LuaObjectScript.IsEnabled() and destinationFound and fastTravelWadsLoaded then
      EnableFTDoor()
    end
  end
end
function UpdateLoadGroup()
  game.EvaluateLoadZones()
end
function OnEvaluateLoadGroup_CalderaShop_Cine_LoadGroup()
  local completedCineNum = game.Level.GetVariable("CompletedCineNumber")
  if completedCineNum == 310 then
    return tostring("brokCares")
  elseif completedCineNum == 380 then
    return tostring("brokSavesCat")
  else
    return tostring("")
  end
end
function OnEvaluateLoadGroup_BeachWaterfall_AI_LoadGroup()
  local completedCineNum = game.Level.GetVariable("CompletedCineNumber")
  if completedCineNum < 340 then
    return tostring("waterLevel1")
  else
    return tostring("waterLevel2")
  end
end
function OnEvaluateLoadGroup_StnWads()
  local cineNum = game.Level.GetVariable("CompletedCineNumber")
  if 570 <= cineNum then
    return "PostFinalBoss"
  else
    return "PreFinalBoss"
  end
end
