local LD = require("design.LevelDesignLibrary")
local timers = require("level.timer")
local monitors = require("level.MonitorLibrary")
local thisObj, player
local enabled = false
local state, startFrame
local startClosed = false
local instaKillFrame, closeFrame, closeSpeed
local openFrame = 0
local openSpeed
local animSpeed = 1
local animMonitor, delayTimer
local timeRemaining = -1
local closeDelay, openDelay, spikeCollision, combatCollisionObj
local standardCombatCol = "Traps_CrusherBlock_Standard"
local instaKillCombatCol = "Traps_CrusherBlock_InstantKill"
local frontGear, backGear
local gearMaxAnimSpeed = 0.5
local isFrontGearHidden = false
local isBackGearHidden = false
local surfaceIceBack = {embed = nil, unEmbed = nil}
local surfaceIceFront = {embed = nil, unEmbed = nil}
local orientation, crusherObj
local instaKill_Immediate = false
local instaKillOn = true
local crusherString
local log = ""
local playerInCrushArea = false
local onFullyOpenedCallback, onFullyClosedCallback
local closeFrameCheckOffset = 15
function OnScriptLoaded(level, obj)
  player = game.Player.FindPlayer()
  thisObj = obj
  enabled = thisObj:GetLuaTableAttribute("StartEnabled")
  combatCollisionObj = thisObj:FindSingleGOByName("CombatCollision")
  crusherString = thisObj:GetLuaTableAttribute("CrusherDistance")
  closeFrame = ConvertMetersToFrames(crusherString)
  instaKillOn = thisObj:GetLuaTableAttribute("IsInstantKill_On")
  orientation = thisObj:GetLuaTableAttribute("Orientation")
  if instaKillOn == true then
    instaKillFrame = ConvertInstaKillToFrames(crusherString)
  end
  startFrame = thisObj:GetLuaTableAttribute("StartDistance")
  startFrame = ConvertMetersToFrames(startFrame)
  openDelay = thisObj:GetLuaTableAttribute("StayOpen_Delay")
  closeDelay = thisObj:GetLuaTableAttribute("StayClose_Delay")
  frontGear = thisObj:FindSingleGOByName("WallGear_Front").Child
  backGear = thisObj:FindSingleGOByName("WallGear_Back").Child
  isFrontGearHidden = thisObj:GetLuaTableAttribute("FrontGear_HubCapOn")
  isBackGearHidden = thisObj:GetLuaTableAttribute("BackGear_HubCapOn")
  surfaceIceBack.embed = thisObj:FindSingleGOByName("IceEmbedFX_Surface_Back")
  surfaceIceBack.unEmbed = thisObj:FindSingleGOByName("IceUnembedFX_Surface_Back")
  surfaceIceFront.embed = thisObj:FindSingleGOByName("IceEmbedFX_Surface_Front")
  surfaceIceFront.unEmbed = thisObj:FindSingleGOByName("IceUnembedFX_Surface_Front")
  standardCombatCol = standardCombatCol .. "_" .. orientation
  instaKillCombatCol = instaKillCombatCol .. "_" .. orientation
  game.SubObject.SetEntityZoneHandler(thisObj, thisObj)
  onFullyOpenedCallback = thisObj:GetLuaTableAttribute("OnFullyOpened")
  onFullyClosedCallback = thisObj:GetLuaTableAttribute("OnFullyClosed")
  if onFullyOpenedCallback ~= nil then
    onFullyOpenedCallback = LD.ExtractCallbacksForEvent(level, obj, onFullyOpenedCallback)
  end
  if onFullyClosedCallback ~= nil then
    onFullyClosedCallback = LD.ExtractCallbacksForEvent(level, obj, onFullyClosedCallback)
  end
  spikeCollision = thisObj:FindSingleGOByName("SpikeCollision")
  SoundInit()
  game.SubObject.Sleep(thisObj)
end
function OnFirstPreStart(level, obj)
  local iceFXNames = {
    "IceEmbedFX_Front",
    "IceEmbedFX_Back",
    "IceUnembedFX_Front",
    "IceUnembedFX_Back"
  }
  for i = 1, #iceFXNames do
    iceFXNames[i] = thisObj:FindSingleGOByName(iceFXNames[i])
    if iceFXNames[i] then
      if iceFXNames[i].IsRefNode then
        iceFXNames[i] = iceFXNames[i].Child
      end
      LD.HideFX(iceFXNames[i])
      iceFXNames[i]:HideModel()
    end
  end
  surfaceIceBack.embed:Hide()
  surfaceIceBack.unEmbed:Hide()
  surfaceIceFront.embed:Hide()
  surfaceIceFront.unEmbed:Hide()
