文件 玩法擴展

RPG 玩伴

在 RPG / 自訂地圖裡幫玩家配一個 AI 夥伴:跟著你、幫你打怪、殘血時幫你補血、陪你聊天。四種形態,繼承一個類別、改幾個屬性,就是你自己的玩伴。

不只是對戰。在 RPG、自訂地圖裡,你可以幫自己配一個 AI 夥伴:它跟著你走、幫你打怪,你殘血時幫你補血,沒事的時候陪你聊兩句 —— 台詞還能接本機 LLM。

怎麼用由你自己決定。 這件事拆成三層 API,從底到頂,哪一層都能直接用:

層是什麼適合
JASS 通道 g.jass地圖作者能用的 1291 個 JASS 函式,依名稱直接呼叫(造單位、設盟友、給物品、改名、顯示文字、復活英雄……)想自己打造玩法
便捷 APIg.spawn、g.set_alliance、g.player_slots、g.show_text、g.map_data:常用的幾件事都包好了寫自己的輔助腳本
玩伴框架openwar3.companion.Companion + openwar3.talk.Talk:繼承一下、改幾個屬性,就是一個會跟隨、助戰、補血、聊天的夥伴想要一個夥伴

只用於單機、區域網路自建的遊戲。造單位、設盟友這類操作是本機單方面修改世界:單人局(和電腦打)沒問題;多人局會讓其他玩家不同步,所以多人局裡 JASS 通道只放行唯讀的函式,玩伴自動退回「只說話」。

最快上手:在遠見裡一鍵開啟

  1. 選地圖:遠見「實例」頁 →「下一局設定」→ 地圖,選一張 RPG 地圖(遊戲目錄 Maps 底下 Scenario、Download 裡的都會列出來,例如 (4)WarChasers)。
  2. 選方案:實例卡片的「AI 方案」下拉選單裡選 玩伴範例(buddy) →「選定」。
  3. 開始測試:遊戲啟動後,你自己在遊戲視窗裡玩。玩伴 —— 一個叫「小聖」的聖騎士 —— 會出現在你身邊。

也可以用命令列:

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。範例裡的反應是:主人沒滿血就幫主人補一口血,否則說句話。
  • 頭像對白:打招呼、主人倒下、主人升級、玩伴回來,這幾句會用遊戲自己的頭像對白來說(底部頭像換成玩伴,畫面上出現字幕),其餘的用頭頂氣泡顯示。
  • 狀態面板:畫面左側一塊面板,顯示玩伴的血條、正在做什麼、心情(開心 / 興奮 / 緊張 / 害怕 / 難過)、擊殺和補血次數。它用 畫板 繪製,多人局也安全。

說話,以及本機 LLM

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。

接本機 LLM:Talk(llm=LocalLLM(url, model)),任何 OpenAI 相容的 API 都行(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)→ 打怪、打死一隻說「漂亮!」→ 殘血撤退 → 陣亡後被地圖復活,接著跟。

還沒做的

  1. 讀不到玩家打的任意聊天文字。固定的聊天命令已經能用;要讓玩伴真的陪你自由聊天,還需要拿到文字本身。
  2. 玩伴不懂具體地圖的玩法(任務、商店、劇情)。它做的是通用的跟隨、助戰、補血;要懂某一張地圖,就在子類別裡針對那張地圖寫 —— g.map_data 能查名稱,g.jass 能呼叫任何函式。這正是留給你自己決定的部分。