4U   q   nut scripts/vscripts director_base_addon /`      Q   iladder ~E%  Q   d  shared &         IncludeScript("shared", getroottable())
IncludeScript("iladder", getroottable())::IsMoving <- function(ent, minVelocitySqr=0.0) {
  return (NetProps.GetPropVector(ent, "m_vecAbsVelocity").LengthSqr() > pow(minVelocitySqr,2))? true : false
}

::ILadder <- {
  players = {}
  playersStop = {}
  posTops = null

  function isNearTop(pos) {
    foreach (posTop in posTops) if ((posTop-pos).LengthSqr() < pow(30,2)) return true
    return false
  }

  function update() {
    if (posTops==null) {
      posTops = []
      local tblLadder = {}
      NavMesh.GetAllLadders(tblLadder)
      foreach (name, nav in tblLadder) posTops.append(nav.GetTopOrigin())
    }
    local now = Time()
    foreach (player in Shared.survivorsHuman) {
      local isLadder = NetProps.GetPropInt(player, "movetype") == 9
      if (isLadder && !(player in players)) {
        players[player] <- player.EyeAngles().y
        local cam = Entities.FindByName(null,"cam_ktb"+player.GetPlayerUserId())
        if (!cam) {
          cam = SpawnEntityFromTable("point_viewcontrol_survivor", { targetname = "cam_ktb"+player.GetPlayerUserId() })
          local attachid = player.LookupAttachment("eyes")
          if (!attachid) attachid = player.LookupAttachment("forward")
          NetProps.SetPropInt(cam, "m_MoveType", 0)
          NetProps.SetPropEntity(cam, "m_pParent", player)
          NetProps.SetPropEntity(cam, "m_hMoveParent", player)
          NetProps.SetPropInt(cam, "m_iParentAttachment", attachid)
        }
        NetProps.SetPropVector(cam, "m_vecAbsOrigin", player.GetOrigin())
        for (local i = 0; i < 6; i++) {
            NetProps.SetPropIntArray(player, "m_NetGestureSequence", -1, i)
            NetProps.SetPropIntArray(player, "m_NetGestureActivity", -1, i)
            NetProps.SetPropFloatArray(player, "m_NetGestureStartTime", Time(), i)
        }
        DoEntFire("!self", "Enable", "", 0, player, cam)
      }
      else if (!isLadder && player in players) {
        delete players[player]
        local cam = Entities.FindByName(null, "cam_ktb"+player.GetPlayerUserId())
        cam.SetLocalAngles(QAngle(0,0,0))
        DoEntFire("!self", "Disable", "", 0.1, player, cam) // SetLocalAngles applied only when camera enabled
        local eyeAngles = player.EyeAngles()
        eyeAngles.y = player.GetAttachmentAngles(player.LookupAttachment("eyes")).y
        player.SnapEyeAngles(eyeAngles)
        NetProps.SetPropFloat(player, "m_flFrozen", 0.0)
      }
    }
    foreach (player, initAngles in players) {
      local cam = Entities.FindByName(null, "cam_ktb"+player.GetPlayerUserId())
      cam.SetLocalAngles(QAngle(0,player.EyeAngles().y-initAngles,0))
      if (player.EyeAngles().x > 0 && isNearTop(player.GetOrigin())) NetProps.SetPropInt(player, "m_afButtonDisabled", NetProps.GetPropInt(player, "m_afButtonDisabled") | (1 << 4))
      else NetProps.SetPropInt(player, "m_afButtonDisabled", NetProps.GetPropInt(player, "m_afButtonDisabled") & ~(1 << 4))
      local isStop = !IsMoving(player)
      if (isStop && !(player in playersStop)) {
        playersStop[player] <- now
        NetProps.SetPropFloat(player, "m_flFrozen", 1.0)
      }
      else if (!isStop && player in playersStop) {
        delete playersStop[player]
        NetProps.SetPropFloat(player, "m_flFrozen", 0.0)
      }
    }
    return 0.03
  }
                   
  function init() {
    if (Entities.FindByName(null,"thinkLadder")) return
    local thinkEnt = SpawnEntityFromTable("info_target", {targetname = "thinkLadder"})
    thinkEnt.ValidateScriptScope()
    thinkEnt.GetScriptScope()["update"] <- @() ::ILadder.update()
    AddThinkToEnt(thinkEnt, "update")
  }
}
::ILadder.init()
::Shared <- {
  survivors = {}
  survivorsBot = {}
  survivorsHuman = {}
  onWeaponChanges = {}
  onGameplayStarts = {}
  saveData = {}
  survivorsWeapon = {}
  isFirstMap = false
  thinkWeaponChange = null
  isSaveInitialized = false

  function inTable(value, table) {
    foreach (_, i in table) if (i==value) return true
    return false
  }

  function updateWeaponChange() {
    if (!Shared.onWeaponChanges) return 0.1
    foreach (userid, player in Shared.survivors) {
      local next = player.GetActiveWeapon()
      if (!(userid in survivorsWeapon)) {
        survivorsWeapon[userid] <- next
        continue
      }
      local prev = survivorsWeapon[userid]
      if (next == prev) continue
      survivorsWeapon[userid] = next
      foreach (_, event in Shared.onWeaponChanges) event({userid = userid, player = player, prev = prev, next = next})
    }
    return 0.1
  }

  function init() {
    if (isSaveInitialized) return
    isSaveInitialized = true
    local tmpData = {}
    RestoreTable("shared", tmpData)
    SaveTable("shared", tmpData)
    if (!("firstmap" in tmpData) || tmpData["firstmap"]==Director.GetMapName()) {isFirstMap = true; printl("=== this is first map ===") }
  }

  function OnGameEvent_round_start_post_nav(params) {
    if (!thinkWeaponChange) thinkWeaponChange = Entities.FindByName(null, "thinkWeaponChange")
    if (!thinkWeaponChange) {
      thinkWeaponChange = SpawnEntityFromTable("info_target", {targetname="thinkWeaponChange"})
      thinkWeaponChange.ValidateScriptScope()
      thinkWeaponChange.GetScriptScope()["update"] <- @() ::Shared.updateWeaponChange()
      AddThinkToEnt(thinkWeaponChange, "update")
    }
    RestoreTable("shared", saveData)
    if (!("firstmap" in saveData)) saveData["firstmap"] <- Director.GetMapName()
  }

  function OnGameEvent_round_end(params) {
    printl("===== Round End Reason: "+params.reason)
    if (params.reason != 3) SaveTable("shared", saveData)
  }

  function OnGameEvent_map_transition(params) {
    SaveTable("shared", saveData)
  }

  function OnGameEvent_player_spawn(params) {
    if (!("userid" in params)) return
    local player = GetPlayerFromUserID(params.userid)
    if (!player.IsSurvivor()) return
    survivors[params.userid] <- player
    if (IsPlayerABot(player)) survivorsBot[params.userid]   <- player
    else                      survivorsHuman[params.userid] <- player
  }

  function OnGameEvent_player_disconnect(params) {
    if (!("userid" in params)) return
    survivors.rawdelete(params.userid)
    survivorsBot.rawdelete(params.userid)
    survivorsHuman.rawdelete(params.userid)
  }

  function OnGameEvent_player_bot_replace(params) {
    if (!("player" in params)) return
    if (!("bot" in params)) return
    local bot = GetPlayerFromUserID(params.bot)
    survivors.rawdelete(params.player)
    survivors[params.bot] <- bot
    survivorsHuman.rawdelete(params.player)
    survivorsBot[params.bot] <- bot
  }

  function OnGameEvent_bot_player_replace(params) {
    if (!("player" in params)) return
    if (!("bot" in params)) return
    local player = GetPlayerFromUserID(params.player)
    survivors.rawdelete(params.bot)
    survivors[params.player] <- player
    survivorsBot.rawdelete(params.bot)
    survivorsHuman[params.player] <- player
  }
}
__CollectEventCallbacks(::Shared, "OnGameEvent_", "GameEventCallbacks", RegisterScriptGameEventListener)
Shared.init()

::RandomPick <- function(arr) return arr[RandomInt(0, arr.len()-1)]