end
function OnFirstStart(level, obj)
  HideGear(frontGear, isFrontGearHidden)
  HideGear(backGear, isBackGearHidden)
  if 0 <= startFrame then
    if startFrame > closeFrame then
      startFrame = closeFrame
    end
    thisObj:JumpAnimToFrame(startFrame)
    thisObj:PauseAnim()
  end
  if startFrame < closeFrame then
    state = "Opening"
  else
    state = "Closing"
  end
end
local SetGearCallbacks = function(gear, isFront, embedFxObj, unEmbedFXObj)
  local fnName = "Back"
  if isFront then
    fnName = "Front"
  end
  gear.LuaObjectScript.RegisterUnembedStartCallback(function()
    embedFxObj:Hide()
    LD.ShowFX(unEmbedFXObj)
    unEmbedFXObj:ClearAllAnimationCallbacks()
    unEmbedFXObj:OnAnimDone(thisObj, "Hide" .. fnName .. "_UnEmbedSurfaceIce")
  end)
  gear.LuaObjectScript.RegisterUnembedCallback(Enable)
  gear.LuaObjectScript.RegisterEmbedCallback(function()
    Disable()
    LD.ShowFX(embedFxObj)
  end)
end
function OnPreStart(level, obj)
  if not isFrontGearHidden then
    local iceFX = {
      "IceEmbedFX_Front",
      "IceUnembedFX_Front"
    }
    for i = 1, #iceFX do
      iceFX[i] = thisObj:FindSingleGOByName(iceFX[i])
      iceFX[i]:SetWorldPosition(frontGear:GetWorldPosition())
    end
    frontGear.LuaObjectScript.SetIceEmbedFx(iceFX[1])
    frontGear.LuaObjectScript.SetIceUnembedFx(iceFX[2])
    SetGearCallbacks(frontGear, true, surfaceIceFront.embed, surfaceIceFront.unEmbed)
  end
  if not isBackGearHidden then
    local iceFX = {
      "IceEmbedFX_Back",
      "IceUnembedFX_Back"
    }
    for i = 1, #iceFX do
      iceFX[i] = thisObj:FindSingleGOByName(iceFX[i])
      iceFX[i]:SetWorldPosition(backGear:GetWorldPosition())
    end
    backGear.LuaObjectScript.SetIceEmbedFx(iceFX[1])
    backGear.LuaObjectScript.SetIceUnembedFx(iceFX[2])
    SetGearCallbacks(backGear, false, surfaceIceBack.embed, surfaceIceBack.unEmbed)
  end
  HideSpikeCollision()
end
function HideBack_UnEmbedSurfaceIce()
  surfaceIceBack.unEmbed:Hide()
end
function HideFront_UnEmbedSurfaceIce()
  surfaceIceFront.unEmbed:Hide()
end
function OnStart(level, obj)
  SetAnimationSpeed(crusherString)
  ResetAnimMonitor()
  if enabled == true then
    Enable()
  else
    Disable()
  end
end
function OnSaveCheckpoint(level, obj)
  if delayTimer ~= nil and delayTimer.running == true and delayTimer:GetRemainingTime() > 0 then
    timeRemaining = delayTimer:GetRemainingTime()
  end
  return {
    enabled = enabled,
    closeFrame = closeFrame,
    instaKillFrame = instaKillFrame,
    state = state,
    timeRemaining = timeRemaining
  }
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  enabled = savedInfo.enabled
  closeFrame = savedInfo.closeFrame
  instaKillFrame = savedInfo.instaKillFrame
  state = savedInfo.state
  timeRemaining = savedInfo.timeRemaining
end
function OnMarkerEnterZone(level, scriptObj, volumeGameObject, markerGameObject, markerId)
  if markerGameObject == player and (game.LargeInteger.HashString(3) == markerId or game.LargeInteger.HashString(3) == game.LargeInteger.HashString(tostring(markerId))) then
    playerInCrushArea = true
  end
end
function OnMarkerExitZone(level, scriptObj, volumeGameObject, markerGameObject, markerId)
  if markerGameObject == player and (game.LargeInteger.HashString(3) == markerId or game.LargeInteger.HashString(3) == game.LargeInteger.HashString(tostring(markerId))) then
    playerInCrushArea = false
  end
