local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local monitors = require("level.MonitorLibrary")
local uiCalls = require("ui.uicalls")
local mpicon = require("ui.mpicon")
local ND = require("design.NarrativeDesignLibrary")
local actor = require("narrative.actor")
local CSL = require("camera.shakelibrary")
local thisObj, thisLevel, son, flip, trapFloor
local bPuzzleStarted = false
local bPuzzleGear3LowerIn = true
local bPuzzleGear1Up = true
local bPuzzleGear2Up = true
local bPuzzleGear3Up = true
local bPuzzleSolved = false
local bFloorUp = false
local interactRequested = false
local sliderMonitor, sonActor, sonPuppeteer, selectedLever, gearInputMonitor, gearAnimMonitor
local levers = {
  {enabled = true},
  {enabled = true},
  {enabled = true},
  {enabled = true}
}
local acrossSpeed = 3
local switchDirection = "L"
local currentSwitch = "JointA"
local leversEnabled = false
local gearSlotFrames = {
  0.025,
  0.27,
  0.5,
  0.75,
  1
}
local gearSlotsUp = {}
local gearSlotsDown = {}
local gearSun = {}
local gearMoon = {}
local gearEarth = {}
local gearList = {
  gearMoon,
  gearEarth,
  gearSun
}
local gearAnimWatcher = {animObj = nil, direction = ""}
local lever1, lever2, lever3, sideGear
local bCancelled = false
local pullAnimActive = false
local bVictoryImminent = false
local numberOfPulls = 0
local approachSeq
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  son = game.AI.FindSon()
  flip = level:GetGameObject("FlipSwitch3")
  trapFloor = thisLevel:GetGameObject("outerTrapFloor")
  lever1 = GameObjects.upSlider1
  lever2 = GameObjects.sideSlider
  lever3 = GameObjects.upSlider2
  sideGear = GameObjects.sideGear
  gearSun.Name = "Sun"
  gearSun.AnimOut = thisLevel:GetGameObject("puzzleGear1Out")
  gearSun.AnimUp = thisLevel:GetGameObject("puzzleGear1Up")
  gearSun.AnimAcross = thisLevel:GetGameObject("puzzleGear1Across")
  gearSun.AnimSpin = thisLevel:GetGameObject("puzzleGear1Spin")
  gearSun.AnimNoise = thisLevel:GetGameObject("puzzleGear1Noise")
  gearSun.AnimStop = thisLevel:GetGameObject("puzzleGear1Stop")
  gearSun.SoundEmitter = gearSun.AnimStop.SoundEmitters[1]
  gearEarth.Name = "Earth"
  gearEarth.AnimOut = thisLevel:GetGameObject("puzzleGear2Out")
  gearEarth.AnimUp = thisLevel:GetGameObject("puzzleGear2Up")
  gearEarth.AnimAcross = thisLevel:GetGameObject("puzzleGear2Across")
  gearEarth.AnimSpin = thisLevel:GetGameObject("puzzleGear2Spin")
  gearEarth.AnimNoise = thisLevel:GetGameObject("puzzleGear2Noise")
  gearEarth.AnimStop = thisLevel:GetGameObject("puzzleGear2Stop")
  gearEarth.SoundEmitter = gearEarth.AnimStop.SoundEmitters[1]
  gearMoon.Name = "Moon"
  gearMoon.AnimOut = thisLevel:GetGameObject("puzzleGear3Out")
  gearMoon.AnimUp = thisLevel:GetGameObject("puzzleGear3Up")
  gearMoon.AnimAcross = thisLevel:GetGameObject("puzzleGear3Across")
  gearMoon.AnimSpin = thisLevel:GetGameObject("puzzleGear3Spin")
  gearMoon.AnimNoise = thisLevel:GetGameObject("puzzleGear3Noise")
  gearMoon.AnimStop = thisLevel:GetGameObject("puzzleGear3Stop")
  gearMoon.SoundEmitter = gearMoon.AnimStop.SoundEmitters[1]
  gearSlotsUp[2] = gearSun
  gearSlotsUp[4] = gearEarth
  gearSlotsUp[3] = gearMoon
  levers[1].icon = thisObj:FindSingleGOByName("LeverIcon_A")
  levers[2].icon = thisObj:FindSingleGOByName("LeverIcon_B")
  levers[3].icon = thisObj:FindSingleGOByName("LeverIcon_C")
  levers[4].icon = thisObj:FindSingleGOByName("LeverIcon_RuneRead")
  levers[1].Activate = GoToA
  levers[2].Activate = GoToB
  levers[3].Activate = GoToC
  levers[4].Activate = SonReadClue
  SoundInit()
