Recoil control
A recoil-compensation script that pulls the crosshair downward (and side-to-side) while you're firing, with per-weapon profiles selectable from a dropdown.
Full script
//!gui:dropdown var=weapon label="Weapon" options="AK,SCAR,MP5,Sniper" default=0
//!gui:section label="Pattern" mode="expanded"
//!gui:slider var=vRecoil label="Vertical Pull" min=0 max=30 default=10 step=0.5
//!gui:slider var=hRecoil label="Horizontal Drift" min=-10 max=10 default=0 step=0.5
//!gui:slider var=rampUp label="Ramp Up (shots)" min=1 max=30 default=8
//!gui:endsection
//!gui:section label="Behavior" mode="expanded"
//!gui:slider var=recoverRate label="Recover Speed" min=0.5 max=10 default=2 step=0.5
//!gui:checkbox var=onlyOnAds label="Only when aiming" default=1
//!gui:endsection
// --- Detect firing button (device-aware) ---
function isFiring() {
if (device == "makcu") { return makcu.left; }
if (device == "controller") { return controller.rt; }
return 0;
}
function isAds() {
if (device == "makcu") { return makcu.right; }
if (device == "controller") { return controller.lt; }
return 0;
}
// --- Initialize state ---
if (!isset(state.shotIndex)) { state.shotIndex = 0; }
if (!isset(state.lastFired)) { state.lastFired = 0; }
// --- Gate by ADS if configured ---
if (settings.onlyOnAds && !isAds()) {
state.shotIndex = 0;
return (x, y);
}
// --- Per-weapon multiplier (could be expanded with arrays) ---
weaponMul = 1.0;
if (settings.weapon == 1) { weaponMul = 0.85; } // SCAR — slower
if (settings.weapon == 2) { weaponMul = 1.20; } // MP5 — faster
if (settings.weapon == 3) { weaponMul = 0.30; } // Sniper — minimal
// --- Apply recoil while firing ---
if (isFiring()) {
state.shotIndex = state.shotIndex + 1;
state.lastFired = time;
// Ramp up over the first N shots, then plateau
rampMul = clamp(state.shotIndex / settings.rampUp, 0.3, 1.5);
y = y + settings.vRecoil * rampMul * weaponMul;
x = x + settings.hRecoil * rampMul * weaponMul;
} else {
// Recover the shot counter when not firing
state.shotIndex = max(0, state.shotIndex - settings.recoverRate);
}
monitor("shots", state.shotIndex);
return (x, y);
How it works
Firing & ADS detection
isFiring() and isAds() are helper functions that translate "is the user holding fire" and "is the user aiming" into the right device-specific button:
| Device | Fire | ADS |
|---|---|---|
makcu |
Left mouse | Right mouse |
controller |
Right trigger | Left trigger |
If you use a different mapping, edit the helpers.
Shot counter
The state.shotIndex tracks how many consecutive shots have been fired. It increments each frame while firing, and decreases (at recoverRate per frame) when not firing.
This lets us model the typical FPS recoil shape: minimal kick on the first shot, ramping up over the first ~8 shots, then plateauing.
Ramp curve
rampMul = clamp(state.shotIndex / settings.rampUp, 0.3, 1.5);
This produces:
-
0.3at shot 1 (minimal kick) -
1.0at the configured ramp shot count -
1.5cap after sustained fire
Tune the Ramp Up (shots) slider to match your weapon's actual recoil profile.
Per-weapon multipliers
A simple if chain scales the recoil for different weapon classes. For more complex patterns, store recoil profiles in arrays:
if (!isset(state.akPattern)) {
state.akPattern = [0.5, 0.7, 1.0, 1.2, 1.3, 1.4, 1.5, 1.4, 1.3, 1.2];
}
shotInPattern = state.shotIndex % len(state.akPattern);
y = y + settings.vRecoil * state.akPattern[shotInPattern];
Tuning tips
- Start with Vertical Pull = 0, fire a burst, observe how far your crosshair drifts up
- Increase Vertical Pull until the crosshair stays roughly level during sustained fire
- Add small Horizontal Drift if your weapon kicks to one side
- For weapons with non-linear recoil, replace the per-weapon multiplier with a full pattern array (see above)
Extension ideas
- Spray pattern arrays — define per-shot offsets explicitly instead of using a single multiplier
-
Reset on reload — detect the reload key and zero
state.shotIndex -
Adaptive recoil — measure actual upward drift using
detection.velocityYhistory and auto-tune