local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local color = require("core.color")
local monitors = require("level.MonitorLibrary")
local CCOS = require("camera.camera_oneshot")
local player, thisObj, interactZone, interactZone_Back, interactZonePair, interactFunction
local includeSon = true
local isLocked
local enterBranch = "BRA_DoorSpreadOpen_RealmTravelReverse"
local exitBranch = "BRA_DoorSpreadOpen_RealmTravel"
local objectAnimName = "DoorSpreadRealmOpen"
local objectAnimName_Back = "DoorSpreadRealmOpenReverse"
local promptJointName = "promptJointFront"
local promptJointName_Back = "promptJointBack"
local synchJointName = "synchJointFront"
local synchJointName_Back = "synchJointBack"
local interactionTriggers, interactionTriggers_SideA, interactionTriggers_SideB, cineInteractionTriggers_SideA, cineInteractionTriggers_SideB, interactionOnStart, interactionOnFinish
local triggerCinematic = false
local openAutomatically = false
local rightAnimBase, leftAnimBase, lightAnim, doorRealm, rightDoorCover, leftDoorCover, leftDoor, rightDoor, doorOpenMonitor, doorCloseMonitor
local cineNumber_RoomActivated = 195
local doorOpenFrame = 220
local isOpen = false
local camInteractApproach, camera_InteractApproach, cameraWorldSpaceForward, astralSymbol, doorCrystal, astralRotateAnim, glassL, glassR
function OnScriptLoaded(level, obj)
  player = game.Player.FindPlayer()
  thisObj = obj
  local externalTable = obj:FindSingleGOByName("TableRefNode")
  if externalTable ~= nil then
    if externalTable.IsRefNode == true then
      externalTable = externalTable.Child
    end
  else
    externalTable = obj
  end
  camInteractApproach = "ENV_InteractApproach"
  cameraWorldSpaceForward = engine.Vector.New(0, 0, 1)
  doorRealm = string.gsub(thisObj.Parent:GetName(), "realmtraveldoor_", "")
  cineInteractionTriggers_SideA = obj:FindLuaTableAttribute("cineInteractionTriggers_SideA")
  cineInteractionTriggers_SideB = obj:FindLuaTableAttribute("cineInteractionTriggers_SideB")
  interactionTriggers = obj:GetLuaTableAttribute("interactionTriggers")
  interactionTriggers_SideA = obj:FindLuaTableAttribute("interactionTriggers_SideA")
  interactionTriggers_SideB = obj:FindLuaTableAttribute("interactionTriggers_SideB")
  interactionOnStart = obj:FindLuaTableAttribute("interactionOnStart")
  interactionOnFinish = obj:FindLuaTableAttribute("interactionOnFinish")
  isLocked = false
  leftDoor = thisObj.Parent:FindSingleGOByName("greydoor_left")
  leftDoorCover = thisObj.Parent:FindSingleGOByName("door_left")
  rightDoor = thisObj.Parent:FindSingleGOByName("greydoor_right")
  rightDoorCover = thisObj.Parent:FindSingleGOByName("door_right")
  leftAnimBase = thisObj:FindSingleGOByName("base_left")
  rightAnimBase = thisObj:FindSingleGOByName("base_right")
  lightAnim = thisObj:FindSingleGOByName("doorLight")
  astralSymbol = thisObj:FindSingleGOByName("astral_symbol")
  doorCrystal = thisObj:FindSingleGOByName("doorCrystal")
  astralRotateAnim = thisObj:FindSingleGOByName("astral_rotate_anim")
  glassL = thisObj:FindSingleGOByName("LeftGlass")
  glassR = thisObj:FindSingleGOByName("RightGlass")
  SoundInit()
  triggerCinematic = false
  interactionTriggers = LD.ExtractCallbacksForEvent(level, obj, interactionTriggers)
  if interactionOnStart ~= nil then
    interactionOnStart = LD.ExtractCallbacksForEvent(level, obj, interactionOnStart)
  end
  if interactionOnFinish ~= nil then
    interactionOnFinish = LD.ExtractCallbacksForEvent(level, obj, interactionOnFinish)
  end
  if cineInteractionTriggers_SideA then
    cineInteractionTriggers_SideA = LD.ExtractCallbacksForEvent(level, obj, cineInteractionTriggers_SideA)
  end
  if cineInteractionTriggers_SideB then
    cineInteractionTriggers_SideB = LD.ExtractCallbacksForEvent(level, obj, cineInteractionTriggers_SideB)
  end
  if interactionTriggers_SideA then
    interactionTriggers_SideA = LD.ExtractCallbacksForEvent(level, obj, interactionTriggers_SideA)
  end
  if interactionTriggers_SideB then
    interactionTriggers_SideB = LD.ExtractCallbacksForEvent(level, obj, interactionTriggers_SideB)
  end
  interactZone = LD.CreateInteractZone_Standard_180(obj:FindSingleGOByName("InteractZone_Inner"), "promptJoint")
  interactZone:SetHintAngle(180)
  interactZone:SetXZRange(2.5)
  interactZone:SetHintXZRange(4)
  interactZone_Back = LD.CreateInteractZone_Standard_180(obj, promptJointName_Back)
  interactZone_Back:SetHintAngle(180)
  interactZone:SetTags("NotWhileSonInteracting")
  interactZone_Back:SetTags("NotWhileSonInteracting")
  interactZonePair = {}
  table.insert(interactZonePair, interactZone)
  table.insert(interactZonePair, interactZone_Back)
  interactZonePair.hasMultiple = true
  game.SubObject.Sleep(thisObj)
