4ªU   e   nut scripts/vscripts scar_fire_mode_logic ÀkOh  ÿ8   F  ÿÿdirector_base_addon µ°p  ÿ    8   ÿÿ   IncludeScript("scar_fire_mode_logic")
SCARFM_ArmThink()
if ( "SCARFireModeInstalled" in getroottable() )
    return

::SCARFireModeInstalled <- true

::SCARFM_TOGGLE_BUTTON      <- 32768
::SCARFM_TOGGLE_COOLDOWN    <- 12
::SCARFM_THINK_INTERVAL     <- 0.035  // [수정] 0.01초에서 0.035초로 완화하여 CPU 부하 감소
::SCARFM_RESTORE_DELAY      <- 0.034
::SCARFM_SEMI_RESTORE_DELAY <- 0.034
::SCARFM_MIN_BLIP_INTERVAL  <- 0.15
::SCARFM_SEMI_EXTRA_DELAY   <- 0.14
::SCARFM_DEFAULT_MODE       <- 1
::SCARFM_SWITCH_SOUND       <- "items/flashlight1.wav"

::SCARFM_Mode               <- {}
::SCARFM_ToggleLockout      <- {}
::SCARFM_LastClip           <- {}
::SCARFM_BlipBusyUntil      <- {}
::SCARFM_SemiBlocked        <- {}
::SCARFM_SemiPendingBlockAt <- {}
::SCARFM_SemiReleasedEarly  <- {}
::SCARFM_SwitchSoundCounter <- 0

::SCARFM_BurstCount         <- {}

::SCARFM_ShowSwitchMessage <- function( player, text )
{
    ClientPrint(player, 4, text)
}

::SCARFM_PlaySwitchSound <- function( player )
{
    ::SCARFM_SwitchSoundCounter = (::SCARFM_SwitchSoundCounter + 1) % 128
    local name = "scarfm_switch_snd_" + ::SCARFM_SwitchSoundCounter

    SpawnEntityGroupFromTable(
    {
        a =
        {
            ambient_generic =
            {
                targetname = name
                origin = player.GetOrigin()
                message = ::SCARFM_SWITCH_SOUND
                radius = 385
                spawnflags = 32
                health = 10
                volume = 10
            }
        }
    })

    EntFire(name, "PlaySound", "", 0.03)
    EntFire(name, "Kill", "", 2.0)
}

::SCARFM_TriggerOwnerBlip <- function( weapon, restoreDelay = null )
{
    if ( restoreDelay == null )
        restoreDelay = ::SCARFM_RESTORE_DELAY

    DoEntFire("!self", "RunScriptCode",
        "NetProps.SetPropEntity(self, \"m_hOwner\", null)",
        0.0, null, weapon)
    DoEntFire("!self", "RunScriptCode",
        "NetProps.SetPropEntity(self, \"m_hOwner\", self.GetOwnerEntity())",
        restoreDelay, null, weapon)
}

::SCARFM_Generation <- 0

