GPC Combos & wait() Timing: Deep Dive
How combos execute, what wait() blocks, combo_run vs combo_stop, overlapping combos, and debugging timing bugs in production scripts.
# GPC Combo, wait(), and Timing Deep Dive
Combos are the timed execution engine of GPC — they turn single-poll main logic into multi-step button and stick sequences spanning hundreds of milliseconds. The wait() function controls combo pacing; combo_run and combo_stop control lifecycle. Misunderstanding any of these three causes the majority of custom-script timing bugs: stuck sticks, double triggers, early releases, and online-only inconsistency. This deep dive explains combo internals, wait blocking semantics, interaction with main, debugging techniques, and patterns used in commercial shooter and sports scripts. Prerequisite reading: GPC Script Basics. Context on polling: How Cronus Zen Scripts Work.
Combo anatomy
A combo is a named block of sequential instructions:
combo ExampleSequence {
set_val(PS4_RY, -80);
wait(120);
set_val(PS4_RY, 0);
wait(40);
set_val(PS4_CROSS, 100);
wait(50);
set_val(PS4_CROSS, 0);
}Execution properties:
| Property | Behavior |
|---|---|
| Entry | Starts at line 1 on `combo_run` unless resume logic exists |
| Steps | Sequential top to bottom within combo |
| `wait` | Blocks advance to next combo line until duration elapses |
| Exit | Ends after last line; combo no longer running |
| Override | `set_val` during combo affects report until next `set_val` or `combo_stop` |
Combos are not threads in the operating system sense — they are bytecode coroutines advanced by the Zen interpreter between input polls.
wait() semantics
wait(argument) accepts milliseconds as integer or variable:
int green_delay = 240;
wait(green_delay);
wait(80);What wait blocks
| Blocked | Not blocked |
|---|---|
| Current combo's next lines | `main` loop execution |
| Same combo re-entry from duplicate `combo_run` if guarded | Other combos if already running |
| — | Physical input reading via `get_val` |
Because main continues each poll, other mods may execute simultaneous set_val on the same axis while a combo waits between its own steps. Commercial scripts serialize phases to prevent fights; custom editors must preserve order.
wait precision
| Factor | Effect |
|---|---|
| Integer ms resolution | Sub-millisecond values round |
| Poll alignment | Actual wait ≈ requested ms ± one poll jitter |
| Multiple waits in series | Sum of durations = total combo length |
| Zero wait | `wait(0)` advances one combo step quickly — rare, used for spacing |
Sports green windows and shot releases often hinge on 10–30 ms wait deltas. FPS recoil patterns use 40–200 ms segments per weapon.
Common wait mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| `wait` in `main` (invalid) | Compile error | Move wait inside combo only |
| wait too short online | Inconsistent release | Increase 15–25 ms for high ping |
| wait too long | Sluggish mod | Decrease 10 ms steps |
| No wait between set_val | Instant toggle | Add wait for human-visible duration |
combo_run lifecycle
combo_run(ComboName) instructs the interpreter to execute the named combo.
main {
if (event_press(PS4_R2)) {
combo_run(FirePattern);
}
}First run
Combo starts at first line. If already running, behavior depends on guards:
if (!combo_running(FirePattern)) {
combo_run(FirePattern);
}Without guard, repeated combo_run on every press frame may restart combo from line 1, stuttering output.
Re-trigger on hold
event_press fires once per physical press. Holding R2 does not retrigger. For periodic retrigger while held:
if (get_val(PS4_R2)) {
if (!combo_running(BurstFire)) {
combo_run(BurstFire);
}
}Design burst spacing with wait inside BurstFire combo.
combo_stop lifecycle
combo_stop(ComboName) halts combo immediately:
main {
if (event_release(PS4_R2)) {
combo_stop(FirePattern);
}
}Why stop is mandatory
Without combo_stop on release:
- Last
set_valin combo may persist indefinitely - Stick reports non-zero values at rest → user reports "drift"
- Subsequent combos conflict on same axis
| Pattern | Release handler |
|---|---|
| Hold to aim mod | `combo_stop` on ADS release |
| Tap fire burst | Stop optional if combo self-terminates |
| Toggle fire mode | Stop on toggle off event |
Drift troubleshooting: Troubleshooting Cronus Zen Scripts Tier 4.
combo_running() guards
combo_running(Name) returns TRUE while combo active.
Use cases:
// Prevent stack overflow style restarts
if (event_press(PS4_R2) && !combo_running(Recoil)) {
combo_run(Recoil);
}
// Mutual exclusion between two combos on same axis
if (combo_running(RecoilA)) {
combo_stop(RecoilB);
}Mutual exclusion table:
| Combo A | Combo B | Same axis? | Strategy |
|---|---|---|---|
| AntiRecoil | SnapAim | RY / RX | Stop one when starting other |
| AutoGreen | DribbleMove | RY | Never run concurrently |
| RapidFire | SingleShot | R2 | Toggle mode switch |
Concurrent combo execution
Multiple combos may run if they touch different buttons/axes or provider designed intentional overlap.
combo VerticalPull {
set_val(PS4_RY, pull_strength);
wait(fire_ms);
set_val(PS4_RY, 0);
}
combo HorizontalPull {
set_val(PS4_RX, horiz_strength);
wait(fire_ms);
set_val(PS4_RX, 0);
}Both can combo_run on same fire press if strengths tuned not to overshoot. Same-axis concurrent set_val without coordination — last writer wins per poll rules in main combo interleave.
Interaction between main and active combos
Typical poll ordering conceptually:
- Read physical input
- Execute
maintop to bottom - Advance active combos by elapsed time; execute combo steps due this poll
- Merge outputs into final HID report
If main sets PS4_RY to 0 while combo sets PS4_RY to -80 same poll, implementation-defined winner applies — assume combo override wins during active combo step for safety in design, but do not rely on conflict.
Best practice: when combo active for axis, skip conflicting main set_val on that axis:
if (!combo_running(AntiRecoil)) {
set_val(PS4_RY, manual_ry);
}Timing patterns by genre
FPS anti-recoil combo
combo ARRecoil {
set_val(PS4_RY, ar_vertical);
wait(ar_duration);
set_val(PS4_RY, ar_vertical / 2);
wait(ar_duration / 2);
set_val(PS4_RY, 0);
}Duration variables map to fire rate and magazine length tuning in Cronus Zen Aim Assist and Anti-Recoil Settings.
Sports shot timing combo
combo ReleaseWindow {
set_val(PS4_RY, -100);
wait(release_delay);
set_val(PS4_RY, 0);
wait(40);
set_val(PS4_R2, 0);
}release_delay is the primary online tuning variable — adjust ±15 ms per ping band.
Rapid fire combo
combo RapidBurst {
set_val(PS4_R2, 100);
wait(press_ms);
set_val(PS4_R2, 0);
wait(gap_ms);
}Loop via re-combo_run on timer or hold guard. gap_ms below platform debounce causes ignored inputs.
Debugging combo timing
Compile-time
| Error | Cause |
|---|---|
| Combo not defined | Typo in `combo_run` name |
| wait outside combo | Syntax placement error |
| Combo memory | Too many long combos — trim unused |
Runtime offline
- Enable one combo mod only.
- Log mentally: press → wait duration → release → stop fired?
- Watch stick return to center within 50 ms of release.
- Adjust one
waitvariable ±10 ms per test.
Runtime online
Increase primary wait 15–25 ms over offline optimal when ping exceeds 60 ms. Packet loss requires acceptance of variance — not every combo bug.
Advanced: chained combo_run from combo end
Some patterns trigger secondary combo at end of first:
combo PhaseA {
set_val(PS4_RY, -50);
wait(100);
combo_run(PhaseB);
}
combo PhaseB {
set_val(PS4_RY, -25);
wait(80);
set_val(PS4_RY, 0);
}Chaining risks forgotten combo_stop — stop parent chain name if provider documents chain stop API, or stop each known child on release.
Memory and combo count limits
Each combo consumes bytecode space. Unused combos from deprecated game modes should be removed before approaching slot limits. Compile output reports size — see install guide How to Install a Cronus Zen Script.
Slot and versioning note
Combo timing changes are why providers version scripts per game patch. Backup before tuning extremes — Cronus Zen Slots, Profiles, and Backup.
Frequently asked questions
Can wait pause the entire script?
No. wait pauses only the current combo's step advancement. main continues every poll.
Why does my combo restart mid-sequence?
Duplicate combo_run without combo_running guard on hold or repeated event_press from bounce. Add guard or debounce with short wait combo.
combo_stop vs setting values to zero?
combo_stop halts combo and clears combo state. Manual set_val(0) without stop may leave combo logically running, blocking guards.
How many waits can one combo have?
Dozens technically; practical limits are readability and total combo duration. Long combos exceed human interaction windows.
Does wait account for ping?
No automatically. You tune ms values for your network environment manually.
Can two combo_run calls same poll start two combos?
Yes if names differ and axes independent. Same combo twice — restart behavior without guard.
Is wait affected by frame rate?
Console frame rate does not lock combo timing. Combos use ms timers on Zen, not game frames.
Why compile error "wait not allowed here"?
wait only valid inside combo blocks, not in main or init.
Worked example: building a hold-to-fire recoil combo
The following pattern demonstrates correct pairing of event_press, event_release, combo_run, combo_stop, and variable-driven wait values. Adapt names to your script.
int recoil_ms = 140;
int recoil_pull = -65;
combo SustainRecoil {
set_val(PS4_RY, recoil_pull);
wait(recoil_ms);
set_val(PS4_RY, recoil_pull / 2);
wait(recoil_ms / 2);
set_val(PS4_RY, 0);
}
main {
if (event_press(PS4_R2)) {
if (!combo_running(SustainRecoil)) {
combo_run(SustainRecoil);
}
}
if (get_val(PS4_R2) && combo_running(SustainRecoil)) {
// Re-trigger pattern while holding for long magazines
if (!combo_running(SustainRecoil)) {
combo_run(SustainRecoil);
}
}
if (event_release(PS4_R2)) {
combo_stop(SustainRecoil);
set_val(PS4_RY, 0);
}
}Walk through execution mentally: press fires combo, waits sum to 210 ms per cycle, release stops and zeroes stick. Tweak recoil_ms in 15 ms steps online per Cronus Zen Aim Assist and Anti-Recoil Settings.
Timing measurement without external tools
You do not need oscilloscopes to validate combo length. Use in-game counting:
- Record clip of mod firing at 60 fps if platform allows.
- Count frames from press to stick center on release.
- Multiply frames by 16.67 ms for approximate duration.
- Compare to sum of
waitcalls plus estimated poll jitter.
Discrepancy greater than 40 ms suggests missing combo_stop or competing main set_val on same axis.
Relationship to installation and slots
Combo logic lives in bytecode flashed to slots. After editing wait values, recompile and write per How to Install a Cronus Zen Script. Backup slot before experimental timing extremes per Cronus Zen Slots, Profiles, and Backup.
Worked example: building a hold-to-fire recoil combo from scratch
The following end-to-end example ties together event_press, combo_run, wait, and combo_stop for a hold-activated anti-recoil pattern. Read it alongside GPC Script Basics variable declarations.
int recoil_strength = 30;
int recoil_segment_ms = 90;
int master_toggle = TRUE;
init {
recoil_strength = 30;
recoil_segment_ms = 90;
}
main {
if (!master_toggle) return;
if (get_val(PS4_L2) && get_val(PS4_R2)) {
if (!combo_running(HoldRecoil)) {
combo_run(HoldRecoil);
}
} else {
combo_stop(HoldRecoil);
set_val(PS4_RY, 0);
}
}
combo HoldRecoil {
set_val(PS4_RY, recoil_strength);
wait(recoil_segment_ms);
set_val(PS4_RY, recoil_strength / 2);
wait(recoil_segment_ms);
set_val(PS4_RY, 0);
wait(10);
}Walkthrough by interaction phase:
- Player aims (L2) and fires (R2).
maindetects both held and startsHoldRecoilonce viacombo_runningguard. - Combo applies full
recoil_strengthforrecoil_segment_msmilliseconds using firstwait. - Combo reduces pull to half strength for second segment — mimicking diminishing kick on some weapons.
- Combo zeros stick and waits 10 ms spacer before ending.
- While fire held,
mainre-invokescombo_runonly when combo not running — creating repeating pattern per completion cycle depending on interpreter resume behavior; provider scripts often use looping combos instead.
On release of either L2 or R2, combo_stop fires and explicit set_val(PS4_RY, 0) ensures no residual pull.
Tuning sequence for this example:
| Step | Variable | Action |
|---|---|---|
| 1 | `recoil_strength` | Adjust 25→30→35% watching wall grouping |
| 2 | `recoil_segment_ms` | Adjust 80→90→100 ms for online ping |
| 3 | Release handler | Confirm stop fires — no drift 2 sec after release |
Timing measurement without external tools
You do not need oscilloscopes to validate combo timing. Practical measurement techniques:
| Technique | What it validates |
|---|---|
| Wall bullet count per second | Rapid fire `wait(gap_ms)` calibration |
| Stopwatch + single tap action | Long single-sequence combos |
| Slow-motion phone capture of menu OLED | Menu debounce `wait` values |
| Compare offline vs online same input | Network-sensitive sports releases |
Document baseline before changes: "90 ms segment — tight offline, early online at 65 ms ping → try 105 ms."
Provider vs custom combo patterns
Commercial scripts hide complexity in included .gph libraries. When reverse-engineering behavior:
- Search source for
combo_runcalls inmain— maps features to combo names. - Open each combo; sum
waitdurations for total sequence length. - Note which combos share axes — mutual exclusion required?
- Check
get_slot()branches — timing may differ per slot profile.
Do not paste provider combos into public repos if license prohibits. Use learnings to inform your own test slot tuning per Cronus Zen Slots, Profiles, and Backup.
Related reading
GPC Script Basics: Syntax, Structure & Patterns
Technical introduction to GPC — variables, combos, main loops, conditionals, menu systems, and how to read commercial scripts safely.
How YewScripts Builds NBA 2K GPC Libraries (Engineering Notes)
Inside the implemented YewScripts delivery model: access checks, server-side generation, personalization, leak tracing, user passwords, archives, and flash-only programming.
YewScripts Patch-Day GPC Workflow (Maintainer Playbook)
How a maintained YewScripts release moves through configuration, personalization, trace assignment, password policy, exact-build archiving, and controlled delivery.