"""run_juece13_kill30.py — 裁决13区角色【新角色】→ 任务指引 → 比奇大城 → 杀30怪。 链路(全部复用 pbot 框架已实测模块): 登录盒子 → EnterGameClient → 登录服选角(已有角色直接拿票据) → 游戏服 CS20 → 17帧初始化 → SC50/SC51 出生(等待地图) → KillBehavior 完整状态机: renwu_3031 → 1.0s 黄金时序 → CS1011 @renwu_mov,1(任务指引) → SC20102 受理 → 传送比奇(或等待图 NPC 链) → ACK → A* 跑向杀怪区(287,622) → CS3014 近战 → 无怪: CS5284 问服务器怪位 + 新颖度锚点跑图 + 刷怪NPC → 有怪: A* 跑向怪 → 攻击半径内 0.65s/刀 → SC31/SC32 计杀 → 达标 30 杀退出 用法: uv run python scripts/run_juece13_kill30.py -u longwu988 -p Longwu1988 \ --zone 14570166 --name 新角色 --kill 30 """ from __future__ import annotations import argparse import logging import re import sys import time import requests from pbot.bot.kill import KillBehavior from pbot.bot.runner import run from pbot.config import Account from pbot.errors import PbotError from pbot.game.session import enter_world from pbot.login.box_client import enter_game_client, login from pbot.transport.gateway import login_gate_with_retry from pbot.types import ZoneId _LOG = logging.getLogger("juece13") _PORTS: tuple[int, ...] = (8021, 8061, 8001, 8041) # 等待图→比奇 NPC 链日志辅助: 从 SC643 提取可点链接 _LINK_RE: re.Pattern[str] = re.compile(r"link=@([^\|<>]+)") def setup_logging() -> None: """根日志: 全量 INFO, 关键阶段带分隔条, 便于后续问题分析。""" logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)-7s %(name)s: %(message)s", stream=sys.stdout, ) def banner(msg: str, *args: object) -> None: """阶段分隔条日志(支持 %-style 格式化参数)。""" _LOG.info("======== %s ========", msg % args if args else msg) def parse_args() -> argparse.Namespace: """命令行: 账号/区/角色/杀怪数。""" ap = argparse.ArgumentParser() _ = ap.add_argument("-u", "--user", required=True) _ = ap.add_argument("-p", "--password", required=True) _ = ap.add_argument("--zone", type=int, required=True) _ = ap.add_argument("--name", required=True, help="已有角色名") _ = ap.add_argument("--kill", type=int, default=30) _ = ap.add_argument("--login-port", type=int, default=0) return ap.parse_args() def main() -> int: """全链路: 登录 → 选角 → 进游戏 → 任务指引传比奇 → 杀30怪。""" a = parse_args() setup_logging() banner(f"[0] 启动: 区={a.zone} 角色={a.name} 目标击杀={a.kill}") http = requests.Session() http.trust_env = False http.verify = False # ---- [1] 盒子登录 ---- banner("[1] 盒子登录") jwt, uid = login(Account(username=a.user, password=a.password)) _LOG.info("[✓] uid=%d", uid) # ---- [2] EnterGameClient 选区上报 ---- banner(f"[2] EnterGameClient zone={a.zone}") for attempt in range(4): try: _ = enter_game_client(http, jwt, ZoneId(a.zone)) _LOG.info("[✓] 上报成功") break except Exception as e: if attempt == 3: raise _LOG.warning("[!] 第%d次失败(%s), 12s 后重试", attempt + 1, e) time.sleep(12) # ---- [3] 登录服选角(已有角色 → 票据) ---- banner("[3] 登录服选角: %s", a.name) ports = (a.login_port,) if a.login_port else _PORTS key32, sid, real_name, ticket_key, game_port = login_gate_with_retry( ZoneId(a.zone), jwt, uid, a.name, ports) _LOG.info("[票据] key32=%s sid=%d 角色=%s ticket=%s 游戏端口=%d", key32.decode(errors="replace"), sid, real_name, ticket_key, game_port) # ---- [4] 进游戏 ---- banner("[4] 进游戏(CS20 → SC658 → 17帧初始化 → 出生)") stop: list[bool] = [False] conn, world, updater = enter_world(ZoneId(a.zone), real_name, ticket_key, sid, game_port, uid, stop=stop) _LOG.info("[✓] 出生 %s @(%d,%d) actor=%s kills=%d", world.me.map_name, world.me.x, world.me.y, world.me.actor_key, world.kills) try: # ---- [5] 喝药保护(进游戏先决项, 上一轮已开也再确认一次) ---- banner("[5] 喝药保护确认") from pbot.game.heal import enable_auto_heal state = enable_auto_heal(conn, sid, want_on=True, world=world) if state == "on": _LOG.info("[✓] 喝药保护已开启(服务端 but_1 确认)") else: _LOG.warning("[!] 喝药状态=%s(不阻断, 继续任务链)", state) # ---- [6] 杀怪 30 只 ---- # renwu_act: KillBehavior 默认 renwu_mov,2 是任务二; 我们要任务一 # "前往新手村/比奇大城" = renwu_mov,1(SC643 弹窗 link 实测)。行为内部 # 会优先从实时 SC643 弹窗读 Act, 此处只是无弹窗兜底。 banner("[6] 打怪链启动: 任务指引 → 比奇大城 → 杀 %d 怪", a.kill) _LOG.info("当前状态: map=%s @(%d,%d) 怪视野=%d NPC视野=%d", world.me.map_name, world.me.x, world.me.y, len(world.monsters), len(world.npcs)) kill = KillBehavior( sid=sid, kill_target=a.kill, renwu_act="renwu_mov,1", # 任务一: 前往新手村(比奇大城方向) allow_reroll=True, # 无怪/卡死时允许重走 renwu 链换落点 roam=True, ) # 时长上限: 30怪按 120s/怪 兜底 + 转场 600s duration = max(600.0, a.kill * 120.0 + 600.0) _LOG.info("行为超时上限: %.0fs(达标即提前退出)", duration) result = run(conn, world, updater, kill, duration_s=duration) banner("[7] 结果") _LOG.info("result=%s 击杀=%d/%d 最终位置=(%d,%d) 地图=%s", result.value, world.kills, a.kill, world.me.x, world.me.y, world.me.map_name) top = sorted(world.frame_counts.items(), key=lambda kv: -kv[1])[:15] _LOG.info("收帧Top15(问题分析用): %s", top) ok = world.kills >= a.kill _LOG.info("[%s] %s", "✓" if ok else "✗", "任务完成" if ok else "未达标(看上方日志定位)") return 0 if ok else 1 finally: stop[0] = True conn.close() _LOG.info("连接已关闭") if __name__ == "__main__": try: sys.exit(main()) except PbotError as e: _LOG.error("失败: %s", e.detail) sys.exit(1)