RPG 玩伴
在 RPG / 自定义地图里给玩家配一个 AI 伙伴:跟着你、帮你打怪、残血时给你加血、陪你说话。四种形态,继承一个类、改几个属性就是你自己的玩伴。
不只是对战。在 RPG、自定义地图里,你可以给自己配一个 AI 伙伴:它跟着你走、帮你打怪,你残血时给你加血,没事的时候陪你说两句 —— 台词还能接本地大模型。
怎么用由你自己决定。 这件事拆成三层接口,从底到顶,哪一层都能直接用:
| 层 | 是什么 | 适合 |
|---|---|---|
JASS 通道 g.jass | 地图作者能用的 1291 个 JASS 函数,按名字直接调(造单位、设盟友、给物品、改名、显示文字、复活英雄……) | 想自己造玩法 |
| 便捷接口 | g.spawn、g.set_alliance、g.player_slots、g.show_text、g.map_data:常用的几件事包好了 | 写自己的辅助脚本 |
| 玩伴框架 | openwar3.companion.Companion + openwar3.talk.Talk:继承一下、改几个属性,就是一个会跟随、助战、加血、聊天的伙伴 | 想要一个伙伴 |
只用于单机、局域网自建的游戏。造单位、设盟友这类操作是本机单方面改世界:单人局(和电脑打)没问题;多人局会让其他玩家不同步,所以多人局里 JASS 通道只放行只读的函数,玩伴自动退成「只说话」。
最快上手:在远见里一键开
- 选地图:远见「实例」页 →「下一局设置」→ 地图,选一张 RPG 地图(游戏目录
Maps下的Scenario、Download里的都会列出来,比如(4)WarChasers)。 - 选方案:实例卡片的「AI 方案」下拉里选 玩伴示例(buddy) →「选定」。
- 开始测试:游戏起来后,你自己在游戏窗口里玩。玩伴 —— 一个叫「小圣」的圣骑士 —— 会出现在你身边。
也可以用命令行:
python tools/play.py --bot brains/examples/buddy.py --inst 20 --rpg --map "<游戏目录>\Maps\Scenario\(4)WarChasers.w3m"
--rpg(方案说明书里是 "judge": false)表示不按对战规则判胜负:RPG 里英雄死了能复活,也没有「建筑全没了算输」。很多 RPG 地图载入完停在「按下任意键以继续」,SDK 发现「在局里、但游戏时钟一直是 0」时会自己按一下空格(g.press_to_continue(),只往游戏窗口发按键消息,不抢焦点)。
写一个自己的玩伴
from openwar3.companion import Companion
from openwar3.talk import Talk
class MyBuddy(Companion):
mode = "ally" # 形态,见下表
unit = "Hpal" # 造什么:任何四字码,地图自定义的也行
nickname = "小圣"
heal = ("holybolt", "AHhb", 0.55) # (施法订单名, 要学的技能, 主人血量低于多少就加血);None = 不加血
follow_distance = 350
talk = Talk(persona="活泼的小圣骑士,爱给主人加油")
四种形态
| mode | 玩伴是谁 | 说明 |
|---|---|---|
ally(默认) | 占一个空着的玩家槽,当你的盟友 | 有自己的颜色和名字(计分板、盟友面板显示 nickname);你点不到它,它自己打。框架自动设成同盟 + 共享视野 |
own | 造在你名下 | 你随时能手动指挥它;你不管的时候,AI 替你操作 |
adopt | 接管地图里已有的单位 | 覆盖 adopt(g) 返回那个单位(地图给你的宠物、随从) |
voice | 不造单位,只说话 | 陪聊、提醒;不改世界,多人局也能用 |
没有空槽时 ally 自动退成 own;多人局或造不出来时自动退成 voice。
ally 形态的玩伴认的是「那个槽现在最好的单位」(英雄优先),而不是死认一个单位。实测里有地图把玩伴当成真人玩家,删掉圣骑士、发了一个地图英雄 —— 玩伴会直接接管这个英雄,也会学地图给它定的技能。英雄死了优先原地复活;地图自己复活了就接着用。
每一拍做什么
按顺序检查,哪条成立做哪条:
| 顺序 | 行为 | 条件 | 可调 |
|---|---|---|---|
| 1 | 撤退 | 自己血量低于 25% 且附近有敌人:退到主人身后 | retreat_at |
| 2 | 加血 | 主人血量低于设定值、技能冷却好了、距离 900 以内 | heal(None 关掉) |
| 3 | 助战 | 主人身边有敌人:正在打主人的 > 主人正在打的 > 最近的 | assist_radius,或覆盖 pick_target |
| 4 | 跟随 | 离主人太远就跟上;远到一定程度直接跑回来、不恋战 | follow_distance、leash |
| 5 | 闲聊 | 没有敌人时,隔 1 ~ 2.5 分钟说一句 | 台词表 |
「敌人」按游戏里的同盟关系算(每 20 秒刷新一次)。RPG 地图常有好几家盟友,不能简单地把「除我以外的玩家」都当敌人。
可以覆盖的钩子:find_master(谁是主人,默认本机等级最高的英雄)、adopt、pick_target、on_poke(主人右键点了玩伴),以及 Bot 的 on_start / on_tick / on_event / on_end。加血、助战、击杀、跟随、撤退、说话、复活的次数都记在 self.stats 里,结束时打印。
怎么叫它
- 聊天命令:在聊天框打
-follow跟着我、-stay原地守着、-heal马上加血、-hi打招呼。改命令表就改commands,改反应就覆盖on_command。 - 右键点玩伴:触发
on_poke。示例里的反应是:主人没满血就给主人加一口血,否则说句话。 - 头像对白:打招呼、主人倒下、主人升级、玩伴回来,这几句会用游戏自己的头像对白说(底部头像换成玩伴,屏幕上出字幕),其余的冒头顶气泡。
- 状态面板:屏幕左侧一块面板,显示玩伴的血条、正在干什么、心情(开心 / 兴奋 / 紧张 / 害怕 / 难过)、击杀和加血次数。它用 画板 画,多人局也安全。
说话,和本地大模型
Talk 按事件挑台词,头顶冒气泡;voice 形态或冒不出气泡时,显示在屏幕左下角。每句话也会写进方案日志,事后能查它说过什么。
| 事件 | 什么时候 | 事件 | 什么时候 |
|---|---|---|---|
hello | 刚来 | master_low | 主人残血 |
poke | 主人右键点它 | master_levelup | 主人升级 |
fight | 开打 | master_died / master_back | 主人倒下 / 复活 |
kill | 打死一个怪(会说出怪的名字) | buddy_low / buddy_died / buddy_back | 玩伴自己残血 / 倒下 / 回来 |
healed | 给主人加了血 | idle / item | 闲聊 / 捡到东西 |
台词里能用 {master}、{me}、{map}、{enemy}、{level}、{item} 这些占位符;改台词直接改 talk.lines,冷却在 talk.cooldown。
接本地大模型:Talk(llm=LocalLLM(url, model)),任何 OpenAI 兼容接口都行(LM Studio、Ollama……)。模型在后台线程里回答,答回来才说;没开、超时或出错就说固定台词,不会卡住游戏。请求只发到你给的本机地址,内容是游戏里发生的事(主人叫什么、打了什么怪)。
自定义地图的单位名字
RPG 地图的单位、物品、英雄大多是地图自己新建的(四字码像 HC07、I00A),内置名字表里查不到。g.map_data 直接读当前这一局的地图文件:
md = g.map_data
md.name_of("HC07") # 'Optimus Primo' —— 地图改过的名字优先
md.hero_names("HC07") # 称号列表
md.hero_skills("OC10") # 地图给这个英雄定的技能
md.tooltip("I00A") # 说明文字
保护、优化过的地图(很多热门 RPG)不带标准的物体数据文件,名字会从图里的文本数据里读。实测本机 38 张 RPG / 自定义地图全部解析成功,其中 37 张拿到了单位名字。
做成方案分享
玩伴就是一个 openwar3.Bot 子类,可以做成 AI 方案 分享给别人。说明书里多写两项:
{"id": "my-buddy", "name": "我的玩伴", "entry": "my_buddy.py", "fair": false, "judge": false}
"fair": false:要用 JASS 通道(造单位、设盟友);"judge": false:不按对战规则判胜负。
实测记录
2026-09-24,演练实例,WarChasers 地图,2 倍速:
- JASS 通道 18 项检查全部通过:玩家槽、单位和句柄的往返换算、实数返回值、字符串参数、给空槽造单位、设盟友、改名、删单位;从玩家车道调、参数个数不对,都被正确拒绝。
- 玩伴:自己按过「按任意键继续」→ 出现在主人身边打招呼 → 跟进选英雄的能量圈、被地图发了英雄并接管 → 跟随(离主人 200 ~ 400)→ 打怪、打死一个说「漂亮!」→ 残血撤退 → 阵亡后被地图复活,接着跟。
还没做的
- 读不到玩家打的任意聊天文字。固定的聊天命令已经能用;要让玩伴真的陪你自由聊天,还需要拿到文字本身。
- 玩伴不懂具体地图的玩法(任务、商店、剧情)。它是通用的跟随、助战、加血;要懂某一张图,就在子类里按那张图写 ——
g.map_data能查名字,g.jass能调任何函数。这正是留给你自己决定的部分。