::SCARFM_Think <- function( gen )
{
    if ( gen != ::SCARFM_Generation )
        return

    // [추가] 이번 틱에 존재하는 유효한 무기들의 인덱스를 기록할 추적 테이블
    local activeWeapons = {}

    local player = null
    while ( player = Entities.FindByClassname(player, "player") )
    {
        if ( !player.IsValid() || !player.IsSurvivor() || player.IsDead() )
            continue

        if ( IsPlayerABot(player) )
            continue

        local weapon = player.GetActiveWeapon()
        if ( weapon == null || !weapon.IsValid() || weapon.GetClassname() != "weapon_rifle_desert" )
            continue

        local weaponIndex = weapon.GetEntityIndex()
        activeWeapons[weaponIndex] <- true  // 현재 사용 중인 무기 등록

        local button = player.GetButtonMask()

        local lockout = (player in ::SCARFM_ToggleLockout) ? ::SCARFM_ToggleLockout[player] : 0
        if ( lockout > 0 )
        {
            ::SCARFM_ToggleLockout[player] <- lockout - 1
        }
        else if ( button & ::SCARFM_TOGGLE_BUTTON )
        {
            local current = (player in ::SCARFM_Mode) ? ::SCARFM_Mode[player] : ::SCARFM_DEFAULT_MODE
            local next = (current == 0) ? 1 : 0
            ::SCARFM_Mode[player] <- next
            ::SCARFM_ToggleLockout[player] <- ::SCARFM_TOGGLE_COOLDOWN
            ::SCARFM_ShowSwitchMessage(player, (next == 1) ? "Desert Rifle: FULL AUTO" : "Desert Rifle: 3-ROUND BURST")
            SCARFM_PlaySwitchSound(player)

            if ( weaponIndex in ::SCARFM_SemiBlocked && ::SCARFM_SemiBlocked[weaponIndex] )
            {
                NetProps.SetPropFloat(weapon, "m_flNextPrimaryAttack", 0.0)
                NetProps.SetPropFloat(player, "m_flNextAttack", 0.0)
                ::SCARFM_SemiBlocked[weaponIndex] <- false
            }
        }

		local mode = (player in ::SCARFM_Mode) ? ::SCARFM_Mode[player] : ::SCARFM_DEFAULT_MODE

        // [수정] 점사 모드(0)일 때는 스크립트가 아무런 개입을 하지 않고
        // 레프트 4 데드 2 게임 기본의 Desert Rifle(3점사) 성능으로 온전히 작동하게 합니다.
        if ( mode == 0 )
        {
            // 혹시 연사 모드에서 넘어오면서 걸려있을 수 있는 공격 딜레이 제한을 풀어줍니다.
            if ( weaponIndex in ::SCARFM_SemiBlocked && ::SCARFM_SemiBlocked[weaponIndex] )
            {
                NetProps.SetPropFloat(weapon, "m_flNextPrimaryAttack", 0.0)
                NetProps.SetPropFloat(player, "m_flNextAttack", 0.0)
                ::SCARFM_SemiBlocked[weaponIndex] <- false
            }
            
            // 이 플레이어에 대해서는 아래의 연사 스크립트 로직을 건너뜁니다.
            continue
        }

        local clip = weapon.Clip1()
        local lastClip = (weaponIndex in ::SCARFM_LastClip) ? ::SCARFM_LastClip[weaponIndex] : clip
        ::SCARFM_LastClip[weaponIndex] <- clip

        local justFired = clip < lastClip
        local holdingTrigger = (button & 1) != 0
        local reloading = NetProps.GetPropInt(weapon, "m_bInReload") == 1

        // 아래는 기존 연사 모드(mode == 1) 전용 로직
        local now = Time()
        local busyUntil = (weaponIndex in ::SCARFM_BlipBusyUntil) ? ::SCARFM_BlipBusyUntil[weaponIndex] : 0
        local blipAvailable = now >= busyUntil

        if ( justFired && holdingTrigger && !reloading && clip > 0 && blipAvailable )
        {
            SCARFM_TriggerOwnerBlip(weapon)
            ::SCARFM_BlipBusyUntil[weaponIndex] <- now + ::SCARFM_MIN_BLIP_INTERVAL
        }
    }

    // [추가] 더 이상 존재하지 않거나 버려진 무기 데이터 자동 청소 (메모리 누수 원인 차단)
    foreach (wid, _ in ::SCARFM_LastClip)
    {
        if (!(wid in activeWeapons))
        {
            delete ::SCARFM_LastClip[wid]
            if (wid in ::SCARFM_SemiBlocked) delete ::SCARFM_SemiBlocked[wid]
            if (wid in ::SCARFM_SemiPendingBlockAt) delete ::SCARFM_SemiPendingBlockAt[wid]
            if (wid in ::SCARFM_BurstCount) delete ::SCARFM_BurstCount[wid]
            if (wid in ::SCARFM_BlipBusyUntil) delete ::SCARFM_BlipBusyUntil[wid]
        }
    }

    local world = Entities.FindByClassname(null, "worldspawn")
    if ( world != null )
        DoEntFire("!self", "RunScriptCode", "SCARFM_Think(" + gen + ")", ::SCARFM_THINK_INTERVAL, null, world)
}

::SCARFM_Armed <- false

::SCARFM_ArmThink <- function( params = null )
{
    local world = Entities.FindByClassname(null, "worldspawn")
    if ( world == null )
        return

    if ( !::SCARFM_Armed )
    {
        ::SCARFM_Mode <- {}
        ::SCARFM_ToggleLockout <- {}
        ::SCARFM_LastClip <- {}
        ::SCARFM_BlipBusyUntil <- {}
        ::SCARFM_SemiBlocked <- {}
        ::SCARFM_SemiPendingBlockAt <- {}
        ::SCARFM_SemiReleasedEarly <- {}
        ::SCARFM_BurstCount <- {}
        ::SCARFM_SwitchSoundCounter <- 0
        ::SCARFM_Armed = true
    }

    PrecacheSound(::SCARFM_SWITCH_SOUND)

    ::SCARFM_Generation = ::SCARFM_Generation + 1
    SCARFM_Think(::SCARFM_Generation)
}

::SCARFM_ChainEvent <- function( name, handler )
{
    if ( name in getroottable() )
    {
        local prev = getroottable()[name]
        getroottable()[name] <- function( params )
        {
            prev(params)
            handler(params)
        }
    }
    else
    {
        getroottable()[name] <- function( params )
        {
            handler(params)
        }
    }
}

SCARFM_ChainEvent("OnGameEvent_round_start", SCARFM_ArmThink)
SCARFM_ChainEvent("OnGameEvent_round_start_post_nav", SCARFM_ArmThink)

__CollectGameEventCallbacks(getroottable())

SCARFM_ArmThink()