lgbot: auto recording while two humans fight (lgbot_record 2 / QL_RECORD), analyzer pairs any opponents

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
esefandClaude Opus 5.5 committed 2026-10-15 18:37:40 +03:00
1 parent 3535940331
commit acf625d72b
8 files changed
+133 -51

No files matched your search

+42 -20
View File
@@ -43,9 +43,12 @@ SWITCH_MARGIN = 150
BOT_STEAM_ID = 90071996842377216 # minqlx gives bots this + their client number
# lgbot_record 1: every frame, the position/velocity of the bot and each human (as h<client number>,
# no names or Steam IDs) go to home/lgbot-rec.csv, for tools/lgbot_rec.py to compare how they move.
# Stops by itself after REC_MAX_S.
# Stops by itself after REC_MAX_S. lgbot_record 2 (auto): only frames where two humans are fighting
# (alive, within REC_FIGHT_DIST on about the same floor), no time limit. Both stop at REC_MAX_BYTES.
REC_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lgbot-rec.csv")
REC_MAX_S = 900
REC_MAX_BYTES = 200 * 1024 * 1024
REC_FIGHT_DIST, REC_FIGHT_DZ = 1000, 250
def is_bot(steam_id):
@@ -160,6 +163,15 @@ def approach(cur, want, max_delta):
return (cur[0] + dx / d * max_delta, cur[1] + dy / d * max_delta)
def humans_fighting(positions):
"""True when two of these (x, y, z) are within duel range of each other on about the same floor."""
for i, a in enumerate(positions):
for b in positions[i + 1:]:
if math.hypot(a[0] - b[0], a[1] - b[1]) <= REC_FIGHT_DIST and abs(a[2] - b[2]) <= REC_FIGHT_DZ:
return True
return False
def pick_target(bot_pos, candidates, current=None):
"""Key of the player to fight, from candidates [((x, y, z), key)]. A new target must be within
MAX_TARGET_DIST / MAX_TARGET_DZ; the current one is kept up to the STOP limits unless another
@@ -237,7 +249,7 @@ if minqlx:
self.target_id, self.last_tick = None, None
self.dbg_cmd, self.slow_frames, self.stuck = None, 0, 0 # debug stats
self.state, self.frame_players = "", [] # for the recorder
self.record_on, self.rec, self.rec_start = False, None, 0.0
self.record_mode, self.rec, self.rec_start, self.rec_bytes = 0, None, 0.0, 0
self.add_hook("frame", self.handle_frame)
def handle_frame(self):
@@ -255,7 +267,7 @@ if minqlx:
self.settings = resolve(to_int(self.get_cvar("lgbot_move")), over)
self.debug = bool(to_int(self.get_cvar("lgbot_debug")))
self.target_bots = bool(to_int(self.get_cvar("lgbot_target_bots")))
self.record_on = bool(to_int(self.get_cvar("lgbot_record")))
self.record_mode = to_int(self.get_cvar("lgbot_record")) or 0
self.read_at = now
return self.settings
@@ -320,23 +332,16 @@ if minqlx:
if self.debug:
self.debug_line(now, vel, (vx, vy), tpos, bpos, want, direction)
def record(self, now):
if not self.record_on:
if self.rec:
self.rec.close()
self.rec = None
return
if self.rec is None:
self.rec = open(REC_FILE, "a", buffering=1)
self.rec_start = now
self.rec.write("# start %d map %s\n" % (int(now), self.get_cvar("mapname")))
if now - self.rec_start > REC_MAX_S:
self.set_cvar("lgbot_record", "0")
self.record_on = False
def stop_recording(self):
if self.rec:
self.rec.close()
self.rec = None
return
t = now - self.rec_start
def record(self, now):
mode = self.record_mode
if mode not in (1, 2):
return self.stop_recording()
rows, humans = [], []
for p in self.frame_players:
if p.team == "spectator":
continue
@@ -347,8 +352,25 @@ if minqlx:
else:
who, state = "h%d" % p.id, ""
pos, vel = p.position(), p.velocity()
self.rec.write("%.3f,%s,%d,%d,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%s\n" % (
t, who, int(bool(p.is_alive)), p.health, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z, state))
if who != "bot" and p.is_alive:
humans.append((pos.x, pos.y, pos.z))
rows.append((who, int(bool(p.is_alive)), p.health, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z, state))
if mode == 2 and not humans_fighting(humans):
return
if self.rec is None:
self.rec = open(REC_FILE, "a", buffering=1)
self.rec_bytes = os.path.getsize(REC_FILE)
self.rec_start = now
self.rec.write("# start %d map %s\n" % (int(now), self.get_cvar("mapname")))
if (mode == 1 and now - self.rec_start > REC_MAX_S) or self.rec_bytes > REC_MAX_BYTES:
self.set_cvar("lgbot_record", "0")
self.record_mode = 0
return self.stop_recording()
t = now - self.rec_start
for r in rows:
line = "%.3f,%s,%d,%d,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%s\n" % ((t,) + r)
self.rec.write(line)
self.rec_bytes += len(line)
def debug_line(self, now, vel, cmd, tpos, bpos, want, direction):
# cos = how well last frame's command survived the frame (1 = exactly our direction);