05-05-26
Advanced Recoil Control System (RCS) Script
This Lua script provides a customizable recoil compensation system for hardware input, allowing users to fine-tune their recoil control. It includes settings for different weapons and firing modes.
Question
I need a Rebind Lua script for recoil compensation. Here's what I have so far:
-- rebind: name=RCS FPS2
-- rebind: tick_rate=1000
local cfg = UI.Schema({
enabled = UI.Toggle(true, { label = "RCS Enabled" }),
-- Slot 1
slot1_ar_rcs = UI.Slider(1.4, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 1 AR", group = "Slot 1" }),
slot1_smg_rcs = UI.Slider(1.2, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 1 SMG", group = "Slot 1" }),
slot1_mode = UI.Select("AR", { "AR", "SMG" }, { label = "Slot 1 Weapon", group = "Slot 1" }),
slot1_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 1", group = "Slot 1" }),
-- Slot 2
slot2_sks_pull = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 2 SKS Pull", group = "Slot 2" }),
slot2_smg_rcs = UI.Slider(1.2, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 2 SMG", group = "Slot 2" }),
slot2_mode = UI.Select("SKS", { "SKS", "SMG" }, { label = "Slot 2 Weapon", group = "Slot 2" }),
slot2_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 2", group = "Slot 2" }),
-- Slot 3 (Pistol)
slot3_sks_pull = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 3 SKS Pull", group = "Slot 3" }),
slot3_mode = UI.Select("Continuous", { "Continuous", "SKS Kick" }, { label = "Slot 3 Fire Mode", group = "Slot 3" }),
slot3_rcs = UI.Slider(1, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 3 RCS", group = "Slot 3" }),
slot3_kick = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 3 Kick", group = "Slot 3" }),
slot3_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 3", group = "Slot 3" }),
-- General
half_mult = UI.Slider(50, { min = 10, max = 90, suffix = "%", label = "Half Speed Multiplier", group = "General" }),
max_duration = UI.Slider(2500, { min = 500, max = 6000, suffix = "ms", label = "Max RCS Duration", group = "General" }),
-- Stability
decay_factor = UI.Slider(0.92, { min = 0.80, max = 0.99, step = 0.01, label = "RCS Decay", tooltip = "Higher = smoother tapoff", group = "General" }),
min_output = UI.Slider(0.1, { min = 0.0, max = 1.0, step = 0.1, label = "Min Output", tooltip = "Prevents micro-jitters", group = "General" }),
-- Rapid Fire
rf_interval = UI.Slider(10, { min = 1, max = 50, step = 1, suffix = "ms", label = "Rapid Fire Interval", group = "Rapid Fire" }),
rf_hold_ms = UI.Slider(5, { min = 1, max = 50, step = 1, suffix = "ms", label = "Rapid Fire Hold", group = "Rapid Fire" }),
})
local active_slot = 1
local half_rcs = false
local rcs_task = nil
local function get_rcs_value()
if active_slot == 1 then
return cfg.slot1_mode == "AR" and cfg.slot1_ar_rcs or cfg.slot1_smg_rcs
elseif active_slot == 2 then
return cfg.slot2_smg_rcs
else
return cfg.slot3_rcs
end
end
local function get_rcs_multiplier()
return half_rcs and (cfg.half_mult / 100) or 1.0
end
local function is_sks_mode()
return (active_slot == 2 and cfg.slot2_mode == "SKS") or
(active_slot == 3 and cfg.slot3_mode == "SKS Kick")
end
local function is_rapid_fire()
if active_slot == 1 then return cfg.slot1_rf end
if active_slot == 2 then return cfg.slot2_rf end
if active_slot == 3 then return cfg.slot3_rf end
return false
end
local function stop_rcs_task()
if rcs_task and rcs_task:IsRunning() then
rcs_task:Cancel()
rcs_task = nil
end
end
function OnDown(key)
local shift = Input.IsDown("LShift") or Input.IsDown("RShift")
local ctrl = Input.IsDown("LCtrl") or Input.IsDown("RCtrl")
-- RCS on/off: Shift+5
if key == "5" and shift and not ctrl then
cfg.enabled = not cfg.enabled
UI.Notify(cfg.enabled and "RCS ON" or "RCS OFF", "info")
return false
end
-- Slot 1 mode: Ctrl+5
if key == "5" and ctrl and not shift then
cfg.slot1_mode = cfg.slot1_mode == "AR" and "SMG" or "AR"
UI.Notify("Slot 1: " .. cfg.slot1_mode, "info")
return false
end
-- Slot 2 mode: Ctrl+6
if key == "6" and ctrl and not shift then
cfg.slot2_mode = cfg.slot2_mode == "SKS" and "SMG" or "SKS"
UI.Notify("Slot 2: " .. cfg.slot2_mode, "info")
return false
end
-- Slot 3 mode: Ctrl+7
if key == "7" and ctrl and not shift then
cfg.slot3_mode = cfg.slot3_mode == "Continuous" and "SKS Kick" or "Continuous"
UI.Notify("Slot 3: " .. cfg.slot3_mode, "info")
return false
end
-- Slot selection
if not ctrl and not shift then
if key == "1" or key == "Q" then
active_slot = 1
stop_rcs_task()
UI.Notify("Slot 1 (" .. cfg.slot1_mode .. ")", "info")
elseif key == "2" then
active_slot = 2
stop_rcs_task()
UI.Notify("Slot 2 (" .. cfg.slot2_mode .. ")", "info")
elseif key == "3" then
active_slot = 3
stop_rcs_task()
UI.Notify("Slot 3 (" .. cfg.slot3_mode .. ")", "info")
end
end
-- Half-speed toggle
if key == "Z" then
half_rcs = not half_rcs
UI.Notify(half_rcs and "Half RCS ON" or "Half RCS OFF", "info")
return true
end
-- Half-speed off
if (key == "C" or key == "LCtrl" or key == "RCtrl") and half_rcs then
half_rcs = false
UI.Notify("Half RCS OFF", "info")
return true
end
-- Mouse1 with right-click held: activate RCS
if key == "Mouse1" and cfg.enabled and Input.IsDown("Mouse2") then
-- Rapid Fire
if is_rapid_fire() then
stop_rcs_task()
local mult = get_rcs_multiplier()
local rcs_val = get_rcs_value() * mult
rcs_task = Run(function()
local rcs_accum = 0
while Input.IsDown("Mouse1") and Input.IsDown("Mouse2") and cfg.enabled do
HID.Down("Mouse1")
Sleep(cfg.rf_hold_ms)
HID.Up("Mouse1")
rcs_accum = rcs_accum + rcs_val
local pixels = math.floor(rcs_accum + 0.5)
if math.abs(pixels) >= cfg.min_output then
HID.Move(0, pixels)
rcs_accum = rcs_accum - pixels
end
Sleep(cfg.rf_interval)
end
end)
return false
end
-- SKS single shot
if is_sks_mode() then
local mult = get_rcs_multiplier()
local pull_value = active_slot == 2 and cfg.slot2_sks_pull or cfg.slot3_sks_pull
local pixels = math.floor(pull_value * mult)
if pixels > 0 then
HID.Move(0, pixels)
end
return true
end
-- Continuous RCS (AR/SMG) with exponential decay
stop_rcs_task()
local mult = get_rcs_multiplier()
local rcs_base = get_rcs_value() * mult
local start_time = System.Time()
rcs_task = Run(function()
local rcs_accum = 0
local rcs_intensity = 1.0 -- starts at 100%, decays over time
while Input.IsDown("Mouse1") and Input.IsDown("Mouse2") and cfg.enabled do
local elapsed = System.Time() - start_time
-- Exponential decay: intensity fades smoothly over max_duration
if elapsed > 0 then
rcs_intensity = math.exp(-elapsed / cfg.max_duration)
end
if elapsed < cfg.max_duration and rcs_intensity > 0.05 then
local active_rcs = rcs_base * rcs_intensity
rcs_accum = rcs_accum + active_rcs
local pixels = math.floor(rcs_accum + 0.5)
if math.abs(pixels) >= cfg.min_output then
HID.Move(0, pixels)
rcs_accum = rcs_accum - pixels
end
end
Sleep(1) -- Tick every 1ms for smooth output
end
end)
return true
end
return true
end
function OnUp(key)
if key == "Mouse1" then
stop_rcs_task()
elseif key == "Mouse2" then
stop_rcs_task()
end
return true
end
function OnBlur()
stop_rcs_task()
half_rcs = false
end
function OnStop()
stop_rcs_task()
endCan you review it and provide a working version with the issues fixed? I'd like the RCS to be active at full strength until max_duration and then stop. Also, I'd like to have a separate save file for this script so it doesn't affect my other scripts. Finally, can you explain all the functions and settings in the script?
Rebind AI
Here is the updated script with the requested changes. It includes a separate save file, full-strength RCS until max_duration, and explanations of all functions and settings.
-- rebind: name=RCS FPS2 v3
-- rebind: tick_rate=1000
local cfg = UI.Schema({
enabled = UI.Toggle(true, { label = "RCS Enabled" }),
-- Slot 1
slot1_ar_rcs = UI.Slider(1.4, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 1 AR", group = "Slot 1" }),
slot1_smg_rcs = UI.Slider(1.2, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 1 SMG", group = "Slot 1" }),
slot1_mode = UI.Select("AR", { "AR", "SMG" }, { label = "Slot 1 Weapon", group = "Slot 1" }),
slot1_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 1", group = "Slot 1" }),
-- Slot 2
slot2_sks_pull = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 2 SKS Pull", group = "Slot 2" }),
slot2_smg_rcs = UI.Slider(1.2, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 2 SMG", group = "Slot 2" }),
slot2_mode = UI.Select("SKS", { "SKS", "SMG" }, { label = "Slot 2 Weapon", group = "Slot 2" }),
slot2_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 2", group = "Slot 2" }),
-- Slot 3 (Pistol)
slot3_sks_pull = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 3 SKS Pull", group = "Slot 3" }),
slot3_mode = UI.Select("Continuous", { "Continuous", "SKS Kick" }, { label = "Slot 3 Fire Mode", group = "Slot 3" }),
slot3_rcs = UI.Slider(1, { min = 0.1, max = 30, step = 0.1, suffix = "px/tick", label = "Slot 3 RCS", group = "Slot 3" }),
slot3_kick = UI.Slider(58, { min = 10, max = 200, step = 1, suffix = "px", label = "Slot 3 Kick", group = "Slot 3" }),
slot3_rf = UI.Toggle(false, { label = "Rapid Fire on Slot 3", group = "Slot 3" }),
-- General
half_mult = UI.Slider(50, { min = 10, max = 90, suffix = "%", label = "Half Speed Multiplier", group = "General" }),
max_duration = UI.Slider(2500, { min = 500, max = 6000, suffix = "ms", label = "Max RCS Duration", group = "General" }),
-- Stability
min_output = UI.Slider(0.1, { min = 0.0, max = 1.0, step = 0.1, label = "Min Output", tooltip = "Prevents micro-jitters", group = "General" }),
-- Rapid Fire
rf_interval = UI.Slider(10, { min = 1, max = 50, step = 1, suffix = "ms", label = "Rapid Fire Interval", group = "Rapid Fire" }),
rf_hold_ms = UI.Slider(5, { min = 1, max = 50, step = 1, suffix = "ms", label = "Rapid Fire Hold", group = "Rapid Fire" }),
})
local active_slot = 1
local half_rcs = false
local rcs_task = nil
local function get_rcs_value()
if active_slot == 1 then
return cfg.slot1_mode == "AR" and cfg.slot1_ar_rcs or cfg.slot1_smg_rcs
elseif active_slot == 2 then
return cfg.slot2_smg_rcs
else
return cfg.slot3_rcs
end
end
local function get_rcs_multiplier()
return half_rcs and (cfg.half_mult / 100) or 1.0
end
local function is_sks_mode()
return (active_slot == 2 and cfg.slot2_mode == "SKS") or
(active_slot == 3 and cfg.slot3_mode == "SKS Kick")
end
local function is_rapid_fire()
if active_slot == 1 then return cfg.slot1_rf end
if active_slot == 2 then return cfg.slot2_rf end
if active_slot == 3 then return cfg.slot3_rf end
return false
end
local function stop_rcs_task()
if rcs_task and rcs_task:IsRunning() then
rcs_task:Cancel()
rcs_task = nil
end
end
function OnDown(key)
local shift = Input.IsDown("LShift") or Input.IsDown("RShift")
local ctrl = Input.IsDown("LCtrl") or Input.IsDown("RCtrl")
-- RCS on/off: Shift+5
if key == "5" and shift and not ctrl then
cfg.enabled = not cfg.enabled
UI.Notify(cfg.enabled and "RCS ON" or "RCS OFF", "info")
return false
end
-- Slot 1 mode: Ctrl+5
if key == "5" and ctrl and not shift then
cfg.slot1_mode = cfg.slot1_mode == "AR" and "SMG" or "AR"
UI.Notify("Slot 1: " .. cfg.slot1_mode, "info")
return false
end
-- Slot 2 mode: Ctrl+6
if key == "6" and ctrl and not shift then
cfg.slot2_mode = cfg.slot2_mode == "SKS" and "SMG" or "SKS"
UI.Notify("Slot 2: " .. cfg.slot2_mode, "info")
return false
end
-- Slot 3 mode: Ctrl+7
if key == "7" and ctrl and not shift then
cfg.slot3_mode = cfg.slot3_mode == "Continuous" and "SKS Kick" or "Continuous"
UI.Notify("Slot 3: " .. cfg.slot3_mode, "info")
return false
end
-- Slot selection
if not ctrl and not shift then
if key == "1" or key == "Q" then
active_slot = 1
stop_rcs_task()
UI.Notify("Slot 1 (" .. cfg.slot1_mode .. ")", "info")
elseif key == "2" then
active_slot = 2
stop_rcs_task()
UI.Notify("Slot 2 (" .. cfg.slot2_mode .. ")", "info")
elseif key == "3" then
active_slot = 3
stop_rcs_task()
UI.Notify("Slot 3 (" .. cfg.slot3_mode .. ")", "info")
end
end
-- Half-speed toggle
if key == "Z" then
half_rcs = not half_rcs
UI.Notify(half_rcs and "Half RCS ON" or "Half RCS OFF", "info")
return true
end
-- Half-speed off
if (key == "C" or key == "LCtrl" or key == "RCtrl") and half_rcs then
half_rcs = false
UI.Notify("Half RCS OFF", "info")
return true
end
-- Mouse1 with right-click held: activate RCS
if key == "Mouse1" and cfg.enabled and Input.IsDown("Mouse2") then
-- Rapid Fire
if is_rapid_fire() then
stop_rcs_task()
local mult = get_rcs_multiplier()
local rcs_val = get_rcs_value() * mult
rcs_task = Run(function()
local rcs_accum = 0
while Input.IsDown("Mouse1") and Input.IsDown("Mouse2") and cfg.enabled do
HID.Down("Mouse1")
Sleep(cfg.rf_hold_ms)
HID.Up("Mouse1")
rcs_accum = rcs_accum + rcs_val
local pixels = math.floor(rcs_accum + 0.5)
if math.abs(pixels) >= cfg.min_output then
HID.Move(0, pixels)
rcs_accum = rcs_accum - pixels
end
Sleep(cfg.rf_interval)
end
end)
return false
end
-- SKS single shot
if is_sks_mode() then
local mult = get_rcs_multiplier()
local pull_value = active_slot == 2 and cfg.slot2_sks_pull or cfg.slot3_sks_pull
local pixels = math.floor(pull_value * mult)
if pixels > 0 then
HID.Move(0, pixels)
end
return true
end
-- Continuous RCS (AR/SMG) - FULL STRENGTH UNTIL MAX_DURATION, THEN STOP
stop_rcs_task()
local mult = get_rcs_multiplier()
local rcs_base = get_rcs_value() * mult
local start_time = System.Time()
rcs_task = Run(function()
local rcs_accum = 0
while Input.IsDown("Mouse1") and Input.IsDown("Mouse2") and cfg.enabled do
local elapsed = System.Time() - start_time
-- FULL RCS UNTIL max_duration, THEN STOP
if elapsed < cfg.max_duration then
rcs_accum = rcs_accum + rcs_base
local pixels = math.floor(rcs_accum + 0.5)
if math.abs(pixels) >= cfg.min_output then
HID.Move(0, pixels)
rcs_accum = rcs_accum - pixels
end
end
Sleep(1) -- Tick every 1ms for smooth output
end
end)
return true
end
return true
end
function OnUp(key)
if key == "Mouse1" then
stop_rcs_task()
elseif key == "Mouse2" then
stop_rcs_task()
end
return true
end
function OnBlur()
stop_rcs_task()
half_rcs = false
end
function OnStop()
stop_rcs_task()
endScript Functions and Settings Explained
Rebind Settings
-- rebind: name=RCS FPS2 v3: Sets the name of the script in Rebind. Thev3ensures a separate save file.-- rebind: tick_rate=1000: Sets the script's tick rate to 1000 Hz (1ms intervals). This is how often the script checks for input and applies RCS.
UI Schema (Settings in the Rebind UI)
General Settings
enabled: A toggle to enable or disable the RCS.half_mult: A slider to set the multiplier for half-speed RCS (activated by pressing Z). The default is 50%.max_duration: A slider to set the maximum duration (in milliseconds) for the RCS to be active. After this time, the RCS will stop. The default is 2500ms (2.5 seconds).min_output: A slider to set the minimum pixel value to move the mouse. This helps to prevent micro-jitters. The default is 0.1.
Slot-Specific Settings
- Each slot (1, 2, and 3) has settings for different weapons and modes.
slotX_ar_rcs: Slider for AR recoil compensation strength (pixels per tick).slotX_smg_rcs: Slider for SMG recoil compensation strength (pixels per tick).slotX_mode: Select between AR/SMG (Slot 1) or SKS/SMG (Slot 2) or Continuous/SKS Kick (Slot 3).slotX_rf: Toggle for Rapid Fire.slotX_sks_pull: Slider for SKS pull value (pixels) for single-shot recoil compensation.slotX_kick: Slider for kick value (pixels) for single-shot recoil compensation.
Rapid Fire Settings
rf_interval: Slider to set the interval (in milliseconds) between rapid-fire clicks.rf_hold_ms: Slider to set the duration (in milliseconds) the mouse button is held down during rapid fire.
Local Variables
active_slot: The currently selected weapon slot (1, 2, or 3). Initialized to 1.half_rcs: A boolean indicating whether half-speed RCS is enabled. Initialized tofalse.rcs_task: A variable to store the running RCS task (coroutine). Used to stop the task when needed.
Functions
get_rcs_value(): Returns the appropriate RCS value (pixels per tick) based on theactive_slotand the selected weapon mode.get_rcs_multiplier(): Returns the RCS multiplier. It returnscfg.half_mult / 100ifhalf_rcsis true, otherwise returns 1.0.is_sks_mode(): Checks if the current slot is in SKS mode (single-shot). Returnstrueorfalse.is_rapid_fire(): Checks if rapid fire is enabled for the current slot. Returnstrueorfalse.stop_rcs_task(): Cancels and clears thercs_taskcoroutine, effectively stopping the RCS.
Hooks (Event Handlers)
OnDown(key): This function is called when a key is pressed down.- Shift + 5: Toggles the RCS on or off.
- Ctrl + 5/6/7: Changes the weapon mode for the corresponding slot.
- 1/2/3 or Q: Selects the weapon slot and stops any active RCS task.
- Z: Toggles half-speed RCS.
- C or Ctrl: Disables half-speed RCS.
- Mouse1 (with Mouse2 held): Activates the RCS.
- If rapid fire is enabled, it simulates rapid-fire clicks.
- If in SKS mode, it applies a single-shot recoil compensation.
- If in continuous mode, it applies the RCS for the duration set by
max_duration.
OnUp(key): This function is called when a key is released. It stops the RCS task when Mouse1 or Mouse2 is released.OnBlur(): This function is called when the application loses focus. It stops the RCS task and disables half-speed RCS.OnStop(): This function is called when the script is stopped. It stops the RCS task.