end
function OnFirstStart(level, obj)
  local glowObj = obj:FindSingleGOByName("verticalGlow")
  if glowObj ~= nil then
    glowObj:JumpAnimToFrame(0)
    glowObj:PauseAnim()
  end
  lightAnim:JumpAnimToFrame(0)
  lightAnim:PauseAnim()
  StopIceEffect()
end
function OnPreStart(level, obj)
  if isLocked == true then
    Lock()
  else
    Unlock()
  end
end
function OnUpdate(level, obj)
  if camera_InteractApproach ~= nil then
    camera_InteractApproach:Update()
  end
end
function OnUseWorld(level, obj)
  if interactZone:PlayerCanInteract() then
    if cineInteractionTriggers_SideA then
      LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, cineInteractionTriggers_SideA, "Cine Interaction Event (Side A)")
    end
    if triggerCinematic == false then
      interactFunction = PerformInteraction
      player:RequestInteract(obj, interactZone)
    end
    triggerCinematic = false
  elseif interactZone_Back:PlayerCanInteract() then
    local cineNumber = game.Level.GetVariable("CompletedCineNumber")
    if cineNumber > cineNumber_RoomActivated and GameObjects.realm_controller_object.LuaObjectScript.InJotunheimAfterCompletion(cineNumber) == false and GameObjects.realm_controller_object.LuaObjectScript.WaitForHelheimReturnCompletion(cineNumber) == false and doorRealm ~= "asgard" then
      StartGlow(nil, true)
    end
    if cineInteractionTriggers_SideB then
      LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, cineInteractionTriggers_SideB, "Cine Interaction Event (Side B)")
    end
    if interactionOnStart ~= nil then
      LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnStart, "Interaction Event Start")
    end
    if triggerCinematic == false then
      interactFunction = PerformInteraction_Back
      player:RequestInteract(obj, interactZone_Back)
    end
    triggerCinematic = false
  end
end
function OnInteractStart(level, obj)
  thisObj:HideNavObstacle()
  if interactFunction then
    interactFunction()
  end
  local playerPosition = player:GetWorldPosition()
  local interactPosition = obj:GetWorldPosition()
  local toInteract = interactPosition - playerPosition
  local interactFwd = obj:GetWorldForward()
  local interactAngle
  local cameraSubmitTime = 3
  local InteractApproachYaw
  if toInteract:Dot(interactFwd) > 0 then
    interactAngle = LD.GetAngleBetweenVector(-interactFwd, cameraWorldSpaceForward)
  else
    interactAngle = LD.GetAngleBetweenVector(interactFwd, cameraWorldSpaceForward)
  end
  if toInteract:Length() < 2 then
    cameraSubmitTime = 2
  end
  InteractApproachYaw = {Yaw = interactAngle, Pitch = 4}
  game.Camera.Recenter({
    TimeStart = 0,
    TimeDuration = cameraSubmitTime,
    LockRecenter = 1,
    YawRange = -1,
    TriggerLeft = 0,
    TriggerRight = 0,
    ReturnLeft = 180,
    ReturnRight = -180,
    PitchRange = 0
  })
  camera_InteractApproach = CCOS.OneShotCamera.New(camInteractApproach, cameraSubmitTime, InteractApproachYaw)
  camera_InteractApproach:Start()
  camera_InteractApproach:SetCallback(DestroyApproachCamera)
  game.SubObject.Wake(thisObj)
