-- This file should be in your Game folder (usually C:\Program Files (x86)\Steam\steamapps\common\ELDEN RING\Game) + quickDodge. This file path should be C:\Program Files (x86)\Steam\steamapps\common\ELDEN RING\Game\quickDodge\quickDodge.lua

BACKSTEP_REPLACE_BACKWARDS_DODGE = FALSE -- When dodging backwards, backstep instead.
SPRINTING_R2_ON_BACKSTEP_R2 = FALSE -- When pressing R2 from a backstep, execute the sprinting R2 instead of standing R2.
INSTANT_SPRINTING_FROM_DODGE = TRUE -- If you hold the dodge button from a dodge, you exit it in a sprint.
SPRINTING_ATTACKS_WHILE_HOLDING_DODGE = TRUE -- If you hold the dodge button from a dodge, instead of queueing a dodge attack, you queue a sprinting attack
FAST_DODGE_R2 = FALSE -- When exiting from a dodge, the R2 attack is noticeably faster
FIRST_R1_CHAINS_TO_SECOND_R2 = FALSE -- Your standing R1 chains to your second standing R2 instead of the first standing R2.
DODGE_R1_CHAINS_TO_SECOND_R2 = FALSE
SPRINT_R1_CHAINS_TO_SECOND_R2 = FALSE
BACKSTEP_R1_CHAINS_TO_SECOND_R2 = FALSE
SPRINT_R2_CHAINS_TO_SECOND_R2 = FALSE

DODGE_CANCEL_GRACE_PERIOD = 2.3 -- Measured in seconds. Can dodge cancel out of attacks within the first few seconds here.

CUSTOM_WEIGHT_OVERRIDE = -1 -- -1 for inactive, 1 for Light, 2 for Medium, 3 for Heavy, 4 for Overweight

-- If you see this message you have the Normal version.







-- Table to hold the backup functions
rawset(_G, "backupEnv", {})

-- Function to create a replacement
function createReplacement(functionName, originalFunction, newFunction)
    -- Check if the function already exists in the backup environment using rawget
    if rawget(backupEnv, functionName) == nil then
        -- Store the original function in the backup environment
        backupEnv[functionName] = originalFunction
    end
    
    -- Create a wrapper that calls the new function
    return function(...)
        -- Call the new function
        return newFunction(...)
    end
end

-- Function to restore the original function
function restoreFunction(functionName)
    -- Check if the backup environment has the original function using rawget
    local originalFunction = rawget(backupEnv, functionName)
    if originalFunction then
        -- Return the original function
        return originalFunction
    elseif rawget(_G, functionName) then
        return rawget(_G, functionName)
    else
        return nil
    end
end

-- Function to create a detour
function createDetour(originalFunction, newFunction)
    -- Store the original function in the new function's environment
    local original = originalFunction
    -- Create a wrapper that calls the new function first, then the original function
    return function(...)
        -- Call the new function
        newFunction(...)
        -- Call the original function
        return original(...)
    end
end

-- Function to create a detour
function createPostDetour(originalFunction, newFunction)
    -- Store the original function in the new function's environment
    local original = originalFunction
    -- Create a wrapper that calls the new function first, then the original function
    return function(...)
        -- Call the original function
        local originalResult = original(...)
        -- Call the new function
        newFunction(...)
        return originalResult
    end
end

function customWeightOverride()
    if CUSTOM_WEIGHT_OVERRIDE == -1 then
        return
    end
    if CUSTOM_WEIGHT_OVERRIDE == 1 then
        SetVariable("MoveWeightIndex", MOVE_WEIGHT_LIGHT)
        SetVariable("EvasionWeightIndex", EVASION_WEIGHT_INDEX_LIGHT)
    elseif CUSTOM_WEIGHT_OVERRIDE == 2 then
        SetVariable("MoveWeightIndex", MOVE_WEIGHT_NORMAL)
        SetVariable("EvasionWeightIndex", EVASION_WEIGHT_INDEX_MEDIUM)
    elseif CUSTOM_WEIGHT_OVERRIDE == 3 then
        SetVariable("MoveWeightIndex", MOVE_WEIGHT_HEAVY)
        SetVariable("EvasionWeightIndex", EVASION_WEIGHT_INDEX_HEAVY)
    elseif CUSTOM_WEIGHT_OVERRIDE == 4 then
        SetVariable("MoveWeightIndex", MOVE_WEIGHT_HEAVY)
        SetVariable("EvasionWeightIndex", EVASION_WEIGHT_INDEX_OVERWEIGHT)
    end
