170 lines
9.7 KiB
GDScript
170 lines
9.7 KiB
GDScript
# quest_event_test —— §5.1 Quest EventManager 1:1(CPythonEventManager):
|
||
# EVENT_TYPE_MAP(42 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-1207,42 条)---
|
||
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/END:lWaitingTime ---
|
||
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 -> line;CLEAR / 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.next_button_type == "NEXT" and nx.adjust_line == 2,
|
||
"NEXT -> button type + adjust_line=2 (%d)" % nx.adjust_line)
|
||
var dn := qd.parse_script("x[NEXT][DONE]")
|
||
_ck(dn.has_next and dn.next_button_type == "DONE" and dn.adjust_line == 4,
|
||
"NEXT+DONE -> DONE button type + 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()
|