summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-27 20:17:07 +0100
committersalmonik <salmonik@tuta.io>2026-10-27 20:17:07 +0100
commit59432708a4ae926e4be2959dfbb2b4eb80be3fd6 (patch)
tree3233f49d2c9b3affbb97d832b4afdccbfb5f43b6
parentFix tooltip truncation; add Stucklaunch assist (diff)
downloadhl2h-59432708a4ae926e4be2959dfbb2b4eb80be3fd6.tar.gz
hl2h-59432708a4ae926e4be2959dfbb2b4eb80be3fd6.zip
Stucklaunch: alternate attack/use and expose spam rate
- Gravity-gun spam now alternates +attack (grab) and +use (drop) instead of pressing both together, which would punt the prop. - Add a Spam rate slider (1..16 ticks, default 2) so the pickup/drop cycle can be tuned; it persists as spam_ticks in [stucklaunch]. - Log the prop index the first few times a stucklaunch engages to aid testing.
Diffstat (limited to '')
-rw-r--r--CONTEXT.md6
-rw-r--r--src/gui.cpp4
-rw-r--r--src/hl2h.cpp34
-rw-r--r--src/hl2h.h2
-rw-r--r--src/menu.cpp14
5 files changed, 44 insertions, 16 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index d7ea752..eccf7e2 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -267,9 +267,9 @@ vocabulary / decisions.
267## Stucklaunch assist 267## Stucklaunch assist
268 268
269- Optional Movement feature; Active is off by default. 269- Optional Movement feature; Active is off by default.
270- While engaged it holds jump, alternates +use every 2 ticks to regrab a 270- While engaged it holds jump, alternates +use (or +attack/+use as a
271 physics prop, optionally alternates +attack while the gravity gun is 271 pickup/drop cycle while the gravity gun is active) every N ticks (default
272 active, and air-strafes the hops. 272 2), and air-strafes the hops.
273- Engagement comes from `m_hGroundEntity`, latched for ~0.5 s so the prop 273- Engagement comes from `m_hGroundEntity`, latched for ~0.5 s so the prop
274 stays "active" during each hop. If that netvar is unavailable it runs 274 stays "active" during each hop. If that netvar is unavailable it runs
275 whenever the feature is active, so bind it to a key. 275 whenever the feature is active, so bind it to a key.
diff --git a/src/gui.cpp b/src/gui.cpp
index dc0e4eb..fc95d1f 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -301,10 +301,12 @@ static void build_cards() {
301 "Rapidly press +use to keep regrabbing the prop.", 301 "Rapidly press +use to keep regrabbing the prop.",
302 &g_stucklaunch_use_spam); 302 &g_stucklaunch_use_spam);
303 add_toggle(sl, "Gravity gun spam", 303 add_toggle(sl, "Gravity gun spam",
304 "Rapidly press +attack while holding the gravity gun.", 304 "Alternate +attack and +use while holding the gravity gun.",
305 &g_stucklaunch_gravspam); 305 &g_stucklaunch_gravspam);
306 add_toggle(sl, "Auto-strafe", "Air-strafe each hop.", 306 add_toggle(sl, "Auto-strafe", "Air-strafe each hop.",
307 &g_stucklaunch_autostrafe); 307 &g_stucklaunch_autostrafe);
308 add_slider(sl, "Spam rate", "Ticks between spam toggles; higher is slower.",
309 1, 16, &g_stucklaunch_spam_ticks);
308 310
309 int jb = add_card(0, &g_jumpbug, "Jumpbug", 311 int jb = add_card(0, &g_jumpbug, "Jumpbug",
310 "Duck+jump when landing would hurt.", true); 312 "Duck+jump when landing would hurt.", true);
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index 63fdcdc..84de815 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -178,8 +178,14 @@ static bool stucklaunch_prop_ok(void *ent, int tick) {
178 uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.groundentity); 178 uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.groundentity);
179 int gi = (h == 0xFFFFFFFFu) ? 0 : (int)(h & 0xFFFF); 179 int gi = (h == 0xFFFFFFFFu) ? 0 : (int)(h & 0xFFFF);
180 bool on = gi > 0 && gi <= 4096 && entity_is_physics_prop(gi); 180 bool on = gi > 0 && gi <= 4096 && entity_is_physics_prop(gi);
181 if (on) 181 if (on) {
182 static int prop_logs = 0;
183 if (prop_logs < 4) {
184 prop_logs++;
185 logf("stucklaunch: prop idx=%d", gi);
186 }
182 last_prop_tick = tick; 187 last_prop_tick = tick;
188 }
183 return tick - last_prop_tick < 32; // ~0.5 s at 66 tick 189 return tick - last_prop_tick < 32; // ~0.5 s at 66 tick
184} 190}
185 191
@@ -189,20 +195,28 @@ static bool stucklaunch_prop_ok(void *ent, int tick) {
189static void stucklaunch_apply(CUserCmd *cmd, void *local_ent, bool onground, 195static void stucklaunch_apply(CUserCmd *cmd, void *local_ent, bool onground,
190 const float *vel) { 196 const float *vel) {
191 cmd->buttons |= IN_JUMP; 197 cmd->buttons |= IN_JUMP;
192 int phase = (cmd->tick_count / 2) & 1; 198 int period = stucklaunch_spam_ticks();
193 if (stucklaunch_use_spam_enabled()) { 199 if (period < 1)
200 period = 1;
201 int phase = (cmd->tick_count / period) & 1;
202 bool gravity = stucklaunch_gravspam_enabled() &&
203 active_weapon_is_gravity_gun(local_ent);
204 if (gravity) {
205 // Pick up with +attack, drop with +use on the next half-cycle. Pressing
206 // both together would punt instead of regrabbing.
207 if (phase == 0) {
208 cmd->buttons |= IN_ATTACK;
209 cmd->buttons &= ~IN_USE;
210 } else {
211 cmd->buttons |= IN_USE;
212 cmd->buttons &= ~IN_ATTACK;
213 }
214 } else if (stucklaunch_use_spam_enabled()) {
194 if (phase == 0) 215 if (phase == 0)
195 cmd->buttons |= IN_USE; 216 cmd->buttons |= IN_USE;
196 else 217 else
197 cmd->buttons &= ~IN_USE; 218 cmd->buttons &= ~IN_USE;
198 } 219 }
199 if (stucklaunch_gravspam_enabled() &&
200 active_weapon_is_gravity_gun(local_ent)) {
201 if (phase == 0)
202 cmd->buttons |= IN_ATTACK;
203 else
204 cmd->buttons &= ~IN_ATTACK;
205 }
206 if (stucklaunch_autostrafe_enabled() && !onground) 220 if (stucklaunch_autostrafe_enabled() && !onground)
207 apply_autostrafe(cmd, vel); 221 apply_autostrafe(cmd, vel);
208} 222}
diff --git a/src/hl2h.h b/src/hl2h.h
index aabe8e9..2607ece 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -213,6 +213,7 @@ extern bool g_spread_no_spread;
213extern bool g_stucklaunch_use_spam; 213extern bool g_stucklaunch_use_spam;
214extern bool g_stucklaunch_gravspam; 214extern bool g_stucklaunch_gravspam;
215extern bool g_stucklaunch_autostrafe; 215extern bool g_stucklaunch_autostrafe;
216extern int g_stucklaunch_spam_ticks;
216extern bool g_aim_bones[AIM_BONE_COUNT]; 217extern bool g_aim_bones[AIM_BONE_COUNT];
217extern int g_aim_smooth; 218extern int g_aim_smooth;
218extern bool g_aim_vischeck; 219extern bool g_aim_vischeck;
@@ -254,6 +255,7 @@ bool spread_no_spread_enabled();
254bool stucklaunch_use_spam_enabled(); 255bool stucklaunch_use_spam_enabled();
255bool stucklaunch_gravspam_enabled(); 256bool stucklaunch_gravspam_enabled();
256bool stucklaunch_autostrafe_enabled(); 257bool stucklaunch_autostrafe_enabled();
258int stucklaunch_spam_ticks();
257bool active_weapon_is_gravity_gun(void *local_ent); 259bool active_weapon_is_gravity_gun(void *local_ent);
258bool entity_is_physics_prop(int idx); 260bool entity_is_physics_prop(int idx);
259unsigned aimbot_bone_mask(); 261unsigned aimbot_bone_mask();
diff --git a/src/menu.cpp b/src/menu.cpp
index 00ba9be..0b8e20b 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -48,6 +48,7 @@ FeatureState g_stucklaunch{false, 0}; // opt-in movement assist
48bool g_stucklaunch_use_spam = true; 48bool g_stucklaunch_use_spam = true;
49bool g_stucklaunch_gravspam = false; 49bool g_stucklaunch_gravspam = false;
50bool g_stucklaunch_autostrafe = true; 50bool g_stucklaunch_autostrafe = true;
51int g_stucklaunch_spam_ticks = 2;
51 52
52// Aimbot bone checklist. Head defaults on (v1 behaviour). 53// Aimbot bone checklist. Head defaults on (v1 behaviour).
53bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, 54bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false,
@@ -163,10 +164,10 @@ void config_save() {
163 g_bhop.bind, g_bhop_autostrafe); 164 g_bhop.bind, g_bhop_autostrafe);
164 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 165 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
165 fprintf(f, "[stucklaunch]\nactive=%d\nbind=%d\nuse_spam=%d\n" 166 fprintf(f, "[stucklaunch]\nactive=%d\nbind=%d\nuse_spam=%d\n"
166 "gravspam=%d\nautostrafe=%d\n", 167 "gravspam=%d\nautostrafe=%d\nspam_ticks=%d\n",
167 g_stucklaunch.active, g_stucklaunch.bind, 168 g_stucklaunch.active, g_stucklaunch.bind,
168 (int)g_stucklaunch_use_spam, (int)g_stucklaunch_gravspam, 169 (int)g_stucklaunch_use_spam, (int)g_stucklaunch_gravspam,
169 (int)g_stucklaunch_autostrafe); 170 (int)g_stucklaunch_autostrafe, g_stucklaunch_spam_ticks);
170 fprintf(f, "[jumpbug]\nactive=%d\nbind=%d\nassist=%d\n", 171 fprintf(f, "[jumpbug]\nactive=%d\nbind=%d\nassist=%d\n",
171 g_jumpbug.active, g_jumpbug.bind, (int)g_jumpbug_assist); 172 g_jumpbug.active, g_jumpbug.bind, (int)g_jumpbug_assist);
172 fprintf(f, "[melee]\nactive=%d\nbind=%d\n", g_melee.active, g_melee.bind); 173 fprintf(f, "[melee]\nactive=%d\nbind=%d\n", g_melee.active, g_melee.bind);
@@ -268,6 +269,12 @@ static bool config_load() {
268 g_stucklaunch_gravspam = atoi(val) != 0; 269 g_stucklaunch_gravspam = atoi(val) != 0;
269 else if (strcmp(key, "autostrafe") == 0) 270 else if (strcmp(key, "autostrafe") == 0)
270 g_stucklaunch_autostrafe = atoi(val) != 0; 271 g_stucklaunch_autostrafe = atoi(val) != 0;
272 else if (strcmp(key, "spam_ticks") == 0) {
273 int v = atoi(val);
274 if (v < 1) v = 1;
275 if (v > 16) v = 16;
276 g_stucklaunch_spam_ticks = v;
277 }
271 } 278 }
272 if (in_aim) { 279 if (in_aim) {
273 if (strcmp(key, "smooth") == 0) { 280 if (strcmp(key, "smooth") == 0) {
@@ -351,6 +358,7 @@ void config_reset_defaults() {
351 g_stucklaunch_use_spam = true; 358 g_stucklaunch_use_spam = true;
352 g_stucklaunch_gravspam = false; 359 g_stucklaunch_gravspam = false;
353 g_stucklaunch_autostrafe = true; 360 g_stucklaunch_autostrafe = true;
361 g_stucklaunch_spam_ticks = 2;
354 g_jumpbug = {true, 0}; 362 g_jumpbug = {true, 0};
355 g_jumpbug_assist = false; 363 g_jumpbug_assist = false;
356 g_melee = {true, 0}; 364 g_melee = {true, 0};
@@ -497,6 +505,8 @@ bool stucklaunch_gravspam_enabled() { return g_stucklaunch_gravspam; }
497 505
498bool stucklaunch_autostrafe_enabled() { return g_stucklaunch_autostrafe; } 506bool stucklaunch_autostrafe_enabled() { return g_stucklaunch_autostrafe; }
499 507
508int stucklaunch_spam_ticks() { return g_stucklaunch_spam_ticks; }
509
500unsigned aimbot_bone_mask() { 510unsigned aimbot_bone_mask() {
501 unsigned m = 0; 511 unsigned m = 0;
502 if (g_aim_bones[AIM_BONE_HEAD]) 512 if (g_aim_bones[AIM_BONE_HEAD])