From 696d2f2dad119ce695421530e1fd965b4975c7d6 Mon Sep 17 00:00:00 2001 From: esefxdz Date: Thu, 15 Oct 2026 14:40:14 +0300 Subject: [PATCH] lgbot: friction boost on the ground only Co-Authored-By: Claude Opus 5.5 --- docs/HOW-IT-WORKS.md | 2 +- home/minqlx-plugins/lgbot.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/HOW-IT-WORKS.md b/docs/HOW-IT-WORKS.md index 417496b..5c986fb 100644 --- a/docs/HOW-IT-WORKS.md +++ b/docs/HOW-IT-WORKS.md @@ -23,7 +23,7 @@ In warmup the client draws "The match will begin when more players join" / "Pres Quake 3 bots take their personality from a character file. `home/baseq3/botfiles/bots/lgbot_c.c` (registered by `scripts/lgbot.bot`) sets aim accuracy, aim skill, reaction time and view speed per skill level, sets jumping, crouching and rocket jumping to 0, and weights the LG above everything else. QL reads skill blocks 1, 4 and 5 and interpolates 2 and 3 between 1 and 4, which is why the file has only three. The server loads these loose files from `fs_homepath` alongside the ones in `pak00.pk3`, so no pk3 is needed (bots run on the server only, so clients don't need the files). -The stock bot AI moves like a bot: it runs at you, hops around and goes for items. So the movement is overridden. The `lgbot` minqlx plugin sets the bot's horizontal velocity on every server frame: sideways to its target in the current strafe direction, plus an in/out part that holds the wanted distance (capped at 70% of the speed so it still strafes while closing in). The strafe direction comes from a small pattern generator: hold one side for a random time between the min and max, then switch. Sometimes it fakes instead: a 60–120 ms tap the other way, then a new hold on the same side. An upward speed of 200–320 (a jump; QL jumps at ~275) is cancelled. Falling, slopes and jump pads are left alone. The AI still aims and shoots, so aim and movement don't interfere. Measured on a test server, the plugin costs no extra CPU you could measure. +The stock bot AI moves like a bot: it runs at you, hops around and goes for items. So the movement is overridden. The `lgbot` minqlx plugin sets the bot's horizontal velocity on every server frame: sideways to its target in the current strafe direction, plus an in/out part that holds the wanted distance (capped at 70% of the speed so it still strafes while closing in). The strafe direction comes from a small pattern generator: hold one side for a random time between the min and max, then switch. Sometimes it fakes instead: a 60–120 ms tap the other way, then a new hold on the same side. Ground friction removes about a quarter of the set speed before the move happens, so on the ground it sets 1.3× (measured: the bot then really moves at the set speed). An upward speed of 200–320 (a jump; QL jumps at ~275) is cancelled. Falling, slopes and jump pads are left alone. The AI still aims and shoots, so aim and movement don't interfere. Measured on a test server, the plugin costs no extra CPU you could measure. minqlx gives bots Steam IDs from 90071996842377216 up. The plugin uses that to pick human targets only. diff --git a/home/minqlx-plugins/lgbot.py b/home/minqlx-plugins/lgbot.py index 28397a0..4463edc 100644 --- a/home/minqlx-plugins/lgbot.py +++ b/home/minqlx-plugins/lgbot.py @@ -10,7 +10,8 @@ import time RUN_SPEED = 320 # QL ground run speed, units/s # Ground friction takes ~25% of the velocity we set before the frame's move happens (measured: -# 288 set -> ~220 actual), so we set this much more to end up at the wanted speed. +# 288 set -> ~220 actual), so on the ground we set this much more to end up at the wanted speed. +# In the air there is no friction, so no boost (vertical speed 0 = on the ground, near enough). FRICTION_COMP = 1.3 KEYS = ["strafe_min", "strafe_max", "fake", "range", "range_play", "speed"] @@ -183,7 +184,8 @@ if minqlx: want = wanted_range(s, bot.health, target.health) vx, vy = steer((bpos.x, bpos.y), (tpos.x, tpos.y), direction, s, want) vel = bot.velocity() - bot.velocity(x=vx * FRICTION_COMP, y=vy * FRICTION_COMP, z=fix_z(vel.z)) + comp = FRICTION_COMP if abs(vel.z) < 1 else 1.0 + bot.velocity(x=vx * comp, y=vy * comp, z=fix_z(vel.z)) if self.debug: self.debug_line(now, vel, (vx, vy), tpos, bpos, want, direction)