end
function Enable(enableGears)
  if frontGear.LuaObjectScript.IsAxePinned() == false and backGear.LuaObjectScript.IsAxePinned() == false then
    Disable()
    enabled = true
    if animMonitor ~= nil then
      animMonitor:Start()
    end
    if instaKill_Immediate == true then
      SetToInstantKill()
    end
    if IsInUnwalkableRange() then
      HideSpikeCollision()
    end
    RestartAnimation()
    if enableGears then
      if not isFrontGearHidden then
        frontGear.LuaObjectScript.Enable()
      end
      if not isBackGearHidden then
        backGear.LuaObjectScript.Enable()
      end
    end
    StartAnimSounds()
  end
end
function Disable(disableGears)
  enabled = false
  thisObj:ClearAllAnimationCallbacks()
  thisObj:PauseAnimation()
  SetToStandard()
  PauseGearsAnim()
  if disableGears then
    frontGear.LuaObjectScript.Disable()
    backGear.LuaObjectScript.Disable()
  end
  if animMonitor ~= nil then
    animMonitor:Stop()
    animMonitor:Terminate()
    animMonitor = nil
  end
  if IsInUnwalkableRange() then
    ShowSpikeCollision()
    DisableCombatCollision()
  end
  if delayTimer then
    delayTimer:Stop()
    delayTimer = nil
  end
  if 0 < timeRemaining then
    timeRemaining = -1
  end
  StopAnimSounds()
end
function HideGear(gear, isHidden)
  if not isHidden then
    gear.LuaObjectScript.Enable()
    gear.LuaObjectScript.ShowGear()
  else
    gear.LuaObjectScript.Disable()
    gear.LuaObjectScript.HideGear()
  end
end
function HideSpikeCollision()
  if spikeCollision then
    spikeCollision:HideCollision()
  end
end
function ShowSpikeCollision()
  if spikeCollision then
    spikeCollision:ShowCollision()
  end
end
function OpenCrusherWithDelay()
  DisableCombatCollision()
  PauseGearsAnim()
  state = "Closed"
  if onFullyClosedCallback ~= nil then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, onFullyClosedCallback, "CrusherBlock - OnFullyClosed")
  end
  delayTimer = timers.StartLevelTimer(closeDelay, OpenCrusher)
end
function OpenCrusher()
  state = "Opening"
  PlayGearsAnimOpen()
  SetToStandard()
  thisObj:PlayAnimationToFrame(openFrame, {
    Rate = -1 * animSpeed
  })
  thisObj:OnAnimationDone(thisObj, "CloseCrusherWithDelay", {Force = true})
end
function CloseCrusherWithDelay()
  PauseGearsAnim()
  state = "Opened"
  SetToStandard()
  if onFullyOpenedCallback ~= nil then
    LD.ExecuteCallbacksForEvent(thisObj.Level, thisObj, onFullyOpenedCallback, "CrusherBlock - OnFullyOpened")
  end
  ClearDelayTimer()
  delayTimer = timers.StartLevelTimer(openDelay, CloseCrusher)
end
function CloseCrusher()
  state = "Closing"
  SetToInstantKill()
  PlayGearsAnimClose()
  local closeFrameCheck = closeFrame - closeFrameCheckOffset
  if closeFrameCheck <= thisObj.AnimFrame then
    CheckToSeeIfCrusherWillKill()
  else
    thisObj:PlayAnimationToFrame(closeFrameCheck, {Rate = animSpeed})
    thisObj:OnAnimationDone(thisObj, "CheckToSeeIfCrusherWillKill", {Force = true})
  end
end
function CheckToSeeIfCrusherWillKill()
  if IsPlayingCrusherDeathMove() or playerInCrushArea then
    thisObj:PlayAnimationToFrame(closeFrame - GetDeathCrusherOffset(), {
      Rate = animSpeed / 1.5
    })
    PlaySoundOnKill()
  else
    PlayGearsAnimClose()
    thisObj:PlayAnimationToFrame(closeFrame, {Rate = animSpeed})
    thisObj:OnAnimationDone(thisObj, "OpenCrusherWithDelay", {Force = true})
  end
end
function ClearDelayTimer()
  if delayTimer ~= nil then
    delayTimer:Stop()
    delayTimer = nil
  end
