run_juece13_woma_kill50.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env python3
  2. """run_juece13_woma_kill50.py — 角色【新角色】→ 沃玛寺庙一层杀 50 只.
  3. 复用 AutoFightBehavior(保命三修版) + CaveEnterBehavior 传送链。
  4. CaveEnterBehavior 引用模块常量(CAVE_MAP/_ACK_BODY/_TELEPORT_ACTS),
  5. 故 import 后覆写为沃玛参数(菜单第 3 项 @jiuxian_move_1,1,3,
  6. run14 抓包 SC643 菜单实证)。墙集=d022.map(2026-09-30 凌晨映射定案)。
  7. 用法:
  8. uv run python scripts/run_juece13_woma_kill50.py -u longwu988 -p Longwu1988 \
  9. --zone 14570166 --name 新角色 [--kills 50] [--minutes 60] [--wait 0]
  10. """
  11. from __future__ import annotations
  12. import argparse
  13. import logging
  14. import sys
  15. import time
  16. from collections import deque
  17. sys.path.insert(0, "scripts")
  18. import run_juece13_wugong_farthest as cave_mod
  19. from run_juece13_mine_farthest import banner, setup_logging
  20. from run_juece13_wugong_autofight import DIR_VECS, AutoFightBehavior
  21. from run_juece13_wugong_farthest import CaveEnterBehavior
  22. # 沃玛参数覆写(CaveEnterBehavior 读模块全局)
  23. WOMA_MAP = "沃玛寺庙一层"
  24. cave_mod.CAVE_MAP = WOMA_MAP
  25. # cap_woma_client.jsonl 真客户端抓包定案(2026-09-30 16:26 用户演示):
  26. # CS1010{index:2,UserID:10002} → CS1011 Act=@jiuxian_move_1,1,3
  27. # → CS1011 Act=@act_move,3 ←第二步是,3(进入按钮带地图编号)
  28. # → ACK 5012+5027 body='沃玛一层' ←协议图名别名, 非显示名!
  29. # (run16d 失败根因: 第二步发了,1 + ACK 发显示名'沃玛寺庙一层', 服务器静默)
  30. cave_mod._ACK_BODY = "沃玛一层" # noqa: SLF001
  31. cave_mod._TELEPORT_ACTS = ( # noqa: SLF001
  32. "@jiuxian_move_1,1,3", "@act_move,3")
  33. import requests
  34. from pbot.bot.pathfind import load_walls_from_map # noqa: E402
  35. from pbot.bot.runner import pump, run
  36. from pbot.config import Account
  37. from pbot.errors import PbotError
  38. from pbot.game.session import enter_world
  39. from pbot.login.box_client import enter_game_client, login
  40. from pbot.transport.gateway import login_gate_with_retry
  41. from pbot.login.zone_ports import resolve_zone_gateway
  42. from pbot.types import ZoneId
  43. _LOG = logging.getLogger("juece13_woma50")
  44. _WOMA_MAP_FILE = "assets/maps/d022.map"
  45. _WOMA_W, _WOMA_H = 250, 250
  46. _WOMA_WALLS: set[tuple[int, int]] | None = None
  47. def _woma_walls() -> set[tuple[int, int]]:
  48. """加载 d022.map 墙格(权威地形)."""
  49. global _WOMA_WALLS
  50. if _WOMA_WALLS is None:
  51. _WOMA_WALLS = load_walls_from_map(_WOMA_MAP_FILE)
  52. return _WOMA_WALLS
  53. def _woma_farthest(start: tuple[int, int]) -> tuple[tuple[int, int], int, bool]:
  54. """d022 墙 BFS 最远可达点(撤离目标用)."""
  55. walls = _woma_walls()
  56. visited: set[tuple[int, int]] = {start}
  57. dq = deque([(start, 0)])
  58. far, far_d = start, 0
  59. while dq:
  60. (x, y), d = dq.popleft()
  61. if d > far_d:
  62. far, far_d = (x, y), d
  63. for dx, dy in DIR_VECS:
  64. nxt = (x + dx, y + dy)
  65. if nxt in visited or not (0 <= nxt[0] < _WOMA_W
  66. and 0 <= nxt[1] < _WOMA_H):
  67. continue
  68. if nxt in walls:
  69. continue
  70. visited.add(nxt)
  71. dq.append((nxt, d + 1))
  72. return far, far_d, len(visited) < 50
  73. def parse_args() -> argparse.Namespace:
  74. """命令行."""
  75. ap = argparse.ArgumentParser()
  76. _ = ap.add_argument("-u", "--user", required=True)
  77. _ = ap.add_argument("-p", "--password", required=True)
  78. _ = ap.add_argument("--zone", type=int, required=True)
  79. _ = ap.add_argument("--name", required=True)
  80. _ = ap.add_argument("--kills", type=int, default=50)
  81. _ = ap.add_argument("--minutes", type=float, default=60.0)
  82. _ = ap.add_argument("--wait", type=float, default=0.0,
  83. help="进图后原地静默秒数(仅心跳+收帧)")
  84. _ = ap.add_argument("--login-port", type=int, default=0)
  85. return ap.parse_args()
  86. def main() -> int:
  87. """登录 → 沃玛寺庙一层 → [静默] → 自动战斗至 N 杀或超时 → 退出."""
  88. a = parse_args()
  89. setup_logging()
  90. banner("[0] 沃玛自动战斗: 区=%d 角色=%s 目标=%d杀 时限=%.0fmin 静默=%.0fs",
  91. a.zone, a.name, a.kills, a.minutes, a.wait)
  92. http = requests.Session()
  93. http.trust_env = False
  94. http.verify = False
  95. jwt, uid = login(Account(username=a.user, password=a.password))
  96. _ = enter_game_client(http, jwt, ZoneId(a.zone))
  97. gw_host, conn_port, real_port = (("", a.login_port, a.login_port)
  98. if a.login_port else resolve_zone_gateway(a.zone))
  99. ports = (conn_port,)
  100. _, sid, real_name, ticket_key, game_port = login_gate_with_retry(
  101. ZoneId(a.zone), jwt, uid, a.name, ports, host=gw_host, real_port=real_port)
  102. _LOG.info("[票据] sid=%d 角色=%s 游戏端口=%d", sid, real_name, game_port)
  103. stop: list[bool] = [False]
  104. conn, world, updater = enter_world(ZoneId(a.zone), real_name, ticket_key,
  105. sid, game_port, uid, stop=stop, host=gw_host)
  106. _LOG.info("[✓] 出生 map=%s @(%d,%d)", world.me.map_name, world.me.x,
  107. world.me.y)
  108. try:
  109. from pbot.game.heal import enable_auto_heal
  110. state = None
  111. for _ in range(2):
  112. state = enable_auto_heal(conn, sid, want_on=True, world=world)
  113. if state == "on":
  114. break
  115. _LOG.warning("[喝药✗] 状态=%s — 重试一轮", state)
  116. time.sleep(1.0)
  117. if state != "on":
  118. _LOG.error("[喝药✗✗] 高危图无喝药保护 — 终止, 拒绝送死")
  119. return 1
  120. _LOG.info("[喝药✓] on")
  121. if world.me.map_name != WOMA_MAP:
  122. banner("[1] 传送沃玛寺庙")
  123. bh = CaveEnterBehavior(sid=sid, kill_target=0)
  124. r = run(conn, world, updater, bh, duration_s=300.0)
  125. if world.me.map_name != WOMA_MAP:
  126. _LOG.error("[✗] 传送失败 result=%s", r.value)
  127. return 1
  128. time.sleep(3.0)
  129. _ = pump(conn, world, updater, 2.0)
  130. if a.wait > 0:
  131. banner("[2] 原地静默 %.0f 秒", a.wait)
  132. deadline = time.time() + a.wait
  133. while time.time() < deadline:
  134. _ = pump(conn, world, updater, 0.5)
  135. _LOG.info("[静默✓] hp=%d/%d 视野怪=%d 位置=(%d,%d) map=%s",
  136. world.me.hp, world.me.max_hp,
  137. len(world.monsters), world.me.x, world.me.y,
  138. world.me.map_name)
  139. banner("[3] 自动战斗: 目标 %d 杀", a.kills)
  140. af = AutoFightBehavior(
  141. sid=sid, map_name=WOMA_MAP, walls=_woma_walls(),
  142. farthest_fn=_woma_farthest, kill_goal=a.kills)
  143. r = run(conn, world, updater, af, duration_s=a.minutes * 60)
  144. _LOG.info("[结束] result=%s 击杀=%d/%d 腿=%d 弹回=%d 位置=(%d,%d) "
  145. "hp=%d/%d map=%s",
  146. r.value, af.kills, a.kills, af.legs, af.rejected,
  147. world.me.x, world.me.y, world.me.hp, world.me.max_hp,
  148. world.me.map_name)
  149. return 0 if af.kills >= a.kills else 1
  150. finally:
  151. stop[0] = True
  152. conn.close()
  153. _LOG.info("连接已关闭")
  154. if __name__ == "__main__":
  155. try:
  156. sys.exit(main())
  157. except PbotError as e:
  158. _LOG.error("失败: %s", e.detail)
  159. sys.exit(1)