local LD = require("design.LevelDesignLibrary")
local thisObj, thisLevel, player, son, elevatorObject, elevatorState, switch_Obj, switchTop_Obj, switchBottom_Obj, topFloorAnimName, bottomFloorAnimName, moveUpAnimName, moveDownAnimName
local moveUpSpeed = 1
local moveDownSpeed = 1
local hideExitSheetPercent = {top = 1, bottom = 1}
local invisSheetMonitor
local enabled = true
local checkpointOnFloor = false
local playerActivatedElevator = false
local invisSheet
local triggerCinematic = false
local enableSonWarp = true
local cameraShakeType, rumbleType
local waitWhileSonIsOnScreen = false
local sonRidePositionUp, sonRidePositionDown, son_MoveToPoint, speedControlZone, onMoveDown, onMoveUp, onReachTop, onReachBottom, waitingOnSonCallback, onWaitComplete, checkpointOverrideTopFloor, checkpointOverrideBottomFloor
local log = "NON-BASIC_ELEVATOR: "
function OnScriptLoaded(level, obj)
  thisLevel = level
  thisObj = obj
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  elevatorObject = obj.Parent.Parent
  checkpointOnFloor = obj:GetLuaTableAttribute("CheckpointGame")
  topFloorAnimName = obj:GetLuaTableAttribute("TopFloorAnimName")
  bottomFloorAnimName = obj:GetLuaTableAttribute("BottomFloorAnimName")
  moveUpAnimName = obj:GetLuaTableAttribute("MoveUpAnimName")
  moveDownAnimName = obj:GetLuaTableAttribute("MoveDownAnimName")
  enabled = obj:GetLuaTableAttribute("StartEnabled")
  cameraShakeType = obj:GetLuaTableAttribute("CameraShake")
  moveUpSpeed = obj:GetLuaTableAttribute("MoveUpSpeed")
  moveDownSpeed = obj:GetLuaTableAttribute("MoveDownSpeed")
  hideExitSheetPercent.top = obj:GetLuaTableAttribute("MoveUpExitPercent")
  hideExitSheetPercent.bottom = obj:GetLuaTableAttribute("MoveDownExitPercent")
  hideExitSheetPercent.top = hideExitSheetPercent.top / 100
  hideExitSheetPercent.bottom = hideExitSheetPercent.bottom / 100
  speedControlZone = elevatorObject:FindSingleGOByName("SpeedControlZone")
  sonRidePositionUp = obj:FindLuaTableAttribute("SonRiderJoint_Up")
  sonRidePositionDown = obj:FindLuaTableAttribute("SonRiderJoint_Down")
  if cameraShakeType == "Large" then
    cameraShakeType = "FSE_SHAKE_GENERIC_LARGE"
    rumbleType = "FFB_LARGE"
  elseif cameraShakeType == "Medium" then
    cameraShakeType = "FSE_SHAKE_GENERIC_MEDIUM"
    rumbleType = "FFB_MEDIUM"
  elseif cameraShakeType == "Small" then
    cameraShakeType = "FSE_SHAKE_GENERIC_SMALL"
    rumbleType = "FFB_SMALL"
  end
  onMoveDown = obj:GetLuaTableAttribute("OnMoveDown")
  onMoveUp = obj:GetLuaTableAttribute("OnMoveUp")
  onReachBottom = obj:GetLuaTableAttribute("OnReachBottomFloor")
  onReachTop = obj:GetLuaTableAttribute("OnReachTopFloor")
  onMoveDown = LD.ExtractCallbacksForEvent(level, obj, onMoveDown)
  onMoveUp = LD.ExtractCallbacksForEvent(level, obj, onMoveUp)
  onReachBottom = LD.ExtractCallbacksForEvent(level, obj, onReachBottom)
  onReachTop = LD.ExtractCallbacksForEvent(level, obj, onReachTop)
  checkpointOverrideTopFloor = thisObj:FindLuaTableAttribute("TopFloorCheckpointOverride")
  checkpointOverrideTopFloor = level:FindSingleGameObject(checkpointOverrideTopFloor)
  checkpointOverrideBottomFloor = thisObj:FindLuaTableAttribute("BottomFloorCheckpointOverride")
  checkpointOverrideBottomFloor = level:FindSingleGameObject(checkpointOverrideBottomFloor)
  switch_Obj = elevatorObject:FindSingleGOByName(obj:FindLuaTableAttribute("SwitchName"))
  switchTop_Obj = obj:FindLuaTableAttribute("CallSwitch_Top_Name")
  switchBottom_Obj = obj:FindLuaTableAttribute("CallSwitch_Bottom_Name")
  if switch_Obj ~= nil then
    switch_Obj.LuaObjectScript.AddPreInteractCallback(WaitForSonWhileOnScreen)
    switch_Obj.LuaObjectScript.AddSwitchOnOffCallback(WarpSon)
    switch_Obj.LuaObjectScript.AddSwitchedCompleteCallback(PlayerMoveElevator)
  end
  invisSheet = elevatorObject:FindSingleGOByName(obj:FindLuaTableAttribute("ElevatorObject_InvisSheet"))
  switch_Obj = elevatorObject:FindSingleGOByName(obj:FindLuaTableAttribute("SwitchName"))
  son_MoveToPoint = elevatorObject:FindSingleGOByName("MoveToPoint_Son")
  elevatorState = nil
  game.SubObject.Sleep(obj)
  SoundInit()