end
function OnStart(level, obj)
  if not bPuzzleSolved then
    InitializeGears()
  end
  trapFloor:PauseAnim()
  if bFloorUp then
    trapFloor:JumpAnimToPercent(1)
    flip.LuaObjectScript.SnapToUp()
  end
  if bPuzzleStarted and not bPuzzleSolved then
    StartTrap()
    GameObjects.FlipSwitch3.LuaObjectScript.PlayBGGearsSound()
  end
  sideGear:JumpAnimToPercent(1)
end
function OnUpdate(level, obj)
  UpdateLeverIcons()
end
function OnSaveCheckpoint(level, obj)
  return {
    puzzleSolved = bPuzzleSolved,
    floorUp = bFloorUp,
    bPuzzleStarted = bPuzzleStarted,
    bCancelled = bCancelled,
    bVictoryImminent = bVictoryImminent
  }
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  bPuzzleSolved = savedInfo.puzzleSolved
  bFloorUp = savedInfo.floorUp
  bPuzzleStarted = savedInfo.bPuzzleStarted
  bCancelled = savedInfo.bCancelled
  bVictoryImminent = savedInfo.bVictoryImminent
end
function InitializeGears()
  gearSun.AnimUp:JumpAnimToPercent(1)
  gearSun.AnimAcross:JumpAnimToPercent(gearSlotFrames[2])
  gearSun.AnimAcross:PauseAnim()
  gearEarth.AnimUp:JumpAnimToPercent(1)
  gearEarth.AnimAcross:JumpAnimToPercent(gearSlotFrames[4])
  gearEarth.AnimAcross:PauseAnim()
  gearMoon.AnimUp:JumpAnimToPercent(1)
  gearMoon.AnimAcross:JumpAnimToPercent(gearSlotFrames[3])
  gearMoon.AnimAcross:PauseAnim()
  lever2:JumpAnimToPercent(1)
  lever2:PauseAnim()
end
function StartTrap()
  if bPuzzleStarted then
    RequestInteract()
  else
    sonActor = actor.Actor.New("Gear Puzzle: Son Actor", game.AI.FindSon)
    local approachSeq = ND.CreateCineSequence(thisLevel, thisObj, "Gear Puzzle")
    approachSeq:ActorForceApproachAndWait(sonActor, {
      Branch = "BRA_Runestone_GearIdle",
      ReferenceJoint = "jointSonStart",
      speed = ND.SonRunSpeed,
      stop = true,
      prevent_path_eval = true,
      radius = ND.StopTurnRadius
    })
    approachSeq:WaitForActorPuppeteerComplete(sonActor)
    approachSeq:StopPuppetingActor(sonActor)
    approachSeq:Do(RequestInteract)
    approachSeq:StartSequence()
  end
end
function AlignToPuzzle()
end
function RequestInteract()
  if not interactRequested then
    timers.StartLevelTimer(0.5, function()
      interactRequested = true
      print("REQUESTING INTERACT WATERTRAP")
      son:RequestInteract(thisObj)
      gearInputMonitor = monitors.CreateSquareButtonMonitor()
      gearInputMonitor:OnButtonDown(GearPuzzleInput)
    end)
  end
end
function OnInteractStart(level, obj, creature)
  StartPuzzleSequence()
end
function OnInteractReject(level, obj, creature)
  print("Level: ", level, " Obj: ", obj, " Creature: ", creature)
  engine.Warning("This may not be a bug. If progression is not blocked, please do not right a bug for this.")
