Files
mtgodot-poc/project/quest_event_test.gd
T

168 lines
9.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# quest_event_test —— §5.1 Quest EventManager 1:1CPythonEventManager):
# EVENT_TYPE_MAP42 tag+ GetScriptEventIndex(前导 '/'+ ProcessEventSet 分发 +
# SEventSet 状态(字段名与 REF/UserInterface/PythonEventManager.h 一致)。
# godot --headless --path project --script quest_event_test.gd
# 退出码 0 = 全过。
extends SceneTree
const QuestDialog = preload("res://ui/quest_dialog.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
_run()
if _fail == 0:
print("PASS: quest_event_test (§5.1 EventTypeMap + ProcessEventSet 1:1)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
var qd := QuestDialog.new()
# --- 1. EventTypeMap 完整性(PythonEventManager.cpp:1148-120742 条)---
var tags := ["LETTER", "COLOR", "DELAY", "ENTER", "WAIT", "CLEAR", "QUESTION", "NEXT",
"DONE", "LEFTIMAGE", "TOPIMAGE", "BGIMAGE", "IMAGE", "ADDMAPSIGNAL", "CLEARMAPSIGNAL",
"SETMSGPOS", "ADJMSGPOS", "SETCMAPPOS", "QUESTBUTTON", "QUESTBUTTON_CLOSE", "SLEEP",
"SET_CAMERA", "BLEND_CAMERA", "RESTORE_CAMERA", "FADE_OUT", "FADE_IN", "WHITE_OUT",
"WHITE_IN", "CLEAR_TEXT", "TEXT_HORIZONTAL_ALIGN_CENTER", "TITLE_IMAGE", "RUN_CINEMA",
"DUNGEON_RESULT", "ITEM", "MOB", "COLOR256", "WINDOW_SIZE", "INPUT", "CONFIRM_WAIT",
"END_CONFIRM_WAIT", "INSERT_IMAGE", "SELECT_ITEM"]
_ck(tags.size() == 42, "EventTypeMap: 42 tags enumerated (%d)" % tags.size())
var missing := 0
for t in tags:
if not qd.EVENT_TYPE_MAP.has(t):
missing += 1
printerr(" missing EVENT_TYPE_MAP[%s]" % t)
_ck(missing == 0, "EventTypeMap: every tag present")
_ck(qd.EVENT_TYPE_MAP.size() == 42, "EventTypeMap: no extra keys (%d)" % qd.EVENT_TYPE_MAP.size())
# --- 2. GetScriptEventIndex:前导 '/' = EVENT_POSITION_END ---
var s := qd._get_script_event_index("DELAY")
_ck(s.ok and s.position == qd.EVENT_POSITION_START and s.type == qd.EVT_DELAY, "GSEI: DELAY -> START/EVT_DELAY")
var e := qd._get_script_event_index("/DELAY")
_ck(e.ok and e.position == qd.EVENT_POSITION_END and e.type == qd.EVT_DELAY, "GSEI: /DELAY -> END/EVT_DELAY")
_ck(qd._get_script_event_index("nope").ok == false, "GSEI: unknown -> ok=false")
_ck(qd._get_script_event_index("letter").type == qd.EVT_LETTER, "GSEI: case-insensitive")
# --- 3. DELAY START/ENDlWaitingTime ---
var d := qd.parse_script("[DELAY value(40)]hi")
_ck(d.waiting_time == 40, "DELAY START -> lWaitingTime=40 (%d)" % d.waiting_time)
var d2 := qd.parse_script("[DELAY value(40)]hi[/DELAY]")
_ck(d2.waiting_time == 10, "/DELAY -> lWaitingTime=c_lNormal_Waiting_Time(10) (%d)" % d2.waiting_time)
# --- 4. LETTER / 裸文本 / iRestrictedCharacterCount(30) 断行 ---
var lt := qd.parse_script("[LETTER value(\"0123456789012345678901234567890123\")]tail")
# 显式 [LETTER] 34 字 >= 30 -> __InsertLine"tail" 落到下一行。
_ck(lt.body.split("\n").size() == 2, "LETTER: >=iRestrictedCharacterCount wraps (%d lines)" % lt.body.split("\n").size())
var raw := qd.parse_script("这是一段没有任何 ENTER 的很长的网络文本用来验证裸文本不会被 30 字断行处理掉哦哦哦哦哦哦哦")
_ck(raw.body.split("\n").size() == 1, "raw text: no 30-char guillotine (%d lines)" % raw.body.split("\n").size())
# --- 5. ENTER -> lineCLEAR / CLEAR_TEXT -> reset ---
_ck(qd.parse_script("a[ENTER]b").body == "a\nb", "ENTER -> line break")
_ck(qd.parse_script("gone[CLEAR]kept").body == "kept", "CLEAR -> body reset")
_ck(qd.parse_script("gone[CLEAR_TEXT]kept").body == "kept", "CLEAR_TEXT -> body reset")
# --- 6. QUESTION -> choices + nAnswer ---
var q := qd.parse_script("Pick[QUESTION arg(\"yes\") arg(\"no\") arg(\"maybe\")]")
_ck(q.choices == ["yes", "no", "maybe"] and q.answer_count == 3, "QUESTION -> 3 choices + nAnswer=3")
# --- 7. NEXT / DONE -> has_next + iAdjustLine += 2 ---
var nx := qd.parse_script("x[NEXT]")
_ck(nx.has_next and nx.adjust_line == 2, "NEXT -> has_next + adjust_line=2 (%d)" % nx.adjust_line)
var dn := qd.parse_script("x[NEXT][DONE]")
_ck(dn.has_next and dn.adjust_line == 4, "NEXT+DONE -> adjust_line=4 (%d)" % dn.adjust_line)
# --- 8. WAIT -> isLock ---
var w := qd.parse_script("x[WAIT]y")
_ck(w.is_locked, "WAIT -> isLock true")
# --- 9. COLOR / COLOR256 START(r/g/b & 位置) + END ---
var c1 := qd.parse_script("[COLOR r(0) g(1) b(0)]G[/COLOR]")
_ck(c1.body.contains("[color=#00ff00]") and c1.body.contains("[/color]"), "COLOR r/g/b named -> span")
var c2 := qd.parse_script("[COLOR256 value;255 value;128 value;0]O")
_ck(c2.body.contains("[color=#ff8000]"), "COLOR256 positional -> /255 span")
# --- 10. INPUT / SELECT_ITEM ---
_ck(qd.parse_script("name?[INPUT]").has_input, "INPUT -> has_input")
_ck(qd.parse_script("[SELECT_ITEM]").has_select_item, "SELECT_ITEM -> has_select_item")
# --- 11. CONFIRM_WAIT + END_CONFIRM_WAIT ---
var cw := qd.parse_script("sure?[CONFIRM_WAIT timeout(30)]")
_ck(cw.confirm_wait and cw.confirm_timeout == 30, "CONFIRM_WAIT -> isConfirmWait + timeout=30")
var ce := qd.parse_script("sure?[CONFIRM_WAIT timeout(30)][END_CONFIRM_WAIT]")
_ck(ce.confirm_wait == false and ce.end_confirm, "END_CONFIRM_WAIT -> isConfirmWait cleared + CloseSelf")
# --- 12. DUNGEON_RESULT -> 9 ints ---
var dr := qd.parse_script("[DUNGEON_RESULT killstone_count(2) killmob_count(40) find_hidden(1) hidden_total(3) use_potion(5) is_revived(0) killallmob(1) total_time(600) bonus_exp(1234)]")
_ck(dr.dungeon_result.get("killmob_count", -1) == 40 and dr.dungeon_result.get("bonus_exp", -1) == 1234
and dr.dungeon_result.size() == 9, "DUNGEON_RESULT -> 9 named ints")
# --- 13. INSERT_IMAGE(有 image_name / 无 -> item icon+ iAdjustLine += 2 ---
var ii := qd.parse_script("[INSERT_IMAGE image_name(\"a.tga\") title(\"T\") desc(\"D\") index(1) total(3)]")
_ck(ii.insert_images.size() == 1 and ii.insert_images[0].image_name == "a.tga"
and ii.insert_images[0].is_item_icon == false and ii.adjust_line == 2, "INSERT_IMAGE with image_name")
var ic := qd.parse_script("[INSERT_IMAGE image_type(\"item\") idx(19) title(\"T\") desc(\"D\") index(0) total(1)]")
_ck(ic.insert_images.size() == 1 and ic.insert_images[0].is_item_icon
and ic.insert_images[0].idx == 19, "INSERT_IMAGE empty image_name -> item icon")
# --- 14. IMAGE 家族 ---
var im := qd.parse_script("[IMAGE x(10) y(20) src(\"pic.tga\")][LEFTIMAGE src(\"L.tga\")][TOPIMAGE src(\"T.tga\")][BGIMAGE src(\"B.tga\")][TITLE_IMAGE src(\"TT.tga\")]")
_ck(im.images.size() == 4, "IMAGE/LEFT/TOP/BG -> 4 image entries (%d)" % im.images.size())
_ck(im.images[0].get("path", "") == "pic.tga" and im.images[0].get("x", 0) == 10, "IMAGE x/y/src")
_ck(im.images[1].get("panel", "") == "LEFTIMAGE" and im.images[2].get("panel", "") == "TOPIMAGE", "LEFT/TOP panels")
_ck(im.title_image == "TT.tga", "TITLE_IMAGE -> title_image")
# --- 15. map signals ---
var ms := qd.parse_script("[ADDMAPSIGNAL x(1200) y(3400)][SETCMAPPOS x(8) y(-4)][CLEARMAPSIGNAL]")
_ck(ms.map_signals == [Vector2(1200, 3400)] and ms.atlas_center == [8, -4] and ms.clear_map_signals,
"ADDMAPSIGNAL / SETCMAPPOS / CLEARMAPSIGNAL")
# --- 16. QUESTBUTTON / QUESTBUTTON_CLOSE ---
var qb := qd.parse_script("[QUESTBUTTON idx(12) name(\"Read me\") icon_type(\"file\") icon_name(\"q.sub\")][QUESTBUTTON_CLOSE idx(8)]")
_ck(qb.quest_buttons.size() == 1 and qb.quest_buttons[0].index == 12
and qb.quest_buttons[0].title == "Read me" and qb.quest_buttons[0].icon_name == "q.sub", "QUESTBUTTON keyed")
_ck(qb.quest_button_closes == [8], "QUESTBUTTON_CLOSE idx -> close list")
# --- 17. camera SET/BLEND/RESTORE ---
var cam := qd.parse_script("[SET_CAMERA x(1) y(2) z(3) distance(50)][BLEND_CAMERA x(4) blendtime(1500)][RESTORE_CAMERA]")
_ck(cam.camera_events.size() == 3, "camera: 3 events")
_ck(cam.camera_events[0].get("setting", {}).get("distance", 0) == 50, "SET_CAMERA setting.distance")
_ck(cam.camera_events[1].get("blendtime", 0) == 1500 and cam.camera_events[2].kind == "RESTORE_CAMERA", "BLEND blendtime + RESTORE")
# --- 18. FADE_* / WHITE_* -> fade_events + isWaitFlag ---
var fx := qd.parse_script("[FADE_OUT speed(0.25)]")
_ck(fx.fade_events.size() == 1 and fx.fade_events[0].kind == "FADE_OUT"
and is_equal_approx(fx.fade_events[0].speed, 0.25) and fx.wait_flag, "FADE_OUT -> event + wait_flag")
var wx := qd.parse_script("[WHITE_IN speed(1)]")
_ck(wx.fade_events[0].kind == "WHITE_IN" and wx.wait_flag, "WHITE_IN -> event + wait_flag")
# --- 19. WINDOW_SIZE / TEXT_HORIZONTAL_ALIGN_CENTER ---
var ws := qd.parse_script("[WINDOW_SIZE width(640) height(360)][TEXT_HORIZONTAL_ALIGN_CENTER]")
_ck(ws.window_size == [640, 360] and ws.text_centered, "WINDOW_SIZE keyed + TEXT_CENTER")
# --- 20. ITEM / MOB 占位(无 proto/entity 时回退命名)---
var nm := qd.parse_script("kill [MOB value(77)] get [ITEM value(19)]")
_ck(nm.body.contains("怪物 #77") and nm.body.contains("物品 #19"), "ITEM/MOB -> named placeholders")
# --- 21. SETMSGPOS / ADJMSGPOS 故意留空(PythonEventManager.cpp:562/566---
var mp := qd.parse_script("body[SETMSGPOS x(1) y(2)][ADJMSGPOS x(3) y(4)]end")
_ck(mp.body == "bodyend", "SETMSGPOS/ADJMSGPOS -> no-op, no body change")
# --- 22. 未知 tag 优雅跳过 ---
var uk := qd.parse_script("keep[TOTALLY_MADE_UP arg(1)]this")
_ck(uk.body == "keepthis", "unknown tag -> skipped, text preserved")
# --- 23. RUN_CINEMA 首命令特例 -> cinema_file seam ---
var rc := qd.parse_script("[RUN_CINEMA value(\"intro.msc\")]")
_ck(rc.cinema_file == "intro.msc", "RUN_CINEMA (first cmd) -> cinema_file")
var rc2 := qd.parse_script("hi[RUN_CINEMA value(\"x.msc\")]")
_ck(rc2.cinema_file == "" and rc2.body == "hi", "RUN_CINEMA (non-first) -> silent no-op")
qd.free()