end

SetWeightIndex = createPostDetour(SetWeightIndex, customWeightOverride)

rawset(_G, "sprintToggle", false)
rawset(_G, "prevL3", false)
rawset(_G, "wasMoving", false)
rawset(_G, "stopTime", 0)

function rebindSprint()

    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end

    -- Cancelar sprint si no hay stamina
    if env(GetStamina) <= 0 then
        sprintToggle = false
        return
    end

    local l3Now = env(ActionDuration, ACTION_ARM_L3) > 0
    local dodgeNow = env(ActionDuration, ACTION_ARM_DODGE) > 0
    local moveLevel = GetVariable("MoveSpeedLevel")
    local isLocked = env(IsLocked) == TRUE

    ----------------------------------------------------------------
    -- AUTO-CANCEL POR FIJAR OBJETIVO (LOCK-ON)
    ----------------------------------------------------------------

    if isLocked then
        sprintToggle = false
    end

    ----------------------------------------------------------------
    -- TOGGLE SOLO SI NO HAY LOCK-ON
    ----------------------------------------------------------------

    -- Toggle SOLO en el pulso de L3, sin dodge, y sin lock-on
    if l3Now and not prevL3 and not dodgeNow and not isLocked then
        sprintToggle = not sprintToggle
    end

    prevL3 = l3Now

    ----------------------------------------------------------------
    -- AUTO-CANCEL POR PARAR MOVIMIENTO
    ----------------------------------------------------------------

    if moveLevel > 0.05 then
        wasMoving = true
        stopTime = 0
    else
        if wasMoving then
            if stopTime == 0 then
                stopTime = os.clock()
            elseif os.clock() - stopTime > 0.04 then
                sprintToggle = false
                wasMoving = false
            end
        end
    end

    ----------------------------------------------------------------
    -- AUTO-CANCEL POR ACCIONES
    ----------------------------------------------------------------

	if env(ActionRequest, ACTION_ARM_R1) == TRUE
		or env(ActionRequest, ACTION_ARM_R2) == TRUE
		or env(ActionRequest, ACTION_ARM_SP_MOVE) == TRUE
		or env(ActionRequest, ACTION_ARM_BACKSTEP) == TRUE then

		sprintToggle = false
		wasMoving = false
		stopTime = 0
	end
	if c_IsStealth == TRUE then
		wasMoving = false
		stopTime = 0
	end

    ----------------------------------------------------------------
    -- APLICAR SPRINT
    ----------------------------------------------------------------

    if sprintToggle and moveLevel > 0.05 then
        act(2002, 100220)
    end
end

Update = createPostDetour(Update, rebindSprint)

rawset(_G, "g_EventsLog", { ["CMSG"] = "", ["TIME"] = 0 })

function LogEventsAndTiming(state)
    g_EventsLog["CMSG"] = state
    g_EventsLog["TIME"] = os.clock()
end

ExecEvent = createDetour(ExecEvent, LogEventsAndTiming)

ACTION_ARM_L3_BACK = ACTION_ARM_L3

function SprintCancelOnAttack(state)

    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end

    if sprintToggle ~= true then return end
    if type(state) ~= "string" then return end

    if string.find(state, "Attack") then
        sprintToggle = false
    end
end

ExecEvent = createPostDetour(ExecEvent, SprintCancelOnAttack)

rawset(_G, "g_LastL3Press", 0)
rawset(_G, "g_LastDodgePress", 0)
rawset(_G, "g_CrouchBlocked", false)