end
function OnFirstPreStart(level, obj)
  local startAnimName = obj:GetLuaTableAttribute("StartAnimName")
  if startAnimName ~= nil then
    elevatorObject:StartAnimation({Animation = startAnimName})
    if startAnimName == topFloorAnimName then
      elevatorState = "Top"
    else
      elevatorState = "Bottom"
    end
  end
end
function OnPreStart(level, obj)
  if switch_Obj then
    switch_Obj.LuaObjectScript.OverrideCameraInteractApproach("ENV_Interact_Elevator")
    switch_Obj.LuaObjectScript.SetCameraApproachPitch(8)
  end
  if switchTop_Obj ~= nil and switchTop_Obj ~= "" then
    SetTopFloorCallSwitch(elevatorObject:FindSingleGOByName(switchTop_Obj))
  else
    switchTop_Obj = nil
  end
  if switchBottom_Obj ~= nil and switchBottom_Obj ~= "" then
    SetBottomFloorCallSwitch(elevatorObject:FindSingleGOByName(switchBottom_Obj))
  else
    switchBottom_Obj = nil
  end
  if sonRidePositionUp ~= nil and sonRidePositionUp ~= "" then
    sonRidePositionUp = elevatorObject:FindSingleGOByName(sonRidePositionUp)
    sonRidePositionUp:Hide()
  else
    sonRidePositionUp = nil
  end
  if sonRidePositionDown ~= nil and sonRidePositionDown ~= "" then
    sonRidePositionDown = elevatorObject:FindSingleGOByName(sonRidePositionDown)
    sonRidePositionDown:Hide()
  else
    sonRidePositionDown = nil
  end
  if elevatorState == "Top" then
    elevatorObject:StartAnimation({Animation = topFloorAnimName})
    if invisSheet then
      invisSheet:Hide()
    end
  elseif elevatorState == "Bottom" then
    elevatorObject:StartAnimation({Animation = bottomFloorAnimName})
    if invisSheet then
      invisSheet:Hide()
    end
  elseif (elevatorState == "MovingDown" or elevatorState == "MovingUp") and invisSheet ~= nil then
    invisSheet:Show()
  end
  if enabled == true then
    EnableElevator()
  else
    DisableElevator()
  end
end
function OnStart(level, obj)
  if speedControlZone ~= nil then
    speedControlZone:HideEntityVolume()
  end
  if elevatorState == "MovingDown" and elevatorObject.AnimPrecent == 1 then
    elevatorState = "Bottom"
  elseif elevatorState == "MovingUp" and elevatorObject.AnimPrecent == 1 then
    elevatorState = "Top"
  end
end
function OnUpdate(level, obj)
  if not LocationIsOnScreen(son:GetWorldPosition()) or son:IsInsideEntityZone(elevatorObject) then
    game.SubObject.Sleep(obj)
    ExecuteOnWaitCompleteCallbacks()
  end
