From 9c3c470f3e01a70d4065aef5854e020b5041b373 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 14:24:10 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E9=99=A2=E5=AD=90=E5=88=A4=E6=96=AD=20c0?= =?UTF-8?q?2=5Fball=5Fgame=5Fstage=20>=3D=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scene/ground/scene/c02/s03_院子.gd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scene/ground/scene/c02/s03_院子.gd b/scene/ground/scene/c02/s03_院子.gd index 3c4a338a..e6cb3e3f 100644 --- a/scene/ground/scene/c02/s03_院子.gd +++ b/scene/ground/scene/c02/s03_院子.gd @@ -58,7 +58,6 @@ func _on_ground_ready() -> void: counter.get_node("点燃").modulate.a = 1.0 $VibeSfx氛围.switch_to("c02_火灾") $"../DirectionalLight2D".energy = 0 - eavesdrop_window = $"../DeployLayer/李氏赖子房间人影" madman_npc = $"../DeployLayer/Npc井边疯子" madman_npc.visible = false @@ -66,7 +65,7 @@ func _on_ground_ready() -> void: burning_layer = $"../DeployLayer/火灾" var closeup_well = $"../DeployLayer/Closeup井" # 第一章霸凌阶段(弹珠游戏结束为开始)隐藏疯子与李癞偷听,第二章开始后显示 - if EventManager.get_stage(&"c02_ball_game_stage") == 3 and EventManager.get_chapter_stage() <= 2: + if EventManager.get_stage(&"c02_ball_game_stage") >= 3 and EventManager.get_chapter_stage() <= 2: _setup_bully_or_burning(true) # 弹珠游戏结束后就开始霸凌,此时禁止偷听。第一章结束后(火灾结束),第二章再偷听 $"../DeployLayer/Ambush偷听对话".enabled = false From 70f9d35d079b27f35e7e8dc567520937012e9ee1 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 15:23:00 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E4=BC=98=E5=8C=96NPC2D=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20hook=5Fspeaking&unhook=5Fspeaking=EF=BC=9B=E5=AD=A4?= =?UTF-8?q?=E5=84=BF=E9=99=A2=E7=AC=AC=E4=B8=80=E6=AC=A1=E4=B8=8E=E5=A4=A7?= =?UTF-8?q?=E8=83=96=E5=AF=B9=E8=AF=9D=E4=BD=BF=E7=94=A8=E8=AF=A5=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scene/entity/npc.gd | 317 ++++++++++++++----- scene/ground/scene/c01/s06_孤儿院长廊围墙.gd | 15 +- scene/ground/scene/c01/s07_书店外.gd | 13 +- scene/ground/scene/c01/s07_书店外.tscn | 20 +- 4 files changed, 275 insertions(+), 90 deletions(-) diff --git a/scene/entity/npc.gd b/scene/entity/npc.gd index 1e57d9ec..ff40e767 100644 --- a/scene/entity/npc.gd +++ b/scene/entity/npc.gd @@ -2,10 +2,15 @@ class_name Npc2D extends AnimatedSprite2D signal interacted -# 在 unlock player 之前发射 -signal talk_finished +signal talk_finished # 在 unlock player 之前发射 -# <0 means no walk to edge +# 常量定义 +const SPEAKING_SIGN_FADE_DURATION := 0.3 +const SPEAKING_SCALE_MULTIPLIER := 1.3 + +enum SpeakingSignMode { HIDDEN = 0, SILENT = 1, SPEAKING = 2 } + +# 导出变量 @export var snap_to_edge := true @export var walk_to_edge_width := 25.0 @export var action_key := 4 @@ -14,41 +19,47 @@ signal talk_finished enabled = val if is_node_ready(): _align_signs_status() + @export var sign_mark_height := 10.0: set(val): sign_mark_height = val - if is_node_ready(): + if is_node_ready() and sign_mark: sign_mark.sign_mark_offset.y = -sign_mark_height + @export var speaking_sign_height := 60.0: set(val): speaking_sign_height = val - if is_node_ready(): + if is_node_ready() and speaking_sign: speaking_sign.position.y = -speaking_sign_height + @export var sign_x_offset := 0.0: set(val): sign_x_offset = val if is_node_ready(): - speaking_sign.position.x = sign_x_offset - sign_mark.position.x = sign_x_offset + _update_sign_x_positions() + @export var collision_width_and_x := Vector2(20.0, 0): set(val): collision_width_and_x = val if is_node_ready(): - var shape = area2d.get_node("CollisionShape2D").shape - shape.size.x = collision_width_and_x.x - area2d.position.x = collision_width_and_x.y + _update_collision_shape() + +# 节点引用 @onready var speaking_animation = %SpeakingAnimationPlayer @onready var speaking_sign = %SpeakingSign2D as Node2D @onready var sign_mark = %Sign as Sign @onready var sign_snapper = %SignSnapper as SignSnapper @onready var area2d = %Area2D as Area2D +# 内部变量 var ground_archive: GroundArchive -# 尝试互动的次数 var icount: int: set(val): + if icount == val: + return icount = val - ground_archive.set_pair(name, "icount", val) + if ground_archive: + ground_archive.set_pair(name, "icount", val) _align_signs_status() var dialogue_title := "" @@ -56,105 +67,246 @@ var dialogue_res = preload("res://asset/dialogue/npc.dialogue") var base_scale := Vector2.ONE var base_mod := Color.WHITE_SMOKE - var speaking_sign_tween: Tween - -# 0 hide; 1 silent; 2 speaking -var speaking_sign_mode := 0: +var speaking_sign_mode := SpeakingSignMode.HIDDEN: set(val): - if speaking_sign_mode != val: - speaking_sign_mode = val - if speaking_sign_tween and speaking_sign_tween.is_valid(): - speaking_sign_tween.kill() - speaking_sign_tween = create_tween() - if val == 0: - speaking_sign_tween.tween_property(speaking_sign, "modulate:a", 0.0, 0.3) - speaking_animation.stop() - elif val == 1: - speaking_sign_tween.tween_property(speaking_sign, "modulate", base_mod, 0.3) - speaking_sign_tween.parallel().tween_property( - speaking_sign, "scale", base_scale, 0.3 - ) - speaking_animation.play("speaking") - elif val == 2: - speaking_sign_tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.3) - speaking_sign_tween.parallel().tween_property( - speaking_sign, "scale", base_scale * 1.3, 0.3 - ) - speaking_animation.play("speaking") + if speaking_sign_mode == val: + return + speaking_sign_mode = val + _update_speaking_sign_mode() + +# 强制播放状态管理 +var is_hooked := false +var hook_id := 0 # 用于追踪hook会话,避免异步问题 func _ready() -> void: - # sign position - sign_mark.sign_mark_offset.y = -sign_mark_height - speaking_sign.position.y = -speaking_sign_height - sign_mark.position.x = sign_x_offset - speaking_sign.position.x = sign_x_offset - # collisiong shape - var shape = area2d.get_node("CollisionShape2D").shape - shape.size.x = collision_width_and_x.x - area2d.position.x = collision_width_and_x.y - sign_snapper.action_on_arrived = action_key - sign_snapper.radius = walk_to_edge_width - sign_snapper.enabled = snap_to_edge - # 设置 speaking_sign 默认值 - base_scale = speaking_sign.scale - base_mod = speaking_sign.modulate - speaking_sign.modulate.a = 0.0 + _initialize_components() + if Engine.is_editor_hint(): - # editor 下都显示 + _setup_editor_preview() + return + + _setup_game_mode() + _align_signs_status() + + +func _initialize_components() -> void: + # 设置标记位置 + if sign_mark: + sign_mark.sign_mark_offset.y = -sign_mark_height + sign_mark.position.x = sign_x_offset + + if speaking_sign: + speaking_sign.position.y = -speaking_sign_height + speaking_sign.position.x = sign_x_offset + base_scale = speaking_sign.scale + base_mod = speaking_sign.modulate + speaking_sign.modulate.a = 0.0 + + # 设置碰撞形状 + _update_collision_shape() + + # 配置 sign_snapper + if sign_snapper: + sign_snapper.action_on_arrived = action_key + sign_snapper.radius = walk_to_edge_width + sign_snapper.enabled = snap_to_edge + + +func _setup_editor_preview() -> void: + if speaking_sign: speaking_sign.visible = true speaking_sign.modulate.a = 1.0 - speaking_sign.get_node("Sprite2D").position.x = -60.0 - speaking_sign.get_node("Sprite2D").frame = 2 + var sprite = speaking_sign.get_node_or_null("Sprite2D") + if sprite: + sprite.position.x = -60.0 + sprite.frame = 2 + + if sign_mark: sign_mark.display_sign = true - return - # setup default value + + +func _setup_game_mode() -> void: + # 获取存档数据 ground_archive = ArchiveManager.archive.ground_archive() icount = ground_archive.get_value(name, "icount", 0) - if snap_to_edge: + + # 连接信号 + if snap_to_edge and sign_snapper: sign_snapper.arrived.connect(_on_interacted) - else: + elif sign_mark: sign_mark.interacted.connect(_on_interacted) - # sign_mark.cancel.connect(_stop_speaking) - sign_mark.toggle_active.connect(_on_toggle_active) + + if sign_mark: + sign_mark.toggle_active.connect(_on_toggle_active) + visibility_changed.connect(_on_visibility_changed) + + # 开始动画 if sprite_frames and animation: play() +func _update_sign_x_positions() -> void: + if speaking_sign: + speaking_sign.position.x = sign_x_offset + if sign_mark: + sign_mark.position.x = sign_x_offset + + +func _update_collision_shape() -> void: + if not area2d: + return + + var collision_shape = area2d.get_node_or_null("CollisionShape2D") + if collision_shape and collision_shape.shape: + collision_shape.shape.size.x = collision_width_and_x.x + area2d.position.x = collision_width_and_x.y + + +func _update_speaking_sign_mode() -> void: + if not speaking_sign: + return + + # 清理之前的补间动画 + if speaking_sign_tween and speaking_sign_tween.is_valid(): + speaking_sign_tween.kill() + + speaking_sign_tween = create_tween() + + match speaking_sign_mode: + SpeakingSignMode.HIDDEN: + speaking_sign_tween.tween_property( + speaking_sign, "modulate:a", 0.0, SPEAKING_SIGN_FADE_DURATION + ) + if speaking_animation: + speaking_animation.stop() + + SpeakingSignMode.SILENT: + speaking_sign_tween.tween_property( + speaking_sign, "modulate", base_mod, SPEAKING_SIGN_FADE_DURATION + ) + speaking_sign_tween.parallel().tween_property( + speaking_sign, "scale", base_scale, SPEAKING_SIGN_FADE_DURATION + ) + if speaking_animation: + speaking_animation.play("speaking") + + SpeakingSignMode.SPEAKING: + speaking_sign_tween.tween_property( + speaking_sign, "modulate", Color.WHITE, SPEAKING_SIGN_FADE_DURATION + ) + speaking_sign_tween.parallel().tween_property( + speaking_sign, + "scale", + base_scale * SPEAKING_SCALE_MULTIPLIER, + SPEAKING_SIGN_FADE_DURATION + ) + if speaking_animation: + speaking_animation.play("speaking") + + func _on_visibility_changed() -> void: _align_signs_status() -func _align_signs_status(): - sign_mark.enabled = enabled and is_visible_in_tree() - sign_mark.display_sign = icount == 0 - speaking_sign.visible = enabled and icount > 0 +func _align_signs_status() -> void: + if not is_node_ready(): + return + var is_active = enabled and is_visible_in_tree() + if sign_mark: + sign_mark.enabled = is_active + sign_mark.display_sign = icount == 0 + if speaking_sign: + speaking_sign.visible = enabled and (icount > 0 or is_hooked) func _on_toggle_active(activated: bool) -> void: + # 如果处于hook状态,不响应正常的toggle + if is_hooked: + return if not activated: - speaking_sign_mode = 0 - elif speaking_sign_mode == 0: - speaking_sign_mode = 1 + speaking_sign_mode = SpeakingSignMode.HIDDEN + elif speaking_sign_mode == SpeakingSignMode.HIDDEN: + speaking_sign_mode = SpeakingSignMode.SILENT func _on_interacted() -> void: - # play dialogue - if dialogue_title: - SceneManager.lock_player(0, action_key) - icount += 1 + if not dialogue_title: + return + + # 如果正在hook播放,先取消hook + if is_hooked: + _cancel_hook() + + SceneManager.lock_player(0, action_key) + icount += 1 + + if ground_archive: ground_archive.set_pair(name, "icount", icount) - DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title) - interacted.emit() - var out_of_range = speaking_sign_mode == 0 - speaking_sign_mode = 2 - await DialogueManager.dialogue_ended - speaking_sign_mode = 0 if out_of_range else 1 - # 在 unlock 之前发射 - talk_finished.emit() - SceneManager.unlock_player() + + DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title) + interacted.emit() + + var was_out_of_range = speaking_sign_mode == SpeakingSignMode.HIDDEN + speaking_sign_mode = SpeakingSignMode.SPEAKING + + await DialogueManager.dialogue_ended + + speaking_sign_mode = SpeakingSignMode.HIDDEN if was_out_of_range else SpeakingSignMode.SILENT + talk_finished.emit() + SceneManager.unlock_player() + + +# 新增:强制播放说话动画 +func hook_speaking() -> int: + # 强制显示说话气泡动画 + # 返回值:hook会话ID,用于验证unhook的有效性 + is_hooked = true + hook_id += 1 + var current_hook_id = hook_id + + # 确保speaking_sign可见 + if speaking_sign and not speaking_sign.visible: + speaking_sign.visible = true + + # 保存当前状态并切换到说话模式 + speaking_sign_mode = SpeakingSignMode.SPEAKING + + return current_hook_id + + +# 新增:退出强制播放 +func unhook_speaking(session_id: int = -1) -> void: + # 退出强制播放模式 + # 参数: + # session_id: hook会话ID,如果不匹配则忽略此次unhook + # 如果已经不在hook状态,或session_id不匹配,则忽略 + if not is_hooked or (session_id != -1 and session_id != hook_id): + return + _cancel_hook() + + +# 新增:内部取消hook的方法 +func _cancel_hook() -> void: + # 内部使用的取消hook方法 + is_hooked = false + + # 恢复到适当的状态 + if sign_mark and sign_mark.activated: + speaking_sign_mode = SpeakingSignMode.SILENT + else: + speaking_sign_mode = SpeakingSignMode.HIDDEN + + # 重新对齐显示状态 + _align_signs_status() + + +# 新增:检查是否正在hook播放 +func is_hook_speaking() -> bool: + """返回当前是否处于强制播放状态""" + return is_hooked func _get(property: StringName) -> Variant: @@ -172,8 +324,9 @@ func _set(property: StringName, value: Variant) -> bool: func _get_property_list() -> Array[Dictionary]: var hint_str = "" - if Engine.is_editor_hint(): + if Engine.is_editor_hint() and dialogue_res: hint_str = ",".join(dialogue_res.get_ordered_titles()) + return [ { "name": "dialogue_title", diff --git a/scene/ground/scene/c01/s06_孤儿院长廊围墙.gd b/scene/ground/scene/c01/s06_孤儿院长廊围墙.gd index 8a7f65a3..5e5170de 100644 --- a/scene/ground/scene/c01/s06_孤儿院长廊围墙.gd +++ b/scene/ground/scene/c01/s06_孤儿院长廊围墙.gd @@ -94,8 +94,10 @@ func pre_game_intro(): var p = $"../DeployLayer/四小孩画鬼差的对话ambush/FocusPoint" camera.focus_node(p, 3.0) await Util.wait(2.0) + _hook_npc3_speaking() DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s06_四个小孩画鬼差的对话") await DialogueManager.dialogue_ended + _unhook_npc3_speaking() # 重置镜头 SceneManager.focus_player_and_reset_zoom(2.5) await Util.wait(2.5) @@ -123,14 +125,17 @@ func game_intro() -> void: # DialogueManager.show_dialogue_balloon( # dialogue_c01, "c01_s06_谈论鬼差与猫鼠游戏", [GlobalConfig.DIALOG_IGNORE_INPUT] # ) + _hook_npc3_speaking() DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s06_谈论鬼差与猫鼠游戏") - DialogueManager.dialogue_ended.connect(_game_counting_down, CONNECT_ONE_SHOT) + await DialogueManager.dialogue_ended + _game_counting_down() func _game_counting_down(_res = null): $"Sfx猫鼠游戏".play() DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s06_猫鼠游戏BGM开始") await DialogueManager.dialogue_ended + _unhook_npc3_speaking() # 重置镜头 SceneManager.focus_player_and_reset_zoom(2.5) SceneManager.release_player() @@ -145,6 +150,14 @@ func _game_counting_down(_res = null): cat.get_node("猫咪嘶吼音效").play() +func _hook_npc3_speaking(_res = null) -> void: + game_kid.get_node("Npc对话3").hook_speaking() + + +func _unhook_npc3_speaking(_res = null) -> void: + game_kid.get_node("Npc对话3").unhook_speaking() + + # 玩家与三个小孩的互动计数 func _on_talked(id: int): #talk count diff --git a/scene/ground/scene/c01/s07_书店外.gd b/scene/ground/scene/c01/s07_书店外.gd index de5287bc..b3266430 100644 --- a/scene/ground/scene/c01/s07_书店外.gd +++ b/scene/ground/scene/c01/s07_书店外.gd @@ -71,7 +71,7 @@ func _knock_door(): await Util.wait(2.2) $"敲门音效".play() await Util.wait(1.2) - var stream = preload("res://asset/audio/sfx/交互/序章/03_书店外黄昏_开门.ogg") + var stream = preload("uid://ehgd455wq8to") AudioManager.play_sfx(stream) @@ -95,3 +95,14 @@ func seller_interacted(): # 播放获得动画 SceneManager.enable_prop_item("prop_信碎片2") SceneManager.release_player() + + +func jiandu_dialog_triggered() -> void: + var jiandu = $"../DeployLayer/举碗小孩/Npc监督小孩" + jiandu.hook_speaking() + DialogueManager.dialogue_ended.connect(_on_jiandu_dialog_ended, CONNECT_ONE_SHOT) + + +func _on_jiandu_dialog_ended(_res) -> void: + var jiandu = $"../DeployLayer/举碗小孩/Npc监督小孩" + jiandu.unhook_speaking() diff --git a/scene/ground/scene/c01/s07_书店外.tscn b/scene/ground/scene/c01/s07_书店外.tscn index 055662ee..2ea2c4cb 100644 --- a/scene/ground/scene/c01/s07_书店外.tscn +++ b/scene/ground/scene/c01/s07_书店外.tscn @@ -203,12 +203,6 @@ sprite_frames = ExtResource("6_thm8f") animation = &"杂戏团黄昏-其余小孩" autoplay = "杂戏团黄昏-其余小孩" -[node name="Ambush监督小孩" parent="Ground/DeployLayer/其余小孩" instance=ExtResource("9_f61dl")] -position = Vector2(-688, 53) -cooldown_time = 0.1 -lock_player_on_playing_dialogue = false -hook_dialogue_title = "c01_s07_监督小孩吉祥话" - [node name="Npc吉祥话1" parent="Ground/DeployLayer/其余小孩" instance=ExtResource("6_fw22n")] position = Vector2(-44, 78) sign_mark_height = 23.0 @@ -237,6 +231,20 @@ autoplay = "杂戏团黄昏_举碗小孩" position = Vector2(6, 57) note_key = "c01_s07_钱碗" +[node name="Npc监督小孩" parent="Ground/DeployLayer/举碗小孩" instance=ExtResource("6_fw22n")] +position = Vector2(6, 72) +snap_to_edge = false +enabled = false +sign_mark_height = 11.0 +speaking_sign_height = 54.0 + +[node name="Ambush监督小孩" parent="Ground/DeployLayer/举碗小孩" instance=ExtResource("9_f61dl")] +position = Vector2(-825, 53) +cooldown_time = 0.1 +lock_player_on_playing_dialogue = false +hook_dialogue_title = "c01_s07_监督小孩吉祥话" +hook_method = "jiandu_dialog_triggered" + [node name="报童" parent="Ground/DeployLayer" index="10" instance=ExtResource("9_slaub")] position = Vector2(2080, 6) sprite_frames = ExtResource("6_thm8f") From 4063434c26f31ee0ad3b769521241ef33d58fc45 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 16:01:09 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9B=E4=B9=A6=E5=BA=97=E6=91=94=E5=80=92?= =?UTF-8?q?=E5=90=8E=E5=9B=9E=E5=BF=86=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- asset/dialogue/item_description.csv | 9 +++++---- asset/dialogue/item_description.dialogue | 9 +++++---- manager/config_manager/global_config.gd | 7 +++++-- scene/dialog/balloon.gd | 2 ++ scene/effect/dizzy_shader.gd | 2 +- scene/ground/scene/c01/s08_书店.gd | 17 ++++++++++------- scene/ground/scene/c01/s08_书店.tscn | 12 +++++++++++- 7 files changed, 39 insertions(+), 19 deletions(-) diff --git a/asset/dialogue/item_description.csv b/asset/dialogue/item_description.csv index 940e973a..64f32b02 100644 --- a/asset/dialogue/item_description.csv +++ b/asset/dialogue/item_description.csv @@ -114,19 +114,20 @@ c01_s08_书店工钱,这个月的工钱还没拿。,,,,,I haven't collected this mem_偷听对话,偷听对话,,,,,Eavesdropping mem_疯子看井,疯子看井,,,,,Madman Guards Well c01_鸡毛掸子,这是鸡毛掸子吗?,,,,,Is this a feather duster? -c01_院长书桌,桌上放着一本《圣经》。{br}「得著生命的,將要失喪生命...」,,,,,There's a Bible on the desk.{br}'Whoever finds their life will lose it...' +c01_院长书桌,桌上放着一本《圣经》。{br}「凡要救自己生命的,必丧掉生命...」,,,,,"There's a Bible on the table.{br}'Whoever wants to save their life will lose it, but whoever loses their life for me will find it...'" c01_院长床,这本书已经看过了。{br}...那些句子是什么意思呢?,,,,,I've already read this book.{br}...What do those sentences mean? -c01_院长座钟,这西洋钟没坏的时候,走针会咔哒、咔哒地响...{br}停在未时一刻不动了。,,,,,"When this Western clock wasn't broken, the hands would tick and tock...{br}It's stopped at a quarter past one in the afternoon." +c01_院长座钟,咦?这西洋钟为什么倒着走?,,,,,Huh? Why is this Western clock running backwards? c01_倾斜的洋相片,哇啊,这张洋相片要掉下来了!{br}我得做点什么...,,,,,"Oh no, this Western photograph is about to fall!{br}I need to do something..." c01_摆正的洋相片,这是院长的儿子吗?,,,,,Is this the director's son? c01_s06_院长房间,这是院长的房间,,,,,This is the director's room c01_s06_小朋友房间,这是其他小朋友的房间,,,,,This is the other children's room c01_s06_熟悉的墙画,墙上的画看起来好熟悉。{br}过去问问看吧,没准他们几个知道些什么。,,,,,The painting on the wall looks so familiar.{br}Let me go ask them. Maybe they know something. -c01_s06_四小孩对话结束,怪人、花、门...{br}他在找什么东西,或是什么人?,,,,,"Strange man, flowers, door...{br}What is he looking for, or who?" +c01_s06_四小孩对话结束,怪人、花...{br}有时候真搞不懂他们在说些什么。,,,,,"Strange people, flowers...{br}Sometimes I really don't understand what they're talking about." c01_s07_钱碗,碗里只有一枚铜钱。,,,,,There's only one copper coin in the bowl. c01_s07_获得报纸,这是什么?,,,,,What is this? c01_s07_书店展柜,院长说,读一百本书,就可以成为无所不能的大人。{br}如果我再大一些,没准可以求店长把我留下,我会干很多活,也能吃苦...,,,,,"The director said that reading a hundred books would make me an all-capable adult.{br}If I were a bit older, maybe I could ask the shop owner to keep me. I'd work hard and endure hardship..." -c01_s08_书架游戏完成,这些书都被老鼠啃坏了,连木头架子都没放过。,,,,,"These books have all been gnawed by mice, even the wooden shelves weren't spared." +c01_s08_书架游戏完成,?!{br}刚才那是什么?,,,,,?!{br}What was that just now? +c01_s08_书架游戏恢复记忆,...{br}这是...我之前工作的地方。{br}...{br}现在老板不在了,我也该走了...,,,,,"...{br}This is... where I used to work.{br}...{br}Now that the boss is gone, I should leave too..." c01_s08_获得袁大头后,工钱还在老地方。,,,,,The wages are still in the usual place. c02_海报_剪辫子侦探,剪辫悬梁上侦探奇闻,,,,,Detective Tales of the Queue-Cutting Mystery c02_海报_戏法班,朱连魁全班戏法——「各有幻女...演技新奇」,,,,,Zhu Liankui's Magic Troupe—'Each with enchanting women... performances most novel' diff --git a/asset/dialogue/item_description.dialogue b/asset/dialogue/item_description.dialogue index ee862426..3d9003d2 100644 --- a/asset/dialogue/item_description.dialogue +++ b/asset/dialogue/item_description.dialogue @@ -168,9 +168,9 @@ ~ Notes_c01 # c01-s05 院长房间 这是鸡毛掸子吗? [ID:c01_鸡毛掸子] -桌上放着一本《圣经》。{br}「得著生命的,將要失喪生命...」 [ID:c01_院长书桌] +桌上放着一本《圣经》。{br}「凡要救自己生命的,必丧掉生命...」 [ID:c01_院长书桌] 这本书已经看过了。{br}...那些句子是什么意思呢? [ID:c01_院长床] -这西洋钟没坏的时候,走针会咔哒、咔哒地响...{br}停在未时一刻不动了。 [ID:c01_院长座钟] +咦?这西洋钟为什么倒着走? [ID:c01_院长座钟] 哇啊,这张洋相片要掉下来了!{br}我得做点什么... [ID:c01_倾斜的洋相片] 这是院长的儿子吗? [ID:c01_摆正的洋相片] # c01-s06 院子 @@ -178,13 +178,14 @@ 这是其他小朋友的房间 [ID:c01_s06_小朋友房间] # 院子里四个小孩交谈结束后 墙上的画看起来好熟悉。{br}过去问问看吧,没准他们几个知道些什么。 [ID:c01_s06_熟悉的墙画] -怪人、花、门...{br}他在找什么东西,或是什么人? [ID:c01_s06_四小孩对话结束] +怪人、花...{br}有时候真搞不懂他们在说些什么。 [ID:c01_s06_四小孩对话结束] # c01-s07 书店外 碗里只有一枚铜钱。 [ID:c01_s07_钱碗] 这是什么? [ID:c01_s07_获得报纸] 院长说,读一百本书,就可以成为无所不能的大人。{br}如果我再大一些,没准可以求店长把我留下,我会干很多活,也能吃苦... [ID:c01_s07_书店展柜] # c01-s08 书店 -这些书都被老鼠啃坏了,连木头架子都没放过。 [ID:c01_s08_书架游戏完成] +?!{br}刚才那是什么? [ID:c01_s08_书架游戏完成] +...{br}这是...我之前工作的地方。{br}...{br}现在老板不在了,我也该走了... [ID:c01_s08_书架游戏恢复记忆] 工钱还在老地方。 [ID:c01_s08_获得袁大头后] => END diff --git a/manager/config_manager/global_config.gd b/manager/config_manager/global_config.gd index 49e4c5d9..a51b395c 100644 --- a/manager/config_manager/global_config.gd +++ b/manager/config_manager/global_config.gd @@ -13,10 +13,11 @@ const RES_FILE_FORMAT = ".tres" const CANVAS_LAYER_SETTINGS = 30 # note const CANVAS_LAYER_NOTE = 25 -# dialog -const CANVAS_LAYER_DIALOG = 24 const CANVAS_LAYER_MEM_ITEM = 23 const CANVAS_LAYER_UX_PANEL = 22 +# dialog +const CANVAS_LAYER_DIALOG_MEM = 30 +const CANVAS_LAYER_DIALOG = 22 # main 场景的 UI 层(prop hud、上下mask、notification) const CANVAS_LAYER_UI = 21 # 道具 inspector(prop/local) @@ -33,6 +34,8 @@ const CANVAS_LAYER_LITTLE_GAME = 5 const CANVAS_LAYER_FG = 2 const DIALOG_IGNORE_INPUT = "ignore_input" +# memory layer: 30 +const DIALOG_MEM_LAYER = "mem_layer" const CHARACTER_COLOR_MAP = { "default": Color.LIGHT_SEA_GREEN, diff --git a/scene/dialog/balloon.gd b/scene/dialog/balloon.gd index 23f6d8fc..5ce541a3 100755 --- a/scene/dialog/balloon.gd +++ b/scene/dialog/balloon.gd @@ -153,6 +153,8 @@ func _notification(what: int) -> void: func start( dialogue_resource: DialogueResource, title: String, extra_game_states: Array = [] ) -> void: + if extra_game_states.has(GlobalConfig.DIALOG_MEM_LAYER): + pass temporary_game_states = [self] + extra_game_states is_waiting_for_input = false resource = dialogue_resource diff --git a/scene/effect/dizzy_shader.gd b/scene/effect/dizzy_shader.gd index cd151b8f..6de1a4d5 100644 --- a/scene/effect/dizzy_shader.gd +++ b/scene/effect/dizzy_shader.gd @@ -10,7 +10,7 @@ func _ready() -> void: var tween: Tween -func dizzy(duration := 3.5, intensity := 1.5): +func dizzy(duration := 3.5, intensity := 1.5) -> void: if duration < 0.6: return if tween and tween.is_valid(): diff --git a/scene/ground/scene/c01/s08_书店.gd b/scene/ground/scene/c01/s08_书店.gd index aab9f6e0..d1aad505 100644 --- a/scene/ground/scene/c01/s08_书店.gd +++ b/scene/ground/scene/c01/s08_书店.gd @@ -33,6 +33,7 @@ var shelf_game_success = false var envelope_game_success = false var manager var mice +var fall_off func _on_ground_ready() -> void: @@ -44,6 +45,7 @@ func _on_ground_ready() -> void: coin = $"../DeployLayer/Ambush银元" mice = $"../DeployLayer/自动跟随的老鼠" manager = $"../DeployLayer/老板" + fall_off = $"../DeployLayer/小小蝶坠落" # 首先放报纸,触发动画,领取任务 if counter.interacted_times > 0: ladder.enabled = true @@ -127,9 +129,6 @@ func _on_shelf_game_exiting() -> void: SceneManager.release_player() -var fall_off - - func _on_shelf_game_success() -> void: ArchiveManager.set_global_entry(&"c01_shelf_game_success", true) shelf_game_success = true @@ -143,7 +142,6 @@ func _on_shelf_game_success() -> void: # 相机抖动 SceneManager.get_camera_marker().shake_camera() # 播放小蝶从书架跌倒的动画 - fall_off = $"../DeployLayer/小小蝶坠落" fall_off.visible = true fall_off.play() fall_off.animation_finished.connect(_on_fall_off_finished) @@ -154,11 +152,16 @@ func _on_shelf_game_success() -> void: func _on_fall_off_finished() -> void: fall_off.visible = false + SceneManager.get_player().hide_sprite = false + await Util.wait(0.3) + await SceneManager.pop_os_with_str("c01_s08_书架游戏完成") + # 耳鸣与眩晕 + $"Sfx头痛耳鸣".play() + $"../DizzyShader".dizzy() + await Util.wait(2.5) + await SceneManager.pop_os_with_str("c01_s08_书架游戏恢复记忆") # 最后释放玩家 SceneManager.release_player() - SceneManager.get_player().hide_sprite = false - await Util.wait(1.2) - SceneManager.pop_os_with_str("c01_s08_书架游戏完成") func _setup_weird_bookstore() -> void: diff --git a/scene/ground/scene/c01/s08_书店.tscn b/scene/ground/scene/c01/s08_书店.tscn index b8cf3ca6..48f4c44a 100644 --- a/scene/ground/scene/c01/s08_书店.tscn +++ b/scene/ground/scene/c01/s08_书店.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=3 uid="uid://cwu4dhayra8pg"] +[gd_scene load_steps=23 format=3 uid="uid://cwu4dhayra8pg"] [ext_resource type="PackedScene" uid="uid://dayyx4jerj7io" path="res://scene/ground/ground.tscn" id="1_b3cca"] [ext_resource type="Script" uid="uid://6q2pfbqsw10t" path="res://scene/ground/scene/c01/s08_书店.gd" id="2_0lque"] @@ -16,7 +16,9 @@ [ext_resource type="AudioStream" uid="uid://bnjyw5v85s6qe" path="res://asset/audio/sfx/交互/序章/04_书店内_主角摔倒.ogg" id="8_p6k3c"] [ext_resource type="Texture2D" uid="uid://bho8xal4ha50l" path="res://asset/art/scene/c01/s08_书店/l_打光.png" id="9_i4dqp"] [ext_resource type="AudioStream" uid="uid://cniawn37n8888" path="res://asset/audio/sfx/交互/序章/04_书店内_递交报纸.ogg" id="9_lfr41"] +[ext_resource type="AudioStream" uid="uid://b8sbtn3l37uh" path="res://asset/audio/sfx/旧版/c02/红屏.ogg" id="9_srknn"] [ext_resource type="Texture2D" uid="uid://bp4y3vujvsl7r" path="res://asset/art/scene/c01/s08_书店/fg_前景.png" id="10_4e3a1"] +[ext_resource type="PackedScene" uid="uid://decfqoe5v0y6n" path="res://scene/effect/dizzy_shader.tscn" id="18_wg16e"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_xqgjo"] @@ -81,6 +83,12 @@ bus = &"game_sfx" script = ExtResource("4_p6k3c") metadata/_custom_type_script = "uid://rq6w1vuhuq1m" +[node name="Sfx头痛耳鸣" type="AudioStreamPlayer" parent="Ground/AnimationPlayer" index="5"] +stream = ExtResource("9_srknn") +bus = &"game_sfx" +script = ExtResource("4_p6k3c") +metadata/_custom_type_script = "uid://rq6w1vuhuq1m" + [node name="BGSprite2D" parent="Ground" index="2"] material = SubResource("ShaderMaterial_ddd5v") texture = ExtResource("3_322m4") @@ -198,6 +206,8 @@ points = PackedVector2Array(22, 150, 545, 150) energy = 0.0 blend_mode = 1 +[node name="DizzyShader" parent="Ground" instance=ExtResource("18_wg16e")] + [node name="参考" type="Sprite2D" parent="."] visible = false modulate = Color(1, 1, 1, 0.580392) From 3be55f41e745f6ff1c987389d3ce05763698c835 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 16:05:22 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=86=85=E4=BE=A7?= =?UTF-8?q?=E5=8F=8D=E9=A6=88=E9=97=AE=E5=8D=B7=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scene/index_page.tscn | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scene/index_page.tscn b/scene/index_page.tscn index 604f8b29..670d1ec3 100644 --- a/scene/index_page.tscn +++ b/scene/index_page.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://c4ycvdsabi7lw"] +[gd_scene load_steps=12 format=3 uid="uid://c4ycvdsabi7lw"] [ext_resource type="Script" uid="uid://b4rw4hk7ns4m8" path="res://scene/index_page.gd" id="1_c7y7r"] [ext_resource type="Texture2D" uid="uid://bcc0bk34l5gbc" path="res://asset/art/ui/index/菜单背景.png" id="1_jxn4k"] @@ -6,6 +6,7 @@ [ext_resource type="Script" uid="uid://rq6w1vuhuq1m" path="res://scene/entity/audio/sfx.gd" id="3_5oc6i"] [ext_resource type="Script" uid="uid://bbg4vopj4apl6" path="res://scene/entity/audio/bgm_control.gd" id="4_5oc6i"] [ext_resource type="AudioStream" uid="uid://b3b4a6nm8557i" path="res://asset/audio/专用/衔蝶_主菜单music剪辑人声版.ogg" id="5_5oc6i"] +[ext_resource type="FontFile" uid="uid://bjmhscwn1ixj1" path="res://asset/font/字体/ChillJinshuSongMedium.otf" id="7_1sxgt"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_gu56a"] content_margin_top = 4.0 @@ -137,6 +138,18 @@ theme_override_styles/pressed = SubResource("StyleBoxFlat_scxsv") theme_override_styles/normal = SubResource("StyleBoxEmpty_vyh3d") text = "index_退出游戏" +[node name="ButtonQuery" type="LinkButton" parent="."] +layout_mode = 0 +offset_left = 439.0 +offset_top = 27.0 +offset_right = 535.0 +offset_bottom = 67.0 +theme_override_fonts/font = ExtResource("7_1sxgt") +theme_override_font_sizes/font_size = 12 +text = "衔蝶内测反馈问卷" +underline = 2 +uri = "https://wj.qq.com/s2/23189494/a237/" + [node name="Mask" type="ColorRect" parent="."] visible = false layout_mode = 1 From 740620a1d458a500598dee5aa121261ec7a28ba1 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 16:32:40 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E4=BC=98=E5=8C=96=20prop=20hud=20?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/scene/scene_manager.gd | 6 +- scene/ux/prop_hud.gd | 384 ++++++++++++++++++++------------- 2 files changed, 243 insertions(+), 147 deletions(-) diff --git a/manager/scene/scene_manager.gd b/manager/scene/scene_manager.gd index 1647706b..68b4a7ff 100644 --- a/manager/scene/scene_manager.gd +++ b/manager/scene/scene_manager.gd @@ -175,10 +175,10 @@ func checkout_prop_inventory(character: String) -> void: else: printerr("checkout_prop_inventory: PropHud node not found") - -func get_current_prop(must_selected: bool) -> String: +# 无效参数 +func get_current_prop(_must_selected = null) -> String: var prop_hud = get_prop_hud() - if prop_hud and (not must_selected or prop_hud.selected): + if prop_hud: return prop_hud.inventory.current_item_key() return "" diff --git a/scene/ux/prop_hud.gd b/scene/ux/prop_hud.gd index ef04a7dd..6f161531 100644 --- a/scene/ux/prop_hud.gd +++ b/scene/ux/prop_hud.gd @@ -3,6 +3,14 @@ class_name PropHud extends Control signal current_item_changed(prop_key: String) +# 常量定义 +const PROP_CONTAINER_X = 130.0 +const PROP_CONTROL_X = 110.0 +const TWEEN_DURATION = 0.5 +const SHAKE_FPS = 15.0 +const SHAKE_DURATION = 0.8 +const SHAKE_DELTA = 12.0 + @export_group("DebugItem") @export var enable_item := false: set(value): @@ -10,12 +18,18 @@ signal current_item_changed(prop_key: String) enable_item = false enable_prop_item(item_key) @export var item_key: String + @export_group("Inventory") @export var inventory: PropInventory: set(value): + if inventory == value: + return + if inventory and inventory.current_item_changed.is_connected(_emit_changed): + inventory.current_item_changed.disconnect(_emit_changed) inventory = value if inventory and not inventory.current_item_changed.is_connected(_emit_changed): inventory.current_item_changed.connect(_emit_changed) + @export_group("UI-UX") @export var display_time := 2.5 # 不包含渐入渐出(约 0.6s)的时长 @export var locked := false: @@ -45,10 +59,8 @@ signal current_item_changed(prop_key: String) @onready var props_bag_scroll = %PropsBagScroll as ScrollContainer @onready var props_bag = %PropsBag as HBoxContainer @onready var select_mark = %SelectMark as TextureRect -var prop_containers = [] #CenterContainer -const PROP_CONTAINER_X = 130.0 -const PROP_CONTROL_X = 110.0 +var prop_containers: Array[CenterContainer] = [] var items_dict := {} var items_description_dict = {} # 从配置文件加载 prop items @@ -62,10 +74,16 @@ var displaying = false var timer := Timer.new() var display_tween: Tween var container_tween: Tween +var prop_blink: Tween +var tween_scroll: Tween +var shake_tween: Tween # hud 是否监听快捷键 var listening_hotkey = true +# 缓存的占位符纹理 +var placeholder_texture: Texture2D + func _emit_changed(prop_key := ""): if not selected: @@ -77,33 +95,43 @@ func _emit_changed(prop_key := ""): func _ready() -> void: if Engine.is_editor_hint(): return + + # 初始化占位符纹理 + placeholder_texture = preload("res://asset/art/ui/hud/placeholder.png") + # read prop containers for id in range(props_bag.get_child_count()): var container = props_bag.get_child(id) prop_containers.append(container) - container.get_child(0).get_child(0).gui_input.connect(_on_prop_gui_input.bind(id)) + var button = container.get_child(0).get_child(0) + if button: + button.gui_input.connect(_on_prop_gui_input.bind(id)) + display_prop.gui_input.connect(_on_prop_gui_input.bind(-1)) + _load_items_config_to_dict("ImportantPropItems") _load_items_config_to_dict("PropItems") _reload_cache_and_realign_display() + selecting_bg.modulate.a = 0.0 prop_scroll.scroll_horizontal = PROP_CONTAINER_X props_bag_scroll.scroll_horizontal = 0.0 props_bag_scroll.custom_minimum_size.x = 0.0 - # focus_exited.connect(_on_focus_exited) + # 存档更新时,从存档加载 prop ArchiveManager.archive_loaded.connect(_reload_cache_and_realign_display) + # tween timer timer.wait_time = display_time timer.one_shot = true timer.autostart = false timer.timeout.connect(_on_timer_timeout) add_child(timer) + # connect signals left_btn.pressed.connect(on_left_pressed) right_btn.pressed.connect(on_right_pressed) title_label.modulate.a = 0.0 - # _toggle_btn_ability(false) left_btn.modulate.a = 0.5 right_btn.modulate.a = 0.5 mouse_entered.connect(_on_mouse_entered) @@ -111,51 +139,36 @@ func _ready() -> void: func _load_items_config_to_dict(title: String): + if not item_config_res or not item_config_res.titles.has(title): + return + var id = item_config_res.titles[title] - var current_line = item_config_res.lines[id] + var current_line = item_config_res.lines.get(id) + while current_line: if current_line.has("tags") and current_line.has("translation_key"): - var wrapped_texture := "texture=" - var wrapped_inspect := "inspect=" var texture_path = "" var inspect_path = "" - for t in current_line.tags: - if t.begins_with(wrapped_texture): - texture_path = t.replace(wrapped_texture, "").strip_edges() - elif t.begins_with(wrapped_inspect): - inspect_path = t.replace(wrapped_inspect, "").strip_edges() + + for tag in current_line.tags: + if tag.begins_with("texture="): + texture_path = tag.substr(8).strip_edges() + elif tag.begins_with("inspect="): + inspect_path = tag.substr(8).strip_edges() + var item = PropItem.new() item.key = current_line.translation_key if texture_path: item.texture_path = path_prefix + texture_path if inspect_path: item.inspect_path = path_prefix + inspect_path + items_dict[item.key] = item - # # 检查是否是重要物品,如果是,则添加到 items_description_dict items_description_dict[item.key] = tr(item.key + "_说明").replace("{br}", "\n") - # if item_config_res.titles.has(item.key): - # var important_item_line_id = item_config_res.titles[item.key] - # items_description_dict[item.key] = get_import_item_content(important_item_line_id) if not current_line.has("next_id") or current_line.next_id == "end": break - current_line = item_config_res.lines[current_line.next_id] - - -# func get_import_item_content(line_id) -> String: -# var current_line = item_config_res.lines[line_id] -# var content = "" -# while current_line: -# if current_line.has("text"): -# if current_line.has("translation_key"): -# content += tr(current_line.translation_key) + "\n" -# else: -# content += current_line.text + "\n" -# if not current_line.has("next_id") or current_line.next_id == "end": -# break -# current_line = item_config_res.lines[current_line.next_id] -# content.replace("{br}", "\n") -# return content + current_line = item_config_res.lines.get(current_line.next_id) func _reload_cache_and_realign_display() -> void: @@ -174,57 +187,90 @@ func checkout_inventory(character: String) -> void: _reload_cache_and_realign_display() -func hide_hud(): - # 隐藏 hud 并静音 - hide() +const HUD_FADE_DURATION = 0.3 + +# 在变量定义区域添加 +var hud_visibility_tween: Tween + +func hide_hud(duration: float = HUD_FADE_DURATION): + if not visible: + return listening_hotkey = false + # 清理之前的补间动画 + if hud_visibility_tween and hud_visibility_tween.is_running(): + hud_visibility_tween.kill() + # 创建渐变动画 + hud_visibility_tween = create_tween() + hud_visibility_tween.tween_property(self, "modulate:a", 0.0, duration) + hud_visibility_tween.tween_callback(hide) -func display_hud(): - # 显示 hud 并取消静音 - show() - listening_hotkey = true +func display_hud(duration: float = HUD_FADE_DURATION): + if visible and modulate.a >= 1.0: + return + # 清理之前的补间动画 + if hud_visibility_tween and hud_visibility_tween.is_running(): + hud_visibility_tween.kill() + # 确保节点可见但透明 + if not visible: + modulate.a = 0.0 + show() + # 创建渐变动画 + hud_visibility_tween = create_tween() + hud_visibility_tween.tween_property(self, "modulate:a", 1.0, duration) + hud_visibility_tween.tween_callback(func(): listening_hotkey = true) func _load_texture_cache() -> void: if not inventory: return + + # 移除无效纹理 + var keys_to_remove = [] for k in cached_inventory_textures: - # remove invalid textures if not k in inventory.enabled_items: - cached_inventory_textures.erase(k) + keys_to_remove.append(k) + + for k in keys_to_remove: + cached_inventory_textures.erase(k) + + # 加载新纹理 for key in inventory.enabled_items: if cached_inventory_textures.has(key): continue - # 以 items_dict 为准,如果 enabled_items 中的 key 不存在,则删除 - if not key in items_dict: + + if not items_dict.has(key): inventory.disable_item(key) printerr( "PropHud _load_texture_cache: key not found in items_dict:", key, ". remove item." ) continue + var path = items_dict[key].texture_path if not path: - var placeholder = preload("res://asset/art/ui/hud/placeholder.png") - cached_inventory_textures[key] = placeholder + cached_inventory_textures[key] = placeholder_texture print("Cache load prop placeholder key=", key) continue + var texture = load(path) as Texture2D if texture: if GlobalConfig.DEBUG: print("Cache load prop texture key=", key) cached_inventory_textures[key] = texture + # wrap index - inventory.current_index = wrapi(inventory.current_index, 0, inventory.enabled_items.size()) + if inventory.enabled_items.size() > 0: + inventory.current_index = wrapi(inventory.current_index, 0, inventory.enabled_items.size()) func _update_prop_display_with_texture(): if not inventory: return + var key = "" # 在没有道具时,展示空手 placeholder if inventory.enabled_items.is_empty(): - display_prop.texture_normal = preload("res://asset/art/ui/hud/placeholder.png") + display_prop.texture_normal = placeholder_texture display_prop.size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X) display_prop.scale = Vector2(1.0, 1.0) title_label.text = tr("prop_空手") @@ -232,36 +278,48 @@ func _update_prop_display_with_texture(): key = inventory.current_item_key() _display_texture_by_key(display_prop, key) title_label.text = tr(key) + # 如果当前是 prop_小猫玩具完整,尝试点亮玩家的灯效;否则无需点亮 var player = SceneManager.get_player() if player: player.set_catty_light(key == "prop_小猫玩具完整") + if GlobalConfig.DEBUG: print("[PropHud] current display prop:", key) - # 选中标记 select_mark; 如果被 free 掉,则重新创建 - if select_mark and is_instance_valid(select_mark): - var parent = select_mark.get_parent() - if parent: - parent.remove_child(select_mark) - else: - select_mark = TextureRect.new() - select_mark.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X) - select_mark.texture = preload("res://asset/art/ui/hud/select_mark.png") - # bag + + # 选中标记处理 + _update_select_mark() + + # bag更新 for i in range(inventory.enabled_items.size()): var id = wrapi(i, 0, inventory.enabled_items.size()) var k = inventory.enabled_items[id] - var button = prop_containers[i].get_child(0).get_child(0) as TextureButton + var container = prop_containers[i] + var button = container.get_child(0).get_child(0) as TextureButton _display_texture_by_key(button, k) if id == inventory.current_index: - prop_containers[i].get_child(0).add_child(select_mark) + container.get_child(0).add_child(select_mark) + prop_scroll.scroll_horizontal = PROP_CONTAINER_X - # props_bag_scroll.scroll_horizontal = 0.0 -func _display_texture_by_key(button, key) -> void: - if not key: - button.texture_normal = null +func _update_select_mark(): + if not is_instance_valid(select_mark): + select_mark = TextureRect.new() + select_mark.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X) + select_mark.texture = preload("res://asset/art/ui/hud/select_mark.png") + else: + var parent = select_mark.get_parent() + if parent: + parent.remove_child(select_mark) + + +func _display_texture_by_key(button: TextureButton, key: String) -> void: + if not key or not button: + if button: + button.texture_normal = null + return + if not items_dict.has(key): return var item = items_dict[key] var texture = cached_inventory_textures.get(item.key) @@ -270,14 +328,15 @@ func _display_texture_by_key(button, key) -> void: texture = load(item.texture_path) as Texture2D if not texture: printerr("PropHud _display_texture_by_key: No texture found for item:", item) + return else: cached_inventory_textures[item.key] = texture button.texture_normal = texture - var t_size = button.texture_normal.get_size() - var max_x = max(t_size.x, t_size.y) + var t_size = texture.get_size() + var max_dimension = max(t_size.x, t_size.y) var p_scale = min(PROP_CONTROL_X / t_size.x, PROP_CONTROL_X / t_size.y) button.scale = Vector2(p_scale, p_scale) - button.size = Vector2(max_x, max_x) + button.size = Vector2(max_dimension, max_dimension) func on_left_pressed() -> void: @@ -305,34 +364,37 @@ func on_right_pressed() -> void: func _tween_container(left_to_right := true) -> void: if container_tween and container_tween.is_running(): container_tween.kill() + container_tween = create_tween() - if left_to_right: - prop_scroll.scroll_horizontal = 2 * PROP_CONTAINER_X - else: - prop_scroll.scroll_horizontal = 0.0 - # 通过 Head 与 Tail 占位符,实现滚动效果;否则会导致卡在边界上,无法滚动 + prop_scroll.scroll_horizontal = 2 * PROP_CONTAINER_X if left_to_right else 0.0 container_tween.tween_property(prop_scroll, "scroll_horizontal", PROP_CONTAINER_X, 0.2) -func _on_prop_gui_input(event: InputEvent, id) -> void: - if locked: +func _on_prop_gui_input(event: InputEvent, id: int) -> void: + if locked or not event is InputEventMouseButton: return - if event is InputEventMouseButton and event.is_pressed(): - event = event as InputEventMouseButton - if event.button_index == MOUSE_BUTTON_LEFT: + + var mouse_event = event as InputEventMouseButton + if not mouse_event.is_pressed(): + return + + match mouse_event.button_index: + MOUSE_BUTTON_LEFT: _on_prop_pressed(id) - elif event.button_index == MOUSE_BUTTON_RIGHT: + MOUSE_BUTTON_RIGHT: _on_prop_inspected(id) func _on_prop_inspected(id := 0) -> void: if locked: return + var prop_key = "" - if id >= 0: + if id >= 0 and id < inventory.enabled_items.size(): prop_key = inventory.enabled_items[id] else: prop_key = inventory.current_item_key() + if prop_key: sfx_inspect.play() print("[PropHud] inspect prop:", prop_key) @@ -342,37 +404,37 @@ func _on_prop_inspected(id := 0) -> void: func _on_prop_pressed(id := 0) -> void: if locked: return + sfx_click.play() if GlobalConfig.DEBUG: print("PropHUD Panel pressed.") + focus_mode = FOCUS_ALL grab_focus() selected = true - if id >= 0: + + if id >= 0 and id < inventory.enabled_items.size(): inventory.current_index = id + _update_prop_display_with_texture() _mouse_moved_on_listening(true) -var prop_blink: Tween -var tween_scroll: Tween - - func _toggle_scroll(fold := true) -> void: if prop_blink and prop_blink.is_running(): prop_blink.kill() if tween_scroll and tween_scroll.is_running(): tween_scroll.kill() - # PROP_CONTAINER_X + 10. 为hud的两侧留出的空间 + if fold: # kill blink select_mark.modulate.a = 1.0 selecting_bg.modulate.a = 0.0 # fold tween_scroll = create_tween() - tween_scroll.tween_property(props_bag_scroll, "custom_minimum_size:x", 0.0, 0.5) + tween_scroll.tween_property(props_bag_scroll, "custom_minimum_size:x", 0.0, TWEEN_DURATION) tween_scroll.parallel().tween_property( - hud_rect, "custom_minimum_size:x", PROP_CONTAINER_X + 10., 0.5 + hud_rect, "custom_minimum_size:x", PROP_CONTAINER_X + 10., TWEEN_DURATION ) else: # blink @@ -388,12 +450,17 @@ func _toggle_scroll(fold := true) -> void: prop_blink.parallel().tween_property(selecting_bg, "modulate:a", .6, 1.).set_ease( Tween.EASE_IN_OUT ) + # unfold var bag_x = prop_containers.size() * PROP_CONTAINER_X var hud_x = bag_x + PROP_CONTAINER_X + 10. tween_scroll = create_tween() - tween_scroll.tween_property(props_bag_scroll, "custom_minimum_size:x", bag_x, 0.5) - tween_scroll.parallel().tween_property(hud_rect, "custom_minimum_size:x", hud_x, 0.5) + tween_scroll.tween_property( + props_bag_scroll, "custom_minimum_size:x", bag_x, TWEEN_DURATION + ) + tween_scroll.parallel().tween_property( + hud_rect, "custom_minimum_size:x", hud_x, TWEEN_DURATION + ) func _on_mouse_entered() -> void: @@ -410,9 +477,11 @@ func _on_mouse_exited() -> void: func _mouse_moved_on_listening(unfold_scroll := false) -> void: if unfold_scroll: _toggle_scroll(false) + if not displaying: _toggle_details(true) return + timer.start(display_time) @@ -424,6 +493,7 @@ func _on_timer_timeout() -> void: func _unhandled_input(event: InputEvent) -> void: if locked or not listening_hotkey: return + if event.is_action_pressed("prop_left"): on_left_pressed() get_viewport().set_input_as_handled() @@ -431,17 +501,21 @@ func _unhandled_input(event: InputEvent) -> void: on_right_pressed() get_viewport().set_input_as_handled() elif event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: - on_left_pressed() - get_viewport().set_input_as_handled() - elif event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: - on_right_pressed() - get_viewport().set_input_as_handled() + var mouse_event = event as InputEventMouseButton + if mouse_event.pressed: + match mouse_event.button_index: + MOUSE_BUTTON_WHEEL_DOWN: + on_left_pressed() + get_viewport().set_input_as_handled() + MOUSE_BUTTON_WHEEL_UP: + on_right_pressed() + get_viewport().set_input_as_handled() func _input(event: InputEvent) -> void: if locked: return + if listen_mouse and (event is InputEventMouseMotion or event is InputEventScreenTouch): _mouse_moved_on_listening() @@ -462,7 +536,6 @@ func _toggle_details(display := true) -> void: display_tween.parallel().tween_property(title_label, "modulate:a", 0.0, 0.6) display_tween.parallel().tween_property(left_btn, "modulate:a", 0.5, 0.5) display_tween.parallel().tween_property(right_btn, "modulate:a", 0.5, 0.5) - # display_tween.tween_callback(_toggle_btn_ability.bind(false)) timer.stop() @@ -474,11 +547,14 @@ func _toggle_btn_ability(v: bool) -> void: func enable_important_item(prop_key: String, display_inspector: bool) -> void: if not inventory or not prop_key: return + if not items_dict.has(prop_key): push_error("ImportantPropItem not found! key=" + prop_key) return + inventory.enable_important_item(prop_key) SceneManager.pop_notification("ui_notify_important_item_update") + if display_inspector: sfx_inspect.play() inspect_item(prop_key, true, true) @@ -488,13 +564,17 @@ func enable_prop_item(prop_key: String, inspect := true) -> void: if not inventory or not prop_key: printerr("PropHUD Enable prop item: No inventory or prop_key provided.") return + if not items_dict.has(prop_key): push_error("PropItem not found! key=" + prop_key) return + inventory.enable_item(prop_key) _reload_cache_and_realign_display() + if GlobalConfig.DEBUG: print("PropHUD Enable prop item:", prop_key) + if inspect: sfx_new_prop.play() inspect_item(prop_key) @@ -502,27 +582,31 @@ func enable_prop_item(prop_key: String, inspect := true) -> void: func inspect_item(prop_key: String, display_obtained := true, as_important_item := false): var inspector = SceneManager.get_inspector() - if inspector: - var texture_path = items_dict[prop_key].texture_path - var inspect_path = items_dict[prop_key].inspect_path - # 是否有独立的 inspect 图片 - if inspect_path: - var texture = load(inspect_path) as Texture2D - inspector.pop_prop_inspection( - prop_key, texture, false, display_obtained, as_important_item - ) - else: - var texture = cached_inventory_textures.get(prop_key) - if not texture: - texture = load(texture_path) as Texture2D - if texture: - cached_inventory_textures[prop_key] = texture - else: - printerr("prophud inspect_item invalid texture_path:", texture_path) - return - inspector.pop_prop_inspection( - prop_key, texture, true, display_obtained, as_important_item - ) + if not inspector or not items_dict.has(prop_key): + return + + var item = items_dict[prop_key] + var texture: Texture2D = null + var is_inspect_texture = false + + # 检查是否有独立的 inspect 图片 + if item.inspect_path: + texture = load(item.inspect_path) as Texture2D + is_inspect_texture = true + else: + texture = cached_inventory_textures.get(prop_key) + if not texture and item.texture_path: + texture = load(item.texture_path) as Texture2D + if texture: + cached_inventory_textures[prop_key] = texture + + if not texture: + printerr("prophud inspect_item invalid texture for key:", prop_key) + return + + inspector.pop_prop_inspection( + prop_key, texture, not is_inspect_texture, display_obtained, as_important_item + ) func disable_prop_item(prop_key: String) -> void: @@ -533,16 +617,21 @@ func disable_prop_item(prop_key: String) -> void: func _align_container_size() -> void: - # 判断是否需要添加新的 prop container + if not inventory: + return + + # 添加新容器 while inventory.enabled_items.size() > prop_containers.size(): append_prop_container() - # 判断是否需要删除 prop container,不保留余数 + + # 删除多余容器 while inventory.enabled_items.size() < prop_containers.size(): remove_prop_container() + if displaying: _mouse_moved_on_listening() # 如果正在展示,则更新 scroll 长度 - if displaying and props_bag_scroll.custom_minimum_size.x > 0.0: + if props_bag_scroll.custom_minimum_size.x > 0.0: _toggle_scroll(false) @@ -550,58 +639,65 @@ func append_prop_container() -> void: var container = CenterContainer.new() container.custom_minimum_size = Vector2(PROP_CONTAINER_X, PROP_CONTAINER_X) container.set_anchors_preset(Control.PRESET_FULL_RECT) - prop_containers.append(container) + var control = Control.new() control.custom_minimum_size = Vector2(PROP_CONTROL_X, PROP_CONTROL_X) control.set_anchors_preset(Control.PRESET_FULL_RECT) container.add_child(control) + var prop = TextureButton.new() prop.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED control.add_child(prop) - # 添加到 hbox: container -> control -> prop + props_bag.add_child(container) - prop.gui_input.connect(_on_prop_gui_input.bind(prop_containers.size() - 1)) + prop_containers.append(container) + + var index = prop_containers.size() - 1 + prop.gui_input.connect(_on_prop_gui_input.bind(index)) func remove_prop_container() -> void: - if prop_containers.size() == 0: + if prop_containers.is_empty(): return + var container = prop_containers.pop_back() - props_bag.remove_child(container) - container.queue_free() + if container and is_instance_valid(container): + props_bag.remove_child(container) + container.queue_free() -var shake_tween - - -# 使用无效道具,抖动提示 func on_toggle_invalid_prop(): if not inventory or not inventory.current_item_key(): return + if GlobalConfig.DEBUG: print("使用无效道具. invalid_prop shake. current prop:", inventory.current_item_key()) - if shake_tween: + + if shake_tween and shake_tween.is_running(): shake_tween.kill() - # 抖动效果,逐渐减弱 + + # 抖动效果 shake_tween = create_tween() - var fps := 15.0 - var duration := 0.8 - var delta := 12.0 + var count = int(SHAKE_DURATION * SHAKE_FPS) + var delta_t = 1.0 / SHAKE_FPS var origin_pos = Vector2.ZERO - var count = int(duration * fps) - var delta_t = 1.0 / fps var prop = display_prop - # var prop = prop_containers[0].get_child(0) + prop.modulate = Color(1.0, 0.6, 0.6, 1.0) + for i in range(count): - var offset = Vector2(randf_range(-delta, delta), randf_range(-delta, delta)) * exp(-i) + var decay = exp(-i) + var offset = Vector2( + randf_range(-SHAKE_DELTA, SHAKE_DELTA) * decay, + randf_range(-SHAKE_DELTA, SHAKE_DELTA) * decay + ) shake_tween.tween_property(prop, "position", origin_pos + offset, delta_t).set_trans( Tween.TRANS_CUBIC ) + shake_tween.tween_callback(_reset_prop_modulate) func _reset_prop_modulate(): - var prop = display_prop - # var prop = prop_containers[0].get_child(0) - prop.modulate = Color(1.0, 1.0, 1.0, 1.0) + if display_prop: + display_prop.modulate = Color.WHITE From 214918423d2e9d7ee187a86a5668894f2a6a7e54 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 16:42:59 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=20=E4=BC=98=E5=8C=96balloon=E5=9C=A8?= =?UTF-8?q?=E6=B8=B8=E6=88=8F/=E5=9B=9E=E5=BF=86=E7=9A=84=E4=B8=8D?= =?UTF-8?q?=E5=90=8Clayer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/config_manager/global_config.gd | 5 +++-- scene/dialog/balloon.gd | 2 +- scene/ux/memory/clips/clip1.gd | 2 +- scene/ux/memory/clips/clip2.gd | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/manager/config_manager/global_config.gd b/manager/config_manager/global_config.gd index a51b395c..f080e3a7 100644 --- a/manager/config_manager/global_config.gd +++ b/manager/config_manager/global_config.gd @@ -15,8 +15,9 @@ const CANVAS_LAYER_SETTINGS = 30 const CANVAS_LAYER_NOTE = 25 const CANVAS_LAYER_MEM_ITEM = 23 const CANVAS_LAYER_UX_PANEL = 22 -# dialog -const CANVAS_LAYER_DIALOG_MEM = 30 +# dialog 在游戏场景中使用 CANVAS_LAYER_DIALOG +# 在回忆中使用 CANVAS_LAYER_DIALOG_MEM +const CANVAS_LAYER_DIALOG_MEM = 29 const CANVAS_LAYER_DIALOG = 22 # main 场景的 UI 层(prop hud、上下mask、notification) const CANVAS_LAYER_UI = 21 diff --git a/scene/dialog/balloon.gd b/scene/dialog/balloon.gd index 5ce541a3..ad38df94 100755 --- a/scene/dialog/balloon.gd +++ b/scene/dialog/balloon.gd @@ -154,7 +154,7 @@ func start( dialogue_resource: DialogueResource, title: String, extra_game_states: Array = [] ) -> void: if extra_game_states.has(GlobalConfig.DIALOG_MEM_LAYER): - pass + layer = GlobalConfig.CANVAS_LAYER_DIALOG_MEM temporary_game_states = [self] + extra_game_states is_waiting_for_input = false resource = dialogue_resource diff --git a/scene/ux/memory/clips/clip1.gd b/scene/ux/memory/clips/clip1.gd index d78f488c..0b743aea 100644 --- a/scene/ux/memory/clips/clip1.gd +++ b/scene/ux/memory/clips/clip1.gd @@ -12,7 +12,7 @@ func run_clip(card_mode: bool): $"李氏赖子房间人影".play() if not card_mode: # 增加配音 - dialog_node = DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_04_李氏癞子") + dialog_node = DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_04_李氏癞子", [GlobalConfig.DIALOG_MEM_LAYER]) dialog_node.process_mode = Node.PROCESS_MODE_ALWAYS await DialogueManager.dialogue_ended display_finished.emit() diff --git a/scene/ux/memory/clips/clip2.gd b/scene/ux/memory/clips/clip2.gd index d1710b56..d0fe5201 100644 --- a/scene/ux/memory/clips/clip2.gd +++ b/scene/ux/memory/clips/clip2.gd @@ -13,7 +13,7 @@ func run_clip(card_mode: bool): if not card_mode: await Util.wait(3.0) # 增加配音 - dialog_node = DialogueManager.show_dialogue_balloon(dialogue_res, "c02_井边疯子对话") + dialog_node = DialogueManager.show_dialogue_balloon(dialogue_res, "c02_井边疯子对话", [GlobalConfig.DIALOG_MEM_LAYER]) dialog_node.process_mode = Node.PROCESS_MODE_ALWAYS await DialogueManager.dialogue_ended display_finished.emit() From f267aecfe59e66823a6e0676210aa0088204c0aa Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 16:44:40 +0800 Subject: [PATCH 07/10] =?UTF-8?q?demo0.5.3:=20=E4=BF=AE=E5=A4=8D=E5=BC=B9?= =?UTF-8?q?=E7=8F=A0=E6=B8=B8=E6=88=8F=E5=90=8E=E7=9C=8B=E5=B0=8F=E7=8C=AB?= =?UTF-8?q?=E4=BC=9A=E8=B7=B3=E8=BF=87=E9=9C=B8=E5=87=8Cbug=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96dialog=20layer=E9=97=AE=E9=A2=98=EF=BC=9B?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=86=85=E6=B5=8B=E5=8F=8D=E9=A6=88=E9=97=AE?= =?UTF-8?q?=E5=8D=B7=EF=BC=9B=E4=BC=98=E5=8C=96=20prop=20hud=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=9B=E4=BC=98=E5=8C=96=E5=BA=8F=E7=AB=A0=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=AF=B9=E7=99=BD=E6=96=87=E6=A1=88=EF=BC=9B=E4=B9=A6?= =?UTF-8?q?=E5=BA=97=E4=BB=8E=E4=B9=A6=E6=9E=B6=E6=8E=89=E8=90=BD=E5=90=8E?= =?UTF-8?q?=E5=9B=9E=E5=BF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- export_presets.cfg | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/export_presets.cfg b/export_presets.cfg index 18b02023..eae11aa7 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -9,7 +9,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="" -export_path="../demo0.5.2/xiandie.exe" +export_path="../demo0.5.3/xiandie.exe" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -37,8 +37,8 @@ application/modify_resources=true application/icon="uid://cxgwspjv16j7m" application/console_wrapper_icon="uid://cxgwspjv16j7m" application/icon_interpolation=4 -application/file_version="0.5.2.0" -application/product_version="0.5.2.0" +application/file_version="0.5.3.0" +application/product_version="0.5.3.0" application/company_name="包包丁" application/product_name="衔蝶" application/file_description="衔蝶" @@ -141,8 +141,8 @@ application/icon_interpolation=4 application/bundle_identifier="com.baobaoding.xiandie" application/signature="" application/app_category="Games" -application/short_version="0.2.0" -application/version="0.2.0" +application/short_version="0.5.3" +application/version="0.5.3" application/copyright="爆爆叮" application/copyright_localized={} application/min_macos_version_x86_64="10.12" @@ -368,7 +368,7 @@ rm -rf \"{temp_dir}\"" name="Android" platform="Android" runnable=true -advanced_options=false +advanced_options=true dedicated_server=false custom_features="" export_filter="all_resources" From 10272f6e4581455535e076a983f35d10338ba08e Mon Sep 17 00:00:00 2001 From: bbd_pc Date: Wed, 16 Jul 2025 16:54:55 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E4=B9=A6=E5=BA=97=E5=A4=96=E7=9B=91?= =?UTF-8?q?=E7=9D=A3=E5=B0=8F=E5=AD=A9=E8=AF=B4=E8=AF=9D=E6=97=B6=EF=BC=8C?= =?UTF-8?q?hold=E7=8E=A9=E5=AE=B6=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scene/ground/scene/c01/s07_书店外.gd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scene/ground/scene/c01/s07_书店外.gd b/scene/ground/scene/c01/s07_书店外.gd index b3266430..47d7650a 100644 --- a/scene/ground/scene/c01/s07_书店外.gd +++ b/scene/ground/scene/c01/s07_书店外.gd @@ -100,9 +100,11 @@ func seller_interacted(): func jiandu_dialog_triggered() -> void: var jiandu = $"../DeployLayer/举碗小孩/Npc监督小孩" jiandu.hook_speaking() + SceneManager.hold_player() DialogueManager.dialogue_ended.connect(_on_jiandu_dialog_ended, CONNECT_ONE_SHOT) func _on_jiandu_dialog_ended(_res) -> void: var jiandu = $"../DeployLayer/举碗小孩/Npc监督小孩" jiandu.unhook_speaking() + SceneManager.unhold_player() From 3f3a1f73c465d9fd2b40464b2abcbf8c25a5a098 Mon Sep 17 00:00:00 2001 From: bbd_pc Date: Wed, 16 Jul 2025 17:02:03 +0800 Subject: [PATCH 09/10] =?UTF-8?q?quit=20game=20=E5=89=8D=E5=85=88=E5=8F=96?= =?UTF-8?q?=E6=B6=88=20pause=20=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/scene/scene_manager.gd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manager/scene/scene_manager.gd b/manager/scene/scene_manager.gd index 68b4a7ff..2b41b879 100644 --- a/manager/scene/scene_manager.gd +++ b/manager/scene/scene_manager.gd @@ -418,7 +418,8 @@ func quit_game() -> void: # DialogueManager.dialogue_ended.emit(null) # get_player().os_finished.emit("") # get_player().animation_finished.emit() - + # 首先取消 paused + get_tree().paused = false ArchiveManager.save_all() var ground_loader = get_ground_loader() as GroundLoader if ground_loader: From c7528dd0cc6a1d38692a7e2a3f63f1bd0d3c4291 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 17:19:34 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E7=BA=AF=E9=BB=91?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E7=95=8C=E9=9D=A2=20=E6=A3=95=E8=89=B2?= =?UTF-8?q?=E6=96=91=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- asset/art/ui/index/纯黑启动界面.png | Bin 4284 -> 4178 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/asset/art/ui/index/纯黑启动界面.png b/asset/art/ui/index/纯黑启动界面.png index 845d98d80052389fa384fb9194591ab9feb9677b..db2d13b79d5d0252aa712b5cd30d6bf1de177741 100644 GIT binary patch delta 1535 zcmdm^cu8SH8dp8*HBT4Ekcv5PuN~w)pdfHyL-XJAJa+~AmK6U9>DSKGFf%Z)|1M)- zV8}P15f5Ze2ZQeGCjLSQXV1rHXcn>an_tcJ`rLS5JIU(Zo^pxTA(9_Mux>PhxtZ zwxj9^q)%$whi+XxIhlrvj;bdnlTy(>bnEKLE;*>