function ExecEvasionMonkeyPatchPre()

    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end

    if c_HasActionRequest == FALSE then
        return FALSE
    end

    -- Trackear inputs para detectar L3 + Dodge de forma más confiable
    local l3Pressed = env(ActionRequest, ACTION_ARM_L3) == TRUE
    local dodgePressed = env(ActionRequest, ACTION_ARM_SP_MOVE) == TRUE or env(ActionDuration, ACTION_ARM_SP_MOVE) > 0
    local currentTime = os.clock()
    
    -- Actualizar timestamps
    if l3Pressed then
        g_LastL3Press = currentTime
    end
    if dodgePressed then
        g_LastDodgePress = currentTime
    end
    
    -- Bloquear crouch durante ataques y por un tiempo después
    local isRecentAttack = (currentTime - g_EventsLog["TIME"] < 0.4) and 
                           (string.find(g_EventsLog["CMSG"] or "", "ttack") or 
                            string.find(g_EventsLog["CMSG"] or "", "olling"))
    
    g_CrouchBlocked = isRecentAttack
    
    -- Solo permitir crouch si L3 Y Dodge están presionados simultáneamente
    -- (dentro de 0.15 segundos uno del otro) Y no hay ataques recientes
    local simultaneousPress = math.abs(g_LastL3Press - g_LastDodgePress) < 0.15
    local bothActive = l3Pressed and dodgePressed
    
    if bothActive and simultaneousPress and not g_CrouchBlocked then
        if c_IsStealth == FALSE then
            StealthTransitionIndexUpdate()
            ExecEvent("W_Stealth_to_Stealth_Idle")
            c_HasActionRequest = TRUE
            return TRUE
        elseif c_IsStealth == TRUE then
            StealthTransitionIndexUpdate()
            ExecEvent("W_Stealth_to_Idle")
            c_HasActionRequest = TRUE
            return TRUE
        end
    end

    -- Solo bloquear L3 si realmente necesitamos evitar el crouch
    ACTION_ARM_L3 = 666

end

function ExecEvasionMonkeyPatchPost()

    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end

    ACTION_ARM_L3 = ACTION_ARM_L3_BACK

end

-- TK NOTE: REBIND CROUCH TO L3 + DODGE
ExecEvasion = createDetour(ExecEvasion, ExecEvasionMonkeyPatchPre)
ExecEvasion = createPostDetour(ExecEvasion, ExecEvasionMonkeyPatchPost)

function fixDodges()
    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end
    
    c_RollingAngle = GetVariable("MoveAngle")
    c_ArtsRollingAngle = GetVariable("MoveAngle")
end

-- TK NOTE: USE MOVE ANGLE (Stick/WASD) for dodge dir instead of RollAngle (the direction your character is moving which only updates sometimes)
GetConstVariable = createPostDetour(GetConstVariable, fixDodges)


rawset(_G, "canUpdateSelf", true)

function checkForUpdates()

    -- Solo ejecutar para el jugador, no para NPCs
    if env(IsCOMPlayer) == TRUE then
        return
    end

    if (env(ActionDuration, ACTION_ARM_SP_MOVE) > 15000) and (env(ActionDuration, ACTION_ARM_R1) > 15000) and (env(ActionDuration, ACTION_ARM_L1) > 15000) and canUpdateSelf then
        updateSelf()
        canUpdateSelf = false
        act(1000, -100000)
    end

end

function updateSelf()
    -- Define the URL of the Lua script to download
    local url = "https://raw.githubusercontent.com/FWang1221/ERDodgeAndOtherShittyThings/master/quickDodge.lua"
    -- Define the command to download the file using curl (cross-platform)
    local download_command = 'curl -o quickDodge/quickDodge.lua ' .. url

    -- Download the Lua script using os.execute
    os.execute(download_command)
end

-- TK NOTE: CHECK MOD FOR UPDATES AND UPDATE WHEN YOU HOLD R1 + L1 + Dodge for 15 seconds
Update = createDetour(Update, checkForUpdates)