end
function OnSaveCheckpoint(level, obj)
  return {state = elevatorState, enabled = enabled}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  elevatorState = savedInfo.state
  enabled = savedInfo.enabled
end
function IsEnabled()
  return enabled
end
function GetState()
  return elevatorState
end
function DisableElevator(args)
  enabled = false
  if args then
    if args.animName then
      elevatorObject:StartAnim(args.animName)
    end
    if args.jumpAnim then
      elevatorObject:JumpAnimToPercent(args.jumpAnim)
      elevatorObject:PauseAnim()
    elseif args.cycleAnim then
      elevatorObject:PlayAnimCycle()
    end
  end
  if switch_Obj ~= nil then
    switch_Obj.LuaObjectScript.Enable()
    switch_Obj.LuaObjectScript.Lock()
  end
  if switchBottom_Obj then
    switchBottom_Obj.LuaObjectScript.Enable()
  end
  if switchTop_Obj then
    switchTop_Obj.LuaObjectScript.Enable()
  end
end
function EnableElevator()
  enabled = true
  if switch_Obj ~= nil then
    switch_Obj.LuaObjectScript.Enable()
    switch_Obj.LuaObjectScript.Unlock()
  end
  if switchBottom_Obj and elevatorState == "Top" then
    switchBottom_Obj.LuaObjectScript.Enable()
  end
  if switchTop_Obj and elevatorState == "Bottom" then
    switchTop_Obj.LuaObjectScript.Enable()
  end
end
function EnableCinematicTrigger()
  triggerCinematic = true
end
function DisableCinematicTrigger()
  triggerCinematic = false
end
function DisableSonWarpOnElevator()
  enableSonWarp = false
end
function AttachObject(attachedObj)
  if attachedObj.Parent ~= nil then
    attachedObj:Unparent()
  end
  local joint = elevatorObject:FindJointIndex("zeroJoint")
  elevatorObject:AddChild(attachedObj, elevatorObject:FindJointIndex("zeroJoint"))
end
function IsSwitchEnabled()
  if switch_Obj ~= nil then
    return switch_Obj.LuaObjectScript.IsSwitchEnabled()
  end
  return nil
end
function GetElevatorSwitch()
  if switch_Obj then
    return switch_Obj
  end
  return nil
end
function SetTopFloorCallSwitch(obj)
  if obj then
    switchTop_Obj = obj
    switchTop_Obj.LuaObjectScript.AddSwitchedCompleteCallback(MoveUp)
    if elevatorState == "Bottom" then
      switchTop_Obj.LuaObjectScript.Enable()
    elseif elevatorState == "Top" then
      switchTop_Obj.LuaObjectScript.Disable()
    end
  else
    switchTop_Obj = nil
  end
end
function SetBottomFloorCallSwitch(obj)
  if obj then
    switchBottom_Obj = obj
    switchBottom_Obj.LuaObjectScript.AddSwitchedCompleteCallback(MoveDown)
    if elevatorState == "Bottom" then
      switchBottom_Obj.LuaObjectScript.Disable()
    elseif elevatorState == "Top" then
      switchBottom_Obj.LuaObjectScript.Enable()
    end
  else
    switchBottom_Obj = nil
  end
end
function WaitForSonWhileOnScreen()
  if not triggerCinematic and son and enableSonWarp and not son:IsInsideEntityZone(elevatorObject) then
    waitWhileSonIsOnScreen = LocationIsOnScreen(son:GetWorldPosition())
    if waitWhileSonIsOnScreen then
      EnableGamePad(false)
      ExecuteWaitingCallbacks()
      game.SubObject.Wake(thisObj)
    end
  end
end
function WarpSon()
  waitWhileSonIsOnScreen = false
  EnableGamePad(true)
  if not triggerCinematic and son and enableSonWarp then
    local sonPos
    son_MoveToPoint.LuaObjectScript.Disable()
    if elevatorState == "Top" and sonRidePositionDown then
      sonPos = sonRidePositionDown
    elseif elevatorState == "Bottom" and sonRidePositionUp then
      sonPos = sonRidePositionUp
    end
    if sonPos then
      son_MoveToPoint:SetWorldPosition(sonPos:GetWorldPosition())
      son_MoveToPoint:SetWorldFacing(sonPos:GetWorldForward())
      if not son:IsInsideEntityZone(elevatorObject) then
        son:Warp(sonPos:GetWorldPosition(), sonPos:GetWorldForward())
      end
      son_MoveToPoint.LuaObjectScript.Enable()
    end
  end
  enableSonWarp = true
