完善客户端功能并同步差距文档
This commit is contained in:
+93
-1
@@ -15,12 +15,14 @@ class FakeClient extends Node:
|
||||
var confirms := []
|
||||
var clicks := []
|
||||
var quests := []
|
||||
var script_buttons := []
|
||||
func is_in_game() -> bool: return true
|
||||
func get_quests() -> Array: return quests
|
||||
func script_answer(a) -> bool: answers.append(a); return true
|
||||
func quest_cancel() -> bool: cancels += 1; return true
|
||||
func quest_input(text) -> bool: inputs.append(text); return true
|
||||
func quest_confirm(yes, pid) -> bool: confirms.append([yes, pid]); return true
|
||||
func script_button(index) -> bool: script_buttons.append(index); return true
|
||||
func click_npc(vid) -> bool: clicks.append(vid); return true
|
||||
func get_entity(_v) -> Dictionary: return {}
|
||||
|
||||
@@ -56,6 +58,19 @@ func _run() -> void:
|
||||
_ck(p.choices == ["I accept", "Not now"], "[QUESTION] -> 2 choices")
|
||||
var p2: Dictionary = qd.parse_script("Bye.[DONE]")
|
||||
_ck(p2.has_next and p2.choices.is_empty(), "[DONE] -> continue button, no choices")
|
||||
var p3: Dictionary = qd.parse_script("[COLOR256 value;255 value;128 value;0]Gold[ENTER][COLOR value;0 value;1 value;0]Green[ITEM value;19][MOB value;77][WINDOW_SIZE value;640 value;360][TEXT_HORIZONTAL_ALIGN_CENTER][CONFIRM_WAIT value;30]")
|
||||
_ck(p3.body.contains("[color=#ff8000]") and p3.body.contains("Gold") and p3.body.contains("[color=#00ff00]"),
|
||||
"COLOR/COLOR256 -> RichTextLabel color spans")
|
||||
_ck(p3.body.contains("物品 #19") and p3.body.contains("怪物 #77"), "ITEM/MOB -> named placeholders")
|
||||
_ck(p3.window_size == [640, 360] and p3.text_centered and p3.confirm_wait and p3.confirm_timeout == 30,
|
||||
"WINDOW_SIZE/TEXT_CENTER/CONFIRM_WAIT metadata")
|
||||
var p4: Dictionary = qd.parse_script("[QUESTBUTTON idx(12) name(\"Read me\") icon_type(\"file\") icon_name(\"quest.sub\")][ADDMAPSIGNAL x(1200) y(3400)][SETCMAPPOS x(8) y(-4)][CLEARMAPSIGNAL][SET_CAMERA value(1) value(2)][FADE_OUT speed(0.25)]")
|
||||
_ck(p4.quest_buttons.size() == 1 and p4.quest_buttons[0].index == 12 and
|
||||
p4.quest_buttons[0].title == "Read me", "QUESTBUTTON metadata")
|
||||
_ck(p4.map_signals.size() == 1 and p4.map_signals[0] == Vector2(1200, 3400) and
|
||||
p4.clear_map_signals and p4.atlas_center == [8, -4], "map signal metadata")
|
||||
_ck(p4.camera_events.size() == 1 and p4.fade_events.size() == 1 and
|
||||
p4.fade_events[0].kind == "FADE_OUT", "camera/fade metadata")
|
||||
|
||||
# --- 对话弹窗 + 选择 -> script_answer ---
|
||||
fc.script_dialog.emit(1, "Pick one.[QUESTION arg(\"A\") arg(\"B\") arg(\"C\")]")
|
||||
@@ -94,6 +109,19 @@ func _run() -> void:
|
||||
_ck(fc.inputs == ["Alice"], "[INPUT] submit -> quest_input('Alice')")
|
||||
_ck(not qd.is_open(), "input submit closes quest dialog")
|
||||
|
||||
# EventManager side effects are surfaced to the interface owner.
|
||||
var received := []
|
||||
var cleared := []
|
||||
var signal_points := []
|
||||
qd.quest_button_received.connect(func(index, title, icon_type, icon_name): received.append([index, title, icon_type, icon_name]))
|
||||
qd.quest_button_cleared.connect(func(index): cleared.append(index))
|
||||
qd.map_signal_added.connect(func(x, y): signal_points.append(Vector2(x, y)))
|
||||
fc.script_dialog.emit(0, "[QUESTBUTTON idx(12) name(\"Read me\") icon_type(\"file\") icon_name(\"quest.sub\")][QUESTBUTTON_CLOSE idx(8)][ADDMAPSIGNAL x(1200) y(3400)][CLEARMAPSIGNAL]")
|
||||
await process_frame
|
||||
_ck(received == [[12, "Read me", "file", "quest.sub"]] and cleared == [8],
|
||||
"QUESTBUTTON events reach interface")
|
||||
_ck(signal_points == [Vector2(1200, 3400)], "ADDMAPSIGNAL reaches minimap owner")
|
||||
|
||||
# --- quest_confirm ---
|
||||
fc.quest_confirm_ask.emit("Join the party?", 30, 999)
|
||||
await process_frame
|
||||
@@ -112,8 +140,10 @@ func _run() -> void:
|
||||
var ql: Node = QuestLog.new()
|
||||
get_root().add_child(ql)
|
||||
ql.setup(fc, canvas)
|
||||
qd.quest_button_received.connect(func(index, title, icon_type, icon_name): ql.recv_quest(index, title, icon_type, icon_name))
|
||||
qd.quest_button_cleared.connect(ql.clear_quest)
|
||||
fc.quests = [
|
||||
{"index": 1, "title": "Slay 3 wolves", "counter_name": "Wolves", "counter_value": 2, "clock_name": "", "clock_value": 0},
|
||||
{"index": 1, "title": "Slay 3 wolves", "counter_name": "Wolves", "counter_value": 2, "clock_name": "Time", "clock_value": 65, "icon": "missing/icon.sub"},
|
||||
{"index": 2, "title": "", "counter_name": "", "counter_value": 0, "clock_name": "", "clock_value": 0}, # 完成 -> 不显示
|
||||
]
|
||||
ql.toggle()
|
||||
@@ -126,6 +156,68 @@ func _run() -> void:
|
||||
if String(lbl.text).begins_with("◆"):
|
||||
titles += 1
|
||||
_ck(titles == 1, "quest log shows 1 titled quest (got %d)" % titles)
|
||||
_ck(ql._clock_labels.has(1) and String(ql._clock_labels[1].text).contains("1分5秒"),
|
||||
"quest clock renders minutes and seconds")
|
||||
# ClientVS22 only renders QUEST_MAX_NUM=5 rows and maps the scrollbar to a
|
||||
# discrete questShowingStartIndex. Empty icon paths use the default scroll icon.
|
||||
fc.quests.clear()
|
||||
for i in range(7):
|
||||
fc.quests.append({"index": 100 + i, "title": "Quest %d" % i,
|
||||
"counter_name": "", "counter_value": 0, "clock_name": "", "clock_value": 0, "icon": ""})
|
||||
ql.refresh()
|
||||
await process_frame
|
||||
_ck(ql.quest_count() == 7 and ql._quest_scrollbar.visible, "quest log exposes all quests and scroll bar")
|
||||
_ck(ql.visible_quest_indices() == [0, 1, 2, 3, 4], "quest log initially shows 5 rows")
|
||||
ql.set_scroll_ratio(1.0)
|
||||
await process_frame
|
||||
_ck(ql.quest_start_index() == 2 and ql.visible_quest_indices() == [2, 3, 4, 5, 6],
|
||||
"quest scroll maps to last 5 rows")
|
||||
ql.select_quest(6)
|
||||
_ck(fc.script_buttons == [4, -2147483648 + 106], "quest row selection -> event quest button packet")
|
||||
fc.script_dialog.emit(0, "[QUESTBUTTON idx(4) name(\"Talk to guard\") icon_type(\"highlight\") icon_name(\"\")]")
|
||||
await process_frame
|
||||
_ck(ql.quest_button_count() == 1 and ql._button_root.visible, "quest button strip receives QUESTBUTTON")
|
||||
(ql._button_list.get_child(0) as Button).pressed.emit()
|
||||
_ck(fc.script_buttons == [4] and ql.quest_button_count() == 0, "quest button click -> script_button and remove")
|
||||
fc.quest_info.emit(1) # 刷新不炸
|
||||
await process_frame
|
||||
_ck(true, "quest_info refresh ok")
|
||||
|
||||
# --- 任务信件按钮条:多列定位(1:1 interfacemodule.__ArrangeQuestButton,§5.2 增量 109)---
|
||||
# 参考公式:yPos = 170*H//600 ; yCount = (H-330)//63 ; 第 i 个 =
|
||||
# (xPos + (i//yCount)*100 , yPos + (i%yCount)*63) ; xPos = 20 / 130(组队窗显示)
|
||||
_ck(QuestLog.quest_button_y_count(600.0) == 4, "quest btn: yCount(600) == 4")
|
||||
_ck(QuestLog.quest_button_y_count(1080.0) == 11, "quest btn: yCount(1080) == 11")
|
||||
_ck(QuestLog.quest_button_y_count(350.0) == 1, "quest btn: yCount clamps to 1 below ZeroDivision")
|
||||
_ck(QuestLog.quest_button_y_pos(600.0) == 170, "quest btn: yPos(600) == 170")
|
||||
_ck(QuestLog.quest_button_y_pos(1200.0) == 340, "quest btn: yPos scales with height")
|
||||
ql.set_screen_size_override(Vector2(800, 600))
|
||||
for i in range(6):
|
||||
ql.recv_quest(10 + i, "Letter %d" % i, "file", "")
|
||||
await process_frame
|
||||
# insert(0, btn):最新在前
|
||||
_ck(ql.quest_button_order() == [15, 14, 13, 12, 11, 10], "quest btn: newest inserted at front")
|
||||
_ck(ql.quest_button_position(15) == Vector2(20, 170), "quest btn: slot 0 at (xPos, yPos)")
|
||||
_ck(ql.quest_button_position(12) == Vector2(20, 359), "quest btn: slot 3 stacks down one column")
|
||||
_ck(ql.quest_button_position(11) == Vector2(120, 170), "quest btn: slot 4 wraps to column 2")
|
||||
_ck(ql.quest_button_position(10) == Vector2(120, 233), "quest btn: slot 5 is column 2 row 1")
|
||||
_ck(ql._button_list.get_child(0).position == Vector2(20, 170), "quest btn: node position matches formula")
|
||||
# 同 index 再来 -> 移到最前
|
||||
ql.recv_quest(13, "Letter 3 (again)", "file", "")
|
||||
await process_frame
|
||||
_ck(ql.quest_button_order() == [13, 15, 14, 12, 11, 10], "quest btn: re-received index moves to front")
|
||||
# 组队窗显示 -> xPos 130
|
||||
ql.set_party_shown(true)
|
||||
await process_frame
|
||||
_ck(ql.quest_button_position(13) == Vector2(130, 170), "quest btn: party window shifts xPos to 130")
|
||||
_ck(ql._button_list.get_child(0).position == Vector2(130, 170), "quest btn: node repositioned on party toggle")
|
||||
ql.set_party_shown(false)
|
||||
ql.clear_quest(13)
|
||||
await process_frame
|
||||
_ck(not ql.quest_button_order().has(13) and ql.quest_button_count() == 5,
|
||||
"quest btn: clear drops index from order")
|
||||
for i in range(6):
|
||||
ql.clear_quest(10 + i)
|
||||
await process_frame
|
||||
_ck(ql.quest_button_order().is_empty() and not ql._button_root.visible,
|
||||
"quest btn: emptied strip hides root")
|
||||
|
||||
Reference in New Issue
Block a user