end
local horizontalKillFrameOffset = 12
local verticalKillFrameOffset = 10
function GetDeathCrusherOffset()
  if orientation == "Horizontal" then
    return horizontalKillFrameOffset
  else
    return verticalKillFrameOffset
  end
end
function SetHorizontalKillFrameOffset(frame)
  if frame then
    horizontalKillFrameOffset = frame
  end
end
function SetCloseFrameCheckOffset(frame)
  if frame then
    closeFrameCheckOffset = frame
  end
end
function SetVerticalKillFrameOffset(frame)
  if frame then
    verticalKillFrameOffset = frame
  end
end
local deathMovs = {
  "MOV_DeathFlattenFront",
  "MOV_DeathFlattenHorizontal"
}
function IsPlayingCrusherDeathMove()
  for _, v in ipairs(deathMovs) do
    if player:IsPlayingMove(v) then
      return true
    end
  end
  return false
end
function ChangeCrusherDistance(crusherDistance)
  crusherString = crusherDistance
  closeFrame = ConvertMetersToFrames(crusherDistance)
  instaKillFrame = ConvertInstaKillToFrames(crusherDistance)
  SetAnimationSpeed(crusherDistance)
  ClearDelayTimer()
  RestartAnimation()
  ResetAnimMonitor()
end
function ConvertMetersToFrames(distance)
  local frame = -1
  return 10 * tonumber(string.sub(distance, 1, 1))
end
function ConvertInstaKillToFrames(distance)
  local frame = -1
  if distance == "1m" or distance == "2m" then
    frame = 0
    instaKill_Immediate = true
  elseif orientation == "Vertical" then
    if distance == "3m" then
      instaKill_Immediate = true
      frame = ConvertMetersToFrames("1m")
    elseif distance == "4m" then
      frame = ConvertMetersToFrames("2m")
    elseif distance == "5m" then
      frame = ConvertMetersToFrames("3m")
    elseif distance == "6m" then
      frame = ConvertMetersToFrames("4m")
    elseif distance == "7m" then
      frame = ConvertMetersToFrames("5m")
    elseif distance == "8m" then
      frame = ConvertMetersToFrames("6m")
    end
  elseif orientation == "Horizontal" then
    if distance == "3m" then
      instaKill_Immediate = true
      frame = ConvertMetersToFrames("2m")
    elseif distance == "4m" then
      frame = ConvertMetersToFrames("3m")
    elseif distance == "5m" then
      frame = ConvertMetersToFrames("4m")
    elseif distance == "6m" then
      frame = ConvertMetersToFrames("5m")
    elseif distance == "7m" then
      frame = ConvertMetersToFrames("6m")
    elseif distance == "8m" then
      frame = ConvertMetersToFrames("7m")
    end
  end
  return frame
end
local horizontalUnwalkableFrame = 22
local verticalUnwalkableFrame = 22
function OverrideHorizontalUnwalkableFrame(value)
  if value then
    horizontalUnwalkableFrame = value
  end
end
function OverrideVerticalUnwalkableFrame(value)
  if value then
    verticalUnwalkableFrame = value
  end
end
function IsInUnwalkableRange()
  local frame = 0
  if orientation == "Horizontal" then
    frame = horizontalUnwalkableFrame
  else
    frame = verticalUnwalkableFrame
  end
  return frame >= math.abs(closeFrame - thisObj.AnimFrame)
end
function SetToInstantKill()
  log = "Instant Kill"
  combatCollisionObj.LuaObjectScript.ChangeCombatCollisionType(instaKillCombatCol)
  if not combatCollisionObj.LuaObjectScript.IsEnabled() then
    combatCollisionObj.LuaObjectScript.Enable()
  end
end
function SetToStandard()
  log = "Standard"
  combatCollisionObj.LuaObjectScript.ChangeCombatCollisionType(standardCombatCol)
  if not combatCollisionObj.LuaObjectScript.IsEnabled() then
    combatCollisionObj.LuaObjectScript.Enable()
  end
end
function DisableCombatCollision()
  log = "None"
  combatCollisionObj.LuaObjectScript.Disable()
end
function PlayGearsAnimOpen()
  local gearSpeed = animSpeed
  if gearSpeed > gearMaxAnimSpeed then
    gearSpeed = gearMaxAnimSpeed
  end
  frontGear.LuaObjectScript.SetAnimationSpeed("PlayAnimCycleReverse", gearSpeed)
  backGear.LuaObjectScript.SetAnimationSpeed("PlayAnimCycle", gearSpeed)