end
function GetCurrentSonRidePositionObject()
  if (elevatorState == "Top" or elevatorState == "MovingDown") and sonRidePositionDown then
    return sonRidePositionDown
  elseif (elevatorState == "Bottom" or elevatorState == "MovingUp") and sonRidePositionUp then
    return sonRidePositionUp
  else
    return nil
  end
end
function AddWaitingCallback(fn)
  if waitingOnSonCallback == nil then
    waitingOnSonCallback = {}
  end
  table.insert(waitingOnSonCallback, fn)
end
function ExecuteWaitingCallbacks()
  if waitingOnSonCallback ~= nil then
    for _, callback in ipairs(waitingOnSonCallback) do
      callback(elevatorState)
    end
  end
end
function AddOnWaitCompleteCallback(fn)
  if onWaitComplete == nil then
    onWaitComplete = {}
  end
  table.insert(onWaitComplete, fn)
end
function ExecuteOnWaitCompleteCallbacks()
  if onWaitComplete ~= nil then
    for _, callback in ipairs(onWaitComplete) do
      callback(elevatorState)
    end
  end
end
function MoveUp(overrideAnimName, overrideSpeed)
  if enabled == false or elevatorState == "Top" or elevatorState == "MovingUp" then
    return
  end
  elevatorState = "MovingUp"
  local animName = overrideAnimName or moveUpAnimName
  elevatorObject:OnAnimationDone(thisObj, "ResetElevator", {Animation = animName, Force = true})
  elevatorObject:PlayAnimationToPercent(hideExitSheetPercent.top, {
    Animation = animName,
    Rate = overrideSpeed or moveUpSpeed
  })
  if triggerCinematic == false then
    LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onMoveUp, log .. elevatorState)
  end
  triggerCinematic = false
  PlayOnMoveUpSound()
end
function MoveDown(overrideAnimName, overrideSpeed)
  if enabled == false or elevatorState == "Bottom" or elevatorState == "MovingDown" then
    return
  end
  local animName = overrideAnimName or moveDownAnimName
  elevatorState = "MovingDown"
  elevatorObject:PlayAnimationToPercent(hideExitSheetPercent.bottom, {
    Animation = animName,
    Rate = overrideSpeed or moveDownSpeed
  })
  elevatorObject:OnAnimationDone(thisObj, "ResetElevator", {Animation = animName, Force = true})
  if triggerCinematic == false then
    LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onMoveDown, log .. elevatorState)
  end
  triggerCinematic = false
  PlayOnMoveDownSound()
end
function PlayerMoveElevator(overrideAnimName, overrideSpeed)
  if elevatorState == "MovingUp" or elevatorState == "MovingDown" then
    return
  end
  if switch_Obj then
    switch_Obj.LuaObjectScript.Disable()
  else
    PlayCameraShakeEffect()
  end
  if invisSheet ~= nil then
    invisSheet:Show()
  end
  playerActivatedElevator = true
  if speedControlZone ~= nil then
    speedControlZone:ShowEntityVolume()
  end
  if elevatorState == "Top" then
    MoveDown(overrideAnimName, overrideSpeed)
  elseif elevatorState == "Bottom" then
    MoveUp(overrideAnimName, overrideSpeed)
  end
end
function JumpToTopFloor()
  if enabled == false or elevatorState == "Top" or elevatorState == "MovingUp" then
    return
  end
  if triggerCinematic == false then
    elevatorState = "Top"
    elevatorObject:JumpAnimationToPercent(1, {Animation = topFloorAnimName})
    LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onMoveUp, log .. elevatorState)
  else
    elevatorState = "Top"
  end
  LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onReachTop, log .. "OnReachedTop Callback")