end
function StartPuzzleSequence()
  if not bPuzzleStarted then
    bPuzzleStarted = true
    game.World.StoreCheckpoint()
  end
  sonPuppeteer = game.Puppeteer.NewForce(thisObj, "Gear Puzzle", son)
  sonPuppeteer:Sync("BRA_Runestone_GearIdle", true, {}, "jointSonStart")
  timers.StartLevelTimer(0.5, function()
    print("TURNING ON INTERACTS")
    mpicon.level.Create(levers[1].icon, "WORLD_INTERACT_HINT")
    mpicon.level.Create(levers[2].icon, "WORLD_INTERACT_HINT")
    mpicon.level.Create(levers[3].icon, "WORLD_INTERACT_HINT")
    EnableAllPuzzleInteracts()
  end)
  GameObjects.waterTrap_control.LuaObjectScript.Drop2()
end
function UpdateLeverIcons()
  if leversEnabled == false then
    return
  end
  local bestLever, bestPen
  local currentPen = 0
  local yaw = game.Camera.GetOrbitRotation()
  if yaw < 84 then
    bestLever = levers[4]
  elseif 85 < yaw and yaw < 91 then
    if levers[3].enabled then
      bestLever = levers[3]
    end
  elseif 92 < yaw and yaw < 103 then
    if levers[2].enabled then
      bestLever = levers[2]
    end
  elseif 100 < yaw and yaw < 111 and levers[1].enabled then
    bestLever = levers[1]
  end
  if bestLever ~= selectedLever then
    if selectedLever ~= nil then
      mpicon.level.Create(selectedLever.icon, "WORLD_INTERACT_HINT")
      mpicon.level.Off(selectedLever.icon, "WORLD_INTERACT_SON")
    end
    if bestLever ~= nil then
      mpicon.level.Create(bestLever.icon, "WORLD_INTERACT_SON")
      mpicon.level.Off(bestLever.icon, "WORLD_INTERACT_HINT")
    end
    selectedLever = bestLever
  end
end
function GearPuzzleInput()
  if selectedLever == nil or selectedLever.enabled == false or leversEnabled == false then
    return
  end
  numberOfPulls = numberOfPulls + 1
  timers.StartLevelTimer(3, RandomBanter)
  pullAnimActive = true
  selectedLever.Activate()
  DisableAllPuzzleInteracts()
  CheckForImminentVictory()
end
function RandomBanter()
  if numberOfPulls == 3 then
    game.Audio.PlayBanter("240_WolfTrap_Reminder1")
  elseif numberOfPulls == 5 then
    game.Audio.PlayBanter("240_WolfTrap_Reminder2")
  elseif numberOfPulls == 8 then
    game.Audio.PlayBanter("240_WolfTrap_Reminder3")
  end
end
function CheckForImminentVictory()
  local sunUpSlot, moonUpSlot, earthUpSlot
  for i = 1, 5 do
    if gearSlotsUp[i] == gearEarth then
      earthUpSlot = i
    elseif gearSlotsUp[i] == gearMoon then
      moonUpSlot = i
    elseif gearSlotsUp[i] == gearSun then
      sunUpSlot = i
    end
  end
  if currentSwitch == "JointA" then
    if gearSlotsDown[2] == gearMoon and sunUpSlot ~= nil and earthUpSlot ~= nil then
      if earthUpSlot < sunUpSlot and 2 < earthUpSlot then
        bVictoryImminent = true
      end
    elseif gearSlotsDown[2] == gearEarth and sunUpSlot ~= nil and moonUpSlot ~= nil and moonUpSlot < sunUpSlot and moonUpSlot < 2 then
      bVictoryImminent = true
    end
  elseif currentSwitch == "JointC" then
    if gearSlotsDown[4] == gearSun and earthUpSlot ~= nil and moonUpSlot ~= nil then
      if earthUpSlot > moonUpSlot and earthUpSlot < 4 then
        bVictoryImminent = true
      end
    elseif gearSlotsDown[4] == gearEarth and sunUpSlot ~= nil and moonUpSlot ~= nil and moonUpSlot < sunUpSlot and 4 < sunUpSlot then
      bVictoryImminent = true
    end
  end
  if bVictoryImminent then
    GameObjects.waterTrap_control.LuaObjectScript.PauseDescent()
  end
