2024-12-27 07:56:45 +00:00
|
|
|
|
class_name GroundLoader extends Node2D
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
2025-01-05 11:25:13 +00:00
|
|
|
|
@export_group("Scene")
|
2024-12-30 13:19:10 +00:00
|
|
|
|
@export var ignore_archive := false
|
2024-12-27 13:32:12 +00:00
|
|
|
|
@export var current_scene := "c02_s01"
|
|
|
|
|
@export var entrance_portal := "left"
|
2025-01-05 11:25:13 +00:00
|
|
|
|
@export var debug_reload := false:
|
|
|
|
|
set(new_val):
|
|
|
|
|
debug_reload = false
|
2025-01-21 07:58:21 +00:00
|
|
|
|
if is_node_ready() and current_scene and entrance_portal:
|
2025-02-04 13:30:43 +00:00
|
|
|
|
transition_to_scene(current_scene, entrance_portal, 0.0)
|
2025-01-21 10:08:16 +00:00
|
|
|
|
# 强制覆盖 archive 记录
|
2025-01-21 10:52:36 +00:00
|
|
|
|
@export var force_archive_scene := ""
|
|
|
|
|
@export var force_archive_portal := ""
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
2025-01-21 07:58:21 +00:00
|
|
|
|
@onready var mask_layer := %MaskLayer as CanvasLayer
|
|
|
|
|
@onready var mask := %Mask as ColorRect
|
|
|
|
|
|
2025-01-13 08:09:57 +00:00
|
|
|
|
var first_entered := true
|
2025-01-03 08:07:35 +00:00
|
|
|
|
var ground: Ground2D
|
2025-02-04 13:30:43 +00:00
|
|
|
|
var display_mask_sec = 0.0
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
2025-01-21 12:41:24 +00:00
|
|
|
|
|
2025-01-21 07:58:21 +00:00
|
|
|
|
# 场景名字映射到路径
|
2025-04-01 08:12:29 +00:00
|
|
|
|
static var GROUND_SCENE_PATH_DICT = {
|
|
|
|
|
"c01_s05": "res://scene/ground/scene/c01/s05_院长房间.tscn",
|
|
|
|
|
"c01_s06": "res://scene/ground/scene/c01/s06_孤儿院长廊围墙.tscn",
|
|
|
|
|
"c01_s07": "res://scene/ground/scene/c01/s07_书店外.tscn",
|
|
|
|
|
"c01_s08": "res://scene/ground/scene/c01/s08_书店.tscn",
|
|
|
|
|
"c01_s09": "res://scene/ground/scene/c01/s09_公寓楼外.tscn",
|
|
|
|
|
"c01_s10": "res://scene/ground/scene/c01/s10_公寓楼道.tscn",
|
|
|
|
|
"c01_s11": "res://scene/ground/scene/c01/s11_黄包车演出.tscn",
|
|
|
|
|
"c01_s12": "res://scene/ground/scene/c01/s12_书店外_诡异版.tscn",
|
2025-04-20 10:21:11 +00:00
|
|
|
|
"c02_s01": "res://scene/ground/scene/c02/s01_街道.tscn",
|
2025-05-13 11:45:33 +00:00
|
|
|
|
"c02_s02": "res://scene/ground/scene/c02/s02_过道.tscn",
|
|
|
|
|
"c02_s03": "res://scene/ground/scene/c02/s03_院子.tscn",
|
|
|
|
|
"c02_s04": "res://scene/ground/scene/c02/s04_保卫科.tscn",
|
|
|
|
|
"c02_s05": "res://scene/ground/scene/c02/s05_一楼内侧楼道.tscn",
|
|
|
|
|
"c02_s06": "res://scene/ground/scene/c02/s06_二楼.tscn",
|
|
|
|
|
"c02_s07": "res://scene/ground/scene/c02/s07_二楼内侧楼道.tscn",
|
|
|
|
|
"c02_s08": "res://scene/ground/scene/c02/s08_瞎子卧室.tscn",
|
|
|
|
|
"c02_s09": "res://scene/ground/scene/c02/s09_裂缝.tscn",
|
|
|
|
|
"c02_s10": "res://scene/ground/scene/c02/s10_空房间.tscn",
|
2025-05-21 11:03:44 +00:00
|
|
|
|
"c02_s11": "res://scene/ground/scene/c02/s11_一楼火灾.tscn", # 注:该场景合并在了 c02_s03 院子中
|
2025-06-05 14:01:47 +00:00
|
|
|
|
"c02_s12": "res://scene/ground/scene/c02/s12_盒子猫.tscn",
|
2025-06-15 09:31:43 +00:00
|
|
|
|
"c02_s13": "res://scene/ground/scene/c02/s13_盒子猫二楼.tscn",
|
|
|
|
|
"c02_s14": "res://scene/ground/scene/c02/s14_盒子猫二楼内侧.tscn",
|
|
|
|
|
"c02_s15": "res://scene/ground/scene/c02/s15_盒子猫一楼内侧.tscn",
|
|
|
|
|
"c02_s16": "res://scene/ground/scene/c02/s16_盒子猫三楼内侧.tscn",
|
|
|
|
|
"c02_s17": "res://scene/ground/scene/c02/s17_盒子猫三楼.tscn",
|
2025-04-01 08:12:29 +00:00
|
|
|
|
}
|
2025-01-15 04:02:11 +00:00
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
func _ready() -> void:
|
2025-01-21 07:58:21 +00:00
|
|
|
|
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
|
|
|
|
|
mask.visible = true
|
|
|
|
|
mask.color.a = 0.0
|
2025-01-21 10:08:16 +00:00
|
|
|
|
ground = get_node_or_null("Ground") as Ground2D
|
|
|
|
|
if ground:
|
|
|
|
|
ground.queue_free()
|
2025-01-05 11:25:13 +00:00
|
|
|
|
# load save
|
|
|
|
|
if not ignore_archive:
|
|
|
|
|
_load_save()
|
2025-01-02 11:01:44 +00:00
|
|
|
|
if current_scene and entrance_portal:
|
2025-06-07 09:04:08 +00:00
|
|
|
|
# 首次进入渐隐效果
|
|
|
|
|
transition_to_scene(current_scene, entrance_portal)
|
|
|
|
|
# transition_to_scene(current_scene, entrance_portal, 0.0)
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
2025-01-14 00:56:51 +00:00
|
|
|
|
func _load_save():
|
2025-01-21 10:52:36 +00:00
|
|
|
|
# 强制覆盖 archive 记录
|
|
|
|
|
if force_archive_scene or force_archive_portal:
|
|
|
|
|
current_scene = force_archive_scene
|
|
|
|
|
entrance_portal = force_archive_portal
|
|
|
|
|
return
|
|
|
|
|
if not Engine.is_editor_hint():
|
2025-01-14 00:56:51 +00:00
|
|
|
|
if ArchiveManager.archive.current_scene:
|
2025-01-21 10:52:36 +00:00
|
|
|
|
current_scene = ArchiveManager.archive.current_scene
|
2025-01-14 00:56:51 +00:00
|
|
|
|
if ArchiveManager.archive.entrance_portal:
|
2025-01-21 10:52:36 +00:00
|
|
|
|
entrance_portal = ArchiveManager.archive.entrance_portal
|
2024-12-26 13:58:37 +00:00
|
|
|
|
|
|
|
|
|
|
2025-03-12 10:43:02 +00:00
|
|
|
|
func toggle_mask(display: bool, mask_color: Color, wait_time: float) -> Tween:
|
2025-01-21 13:49:37 +00:00
|
|
|
|
var tween = get_tree().create_tween()
|
2025-02-04 13:30:43 +00:00
|
|
|
|
mask_color.a = mask.color.a
|
|
|
|
|
mask.color = mask_color
|
|
|
|
|
var duration = min(0.3, wait_time * 0.5)
|
2025-01-21 07:58:21 +00:00
|
|
|
|
if display:
|
2025-02-04 13:30:43 +00:00
|
|
|
|
tween.tween_property(mask, "color:a", 1.0, duration).set_trans(Tween.TRANS_CUBIC)
|
|
|
|
|
display_mask_sec = Time.get_ticks_msec() * 0.001
|
2025-01-21 07:58:21 +00:00
|
|
|
|
else:
|
2025-02-04 13:30:43 +00:00
|
|
|
|
# 转场至少 0.6s, 除去 0.3s 最后的淡出,需要 0.3s 的等待时间(包含 mask 的淡入)
|
|
|
|
|
if wait_time:
|
|
|
|
|
var time = Time.get_ticks_msec() * 0.001
|
|
|
|
|
wait_time = max(wait_time + display_mask_sec - time - 0.3, 0.0)
|
|
|
|
|
if wait_time:
|
|
|
|
|
tween.tween_interval(wait_time)
|
|
|
|
|
tween.tween_property(mask, "color:a", 0.0, duration).set_trans(Tween.TRANS_CUBIC)
|
2025-01-21 07:58:21 +00:00
|
|
|
|
return tween
|
|
|
|
|
|
2025-02-04 13:30:43 +00:00
|
|
|
|
func transition_to_scene(
|
2025-05-13 11:45:33 +00:00
|
|
|
|
scene_name: String, portal: String, wait_time := 1.4, mask_color := Color.BLACK
|
2025-02-04 13:30:43 +00:00
|
|
|
|
) -> void:
|
2025-01-21 12:41:24 +00:00
|
|
|
|
var scene_path = GROUND_SCENE_PATH_DICT.get(scene_name)
|
2024-12-26 13:58:37 +00:00
|
|
|
|
if scene_path:
|
2025-01-21 07:58:21 +00:00
|
|
|
|
current_scene = scene_name
|
2024-12-27 13:32:12 +00:00
|
|
|
|
entrance_portal = portal
|
2025-01-14 00:56:51 +00:00
|
|
|
|
# 优先更新 archive,使 ground 可以访问自己的 current_scene 键值
|
2025-01-21 10:08:16 +00:00
|
|
|
|
if not Engine.is_editor_hint():
|
|
|
|
|
_update_archive()
|
2025-02-04 13:30:43 +00:00
|
|
|
|
if wait_time > 0.0:
|
2025-01-21 13:49:37 +00:00
|
|
|
|
# 转场效果,在 _load_ground_node 之前播放
|
2025-03-12 10:43:02 +00:00
|
|
|
|
var tween = toggle_mask(true, mask_color, wait_time)
|
2025-02-01 06:44:12 +00:00
|
|
|
|
tween.tween_callback(call_deferred.bind("_do_transition", scene_name))
|
2025-03-12 10:43:02 +00:00
|
|
|
|
tween.tween_callback(toggle_mask.bind(false, mask_color, wait_time))
|
2025-01-21 13:49:37 +00:00
|
|
|
|
else:
|
2025-06-14 08:46:32 +00:00
|
|
|
|
_do_transition.call_deferred(scene_name)
|
2024-12-26 13:58:37 +00:00
|
|
|
|
else:
|
2025-01-21 07:58:21 +00:00
|
|
|
|
print("Scene not found: " + scene_name)
|
2024-12-27 13:32:12 +00:00
|
|
|
|
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
2025-01-21 10:08:16 +00:00
|
|
|
|
func _update_archive():
|
|
|
|
|
ArchiveManager.archive.current_scene = current_scene
|
|
|
|
|
ArchiveManager.archive.entrance_portal = entrance_portal
|
2024-12-27 13:32:12 +00:00
|
|
|
|
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
2025-02-04 13:30:43 +00:00
|
|
|
|
func _do_transition(scene_name: String) -> void:
|
2025-01-21 07:58:21 +00:00
|
|
|
|
# SceneManager.freeze_player(0)
|
2025-01-31 10:44:30 +00:00
|
|
|
|
ground = get_node_or_null("Ground") as Ground2D
|
2024-12-27 13:32:12 +00:00
|
|
|
|
if ground:
|
2025-01-31 10:44:30 +00:00
|
|
|
|
# 防止命名冲突
|
2025-02-01 06:44:12 +00:00
|
|
|
|
remove_child(ground)
|
2025-01-21 10:08:16 +00:00
|
|
|
|
ground.queue_free()
|
2025-01-21 07:58:21 +00:00
|
|
|
|
# 先设置 ground,再添加到场景中
|
|
|
|
|
# 因为 ground 在 enter_tree 时会用到 SceneManager 的方法
|
|
|
|
|
# 其中间接用到了 GroundLoader 的 ground
|
2025-01-31 10:44:30 +00:00
|
|
|
|
ground = _load_ground_node(scene_name)
|
2025-01-21 07:58:21 +00:00
|
|
|
|
_add_ground()
|
|
|
|
|
# 预先加载邻居场景
|
|
|
|
|
_post_transition()
|
|
|
|
|
if GlobalConfig.DEBUG and not Engine.is_editor_hint():
|
|
|
|
|
_watch_scene_update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _add_ground():
|
|
|
|
|
add_child(ground)
|
2025-01-31 10:44:30 +00:00
|
|
|
|
ground.name = "Ground"
|
2025-01-21 13:49:37 +00:00
|
|
|
|
if not Engine.is_editor_hint():
|
2025-01-21 10:08:16 +00:00
|
|
|
|
# move player to portal
|
|
|
|
|
ground.move_player_to_portal(entrance_portal)
|
2025-01-21 13:49:37 +00:00
|
|
|
|
# 更新玩家位置
|
|
|
|
|
if first_entered:
|
|
|
|
|
_update_player_position_from_archive()
|
2025-01-21 07:58:21 +00:00
|
|
|
|
first_entered = false
|
|
|
|
|
# SceneManager.release_player()
|
2024-12-27 13:32:12 +00:00
|
|
|
|
|
|
|
|
|
|
2025-01-21 10:08:16 +00:00
|
|
|
|
func _update_player_position_from_archive():
|
|
|
|
|
if ignore_archive or Engine.is_editor_hint():
|
|
|
|
|
return
|
|
|
|
|
var player = SceneManager.get_player() as MainPlayer
|
2025-01-21 10:52:36 +00:00
|
|
|
|
if player:
|
2025-01-21 10:08:16 +00:00
|
|
|
|
# if GlobalConfig.DEBUG:
|
|
|
|
|
# print("update player position", ArchiveManager.archive.player_global_position)
|
|
|
|
|
if ArchiveManager.archive.player_global_position_x >= 0:
|
|
|
|
|
player.global_position.x = ArchiveManager.archive.player_global_position_x
|
|
|
|
|
player.set_facing_direction(ArchiveManager.archive.player_direction)
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
|
|
|
|
|
2025-02-04 13:30:43 +00:00
|
|
|
|
func _load_ground_node(scene_name: String) -> Ground2D:
|
2025-01-21 12:41:24 +00:00
|
|
|
|
if not GROUND_SCENE_PATH_DICT.has(scene_name):
|
2025-01-21 07:58:21 +00:00
|
|
|
|
return null
|
2025-01-21 12:41:24 +00:00
|
|
|
|
var path = GROUND_SCENE_PATH_DICT[scene_name]
|
2025-02-04 13:30:43 +00:00
|
|
|
|
var scene: PackedScene
|
|
|
|
|
if ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_LOADED:
|
|
|
|
|
scene = ResourceLoader.load_threaded_get(path) as PackedScene
|
|
|
|
|
else:
|
|
|
|
|
scene = ResourceLoader.load(path) as PackedScene
|
2025-01-21 07:58:21 +00:00
|
|
|
|
if scene:
|
|
|
|
|
var instance = scene.instantiate() as Node2D
|
|
|
|
|
var ground_node = instance.get_child(0)
|
|
|
|
|
instance.remove_child(ground_node)
|
|
|
|
|
ground_node.owner = null
|
|
|
|
|
instance.queue_free()
|
|
|
|
|
return ground_node
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 读取 portals,预加载邻居场景
|
|
|
|
|
func _post_transition():
|
|
|
|
|
if ground:
|
|
|
|
|
var scene_names = []
|
|
|
|
|
var deploy_layer = ground.get_node("DeployLayer")
|
|
|
|
|
if deploy_layer:
|
|
|
|
|
for node in deploy_layer.get_children():
|
|
|
|
|
var portal = node as Portal2D
|
|
|
|
|
if not portal or not portal.target_scene:
|
|
|
|
|
continue
|
2025-01-21 12:41:24 +00:00
|
|
|
|
if GROUND_SCENE_PATH_DICT.has(portal.target_scene):
|
2025-01-21 07:58:21 +00:00
|
|
|
|
scene_names.append(portal.target_scene)
|
|
|
|
|
if scene_names:
|
|
|
|
|
for scene_name in scene_names:
|
2025-01-21 12:41:24 +00:00
|
|
|
|
ResourceLoader.load_threaded_request(GROUND_SCENE_PATH_DICT[scene_name])
|
2025-01-21 07:58:21 +00:00
|
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
|
print("preload neighbor scenes:", scene_names)
|
|
|
|
|
|
|
|
|
|
|
2024-12-30 13:19:10 +00:00
|
|
|
|
var update_watcher: Timer
|
2025-01-14 00:56:51 +00:00
|
|
|
|
var last_modify_time = 0
|
2024-12-30 13:19:10 +00:00
|
|
|
|
|
2025-01-02 11:01:44 +00:00
|
|
|
|
|
2025-01-14 00:56:51 +00:00
|
|
|
|
# DEBUG 时重新加载资源
|
2024-12-30 13:19:10 +00:00
|
|
|
|
func _watch_scene_update():
|
2025-01-21 12:41:24 +00:00
|
|
|
|
var scene_path = GROUND_SCENE_PATH_DICT[current_scene]
|
2024-12-30 13:19:10 +00:00
|
|
|
|
if scene_path:
|
2025-01-14 00:56:51 +00:00
|
|
|
|
last_modify_time = FileAccess.get_modified_time(scene_path)
|
2024-12-30 13:19:10 +00:00
|
|
|
|
if not update_watcher:
|
|
|
|
|
update_watcher = Timer.new()
|
|
|
|
|
update_watcher.wait_time = 1
|
|
|
|
|
update_watcher.one_shot = false
|
|
|
|
|
add_child(update_watcher)
|
|
|
|
|
update_watcher.start()
|
|
|
|
|
else:
|
|
|
|
|
# remove all connections
|
|
|
|
|
for c in update_watcher.timeout.get_connections():
|
|
|
|
|
update_watcher.timeout.disconnect(c.callable)
|
|
|
|
|
update_watcher.timeout.connect(_check_scene_update.bind(scene_path))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _check_scene_update(scene_path):
|
|
|
|
|
var modify = FileAccess.get_modified_time(scene_path)
|
2025-01-14 00:56:51 +00:00
|
|
|
|
if modify != last_modify_time:
|
|
|
|
|
last_modify_time = modify
|
2024-12-30 13:19:10 +00:00
|
|
|
|
_on_resources_reload(scene_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_resources_reload(res):
|
|
|
|
|
print("resources_reload processing:", res)
|
2025-01-02 11:01:44 +00:00
|
|
|
|
if not Engine.is_editor_hint() and res.ends_with(".tscn"):
|
2024-12-30 13:19:10 +00:00
|
|
|
|
ArchiveManager.save_all()
|
2025-01-29 14:05:27 +00:00
|
|
|
|
first_entered = true
|
2025-06-14 08:46:32 +00:00
|
|
|
|
transition_to_scene.call_deferred(current_scene, entrance_portal, 0.0)
|