end
function JumpToBottomFloor()
  if enabled == false or elevatorState == "Bottom" or elevatorState == "MovingDown" then
    return
  end
  if triggerCinematic == false then
    elevatorState = "Bottom"
    elevatorObject:JumpAnimationToPercent(1, {Animation = bottomFloorAnimName})
    LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onMoveDown, log .. elevatorState)
  else
    elevatorState = "Bottom"
  end
  LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onReachBottom, log .. "OnReachedBottom Callback")
end
function ResetElevator()
  if elevatorState == "Top" or elevatorState == "Bottom" or triggerCinematic then
    return
  end
  son_MoveToPoint.LuaObjectScript.Disable()
  if switch_Obj and playerActivatedElevator == true then
    switch_Obj.LuaObjectScript.Enable()
  end
  playerActivatedElevator = false
  if invisSheet then
    invisSheet:Hide()
  end
  if speedControlZone ~= nil then
    speedControlZone:HideEntityVolume()
  end
  if elevatorState == "MovingDown" then
    elevatorState = "Bottom"
    if triggerCinematic == false then
      LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onReachBottom, log .. "OnReachedBottom Callback")
    end
    PlayCameraShakeEffect()
    PlayBottomSound()
  elseif elevatorState == "MovingUp" then
    elevatorState = "Top"
    if triggerCinematic == false then
      LD.ExecuteCallbacksForEvent(thisLevel, elevatorObject, onReachTop, log .. "OnReachedTop Callback")
    end
    PlayCameraShakeEffect()
    PlayTopSound()
  end
  if checkpointOnFloor == true then
    if elevatorState == "Top" then
      LD.StoreCheckpointOverride(checkpointOverrideTopFloor)
    elseif elevatorState == "Bottom" then
      LD.StoreCheckpointOverride(checkpointOverrideBottomFloor)
    end
  end
end
function PlayCameraShakeEffect()
  game.Blender.Trigger({Name = cameraShakeType})
  game.Blender.Trigger({Name = rumbleType})
end
function LocationIsOnScreen(loc, useCollisionCheck)
  local yOffset = engine.Vector.New(0, 0.5, 0)
  if not useCollisionCheck then
    return game.World.IsSphereOnScreen(loc + yOffset, 0.25)
  else
    local collisionCheck = require("camera.collisioncheck")
    local check = collisionCheck.isTargetInFrustumUnobstructed(loc + yOffset, 1, 1, 0, false)
    if check then
      return true
    else
      return false
    end
  end
end
function EnableGamePad(value)
  local buttons = {
    "kPadRightStickX",
    "kPadRightStickY"
  }
  for i = 1, #buttons do
    if value then
      player.Pad:EnableGameButton(tweaks.ePad[buttons[i]])
    else
      player.Pad:DisableGameButton(tweaks.ePad[buttons[i]])
    end
  end