end
function GoToA()
  GameObjects.Btr_WaterTrap.LuaObjectScript.PlayLeft()
  if currentSwitch == "JointB" then
    son:TriggerMoveEvent("LE_GEAR_BA")
  elseif currentSwitch == "JointC" then
    son:TriggerMoveEvent("LE_GEAR_CA")
  else
    son:TriggerMoveEvent("LE_GEAR_PULL_VERTICAL")
  end
  currentSwitch = "JointA"
end
function GoToB()
  GameObjects.Btr_WaterTrap.LuaObjectScript.PlayMiddle()
  if currentSwitch == "JointA" then
    son:TriggerMoveEvent("LE_GEAR_AB")
  elseif currentSwitch == "JointC" then
    son:TriggerMoveEvent("LE_GEAR_CB")
  else
    son:TriggerMoveEvent("LE_GEAR_PULL_VERTICAL")
  end
  currentSwitch = "JointB"
end
function GoToC()
  GameObjects.Btr_WaterTrap.LuaObjectScript.PlayRight()
  if currentSwitch == "JointA" then
    son:TriggerMoveEvent("LE_GEAR_AC")
  elseif currentSwitch == "JointB" then
    son:TriggerMoveEvent("LE_GEAR_BC")
  else
    son:TriggerMoveEvent("LE_GEAR_PULL_VERTICAL")
  end
  currentSwitch = "JointC"
end
function LuaHook_PullLever()
  if currentSwitch == "JointA" then
    timers.StartLevelTimer(1, function()
      ToggleLift(2)
    end)
    PlayLeverSound(1)
    lever1:JumpAnimToPercent(0)
    lever1:PlayAnimToPercent(1)
  elseif currentSwitch == "JointB" then
    PlayLeverSound(2)
    lever2:JumpAnimToPercent(0)
    lever2:PlayAnimToPercent(1)
    if switchDirection == "L" then
      timers.StartLevelTimer(1, PushLeft)
      switchDirection = "R"
    else
      timers.StartLevelTimer(1, PushRight)
      switchDirection = "L"
    end
  elseif currentSwitch == "JointC" then
    timers.StartLevelTimer(1, function()
      ToggleLift(4)
    end)
    PlayLeverSound(3)
    lever3:JumpAnimToPercent(0)
    lever3:PlayAnimToPercent(1)
  end
end
function PushRight()
  local nextSlot = #gearSlotFrames
  local gap = -1
  local watchGear
  for i = #gearSlotFrames, 1, -1 do
    if gearSlotsUp[i] ~= nil then
      gearSlotsUp[i].AnimAcross:PlayAnimToPercent(gearSlotFrames[nextSlot], acrossSpeed)
      gearSlotsUp[i].AnimNoise:JumpAnimToFrame(0)
      gearSlotsUp[i].AnimNoise:PlayAnimToEnd()
      PlaySoundGearMoveRight(gearSlotsUp[i], gearSlotFrames[nextSlot])
      if gap < nextSlot - i then
        gap = nextSlot - i
        watchGear = gearSlotsUp[i]
      end
      gearSlotsUp[nextSlot] = gearSlotsUp[i]
      gearSlotsUp[i] = nil
      nextSlot = nextSlot - 1
    end
  end
  sideGear:PlayAnimToPercent(1)
  WatchGearAnim(watchGear.AnimAcross)
  levers[1].enabled = gearSlotsUp[2] ~= nil and gearSlotsDown[2] == nil or gearSlotsUp[2] == nil and gearSlotsDown[2] ~= nil
  levers[3].enabled = gearSlotsUp[4] ~= nil and gearSlotsDown[4] == nil or gearSlotsUp[4] == nil and gearSlotsDown[4] ~= nil
  selectedLever = nil
  CSL.PlayEffect("FFB_GENERIC_RUMBLE_LOW", 1.4, 0, 0.3)