end
function PlayGearsAnimClose()
  local gearSpeed = animSpeed
  if gearSpeed > gearMaxAnimSpeed then
    gearSpeed = gearMaxAnimSpeed
  end
  frontGear.LuaObjectScript.SetAnimationSpeed("PlayAnimCycle", -gearSpeed)
  backGear.LuaObjectScript.SetAnimationSpeed("PlayAnimCycleReverse", -gearSpeed)
end
function PauseGearsAnim()
  frontGear.LuaObjectScript.SetAnimationSpeed("PauseAnim", 0)
  backGear.LuaObjectScript.SetAnimationSpeed("PauseAnim", 0)
end
function SetAnimationSpeed(distance)
  local baseSpeed = 4
  animSpeed = baseSpeed * (ConvertMetersToFrames(distance) / 40)
end
function RestartAnimation()
  thisObj:ClearAllAnimationCallbacks()
  thisObj:PauseAnimation()
  if enabled then
    if delayTimer ~= nil and delayTimer.running == false and delayTimer:GetRemainingTime() > 0 then
      delayTimer:Unpause()
    elseif state == "Closing" or state == nil then
      CloseCrusher()
    elseif state == "Closed" then
      if 0 < timeRemaining then
        timeRemaining = -1
        ClearDelayTimer()
        delayTimer = timers.StartLevelTimer(timeRemaining, OpenCrusher)
      else
        OpenCrusherWithDelay()
      end
    elseif state == "Opening" then
      OpenCrusher()
    elseif state == "Opened" then
      if 0 < timeRemaining then
        timeRemaining = -1
        ClearDelayTimer()
        delayTimer = timers.StartLevelTimer(timeRemaining, CloseCrusher)
      else
        CloseCrusherWithDelay()
      end
    end
  end
end
function ResetAnimMonitor()
  if animMonitor ~= nil then
    animMonitor:Stop()
    animMonitor:Terminate()
    animMonitor = nil
  end
  animMonitor = monitors.CreateAnimFrameMonitor(thisObj)
  animMonitor:SetPrintsEnabled(false)
  if instaKillOn == true then
    if instaKill_Immediate == false then
      animMonitor:OnFrameForward(instaKillFrame, SetToInstantKill)
    elseif instaKill_Immediate == true then
      SetToInstantKill()
    end
  end
end
function ForceOpenCrusher(disableGears)
  state = "Opened"
  Disable(disableGears)
  thisObj:JumpAnimationToFrame(openFrame)
  thisObj:PauseAnimation()
  ClearDelayTimer()
  if 0 < timeRemaining then
    timeRemaining = -1
  end
  if animMonitor ~= nil then
    animMonitor:Stop()
  end
end
function ForceCloseCrusher(disableGears)
  state = "Closed"
  Disable(disableGears)
  thisObj:JumpAnimationToFrame(closeFrame)
  thisObj:PauseAnimation()
  ClearDelayTimer()
  if 0 < timeRemaining then
    timeRemaining = -1
  end
  if animMonitor ~= nil then
    animMonitor:Stop()
  end
end
function ForceOverrideState(overrideState)
  if overrideState == "Closed" or overrideState == "Closing" or overrideState == "Opened" or overrideState == "Opening" then
    state = overrideState
  else
    engine.Error(thisObj.Parent:GetName() .. " getting set to invalid state [ " .. overrideState .. " ] in  ForceOverrideState( overrideState ) ")
  end