end
local animForwardOnElevatorUp
local soundEvents = {
  soundEmitter = nil,
  soundEmitterLeft = nil,
  soundEmitterRight = nil,
  soundEmitterName = nil,
  soundOnStart = "",
  soundAnimFrameOnStart = 0,
  soundOnReturnToStart = "",
  soundAnimFrameOnReturnToStart = 0,
  soundOnForward = "",
  soundAnimFrameOnForward = 0,
  soundOnBackward = "",
  soundAnimFrameOnBackward = 0,
  soundOnStartFromEnd = "",
  soundAnimFrameOnStartFromEnd = 0,
  soundOnEnd = "",
  soundAnimFrameOnEnd = 0,
  soundAnimFrameTopFloor = -1,
  soundAnimFrameBottomFloor = -1
}
function SoundInit()
  animForwardOnElevatorUp = false
  soundEvents.soundEmitterName = thisObj:FindLuaTableAttribute("soundEmitterName")
  if soundEvents.soundEmitterName ~= nil and soundEvents.soundEmitterName ~= "" then
    soundEvents.soundEmitter = thisObj.Parent.Parent:FindSingleSoundEmitterByName(soundEvents.soundEmitterName)
    soundEvents.soundEmitterLeft = thisObj.Parent.Parent:FindSingleSoundEmitterByName(soundEvents.soundEmitterName .. "_Left")
    soundEvents.soundEmitterRight = thisObj.Parent.Parent:FindSingleSoundEmitterByName(soundEvents.soundEmitterName .. "_Right")
  end
  soundEvents.soundOnStart = thisObj:FindLuaTableAttribute("soundOnStart") or ""
  soundEvents.soundAnimFrameOnStart = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnStart"))
  soundEvents.soundOnReturnToStart = thisObj:FindLuaTableAttribute("soundOnReturnToStart") or ""
  soundEvents.soundAnimFrameOnReturnToStart = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnReturnToStart"))
  soundEvents.soundOnForward = thisObj:FindLuaTableAttribute("soundOnForward") or ""
  soundEvents.soundAnimFrameOnForward = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnForward"))
  soundEvents.soundOnBackward = thisObj:FindLuaTableAttribute("soundOnBackward") or ""
  soundEvents.soundAnimFrameOnBackward = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnBackward"))
  soundEvents.soundOnStartFromEnd = thisObj:FindLuaTableAttribute("soundOnStartFromEnd") or ""
  soundEvents.soundAnimFrameOnStartFromEnd = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnStartFromEnd"))
  soundEvents.soundOnEnd = thisObj:FindLuaTableAttribute("soundOnEnd") or ""
  soundEvents.soundAnimFrameOnEnd = select(2, LD.FindAnimFrameOrPercentInfoFromLuaTableAttr(thisObj, "soundAnimFrameOnEnd"))
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      soundEvents.SoundEmitter = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitter)
    end
    if sounds.SoundEmitterLeft ~= nil then
      soundEvents.SoundEmitterLeft = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitterLeft)
    end
    if sounds.SoundEmitterRight ~= nil then
      soundEvents.SoundEmitterRight = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitterRight)
    end
  end
  for key, value in pairs(soundEvents) do
    LD.SoundDebug("BEFORE: " .. tostring(key) .. ": " .. tostring(soundEvents[key]))
    for newKey, newValue in pairs(sounds) do
      if newKey == key and newValue ~= nil and newValue ~= value then
        soundEvents[key] = newValue
      end
    end
    LD.SoundDebug(tostring(key) .. ": " .. tostring(soundEvents[key]))
  end
end
function PlayOnMoveUpSound()
  print("PlayOnMoveUpSound")
  local direction
  if animForwardOnElevatorUp then
    PlayForwardSound()
    direction = "forward"
  else
    PlayBackwardSound()
    direction = "backward"
  end
  PlayEndSound(direction)
  if soundEvents.soundAnimFrameTopFloor > -1 then
    StopLoopingSoundOnFrame(soundEvents.soundAnimFrameTopFloor, direction)
    print("Using custom top floor frame for sound.")
  end
end
function PlayOnMoveDownSound()
  print("PlayOnMoveDownSound")
  local direction
  if animForwardOnElevatorUp then
    PlayBackwardSound()
    direction = "backward"
  else
    PlayForwardSound()
    direction = "forward"
  end
  PlayEndSound(direction)
  if soundEvents.soundAnimFrameBottomFloor > -1 then
    StopLoopingSoundOnFrame(soundEvents.soundAnimFrameBottomFloor, direction)
    print("Using custom bottom floor frame for sound.")
  end
end
function PlayStartSound()
  LD.PlaySoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnStart, soundEvents.soundAnimFrameOnStart, "forward")
end
function PlayTopSound()
  if soundEvents.soundAnimFrameBottomFloor == -1 then
    LD.StopSound(soundEvents.soundEmitter, soundEvents.soundOnBackward)
  end
end
function PlayForwardSound()
  print("PlayForwardSound")
  PlayStartSound()
  LD.PlaySoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnForward, soundEvents.soundAnimFrameOnForward, "forward")
end
function PlayBackwardSound()
  print("PlayBackwardSound")
  PlayStartFromEndSound()
  LD.PlaySoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnBackward, soundEvents.soundAnimFrameOnBackward, "forward")
end
function PlayBottomSound()
  if soundEvents.soundAnimFrameBottomFloor == -1 then
    LD.StopSound(soundEvents.soundEmitter, soundEvents.soundOnForward)
  end