end
function PushLeft()
  local nextSlot = 1
  local gap = -1
  local watchGear
  for i = 1, #gearSlotFrames do
    if gearSlotsUp[i] ~= nil then
      gearSlotsUp[i].AnimAcross:PlayAnimToPercent(gearSlotFrames[nextSlot], -acrossSpeed)
      gearSlotsUp[i].AnimNoise:JumpAnimToFrame(0)
      gearSlotsUp[i].AnimNoise:PlayAnimToEnd()
      PlaySoundGearMoveLeft(gearSlotsUp[i], gearSlotFrames[nextSlot])
      if gap < i - nextSlot then
        gap = i - nextSlot
        watchGear = gearSlotsUp[i]
      end
      gearSlotsUp[nextSlot] = gearSlotsUp[i]
      gearSlotsUp[i] = nil
      nextSlot = nextSlot + 1
    end
  end
  sideGear:PlayAnimToPercent(0, -1)
  WatchGearAnim(watchGear.AnimAcross)
  levers[1].enabled = gearSlotsUp[2] ~= nil and gearSlotsDown[2] == nil or gearSlotsUp[2] == nil and gearSlotsDown[2] ~= nil
  levers[3].enabled = gearSlotsUp[4] ~= nil and gearSlotsDown[4] == nil or gearSlotsUp[4] == nil and gearSlotsDown[4] ~= nil
  selectedLever = nil
  CSL.PlayEffect("FFB_GENERIC_RUMBLE_LOW", 1.4, 0, 0.3)
end
function ToggleLift(slot)
  if gearSlotsUp[slot] ~= nil and gearSlotsDown[slot] ~= nil then
    return
  elseif gearSlotsUp[slot] ~= nil then
    gearSlotsUp[slot].AnimUp:PlayAnimToEnd(-1)
    gearSlotsUp[slot].AnimNoise:JumpAnimToFrame(0)
    gearSlotsUp[slot].AnimNoise:PlayAnimToEnd()
    PlaySoundGearMoveDown(gearSlotsUp[slot])
    WatchGearAnim(gearSlotsUp[slot].AnimUp)
    gearSlotsDown[slot] = gearSlotsUp[slot]
    gearSlotsUp[slot] = nil
    CSL.PlayEffect("FFB_GENERIC_RUMBLE_LOW", 0.8, 0, 0.3)
  else
    gearSlotsDown[slot].AnimUp:PlayAnimToEnd(1)
    gearSlotsDown[slot].AnimNoise:JumpAnimToFrame(0)
    gearSlotsDown[slot].AnimNoise:PlayAnimToEnd()
    PlaySoundGearMoveUp(gearSlotsDown[slot])
    WatchGearAnim(gearSlotsDown[slot].AnimUp, "Up")
    gearSlotsUp[slot] = gearSlotsDown[slot]
    gearSlotsDown[slot] = nil
    CSL.PlayEffect("FFB_GENERIC_RUMBLE_LOW", 0.8, 0, 0.3)
  end
end
function WatchGearAnim(obj, dir)
  if gearAnimWatcher.animObj ~= nil then
    return
  end
  gearAnimWatcher.animObj = obj
  gearAnimWatcher.direction = dir
  gearAnimWatcher.animObj:OnAnimDone(thisObj, "GearWatcherDone")
end
function LuaHook_PullDone()
  pullAnimActive = false
  if gearAnimWatcher.animObj == nil then
    EvaluatePuzzle()
  end
end
function GearWatcherDone()
  gearAnimWatcher.animObj:ClearAllAnimCallbacks()
  gearAnimWatcher.animObj = nil
  if pullAnimActive == false then
    EvaluatePuzzle()
  end
  if bVictoryImminent then
    PlaySoundOnPuzzleSolved()
  end
  CSL.PlayShake("FSE_SHAKE_GENERIC_VERYSMALL", 0.35, 0.2, 0.3)
  CSL.PlayShake("FFB_GENERIC_HIT_SMALL", 0.35, 0, 0.3)
end
function EvaluatePuzzle()
  if gearAnimWatcher.direction ~= nil and gearAnimWatcher.direction == "Up" and bVictoryImminent then
    PlayVictory()
  else
    EnableAllPuzzleInteracts()
  end
end
function EnableAllPuzzleInteracts()
  leversEnabled = true
  for _, lever in ipairs(levers) do
    if lever.enabled == true then
      mpicon.level.Create(lever.icon, "WORLD_INTERACT_HINT")
    end
  end
end
function DisableAllPuzzleInteracts()
  selectedLever = nil
  leversEnabled = false
  for _, lever in ipairs(levers) do
    mpicon.level.Off(lever.icon, "WORLD_INTERACT_HINT")
    mpicon.level.Off(lever.icon, "WORLD_INTERACT_SON")
  end
