{$CLEO .cs}
{$USE cleo+}
{$USE ini}
nop
script_name 'DDTTWKR'
wait 0
wait 0

0@ = 1

if
    load_dynamic_library 31@ "CLEO+.cleo"
jf @ILoveMyGirlfriendJessica
if
    get_dynamic_library_procedure 30@ "GetCleoPlusVersion" library 31@
then
    call_function_return 30@ num_params 0 pop 0 29@
end
free_dynamic_library 31@
if
    29@ >= 0x1020000
jf @ILoveMyGirlfriendJessica

if
    is_on_samp
then
    31@ = 0
    read_int_from_ini_file 31@ "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key "DisableOnSAMP"
    if
        31@ > 0
    then
        terminate_this_custom_script
    end
end

31@ = 0
30@ = 0
29@ = 0

:Initialization
0@ = 0

if
    does_file_exist "cleo\DrawDistanceTweaker.ini"
jf @ILoveMyGirlfriendJessica


int iWeather, iLastWeather, iWeatherType = -2
int iDebug, iActiveFlag = -1
int i1, i2
float fFarClip, fFogStart
float fFarClipMult[5], fFogStartMult[5], fMinDistGround[5]
float fHeightFactor, fMinDistHeight = 500.0, fMaxDist = 6000.0
float f1, f2, fZ
shortstring sWeather

const
    Sunny = 0
    Cloudy = 1
    Rainy = 2
    Foggy = 3
    Sandstorm = 4
    FarClipAddr = 0xB7C4F0
    FogStartAddr = 0xB7C4F4
end


read_int_from_ini_file iDebug "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key "DebugMode"
clamp_int iDebug min 0 max 1 store_to iDebug

for iWeather = 0 to 4 step 1
    gosub @GetWeatherName
    i1 = 0
    allocate_memory i1 size 256
    if 
        read_string_from_ini_file i1 "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key sWeather
    then
        scan_string i2 i1 format "%f / %f / %f" fFarClipMult[iWeather] fFogStartMult[iWeather] fMinDistGround[iWeather] 
    end
    free_memory i1
    clamp_float fFarClipMult[iWeather] min 0.0 max 1000.0 store_to fFarClipMult[iWeather]
    clamp_float fFogStartMult[iWeather] min 0.0 max 1000.0 store_to fFogStartMult[iWeather]
    clamp_float fMinDistGround[iWeather] min 10.0 max 100000.0 store_to fMinDistGround[iWeather]
    if
        fFarClipMult[iWeather] == 0.0
    then
        fFarClipMult[iWeather] = 1.0
    end
    if
        fFogStartMult[iWeather] == 0.0
    then
        fFogStartMult[iWeather] = 1.0
    end        
end

read_float_from_ini_file fHeightFactor "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key "HeightFactor"
clamp_float fHeightFactor min 0.0 max 1000.0 store_to fHeightFactor
read_float_from_ini_file fMinDistHeight "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key "MinDist"
clamp_float fMinDistHeight min 10.0 max 100000.0 store_to fMinDistHeight
read_float_from_ini_file fMaxDist "cleo\DrawDistanceTweaker.ini" section "SETTINGS" key "MaxDist"
clamp_float fMaxDist min 10.0 max 100000.0 store_to fMaxDist

wait 0

// Disable vanilla FarClip limit at high altitudes
read_memory i1 0x5613A3 size 2 virtual_protect 0
read_memory i2 0x5613A5 size 1 virtual_protect 0
if and
    i1 == 0x5ED9
    i2 == 0x50
then
    // fstp dword ptr [esi+50] -> fstp st(0) + nop
    write_memory 0x5613A3 size 2 value 0xD8DD virtual_protect 0
    write_memory 0x5613A5 size 1 value 0x90 virtual_protect 0
end

TIMERA = 0
TIMERB = 0

set_script_event_after_game_process 1 @SetDrawDistance