-- TK NOTE: This replaces GetEvasionRequestCustom in c0000.hks, you can compare the two to see what's different
function GetEvasionRequestCustom()

    -- Solo ejecutar versión custom para el jugador, usar original para NPCs
    if env(IsCOMPlayer) == TRUE then
        return backupEnv["GetEvasionRequest"]()
    end

    local dodgeDecider = FALSE

    -- TK NOTE: ACTION_ARM_SP_MOVE is dodge PRESS, ACTION_ARM_ROLLING is normal base game dodge (release+direction)
    dodgeDecider = env(ActionRequest, ACTION_ARM_ROLLING)
    -- TK NOTE: This handles dodge cancelling an attack
    if (os.clock() - g_EventsLog["TIME"] < DODGE_CANCEL_GRACE_PERIOD) and (string.find(g_EventsLog["CMSG"], "ttack")) then
        if env(ActionDuration, ACTION_ARM_SP_MOVE) > 0 then
            dodgeDecider = TRUE
            -- TK NOTE: Added this to go back to stick based dodge direction when attack cancelling since roll direction doesn't update during an attack
            c_RollingAngle = GetVariable("MoveAngle")
            c_ArtsRollingAngle = GetVariable("MoveAngle")
        end
    end

    local move_angle = GetVariable("MoveAngle")
    local stick_level = GetVariable("MoveSpeedLevel")


    if env(GetStamina) < STAMINA_MINIMUM or env(ActionDuration, ACTION_ARM_L3) > 0 then
        return ATTACK_REQUEST_INVALID
    end
    if (dodgeDecider == TRUE and stick_level > 0.05) then
        if (move_angle > 135 or move_angle < -135) and BACKSTEP_REPLACE_BACKWARDS_DODGE == TRUE then
            return ATTACK_REQUEST_BACKSTEP
        else
            return ATTACK_REQUEST_ROLLING
        end
    elseif env(ActionDuration, ACTION_ARM_L1) > 0 then
        if env(ActionRequest, ACTION_ARM_EMERGENCYSTEP) == TRUE then
            if env(IsEmergencyEvasionPossible, 0) == TRUE or env(IsEmergencyEvasionPossible, 1) == TRUE then
                return ATTACK_REQUEST_EMERGENCYSTEP
            end
        elseif env(ActionRequest, ACTION_ARM_BACKSTEP) == TRUE then
            return ATTACK_REQUEST_BACKSTEP
        else
            return ATTACK_REQUEST_INVALID
        end
    elseif env(ActionRequest, ACTION_ARM_BACKSTEP) == TRUE then
        return ATTACK_REQUEST_BACKSTEP
    end
    return ATTACK_REQUEST_INVALID
end

function DefaultBackStep_onUpdateCustom()
    act(DisallowAdditiveTurning, TRUE)

    if EvasionCommonFunction(FALL_TYPE_DEFAULT, "W_AttackRightBackstep", "W_AttackRightHeavyDash",
        "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothBackstep", "W_AttackBothHeavyDash",
        QUICKTYPE_BACKSTEP) == TRUE then
        return
    end
end

function Rolling_onUpdateCustom()
    act(DisallowAdditiveTurning, TRUE)
    SetThrowAtkInvalid()

    if env(GetSpEffectID, 100390) == TRUE then
        ResetDamageCount()
    end

    SetEnableAimMode()

    if SPRINTING_ATTACKS_WHILE_HOLDING_DODGE == TRUE and env(ActionDuration, ACTION_ARM_SP_MOVE) > 0 then

        if EvasionCommonFunction(FALL_TYPE_DEFAULT, "W_AttackRightLightDash", "W_AttackRightHeavyDash",         "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothDash", "W_AttackBothHeavyDash", QUICKTYPE_ROLLING) == TRUE then
            return
        end
    elseif FAST_DODGE_R2 == TRUE then

        if EvasionCommonFunction(FALL_TYPE_DEFAULT, "W_AttackRightLightStep", "W_AttackRightHeavy1End",         "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLightStep", "W_AttackBothHeavy1End", QUICKTYPE_ROLLING) == TRUE then
            return
        end
    else
        if EvasionCommonFunction(FALL_TYPE_DEFAULT, "W_AttackRightLightStep", "W_AttackRightHeavy1Start",         "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLightStep", "W_AttackBothHeavy1Start", QUICKTYPE_ROLLING) == TRUE then
            return
        end
    end

    if env(IsAnimEnd, 1) == TRUE then
        ExecEventAllBody("W_Idle")
        return
    end
    SetRollingTurnCondition(FALSE)

    if env(ActionDuration, ACTION_ARM_SP_MOVE) > 0 and SPRINTING_ATTACKS_WHILE_HOLDING_DODGE == TRUE then
        SetVariable("ToggleDash", 1)
    end