end
function DestroyApproachCamera()
  camera_InteractApproach = nil
end
function OnInteractFinish()
  game.SubObject.Sleep(thisObj)
  thisObj:ShowNavObstacle()
  if interactionOnFinish ~= nil then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionOnFinish, "Interaction Event Finish")
  end
end
function OnInteractDone()
  thisObj:ShowNavObstacle()
end
function OnSaveCheckpoint(level, obj)
  return {isLocked = isLocked, isOpen = isOpen}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  isLocked = savedInfo.isLocked
  isOpen = savedInfo.isOpen
end
function PerformInteraction()
  LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionTriggers, "Interaction Event")
  if interactionTriggers_SideA then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionTriggers_SideA, "Interaction Event (Side A)")
  end
  if triggerCinematic == false then
    if game.AI.FindSon() ~= nil and includeSon then
      LD.PlaySingleSynchMove_KratosSonObject(thisObj, synchJointName, "RealmTravelDoor_WithSon_" .. thisObj:GetName(), exitBranch, exitBranch, objectAnimName, interactZonePair, false, nil, nil, {completion_percentage = 0.7})
    else
      LD.PlaySingleSynchMove_KratosObject(thisObj, synchJointName, "RealmTravelDoor_" .. thisObj:GetName(), exitBranch, objectAnimName, interactZonePair, false, nil, nil, {completion_percentage = 0.7})
    end
  end
  triggerCinematic = false
end
function PerformInteraction_Back()
  LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionTriggers, "Interaction Event")
  if interactionTriggers_SideB then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, interactionTriggers_SideB, "Interaction Event (Side B)")
  end
  if triggerCinematic == false then
    if game.AI.FindSon() ~= nil and includeSon then
      LD.PlaySingleSynchMove_KratosSonObject(thisObj, synchJointName_Back, "RealmTravelDoor_BackWithSon_" .. thisObj:GetName(), enterBranch, enterBranch, objectAnimName_Back, interactZonePair, false, true, nil, {completion_percentage = 0.7})
    else
      LD.PlaySingleSynchMove_KratosObject(thisObj, synchJointName_Back, "RealmTravelDoor_Back_" .. thisObj:GetName(), enterBranch, objectAnimName_Back, interactZonePair, false, nil, nil, {completion_percentage = 0.7})
    end
  end
  triggerCinematic = false
end
function Enable()
  interactZone:Enable()
  interactZone_Back:Enable()
end
function Disable()
  interactZone:Disable()
  interactZone_Back:Disable()
end
function EnableOutterPrompt()
  interactZone_Back:Enable()
end
function DisableOutterPrompt()
  interactZone_Back:Disable()
end
function EnableInnerPrompt()
  interactZone:Enable()
end
function DisableInnerPrompt()
  interactZone:Disable()
end
function Lock()
  isLocked = true
  interactZone:Lock()
  interactZone_Back:Lock()
end
function Unlock()
  isLocked = false
  interactZone:Unlock()
  interactZone_Back:Unlock()
end
function EnableCinematicTrigger()
  triggerCinematic = true
end
function DisableCinematicTrigger()
  triggerCinematic = false
end
function IncludeSon()
  includeSon = true
end
function ExcludeSon()
  includeSon = false
end
function IsOpen()
  return isOpen
end
function AnimateInteractFXForward()
  astralRotateAnim:PlayAnimToEnd(1.5)
  leftDoorCover:JumpAnimToFrame(30)
  leftDoorCover:PlayAnimToFrame(120, 1)
  rightDoorCover:JumpAnimToFrame(30)
  rightDoorCover:PlayAnimToFrame(120, 1)
  rightDoor:JumpAnimToFrame(30)
  rightDoor:PlayAnimToFrame(120, 1)
  leftDoor:JumpAnimToFrame(30)
  leftDoor:PlayAnimToFrame(120, 1)