end
function PlayEndSound(direction)
  local slamEmitter
  if direction == "forward" then
    slamEmitter = soundEvents.soundEmitterRight
  elseif direction == "backward" then
    slamEmitter = soundEvents.soundEmitterLeft
  end
  if animForwardOnElevatorUp then
    LD.PlaySoundOnFrame(slamEmitter, elevatorObject, soundEvents.soundOnEnd, soundEvents.soundAnimFrameOnEnd, "forward")
  else
    LD.PlaySoundOnFrame(slamEmitter, elevatorObject, soundEvents.soundOnReturnToStart, soundEvents.soundAnimFrameOnReturnToStart, "forward")
  end
end
function PlayStartFromEndSound()
  LD.PlaySoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnStartFromEnd, soundEvents.soundAnimFrameOnStartFromEnd, "forward")
end
function StopLoopingSoundOnFrame(frame, direction)
  print("StopAllLoopingSoundsOnFrame : " .. direction .. frame)
  if string.lower(direction) == "backward" then
    LD.StopSoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnBackward, frame)
  elseif string.lower(direction) == "forward" then
    LD.StopSoundOnFrame(soundEvents.soundEmitter, elevatorObject, soundEvents.soundOnForward, frame)
  end
end
function ShowDebugTable(X, Y, Title)
  local debugTable = {}
  debugTable.Title = Title or "Non-Basic Elevator Info"
  debugTable.X = X or 120
  debugTable.Y = Y or 10
  debugTable.TitleColor = engine.Vector.New(255, 0, 128)
  debugTable[#debugTable + 1] = {
    "elevatorObject: ",
    elevatorObject
  }
  debugTable[#debugTable + 1] = {
    "Anim Name: ",
    elevatorObject:GetAnimationName()
  }
  debugTable[#debugTable + 1] = {
    "AnimFrame: ",
    elevatorObject.AnimFrame
  }
  debugTable[#debugTable + 1] = {
    "AnimPercent: ",
    tostring(elevatorObject.AnimPercent * 100) .. "%"
  }
  debugTable[#debugTable + 1] = {"State: ", elevatorState}
  debugTable[#debugTable + 1] = {"enabled: ", enabled}
  debugTable[#debugTable + 1] = {
    "Is Son On Elevator: ",
    tostring(son:IsInsideEntityZone(elevatorObject))
  }
  debugTable[#debugTable + 1] = {
    "waitWhileSonIsOnScreen: ",
    tostring(waitWhileSonIsOnScreen)
  }
  debugTable[#debugTable + 1] = {
    "IsSonOnScreen: ",
    tostring(LocationIsOnScreen(son:GetWorldPosition()))
  }
  engine.DrawDebugTable(debugTable)
end
function ShowDebugText(distance)
  distance = distance or 100
  if distance >= game.Player.FindPlayer():GetWorldPosition():Distance(elevatorObject:GetWorldPosition()) then
    local debugText = "elevatorObject: " .. ", " .. elevatorObject.Parent:GetName()
    debugText = debugText .. "\n" .. "Anim Name: , " .. elevatorObject:GetAnimationName()
    debugText = debugText .. "\n" .. "AnimFrame: , " .. tostring(elevatorObject.AnimFrame)
    debugText = debugText .. "\n" .. "AnimPercent: , " .. tostring(elevatorObject.AnimPercent * 100) .. "%"
    debugText = debugText .. "\n" .. "State: , " .. (tostring(elevatorState) or "nil")
    debugText = debugText .. "\n" .. "isEnabled: , " .. tostring(enabled)
    debugText = debugText .. "\n" .. "sonRidePositionUp: " .. tostring(sonRidePositionUp or "nil")
    debugText = debugText .. "\n" .. "sonRidePositionDown: " .. tostring(sonRidePositionDown or "nil")
    debugText = debugText .. "\n" .. "switch_GO: " .. ", " .. tostring(switch_Obj)
    local position = thisObj:GetWorldPosition()
    if switch_Obj then
      position = switch_Obj:GetWorldPosition()
    end
    local color = require("core.color")
    engine.DrawTextInWorld(position, debugText, color.white)
  end
end