end
function ShowDebugTable(x, y)
  if engine.IsDebug() then
    local debugTable = {}
    debugTable.Title = "Crusher Block: " .. thisObj.Parent:GetName()
    debugTable.X = x or 120
    debugTable.Y = y or 10
    debugTable.TitleColor = engine.Vector.New(255, 0, 128)
    debugTable[#debugTable + 1] = {
      "AnimFrame: ",
      thisObj.AnimFrame
    }
    debugTable[#debugTable + 1] = {"State: ", state}
    debugTable[#debugTable + 1] = {
      "playerInCrushArea: ",
      playerInCrushArea
    }
    debugTable[#debugTable + 1] = {
      "CrusherDistance: ",
      crusherString
    }
    debugTable[#debugTable + 1] = {"Log  ", log}
    debugTable[#debugTable + 1] = {
      "IsLoopingSound  ",
      GetSoundEvents().isLooping
    }
    if delayTimer ~= nil then
      debugTable[#debugTable + 1] = {
        "RemainingTime ",
        delayTimer:GetRemainingTime()
      }
    end
    if delayTimer ~= nil then
      debugTable[#debugTable + 1] = {
        "AnimMonitor isn't nil",
        true
      }
    end
    debugTable[#debugTable + 1] = {
      "CheckPointed Time ",
      timeRemaining
    }
    engine.DrawDebugTable(debugTable)
  end
end
local soundEmitter, soundOnOpen, soundOnClose, CrusherSoundActionMonitor
local soundEvents = {
  isLooping = true,
  OnRetract = "SND_TRAP_Block_Crusher_Retract_LP",
  OnRetractHit = "SND_TRAP_Block_Crusher_Retract_Hit_Top",
  OnProtract = "SND_TRAP_Block_Crusher_Protract_LP",
  OnProtractHit = "SND_TRAP_Block_Crusher_Protract_Hit_Bottom",
  OnKillKratos = "SND_TRAP_Block_Crusher_Damage_Gore"
}
local soundAnimActions = {}
function SoundInit()
  soundEmitter = thisObj.SoundEmitters[1]
  SoundSetup(soundEvents)
end
function SoundSetup(sounds)
  if sounds ~= nil then
    if sounds.SoundEmitter ~= nil then
      soundEmitter = thisObj:FindSingleSoundEmitterByName(sounds.SoundEmitter)
    end
    for k, v in pairs(soundEvents) do
      for i, j in pairs(sounds) do
        if i == k and j ~= nil and j ~= "" then
          soundEvents[k] = j
        end
      end
      LD.SoundDebug(tostring(k) .. ": " .. tostring(v))
    end
  end
  if soundEvents.isLooping then
    soundAnimActions = {
      {
        action = "Play",
        frame = openFrame + 3,
        emitter = soundEmitter,
        direction = "forward",
        soundEvent = soundEvents.OnProtract
      },
      {
        action = "Stop",
        frame = closeFrame - 4,
        emitter = soundEmitter,
        direction = "forward",
        soundEvent = soundEvents.OnProtract
      },
      {
        action = "Play",
        frame = closeFrame - 3,
        emitter = soundEmitter,
        direction = "forward",
        soundEvent = soundEvents.OnProtractHit
      },
      {
        action = "Play",
        frame = closeFrame - 3,
        emitter = soundEmitter,
        direction = "backward",
        soundEvent = soundEvents.OnRetract
      },
      {
        action = "Stop",
        frame = openFrame + 4,
        emitter = soundEmitter,
        direction = "backward",
        soundEvent = soundEvents.OnRetract
      },
      {
        action = "Play",
        frame = openFrame + 3,
        emitter = soundEmitter,
        direction = "backward",
        soundEvent = soundEvents.OnRetractHit
      }
    }
  else
    soundAnimActions = {
      {
        action = "Play",
        frame = openFrame + 3,
        emitter = soundEmitter,
        direction = "forward",
        soundEvent = soundEvents.OnProtract
      },
      {
        action = "Play",
        frame = closeFrame - 3,
        emitter = soundEmitter,
        direction = "forward",
        soundEvent = soundEvents.OnProtractHit
      },
      {
        action = "Play",
        frame = closeFrame - 3,
        emitter = soundEmitter,
        direction = "backward",
        soundEvent = soundEvents.OnRetract
      },
      {
        action = "Play",
        frame = openFrame + 3,
        emitter = soundEmitter,
        direction = "backward",
        soundEvent = soundEvents.OnRetractHit
      }
    }
  end
  CrusherSoundActionMonitor = monitors.CreateAudioAnimFrameMonitor(thisObj)
  CrusherSoundActionMonitor:AddSoundActionList(soundAnimActions)
end
function StopAnimSounds()
  if CrusherSoundActionMonitor ~= nil then
    CrusherSoundActionMonitor:Stop()
  end
end
function StartAnimSounds()
  if CrusherSoundActionMonitor ~= nil then
    CrusherSoundActionMonitor:Start()
  end
end
function GetSoundEvents()
  return soundEvents
end
function PlaySoundOnKill()
  LD.PlaySound(soundEmitter, soundEvents.OnProtractHit)
  if not player:FindSingleSoundEmitterByName("SNDKratos"):IsPlaying(soundEvents.OnKillKratos) then
    LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), soundEvents.OnKillKratos)
  end
end