end
function AnimateInteractFXBackward(immediate)
  if immediate then
    astralRotateAnim:JumpAnimToFrame(0)
    astralRotateAnim:PauseAnim()
    leftDoorCover:JumpAnimToFrame(0)
    leftDoorCover:PauseAnim()
    rightDoorCover:JumpAnimToFrame(0)
    rightDoorCover:PauseAnim()
    rightDoor:JumpAnimToFrame(0)
    rightDoor:PauseAnim()
    leftDoor:JumpAnimToFrame(0)
    leftDoor:PauseAnim()
  else
    astralRotateAnim:PlayAnimToFrame(0, -1)
    leftDoorCover:PlayAnimToFrame(0, -1)
    rightDoorCover:PlayAnimToFrame(0, -1)
    rightDoor:PlayAnimToFrame(0, -1)
    leftDoor:PlayAnimToFrame(0, -1)
  end
end
function CleanupInteractFX()
  astralRotateAnim:JumpAnimToFrame(0)
  astralRotateAnim:PauseAnim()
  leftDoorCover:PlayAnimToFrame(0, -2)
  rightDoorCover:PlayAnimToFrame(0, -2)
  rightDoor:PlayAnimToFrame(0, -5)
  leftDoor:PlayAnimToFrame(0, -5)
end
function StartGlow(speed, playLightAnim)
  if astralSymbol:FindSingleGOByName("particleMagic").AnimFrame < astralSymbol:FindSingleGOByName("particleMagic").AnimLengthFrames then
    if speed == nil then
      speed = 1
    end
    doorCrystal.Child:PlayAnimToEnd()
    glassL:PlayAnimToFrame(60, 1.5)
    glassR:PlayAnimToFrame(60, 1.5)
    LD.ShowFX(rightDoorCover.Child)
    LD.ShowFX(leftDoorCover.Child)
    LD.ShowFX(astralSymbol)
    if playLightAnim == true then
      lightAnim:PlayAnimToEnd()
    end
  end
  PlaySoundAstralSymbolGlowLoop()
end
function StopGlow(immediate)
  AnimateInteractFXBackward(immediate)
  LD.HideFX(rightDoorCover.Child, {immediate = immediate, playAnimReverse = true})
  LD.HideFX(leftDoorCover.Child, {immediate = immediate, playAnimReverse = true})
  LD.HideFX(astralSymbol, {immediate = immediate, playAnimReverse = true})
  if immediate then
    lightAnim:JumpAnimToFrame(0)
    lightAnim:PauseAnim()
    doorCrystal.Child:JumpAnimToFrame(0)
    doorCrystal.Child:PauseAnim()
    glassL:JumpAnimToFrame(0)
    glassL:PauseAnim()
    glassR:JumpAnimToFrame(0)
    glassR:PauseAnim()
  else
    doorCrystal.Child:PlayAnimToEnd(-1)
    glassL:PlayAnimToEnd(-0.5)
    glassR:PlayAnimToEnd(-0.5)
    lightAnim:PlayAnimToEnd(-1)
  end
  StopSoundAstralSymbolGlowLoop()
end
function StartIceEffect()
  local iceStatic = thisObj:FindSingleGOByName("ice_static")
  if iceStatic then
    iceStatic:PlayAnimToEnd(1)
  end
  local iceDoorLeft = thisObj:FindSingleGOByName("ice_door_left")
  if iceDoorLeft then
    iceDoorLeft:PlayAnimToEnd(1)
  end
  local iceBaseLeft = thisObj:FindSingleGOByName("ice_base_left")
  if iceBaseLeft then
    iceBaseLeft:PlayAnimToEnd(1)
  end
  local iceDoorRight = thisObj:FindSingleGOByName("ice_door_right")
  if iceDoorRight then
    iceDoorRight:PlayAnimToEnd(1)
  end
  local iceBaseRight = thisObj:FindSingleGOByName("ice_base_right")
  if iceBaseRight then
    iceBaseRight:PlayAnimToEnd(1)
  end
