Compare commits
42 Commits
96e6590a86
...
c2440b5272
Author | SHA1 | Date | |
---|---|---|---|
c2440b5272 | |||
187a86c915 | |||
e97a70faac | |||
08cc9c643b | |||
b42f6e510e | |||
6fc8efd46d | |||
b0ba970651 | |||
ab5b7ecf4f | |||
85475ff549 | |||
aaa7dc26bb | |||
301ee98c0f | |||
cce0a02fb8 | |||
86b6bc733b | |||
658fd923d5 | |||
![]() |
350184821d | ||
4d4ab68f70 | |||
644d8bcc39 | |||
d465857737 | |||
72f00c821f | |||
b4d34bccb2 | |||
571c6ab74c | |||
850900ad5f | |||
4e3183b013 | |||
2f431cff52 | |||
a60beeea1d | |||
87da4d2e73 | |||
e42b434bed | |||
746a25fd5b | |||
c720a78161 | |||
ffac8f61ef | |||
7f8136784f | |||
f2c2af8c57 | |||
c7528dd0cc | |||
![]() |
3f3a1f73c4 | ||
![]() |
10272f6e45 | ||
f267aecfe5 | |||
214918423d | |||
740620a1d4 | |||
3be55f41e7 | |||
4063434c26 | |||
70f9d35d07 | |||
9c3c470f3e |
30
README.md
@ -68,7 +68,7 @@ GroundLoader 加载/切换 Ground 时,分为上下两段转场:
|
||||
1. ambient:光照
|
||||
2. general:音效等
|
||||
3. partical:粒子效果
|
||||
4. ux:用户交互
|
||||
4. ux:用户交互相关
|
||||
|
||||
主要类型说明:
|
||||
|
||||
@ -77,17 +77,37 @@ GroundLoader 加载/切换 Ground 时,分为上下两段转场:
|
||||
2. 可配置触发方式:enter, area_enter, interact
|
||||
3. 可在场景加载时触发
|
||||
4. 触发效果有:播放对话(期间锁定玩家);播放动画;回调方法(AnimationPlayer 的方法,注意“_”开头的方法会被忽略)
|
||||
2. inspectable:可在检阅窗口进行审视的物品,可以附加文案
|
||||
2. 【已废弃】inspectable:可在检阅窗口进行审视的物品,可以附加文案
|
||||
3. local_inspectable:运镜+检阅,无需检阅窗口
|
||||
4. note:显示文案
|
||||
1. 显示方式:os(玩家头顶气泡),ballon(下方字幕,可播放配音)
|
||||
5. npc
|
||||
1. hook/unhook speaking:控制 NPC 在玩家未交互时显示对话气泡,配合触发播放 Dialogue 的情景(enable==false时也可生效)
|
||||
6. portal:传送门
|
||||
1. 名称有: left(默认), right(默认), 1, 2, 3, ...
|
||||
1. 名称有: left(默认), right(默认), 1, 2, 3, ... 其中 left/right 在玩家进入时会改变朝向(left 进入时朝右,right 进入时朝左)
|
||||
2. 关键参数:targer_scene 与 target_portal(target_portal 为 none 时不启用)
|
||||
3. 三种模式:default(通道);opened(打开的门);locked(锁定的门);对应不同图标与操作音效
|
||||
4. 锁定的门可以配置启用钥匙(prop_key),可在使用后自动消耗该钥匙
|
||||
7. pickable: 可用于拾取道具,可作为重要物品(as_important_prop)
|
||||
8. closeup: 可配置内部 PackedScene,display 在 child 下
|
||||
1. 可设置是否 exit_on_cancel
|
||||
2. 读取并连接 PackedScene 的 exit 信号,PackedScene exit 可传递参数(可选)
|
||||
|
||||
特殊 UX 类型:
|
||||
|
||||
1. 刮刮乐:可以按进度刮开其中内容
|
||||
2. Sign Snapper 拉动机制:
|
||||
1. 自动锁定玩家操作,调用 walk_to 指定位置
|
||||
2. 自动优化范围:忽略小范围移动以防抽动,最低活动距离
|
||||
3. Draggable (目录: scene/little_game/general/)
|
||||
1. hover 时 sprite 自动描白边(需配置子节点 CollisionShape)
|
||||
2. pick & drag & drop
|
||||
3. drag 活动范围
|
||||
4. 可启用 as_button 效果,picked signal
|
||||
4. wheel: 检测鼠标按住后,围绕中心旋转操作
|
||||
5. HoverLightClickArea
|
||||
1. hover 时高亮(需配置子节点 PointLight 与 CollisionShape))
|
||||
|
||||
### AimationPlayer Tool Button 说明
|
||||
|
||||
#### 存档 Tool Button
|
||||
@ -170,9 +190,6 @@ current_scene 是通过 GroundLoader 加载的,在 ground loader 加载 ground
|
||||
- PropHud 切换与点击展示
|
||||
- 左键点击展开 Hud
|
||||
- 右键点击检阅道具
|
||||
- SignSnapper 拉动机制
|
||||
- 自动锁定玩家操作,调用 walk_to 指定位置
|
||||
- 自动优化范围:忽略小范围移动以防抽动,最低活动距离
|
||||
- ReenterLock 机制
|
||||
- 可重入锁
|
||||
- Editor 中运行可 Debug 输出调用位置(调用 lock 的代码文件&代码行)
|
||||
@ -185,4 +202,5 @@ current_scene 是通过 GroundLoader 加载的,在 ground loader 加载 ground
|
||||
- 比 Event2D 更轻量灵活的 EventBinder,内有 updater 与 trigger 两种绑定
|
||||
- updater 由 event 驱动更新父节点状态
|
||||
- trigger 由父节点 signal 驱动更新 event
|
||||
- SavingsPanel 存档管理器:可便捷地增、删、加载存档
|
||||
- Vibe Control 控制氛围情绪音乐
|
||||
|
@ -18,20 +18,9 @@ func _ready():
|
||||
|
||||
# Normally you can just call DialogueManager directly but doing so before the plugin has been
|
||||
# enabled in settings will throw a compiler error here so I'm using `get_singleton` instead.
|
||||
var dialogue_manager = Engine.get_singleton("DialogueManager")
|
||||
dialogue_manager.dialogue_ended.connect(_on_dialogue_ended)
|
||||
dialogue_manager.show_dialogue_balloon(resource, title if not title.is_empty() else resource.first_title)
|
||||
DialogueManager.show_dialogue_balloon(resource, title if not title.is_empty() else resource.first_title)
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
DialogueSettings.set_user_value("is_running_test_scene", false)
|
||||
|
||||
|
||||
#region Signals
|
||||
|
||||
|
||||
func _on_dialogue_ended(_resource: DialogueResource):
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
#endregion
|
||||
|
@ -48,7 +48,7 @@ static func new_action_config() -> Dictionary:
|
||||
return ACTION_CONFIG.duplicate()
|
||||
|
||||
|
||||
@onready var debug_mov_onion_sprite2d = $DebugMovOnionSkinSprite2D as Sprite2D
|
||||
var debug_mov_onion_sprite2d: Sprite2D
|
||||
|
||||
# 从 intro 到 next 的配置信息
|
||||
var auto_checkout_dict = {
|
||||
@ -75,12 +75,16 @@ func _ready() -> void:
|
||||
animation_finished.connect(_on_animation_finished)
|
||||
animation_looped.connect(_on_animation_finished)
|
||||
# 处理 debug_mov_onion_sprite2d
|
||||
debug_mov_onion_sprite2d = get_node_or_null("DebugMovOnionSkinSprite2D")
|
||||
if Engine.is_editor_hint():
|
||||
# stop()
|
||||
# frame = 0
|
||||
if not debug_mov_onion_sprite2d:
|
||||
debug_mov_onion_sprite2d = Sprite2D.new()
|
||||
add_child(debug_mov_onion_sprite2d)
|
||||
debug_mov_onion_sprite2d.name = "DebugMovOnionSkinSprite2D"
|
||||
debug_mov_onion_sprite2d.modulate.a = 0.5
|
||||
debug_playing = false
|
||||
_debug_mov_projection()
|
||||
else:
|
||||
elif debug_mov_onion_sprite2d:
|
||||
debug_mov_onion_sprite2d.queue_free()
|
||||
# autoplay 会自己 play, 只有自定义的 autostart 手动调用 play
|
||||
if not Engine.is_editor_hint() and autostart:
|
||||
@ -93,8 +97,8 @@ func _debug_mov_projection():
|
||||
var mov_config = animation_mov_dict[debug_mov_animation]
|
||||
# 展示 accumulated animation 的目标位置
|
||||
debug_mov_onion_sprite2d.position.x = (
|
||||
mov_config.movement_x * (-1 if flip_h else 1) / scale.x
|
||||
) * sign(mov_config.velocity.x)
|
||||
(mov_config.movement_x * (-1 if flip_h else 1) / scale.x) * sign(mov_config.velocity.x)
|
||||
)
|
||||
debug_mov_onion_sprite2d.texture = sprite_frames.get_frame_texture(debug_mov_animation, 0)
|
||||
debug_mov_onion_sprite2d.flip_h = flip_h
|
||||
elif debug_mov_animation:
|
||||
|
@ -1,17 +1,80 @@
|
||||
[gd_resource type="SpriteFrames" load_steps=9 format=3 uid="uid://cmvr3lbwe3h7p"]
|
||||
[gd_resource type="SpriteFrames" load_steps=21 format=3 uid="uid://cmvr3lbwe3h7p"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dcwbe6hb3gdcp" path="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/0.png" id="1_fctjd"]
|
||||
[ext_resource type="Texture2D" uid="uid://cdyq7y562gsxp" path="res://asset/art/gif/c03_特写与游戏动画/绞肉机特写动画/0.png" id="1_uknvx"]
|
||||
[ext_resource type="Texture2D" uid="uid://bhrahd7u0yoba" path="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/1.png" id="2_g03au"]
|
||||
[ext_resource type="Texture2D" uid="uid://botg6n14al2eu" path="res://asset/art/gif/c03_特写与游戏动画/绞肉机特写动画/1.png" id="2_up5wq"]
|
||||
[ext_resource type="Texture2D" uid="uid://dweny6ivmkanl" path="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/2.png" id="3_5wyv8"]
|
||||
[ext_resource type="Texture2D" uid="uid://cvhquuvipetio" path="res://asset/art/gif/c03_特写与游戏动画/绞肉机特写动画/2.png" id="3_oilu3"]
|
||||
[ext_resource type="Texture2D" uid="uid://nvddvjnphsqx" path="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/3.png" id="4_ag85t"]
|
||||
[ext_resource type="Texture2D" uid="uid://bn3x37ya65ixm" path="res://asset/art/gif/c03_特写与游戏动画/绞肉机特写动画/3.png" id="4_uidoe"]
|
||||
[ext_resource type="Texture2D" uid="uid://bhhjsj1dqep40" path="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/0.png" id="5_5m4aa"]
|
||||
[ext_resource type="Texture2D" uid="uid://c5sc5ctss08qc" path="res://asset/art/gif/c03_特写与游戏动画/鬼母子神/0.png" id="5_uidoe"]
|
||||
[ext_resource type="Texture2D" uid="uid://lop0mxjb5y71" path="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/1.png" id="6_0df74"]
|
||||
[ext_resource type="Texture2D" uid="uid://dx2v7bicpg7b4" path="res://asset/art/gif/c03_特写与游戏动画/鬼母子神/1.png" id="6_yp83y"]
|
||||
[ext_resource type="Texture2D" uid="uid://bkh3cc2fg486c" path="res://asset/art/gif/c03_特写与游戏动画/鬼母子神/2.png" id="7_axfhb"]
|
||||
[ext_resource type="Texture2D" uid="uid://c08v3b4prnqyt" path="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/2.png" id="7_xhhcn"]
|
||||
[ext_resource type="Texture2D" uid="uid://4m0ix5nhdmdf" path="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/3.png" id="8_dspyd"]
|
||||
[ext_resource type="Texture2D" uid="uid://d0ad1s3sfsejp" path="res://asset/art/gif/c03_特写与游戏动画/鬼母子神/3.png" id="8_yp83y"]
|
||||
[ext_resource type="Texture2D" uid="uid://c6gpicmu30026" path="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/0.png" id="9_naxgo"]
|
||||
[ext_resource type="Texture2D" uid="uid://b7ufyuiusqmh7" path="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/1.png" id="10_qcrrr"]
|
||||
[ext_resource type="Texture2D" uid="uid://c21g54sgebjti" path="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/2.png" id="11_buon4"]
|
||||
[ext_resource type="Texture2D" uid="uid://c7vjtmjhv7v7f" path="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/3.png" id="12_cx8xa"]
|
||||
|
||||
[resource]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("1_fctjd")
|
||||
}, {
|
||||
"duration": 8.0,
|
||||
"texture": ExtResource("2_g03au")
|
||||
}, {
|
||||
"duration": 30.0,
|
||||
"texture": ExtResource("3_5wyv8")
|
||||
}, {
|
||||
"duration": 15.0,
|
||||
"texture": ExtResource("4_ag85t")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"父亲抱小孩",
|
||||
"speed": 30.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("5_5m4aa")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("6_0df74")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("7_xhhcn")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("8_dspyd")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"癞子背坐呼吸",
|
||||
"speed": 30.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("9_naxgo")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("10_qcrrr")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("11_buon4")
|
||||
}, {
|
||||
"duration": 20.0,
|
||||
"texture": ExtResource("12_cx8xa")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"癞子背对侧头呼吸",
|
||||
"speed": 30.0
|
||||
}, {
|
||||
"frames": [{
|
||||
"duration": 6.0,
|
||||
"texture": ExtResource("1_uknvx")
|
||||
}, {
|
||||
|
BIN
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/0.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
34
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/0.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dcwbe6hb3gdcp"
|
||||
path="res://.godot/imported/0.png-a3269686a9806970c0dfd8799afcb5ce.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/0.png"
|
||||
dest_files=["res://.godot/imported/0.png-a3269686a9806970c0dfd8799afcb5ce.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
|
BIN
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/1.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
34
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhrahd7u0yoba"
|
||||
path="res://.godot/imported/1.png-2461ac2fae16cf03e4832dc2fe3f2555.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/1.png"
|
||||
dest_files=["res://.godot/imported/1.png-2461ac2fae16cf03e4832dc2fe3f2555.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
|
BIN
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/2.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
34
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dweny6ivmkanl"
|
||||
path="res://.godot/imported/2.png-afafb5161ae4bca2cba94e19f92d32ed.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/2.png"
|
||||
dest_files=["res://.godot/imported/2.png-afafb5161ae4bca2cba94e19f92d32ed.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
|
BIN
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/3.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
34
asset/art/gif/c03_特写与游戏动画/父亲抱小孩/3.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://nvddvjnphsqx"
|
||||
path="res://.godot/imported/3.png-f1b878a4ae5a1352052eb9c3414b1c64.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/父亲抱小孩/3.png"
|
||||
dest_files=["res://.godot/imported/3.png-f1b878a4ae5a1352052eb9c3414b1c64.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/0.png
Normal file
After Width: | Height: | Size: 778 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/0.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhhjsj1dqep40"
|
||||
path="res://.godot/imported/0.png-29a92c9e2d863c26da560ecf9d7f55e9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/0.png"
|
||||
dest_files=["res://.godot/imported/0.png-29a92c9e2d863c26da560ecf9d7f55e9.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/1.png
Normal file
After Width: | Height: | Size: 771 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://lop0mxjb5y71"
|
||||
path="res://.godot/imported/1.png-9504242d789560b7aa7bd583e9c9ee9b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/1.png"
|
||||
dest_files=["res://.godot/imported/1.png-9504242d789560b7aa7bd583e9c9ee9b.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/2.png
Normal file
After Width: | Height: | Size: 768 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c08v3b4prnqyt"
|
||||
path="res://.godot/imported/2.png-771674e6060afe77d45d1ae84d22c7c2.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/2.png"
|
||||
dest_files=["res://.godot/imported/2.png-771674e6060afe77d45d1ae84d22c7c2.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/3.png
Normal file
After Width: | Height: | Size: 762 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/3.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://4m0ix5nhdmdf"
|
||||
path="res://.godot/imported/3.png-4b276625cb8a4697e2e33c8df223b9b9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背坐呼吸/3.png"
|
||||
dest_files=["res://.godot/imported/3.png-4b276625cb8a4697e2e33c8df223b9b9.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/0.png
Normal file
After Width: | Height: | Size: 737 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/0.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c6gpicmu30026"
|
||||
path="res://.godot/imported/0.png-218e6fe7dc6cd000a213f3a006d18933.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/0.png"
|
||||
dest_files=["res://.godot/imported/0.png-218e6fe7dc6cd000a213f3a006d18933.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/1.png
Normal file
After Width: | Height: | Size: 734 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b7ufyuiusqmh7"
|
||||
path="res://.godot/imported/1.png-d46a9f066213228516ec78730973b1b1.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/1.png"
|
||||
dest_files=["res://.godot/imported/1.png-d46a9f066213228516ec78730973b1b1.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/2.png
Normal file
After Width: | Height: | Size: 732 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c21g54sgebjti"
|
||||
path="res://.godot/imported/2.png-65316c99b267f6b126a0514cf04f68d3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/2.png"
|
||||
dest_files=["res://.godot/imported/2.png-65316c99b267f6b126a0514cf04f68d3.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
|
BIN
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/3.png
Normal file
After Width: | Height: | Size: 734 B |
34
asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/3.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c7vjtmjhv7v7f"
|
||||
path="res://.godot/imported/3.png-77114853f7248e4f7711c27bfb05bb12.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/gif/c03_特写与游戏动画/癞子背对侧头呼吸/3.png"
|
||||
dest_files=["res://.godot/imported/3.png-77114853f7248e4f7711c27bfb05bb12.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
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 576 B |
BIN
asset/art/scene/c03/s03_瞎子理发店/癞子特写/伤口血迹.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
34
asset/art/scene/c03/s03_瞎子理发店/癞子特写/伤口血迹.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dr51e8ell86"
|
||||
path="res://.godot/imported/伤口血迹.png-09a80deddc6822152bc5759dfa5b43c4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/scene/c03/s03_瞎子理发店/癞子特写/伤口血迹.png"
|
||||
dest_files=["res://.godot/imported/伤口血迹.png-09a80deddc6822152bc5759dfa5b43c4.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
|
BIN
asset/art/scene/c03/s03_瞎子理发店/癞子特写/刀劈特效.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
34
asset/art/scene/c03/s03_瞎子理发店/癞子特写/刀劈特效.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://k4eplss3bx5g"
|
||||
path="res://.godot/imported/刀劈特效.png-303e36c2e802d04a4dbed5e046db47cf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/art/scene/c03/s03_瞎子理发店/癞子特写/刀劈特效.png"
|
||||
dest_files=["res://.godot/imported/刀劈特效.png-303e36c2e802d04a4dbed5e046db47cf.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
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.1 KiB |
@ -4,7 +4,7 @@ extends CanvasLayer
|
||||
|
||||
var root_dir = "res://config/audio/"
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
var dir_access = DirAccess.open(root_dir) as DirAccess
|
||||
for dir in dir_access.get_directories():
|
||||
container.add_child(HSeparator.new())
|
||||
|
BIN
asset/audio/peiyin/c03/c03_理发店_小小蝶_5.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_小小蝶_5.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://ba2wk1ox76efe"
|
||||
path="res://.godot/imported/c03_理发店_小小蝶_5.ogg-66a9a4612eefec52289193483440da3f.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_小小蝶_5.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_小小蝶_5.ogg-66a9a4612eefec52289193483440da3f.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_小小蝶_6.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_小小蝶_6.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://cun811u2e7345"
|
||||
path="res://.godot/imported/c03_理发店_小小蝶_6.ogg-38c66f064187bd8dba9d0e151e32d032.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_小小蝶_6.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_小小蝶_6.ogg-38c66f064187bd8dba9d0e151e32d032.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_小小蝶_7.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_小小蝶_7.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://b0jpnl74h0r5j"
|
||||
path="res://.godot/imported/c03_理发店_小小蝶_7.ogg-7278d678564f0d8cfb1baab6ce4a8912.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_小小蝶_7.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_小小蝶_7.ogg-7278d678564f0d8cfb1baab6ce4a8912.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_10.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_10.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dpmc3ku12hvgg"
|
||||
path="res://.godot/imported/c03_理发店_癞子_10.ogg-8654894b5ed36318642f4dd8c25d8a89.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_10.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_10.ogg-8654894b5ed36318642f4dd8c25d8a89.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_11.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_11.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://duj1rj5pqaypw"
|
||||
path="res://.godot/imported/c03_理发店_癞子_11.ogg-3cb72d9b6ab549acd5cc78b162889624.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_11.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_11.ogg-3cb72d9b6ab549acd5cc78b162889624.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_12.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_12.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://b0sauuigs8y0p"
|
||||
path="res://.godot/imported/c03_理发店_癞子_12.ogg-c35d34415f87cc409e4eba30b914bb4a.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_12.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_12.ogg-c35d34415f87cc409e4eba30b914bb4a.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_13.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_13.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://8wtlftvqn5gp"
|
||||
path="res://.godot/imported/c03_理发店_癞子_13.ogg-ad4f488cd4278b06727a566379a9cca8.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_13.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_13.ogg-ad4f488cd4278b06727a566379a9cca8.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_14.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_14.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://bplaj6uidsias"
|
||||
path="res://.godot/imported/c03_理发店_癞子_14.ogg-0ae7c592462185954fbc2fc44ff8161b.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_14.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_14.ogg-0ae7c592462185954fbc2fc44ff8161b.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_15.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_15.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dqyythgj4b2a7"
|
||||
path="res://.godot/imported/c03_理发店_癞子_15.ogg-13ac10d8375509b479ea2e94d96f3cfa.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_15.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_15.ogg-13ac10d8375509b479ea2e94d96f3cfa.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_8.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_8.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://bylaoxlh2bii"
|
||||
path="res://.godot/imported/c03_理发店_癞子_8.ogg-5cd1c86dea61b02ba8a86260dc6c73d9.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_8.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_8.ogg-5cd1c86dea61b02ba8a86260dc6c73d9.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_癞子_9.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_癞子_9.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://d4mihdw45ed6x"
|
||||
path="res://.godot/imported/c03_理发店_癞子_9.ogg-502658df3b170c4f123c76ea08ed87ec.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_癞子_9.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_癞子_9.ogg-502658df3b170c4f123c76ea08ed87ec.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_15.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_15.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://tmsyvdoklrid"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_15.ogg-993c9e658b98f80d58aa324105579201.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_15.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_15.ogg-993c9e658b98f80d58aa324105579201.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_16.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_16.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://cyfi6r567yo1u"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_16.ogg-cf3255fb045aae07a6e320b54f000c3f.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_16.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_16.ogg-cf3255fb045aae07a6e320b54f000c3f.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_17.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_17.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://d0tfvpokjdcp1"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_17.ogg-77d1e04338e47990fbcecedf8301c49d.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_17.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_17.ogg-77d1e04338e47990fbcecedf8301c49d.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_18.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_18.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dtbb5vi4ry85b"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_18.ogg-4e672df3b198ee52c88f0ce7cfef4c3a.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_18.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_18.ogg-4e672df3b198ee52c88f0ce7cfef4c3a.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_19.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_19.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://ckl6eyb6fn6ui"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_19.ogg-f6fd7591a238ead0b34e2d58608527ea.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_19.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_19.ogg-f6fd7591a238ead0b34e2d58608527ea.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_20.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_20.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://bvrkiuimnlh5w"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_20.ogg-02426bd5c2b1e5597e2b937a0c0940eb.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_20.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_20.ogg-02426bd5c2b1e5597e2b937a0c0940eb.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_21.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_21.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dnctmpsv0xrad"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_21.ogg-620878f56dfc42101cca76bc6de2b9ec.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_21.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_21.ogg-620878f56dfc42101cca76bc6de2b9ec.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_22.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_22.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://uf14ji1pkqaf"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_22.ogg-50207038a1cfd50caa472f5ab22a8778.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_22.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_22.ogg-50207038a1cfd50caa472f5ab22a8778.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_瞎子_23.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_瞎子_23.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dsymgeq6i02mj"
|
||||
path="res://.godot/imported/c03_理发店_瞎子_23.ogg-9721e730eb715d8a9090aee47d6b7171.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_瞎子_23.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_瞎子_23.ogg-9721e730eb715d8a9090aee47d6b7171.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
BIN
asset/audio/peiyin/c03/c03_理发店_陆仁.ogg
Normal file
19
asset/audio/peiyin/c03/c03_理发店_陆仁.ogg.import
Normal file
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://bqhqxmf5hsed0"
|
||||
path="res://.godot/imported/c03_理发店_陆仁.ogg-5b28d2fa4f1ffe8f2ecd2c7ee6f218e2.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://asset/audio/peiyin/c03/c03_理发店_陆仁.ogg"
|
||||
dest_files=["res://.godot/imported/c03_理发店_陆仁.ogg-5b28d2fa4f1ffe8f2ecd2c7ee6f218e2.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
@ -8,3 +8,84 @@ Second one,Second one,(response),,
|
||||
Start again,Start again,(response),,
|
||||
End the conversation,End the conversation,(response),,
|
||||
For more information see the online documentation.,For more information see the online documentation.,Nathan,,
|
||||
(看病费用涨价,为了攒钱让小蝶上学,),(看病费用涨价,为了攒钱让小蝶上学,),陆仁,,
|
||||
(小蝶想请小蝉来家里吃饭,写了请柬),(小蝶想请小蝉来家里吃饭,写了请柬),小蝶,,
|
||||
(父母让小蝶去送请柬,顺便带肉回家做饭),(父母让小蝶去送请柬,顺便带肉回家做饭),陆仁,,
|
||||
那个...方叔叔,小蝉在吗? [ID:],那个...方叔叔,小蝉在吗? [ID:],小小蝶,,
|
||||
咳咳,她,咳咳,不在。 [ID:],咳咳,她,咳咳,不在。 [ID:],瞎子,,
|
||||
她是生病了吗,好几天没见她了。 [ID:],她是生病了吗,好几天没见她了。 [ID:],小小蝶,,
|
||||
她... [ID:],她... [ID:],瞎子,,
|
||||
我挨家挨户地问啊,都说没看见她。 [ID:],我挨家挨户地问啊,都说没看见她。 [ID:],瞎子,,
|
||||
一个活生生的小孩子,就这么找不见了... [ID:],一个活生生的小孩子,就这么找不见了... [ID:],瞎子,,
|
||||
c03_理发店_癞子_8,我家那个昨天想拿推子给我剃头,差点给我开了瓢!,癞子,,
|
||||
c03_理发店_癞子_9,你瞅瞅,这么长一道血印子!唉呀...,癞子,,
|
||||
c03_理发店_瞎子_15,可不能这么说。,瞎子,,
|
||||
c03_理发店_瞎子_16,弟妹这一手,没准是变相消了你的血光灾呢。,瞎子,,
|
||||
c03_理发店_瞎子_17,逢凶化吉,灾消晦退,好日子还在后头。,瞎子,,
|
||||
c03_理发店_癞子_10,哼,听你这么一说,好像确实有那么几分道理。,癞子,,
|
||||
c03_理发店_癞子_11,不过这辫子留了这么多年,乍一找人剪了,心里还有点怪不是滋味儿。,癞子,,
|
||||
c03_理发店_瞎子_18,怎么舍得剪了?,瞎子,,
|
||||
c03_理发店_癞子_12,妇人家家的,不知道从哪打听到用男人的辫子入药能治病,一直念叨个不停。,癞子,,
|
||||
c03_理发店_癞子_13,...你听过这说法没有?,癞子,,
|
||||
c03_理发店_瞎子_19,我不懂医,不好乱说。,瞎子,,
|
||||
c03_理发店_癞子_14,最近街上也是彻底乱了套,那些人看见你留了“阴阳头”,就跟那闻着味儿的狗一样凑过来...,癞子,,
|
||||
c03_理发店_癞子_15,哦,瞧我这记性,忘了你眼神不好,不爱出门。,癞子,,
|
||||
c03_理发店_小小蝶_5,方叔叔,我来找小蝉玩儿,她在屋里吗?,小小蝶,,
|
||||
c03_理发店_瞎子_20,她摔了一跤,还睡着呢,我喊她起来?,瞎子,,
|
||||
c03_理发店_小小蝶_6,不用不用,我在这等一会儿就行。,小小蝶,,
|
||||
c03_理发店_陆仁,方老弟,你正好给我家这小皮猴儿修修毛,成天跟个假小子似的,没个小姑娘样子。,陆仁,,
|
||||
c03_理发店_瞎子_21,小孩子嘛,高高兴兴就好。,瞎子,,
|
||||
c03_理发店_瞎子_22,你们也别在这干等,我去给你们拿些“洋点心”。,瞎子,,
|
||||
c03_理发店_瞎子_23,说是什么舶来品,高级货,你们拿出尝尝,甜个嘴。,瞎子,,
|
||||
c03_理发店_小小蝶_7,谢谢方叔叔。,小小蝶,,
|
||||
记得当年这箱麻将还是我弟弟背回来的。,记得当年这箱麻将还是我弟弟背回来的。,李氏,,
|
||||
那天下了雨,他光着膀子回来,用衣裳包着,说木头怕水他不怕,就当免费洗了个澡...,那天下了雨,他光着膀子回来,用衣裳包着,说木头怕水他不怕,就当免费洗了个澡...,李氏,,
|
||||
(轻笑一声)后来发现少了张牌,这小崽子又背着我们敲敲打打,硬是做了张一模一样的补上。,(轻笑一声)后来发现少了张牌,这小崽子又背着我们敲敲打打,硬是做了张一模一样的补上。,癞子,,
|
||||
(苦笑)他呀,就是个死心眼,犟种!,(苦笑)他呀,就是个死心眼,犟种!,李氏,,
|
||||
得了病就一直忍着,等到实在瞒不住了才告诉咱们!,得了病就一直忍着,等到实在瞒不住了才告诉咱们!,李氏,,
|
||||
他认了命等死,咱们不能认,只要今晚赢了钱...,他认了命等死,咱们不能认,只要今晚赢了钱...,癞子,,
|
||||
哟,人都来齐了怎么还在理麻将?今晚大家可得好好地打一场!,哟,人都来齐了怎么还在理麻将?今晚大家可得好好地打一场!,胖子,,
|
||||
我观张老弟面色晦暗、嘴唇发白,难不成是这些日子手气不佳,输多了牌才...,我观张老弟面色晦暗、嘴唇发白,难不成是这些日子手气不佳,输多了牌才...,瞎子,,
|
||||
(哼)以前可不这样,不说牌桌通杀,也是赢多输少,偏偏最近...,(哼)以前可不这样,不说牌桌通杀,也是赢多输少,偏偏最近...,胖子,,
|
||||
最近怎么了?,最近怎么了?,李氏,,
|
||||
碰。,碰。,瞎子,,
|
||||
自从那三楼的婆娘出了名以后,我这手气就臭得不行...怕不是让人给“借运”了?,自从那三楼的婆娘出了名以后,我这手气就臭得不行...怕不是让人给“借运”了?,胖子,,
|
||||
还真不好说。运气这玩意玄乎得很。,还真不好说。运气这玩意玄乎得很。,癞子,,
|
||||
我以前走南闯北的时候,见过一个会使邪门功夫的老爷子。,我以前走南闯北的时候,见过一个会使邪门功夫的老爷子。,癞子,,
|
||||
说是能“借”人家的命来延长自己的寿命...,说是能“借”人家的命来延长自己的寿命...,癞子,,
|
||||
听着怪瘆人的。,听着怪瘆人的。,胖子,,
|
||||
人外有人,天外有天。,人外有人,天外有天。,瞎子,,
|
||||
天外还有什么...咱们这些闲杂人等也说了不算。,天外还有什么...咱们这些闲杂人等也说了不算。,瞎子,,
|
||||
六万。,六万。,李氏,,
|
||||
话又说回来,你们喝过洋酒没有?用葡萄发酵成的,红得发黑,度数还高。,话又说回来,你们喝过洋酒没有?用葡萄发酵成的,红得发黑,度数还高。,李氏,,
|
||||
那叫香槟,洋人就喜欢整那些面子货,不适口。,那叫香槟,洋人就喜欢整那些面子货,不适口。,癞子,,
|
||||
(啧)见多识广还得看王兄啊。,(啧)见多识广还得看王兄啊。,胖子,,
|
||||
瞧这架势,以前没准多风光...,瞧这架势,以前没准多风光...,胖子,,
|
||||
英雄不问出处。,英雄不问出处。,瞎子,,
|
||||
都过去了,都过去了。,都过去了,都过去了。,癞子,,
|
||||
为了跑出来,我们还睡过桥洞呢!,为了跑出来,我们还睡过桥洞呢!,李氏,,
|
||||
(此处响起癞子的咳嗽声)那阵子我老是梦见让人剁了手指头,八成就是因为那桥是洋人建的...,(此处响起癞子的咳嗽声)那阵子我老是梦见让人剁了手指头,八成就是因为那桥是洋人建的...,李氏,,
|
||||
你说的是外摆渡桥?,你说的是外摆渡桥?,胖子,,
|
||||
应该是吧,我们再没回去过,也不知道后来成了什么样...,应该是吧,我们再没回去过,也不知道后来成了什么样...,癞子,,
|
||||
(脏话)怎么一张牌都不来!,(脏话)怎么一张牌都不来!,胖子,,
|
||||
八条,八条,瞎子,,
|
||||
对了,小蝉那孩子有消息了吗?,对了,小蝉那孩子有消息了吗?,癞子,,
|
||||
要是有消息,我也不会跟你们在这打牌,虚度光阴。,要是有消息,我也不会跟你们在这打牌,虚度光阴。,瞎子,,
|
||||
方大哥,你这话说得不妥。,方大哥,你这话说得不妥。,李氏,,
|
||||
咱们哪个心里头不苦的?不都是在这牌桌上苦中作乐呢。,咱们哪个心里头不苦的?不都是在这牌桌上苦中作乐呢。,李氏,,
|
||||
找点乐子,何必想那么多。,找点乐子,何必想那么多。,胖子,,
|
||||
我这头生意都不做了,帮你满大街的又找又问,不也是白忙活一场。,我这头生意都不做了,帮你满大街的又找又问,不也是白忙活一场。,李氏,,
|
||||
要我说,还是得从身边的人下手。,要我说,还是得从身边的人下手。,李氏,,
|
||||
指不定是被楼里的那个邻居拐了呢,就那么一丁点的小女孩,谁都能敲昏了抱回家去。,指不定是被楼里的那个邻居拐了呢,就那么一丁点的小女孩,谁都能敲昏了抱回家去。,癞子,,
|
||||
...我看她跟三楼那个叫什么小蝶的走得很近,老是手拉手出去,成天凑在一块说悄悄话。,...我看她跟三楼那个叫什么小蝶的走得很近,老是手拉手出去,成天凑在一块说悄悄话。,胖子,,
|
||||
他们一家人最近都不怎么出门,我看就是心里有鬼。,他们一家人最近都不怎么出门,我看就是心里有鬼。,胖子,,
|
||||
把小孩骗走了换钱换药,搞邪术,什么都干得出来。,把小孩骗走了换钱换药,搞邪术,什么都干得出来。,胖子,,
|
||||
这么一说,好像确实有点道理。,这么一说,好像确实有点道理。,癞子,,
|
||||
我小弟去看过病了,那药方邪门得很,待会我拿给你们瞧瞧。,我小弟去看过病了,那药方邪门得很,待会我拿给你们瞧瞧。,李氏,,
|
||||
我活了这么大岁数,也是第一次见。,我活了这么大岁数,也是第一次见。,李氏,,
|
||||
道听途说而已。,道听途说而已。,瞎子,,
|
||||
自摸!(笑),自摸!(笑),癞子,,
|
||||
(脏话)不玩了不玩了,今天一把都没胡过!,(脏话)不玩了不玩了,今天一把都没胡过!,胖子,,
|
||||
我也得早点回去,没准小蝉今儿个能回来,我总惦记着回去看看。,我也得早点回去,没准小蝉今儿个能回来,我总惦记着回去看看。,瞎子,,
|
||||
天黑了,你们回去都小心着点。,天黑了,你们回去都小心着点。,李氏,,
|
||||
嘶...,嘶...,癞子,,
|
||||
|
|
@ -5,17 +5,46 @@
|
||||
=> END
|
||||
|
||||
~ c03_s02_邀请小蝉与瞎子对话1
|
||||
小小蝶: 那个...方叔叔,小蝉在吗?
|
||||
瞎子: 咳咳,她,咳咳,不在。
|
||||
小小蝶: 她是生病了吗,好几天没见她了。
|
||||
小小蝶: 那个...方叔叔,小蝉在吗? [ID:]
|
||||
瞎子: 咳咳,她,咳咳,不在。 [ID:]
|
||||
小小蝶: 她是生病了吗,好几天没见她了。 [ID:]
|
||||
=> END
|
||||
|
||||
~ c03_s02_邀请小蝉与瞎子对话2
|
||||
瞎子: 她...
|
||||
瞎子: 我挨家挨户地问啊,都说没看见她。
|
||||
瞎子: 一个活生生的小孩子,就这么找不见了...
|
||||
瞎子: 她... [ID:]
|
||||
瞎子: 我挨家挨户地问啊,都说没看见她。 [ID:]
|
||||
瞎子: 一个活生生的小孩子,就这么找不见了... [ID:]
|
||||
=> END
|
||||
|
||||
~ c03_s03_理发店演出1
|
||||
癞子: 我家那个昨天想拿推子给我剃头,差点给我开了瓢! [ID:c03_理发店_癞子_8]
|
||||
癞子: 你瞅瞅,这么长一道血印子!唉呀... [ID:c03_理发店_癞子_9]
|
||||
瞎子: 可不能这么说。 [ID:c03_理发店_瞎子_15]
|
||||
瞎子: 弟妹这一手,没准是变相消了你的血光灾呢。 [ID:c03_理发店_瞎子_16]
|
||||
瞎子: 逢凶化吉,灾消晦退,好日子还在后头。 [ID:c03_理发店_瞎子_17]
|
||||
癞子: 哼,听你这么一说,好像确实有那么几分道理。 [ID:c03_理发店_癞子_10]
|
||||
癞子: 不过这辫子留了这么多年,乍一找人剪了,心里还有点怪不是滋味儿。 [ID:c03_理发店_癞子_11]
|
||||
瞎子: 怎么舍得剪了? [ID:c03_理发店_瞎子_18]
|
||||
癞子: 妇人家家的,不知道从哪打听到用男人的辫子入药能治病,一直念叨个不停。 [ID:c03_理发店_癞子_12]
|
||||
癞子: ...你听过这说法没有? [ID:c03_理发店_癞子_13]
|
||||
瞎子: 我不懂医,不好乱说。 [ID:c03_理发店_瞎子_19]
|
||||
癞子: 最近街上也是彻底乱了套,那些人看见你留了“阴阳头”,就跟那闻着味儿的狗一样凑过来... [ID:c03_理发店_癞子_14]
|
||||
癞子: 哦,瞧我这记性,忘了你眼神不好,不爱出门。 [#wait=1.5] [ID:c03_理发店_癞子_15]
|
||||
小小蝶: 方叔叔,我来找小蝉玩儿,她在屋里吗? [ID:c03_理发店_小小蝶_5]
|
||||
=> END
|
||||
|
||||
~ c03_s03_理发店演出2
|
||||
瞎子: 她摔了一跤,还睡着呢,我喊她起来? [ID:c03_理发店_瞎子_20]
|
||||
小小蝶: 不用不用,我在这等一会儿就行。 [ID:c03_理发店_小小蝶_6]
|
||||
陆仁: 方老弟,你正好给我家这小皮猴儿修修毛,成天跟个假小子似的,没个小姑娘样子。 [ID:c03_理发店_陆仁]
|
||||
瞎子: 小孩子嘛,高高兴兴就好。 [ID:c03_理发店_瞎子_21]
|
||||
瞎子: 你们也别在这干等,我去给你们拿些“洋点心”。 [ID:c03_理发店_瞎子_22]
|
||||
=> END
|
||||
|
||||
~ c03_s03_理发店演出3
|
||||
瞎子: 说是什么舶来品,高级货,你们拿出尝尝,甜个嘴。 [ID:c03_理发店_瞎子_23]
|
||||
小小蝶: 谢谢方叔叔。 [ID:c03_理发店_小小蝶_7]
|
||||
=> END
|
||||
|
||||
~ c03_s04_整理麻将游戏0
|
||||
# 打牌准备,整理麻将盒游戏成功(演出至麻将搬到桌上):
|
||||
|
@ -1,15 +1 @@
|
||||
keys,zh_CN,_character,_notes,_tags
|
||||
c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 ~ c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 ~ c03_f2_madman_runaway=2,,,
|
||||
c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 -> c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 -> c03_f2_madman_runaway=2,,,
|
||||
0:demo 1:release,0:demo 1:release,release_stage,,
|
||||
1:序章 2:第一章 3:第二章 4:第三章 5:第四章 6:尾声,1:序章 2:第一章 3:第二章 4:第三章 5:第四章 6:尾声,current_chapter_stage,,
|
||||
0:初始化_关闭 1:打开 2:放入小蝉人偶 3:全部放置正确_可摇手柄 4:已播放完成,0:初始化_关闭 1:打开 2:放入小蝉人偶 3:全部放置正确_可摇手柄 4:已播放完成,c02_musicbox_stage,,
|
||||
0:初始化 1:已交互疯子 2:小鞋已掉落,0:初始化 1:已交互疯子 2:小鞋已掉落,c02_madman_interacted_stage,,
|
||||
0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给出弹珠 3:游戏结束_小猫纸片 4:游戏结束_小猫离开,0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给出弹珠 3:游戏结束_小猫纸片 4:游戏结束_小猫离开,c02_ball_game_stage,,
|
||||
0:初始化 1:已放肉,0:初始化 1:已放肉,c03_s01_meat_put,,
|
||||
0:初始化 1:已偷听_需邀请 2:完成邀请,0:初始化 1:已偷听_需邀请 2:完成邀请,c03_invite_xchan_supper,,
|
||||
0:初始化 1:已使用剪刀 2:已剪下,0:初始化 1:已使用剪刀 2:已剪下,c03_s03_laizi_braid,,
|
||||
0:初始化 1:跑开_纸人挡路 2:消除纸人,0:初始化 1:跑开_纸人挡路 2:消除纸人,c03_f2_madman_runaway,,
|
||||
c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 & c03_mahjong_game=1 -> c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 & c03_mahjong_game=1 -> c03_f2_madman_runaway=2,,,
|
||||
0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏,0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏,c03_before_mahjong_game,,
|
||||
0:麻将理牌 1:麻将出千 2:麻将结束 3:演出结束,0:麻将理牌 1:麻将出千 2:麻将结束 3:演出结束,c03_mahjong_game,,
|
||||
|
|
@ -12,7 +12,7 @@ c02_ball_game_stage: 0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给
|
||||
~ EventStage_c03
|
||||
c03_s01_meat_put: 0:初始化 1:已放肉
|
||||
c03_invite_xchan_supper: 0:初始化 1:已偷听_需邀请 2:完成邀请
|
||||
c03_s03_laizi_braid: 0:初始化 1:已使用剪刀 2:已剪下
|
||||
c03_s03_laizi_braid: 0:初始化 1:已剪下辫子 2:演出结束
|
||||
c03_f2_madman_runaway: 0:初始化 1:跑开_纸人挡路 2:消除纸人
|
||||
c03_invite_xchan_supper=2 & c03_s03_laizi_braid=2 -> c03_f2_madman_runaway=2
|
||||
c03_before_mahjong_game: 0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏
|
||||
|
@ -147,19 +147,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'
|
||||
@ -239,3 +240,4 @@ c03_s02_小蝉寻人启事,?小蝉寻人启事,,,,,
|
||||
c03_s03_获得剪刀纸舌头,?✂️纸人,,,,,
|
||||
c03_s03_桌子,?桌子,,,,,
|
||||
c03_s03_洗头盆,?洗头盆,,,,,
|
||||
c03_s03_演出结束,?演出结束,刚刚他们?,,,,,
|
||||
|
|
@ -204,9 +204,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 院子
|
||||
@ -214,13 +214,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
|
||||
|
||||
@ -323,6 +324,7 @@
|
||||
?✂️纸人 [ID:c03_s03_获得剪刀纸舌头]
|
||||
?桌子 [ID:c03_s03_桌子]
|
||||
?洗头盆 [ID:c03_s03_洗头盆]
|
||||
?演出结束,刚刚他们? [ID:]
|
||||
|
||||
# s04 李癞房间
|
||||
# s05 肉铺
|
||||
|
@ -54,7 +54,7 @@ func _ready() -> void:
|
||||
next_btn.pressed.connect(_on_next_btn_pressed)
|
||||
|
||||
|
||||
func _validate_json():
|
||||
func _validate_json() -> void:
|
||||
# first_frame_mapping
|
||||
if !original_config.data.has("first_frame_mapping"):
|
||||
original_config.data["first_frame_mapping"] = {}
|
||||
@ -69,7 +69,7 @@ func _validate_json():
|
||||
original_config.data["frames_per_second"] = {}
|
||||
|
||||
|
||||
func _scan():
|
||||
func _scan() -> void:
|
||||
var dir = DirAccess.open(scan_path)
|
||||
if !dir:
|
||||
printerr("Failed to open directory:", scan_path)
|
||||
@ -109,7 +109,7 @@ func _scan_subdir_frames(path: String) -> Dictionary:
|
||||
return frames
|
||||
|
||||
|
||||
func load_and_display_pages(search_text):
|
||||
func load_and_display_pages(search_text) -> void:
|
||||
all_keys = original_config.data.dirs.keys()
|
||||
all_keys.sort_custom(func(a, b): return a < b)
|
||||
if search_edit.text:
|
||||
@ -126,7 +126,7 @@ func load_and_display_pages(search_text):
|
||||
_load_current_page()
|
||||
|
||||
|
||||
func _on_prev_btn_pressed():
|
||||
func _on_prev_btn_pressed() -> void:
|
||||
if current_page <= 1:
|
||||
current_page = all_pages
|
||||
else:
|
||||
@ -134,7 +134,7 @@ func _on_prev_btn_pressed():
|
||||
_load_current_page()
|
||||
|
||||
|
||||
func _on_next_btn_pressed():
|
||||
func _on_next_btn_pressed() -> void:
|
||||
if current_page >= all_pages:
|
||||
current_page = 1
|
||||
else:
|
||||
@ -142,7 +142,7 @@ func _on_next_btn_pressed():
|
||||
_load_current_page()
|
||||
|
||||
|
||||
func _load_current_page():
|
||||
func _load_current_page() -> void:
|
||||
page_label.text = str(current_page) + "/" + str(all_pages)
|
||||
var start = (current_page - 1) * PAGE_SIZE
|
||||
var end = min(start + PAGE_SIZE, matched_keys.size())
|
||||
@ -155,7 +155,7 @@ func _load_current_page():
|
||||
config_edit.text = text
|
||||
|
||||
|
||||
func _display_cards(current_keys: Array):
|
||||
func _display_cards(current_keys: Array) -> void:
|
||||
# clear frames_display_grid
|
||||
for child in frames_display_grid.get_children():
|
||||
child.queue_free()
|
||||
|
@ -34,7 +34,7 @@ func _ready() -> void:
|
||||
var reload_lock := Mutex.new()
|
||||
|
||||
|
||||
func display_frames():
|
||||
func display_frames() -> void:
|
||||
if mapping_name and animated_sprite.sprite_frames.has_animation(mapping_name):
|
||||
animated_sprite.play(mapping_name)
|
||||
# scale down if the frame is too big
|
||||
@ -47,7 +47,7 @@ func display_frames():
|
||||
reload_frames()
|
||||
|
||||
|
||||
func reload_frames():
|
||||
func reload_frames() -> void:
|
||||
# 暂不启用,使用手动调整
|
||||
# return
|
||||
var sprite_frames = animated_sprite.sprite_frames as SpriteFrames
|
||||
@ -85,7 +85,7 @@ func reload_frames():
|
||||
display_frames()
|
||||
|
||||
|
||||
func _on_mapping_submitted(new_text: String):
|
||||
func _on_mapping_submitted(new_text: String) -> void:
|
||||
# clear the old mapping
|
||||
var sprite_frames = animated_sprite.sprite_frames as SpriteFrames
|
||||
if sprite_frames.has_animation(mapping_name):
|
||||
@ -97,7 +97,7 @@ func _on_mapping_submitted(new_text: String):
|
||||
_save_and_update_frames_and_config()
|
||||
|
||||
|
||||
func _on_frames_speed_submitted(new_text: String):
|
||||
func _on_frames_speed_submitted(new_text: String) -> void:
|
||||
frames_per_sec = int(new_text)
|
||||
if frames_per_sec < 1:
|
||||
frames_per_sec = 1
|
||||
@ -108,7 +108,7 @@ func _on_frames_speed_submitted(new_text: String):
|
||||
_save_and_update_frames_and_config()
|
||||
|
||||
|
||||
func _on_first_frame_mapping_submitted(new_text: String):
|
||||
func _on_first_frame_mapping_submitted(new_text: String) -> void:
|
||||
# clear the old first frame mapping
|
||||
if first_frame_mapping != "":
|
||||
var sprite_frames = animated_sprite.sprite_frames as SpriteFrames
|
||||
@ -120,7 +120,7 @@ func _on_first_frame_mapping_submitted(new_text: String):
|
||||
_save_and_update_frames_and_config()
|
||||
|
||||
|
||||
func _on_mirror_mapping_submitted(new_text: String):
|
||||
func _on_mirror_mapping_submitted(new_text: String) -> void:
|
||||
# clear the old mirror mapping
|
||||
if mirror_mapping != "":
|
||||
var sprite_frames = animated_sprite.sprite_frames as SpriteFrames
|
||||
@ -132,7 +132,7 @@ func _on_mirror_mapping_submitted(new_text: String):
|
||||
_save_and_update_frames_and_config()
|
||||
|
||||
|
||||
func _create_mirror():
|
||||
func _create_mirror() -> void:
|
||||
if not mirror_mapping:
|
||||
return
|
||||
var mirror_dir_path = "res://asset/art/animation/" + mirror_mapping
|
||||
@ -159,7 +159,7 @@ func _create_mirror():
|
||||
sprite_frames.set_animation_speed(mapping_name, frames_per_sec)
|
||||
|
||||
|
||||
func _save_and_update_frames_and_config():
|
||||
func _save_and_update_frames_and_config() -> void:
|
||||
# save the sprite_frames
|
||||
if animated_sprite:
|
||||
ResourceSaver.save(animated_sprite.sprite_frames)
|
||||
|
@ -4,7 +4,7 @@ var gif_path = "res://asset/art/gif/"
|
||||
var sprite_frames = preload("res://config/animation/entity_sprite_frames.tres")
|
||||
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
var gif_files = []
|
||||
for file in DirAccess.open(gif_path).get_files():
|
||||
if file.get_extension() == "gif":
|
||||
|
@ -11,7 +11,7 @@ func _ready() -> void:
|
||||
load_btn.pressed.connect(_reload_all)
|
||||
|
||||
|
||||
func _reload_all():
|
||||
func _reload_all() -> void:
|
||||
static_frames.clear_all()
|
||||
var dir = DirAccess.open(path) as DirAccess
|
||||
static_frames.add_animation("placeholder")
|
||||
|
@ -9,7 +9,7 @@ func _init() -> void:
|
||||
bus = "game_sfx"
|
||||
|
||||
|
||||
func play_random():
|
||||
func play_random() -> void:
|
||||
if audio_collections == null:
|
||||
push_warning("empty audio_collections")
|
||||
return
|
||||
|
@ -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="衔蝶"
|
||||
@ -97,8 +97,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"
|
||||
@ -586,7 +586,7 @@ storyboard/custom_bg_color=Color(0, 0, 0, 1)
|
||||
name="Android"
|
||||
platform="Android"
|
||||
runnable=true
|
||||
advanced_options=false
|
||||
advanced_options=true
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
|
@ -3,24 +3,30 @@ extends Node
|
||||
|
||||
signal archive_loaded
|
||||
|
||||
# Constants
|
||||
const CURRENT_VERSION = 6
|
||||
const ARCHIVE_ID_MIN = 0
|
||||
const ARCHIVE_ID_MAX = 99
|
||||
const ARCHIVE_ID_DIGITS = 3
|
||||
|
||||
# Static paths
|
||||
static var user_data_root_dir := "user://data/" # must end with "/"
|
||||
static var user_archives_dir := "user://data/archives/"
|
||||
static var archive_prefix := "save"
|
||||
|
||||
# Archive management
|
||||
var archive: AssembledArchive:
|
||||
set(val):
|
||||
archive = val
|
||||
if archive:
|
||||
# emit signal
|
||||
archive_loaded.emit()
|
||||
GlobalConfigManager.print_global_info()
|
||||
print("use archive ", archive.resource_path)
|
||||
archive.event_stage["release_stage"] = GlobalConfig.RELEASE_STAGE
|
||||
print_rich("[color=brown] release_stage = %s[/color]" % GlobalConfig.RELEASE_STAGE)
|
||||
|
||||
|
||||
|
||||
# current archive
|
||||
static var user_root_dir := "user://data/" # must end with "/"
|
||||
static var archive_dir := "user://data/archives/"
|
||||
static var archive_prefix := "save"
|
||||
|
||||
const CURRENT_VERSION = 6
|
||||
|
||||
var archives_dict: Dictionary[int, AssembledArchive] = {}
|
||||
var archives_notes_dict: Dictionary[int, String] = {}
|
||||
var autosave_timer := Timer.new()
|
||||
@ -32,24 +38,22 @@ func _ready() -> void:
|
||||
# 禁用默认退出行为,在 _notification 处理 NOTIFICATION_WM_CLOSE_REQUEST 时保存数据
|
||||
get_tree().set_auto_accept_quit(false)
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
|
||||
if not _check_dirs_and_archives():
|
||||
_handle_load_error("存档目录", "读写")
|
||||
return
|
||||
autosave_timer.timeout.connect(_try_auto_save)
|
||||
autosave_timer.stop()
|
||||
add_child(autosave_timer)
|
||||
|
||||
_setup_autosave_timer()
|
||||
|
||||
# config should be loaded first
|
||||
load_config()
|
||||
|
||||
# 在 debug or editor 模式下,直接保证有 archive
|
||||
if GlobalConfig.DEBUG or Engine.is_editor_hint():
|
||||
if archives_dict.size() == 0:
|
||||
create_and_use_new_archive(0)
|
||||
else:
|
||||
# debug 模式下默认使用 0 号存档
|
||||
GlobalConfigManager.config.current_selected_archive_id = 0
|
||||
_ensure_debug_archive()
|
||||
|
||||
|
||||
func _notification(what):
|
||||
func _notification(what: int) -> void:
|
||||
# handle window close request
|
||||
if what == NOTIFICATION_WM_CLOSE_REQUEST:
|
||||
save_all()
|
||||
@ -59,14 +63,27 @@ func _notification(what):
|
||||
SceneManager.quit_game()
|
||||
|
||||
|
||||
func _on_archive_id_changed():
|
||||
func _setup_autosave_timer() -> void:
|
||||
autosave_timer.timeout.connect(_try_auto_save)
|
||||
autosave_timer.stop()
|
||||
add_child(autosave_timer)
|
||||
|
||||
|
||||
func _ensure_debug_archive() -> void:
|
||||
if archives_dict.is_empty():
|
||||
create_and_use_new_archive(0)
|
||||
else:
|
||||
# debug 模式下默认使用 0 号存档
|
||||
GlobalConfigManager.config.current_selected_archive_id = 0
|
||||
|
||||
|
||||
func _on_archive_id_changed() -> void:
|
||||
var selected_id = GlobalConfigManager.config.current_selected_archive_id
|
||||
if selected_id < 0:
|
||||
return
|
||||
# if archive and selected_id == archive.archive_id:
|
||||
# print("_on_archive_id_changed same id=", selected_id)
|
||||
# return
|
||||
|
||||
print("_on_archive_id_changed id=", selected_id)
|
||||
|
||||
if not archives_dict.has(selected_id):
|
||||
print("新建存档 ", selected_id)
|
||||
create_and_use_new_archive(selected_id)
|
||||
@ -76,16 +93,19 @@ func _on_archive_id_changed():
|
||||
load_archive()
|
||||
|
||||
|
||||
func check_autosave_options():
|
||||
if (
|
||||
GlobalConfigManager.config.auto_save_enabled
|
||||
and archive
|
||||
and GlobalConfigManager.config.auto_save_seconds > 1
|
||||
):
|
||||
func check_autosave_options() -> void:
|
||||
var config = GlobalConfigManager.config
|
||||
var should_enable_autosave = (
|
||||
config.auto_save_enabled
|
||||
and archive
|
||||
and config.auto_save_seconds > 1
|
||||
)
|
||||
|
||||
if should_enable_autosave:
|
||||
# reset left time
|
||||
autosave_timer.stop()
|
||||
autosave_timer.one_shot = false
|
||||
autosave_timer.wait_time = GlobalConfigManager.config.auto_save_seconds
|
||||
autosave_timer.wait_time = config.auto_save_seconds
|
||||
autosave_timer.start()
|
||||
else:
|
||||
autosave_timer.stop()
|
||||
@ -93,15 +113,16 @@ func check_autosave_options():
|
||||
if GlobalConfig.DEBUG:
|
||||
print(
|
||||
"check_autosave_option: ",
|
||||
GlobalConfigManager.config.auto_save_enabled,
|
||||
config.auto_save_enabled,
|
||||
" wait_time=",
|
||||
autosave_timer.wait_time
|
||||
)
|
||||
|
||||
|
||||
func _try_auto_save():
|
||||
func _try_auto_save() -> void:
|
||||
if GlobalConfig.DEBUG:
|
||||
print("Auto save")
|
||||
|
||||
if archive and GlobalConfigManager.config.auto_save_seconds > 1:
|
||||
save_all()
|
||||
# 自动保存成功 [ID:ui_auto_saved]
|
||||
@ -109,72 +130,109 @@ func _try_auto_save():
|
||||
|
||||
|
||||
func _check_dirs_and_archives() -> bool:
|
||||
if !DirAccess.dir_exists_absolute(user_root_dir):
|
||||
DirAccess.make_dir_recursive_absolute(user_root_dir)
|
||||
print("Create user_root_dir:", user_root_dir)
|
||||
# Ensure directories exist
|
||||
_ensure_directory_exists(user_data_root_dir)
|
||||
_ensure_directory_exists(user_archives_dir)
|
||||
|
||||
# Check if the archive directory is accessible
|
||||
if !DirAccess.dir_exists_absolute(archive_dir):
|
||||
DirAccess.make_dir_recursive_absolute(archive_dir)
|
||||
print("Create archive_dir:", archive_dir)
|
||||
var archive_dir_access = DirAccess.open(archive_dir)
|
||||
if !archive_dir_access:
|
||||
var archive_dir_access = DirAccess.open(user_archives_dir)
|
||||
if not archive_dir_access:
|
||||
_handle_load_error("存档目录", "读取")
|
||||
# TODO pop up a dialog to inform the user
|
||||
return false
|
||||
var files = archive_dir_access.get_files()
|
||||
files.sort()
|
||||
# get archive number
|
||||
for file in files:
|
||||
if file.begins_with(archive_prefix) and file.ends_with(GlobalConfig.RES_FILE_FORMAT):
|
||||
# format: save012_xxxxx; save000
|
||||
var id_and_note = file.get_basename().substr(archive_prefix.length()).strip_escapes().split("_", true, 1)
|
||||
var id_str = id_and_note[0]
|
||||
var note_str = id_and_note[1] if id_and_note.size() >= 2 else (archive_prefix + id_str)
|
||||
# 非三位数的 id 会被忽略
|
||||
if id_str.length() != 3:
|
||||
continue
|
||||
var id = int(id_str)
|
||||
# 读取范围是 0-99
|
||||
if id < 0 or id > 99:
|
||||
continue
|
||||
archives_notes_dict[id] = note_str
|
||||
var path = archive_dir + file
|
||||
if not archives_dict.has(id):
|
||||
var res = ResourceLoader.load(
|
||||
path, "AssembledArchive", ResourceLoader.CACHE_MODE_REPLACE_DEEP
|
||||
)
|
||||
if is_instance_valid(res) and res.version >= CURRENT_VERSION:
|
||||
archives_dict[id] = res
|
||||
else:
|
||||
printerr("SKIP INVALID ARCHIVE! file=", file)
|
||||
|
||||
# Load existing archives
|
||||
_load_existing_archives(archive_dir_access)
|
||||
return true
|
||||
|
||||
|
||||
func _ensure_directory_exists(dir_path: String) -> void:
|
||||
if not DirAccess.dir_exists_absolute(dir_path):
|
||||
DirAccess.make_dir_recursive_absolute(dir_path)
|
||||
print("Create directory:", dir_path)
|
||||
|
||||
|
||||
func _load_existing_archives(dir_access: DirAccess) -> void:
|
||||
var files = dir_access.get_files()
|
||||
files.sort()
|
||||
|
||||
for file in files:
|
||||
if not _is_valid_archive_filename(file):
|
||||
continue
|
||||
var archive_info = _parse_archive_filename(file)
|
||||
if not archive_info:
|
||||
continue
|
||||
var id = archive_info.id
|
||||
var note = archive_info.note
|
||||
archives_notes_dict[id] = note
|
||||
if not archives_dict.has(id):
|
||||
var archive_resource = _load_archive_resource(user_archives_dir + file)
|
||||
if archive_resource:
|
||||
archives_dict[id] = archive_resource
|
||||
|
||||
|
||||
func _is_valid_archive_filename(filename: String) -> bool:
|
||||
return filename.begins_with(archive_prefix) and filename.ends_with(GlobalConfig.RES_FILE_FORMAT)
|
||||
|
||||
|
||||
func _parse_archive_filename(filename: String) -> Dictionary:
|
||||
# format: save012_xxxxx; save000
|
||||
var basename = filename.get_basename()
|
||||
var id_and_note = basename.substr(archive_prefix.length()).strip_escapes().split("_", true, 1)
|
||||
var id_str = id_and_note[0]
|
||||
# 非三位数的 id 会被忽略
|
||||
if id_str.length() != ARCHIVE_ID_DIGITS:
|
||||
return {}
|
||||
var id = int(id_str)
|
||||
# 读取范围是 0-99
|
||||
if id < ARCHIVE_ID_MIN or id > ARCHIVE_ID_MAX:
|
||||
return {}
|
||||
var note_str = id_and_note[1] if id_and_note.size() >= 2 else (archive_prefix + id_str)
|
||||
return {
|
||||
"id": id,
|
||||
"note": note_str
|
||||
}
|
||||
|
||||
|
||||
func _load_archive_resource(path: String) -> AssembledArchive:
|
||||
var res = ResourceLoader.load(
|
||||
path, "AssembledArchive", ResourceLoader.CACHE_MODE_REPLACE_DEEP
|
||||
)
|
||||
if is_instance_valid(res) and res.version >= CURRENT_VERSION:
|
||||
return res
|
||||
else:
|
||||
printerr("SKIP INVALID ARCHIVE! path=", path)
|
||||
return null
|
||||
|
||||
|
||||
# id = -1 means create a new archive, otherwise create an archive with the given id
|
||||
func create_and_use_new_archive(id := -1) -> void:
|
||||
_check_dirs_and_archives()
|
||||
var archive_path = _get_archive_path(id)
|
||||
|
||||
if id < 0:
|
||||
# 如果 id 小于 0,找到一个新的 id,创建新存档
|
||||
id = 0
|
||||
# find a new id
|
||||
archive_path = _get_archive_path(id)
|
||||
while FileAccess.file_exists(archive_path):
|
||||
id += 1
|
||||
archive_path = _get_archive_path(id)
|
||||
id = _find_next_available_id()
|
||||
_create_and_save_new_archive_resoure(id)
|
||||
else:
|
||||
# 如果 id 大于等于 0,创建指定 id 的存档
|
||||
if FileAccess.file_exists(archive_path):
|
||||
_create_and_save_new_archive_resoure(id, true)
|
||||
else:
|
||||
_create_and_save_new_archive_resoure(id)
|
||||
var archive_path = get_archive_path(id)
|
||||
var take_over_path = FileAccess.file_exists(archive_path)
|
||||
_create_and_save_new_archive_resoure(id, take_over_path)
|
||||
|
||||
# this will auto trigger signal and load the new archive
|
||||
GlobalConfigManager.config.current_selected_archive_id = id
|
||||
|
||||
|
||||
func _create_and_save_new_archive_resoure(id, take_over_path = false) -> void:
|
||||
var archive_path = _get_archive_path(id)
|
||||
func _find_next_available_id() -> int:
|
||||
var id = 0
|
||||
var archive_path = get_archive_path(id)
|
||||
while FileAccess.file_exists(archive_path) and id <= ARCHIVE_ID_MAX:
|
||||
id += 1
|
||||
archive_path = get_archive_path(id)
|
||||
return id
|
||||
|
||||
|
||||
func _create_and_save_new_archive_resoure(id: int, take_over_path := false) -> void:
|
||||
var archive_path = get_archive_path(id)
|
||||
archive = AssembledArchive.new() as Resource
|
||||
archive.version = CURRENT_VERSION
|
||||
if take_over_path:
|
||||
@ -188,15 +246,9 @@ func _create_and_save_new_archive_resoure(id, take_over_path = false) -> void:
|
||||
|
||||
|
||||
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
|
||||
func _get_archive_path(id: int) -> String:
|
||||
var id_str := ""
|
||||
if id < 10:
|
||||
id_str = "00" + str(id)
|
||||
elif id < 100:
|
||||
id_str = "0" + str(id)
|
||||
else:
|
||||
id_str = str(id)
|
||||
return archive_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
|
||||
func get_archive_path(id: int) -> String:
|
||||
var id_str := str(id).pad_zeros(ARCHIVE_ID_DIGITS)
|
||||
return user_archives_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
|
||||
|
||||
|
||||
func allow_resume(id := 1) -> bool:
|
||||
@ -208,41 +260,54 @@ func save_all() -> void:
|
||||
var config = GlobalConfigManager.config
|
||||
if config:
|
||||
ResourceSaver.save(config)
|
||||
|
||||
# player_global_position
|
||||
var player = SceneManager.get_player() as MainPlayer
|
||||
# 在此处保存 player 的位置信息
|
||||
if archive and player:
|
||||
archive.player_global_position_x = player.global_position.x
|
||||
archive.player_direction = player.facing_direction
|
||||
# save player state
|
||||
_save_player_state()
|
||||
# save archive
|
||||
if archive:
|
||||
ResourceSaver.save(archive)
|
||||
# reset autosave timer
|
||||
check_autosave_options()
|
||||
|
||||
|
||||
func _save_player_state() -> void:
|
||||
if not archive:
|
||||
return
|
||||
var player = SceneManager.get_player() as MainPlayer
|
||||
if player:
|
||||
archive.player_global_position_x = player.global_position.x
|
||||
archive.player_direction = player.facing_direction
|
||||
|
||||
|
||||
func load_config() -> void:
|
||||
if GlobalConfigManager.config:
|
||||
return
|
||||
var path = user_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT
|
||||
var path = user_data_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT
|
||||
if FileAccess.file_exists(path):
|
||||
var config = ResourceLoader.load(path)
|
||||
if is_instance_valid(config) and config.version >= CURRENT_VERSION:
|
||||
GlobalConfigManager.config = config
|
||||
var loaded_config = ResourceLoader.load(path)
|
||||
if is_instance_valid(loaded_config) and loaded_config.version >= CURRENT_VERSION:
|
||||
GlobalConfigManager.config = loaded_config
|
||||
else:
|
||||
printerr("SKIP INVALID CONFIG!")
|
||||
if GlobalConfigManager.config == null:
|
||||
var config = GlobalConfig.new()
|
||||
config.version = CURRENT_VERSION
|
||||
GlobalConfigManager.config = config
|
||||
ResourceSaver.save(config, path)
|
||||
GlobalConfigManager.config.resource_path = path
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
# connect signals
|
||||
GlobalConfigManager.config.current_selected_archive_id_changed.connect(_on_archive_id_changed)
|
||||
GlobalConfigManager.config.auto_save_seconds_changed.connect(check_autosave_options)
|
||||
GlobalConfigManager.config.auto_save_enabled_changed.connect(check_autosave_options)
|
||||
|
||||
if not GlobalConfigManager.config:
|
||||
_create_default_config(path)
|
||||
if not Engine.is_editor_hint():
|
||||
_connect_config_signals()
|
||||
|
||||
|
||||
func _create_default_config(path: String) -> void:
|
||||
var config = GlobalConfig.new()
|
||||
config.version = CURRENT_VERSION
|
||||
config.resource_path = path
|
||||
GlobalConfigManager.config = config
|
||||
ResourceSaver.save(config, path)
|
||||
|
||||
|
||||
func _connect_config_signals() -> void:
|
||||
var config = GlobalConfigManager.config
|
||||
config.current_selected_archive_id_changed.connect(_on_archive_id_changed)
|
||||
config.auto_save_seconds_changed.connect(check_autosave_options)
|
||||
config.auto_save_enabled_changed.connect(check_autosave_options)
|
||||
|
||||
|
||||
func load_archive() -> void:
|
||||
@ -250,19 +315,16 @@ func load_archive() -> void:
|
||||
var selected_id = 0
|
||||
if GlobalConfigManager.config:
|
||||
selected_id = GlobalConfigManager.config.current_selected_archive_id
|
||||
# if archive and selected_id == archive.archive_id:
|
||||
# return
|
||||
|
||||
print("load_archive ", selected_id)
|
||||
if not archives_dict.has(selected_id):
|
||||
_handle_load_error(str(selected_id) + " 号存档", "查找")
|
||||
return
|
||||
archive = archives_dict[selected_id]
|
||||
# emit signal
|
||||
archive_loaded.emit()
|
||||
check_autosave_options()
|
||||
|
||||
|
||||
func _handle_load_error(target, action) -> void:
|
||||
func _handle_load_error(target: String, action: String) -> void:
|
||||
var msg = str(target) + " " + str(action) + " failed. Permission Error."
|
||||
SceneManager.pop_notification(msg)
|
||||
printerr(msg)
|
||||
@ -281,9 +343,7 @@ func get_global_value(property: StringName, default = null) -> Variant:
|
||||
printerr("Archive is null, cannot get global value")
|
||||
return default
|
||||
var val = archive.global_data_dict.get(property)
|
||||
if val == null:
|
||||
return default
|
||||
return val
|
||||
return default if val == null else val
|
||||
|
||||
|
||||
func set_chapter_if_greater(c: int) -> void:
|
||||
@ -291,7 +351,9 @@ func set_chapter_if_greater(c: int) -> void:
|
||||
printerr("Archive is null, cannot set chapter")
|
||||
return
|
||||
# 1:序章;2-5:一~四章;6:结尾
|
||||
if c < 1 or c > 6:
|
||||
const MIN_CHAPTER = 1
|
||||
const MAX_CHAPTER = 6
|
||||
if c < MIN_CHAPTER or c > MAX_CHAPTER:
|
||||
printerr("[ArchiveManager] set_chapter_if_greater: invalid chapter value: " + str(c))
|
||||
return
|
||||
if EventManager.get_chapter_stage() >= c:
|
||||
@ -302,14 +364,14 @@ func set_chapter_if_greater(c: int) -> void:
|
||||
|
||||
|
||||
func unlock_memory(id: int) -> void:
|
||||
if archive:
|
||||
if archive.mem_display_dict.get(id):
|
||||
print("memory already unlocked. id=", id)
|
||||
return
|
||||
archive.mem_display_dict[id] = true
|
||||
SceneManager.pop_notification("ui_notify_mem_update")
|
||||
else:
|
||||
if not archive:
|
||||
printerr("Archive is null, cannot unlock memory. id=", id)
|
||||
return
|
||||
if archive.mem_display_dict.get(id):
|
||||
print("memory already unlocked. id=", id)
|
||||
return
|
||||
archive.mem_display_dict[id] = true
|
||||
SceneManager.pop_notification("ui_notify_mem_update")
|
||||
|
||||
|
||||
# 供运行时缓存跨场景数据
|
||||
|
@ -11,8 +11,8 @@ class_name AssembledArchive extends Resource
|
||||
printerr("[AssembledArchive] current_scene is not valid: " + val)
|
||||
return
|
||||
# 尝试后台预先加载该场景
|
||||
if GroundLoader.GROUND_SCENE_PATH_DICT.has(val):
|
||||
var path = GroundLoader.GROUND_SCENE_PATH_DICT[val]
|
||||
var path = GroundLoader.get_ground_scene_uid(val)
|
||||
if path:
|
||||
if GlobalConfig.DEBUG:
|
||||
print("[AssembledArchive] current_scene: " + current_scene)
|
||||
ResourceLoader.load_threaded_request(path, "PackedScene")
|
||||
|
43
manager/archive_manager/debug_panel.gd
Normal file
@ -0,0 +1,43 @@
|
||||
extends CanvasLayer
|
||||
|
||||
@onready var quit_debug_button: Button = %QuitDebugModeButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
AudioManager.process_mode = Node.PROCESS_MODE_PAUSABLE
|
||||
SceneManager.toggle_pause_counter(true, "debugging")
|
||||
layer = GlobalConfig.CANVAS_LAYER_SETTINGS
|
||||
quit_debug_button.pressed.connect(_on_quit_debug_button_pressed)
|
||||
|
||||
# 恢复上次打开的 tab
|
||||
var tab = ArchiveManager.get_global_value(&"debug_panel_tab", 0)
|
||||
%TabContainer.current_tab = tab
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if (
|
||||
event.is_action_pressed("debugging")
|
||||
or event.is_action_pressed("cancel")
|
||||
or event.is_action_pressed("escape")
|
||||
):
|
||||
get_viewport().set_input_as_handled()
|
||||
quit()
|
||||
|
||||
|
||||
func _on_quit_debug_button_pressed() -> void:
|
||||
# 不写入配置
|
||||
GlobalConfig.DEBUG = false
|
||||
quit()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
AudioManager.process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
SceneManager.toggle_pause_counter(false, "debugging")
|
||||
|
||||
|
||||
func quit() -> void:
|
||||
queue_free()
|
||||
|
||||
|
||||
func _on_tab_container_tab_changed(tab: int) -> void:
|
||||
ArchiveManager.set_global_entry(&"debug_panel_tab", tab)
|
1
manager/archive_manager/debug_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://d3aitlnh5hrfc
|
60
manager/archive_manager/debug_panel.tscn
Normal file
@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://d4jeeteyq8kk3"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bo4f3sdubdp61" path="res://manager/archive_manager/savings_panel.tscn" id="1_fbybo"]
|
||||
[ext_resource type="Script" uid="uid://d3aitlnh5hrfc" path="res://manager/archive_manager/debug_panel.gd" id="1_k0pa6"]
|
||||
[ext_resource type="PackedScene" uid="uid://b6ogrp5ec5nr3" path="res://manager/archive_manager/sfx_config_panel.tscn" id="2_t7mby"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k0pa6"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="DebugLayer" type="CanvasLayer"]
|
||||
process_mode = 3
|
||||
scale = Vector2(0.5, 0.5)
|
||||
transform = Transform2D(0.5, 0, 0, 0.5, 0, 0)
|
||||
script = ExtResource("1_k0pa6")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
offset_left = 68.0
|
||||
offset_top = 28.0
|
||||
offset_right = 1068.0
|
||||
offset_bottom = 606.0
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_k0pa6")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "Debug 面板"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="QuitDebugModeButton" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "关闭 Debug 模式"
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="PanelContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(1000, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 0
|
||||
current_tab = 0
|
||||
|
||||
[node name="存档控制" parent="PanelContainer/VBoxContainer/TabContainer" instance=ExtResource("1_fbybo")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="音效控制" parent="PanelContainer/VBoxContainer/TabContainer" instance=ExtResource("2_t7mby")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[connection signal="tab_changed" from="PanelContainer/VBoxContainer/TabContainer" to="." method="_on_tab_container_tab_changed"]
|
282
manager/archive_manager/savings_panel.gd
Normal file
@ -0,0 +1,282 @@
|
||||
extends Control
|
||||
|
||||
# UI References
|
||||
@onready var archive_grid: GridContainer = %ArchiveGrid
|
||||
@onready var scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var current_archive_label: Label = %CurrentArchiveLabel
|
||||
@onready var name_input: LineEdit = %NameInput
|
||||
@onready var save_button: Button = %SaveButton
|
||||
@onready var refresh_button: Button = %RefreshButton
|
||||
# Constants
|
||||
const MAX_MANUAL_ARCHIVES = 99
|
||||
const GRID_COLUMNS = 4
|
||||
|
||||
# Variables
|
||||
var manual_archives: Dictionary = {} # {id: {name: String, path: String, time: String}}
|
||||
var next_available_id: int = 2 # Start from 2 since 1 is reserved for main archive
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Setup UI
|
||||
archive_grid.columns = GRID_COLUMNS
|
||||
# Connect signals
|
||||
save_button.pressed.connect(_on_save_button_pressed)
|
||||
refresh_button.pressed.connect(_refresh_archive_list)
|
||||
name_input.text_submitted.connect(_on_name_submitted)
|
||||
# Set default name
|
||||
_update_default_name()
|
||||
# Initial load
|
||||
_refresh_archive_list()
|
||||
_update_current_archive_label()
|
||||
|
||||
|
||||
func _update_current_archive_label() -> void:
|
||||
current_archive_label.text = "当前使用存档:1号存档(主存档)"
|
||||
|
||||
|
||||
func _update_default_name() -> void:
|
||||
name_input.placeholder_text = "输入存档名称"
|
||||
var chapter_name = EventManager.get_chapter_stage()
|
||||
if chapter_name == 1:
|
||||
chapter_name = "序章"
|
||||
elif chapter_name <= 5:
|
||||
chapter_name = "第%s章" % (chapter_name - 1)
|
||||
elif chapter_name == 6:
|
||||
chapter_name = "结尾"
|
||||
else:
|
||||
chapter_name = "未知"
|
||||
var scene_name = SceneManager.get_current_scene_name()
|
||||
var saving_name = chapter_name + "_" + scene_name
|
||||
name_input.text = _get_unique_archive_name(saving_name)
|
||||
|
||||
|
||||
func _on_name_submitted(_text: String) -> void:
|
||||
_on_save_button_pressed()
|
||||
|
||||
|
||||
func _on_save_button_pressed() -> void:
|
||||
# Check limit
|
||||
if manual_archives.size() >= MAX_MANUAL_ARCHIVES:
|
||||
_show_notification("已达到最大存档数量限制(99个)")
|
||||
return
|
||||
# Get and validate name
|
||||
var archive_name = name_input.text.strip_edges()
|
||||
if archive_name.is_empty():
|
||||
archive_name = "未命名存档_" + Time.get_datetime_string_from_system()
|
||||
# Save current progress
|
||||
ArchiveManager.save_all()
|
||||
# Get unique name
|
||||
archive_name = _get_unique_archive_name(archive_name)
|
||||
# Copy current archive
|
||||
_copy_current_archive(archive_name)
|
||||
# Reset input field
|
||||
_update_default_name()
|
||||
name_input.select_all()
|
||||
|
||||
|
||||
func _get_unique_archive_name(base_name: String) -> String:
|
||||
var final_name = base_name
|
||||
var counter = 1
|
||||
|
||||
# Check if name already exists
|
||||
var name_exists = true
|
||||
while name_exists:
|
||||
name_exists = false
|
||||
for data in manual_archives.values():
|
||||
if data.name == final_name:
|
||||
name_exists = true
|
||||
final_name = base_name + "_" + str(counter)
|
||||
counter += 1
|
||||
break
|
||||
|
||||
return final_name
|
||||
|
||||
|
||||
func _copy_current_archive(archive_name: String) -> void:
|
||||
# Get current archive path
|
||||
var current_archive = ArchiveManager.archive
|
||||
if not current_archive:
|
||||
_show_notification("当前没有活动存档")
|
||||
return
|
||||
|
||||
# Find next available ID
|
||||
while manual_archives.has(next_available_id) and next_available_id <= MAX_MANUAL_ARCHIVES + 1:
|
||||
next_available_id += 1
|
||||
|
||||
if next_available_id > MAX_MANUAL_ARCHIVES + 1:
|
||||
_show_notification("无法创建更多存档")
|
||||
return
|
||||
|
||||
# Create new archive path
|
||||
var new_archive_path = (
|
||||
ArchiveManager.user_archives_dir
|
||||
+ "manual_"
|
||||
+ str(next_available_id)
|
||||
+ "_"
|
||||
+ archive_name.validate_filename()
|
||||
+ GlobalConfig.RES_FILE_FORMAT
|
||||
)
|
||||
|
||||
# Copy the archive file
|
||||
var source_path = current_archive.resource_path
|
||||
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
|
||||
if dir:
|
||||
var error = dir.copy(source_path, new_archive_path)
|
||||
print("Copying archive from: ", source_path, " to: ", new_archive_path)
|
||||
if error == OK:
|
||||
# Save manual archive info
|
||||
manual_archives[next_available_id] = {
|
||||
"name": archive_name,
|
||||
"path": new_archive_path,
|
||||
"time": Time.get_datetime_string_from_system(false, true)
|
||||
}
|
||||
# Save manual archives data
|
||||
_save_manual_archives_data()
|
||||
# Refresh UI
|
||||
_refresh_archive_list()
|
||||
_show_notification("存档已保存:" + archive_name)
|
||||
next_available_id += 1
|
||||
else:
|
||||
_show_notification("存档复制失败:" + error_string(error))
|
||||
else:
|
||||
_show_notification("无法访问存档目录")
|
||||
|
||||
|
||||
func _refresh_archive_list() -> void:
|
||||
# refresh debugging name
|
||||
_update_default_name()
|
||||
|
||||
# Clear existing items
|
||||
for child in archive_grid.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# Load manual archives data
|
||||
_load_manual_archives_data()
|
||||
|
||||
# Create UI items for each manual archive
|
||||
var sorted_ids = manual_archives.keys()
|
||||
sorted_ids.sort()
|
||||
|
||||
for id in sorted_ids:
|
||||
var data = manual_archives[id]
|
||||
_create_archive_item(id, data)
|
||||
|
||||
|
||||
func _create_archive_item(id: int, data: Dictionary) -> void:
|
||||
# Create container for the archive item
|
||||
var item_container = PanelContainer.new()
|
||||
item_container.custom_minimum_size = Vector2(200, 60)
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 4)
|
||||
item_container.add_child(vbox)
|
||||
|
||||
# Archive name (editable)
|
||||
var name_edit = LineEdit.new()
|
||||
name_edit.text = data.name
|
||||
name_edit.tooltip_text = "创建时间:" + data.time
|
||||
vbox.add_child(name_edit)
|
||||
|
||||
# Time label
|
||||
var time_label = Label.new()
|
||||
time_label.text = data.time
|
||||
time_label.add_theme_font_size_override("font_size", 12)
|
||||
time_label.modulate.a = 0.7
|
||||
vbox.add_child(time_label)
|
||||
|
||||
# Action buttons container
|
||||
var button_container = HBoxContainer.new()
|
||||
button_container.add_theme_constant_override("separation", 4)
|
||||
|
||||
# Load button
|
||||
var load_btn = Button.new()
|
||||
load_btn.text = "加载"
|
||||
load_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
button_container.add_child(load_btn)
|
||||
|
||||
# Delete button
|
||||
var delete_btn = Button.new()
|
||||
delete_btn.text = "删除"
|
||||
delete_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
button_container.add_child(delete_btn)
|
||||
|
||||
vbox.add_child(button_container)
|
||||
# Connect signals
|
||||
name_edit.text_changed.connect(func(new_text): _on_archive_renamed(id, new_text))
|
||||
load_btn.pressed.connect(func(): _load_manual_archive(id, data))
|
||||
delete_btn.pressed.connect(func(): _delete_manual_archive(id))
|
||||
archive_grid.add_child(item_container)
|
||||
|
||||
|
||||
func _on_archive_renamed(id: int, new_name: String) -> void:
|
||||
if new_name.strip_edges().is_empty():
|
||||
return
|
||||
# Update name in data
|
||||
manual_archives[id].name = new_name.strip_edges()
|
||||
# Save changes
|
||||
_save_manual_archives_data()
|
||||
|
||||
|
||||
func _load_manual_archive(_id: int, data: Dictionary) -> void:
|
||||
# Save current state first
|
||||
ArchiveManager.save_all()
|
||||
# Copy manual archive to archive 1
|
||||
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
|
||||
if dir:
|
||||
var saving_archive = load(data.path)
|
||||
if saving_archive:
|
||||
var target_path = ArchiveManager.get_archive_path(1)
|
||||
print("Loading archive from: ", data.path, " to: ", target_path)
|
||||
GlobalConfigManager.config.current_selected_archive_id = 1
|
||||
if ArchiveManager.archive:
|
||||
saving_archive.take_over_path(target_path)
|
||||
else:
|
||||
saving_archive.resource_path = target_path
|
||||
ArchiveManager.archives_dict[1] = saving_archive
|
||||
ArchiveManager.archive = saving_archive
|
||||
print("Loading archive from: ", data.path, " to: ", target_path)
|
||||
# Reload current scene
|
||||
SceneManager.enter_main_scene()
|
||||
else:
|
||||
_show_notification("加载存档失败:" + data.name)
|
||||
|
||||
|
||||
func _delete_manual_archive(id: int) -> void:
|
||||
var data = manual_archives[id]
|
||||
# Delete file
|
||||
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
|
||||
if dir:
|
||||
dir.remove(data.path)
|
||||
# Remove from dictionary
|
||||
manual_archives.erase(id)
|
||||
# Save changes
|
||||
_save_manual_archives_data()
|
||||
# Refresh UI
|
||||
_refresh_archive_list()
|
||||
_show_notification("已删除存档:" + data.name)
|
||||
|
||||
|
||||
func _save_manual_archives_data() -> void:
|
||||
var save_path = ArchiveManager.user_data_root_dir + "test_manual_archives.dat"
|
||||
var file = FileAccess.open(save_path, FileAccess.WRITE)
|
||||
if file:
|
||||
file.store_var(manual_archives)
|
||||
file.close()
|
||||
|
||||
|
||||
func _load_manual_archives_data() -> void:
|
||||
var save_path = ArchiveManager.user_data_root_dir + "test_manual_archives.dat"
|
||||
if FileAccess.file_exists(save_path):
|
||||
var file = FileAccess.open(save_path, FileAccess.READ)
|
||||
if file:
|
||||
manual_archives = file.get_var()
|
||||
file.close()
|
||||
# Find next available ID
|
||||
next_available_id = 2
|
||||
for id in manual_archives.keys():
|
||||
if id >= next_available_id:
|
||||
next_available_id = id + 1
|
||||
|
||||
|
||||
func _show_notification(message: String) -> void:
|
||||
SceneManager.pop_notification(message)
|
1
manager/archive_manager/savings_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cirf1nw72l315
|
83
manager/archive_manager/savings_panel.tscn
Normal file
@ -0,0 +1,83 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bo4f3sdubdp61"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cirf1nw72l315" path="res://manager/archive_manager/savings_panel.gd" id="1_oo2ip"]
|
||||
|
||||
[node name="存档控制" type="PanelContainer"]
|
||||
offset_right = 405.0
|
||||
offset_bottom = 531.0
|
||||
mouse_filter = 1
|
||||
script = ExtResource("1_oo2ip")
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "存档测试管理器(测试专用)"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="存档管理" type="VBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="CurrentArchiveLabel" type="Label" parent="VBoxContainer/存档管理"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.7, 0.9, 0.7, 1)
|
||||
text = "当前使用存档:1号存档(主存档)"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/存档管理"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SaveContainer" type="HBoxContainer" parent="VBoxContainer/存档管理"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="NameInput" type="LineEdit" parent="VBoxContainer/存档管理/SaveContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "输入存档名称"
|
||||
|
||||
[node name="SaveButton" type="Button" parent="VBoxContainer/存档管理/SaveContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(120, 0)
|
||||
layout_mode = 2
|
||||
text = "保存当前进度"
|
||||
|
||||
[node name="RefreshButton" type="Button" parent="VBoxContainer/存档管理/SaveContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "刷新"
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer/存档管理"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/存档管理"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.8, 0.8, 0.8, 1)
|
||||
text = "手动存档列表:"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/存档管理"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 380)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
vertical_scroll_mode = 2
|
||||
|
||||
[node name="ArchiveGrid" type="GridContainer" parent="VBoxContainer/存档管理/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 10
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 4
|
526
manager/archive_manager/sfx_config_panel.gd
Normal file
@ -0,0 +1,526 @@
|
||||
class_name SfxConfigPanel
|
||||
extends Control
|
||||
|
||||
# signal config_changed(node_name: String, property: String, value)
|
||||
|
||||
# 面板相关
|
||||
@onready var scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var vbox_container: VBoxContainer = %VBoxContainer
|
||||
@onready var reset_button: Button = %ResetButton
|
||||
@onready var import_button: Button = %ImportButton
|
||||
@onready var export_button: Button = %ExportButton
|
||||
# 音频预览播放器
|
||||
@onready var preview_player: AudioStreamPlayer = %AudioStreamPlayer
|
||||
# 文件对话框
|
||||
@onready var file_dialog: FileDialog = %FileDialog
|
||||
|
||||
# 数据存储
|
||||
var sfx_nodes: Array[Node] = []
|
||||
var config_data: Dictionary = {}
|
||||
var current_scene_name: String = ""
|
||||
|
||||
|
||||
func _ready():
|
||||
file_dialog.file_selected.connect(_on_file_selected)
|
||||
reset_button.pressed.connect(_on_reset_pressed)
|
||||
import_button.pressed.connect(_on_import_pressed)
|
||||
export_button.pressed.connect(_on_export_pressed)
|
||||
# 初始加载
|
||||
refresh_sfx_list(SceneManager.get_ground())
|
||||
|
||||
|
||||
# 1. 检测 current_scene 下 Sfx 节点
|
||||
func refresh_sfx_list(ground: Ground2D, headless := false):
|
||||
sfx_nodes.clear()
|
||||
current_scene_name = GroundLoader.get_ground_scene_readable_name(ground.scene_name)
|
||||
find_sfx_nodes(ground)
|
||||
load_config()
|
||||
if not headless:
|
||||
refresh_ui()
|
||||
|
||||
var ignore_class_list = ["Portal2D", "Interactable2D", "Note2D", "Inspectable2D", "Pickable2D", "Npc2D", "Ambush2D"]
|
||||
|
||||
func find_sfx_nodes(node: Node):
|
||||
var script = node.get_script()
|
||||
if script:
|
||||
if script.get_global_name() in ignore_class_list:
|
||||
return
|
||||
# if node is Sfx or node is VibeSfx:
|
||||
if node is Sfx:
|
||||
if node.has_method("play") and node.get("volume_db") != null and node.get("stream") != null:
|
||||
sfx_nodes.append(node)
|
||||
# 递归检查子节点
|
||||
for child in node.get_children():
|
||||
find_sfx_nodes(child)
|
||||
|
||||
func clear_ui():
|
||||
for child in vbox_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# 2. 在面板生成对应条目,双向绑定
|
||||
func create_ui_items():
|
||||
for sfx_node in sfx_nodes:
|
||||
create_sfx_item(sfx_node)
|
||||
|
||||
func refresh_ui():
|
||||
clear_ui()
|
||||
create_ui_items()
|
||||
|
||||
|
||||
func _build_sfx_name(sfx: Sfx) -> String:
|
||||
var parent_name = ""
|
||||
var parent = sfx.get_parent()
|
||||
while not parent_name and parent:
|
||||
if parent and parent.get_script() and parent.get_script().get_global_name() in ignore_class_list:
|
||||
parent_name = parent.name
|
||||
else:
|
||||
parent = parent.get_parent()
|
||||
if parent_name:
|
||||
return parent_name + " > " + sfx.name
|
||||
else:
|
||||
return sfx.name
|
||||
|
||||
|
||||
func create_sfx_item(sfx_node: Node):
|
||||
var item_container = VBoxContainer.new()
|
||||
item_container.add_theme_constant_override("separation", 8)
|
||||
vbox_container.add_child(item_container)
|
||||
|
||||
# 节点名称标签
|
||||
var name_label = Label.new()
|
||||
name_label.text = _build_sfx_name(sfx_node)
|
||||
name_label.add_theme_font_size_override("font_size", 14)
|
||||
item_container.add_child(name_label)
|
||||
|
||||
var controls_hbox = HBoxContainer.new()
|
||||
controls_hbox.add_theme_constant_override("separation", 10)
|
||||
item_container.add_child(controls_hbox)
|
||||
|
||||
# 音量滑块
|
||||
var volume_vbox = VBoxContainer.new()
|
||||
controls_hbox.add_child(volume_vbox)
|
||||
|
||||
|
||||
var volume_title_hbox = HBoxContainer.new()
|
||||
volume_vbox.add_child(volume_title_hbox)
|
||||
|
||||
var volume_label = Label.new()
|
||||
volume_label.text = "音量 (dB)"
|
||||
volume_title_hbox.add_child(volume_label)
|
||||
|
||||
# 重置音量按钮
|
||||
var reset_volumn_button = Button.new()
|
||||
reset_volumn_button.text = "重置音量"
|
||||
reset_volumn_button.pressed.connect(reset_volumn.bind(sfx_node))
|
||||
volume_title_hbox.add_child(reset_volumn_button)
|
||||
|
||||
var volume_slider_hbox = HBoxContainer.new()
|
||||
volume_vbox.add_child(volume_slider_hbox)
|
||||
|
||||
var volume_slider = HSlider.new()
|
||||
volume_slider.min_value = -60.0
|
||||
volume_slider.max_value = 20.0
|
||||
volume_slider.step = 0.1
|
||||
volume_slider.value = sfx_node.volume_db
|
||||
volume_slider.custom_minimum_size.x = 200
|
||||
volume_slider_hbox.add_child(volume_slider)
|
||||
|
||||
var volume_value_label = Label.new()
|
||||
volume_value_label.text = str(snapped(sfx_node.volume_db, 0.1))
|
||||
volume_value_label.custom_minimum_size.x = 50
|
||||
volume_slider_hbox.add_child(volume_value_label)
|
||||
|
||||
# 双向绑定音量
|
||||
volume_slider.value_changed.connect(func(value):
|
||||
sfx_node.volume_db = value
|
||||
volume_value_label.text = str(snapped(value, 0.1))
|
||||
save_node_config(sfx_node.get_path(), "volume_db", value)
|
||||
)
|
||||
|
||||
# Stream 信息和控制
|
||||
var stream_vbox = VBoxContainer.new()
|
||||
controls_hbox.add_child(stream_vbox)
|
||||
|
||||
var stream_label = Label.new()
|
||||
stream_label.text = "音频文件"
|
||||
stream_vbox.add_child(stream_label)
|
||||
|
||||
var stream_hbox = HBoxContainer.new()
|
||||
stream_vbox.add_child(stream_hbox)
|
||||
|
||||
var stream_name_label = Label.new()
|
||||
stream_name_label.custom_minimum_size.x = 150
|
||||
_set_stream_name_label(sfx_node, stream_name_label)
|
||||
stream_hbox.add_child(stream_name_label)
|
||||
|
||||
|
||||
# 预览播放按钮
|
||||
var preview_button = Button.new()
|
||||
preview_button.text = "▶"
|
||||
preview_button.custom_minimum_size = Vector2(30, 30)
|
||||
preview_button.pressed.connect(preview_audio.bind(sfx_node))
|
||||
stream_hbox.add_child(preview_button)
|
||||
|
||||
# 恢复默认按钮
|
||||
if sfx_node.stream_was_replaced():
|
||||
var reset_btn = Button.new()
|
||||
reset_btn.text = "恢复默认音频"
|
||||
reset_btn.custom_minimum_size = Vector2(30, 30)
|
||||
reset_btn.pressed.connect(reset_audio.bind(sfx_node))
|
||||
stream_hbox.add_child(reset_btn)
|
||||
|
||||
# 3. 上传文件按钮
|
||||
var upload_button = Button.new()
|
||||
upload_button.text = "上传音频"
|
||||
upload_button.pressed.connect(open_file_dialog.bind(sfx_node, stream_name_label))
|
||||
stream_hbox.add_child(upload_button)
|
||||
|
||||
# 分割线
|
||||
var separator = HSeparator.new()
|
||||
item_container.add_child(separator)
|
||||
|
||||
# 存储节点引用用于后续操作
|
||||
item_container.set_meta("sfx_node", sfx_node)
|
||||
item_container.set_meta("stream_label", stream_name_label)
|
||||
|
||||
|
||||
func _set_stream_name_label(sfx_node:Sfx, stream_name_label:Label) -> void:
|
||||
if sfx_node.stream:
|
||||
stream_name_label.text = sfx_node.stream.resource_path.get_file()
|
||||
else:
|
||||
stream_name_label.text = "无音频文件"
|
||||
|
||||
|
||||
func reset_volumn(sfx_node: Sfx):
|
||||
sfx_node.volume_db = sfx_node.default_db
|
||||
save_config()
|
||||
refresh_ui()
|
||||
|
||||
|
||||
func preview_audio(sfx_node: Sfx):
|
||||
if sfx_node.stream:
|
||||
preview_player.stream = sfx_node.stream
|
||||
preview_player.volume_db = sfx_node.volume_db
|
||||
preview_player.play()
|
||||
|
||||
|
||||
func reset_audio(sfx_node: Sfx):
|
||||
sfx_node.reset_original_stream()
|
||||
config_data.erase(sfx_node.get_path())
|
||||
save_config()
|
||||
refresh_ui()
|
||||
|
||||
|
||||
var current_upload_node: Node
|
||||
var current_stream_label: Label
|
||||
|
||||
func open_file_dialog(sfx_node: Node, stream_label: Label):
|
||||
current_upload_node = sfx_node
|
||||
current_stream_label = stream_label
|
||||
file_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_file_selected(path: String):
|
||||
if not current_upload_node:
|
||||
return
|
||||
copy_and_load_audio_file(path, current_upload_node, current_stream_label)
|
||||
|
||||
|
||||
# 3. 复制文件并加载
|
||||
func copy_and_load_audio_file(source_path: String, sfx_node: Node, stream_label: Label):
|
||||
var file_name = source_path.get_file()
|
||||
var audio_dir = "user://audio/" + current_scene_name + "/"
|
||||
var target_path = audio_dir + file_name
|
||||
# 确保目录存在
|
||||
DirAccess.open("user://").make_dir_recursive("audio/" + current_scene_name)
|
||||
# 复制文件
|
||||
var source_file = FileAccess.open(source_path, FileAccess.READ)
|
||||
if not source_file:
|
||||
push_error("无法读取源文件: " + source_path)
|
||||
return
|
||||
var target_file = FileAccess.open(target_path, FileAccess.WRITE)
|
||||
if not target_file:
|
||||
push_error("无法创建目标文件: " + target_path)
|
||||
source_file.close()
|
||||
return
|
||||
target_file.store_buffer(source_file.get_buffer(source_file.get_length()))
|
||||
source_file.close()
|
||||
target_file.close()
|
||||
# 加载新的音频流
|
||||
var new_stream = AudioLoader.new().loadfile(target_path)
|
||||
if new_stream:
|
||||
sfx_node.replace_stream(new_stream)
|
||||
stream_label.text = file_name
|
||||
save_node_config(sfx_node.get_path(), "stream", target_path)
|
||||
print("音频文件已更新: ", sfx_node.name, " -> ", file_name)
|
||||
else:
|
||||
push_error("无法加载音频文件: " + target_path)
|
||||
refresh_ui()
|
||||
|
||||
|
||||
# 4. 配置保存与加载
|
||||
func save_node_config(node_path: String, property: String, value):
|
||||
if not config_data.has(node_path):
|
||||
config_data[node_path] = {}
|
||||
config_data[node_path][property] = value
|
||||
save_config()
|
||||
|
||||
func save_config():
|
||||
var config_dir = "user://audio/" + current_scene_name + "/"
|
||||
DirAccess.open("user://").make_dir_recursive("audio/" + current_scene_name)
|
||||
|
||||
var config_path = config_dir + "audio_config.dat"
|
||||
var file = FileAccess.open(config_path, FileAccess.WRITE)
|
||||
if file:
|
||||
file.store_string(var_to_str(config_data))
|
||||
file.close()
|
||||
|
||||
func load_config():
|
||||
var config_path = "user://audio/" + current_scene_name + "/audio_config.dat"
|
||||
var file = FileAccess.open(config_path, FileAccess.READ)
|
||||
if file:
|
||||
var config_str = file.get_as_text()
|
||||
file.close()
|
||||
config_data = str_to_var(config_str)
|
||||
if not config_data:
|
||||
config_data = {}
|
||||
apply_config()
|
||||
else:
|
||||
config_data = {}
|
||||
|
||||
|
||||
func apply_config():
|
||||
for sfx_node in sfx_nodes:
|
||||
var sfx_path = sfx_node.get_path()
|
||||
if config_data.has(sfx_path):
|
||||
var node_config = config_data[sfx_path]
|
||||
if node_config.has("volume_db"):
|
||||
sfx_node.volume_db = node_config["volume_db"]
|
||||
if node_config.has("stream"):
|
||||
var stream_path = node_config["stream"]
|
||||
if FileAccess.file_exists(stream_path):
|
||||
var new_stream = AudioLoader.new().loadfile(stream_path)
|
||||
# var new_stream = load(stream_path)
|
||||
if new_stream:
|
||||
sfx_node.replace_stream(new_stream)
|
||||
|
||||
|
||||
func _on_reset_pressed() -> void:
|
||||
for sfx_node in sfx_nodes:
|
||||
sfx_node.reset_original_stream()
|
||||
var config_file_path = "user://audio/" + current_scene_name + "/" + "/audio_config.dat"
|
||||
if FileAccess.file_exists(config_file_path):
|
||||
DirAccess.remove_absolute(config_file_path)
|
||||
SceneManager.enter_main_scene()
|
||||
|
||||
|
||||
# 5. Import/Export 功能
|
||||
func _on_import_pressed():
|
||||
# 打开文件对话框选择导入文件夹或配置文件
|
||||
var import_dialog = FileDialog.new()
|
||||
import_dialog.size = Vector2(1400, 800)
|
||||
import_dialog.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
|
||||
import_dialog.content_scale_factor = 3
|
||||
import_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
|
||||
import_dialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
import_dialog.dir_selected.connect(_on_import_folder_selected)
|
||||
add_child(import_dialog)
|
||||
import_dialog.popup_centered()
|
||||
|
||||
func _on_import_folder_selected(folder_path: String):
|
||||
var config_file_path = folder_path + "/audio_config.dat"
|
||||
if not FileAccess.file_exists(config_file_path):
|
||||
push_error("所选文件夹中没有找到 audio_config.dat 配置文件")
|
||||
return
|
||||
# 确保目标目录存在
|
||||
var target_dir = "user://audio/" + current_scene_name + "/"
|
||||
DirAccess.open("user://").make_dir_recursive("audio/" + current_scene_name)
|
||||
# 复制配置文件
|
||||
copy_file(config_file_path, target_dir + "audio_config.dat")
|
||||
# 复制所有音频文件
|
||||
var dir = DirAccess.open(folder_path)
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir():
|
||||
var extension = file_name.get_extension().to_lower()
|
||||
if extension in ["wav", "ogg", "mp3"]:
|
||||
var source_path = folder_path + "/" + file_name
|
||||
var target_path = target_dir + file_name
|
||||
copy_file(source_path, target_path)
|
||||
file_name = dir.get_next()
|
||||
dir.list_dir_end()
|
||||
# 重新加载配置
|
||||
refresh_sfx_list(SceneManager.get_ground())
|
||||
print("配置和音频文件已导入")
|
||||
|
||||
func copy_file(source_path: String, target_path: String) -> bool:
|
||||
var source_file = FileAccess.open(source_path, FileAccess.READ)
|
||||
if not source_file:
|
||||
push_error("无法读取文件: " + source_path)
|
||||
return false
|
||||
var target_file = FileAccess.open(target_path, FileAccess.WRITE)
|
||||
if not target_file:
|
||||
push_error("无法创建文件: " + target_path)
|
||||
source_file.close()
|
||||
return false
|
||||
target_file.store_buffer(source_file.get_buffer(source_file.get_length()))
|
||||
source_file.close()
|
||||
target_file.close()
|
||||
return true
|
||||
|
||||
func _on_export_pressed():
|
||||
var desktop_path = OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP)
|
||||
var export_folder = desktop_path + "/" + current_scene_name + "_audio_export"
|
||||
var config_path = "user://audio/" + current_scene_name + "/audio_config.dat"
|
||||
|
||||
# 创建导出文件夹
|
||||
var dir = DirAccess.open(desktop_path)
|
||||
if not dir:
|
||||
push_error("无法访问桌面目录")
|
||||
return
|
||||
|
||||
# 如果文件夹已存在,先删除
|
||||
if dir.dir_exists(export_folder):
|
||||
remove_directory_recursive(export_folder)
|
||||
|
||||
dir.make_dir(current_scene_name + "_audio_export")
|
||||
|
||||
var exported_files = []
|
||||
var failed_files = []
|
||||
|
||||
# 1. 导出配置文件
|
||||
if FileAccess.file_exists(config_path):
|
||||
var source_file = FileAccess.open(config_path, FileAccess.READ)
|
||||
var target_file = FileAccess.open(export_folder + "/audio_config.dat", FileAccess.WRITE)
|
||||
|
||||
if source_file and target_file:
|
||||
target_file.store_buffer(source_file.get_buffer(source_file.get_length()))
|
||||
source_file.close()
|
||||
target_file.close()
|
||||
exported_files.append("audio_config.dat")
|
||||
else:
|
||||
failed_files.append("audio_config.dat")
|
||||
push_error("配置文件导出失败")
|
||||
else:
|
||||
push_error("没有找到配置文件")
|
||||
return
|
||||
|
||||
# 2. 导出所有关联的音频文件
|
||||
var audio_dir = "user://audio/" + current_scene_name + "/"
|
||||
|
||||
# 从配置数据中收集所有音频文件路径
|
||||
var audio_files_to_export = []
|
||||
|
||||
# 收集配置文件中引用的音频文件
|
||||
for node_name in config_data.keys():
|
||||
var node_config = config_data[node_name]
|
||||
if node_config.has("stream"):
|
||||
var stream_path = node_config["stream"]
|
||||
if stream_path.begins_with("user://audio/" + current_scene_name + "/"):
|
||||
audio_files_to_export.append(stream_path)
|
||||
|
||||
# 收集当前目录下的所有音频文件
|
||||
var audio_dir_access = DirAccess.open(audio_dir)
|
||||
if audio_dir_access:
|
||||
audio_dir_access.list_dir_begin()
|
||||
var file_name = audio_dir_access.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if not audio_dir_access.current_is_dir():
|
||||
var full_path = audio_dir + file_name
|
||||
var extension = file_name.get_extension().to_lower()
|
||||
|
||||
# 检查是否是音频文件
|
||||
if extension in ["wav", "ogg", "mp3"]:
|
||||
if not full_path in audio_files_to_export:
|
||||
audio_files_to_export.append(full_path)
|
||||
|
||||
file_name = audio_dir_access.get_next()
|
||||
audio_dir_access.list_dir_end()
|
||||
|
||||
# 导出音频文件
|
||||
for audio_path in audio_files_to_export:
|
||||
if FileAccess.file_exists(audio_path):
|
||||
var file_name = audio_path.get_file()
|
||||
var source_file = FileAccess.open(audio_path, FileAccess.READ)
|
||||
var target_file = FileAccess.open(export_folder + "/" + file_name, FileAccess.WRITE)
|
||||
|
||||
if source_file and target_file:
|
||||
target_file.store_buffer(source_file.get_buffer(source_file.get_length()))
|
||||
source_file.close()
|
||||
target_file.close()
|
||||
exported_files.append(file_name)
|
||||
else:
|
||||
failed_files.append(file_name)
|
||||
if source_file:
|
||||
source_file.close()
|
||||
if target_file:
|
||||
target_file.close()
|
||||
else:
|
||||
failed_files.append(audio_path.get_file() + " (文件不存在)")
|
||||
|
||||
# 3. 创建导出说明文件
|
||||
create_export_readme(export_folder, exported_files, failed_files)
|
||||
|
||||
# 显示导出结果
|
||||
var message = "导出完成!\n导出位置: " + export_folder + "\n"
|
||||
message += "成功导出 " + str(exported_files.size()) + " 个文件"
|
||||
|
||||
if failed_files.size() > 0:
|
||||
message += "\n失败 " + str(failed_files.size()) + " 个文件: " + str(failed_files)
|
||||
|
||||
print(message)
|
||||
|
||||
# 可选:打开导出文件夹
|
||||
OS.shell_open(export_folder)
|
||||
|
||||
func create_export_readme(export_folder: String, exported_files: Array, failed_files: Array):
|
||||
var readme_path = export_folder + "/README.txt"
|
||||
var readme_file = FileAccess.open(readme_path, FileAccess.WRITE)
|
||||
|
||||
if readme_file:
|
||||
var content = "音效配置导出说明\n"
|
||||
content += "===================\n\n"
|
||||
content += "场景名称: " + current_scene_name + "\n"
|
||||
content += "导出时间: " + Time.get_datetime_string_from_system() + "\n\n"
|
||||
|
||||
content += "导出文件清单:\n"
|
||||
content += "-----------------\n"
|
||||
for file_name in exported_files:
|
||||
content += "✓ " + file_name + "\n"
|
||||
|
||||
if failed_files.size() > 0:
|
||||
content += "\n失败文件:\n"
|
||||
content += "-----------------\n"
|
||||
for file_name in failed_files:
|
||||
content += "✗ " + file_name + "\n"
|
||||
|
||||
content += "\n使用说明:\n"
|
||||
content += "-----------------\n"
|
||||
content += "1. audio_config.dat 是配置文件,包含音量和文件关联信息\n"
|
||||
content += "2. 其他 .wav/.ogg/.mp3 文件是对应的音频资源\n"
|
||||
content += "3. 导入时请将所有文件放在同一目录下\n"
|
||||
|
||||
readme_file.store_string(content)
|
||||
readme_file.close()
|
||||
|
||||
func remove_directory_recursive(path: String):
|
||||
var dir = DirAccess.open(path)
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
var full_path = path + "/" + file_name
|
||||
|
||||
if dir.current_is_dir():
|
||||
remove_directory_recursive(full_path)
|
||||
else:
|
||||
dir.remove(file_name)
|
||||
|
||||
file_name = dir.get_next()
|
||||
|
||||
dir.list_dir_end()
|
||||
dir.remove(path)
|
1
manager/archive_manager/sfx_config_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dtwvx0vs0uoun
|
68
manager/archive_manager/sfx_config_panel.tscn
Normal file
@ -0,0 +1,68 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b6ogrp5ec5nr3"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dtwvx0vs0uoun" path="res://manager/archive_manager/sfx_config_panel.gd" id="1_386ty"]
|
||||
|
||||
[node name="SfxConfigPanel" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(500, 0)
|
||||
offset_right = 508.0
|
||||
offset_bottom = 335.0
|
||||
mouse_filter = 1
|
||||
script = ExtResource("1_386ty")
|
||||
|
||||
[node name="MainVBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HeaderHBox" type="HBoxContainer" parent="MainVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MainVBox/HeaderHBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "音效配置面板"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ResetButton" type="Button" parent="MainVBox/HeaderHBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
text = "重置所有配置"
|
||||
|
||||
[node name="ImportButton" type="Button" parent="MainVBox/HeaderHBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
text = "导入配置"
|
||||
|
||||
[node name="ExportButton" type="Button" parent="MainVBox/HeaderHBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
text = "导出配置"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MainVBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(500, 400)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
horizontal_scroll_mode = 0
|
||||
vertical_scroll_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MainVBox/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Open a File"
|
||||
initial_position = 1
|
||||
size = Vector2i(1500, 1000)
|
||||
content_scale_mode = 1
|
||||
content_scale_factor = 3.0
|
||||
ok_button_text = "Open"
|
||||
file_mode = 0
|
||||
access = 2
|
||||
filters = PackedStringArray("*.wav", "*.ogg", "*.mp3")
|