end

function AttackRightLight1_onUpdateCustom()
    local r1 = "W_AttackRightLight2"

    if g_ComboReset == TRUE then
        r1 = "W_AttackRightLight1"
    end
    if FIRST_R1_CHAINS_TO_SECOND_R2 == TRUE then
        if AttackCommonFunction(r1, "W_AttackRightHeavy2Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
            "W_AttackBothLight2", "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
            return
        end
    else
        if AttackCommonFunction(r1, "W_AttackRightHeavy1Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
            "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE, 1) == TRUE then
            return
        end
    end
end

function AttackBothLight1_onUpdateCustom()
    local b1 = "W_AttackBothLight2"
    if g_ComboReset == TRUE then
        b1 = "W_AttackBothLight1"
    end
    if FIRST_R1_CHAINS_TO_SECOND_R2 == TRUE then
        if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy2Start", "W_AttackBothLeft2",
            "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
            return
        end
    else
        if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1Start", "W_AttackBothLeft2",
            "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy1Start", FALSE, TRUE, 1) == TRUE then
            return
        end

    end
end

function AttackRightLightStep_onUpdateCustom()
    local r1 = "W_AttackRightLightSubStart"
    if g_ComboReset == TRUE then
        r1 = "W_AttackRightLight1"
    end
    if AttackCommonFunction(r1, "W_AttackRightHeavy2Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
        "W_AttackBothLight1", "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackBothLightStep_onUpdateCustom()
    local b1 = "W_AttackBothLightSubStart"
    if g_ComboReset == TRUE then
        b1 = "W_AttackBothLight1"
    end
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy2Start", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackRightLightDash_onUpdateCustom()
    local r1 = "W_AttackRightLightSubStart"
    if g_ComboReset == TRUE then
        r1 = "W_AttackRightLight1"
    end
    if AttackCommonFunction(r1, "W_AttackRightHeavy2Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
        "W_AttackBothLight1", "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackBothDash_onUpdateCustom()
    local b1 = "W_AttackBothLightSubStart"
    if g_ComboReset == TRUE then
        b1 = "W_AttackBothLight1"
    end
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy2Start", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackRightBackstep_onUpdateCustom()
    local r1 = "W_AttackRightLightSubStart"
    if g_ComboReset == TRUE then
        r1 = "W_AttackRightLight1"
    end
    if AttackCommonFunction(r1, "W_AttackRightHeavy2Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
        "W_AttackBothLight1", "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackBothBackstep_onUpdateCustom()
    local b1 = "W_AttackBothLightSubStart"
    if g_ComboReset == TRUE then
        b1 = "W_AttackBothLight1"
    end
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy2Start", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackRightHeavyDash_onUpdateCustom()
    local r1 = "W_AttackRightLightSubStart"
    if g_ComboReset == TRUE then
        r1 = "W_AttackRightLight1"
    end
    if AttackCommonFunction(r1, "W_AttackRightHeavy2Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1",
        "W_AttackBothLight1", "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

function AttackBothHeavyDash_onUpdateCustom()
    local b1 = "W_AttackBothLightSubStart"
    if g_ComboReset == TRUE then
        b1 = "W_AttackBothLight1"
    end
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy2Start", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", b1, "W_AttackBothHeavy2Start", FALSE, TRUE, 1) == TRUE then
        return
    end
end

-- ============================================================================
-- NUEVAS FUNCIONES PARA ATAQUES DE MANO IZQUIERDA (L1/L2)
-- ============================================================================

function AttackLeftLight1_onUpdateCustom()
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1Start", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, TRUE, 0) == TRUE then
        return
    end
end

function AttackLeftHeavy1_onUpdateCustom()
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1Start", nil, "W_AttackLeftHeavy2",
        "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, TRUE, 0) == TRUE then
        return
    end
end

-- ============================================================================

--TK NOTE: Replace the default game evasion request with the custom one. Mostly just affects dodge on press and attack dodge cancelling
GetEvasionRequest = createReplacement("GetEvasionRequest", GetEvasionRequest, GetEvasionRequestCustom)

rawset(_G, "g_LastSettingsCheckTime", 0)

-- TK NOTE: This bit reads the settings file and enables most of the stuff there by replacing the base game functions/logic with these ones
-- Might be able to figure out what's going on here more easily by comparing the custom functions to the non-custom ones found in c0000.hks I haven't looked at these yet myself
function applySettings()

    if SPRINTING_R2_ON_BACKSTEP_R2 == TRUE then
        DefaultBackStep_onUpdate = createReplacement("DefaultBackStep_onUpdate", DefaultBackStep_onUpdate, DefaultBackStep_onUpdateCustom)
    else
        DefaultBackStep_onUpdate = restoreFunction("DefaultBackStep_onUpdate")
    end

    -- Do not override the game's `Rolling_onUpdate` so HKS rolling handlers
    -- (including SPEED_COMBO logic) run. The mod's custom rolling override
    -- forced a constant step attack and prevented combo follow-ups.
    Rolling_onUpdate = restoreFunction("Rolling_onUpdate")

    if FIRST_R1_CHAINS_TO_SECOND_R2 == TRUE then
        AttackRightLight1_onUpdate = createReplacement("AttackRightLight1_onUpdate", AttackRightLight1_onUpdate, AttackRightLight1_onUpdateCustom)
        AttackBothLight1_onUpdate = createReplacement("AttackBothLight1_onUpdate", AttackBothLight1_onUpdate, AttackBothLight1_onUpdateCustom)
    else
        AttackRightLight1_onUpdate = restoreFunction("AttackRightLight1_onUpdate")
        AttackBothLight1_onUpdate = restoreFunction("AttackBothLight1_onUpdate")
    end

    if DODGE_R1_CHAINS_TO_SECOND_R2 == TRUE then
        AttackRightLightStep_onUpdate = createReplacement("AttackRightLightStep_onUpdate", AttackRightLightStep_onUpdate, AttackRightLightStep_onUpdateCustom)
        AttackBothLightStep_onUpdate = createReplacement("AttackBothLightStep_onUpdate", AttackBothLightStep_onUpdate, AttackBothLightStep_onUpdateCustom)
    else
        AttackRightLightStep_onUpdate = restoreFunction("AttackRightLightStep_onUpdate")
        AttackBothLightStep_onUpdate = restoreFunction("AttackBothLightStep_onUpdate")
    end

    if SPRINT_R1_CHAINS_TO_SECOND_R2 == TRUE then
        AttackRightLightDash_onUpdate = createReplacement("AttackRightLightDash_onUpdate", AttackRightLightDash_onUpdate, AttackRightLightDash_onUpdateCustom)
        AttackBothDash_onUpdate = createReplacement("AttackBothDash_onUpdate", AttackBothDash_onUpdate, AttackBothDash_onUpdateCustom)
    else
        AttackRightLightDash_onUpdate = restoreFunction("AttackRightLightDash_onUpdate")
        AttackBothDash_onUpdate = restoreFunction("AttackBothDash_onUpdate")
    end

    if BACKSTEP_R1_CHAINS_TO_SECOND_R2 == TRUE then
        AttackRightBackstep_onUpdate = createReplacement("AttackRightBackstep_onUpdate", AttackRightBackstep_onUpdate, AttackRightBackstep_onUpdateCustom)
        AttackBothBackstep_onUpdate = createReplacement("AttackBothBackstep_onUpdate", AttackBothBackstep_onUpdate, AttackBothBackstep_onUpdateCustom)
    else
        AttackRightBackstep_onUpdate = restoreFunction("AttackRightBackstep_onUpdate")
        AttackBothBackstep_onUpdate = restoreFunction("AttackBothBackstep_onUpdate")
    end

    if SPRINT_R2_CHAINS_TO_SECOND_R2 == TRUE or true then
        AttackRightHeavyDash_onUpdate = createReplacement("AttackRightHeavyDash_onUpdate", AttackRightHeavyDash_onUpdate, AttackRightHeavyDash_onUpdateCustom)
        AttackBothHeavyDash_onUpdate = createReplacement("AttackBothHeavyDash_onUpdate", AttackBothHeavyDash_onUpdate, AttackBothHeavyDash_onUpdateCustom)
    else
        AttackRightHeavyDash_onUpdate = restoreFunction("AttackRightHeavyDash_onUpdate")
        AttackBothHeavyDash_onUpdate = restoreFunction("AttackBothHeavyDash_onUpdate")
    end

    -- ============================================================================
    -- APLICAR FUNCIONES PERSONALIZADAS PARA ATAQUES DE MANO IZQUIERDA
    -- ============================================================================
    -- Siempre activas para permitir dodge cancel en ataques L1/L2
    AttackLeftLight1_onUpdate = createReplacement("AttackLeftLight1_onUpdate", AttackLeftLight1_onUpdate, AttackLeftLight1_onUpdateCustom)
    AttackLeftHeavy1_onUpdate = createReplacement("AttackLeftHeavy1_onUpdate", AttackLeftHeavy1_onUpdate, AttackLeftHeavy1_onUpdateCustom)
    -- ============================================================================
end

function checkSettings()

    if os.clock() - g_LastSettingsCheckTime > 1 then
        g_LastSettingsCheckTime = os.clock()
    else
        return
    end

    local filePath = "quickDodge/quickDodgeSettings.txt"
    -- Function to check if a file exists
    local function fileExists(file)
        local f = io.open(file, "r")
        if f then
            io.close(f)
            return true
        else
            return false
        end
    end

    -- Function to run the file
    local function runFile(file)
        pcall(loadfile(file))
    end

    -- Function to create the file with specified content
    local function createFile(file, content)
        local f = io.open(file, "w")
        f:write(content)
        io.close(f)
    end

    -- Main logic
    if fileExists(filePath) then
        runFile(filePath)
        success, error = pcall(applySettings())
        if not success then
            createFile("quickDodge/errors.txt", error)
        end
    else
        createFile(filePath,
[[    

BACKSTEP_REPLACE_BACKWARDS_DODGE = FALSE -- When dodging backwards, backstep instead.
SPRINTING_R2_ON_BACKSTEP_R2 = FALSE -- When pressing R2 from a backstep, execute the sprinting R2 instead of standing R2.
INSTANT_SPRINTING_FROM_DODGE = TRUE -- If you hold the dodge button from a dodge, you exit it in a sprint.
SPRINTING_ATTACKS_WHILE_HOLDING_DODGE = TRUE -- If you hold the dodge button from a dodge, instead of queueing a dodge attack, you queue a sprinting attack
FAST_DODGE_R2 = TRUE -- When exiting from a dodge, the R2 attack is noticeably faster
FIRST_R1_CHAINS_TO_SECOND_R2 = FALSE -- Your standing R1 chains to your second standing R2 instead of the first standing R2.
DODGE_R1_CHAINS_TO_SECOND_R2 = FALSE
SPRINT_R1_CHAINS_TO_SECOND_R2 = FALSE
BACKSTEP_R1_CHAINS_TO_SECOND_R2 = FALSE
SPRINT_R2_CHAINS_TO_SECOND_R2 = FALSE

DODGE_CANCEL_GRACE_PERIOD = 0.3 -- Measured in seconds. Can dodge cancel out of attacks within the first few seconds here.

CUSTOM_WEIGHT_OVERRIDE = -1 -- -1 for inactive, 1 for Light, 2 for Medium, 3 for Heavy, 4 for Overweight
]])
    end
end

Update = createDetour(Update, checkSettings)