end
function StopIceEffect()
  local iceStatic = thisObj:FindSingleGOByName("ice_static")
  if iceStatic then
    iceStatic:PlayAnimToEnd(-1)
  end
  local iceDoorLeft = thisObj:FindSingleGOByName("ice_door_left")
  if iceDoorLeft then
    iceDoorLeft:PlayAnimToEnd(-1)
  end
  local iceBaseLeft = thisObj:FindSingleGOByName("ice_base_left")
  if iceBaseLeft then
    iceBaseLeft:PlayAnimToEnd(-1)
  end
  local iceDoorRight = thisObj:FindSingleGOByName("ice_door_right")
  if iceDoorRight then
    iceDoorRight:PlayAnimToEnd(-1)
  end
  local iceBaseRight = thisObj:FindSingleGOByName("ice_base_right")
  if iceBaseRight then
    iceBaseRight:PlayAnimToEnd(-1)
  end
end
function SnapOutterDoorToCloseStartFrame()
  if rightDoor.AnimFrame > 120 then
    rightDoor:JumpAnimToFrame(120)
  end
  if leftDoor.AnimFrame > 120 then
    leftDoor:JumpAnimToFrame(120)
  end
end
function SnapDoorsToOpenStartFrames()
  if rightAnimBase.AnimFrame < 200 then
    rightAnimBase:JumpAnimToFrame(200)
  end
  if leftAnimBase.AnimFrame < 200 then
    leftAnimBase:JumpAnimToFrame(200)
  end
  leftDoorCover:JumpAnimToFrame(120)
  leftDoorCover:PauseAnim()
  rightDoorCover:JumpAnimToFrame(120)
  rightDoorCover:PauseAnim()
  if rightDoor.AnimFrame < 60 then
    rightDoor:JumpAnimToFrame(60)
  end
  if leftDoor.AnimFrame < 60 then
    leftDoor:JumpAnimToFrame(60)
  end
end
function CloseDoorAndEnable(Immediate, playLightAnim)
  isOpen = false
  if playLightAnim == nil then
    playLightAnim = true
  end
  Immediate = Immediate or false
  local speed = -4
  local cineNumber = game.Level.GetVariable("CompletedCineNumber")
  Enable()
  Unlock()
  if Immediate then
    if cineNumber > cineNumber_RoomActivated and playLightAnim then
      lightAnim:JumpAnimToFrame(lightAnim.AnimLengthFrames)
      lightAnim:PauseAnim()
    end
  elseif cineNumber > cineNumber_RoomActivated and playLightAnim then
    lightAnim:PlayAnimToEnd()
  end
  if cineNumber > cineNumber_RoomActivated then
    LD.ShowFX(thisObj:FindSingleGOByName("door_left").Child)
    LD.ShowFX(thisObj:FindSingleGOByName("door_right").Child)
  end
  thisObj:ShowNavObstacle()
end
function CloseDoorAndDisable()
  isOpen = false
  local speed = -4
  DisableInnerPrompt()
  EnableOutterPrompt()
  thisObj:ShowNavObstacle()
end
function OpenDoorAndDisable(Immediate, rateOverride)
  isOpen = true
  Immediate = Immediate or false
  Disable()
  if Immediate then
    lightAnim:JumpAnimToFrame(0)
    lightAnim:PauseAnim()
  else
    lightAnim:PlayAnimToEnd(-1)
  end
  LD.HideFX(thisObj:FindSingleGOByName("door_left").Child)
  LD.HideFX(thisObj:FindSingleGOByName("door_right").Child)
  thisObj:HideNavObstacle()