end
function SonReadClue()
  uiCalls.SendBottomDesignerMessage({Text = 43033, DisplayTime = 3})
  timers.StartLevelTimer(3, EnableAllPuzzleInteracts)
end
function LuaHook_CheckForImminentVictory()
  if currentSwitch == "JointA" and bVictoryImminent then
    son:TriggerMoveEvent("LE_VictoryA")
  elseif currentSwitch == "JointC" and bVictoryImminent then
    son:TriggerMoveEvent("LE_VictoryC")
  else
    son:TriggerMoveEvent("LE_OrdinaryPull")
  end
end
function PlayVictory()
  if not bCancelled then
    game.Audio.StartMusic("SND_MX_CAL_vault_puzzle_complete")
    GameObjects.waterTrap_control.LuaObjectScript.CameraGearPuzzleSuccess()
    GameObjects.Btr_WaterTrap.LuaObjectScript.PlayWolfTrap1_Succeed()
    GameObjects.waterSplashes:Hide()
    gearList[1].AnimOut:PlayAnimToEnd(6)
    gearList[2].AnimOut:PlayAnimToEnd(6)
    gearList[3].AnimOut:PlayAnimToEnd(6)
    CSL.PlayEffect("FFB_GENERIC_RUMBLE_MEDIUM", 3, 0.05, 0.05)
    timers.StartLevelTimer(3.1, function()
      CSL.PlayShake("FSE_SHAKE_GENERIC_SMALL", 0.35, 0.2, 0.3)
      CSL.PlayShake("FFB_SMALLER", 0.35, 0, 0.5)
    end)
    SpreadSymbols()
    bPuzzleStarted = false
    bPuzzleSolved = true
    DisableAllPuzzleInteracts()
  end
end
function ClearPuppet()
  son:AbortInteract()
  son:TriggerMoveEvent("LE_GEAR_EXIT")
  if sonPuppeteer ~= nil then
    sonPuppeteer:Clear()
    sonPuppeteer:ReturnToNav()
    sonPuppeteer = nil
  end
