diff --git a/Data/Init_Base.json b/Data/Init_Base.json index 7cd415d..6745b6d 100644 --- a/Data/Init_Base.json +++ b/Data/Init_Base.json @@ -6,7 +6,7 @@ "week": 1, "day": 1 }, - "money": 3000, + "money": 5000, "idea":0, "fans": 0, "staff_count": 0, diff --git a/Data/npc.json b/Data/npc.json index 29c79f5..8589bfb 100644 --- a/Data/npc.json +++ b/Data/npc.json @@ -2,6 +2,7 @@ "npc": { "米子三": { "type": "员工", + "icon": "米子三", "title": "项目总监", "level": 1, "power": 180, @@ -14,6 +15,7 @@ }, "塔祺": { "type": "员工", + "icon": "塔祺", "title": "美术设计师", "level": 3, "power": 150, @@ -26,6 +28,7 @@ }, "瞎五": { "type": "外包", + "icon": "瞎五", "title": "策划", "level": 2, "power": 160, @@ -34,7 +37,7 @@ "code": 50, "art": 70, "audio": 70, - "outsourcing": 1000 + "outsourcing": 2000 } } } diff --git a/Entity/NPC/npc_ability_icon.png b/Entity/NPC/art/npc_ability_icon.png similarity index 100% rename from Entity/NPC/npc_ability_icon.png rename to Entity/NPC/art/npc_ability_icon.png diff --git a/Entity/NPC/npc_ability_icon.png.import b/Entity/NPC/art/npc_ability_icon.png.import similarity index 69% rename from Entity/NPC/npc_ability_icon.png.import rename to Entity/NPC/art/npc_ability_icon.png.import index 2d18ea0..a79ede0 100644 --- a/Entity/NPC/npc_ability_icon.png.import +++ b/Entity/NPC/art/npc_ability_icon.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://hdidwixuokxn" -path="res://.godot/imported/npc_ability_icon.png-bfd6d66f16fe1f985312ee4082b5b29d.ctex" +path="res://.godot/imported/npc_ability_icon.png-50cfe5e6f622de28a6626dfe9cf951f8.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://Entity/NPC/npc_ability_icon.png" -dest_files=["res://.godot/imported/npc_ability_icon.png-bfd6d66f16fe1f985312ee4082b5b29d.ctex"] +source_file="res://Entity/NPC/art/npc_ability_icon.png" +dest_files=["res://.godot/imported/npc_ability_icon.png-50cfe5e6f622de28a6626dfe9cf951f8.ctex"] [params] diff --git a/Entity/NPC/NPC_1_UI.png b/Entity/NPC/art/塔祺.png similarity index 100% rename from Entity/NPC/NPC_1_UI.png rename to Entity/NPC/art/塔祺.png diff --git a/Entity/NPC/art/塔祺.png.import b/Entity/NPC/art/塔祺.png.import new file mode 100644 index 0000000..2aae8bf --- /dev/null +++ b/Entity/NPC/art/塔祺.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://l10c627dkvqk" +path="res://.godot/imported/塔祺.png-b299ce4379c4a1dbc822b8004e91f428.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Entity/NPC/art/塔祺.png" +dest_files=["res://.godot/imported/塔祺.png-b299ce4379c4a1dbc822b8004e91f428.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Entity/NPC/art/瞎五.png b/Entity/NPC/art/瞎五.png new file mode 100644 index 0000000..4cc7ea0 Binary files /dev/null and b/Entity/NPC/art/瞎五.png differ diff --git a/Entity/NPC/art/瞎五.png.import b/Entity/NPC/art/瞎五.png.import new file mode 100644 index 0000000..3bc66e1 --- /dev/null +++ b/Entity/NPC/art/瞎五.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1yh5gywpavyy" +path="res://.godot/imported/瞎五.png-5318b112b2d2eed9a5229dbdc2071b51.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Entity/NPC/art/瞎五.png" +dest_files=["res://.godot/imported/瞎五.png-5318b112b2d2eed9a5229dbdc2071b51.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Entity/NPC/art/秘书.png b/Entity/NPC/art/秘书.png new file mode 100644 index 0000000..4cc7ea0 Binary files /dev/null and b/Entity/NPC/art/秘书.png differ diff --git a/Entity/NPC/NPC_1_UI.png.import b/Entity/NPC/art/秘书.png.import similarity index 72% rename from Entity/NPC/NPC_1_UI.png.import rename to Entity/NPC/art/秘书.png.import index acccfff..b5fbc21 100644 --- a/Entity/NPC/NPC_1_UI.png.import +++ b/Entity/NPC/art/秘书.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://bb3kyiufyyj05" -path="res://.godot/imported/NPC_1_UI.png-83ecc50c62455610d5961b891015b3cc.ctex" +path="res://.godot/imported/秘书.png-ed96a35be8dbb7f405ac82294c185d80.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://Entity/NPC/NPC_1_UI.png" -dest_files=["res://.godot/imported/NPC_1_UI.png-83ecc50c62455610d5961b891015b3cc.ctex"] +source_file="res://Entity/NPC/art/秘书.png" +dest_files=["res://.godot/imported/秘书.png-ed96a35be8dbb7f405ac82294c185d80.ctex"] [params] diff --git a/Entity/NPC/art/米子三.png b/Entity/NPC/art/米子三.png new file mode 100644 index 0000000..c7d1a06 Binary files /dev/null and b/Entity/NPC/art/米子三.png differ diff --git a/Entity/NPC/art/米子三.png.import b/Entity/NPC/art/米子三.png.import new file mode 100644 index 0000000..2d62f44 --- /dev/null +++ b/Entity/NPC/art/米子三.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2hovy2llqpgk" +path="res://.godot/imported/米子三.png-97aa29879811dabba52f779af04a6ad2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Entity/NPC/art/米子三.png" +dest_files=["res://.godot/imported/米子三.png-97aa29879811dabba52f779af04a6ad2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/UI/class/TypewriterLogic.gd b/UI/class/TypewriterLogic.gd index ac43ba4..0574aa1 100644 --- a/UI/class/TypewriterLogic.gd +++ b/UI/class/TypewriterLogic.gd @@ -5,7 +5,7 @@ class_name TypewriterLogic # 使用 class_name 使其可以在其他脚本中按类型提示引用 # --- 信号 --- -signal finished # 当打字动画自然完成时发出 +signal finished # 当打字动画自然完成时,或在start_typing中因文本为空/速度无效而立即完成时发出 # --- 属性 --- var target_label: Label # 要应用效果的目标 Label 节点 @@ -23,7 +23,7 @@ func _init(p_target_label: Label, p_typing_speed: float = 20.0): target_label = p_target_label typing_speed = p_typing_speed - print("TypewriterLogic initialized for Label: ", target_label.name) + print("TypewriterLogic initialized for Label: ", target_label.name if is_instance_valid(target_label) else "INVALID_LABEL") # --- 公共方法 --- @@ -43,9 +43,11 @@ func start_typing(full_text: String): # 3. 如果文本为空或速度<=0,则直接显示完成,不创建动画 if full_text.length() == 0 or typing_speed <= 0: - target_label.visible_characters = full_text.length() # 直接显示全部 + if is_instance_valid(target_label): + target_label.visible_characters = full_text.length() # 直接显示全部 _is_typing = false finished.emit() # 即使没有动画,也视为完成 + print("TypewriterLogic: Text is empty or speed is non-positive. Displaying all and emitting finished.") return # 4. 设置标记,表示正在打字 @@ -53,11 +55,18 @@ func start_typing(full_text: String): # 5. 创建新的Tween动画 (使用 Label 节点作为 Tween 的宿主) # 注意:Tween 需要一个 Node 来运行,我们使用 target_label 本身 + # 确保 target_label 仍然有效才创建 Tween + if not is_instance_valid(target_label): # 再次检查,以防在 stop_typing 后 target_label 失效 + printerr("TypewriterLogic: Target Label became invalid before Tween creation.") + _is_typing = false + return + current_tween = target_label.create_tween() # 6. 绑定动画完成信号到内部处理函数 # 使用 bind() 来确保 _on_typing_finished 被这个实例调用 - current_tween.finished.connect(_on_typing_finished) + # Callable(self, "_on_typing_finished") 是更现代的写法 + current_tween.finished.connect(Callable(self, "_on_typing_finished")) # 7. 计算动画时长 var duration = float(full_text.length()) / typing_speed @@ -77,7 +86,11 @@ func skip_typing(): return if not is_instance_valid(target_label): - printerr("TypewriterLogic: Target Label is not valid.") + printerr("TypewriterLogic: Target Label is not valid for skip_typing.") + _is_typing = false # 确保状态更新 + if current_tween != null and current_tween.is_valid(): # 尝试清理 tween + current_tween.kill() + current_tween = null return if current_tween != null and current_tween.is_valid(): @@ -87,23 +100,23 @@ func skip_typing(): target_label.visible_characters = target_label.text.length() # 显示全部字符 _is_typing = false # 更新状态 # 注意:跳过时通常不发出 finished 信号,因为这不是自然完成 - # 如果需要,可以在调用 skip_typing 的地方手动处理完成逻辑 + # 如果需要在跳过时也认为“完成”,调用方应在调用 skip_typing() 后手动处理, + # 或在此处添加 finished.emit() 并明确其行为。 print("TypewriterLogic: Skipped typing on Label '", target_label.name, "'") # 停止当前的打字动画 (例如,对话框提前关闭) func stop_typing(): - if not is_instance_valid(target_label): - # 如果 Label 无效,也可能意味着 Tween 已经无效或不存在 - current_tween = null - _is_typing = false - return - + # 即使 Label 无效,也要尝试清理 _is_typing 和 current_tween if current_tween != null and current_tween.is_valid(): - current_tween.kill() - current_tween = null + current_tween.kill() # 停止动画 + current_tween = null _is_typing = false # 确保状态被重置 - print("TypewriterLogic: Stopped typing on Label '", target_label.name, "'") + + if is_instance_valid(target_label): + print("TypewriterLogic: Stopped typing on Label '", target_label.name, "'") + else: + print("TypewriterLogic: Stopped typing (target Label was invalid).") # 返回当前是否正在进行打字动画 @@ -117,5 +130,8 @@ func is_typing() -> bool: func _on_typing_finished(): _is_typing = false current_tween = null # 清理 Tween 引用 - print("TypewriterLogic: Typing finished naturally on Label '", target_label.name, "'") + if is_instance_valid(target_label): + print("TypewriterLogic: Typing finished naturally on Label '", target_label.name, "'") + else: + print("TypewriterLogic: Typing finished naturally (target Label was invalid).") finished.emit() # 发出完成信号 diff --git a/UI/class/ui公共类使用说明.md b/UI/class/ui公共类使用说明.md index 4567fc9..132a1cf 100644 --- a/UI/class/ui公共类使用说明.md +++ b/UI/class/ui公共类使用说明.md @@ -2,70 +2,108 @@ ## 1. 目标 -`TypewriterLogic` 是一个 GDScript 辅助类,用于给 `Label` 节点添加逐字显示(打字机)效果。它封装了动画逻辑和状态,实现代码复用。 +`TypewriterLogic` 是一个 GDScript 辅助类,用于给 `Label` 节点添加逐字显示(打字机)效果。它封装了动画逻辑和状态,旨在简化打字机效果的实现和代码复用。该类使用 `class_name TypewriterLogic` 注册,方便在其他脚本中进行类型提示和引用。 ## 2. 使用方法 **a. 引入与实例化:** ```gdscript -# 预加载类 (确保路径正确) -const TypewriterLogic = preload("res://path/to/TypewriterLogic.gd") +# 预加载类 (如果 TypewriterLogic.gd 在 res://scripts/ 目录下) +const TypewriterLogicClass = preload("res://scripts/TypewriterLogic.gd") # 确保路径正确 # 获取 Label 节点 -@onready var dialogue_label: Label = $Path/To/Label +@onready var dialogue_label: Label = $Path/To/Your/LabelNode # 声明实例变量 var typewriter: TypewriterLogic func _ready(): - # 创建实例,传入 Label 和速度 - if is_instance_valid(dialogue_label): - typewriter = TypewriterLogic.new(dialogue_label, 25.0) # 25.0 是打字速度 - # (可选) 连接信号 - typewriter.finished.connect(_on_typing_done) + # 确保 Label 节点有效 + if not is_instance_valid(dialogue_label): + printerr("Error: Dialogue Label node not found or invalid.") + return + + # 创建实例,传入 Label 和打字速度 + # TypewriterLogic 是 class_name,可以直接用作类型 + typewriter = TypewriterLogicClass.new(dialogue_label, 25.0) # 25.0 是打字速度 (字符/秒) + + # (可选) 连接信号,以在打字自然完成时执行操作 + if typewriter != null and typewriter.has_signal("finished"): # 检查实例和信号是否存在 + typewriter.finished.connect(_on_typing_naturally_finished) + +func _on_typing_naturally_finished(): + print("打字效果已自然播放完毕!") + # 在这里可以添加例如显示“继续”图标、启用下一页按钮等逻辑 ``` **b. 控制效果:** ```gdscript -# 开始打字 -func show_message(text: String): +# 开始播放打字效果 +func display_message(new_text: String): if typewriter != null: - typewriter.start_typing(text) + typewriter.start_typing(new_text) -# 跳过打字 (例如,点击时) -func skip_message(): +# 如果正在打字,立即显示全部文本 (跳过动画) +func skip_current_typing_animation(): if typewriter != null and typewriter.is_typing(): typewriter.skip_typing() + # 注意:skip_typing() 默认不发出 finished 信号。 + # 如果跳过后需要执行与自然完成相同的逻辑,可以在这里手动调用: + # _on_typing_naturally_finished() + # 或者根据需求处理不同的逻辑 -# 停止打字 (例如,关闭UI时) -func force_stop_typing(): +# 强制停止当前的打字效果 (例如,UI关闭或切换时) +func stop_current_typing_immediately(): if typewriter != null: typewriter.stop_typing() -# (可选) 处理完成信号 -func _on_typing_done(): - print("打字自然完成") +# 检查是否正在打字 +func check_if_typing(): + if typewriter != null and typewriter.is_typing(): + print("打字机正在工作中...") + else: + print("打字机当前空闲。") + ``` -## 3. API +## 3. API 详解 * **`TypewriterLogic.new(target_label: Label, typing_speed: float = 20.0)`** - * 构造函数。需要目标 `Label` 和可选的打字速度 (字符/秒)。 + * **构造函数 (`_init`)**: 创建 `TypewriterLogic` 的一个新实例。 + * `target_label`: (必需) `Label` 类型的节点,打字效果将应用于此节点。如果无效,将打印错误。 + * `typing_speed`: (可选) `float` 类型,表示打字速度,单位为“字符/秒”。默认值为 `20.0`。如果速度小于或等于0,文本将立即完全显示。 + * **`start_typing(full_text: String)`** - * 方法。开始播放指定文本的打字动画。 + * **方法**: 开始在关联的 `Label` 上播放指定文本的打字动画。 + * `full_text`: `String` 类型,要显示的完整文本。 + * 如果先前有动画正在播放,会先停止旧动画。 + * 如果 `full_text` 为空字符串,或者 `typing_speed` 设置为0或负数,文本会立即完全显示,并发出 `finished` 信号。 + * **`skip_typing()`** - * 方法。立即停止动画并显示全部文本。 + * **方法**: 如果当前正在进行打字动画,则立即停止动画并将 `Label` 的文本设置为完整内容。 + * 如果当前没有在打字,则此方法不执行任何操作。 + * **注意**: 此方法**不会**发出 `finished` 信号,因为它表示的是非自然完成。如果需要在跳过时执行特定逻辑,应在调用此方法后手动处理。 + * **`stop_typing()`** - * 方法。立即停止动画。 + * **方法**: 立即停止任何正在进行的打字动画。`Label` 的 `visible_characters` 将停留在被停止时的状态。 + * 此方法主要用于对话框提前关闭或需要中断效果的场景。 + * **注意**: 此方法**不会**发出 `finished` 信号。 + * **`is_typing() -> bool`** - * 方法。返回 `true` 如果正在打字。 + * **方法**: 返回一个布尔值,`true` 表示当前正在进行打字动画,`false` 则表示没有。 + * **`finished`** - * 信号。当动画**自然**完成时发出。`skip_typing()` 或 `stop_typing()` 不会触发。 + * **信号**: 当打字动画**自然**完成(即,所有字符都已按设定速度逐个显示完毕)时发出。 + * 也会在调用 `start_typing` 时,如果提供的文本为空或打字速度无效(≤0),导致文本立即显示完全时发出。 + * 通过 `skip_typing()` 或 `stop_typing()` 中断动画**不会**触发此信号。 -## 4. 注意 +## 4. 注意事项 -* 必须关联一个有效的 `Label` 节点。 -* `Tween` 动画由目标 `Label` 节点管理。 +* **有效 `Label`**: `TypewriterLogic` 必须与一个有效的 `Label` 节点关联才能工作。构造时会检查 `Label` 的有效性。 +* **Tween 管理**: 内部使用 `Tween` 来实现动画效果。`Tween` 实例由 `TypewriterLogic` 创建并附加到 `target_label` 节点上运行。`TypewriterLogic` 负责管理这些 `Tween` 的生命周期(创建、停止、清理)。 +* **`class_name`**: `TypewriterLogic` 使用 `class_name TypewriterLogic` 声明,这意味着你可以在其他 GDScript 文件中直接使用 `TypewriterLogic` 作为类型提示,例如 `var typewriter: TypewriterLogic`。 +* **信号连接**: 连接到 `finished` 信号时,请确保 `TypewriterLogic` 实例已成功创建。 +* **错误处理**: 类内部包含对无效 `Label` 的基本错误打印,但更健壮的错误处理(如游戏崩溃或替代行为)应由调用方代码根据具体需求实现。 ``` diff --git a/UI/main/ui_top_info.gd b/UI/main/ui_top_info.gd index af4fac1..a938ecb 100644 --- a/UI/main/ui_top_info.gd +++ b/UI/main/ui_top_info.gd @@ -72,7 +72,7 @@ func _on_date_changed(new_date: Dictionary) -> void: printerr("警告:接收到的 date_changed 信号数据格式不正确。") func _on_state_value_changed(key: String, new_value: Variant) -> void: - print("接收到 state_value_changed 信号: key=", key, ", value=", new_value) + #print("接收到 state_value_changed 信号: key=", key, ", value=", new_value) match key: "money": money_button.text = _format_value_as_int_string(new_value, "0") diff --git a/UI/popup/dialogue.gd b/UI/popup/dialogue.gd index 46fb8b5..bda4349 100644 --- a/UI/popup/dialogue.gd +++ b/UI/popup/dialogue.gd @@ -4,139 +4,149 @@ extends UIPage const _TypewriterLogic = preload("res://UI/class/TypewriterLogic.gd") @export var dialogues: Array +@export var default_npc_name = '秘书' +@onready var npc_icon: TextureRect = $Part_2/npc_icon + +var npc_icon_png_folder = "res://Entity/NPC/art/" var typing_speed: float = 20.0 # 每秒显示的字符数 var label_node_path: NodePath = "Part_2/Part_3/Label" # Label节点的路径 # --- 基础变量 --- var node_name: String -# var next_ui_node = null # next_ui_node_path 从 UIPage 继承,这里不需要重复定义 var dialogue_queue = [] -var current_dialogue_index = 0 +var current_dialogue_index = -1 # Initialize to -1, so first advance makes it 0 # --- 节点和模块引用 --- -var label: Label = null # 缓存Label节点引用 -var typewriter: TypewriterLogic = null # 用于存储 TypewriterLogic 实例 +var label: Label = null +var typewriter: TypewriterLogic = null func _ready() -> void: - super._ready() # 确保调用父类的 _ready 方法 + super._ready() node_name = str(self.name) print(node_name + ": _ready executed.") - # 尝试获取 Label 节点引用 label = get_node_or_null(label_node_path) if label == null: printerr(node_name + ": Failed to find Label node at path: ", label_node_path) else: - # 2. 创建 TypewriterLogic 实例 print(node_name + ": Label found. Creating TypewriterLogic instance.") typewriter = _TypewriterLogic.new(label, typing_speed) - # 检查实例是否成功创建 (TypewriterLogic 内部会检查 Label 有效性) if typewriter != null: - # 3. 连接 TypewriterLogic 的 finished 信号 (如果需要响应) if not typewriter.is_connected("finished", Callable(self, "_on_dialogue_typing_finished")): - typewriter.finished.connect(_on_dialogue_typing_finished) + typewriter.finished.connect(Callable(self, "_on_dialogue_typing_finished")) print(node_name + ": TypewriterLogic instance created and signal connected.") else: printerr(node_name + ": Failed to create TypewriterLogic instance.") func _on_page_activated(): - super._on_page_activated() # 调用父类的方法 + super._on_page_activated() + if GameState.get_value("dialogue_npc"): + var npc_name = GameState.get_value("dialogue_npc") + npc_icon.texture = load(npc_icon_png_folder+npc_name+'.png') + GameState.set_value("dialogue_npc",null) + else: + npc_icon.texture = load(npc_icon_png_folder+default_npc_name+'.png') print(node_name + ": Page activated.") _start(dialogues) func _on_page_deactivated(): - super._on_page_deactivated() # 调用父类的方法 + super._on_page_deactivated() print(node_name + ": Page deactivated.") - # 确保在页面停用时也停止输入处理和可能的游戏暂停状态 if typewriter != null: typewriter.stop_typing() - set_process_input(false) - -# 启动对话框显示 -func _start(p_dialogues) -> void: # Renamed parameter to avoid conflict +func _start(p_dialogues) -> void: print(node_name + ": _start function called.") if p_dialogues is String: dialogue_queue = [p_dialogues] elif p_dialogues is Array: - dialogue_queue = p_dialogues.duplicate() # 使用 duplicate 以免修改原始导出数组 + dialogue_queue = p_dialogues.duplicate() else: printerr(node_name + ": Invalid dialogues data type.") - dialogue_queue = [] # 置空以避免后续错误 + dialogue_queue = [] - current_dialogue_index = 0 + current_dialogue_index = -1 # Reset index - _show_current_dialogue() + # Advance to the first dialogue automatically when starting + _advance_dialogue_or_finish() - set_process_input(true) # 在页面激活时启用输入处理 + set_process_input(true) +# Renamed and repurposed: Tries to show next dialogue or finishes the page +func _advance_dialogue_or_finish() -> void: + current_dialogue_index += 1 + print(node_name + ": Attempting to advance. New index: ", current_dialogue_index) -# 使用 TypewriterLogic 显示当前对话 -func _show_current_dialogue() -> void: if current_dialogue_index < dialogue_queue.size(): - if typewriter == null: - printerr(node_name + ": TypewriterLogic instance is null. Cannot display text with effect.") - if label != null: - label.text = dialogue_queue[current_dialogue_index] - label.visible_characters = label.text.length() - return - - var full_text = dialogue_queue[current_dialogue_index] - print(node_name + ": Requesting typewriter to start typing: '", full_text, "'") - typewriter.start_typing(full_text) - + _show_current_dialogue_line() else: - # 对话队列结束 - print(node_name + ": Dialogue queue finished.") - _close_dialogue() + # All dialogues in this page's queue are finished + print(node_name + ": Dialogue queue for this page finished. Calling go_next().") + go_next() # This will eventually call _close_dialogue and hide the page +# Renamed to reflect it shows a single line +func _show_current_dialogue_line() -> void: + if not (current_dialogue_index >= 0 and current_dialogue_index < dialogue_queue.size()): + printerr(node_name + ": Invalid current_dialogue_index: ", current_dialogue_index) + return -# 关闭对话框 -func _close_dialogue(): + if typewriter == null: + printerr(node_name + ": TypewriterLogic instance is null. Cannot display text with effect.") + if label != null: + label.text = dialogue_queue[current_dialogue_index] + label.visible_characters = label.text.length() + # Manually call finished if no typewriter, as there's no typing to finish + _on_dialogue_typing_finished() + return + + var full_text = dialogue_queue[current_dialogue_index] + print(node_name + ": Requesting typewriter to start typing: '", full_text, "'") + typewriter.start_typing(full_text) + +func _close_dialogue(): # This is typically called by go_next() via UIPage's hide_page -> _on_page_deactivated print(node_name + ": Closing dialogue. Stopping typewriter if active.") if typewriter != null: typewriter.stop_typing() - - set_process_input(false) # 在关闭时禁用输入处理 + set_process_input(false) print(node_name + ": Dialogue UI processing stopped.") - -# 处理 TypewriterLogic 发出的 finished 信号的回调函数 func _on_dialogue_typing_finished(): - print(node_name + ": Received 'finished' signal from TypewriterLogic.") - # 文本自然显示完毕后的逻辑。 - # 例如,可以显示一个“点击继续”的提示图标。 - # 对于当前的“点击任意处继续”逻辑,此函数可能不需要执行太多操作, - # 因为 _input 函数会处理点击事件。 + print(node_name + ": Received 'finished' signal from TypewriterLogic for line: '", dialogue_queue[current_dialogue_index] if current_dialogue_index < dialogue_queue.size() and current_dialogue_index >=0 else "N/A" , "'") + # Current line finished typing. Ready for next input. + # You could show a "continue" prompt icon here if desired. pass - -# --- 输入处理 --- -# 新增/修改: 处理输入事件,特别是鼠标左键点击 func _input(event: InputEvent) -> void: - # 只有当页面可见且输入处理已启用时才处理输入 - # (set_process_input(true) 会在 _start 中调用) - # (visible 状态由 MainController 和 UIPage 的 show_page/hide_page 管理) - if not visible: + if not visible: # Only process if page is visible return - if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed(): - print(node_name + ": Left-click detected.") - - # 行为1: 如果打字机正在打字,则立即完成当前行打字 (如果TypewriterLogic支持) - # 行为2: 如果打字机已完成或未激活,则调用 go_next() - # 当前的 TypewriterLogic 没有明确的 is_typing 或 skip_to_end 方法, - # 所以我们直接调用 go_next()。 - # go_next() -> _close_dialogue() -> typewriter.stop_typing() 会停止打字。 - - print(node_name + ": Calling go_next() due to left-click.") - go_next() - get_viewport().set_input_as_handled() # 消费事件,防止其他节点处理 - return # 事件已处理 + var relevant_click = false + if event is InputEventMouseButton and event.is_pressed(): + if event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT: + relevant_click = true - # 调用父类 (UIPage) 的 _input 方法,以保留其功能 (例如右键返回) - # 如果此处的左键点击事件被处理并返回,则父类的 _input 不会再收到此特定事件。 - super._input(event) + if relevant_click: + get_viewport().set_input_as_handled() # Consume the click event early + print(node_name + ": Relevant mouse click detected (Left or Right).") + + # Behavior 1: If typewriter is currently typing, skip to the end of the current line. + # Requires TypewriterLogic to have is_typing() and skip_to_end() methods. + if typewriter != null and typewriter.is_typing(): + print(node_name + ": Typewriter is typing. Calling skip_typing().") + typewriter.skip_typing() # This should make it emit 'finished' signal + return # Wait for next click to advance dialogue + + # Behavior 2: If typewriter is NOT typing (or no typewriter), advance to the next dialogue or finish. + print(node_name + ": Typewriter not typing or no typewriter. Advancing dialogue or finishing.") + _advance_dialogue_or_finish() + return + + # If not a relevant click for dialogue, pass to super class (e.g., for other UI interactions) + # Note: If you want right-click to *only* advance dialogue and not trigger parent's go_back, + # this structure is correct because we consume the event. + # If UIPage's _input handles other specific mouse buttons (e.g. middle mouse) or other event types, + # they will still be processed if not handled above. + if not relevant_click: # Only call super if we didn't handle it + super._input(event) diff --git a/UI/popup/dialogue.tscn b/UI/popup/dialogue.tscn index d2f125a..95ebcf7 100644 --- a/UI/popup/dialogue.tscn +++ b/UI/popup/dialogue.tscn @@ -1,9 +1,8 @@ -[gd_scene load_steps=8 format=3 uid="uid://dtywh0m5odikx"] +[gd_scene load_steps=7 format=3 uid="uid://dtywh0m5odikx"] [ext_resource type="Script" uid="uid://dml4o41teef7h" path="res://UI/popup/dialogue.gd" id="1_ilhgc"] [ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="2_ilhgc"] [ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="3_ilhgc"] -[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/NPC_1_UI.png" id="3_nrcga"] [sub_resource type="Animation" id="Animation_ilhgc"] length = 0.001 @@ -178,7 +177,7 @@ libraries = { } autoplay = "base" -[node name="Human" type="TextureRect" parent="Part_2"] +[node name="npc_icon" type="TextureRect" parent="Part_2"] layout_mode = 1 anchors_preset = 3 anchor_left = 1.0 @@ -191,4 +190,3 @@ offset_right = -11.0 offset_bottom = -4.0 grow_horizontal = 0 grow_vertical = 0 -texture = ExtResource("3_nrcga") diff --git a/UI/popup/task_development/main_plan/strategy/strategy.tscn b/UI/popup/task_development/main_plan/strategy/strategy.tscn index bb88169..54b7b01 100644 --- a/UI/popup/task_development/main_plan/strategy/strategy.tscn +++ b/UI/popup/task_development/main_plan/strategy/strategy.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" uid="uid://by3rdgo6jlbff" path="res://UI/popup/task_development/main_plan/strategy/strategy.gd" id="1_pbd5q"] [ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="2_eh5ff"] [ext_resource type="Theme" uid="uid://ddq54ba6vwyn0" path="res://UI/popup/task_development/main_plan/gameplay/gameplay_option_button.tres" id="3_rq5uk"] -[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/NPC_1_UI.png" id="4_vx267"] +[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/art/秘书.png" id="4_vx267"] [node name="strategy" type="Control"] visible = false diff --git a/UI/popup/task_development/npc_select_1/npc_select_1.gd b/UI/popup/task_development/npc_select_1/npc_select_1.gd index 7a909cf..046e334 100644 --- a/UI/popup/task_development/npc_select_1/npc_select_1.gd +++ b/UI/popup/task_development/npc_select_1/npc_select_1.gd @@ -28,10 +28,11 @@ var _power_bar_segments: Array[ColorRect] = [] # 用于存储体力条的各个 var _all_npcs_data: Dictionary = {} # 存储从 GameState 加载的所有NPC原始数据 var enabled_npc_keys: Array[String] = [] # 存储所有当前可选的NPC的键 (名字) var current_npc_index: int = -1 # 当前在 enabled_npc_keys 数组中选中的NPC的索引 +var temp_next_ui_node_path = '' # --- 常量 --- -const NPC_ICON_BASE_PATH = "res://Entity/NPC/" # NPC 图标的基础路径 (如果后续有 Icon 字段) -const DEFAULT_NPC_ICON_PATH = "res://Entity/NPC/NPC_1_UI.png" # 默认/占位NPC图标 +const NPC_ICON_BASE_PATH = "res://Entity/NPC/art/" # NPC 图标的基础路径 (如果后续有 Icon 字段) +const DEFAULT_NPC_ICON_PATH = "res://Entity/NPC/art/秘书.png" # 默认/占位NPC图标 const POWER_CURRENT_COLOR = Color.GREEN_YELLOW # 当前体力颜色 const POWER_MAX_POTENTIAL_COLOR = Color(0.2, 0.4, 0.2, 0.8) # 最大潜力槽颜色 (稍暗的绿色) @@ -43,6 +44,8 @@ func _ready() -> void: var node_name = str(self.name) add_to_group(node_name) # 方便父节点或其他系统查找 + temp_next_ui_node_path = next_ui_node_path + # 收集体力条的片段 for child in power_bar_segments_container.get_children(): if child is ColorRect: @@ -184,7 +187,7 @@ func _display_current_npc() -> void: npc_level_label.text = "Lv" + str(int(npc_info.get("level", 1))) # 更新图标 (后续会从npc_info.Icon获取) - var icon_name_from_data = npc_info.get("Icon", "") # 假设后续会有这个字段 + var icon_name_from_data = npc_info.get("icon", "") # 假设后续会有这个字段 if not icon_name_from_data.is_empty(): var dynamic_icon_path = NPC_ICON_BASE_PATH + icon_name_from_data + ".png" # 假设文件名规则 if ResourceLoader.exists(dynamic_icon_path): @@ -251,13 +254,14 @@ func _on_confirm_pressed() -> void: var selected_npc_key = enabled_npc_keys[current_npc_index] # 导航到下一个页面或结束工作流 + next_ui_node_path = temp_next_ui_node_path if GameState.get_value("money") < int(cost_label.text): next_ui_node_path = "../notice_2" go_next() return else: - next_ui_node_path = "" var new_money = GameState.get_value("money")-get_main_data("预算") GameState.set_value("money",new_money) + #GameState.set_value("dialogue_npc",selected_npc_key) set_main_data("关键环节负责人", selected_npc_key) go_next() diff --git a/UI/popup/task_development/npc_select_1/npc_select_1.tscn b/UI/popup/task_development/npc_select_1/npc_select_1.tscn index 0faf2c0..e5f68b2 100644 --- a/UI/popup/task_development/npc_select_1/npc_select_1.tscn +++ b/UI/popup/task_development/npc_select_1/npc_select_1.tscn @@ -4,7 +4,7 @@ [ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="2_oyv27"] [ext_resource type="Texture2D" uid="uid://dgcugleiv7sfw" path="res://UI/popup/task_development/arrow.png" id="3_qga8r"] [ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="4_mk7go"] -[ext_resource type="Texture2D" uid="uid://hdidwixuokxn" path="res://Entity/NPC/npc_ability_icon.png" id="5_8ts75"] +[ext_resource type="Texture2D" uid="uid://hdidwixuokxn" path="res://Entity/NPC/art/npc_ability_icon.png" id="5_8ts75"] [ext_resource type="LabelSettings" uid="uid://qcn0aduvrgvw" path="res://UI/tres/task_development_platform_label_settings.tres" id="6_3iojh"] [ext_resource type="Theme" uid="uid://bau80ps6kx783" path="res://UI/tres/Bottom_Info_button_theme.tres" id="7_mchvh"] diff --git a/UI/popup/task_development/proposal/proposal.tscn b/UI/popup/task_development/proposal/proposal.tscn index ce8020f..079b0e8 100644 --- a/UI/popup/task_development/proposal/proposal.tscn +++ b/UI/popup/task_development/proposal/proposal.tscn @@ -5,9 +5,9 @@ [ext_resource type="Texture2D" uid="uid://dgcugleiv7sfw" path="res://UI/popup/task_development/arrow.png" id="3_uudne"] [ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="4_csatq"] [ext_resource type="LabelSettings" uid="uid://qcn0aduvrgvw" path="res://UI/tres/task_development_platform_label_settings.tres" id="5_l6j0s"] -[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/NPC_1_UI.png" id="5_uudne"] +[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/art/秘书.png" id="5_uudne"] [ext_resource type="Theme" uid="uid://bau80ps6kx783" path="res://UI/tres/Bottom_Info_button_theme.tres" id="6_6v1sj"] -[ext_resource type="Texture2D" uid="uid://hdidwixuokxn" path="res://Entity/NPC/npc_ability_icon.png" id="6_ybd2x"] +[ext_resource type="Texture2D" uid="uid://hdidwixuokxn" path="res://Entity/NPC/art/npc_ability_icon.png" id="6_ybd2x"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6a8uh"] diff --git a/UI/popup/task_development/task_development_start.tscn b/UI/popup/task_development/task_development_start.tscn index 75b7a7f..2c140aa 100644 --- a/UI/popup/task_development/task_development_start.tscn +++ b/UI/popup/task_development/task_development_start.tscn @@ -63,13 +63,13 @@ parent_page_node_path = NodePath("..") [node name="product_focus" parent="." instance=ExtResource("7_l1sf4")] visible = false layout_mode = 1 -next_ui_node_path = NodePath("../dialogue") +next_ui_node_path = NodePath("../dialogue_secretary") back_ui_node_path = NodePath("../main_plan") -[node name="dialogue" parent="." instance=ExtResource("8_6il88")] +[node name="dialogue_secretary" parent="." instance=ExtResource("8_6il88")] visible = false layout_mode = 1 -dialogues = ["谁来写立项书呢?"] +dialogues = ["由谁来写立项书呢?", "立项书只能由公司员工完成。"] next_ui_node_path = NodePath("../npc_select_1") back_ui_node_path = NodePath("../npc_select_1") @@ -77,8 +77,14 @@ back_ui_node_path = NodePath("../npc_select_1") visible = false layout_mode = 1 Suitable_position = ["项目总监", "策划", "美术设计师"] +next_ui_node_path = NodePath("../dialogue_npc") back_ui_node_path = NodePath("../main_plan") +[node name="dialogue_npc" parent="." instance=ExtResource("8_6il88")] +visible = false +layout_mode = 1 +dialogues = ["感谢给我这个机会,我会尽力!"] + [node name="notice_1" parent="." instance=ExtResource("10_5pged")] visible = false layout_mode = 1