end
local soundEmitterLeftDoor, soundEmitterRightDoor, soundEmitterLeftBase, soundEmitterRightBase, soundEmitterAstralSymbol, soundDoorOpen, soundBaseOpen, soundCloseFinalImpact, soundOpenFinalImpact, soundDoorOpenInteractRight, soundDoorOpenInteractLeft, soundAstralSymbolFXLoop, soundAstralSymbolOnOpen
function SoundInit()
  soundEmitterLeftDoor = thisObj:FindSingleGOByName("door_left"):FindSingleSoundEmitterByName("SNDSpreadLeft")
  soundEmitterRightDoor = thisObj:FindSingleGOByName("door_right"):FindSingleSoundEmitterByName("SNDSpreadRight")
  soundEmitterLeftBase = thisObj:FindSingleGOByName("base_left"):FindSingleSoundEmitterByName("SNDBaseLeft")
  soundEmitterRightBase = thisObj:FindSingleGOByName("base_right"):FindSingleSoundEmitterByName("SNDBaseRight")
  soundEmitterAstralSymbol = thisObj:FindSingleGOByName("astral_rotate_anim"):FindSingleSoundEmitterByName("SNDAstralSymbol")
  soundDoorOpen = thisObj:FindLuaTableAttribute("soundDoorOpen")
  soundBaseOpen = thisObj:FindLuaTableAttribute("soundBaseOpen")
  soundCloseFinalImpact = thisObj:FindLuaTableAttribute("soundCloseFinalImpact")
  soundOpenFinalImpact = thisObj:FindLuaTableAttribute("soundOpenFinalImpact")
  soundDoorOpenInteractRight = "SND_DOOR_Realm_Room_Double_Door_Pry_Open_R"
  soundDoorOpenInteractLeft = "SND_DOOR_Realm_Room_Double_Door_Pry_Open_L"
  soundAstralSymbolFXLoop = "SND_Door_Realm_Room_Double_Door_Magic_Idle_LP"
  soundAstralSymbolOnOpen = "SND_Door_Realm_Room_Double_Door_Magic_Open"
end
function PlayTravelDoorOpen(anim)
  print("PlayTravelDoorOpen")
  StopDoorOpenMonitor()
  doorOpenMonitor = monitors.CreateAnimFrameMonitor(anim)
  doorOpenMonitor:OnFrameForward(62, function()
    PlaySoundDoorLeft(soundDoorOpen)
    PlaySoundDoorRight(soundDoorOpen)
  end)
  doorOpenMonitor:OnFrameForward(120, function()
    PlaySoundDoorLeft(soundOpenFinalImpact)
    PlaySoundDoorRight(soundOpenFinalImpact)
  end)
  doorOpenMonitor:OnFrameForward(160, function()
    if rightAnimBase.AnimFrame < 220 then
      PlaySoundBaseLeft(soundBaseOpen)
      PlaySoundBaseRight(soundBaseOpen)
    end
  end)
  doorOpenMonitor:OnFrameForward(219, function()
    if rightAnimBase.AnimFrame < 220 then
      PlaySoundBaseLeft(soundOpenFinalImpact)
      PlaySoundBaseRight(soundOpenFinalImpact)
    end
    StopDoorOpenMonitor()
  end)
end
function PlayTravelDoorClose(anim)
  print("PlayTravelDoorClose")
  StopDoorCloseMonitor()
  doorCloseMonitor = nil
  doorCloseMonitor = monitors.CreateAnimFrameMonitor(anim)
  doorCloseMonitor:OnFrameBackward(219, function()
    PlaySoundBaseLeft(soundBaseOpen)
    PlaySoundBaseRight(soundBaseOpen)
  end)
  doorCloseMonitor:OnFrameBackward(160, function()
    PlaySoundBaseLeft(soundCloseFinalImpact)
    PlaySoundBaseRight(soundCloseFinalImpact)
  end)
  doorCloseMonitor:OnFrameBackward(119, function()
    PlaySoundDoorLeft(soundDoorOpen)
    PlaySoundDoorRight(soundDoorOpen)
  end)
  doorCloseMonitor:OnFrameBackward(62, function()
    PlaySoundDoorLeft(soundCloseFinalImpact)
    PlaySoundDoorRight(soundCloseFinalImpact)
    StopDoorCloseMonitor()
  end)