end
function SpreadSymbols()
  if gearList[1].AnimAcross.AnimPercent > gearSlotFrames[1] then
    gearList[1].AnimAcross:PlayAnimToPercent(gearSlotFrames[1], -1)
    PlaySoundGearMoveLeft(gearList[1], gearSlotFrames[1])
  end
  if gearList[2].AnimAcross.AnimPercent > gearSlotFrames[3] then
    gearList[2].AnimAcross:PlayAnimToPercent(gearSlotFrames[3], -1)
    PlaySoundGearMoveLeft(gearList[2], gearSlotFrames[3])
  else
    gearList[2].AnimAcross:PlayAnimToPercent(gearSlotFrames[3], 1)
    PlaySoundGearMoveRight(gearList[2], gearSlotFrames[3])
  end
  if gearList[3].AnimAcross.AnimPercent < gearSlotFrames[#gearSlotFrames] then
    gearList[3].AnimAcross:PlayAnimToPercent(gearSlotFrames[#gearSlotFrames])
    PlaySoundGearMoveRight(gearList[3], gearSlotFrames[#gearSlotFrames])
  end
  timers.StartLevelTimer(2, SpringSecondaryTrap)
end
function SpringSecondaryTrap()
  GameObjects.spikeTrap_control.LuaObjectScript.StartSpikeTrap()
end
function Cancel()
  DisableAllPuzzleInteracts()
  ClearPuppet()
  bCancelled = true
end
local leverEmitters = {}
local leverSoundEvents = {
  "SND_MECH_Cal500_SunMoon_Lever_Pull_01",
  "SND_MECH_Cal500_SunMoon_Lever_Pull_02",
  "SND_MECH_Cal500_SunMoon_Lever_Pull_03"
}
local soundEvents = {
  gearSlide = "SND_MECH_Cal500_SunMoon_Symbols_Slide_LP",
  gearSlideToEdge = "SND_MECH_Cal500_SunMoon_Symbols_Slide_Edge_Hit",
  gearMove = "SND_MECH_Cal500_SunMoon_Symbol_Move_LP"
}
function SoundInit()
  leverEmitters[1] = GameObjects.WaterTrap_Levers_01.SoundEmitters[1]
  leverEmitters[2] = GameObjects.WaterTrap_Levers_02:FindSingleSoundEmitterByName("SNDLeverCenter")
  leverEmitters[3] = GameObjects.WaterTrap_Levers_03.SoundEmitters[1]
end
function SoundDebugTable()
  local debugTable = {}
  debugTable.Title = "Gear Trap Sound Info"
  debugTable.X = 2
  debugTable.Y = 2
  debugTable.TitleColor = engine.Vector.New(255, 0, 128)
  table.insert(debugTable, {
    "SUN",
    " ",
    "EARTH",
    " ",
    "MOON"
  })
  table.insert(debugTable, {
    "gearSun.AnimOut",
    gearSun.AnimOut.AnimFrame,
    "gearEarth.AnimOut",
    gearEarth.AnimOut.AnimFrame,
    "gearMoon.AnimOut",
    gearMoon.AnimOut.AnimFrame
  })
  table.insert(debugTable, {
    "gearSun.AnimUp",
    gearSun.AnimUp.AnimFrame,
    "gearEarth.AnimUp",
    gearEarth.AnimUp.AnimFrame,
    "gearMoon.AnimUp",
    gearMoon.AnimUp.AnimFrame
  })
  table.insert(debugTable, {
    "gearSun.AnimAcross",
    gearSun.AnimAcross.AnimFrame,
    "gearEarth.AnimAcross",
    gearEarth.AnimAcross.AnimFrame,
    "gearMoon.AnimAcross",
    gearMoon.AnimAcross.AnimFrame
  })
  engine.DrawDebugTable(debugTable)
end
function PlaySoundGearMoveLeft(gear, slot)
  LD.PlaySound(gear.SoundEmitter, soundEvents.gearSlide)
  LD.StopSoundOnFrame(gear.SoundEmitter, gear.AnimAcross, soundEvents.gearSlide, GetAnimFrameFromPercent(gear.AnimAcross, slot), "backward")
  if slot == gearSlotFrames[1] then
    LD.PlaySoundOnFrame(gear.SoundEmitter, gear.AnimAcross, soundEvents.gearSlideToEdge, GetAnimFrameFromPercent(gear.AnimAcross, slot), "backward")
  end
end
function PlaySoundGearMoveRight(gear, slot)
  LD.PlaySound(gear.SoundEmitter, soundEvents.gearSlide)
  LD.StopSoundOnFrame(gear.SoundEmitter, gear.AnimAcross, soundEvents.gearSlide, GetAnimFrameFromPercent(gear.AnimAcross, slot), "forward")
  if slot == gearSlotFrames[#gearSlotFrames] then
    LD.PlaySoundOnFrame(gear.SoundEmitter, gear.AnimAcross, soundEvents.gearSlideToEdge, gear.AnimAcross.AnimLengthFrames, "forward")
  end
end
function PlaySoundGearMoveUp(gear)
  LD.PlaySound(gear.SoundEmitter, soundEvents.gearMove)
  LD.StopSoundOnFrame(gear.SoundEmitter, gear.AnimUp, soundEvents.gearMove, gear.AnimUp.AnimLengthFrames, "forward")
end
function PlaySoundGearMoveDown(gear)
  LD.PlaySound(gear.SoundEmitter, soundEvents.gearMove)
  LD.StopSoundOnFrame(gear.SoundEmitter, gear.AnimUp, soundEvents.gearMove, 0, "backward")
end
function PlayLeverSound(leverIndex)
  LD.PlaySound(leverEmitters[leverIndex], leverSoundEvents[leverIndex])
end
function PlaySoundOnPuzzleSolved()
  LD.CallFunctionAfterDelay(function()
    LD.PlaySound(leverEmitters[2], "SND_MECH_Cal500_SunMoon_Symbols_Win")
    GameObjects.FlipSwitch3.LuaObjectScript.StopBGGearsSound()
  end, 1.15)
end
function GetAnimFrameFromPercent(anim, percent)
  return math.floor(anim.AnimLengthFrames * percent)
end