while true
    wait 0
    
    if
        test_cheat "DDTRELOAD"
    then
        set_script_event_after_game_process 0 @SetDrawDistance
        get_var_pointer 33@ = var 0@
        write_memory 33@ size 128 value 0x0 virtual_protect 1    // reset variables
        print_help_formatted "Draw Distance Tweaker settings reloaded"
        jump @Initialization
    end
    if
        test_cheat "DDTDEBUG"
    then
        if
            iDebug == 0
        then
            iDebug = 1
        else
            iDebug = 0
        end
    end       
    if
        iDebug == 1
    then
        print_formatted_now "FarClip: %f, FogStart: %f, Weathertype: %d, Weather: %s" 200 fFarClip fFogStart iWeatherType sWeather
    end
    
    get_current_weather i1
    if
        i1 <> iWeatherType
    then
        iLastWeather = iWeather
        iWeather = Sunny
        if or
            i1 == 4     // Cloudy LA
            i1 == 7     // Cloudy SF
            i1 == 12    // Cloudy LV
            i1 == 15    // Cloudy CS
        then
            iWeather = Cloudy
        else
            if or
                i1 == 8     // Rainy SF
                i1 == 16    // Rainy CS
            then
                iWeather = Rainy
            else
                if
                    i1 == 9     // Foggy SF
                then
                    iWeather = Foggy
                else
                    if
                        i1 == 19    // Sandstorm
                    then
                        iWeather = Sandstorm
                    end
                end
            end
        end
        gosub @GetWeatherName
        if
            iWeathertype == -2
        then
            iLastWeather = iWeather
        end
        if and
            TIMERA > 10000
            fFarClipMult[iWeather] <> fFarClipMult[iLastWeather]
        then
            TIMERA = -10000
        end
        if and
            TIMERB > 10000
            fFogStartMult[iWeather] <> fFogStartMult[iLastWeather]
        then
            TIMERB = -10000
        end
        iWeatherType = i1
    end
    
    get_active_camera_coordinates f1 f2 fZ
       
    get_area_visible i1
       
    if
        i1 == 0
    then
        if
            iActiveFlag <> 1
        jf continue
        if or
            fZ < 900.0
            iActiveFlag == -1
            is_char_really_in_air $PLAYER_ACTOR
        jf continue
        iActiveFlag = 1
        continue
    end
    if or
        fZ > 900.0
        iActiveFlag == -1
    jf continue
    iActiveFlag = 0
end


:SetDrawDistance
read_memory fFarClip FarClipAddr size 4 virtual_protect 0
read_memory fFogStart FogStartAddr size 4 virtual_protect 0
if
    iActiveFlag == 1
then
    if or
        fFarClipMult[iWeather] <> 1.0
        TIMERA < 0
        fHeightFactor > 0.0
    then
        if or
            fFarClipMult[iWeather] <> 1.0
            TIMERA < 0
        then
            if
                TIMERA >= 0
            then
                fFarClip *= fFarClipMult[iWeather]
            else
                f1 = fFarClipMult[iLastWeather]
                f1 -= fFarClipMult[iWeather]
                f1 /= -10000.0
                cset_lvar_float_to_lvar_int f2 = TIMERA
                f1 *= f2
                f2 = fFarClipMult[iWeather]
                f1 += f2
                fFarClip *= f1
            end
        end
        if
            fHeightFactor > 0.0
        then    
            if
                fZ < 250.0
            then
                if and
                    fZ > 150.0
                    fFarClip < fMinDistHeight
                then
                    fZ -= 150.0
                    f1 = fMinDistHeight
                    f1 -= fFarClip 
                    f1 /= 100.0
                    f1 *= fZ
                    fFarClip += f1
                end
            else
                clamp_float fFarClip min fMinDistHeight max fMaxDist store_to fFarClip
                cset_lvar_float_to_lvar_int f2 = iWeather
                pow f1 0.9 f2
                fZ -= 250.0
                f1 *= fHeightFactor
                f1 *= fZ     
                fFarClip += f1
            end
        end
        clamp_float fFarClip min fMinDistGround[iWeather] max fMaxDist store_to fFarClip   
        write_memory FarClipAddr size 4 value fFarClip virtual_protect 0
    end
    if or
        fFogStartMult[iWeather] <> 1.0
        TIMERB < 0
    then
        if
            TIMERB >= 0
        then
            fFogStart *= fFogStartMult[iWeather]
        else
            f1 = fFogStartMult[iLastWeather]
            f1 -= fFogStartMult[iWeather]
            f1 /= -10000.0
            cset_lvar_float_to_lvar_int f2 = TIMERB
            f1 *= f2
            f2 = fFogStartMult[iWeather]
            f1 += f2
            fFogStart *= f1
        end
        write_memory FogStartAddr size 4 value fFogStart virtual_protect 0
    end
end
return_script_event


:GetWeatherName
if
    iWeather == Sunny
then
    sWeather = 'Sun'
end
if
    iWeather == Cloudy
then
    sWeather = 'Cloud'
end
if
    iWeather == Rainy
then
    sWeather = 'Rain'
end
if
    iWeather == Foggy
then
    sWeather = 'Fog'
end
if
    iWeather == Sandstorm
then
    sWeather = 'Sand'
end
return


:ILoveMyGirlfriendJessica
TIMERA = 0

repeat
    wait 0
    if
        0@ == 1
    then
        print_formatted_now "Draw Distance Tweaker: CLEO+ 1.2.0 or newer required. It's a plugin for CLEO. Please download and install it." time 200
    else
        print_formatted_now "Draw Distance Tweaker: Failed to find the file 'DrawDistanceTweaker.ini'. Check the file path or file name." time 200
    end
    if and
        is_player_control_on $PLAYER_CHAR
        not fading
    jf @ILoveMyGirlfriendJessica
until TIMERA > 10000          

terminate_this_custom_script