end
function PlayInteractDoorOpen(startFrame, endFrame, direction)
  print("PlayInteractDoorOpen")
  StopDoorOpenMonitor()
  doorOpenMonitor = nil
  doorOpenMonitor = monitors.CreateAnimFrameMonitor(thisObj)
  if direction == "InToOut" then
    PlaySoundAstralSymbolRotateOpen()
    doorOpenMonitor:OnFrameForward(32, function()
      PlaySoundDoorLeft(soundDoorOpen)
      PlaySoundDoorRight(soundDoorOpen)
    end)
    doorOpenMonitor:OnFrameForward(90, function()
      PlaySoundDoorLeft(soundOpenFinalImpact)
      PlaySoundDoorRight(soundOpenFinalImpact)
    end)
    doorOpenMonitor:OnFrame(startFrame, function()
      PlaySoundBaseLeft(soundDoorOpenInteractLeft)
      PlaySoundBaseRight(soundDoorOpenInteractRight)
    end)
    doorOpenMonitor:OnFrameForward(160, function()
      PlaySoundDoorLeft(soundDoorOpen)
      PlaySoundDoorRight(soundDoorOpen)
    end)
    doorOpenMonitor:OnFrameForward(174, function()
      PlaySoundDoorLeft(soundCloseFinalImpact)
      PlaySoundDoorRight(soundCloseFinalImpact)
      StopDoorOpenMonitor()
    end)
  else
    doorOpenMonitor:OnFrame(startFrame, function()
      PlaySoundDoorLeft(soundDoorOpenInteractLeft)
      PlaySoundDoorRight(soundDoorOpenInteractRight)
    end)
    doorOpenMonitor:OnFrameForward(114, function()
      PlaySoundDoorLeft(soundDoorOpen)
      PlaySoundDoorRight(soundDoorOpen)
    end)
    doorOpenMonitor:OnFrameForward(124, function()
      PlaySoundDoorLeft(soundCloseFinalImpact)
      PlaySoundDoorRight(soundCloseFinalImpact)
      StopDoorOpenMonitor()
    end)
  end
end
function RealmTravelRoom2POISoundSequence()
  LD.CallFunctionAfterDelay(function()
    PlaySoundDoorLeft("SND_DOOR_Realm_Room_Double_Freya_Close_L")
    PlaySoundDoorRight("SND_DOOR_Realm_Room_Double_Freya_Close_R")
  end, 18)
end
function PlaySoundBaseLeft(sound)
  LD.PlaySound(soundEmitterLeftBase, sound)
end
function PlaySoundBaseRight(sound)
  LD.PlaySound(soundEmitterRightBase, sound)
end
function PlaySoundDoorLeft(sound)
  LD.PlaySound(soundEmitterLeftDoor, sound)
end
function PlaySoundDoorRight(sound)
  LD.PlaySound(soundEmitterRightDoor, sound)
end
function PlaySoundAstralSymbolRotateOpen()
  LD.PlaySound(soundEmitterAstralSymbol, soundAstralSymbolOnOpen)
end
function PlaySoundAstralSymbolGlowLoop()
  LD.PlayRestartableSoundLoop(soundEmitterAstralSymbol, soundAstralSymbolFXLoop)
end
function StopSoundAstralSymbolGlowLoop()
  LD.StopRestartableSoundLoop(soundEmitterAstralSymbol, soundAstralSymbolFXLoop)
end
function LuaHook_CleanupDoorAnim()
  if game.Level.GetVariable("CompletedCineNumber") > 195 then
    StopGlow(false)
    CleanupInteractFX()
  end
end
function LuaHook_OnStartOpen()
  PlayInteractDoorOpen(12, 99, "OutToIn")
end
function LuaHook_OnStartOpenFromInside()
  AnimateInteractFXForward()
  PlayInteractDoorOpen(64, 148, "InToOut")
end
function LuaHook_OnStartClose()
  game.Blender.Trigger({
    Name = "FFB_GENERIC_RUMBLE_LOW",
    Duration = 0.5,
    TweenIn = {Time = 0},
    TweenOut = {Time = 0.1}
  })
  LD.CallFunctionAfterDelay(function()
    game.Blender.Trigger({
      Name = "FFB_LARGE",
      Duration = 0.35,
      Priority = 50,
      TweenIn = {Time = 0},
      TweenOut = {Time = 0.1}
    })
  end, 0.5)
end
function StopDoorOpenMonitor()
  if doorOpenMonitor ~= nil then
    doorOpenMonitor:Stop()
    doorOpenMonitor:Terminate()
    doorOpenMonitor = nil
  end
end
function StopDoorCloseMonitor()
  if doorCloseMonitor ~= nil then
    doorCloseMonitor:Stop()
    doorCloseMonitor:Terminate()
    doorCloseMonitor = nil
  end
