Your First GPC Script: Rapid-Fire Toggle Step by Step
Build your first GPC script for the Cronus Zen step by step: a rapid-fire toggle using get_val, set_val, event_press, and a timed combo block.
On this page
- Before you start
- Step 1: The smallest valid script
- Step 2: Read the trigger with get_val
- Step 3: Pulse the trigger with a combo
- Step 4: Add the toggle
- Step 5: Name your numbers, then flash it
- Troubleshooting
- FAQ
- What should my first GPC script be?
- Why does my rapid fire feel slower than advertised numbers?
- Does the script keep running when I turn the toggle off?
- Can I use PlayStation button names instead?
- Is a rapid-fire script allowed online?
Your first GPC script should be a rapid-fire toggle: it is short, it teaches the four ideas every Cronus Zen script is built from — the main loop, reading inputs, combos, and toggle state — and you can feel it working within seconds of flashing it. This tutorial builds the script one line at a time, and every intermediate stage is a complete script you can compile and test. By the end you will have a toggle you can switch on and off in-game and, more usefully, you will understand every line of it.
If you have not read what GPC is yet, skim it first — this page assumes you know that a script's main block re-runs continuously while the script is active.
Before you start
You need three things:
- Zen Studio installed on your PC and able to see your Zen.
- The Zen wired up to your console and controller in its normal playing configuration.
- A game where rapid fire is observable — anything with a semi-automatic weapon works.
This tutorial uses XB1_ identifiers. They are aliases for the same internal input slots as the PS4_ names, so the script works on either console family; use whichever names you find more readable.
Tip
A note on expectations: rapid fire only helps with semi-automatic inputs, and many games cap effective fire rate on the server side. The point of this script is to learn GPC, not to win an arms race.
Step 1: The smallest valid script
Open Zen Studio's compiler view and start with the minimum:
// rapid_fire.gpc — step 1: the smallest useful skeleton.
// The main block runs over and over while the script is active.
main {
// Empty. The Zen passes your controller through untouched.
}
Compile it. It does nothing, deliberately — but a clean compile confirms your toolchain works before any real logic exists, which makes every later error unambiguously yours.
Step 2: Read the trigger with get_val
get_val() returns the current value of one control: 0 to 100 for buttons and triggers, -100 to +100 for stick axes. In an if condition, any nonzero value counts as true.
// Step 2: react while the right trigger is held.
// Complete script — compiles and runs.
main {
if (get_val(XB1_RT)) {
// The trigger is pressed at least slightly.
// Nothing here yet — the combo arrives in step 3.
}
}
Because triggers are analog, get_val(XB1_RT) goes true at the lightest pull. If you later want the script to engage only on a deliberate full pull, change the condition to get_val(XB1_RT) > 90.
Step 3: Pulse the trigger with a combo
set_val() inside main only lasts for the single pass where it executes. For anything with duration you use a combo block: each wait(ms) holds whatever the lines above it set, for that many milliseconds.
// Step 3: convert a held trigger into timed pulses.
// Complete script — this is rapid fire, minus the toggle.
main {
if (get_val(XB1_RT)) {
combo_run(RapidFire);
}
}
combo RapidFire {
set_val(XB1_RT, 100); // hold the trigger fully...
wait(40); // ...for 40 ms (the "shot")
set_val(XB1_RT, 0); // then force it released...
wait(30); // ...for 30 ms (the gap)
}
Two details here do the real work:
- The forced release is the whole trick. Your finger is physically holding the trigger the entire time. During the second window,
set_val(XB1_RT, 0)overrides that and the game sees a clean release — which is what lets the next pull register as a new shot. - Calling
combo_run()every pass is fine. While a combo is already running,combo_run()on it has no effect (restarting one mid-flight is whatcombo_restart()is for). So the combo loops seamlessly for as long as you hold the trigger, and stops when you let go.
Flash this to a slot and try it — it should already feel like rapid fire. The problem: it is always on. Every trigger pull is now pulsed, in every weapon and every menu.
Step 4: Add the toggle
A toggle needs two things: a variable that survives between passes of main, and an edge detector so one button press flips it exactly once. That second part is event_press(), which is true only on the single pass where a button goes from released to pressed. If you used get_val() instead, the flag would flip on every pass for as long as you held the button — hundreds of times per second.
// Step 4: toggle rapid fire with LT + D-pad Up.
// Complete script.
int rapid_on; // 0 = off, 1 = on; ints start at 0
main {
// Flip the flag once per LT+Up chord press.
if (get_val(XB1_LT) && event_press(XB1_UP)) {
rapid_on = !rapid_on;
}
// Only pulse when the toggle is on.
if (rapid_on && get_val(XB1_RT)) {
combo_run(RapidFire);
}
}
combo RapidFire {
set_val(XB1_RT, 100);
wait(40);
set_val(XB1_RT, 0);
wait(30);
}
The chord (get_val(XB1_LT) && event_press(XB1_UP)) is a standard GPC idiom: the held half uses get_val, the tapped half uses event_press, so the toggle fires exactly once per deliberate combination and never from an accidental D-pad tap alone — unless LT happens to be held, which in a shooter usually means aiming. Pick a chord that suits your game.
Step 5: Name your numbers, then flash it
Magic numbers rot. Promote the two timing values to define constants so future-you can retune them in one place, and add a header comment saying what the script does:
// ============================================================
// rapid_fire_toggle.gpc
// Hold LT and press D-pad Up to toggle rapid fire on or off.
// While enabled, holding RT fires in timed pulses.
// Tune HOLD_MS and RELEASE_MS per game and weapon.
// ============================================================
define HOLD_MS = 40; // how long each simulated pull lasts
define RELEASE_MS = 30; // gap between pulls
int rapid_on; // 0 = off, 1 = on
main {
// Toggle: hold Left Trigger, tap D-pad Up.
if (get_val(XB1_LT) && event_press(XB1_UP)) {
rapid_on = !rapid_on;
}
// While enabled, convert a held Right Trigger into pulses.
if (rapid_on && get_val(XB1_RT)) {
combo_run(RapidFire);
}
}
combo RapidFire {
set_val(XB1_RT, 100); // press the trigger fully...
wait(HOLD_MS); // ...and hold it
set_val(XB1_RT, 0); // force a release (overrides your finger)
wait(RELEASE_MS); // stay released before the next pull
}
To get it running:
- Compile in Zen Studio. Fix any reported line before continuing.
- Program the compiled script to one of the Zen's memory slots.
- Select that slot on the device.
- In-game, hold LT and tap D-pad Up, then hold RT with a semi-automatic weapon. Toggle it off the same way and confirm the trigger behaves normally again.
Exact Zen Studio button labels change between versions, so if the interface differs from a tutorial you are following, trust Cronus's current documentation.
Tip
If 40/30 feels wrong in your game, tune RELEASE_MS first. Too short and the game drops shots (it never sees a clean release); too long and you are slower than your own finger.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Fires one shot, then nothing | Game needs a longer release to register a new press | Raise RELEASE_MS in steps of 10 |
| Toggle never engages | Chord conflicts with the game or wrong identifiers | Try a different chord; confirm you are testing the slot you flashed |
| Toggle flips on and off rapidly | Used get_val instead of event_press for the tap half |
Re-check the step 4 condition |
| Fires while aiming without shooting | Trigger threshold too sensitive | Use get_val(XB1_RT) > 90 as the fire condition |
| Script compiles but nothing changes in-game | Wrong memory slot selected on the Zen | Cycle to the slot you actually programmed |
If the script misbehaves in ways a table cannot solve, the combos guide explains the timing model in depth, and the syntax reference covers every keyword used here.
FAQ
What should my first GPC script be?
A rapid-fire toggle, as built above. It exercises the main loop, get_val/set_val, a combo with wait() timing, and event_press toggle state — the four building blocks nearly every other script recombines.
Why does my rapid fire feel slower than advertised numbers?
Each cycle here is 70 ms (40 hold + 30 release), which is an upper bound of about 14 presses per second — but games apply their own input buffering and fire-rate caps, often on the server. No script exceeds a game's real cap, regardless of the wait() values.
Does the script keep running when I turn the toggle off?
The combo finishes its current cycle (at most 70 ms) and then nothing re-triggers it, so the trigger reverts to fully manual. int variables keep their value until the script restarts.
Can I use PlayStation button names instead?
Yes. Swap XB1_RT for PS4_R2, XB1_LT for PS4_L2, and XB1_UP for PS4_UP. The identifier families are interchangeable aliases, so this is purely about readability.
Is a rapid-fire script allowed online?
Using input-automation devices violates most games' terms of service, and enforcement approaches differ by publisher and change over time. Treat this tutorial as a programming lesson and check the current stance for your game before using it in multiplayer.
