extends Node
enum VIBE {
NORAML,
MYSTERY,
DANGEROUS,
TOUCHING,
}
# 从 ground loader 控制该信号
signal ground_ready(ground: Ground2D)
signal ground_start
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
# get_tree().node_added.connect("_on_node_added")
#### Ground and Loader ####
func get_ground_loader() -> GroundLoader:
return get_node_or_null("/root/Main/GroundLoader") as GroundLoader
func toggle_ground_mask(
display: bool, wait_time := 1.5, ease_min_duration := 0.5, mask_color := Color.BLACK
) -> Tween:
return get_ground_loader().toggle_mask(display, wait_time, ease_min_duration, mask_color)
# restart scene in debug launch...
func is_restarting() -> bool:
var ground = get_ground()
if ground:
return ground.restarting
return false
func get_ground() -> Ground2D:
var loader = get_ground_loader()
if loader:
return loader.ground
if get_tree().current_scene:
return get_tree().current_scene.get_node_or_null("Ground")
return null
func get_camera_marker() -> CameraFocusMarker:
var ground = get_ground()
if ground:
return ground.camera_focus_marker
return null
func get_player() -> MainPlayer:
var ground = get_ground()
if ground:
return ground.player
return null
func focus_node(node: Node2D, duration := 0.0) -> void:
var marker = get_camera_marker()
if marker:
marker.focus_node(node, duration)
func focus_player_and_reset_zoom(duration := 1.2) -> void:
var marker = get_camera_marker()
if marker:
# marker.force_offset = Vector2.ZERO
marker.tween_zoom(1.0, duration)
# 运镜更平滑一些
marker.focus_node(get_player(), duration + .3)
func get_lock() -> PlayerReenterLock:
var ground = get_ground()
if ground:
if ground.reenter_lock:
# 允许访问 lock
return ground.reenter_lock
return ground.get_node_or_null("PlayerReenterLock") as PlayerReenterLock
printerr("get_lock: Ground node not found. return new detached lock")
# 允许访问 lock
return PlayerReenterLock.new()
func lock_player(duration := 0.0, action := 3, auto_quit := false) -> void:
get_lock().lock_all(duration)
if action != 3:
# 先 freeze 再 action,否则会重置 action
await player_action(action, auto_quit)
func unlock_player() -> void:
get_lock().unlock_all()
func hold_player(duration := 0) -> void:
get_lock().hold(duration)
func unhold_player() -> void:
get_lock().unhold()
# lock_time: the time to lock the player action. 0 means lock forever, thus the player will be locked until release_player is called.
func freeze_player(lock_time := 0.0, action := 3, auto_quit := false) -> void:
# 先 freeze 再 action,否则会重置 action
get_lock().freeze(lock_time)
await player_action(action, auto_quit)
func release_player() -> void:
get_lock().release()
func player_action(action_code: int, auto_quit := false) -> void:
var player = get_player()
if player:
await player.player_action(action_code, auto_quit)
else:
printerr("player_action: Player node not found")
var debug_balloon_scene = preload("res://scene/dialog/balloon_debug.tscn")
var debug_balloon_node
func pop_debug_dialog_info(character: String, content: String) -> void:
if GlobalConfig.DEBUG:
if is_instance_valid(debug_balloon_node):
debug_balloon_node.queue_free()
var title = "title"
var body = "~ " + title + "\n"
body += character + ": " + content + "\n"
body += "=> END"
var res = DialogueManager.create_resource_from_text(body)
debug_balloon_node = DialogueManager.show_dialogue_balloon_scene(
debug_balloon_scene, res, title
)
#### Prop ####
func get_prop_hud() -> PropHud:
return get_node_or_null("/root/Main/UILayer/PropHUD") as PropHud
func toggle_hud_display(display_hud: bool) -> void:
var prop_hud = get_prop_hud() as PropHud
if prop_hud:
if display_hud:
prop_hud.display_hud()
else:
prop_hud.hide_hud()
else:
printerr("toggle_hud_display: PropHud node not found")
func checkout_prop_inventory(character: String) -> void:
var prop_hud = get_prop_hud() as PropHud
if prop_hud:
prop_hud.checkout_inventory(character)
else:
printerr("checkout_prop_inventory: PropHud node not found")
func get_current_prop(must_selected: bool) -> String:
var prop_hud = get_prop_hud()
if prop_hud and (not must_selected or prop_hud.selected):
return prop_hud.inventory.current_item_key()
return ""
func has_prop(prop_key: String) -> bool:
var prop_hud = get_prop_hud()
if prop_hud:
return prop_hud.inventory.has_prop(prop_key)
return false
# 静默增加 prop,一般用于提前保存数据,确保数据一致性(防止在 await 时退出等导致丢数据)
func enable_prop_item_silently(prop_key: String) -> void:
var prop_hud = get_prop_hud()
if prop_hud:
prop_hud.enable_prop_item(prop_key, false)
else:
printerr("enable_prop_item_silently: PropHud node not found")
func enable_prop_item(prop_key: String) -> void:
var prop_hud = get_prop_hud()
if prop_hud:
prop_hud.enable_prop_item(prop_key)
else:
printerr("enable_prop_item PropHud node not found")
func enable_important_item(prop_key: String) -> void:
var prop_hud = get_prop_hud()
if prop_hud:
prop_hud.inventory.enable_important_item(prop_key)
else:
printerr("enable_important_item PropHud node not found")
func disable_prop_item(prop_key: String) -> void:
var prop_hud = get_prop_hud()
if prop_hud:
prop_hud.disable_prop_item(prop_key)
else:
printerr("disable_prop_item PropHud node not found")
func pop_os_with_str(translation_key: String) -> void:
var player = get_player() as MainPlayer
if player:
var msg = tr(translation_key).replace("
", "\n")
var lines = await Util.generate_lines(msg)
player.pop_os(lines, translation_key)
# 等待 os 结束
var k = await player.os_finished
if k != translation_key:
printerr(
"pop_os_with_str os_finished. translation_key mismatch, expected ", translation_key
)
else:
print("pop_os_with_str os_finished. translation_key matched: ", translation_key)
else:
printerr("Player node not found")
func pop_notification(translation_key: String, number := 1) -> void:
var notification_node = get_node_or_null("/root/Main/UILayer/Notification")
if notification_node:
notification_node.show_notification(tr(translation_key).replace("
", "\n"), number)
else:
printerr("pop_notification: Notification node not found")
func pop_center_notification(translation_key: String) -> void:
var notification_node = get_node_or_null("/root/Main/UILayer/Notification")
if notification_node:
notification_node.show_center_notification(tr(translation_key).replace("
", "\n"))
else:
printerr("pop_center_notification: Notification node not found")
func pop_center_texture(texture: Texture2D, duration := 3.5) -> void:
var notification_node = get_node_or_null("/root/Main/UILayer/Notification")
if notification_node:
notification_node.show_center_texture(texture, duration)
else:
printerr("pop_center_texture: Notification node not found")
func pop_dialog(
character: String,
content: String,
character_color := "orange",
content_color := "white",
duration := 2.5
) -> void:
var dialog_node = get_node_or_null("/root/Main/UILayer/Dialog")
if dialog_node:
dialog_node.append_dialog(character, content, character_color, content_color, duration)
else:
printerr("Dialog node not found")
# func pop_note(note: String, note_color := "white", duration := 2.5) -> void:
# var dialog_node = get_node_or_null("/root/Main/UILayer/Dialog")
# if dialog_node:
# dialog_node.append_note(note, note_color, duration)
# else:
# printerr("Dialog node not found")
func get_inspector() -> PropInspector:
return get_node_or_null("/root/Main/PropInspector") as PropInspector
func checkout_index_page(transition := true) -> void:
# 保存后,渐隐切换场景
ArchiveManager.save_all()
if transition:
var ground_loader = get_ground_loader() as GroundLoader
if ground_loader:
var tween = toggle_ground_mask(true)
tween.tween_callback(_jump_back_to_index_and_quit_main)
else:
_jump_back_to_index_and_quit_main()
else:
_jump_back_to_index_and_quit_main()
var packed_index_page := preload("uid://c4ycvdsabi7lw")
func _jump_back_to_index_and_quit_main() -> void:
# 技术问题:DialogueManager 中 do SceneManager.checkout_index_page()时,checkout scene 原本 /root/Main 节点未被删除
# * 原本除 standalone 的节点外,只有 /root/Main (current_scene)
# * checkout 后,/root 下同时存在 Index 页面节点与 Main 节点
# * 导致 Camera 仍旧在 Main 下,仍旧可以切换道具,但是玩家不能移动(玩家 Input 失效)
# * 手动 queue_free 原 Main 节点可行
# * 阅读 SceneTree 的 cpp 源码,change_scene_to_packed 看似无异常
# 问题出现在 Ground 中 get_node("/root").add_child(main)
# 需要再指定 current_scene: get_tree().current_scene = main
# 防止撕裂帧
await get_tree().process_frame
get_tree().change_scene_to_packed(packed_index_page)
# 防止游戏卡死 reset pause counter
pause_counter = 0
get_tree().paused = false
var main_scene = preload("uid://dygvcmykn02n8")
func enter_main_scene() -> void:
# 防止撕裂帧
await get_tree().process_frame
get_tree().change_scene_to_packed.call_deferred(main_scene)
# get_tree().change_scene_to_file.call_deferred("uid://dygvcmykn02n8")
pause_counter = 0
get_tree().paused = false
#### UX: settings; panel: note, bag, memory ####
var panel_scene = preload("uid://ddlwnsccsmr8u")
func show_panel() -> void:
get_tree().current_scene.add_child(panel_scene.instantiate())
var note_scene = preload("uid://cy7wrrsm8e04f")
func show_note() -> void:
get_tree().current_scene.add_child(note_scene.instantiate())
var bag_scene = preload("uid://b6gbolo1o7wdf")
func show_bag() -> void:
get_tree().current_scene.add_child(bag_scene.instantiate())
var memory_scene = preload("uid://gfkdh8pqhixn")
func show_memory() -> void:
get_tree().current_scene.add_child(memory_scene.instantiate())
var settings_scene = preload("uid://beok2r6fgburn")
func show_settings() -> void:
get_tree().current_scene.add_child(settings_scene.instantiate())
#### 游戏场景树暂停计数器,设置、memory、bag 等菜单都会导致 pause
var pause_counter := 0
func toggle_pause_counter(plus := true) -> void:
pause_counter += 1 if plus else -1
print("SceneTree pause_counter: ", pause_counter)
get_tree().paused = pause_counter > 0
func quit_game() -> void:
ArchiveManager.save_all()
var ground_loader = get_ground_loader() as GroundLoader
if ground_loader:
var tween = toggle_ground_mask(true, 2.0, 1.0)
if GlobalConfig.DEBUG:
print("quit_game with transition")
tween.tween_callback(get_tree().quit)
else:
get_tree().quit()
###### Effects
# 黑手
var black_hand_scene = preload("uid://d2ftpqvtvl00h")
func show_black_hand(play_sfx := true, queue_free_delay := 0.0) -> void:
var hand = black_hand_scene.instantiate()
hand.name = "BlackHand"
get_tree().current_scene.add_child(hand)
hand.run_effect(play_sfx, queue_free_delay)
# 黑屏转场
var transition_scene = preload("uid://7dxvwqm53ugh")
func black_transition(half_duration := 0.7) -> void:
var scene = transition_scene.instantiate()
scene.name = "Transition"
get_tree().current_scene.add_child(scene)
scene.run_effect(half_duration)