end
function ForceAutoOpen(speed)
  isOpen = true
  interactZone:Disable()
  interactZone_Back:Disable()
  rightDoor:PlayAnimToFrame(220, speed or 1)
  leftDoor:PlayAnimToFrame(220, speed or 1)
  rightDoorCover:PlayAnimToFrame(220, speed or 1)
  leftDoorCover:PlayAnimToFrame(220, speed or 1)
  rightAnimBase:PlayAnimToFrame(220, speed or 1)
  leftAnimBase:PlayAnimToFrame(220, speed or 1)
  thisObj:HideNavObstacle()
  PlayTravelDoorOpen(rightDoor)
end
function ForceAutoClose(speed)
  isOpen = false
  rightDoor:PlayAnimToFrame(0, speed or -4)
  leftDoor:PlayAnimToFrame(0, speed or -4)
  rightDoorCover:PlayAnimToFrame(0, speed or -4)
  leftDoorCover:PlayAnimToFrame(0, speed or -4)
  rightAnimBase:PlayAnimToFrame(0, (speed or -4) / 2)
  leftAnimBase:PlayAnimToFrame(0, (speed or -4) / 2)
  thisObj:ShowNavObstacle()
  PlayTravelDoorClose(rightDoor)
end
function CallScript_JotunheimReturnCleanup(level, obj)
  CloseDoorAndDisable()
  Disable()
  ForceAutoClose()
  StopGlow()
end
function Hack_OpenDoorCover()
  isOpen = true
  SlideBase_Hide()
end
function SlideBase_Hide()
  rightAnimBase:Hide()
  leftAnimBase:Hide()
end
function SlideBase_Show()
  rightAnimBase:Show()
  leftAnimBase:Show()
end
function DebugDrawJoints(inputname, duration)
  local frontColor = color.fuchsia
  local backColor = color.green
  local approachColor = color.red
  if duration == nil or duration < 1 then
    duration = 60
  end
  local templateObj = thisObj.Child
  local indexfront = thisObj:GetJointIndex(synchJointName)
  local indexback = thisObj:GetJointIndex(synchJointName_Back)
  local syncFrontPos = thisObj:GetWorldJointPosition(indexfront)
  local syncBackPos = thisObj:GetWorldJointPosition(indexback)
  local approachIndex = thisObj:FindJointIndex("approachJoint")
  if approachIndex ~= nil then
    local approachPos = thisObj:GetWorldJointPosition(approachIndex)
    engine.DrawTextInWorld(approachPos, "approachJoint", approachColor)
    engine.DrawPoint(approachPos, approachColor, duration)
  end
  engine.DrawTextInWorld(syncFrontPos, synchJointName, frontColor)
  engine.DrawPoint(syncFrontPos, frontColor, duration)
  if syncFrontPos ~= syncBackPos then
    engine.DrawTextInWorld(syncBackPos, synchJointName_Back, backColor)
    engine.DrawPoint(syncBackPos, backColor, duration)
  end
end
function ShowDebugTable(x, y)
  if engine.IsDebug() and LD.GetDistanceBetweenTwoObjects(player, thisObj) < 25 then
    local debugTable = {}
    debugTable.Title = "RealmTravelDoor Info"
    debugTable.X = x or 120
    debugTable.Y = y or 10
    debugTable.TitleColor = engine.Vector.New(255, 0, 128)
    debugTable[#debugTable + 1] = {
      " ",
      thisObj.Parent:GetName()
    }
    debugTable[#debugTable + 1] = {
      "rightAnimBase: ",
      rightAnimBase.AnimFrame
    }
    debugTable[#debugTable + 1] = {
      "leftAnimBase: ",
      leftAnimBase.AnimFrame
    }
    debugTable[#debugTable + 1] = {
      "rightDoor: ",
      rightDoor.AnimFrame
    }
    debugTable[#debugTable + 1] = {
      "leftDoor: ",
      leftDoor.AnimFrame
    }
    debugTable[#debugTable + 1] = {
      "thisObj: ",
      thisObj.AnimFrame
    }
    engine.DrawDebugTable(debugTable)
  end
end
