extends Node2D # ============================================================================== # 🎛️ SETELAN AUDIO MANUAL CUTSCENE (UBAH DI SINI) # ============================================================================== @export var VOLUME_BGM: float = -10.0 # Volume musik latar (dB) @export var VOLUME_KETIKAN: float = -15.0 # Volume suara ketikan teks (dB) @export var VOLUME_AUDIO_3: float = -12.0 # Volume untuk audio ketiga (dB) @export var DURASI_FADE_IN: float = 2.5 # Durasi musik memudar masuk (detik) @export var DURASI_FADE_OUT: float = 1.5 # Durasi musik memudar keluar (detik) @export var JEDA_AUDIO_3: float = 1.5 # ⏱️ BARU: Jeda waktu sebelum audio ketiga mulai (detik) # ============================================================================== @onready var label = $CanvasLayer/RichTextLabel @onready var anim_player = $AnimationPlayer @onready var timer_jeda = $TimerJeda # 🎵 AMBIL REFERENSI KETIGA NODE AUDIO STREAM @onready var audio_bgm = $AudioStreamPlayer @onready var audio_ketikan = $AudioStreamPlayer2 @onready var audio_tambahan = $AudioStreamPlayer3 @export_file("*.tscn") var target_scene @export_multiline var dialog_teks = """(1 September 1939) Langit di atas Polandia masih meremang kelabu, menyisakan sisa-sisa dingin malam yang perlahan memudar. Di sudut kota, detak kehidupan justru mulai menggeliat lebih awal. Sayup-sayup terdengar suara riuh rendah dari para pedagang yang mulai menggelar dagangannya di pasar, kepulan asap hangat dari kedai roti beraliran arsitektur klasik, serta langkah kaki beberapa warga yang menyapa fajar dengan senyuman. Berjalan di antara ramahnya trotoar kota ini, semuanya terasa begitu damai. Sebuah simfoni pagi yang tenang, hangat, dan hidup.""" var kecepatan_teks = 0.03 var sudah_mulai = false # 🛠️ VARIABEL BARU UNTUK KONTROL FITUR KLIK var teks_selesai_diketik = false # Penanda teks sudah muncul semua (lewat loop atau skip) var boleh_klik_instan = false # Kunci pengaman 2 detik pertama var paksa_tampilkan_semua = false # Trigger untuk memotong loop ketikan func _ready(): label.text = "" label.visible_characters = 0 sudah_mulai = false teks_selesai_diketik = false boleh_klik_instan = false paksa_tampilkan_semua = false # Kita matikan autostart pada TimerJeda karena transisi level dikontrol oleh klik pemain timer_jeda.one_shot = true if timer_jeda.timeout.is_connected(_on_timer_jeda_timeout): timer_jeda.timeout.disconnect(_on_timer_jeda_timeout) # Play BGM dengan efek Fade In if audio_bgm: audio_bgm.volume_db = -80.0 audio_bgm.play() var tween_in = create_tween() tween_in.tween_property(audio_bgm, "volume_db", VOLUME_BGM, DURASI_FADE_IN)\ .set_trans(Tween.TRANS_QUAD)\ .set_ease(Tween.EASE_OUT) # 🔊 BARU: Beri jeda waktu sebelum Audio Ketiga otomatis berputar if audio_tambahan: audio_tambahan.volume_db = VOLUME_AUDIO_3 # Menggunakan timer asinkron bawaan scene tree agar tidak membekukan game get_tree().create_timer(JEDA_AUDIO_3).timeout.connect(func(): # Pastikan musuh atau scene belum berganti/mati saat timer selesai if is_inside_tree() and audio_tambahan: audio_tambahan.play() print("🔊 Audio ketiga mulai berputar setelah jeda ", JEDA_AUDIO_3, " detik.") ) anim_player.play("fadein") func mulai_tulis_teks(): print("Fungsi mulai_tulis_teks terpanggil!") if sudah_mulai: return sudah_mulai = true label.text = dialog_teks label.visible_characters = 0 label.autowrap_mode = TextServer.AUTOWRAP_WORD # ⏱️ Jalankan timer internal 2 detik untuk mengaktifkan fitur klik instan get_tree().create_timer(2.0).timeout.connect(func(): boleh_klik_instan = true print("⚡ Fitur klik instan/skip sekarang aktif!") ) for i in range(dialog_teks.length() + 1): # Jika pemain melakukan klik setelah 2 detik, batalkan loop ketikan if paksa_tampilkan_semua: break label.visible_characters = i if i > 0 and i - 1 < dialog_teks.length(): var karakter_saat_ini = dialog_teks[i - 1] if karakter_saat_ini != " " and karakter_saat_ini != "\n" and karakter_saat_ini != "\t": bunyikan_sfx_ketikan_bebas() await get_tree().create_timer(kecepatan_teks).timeout # Blok eksekusi setelah teks tampil penuh (baik lewat ketikan normal maupun di-skip) label.visible_characters = dialog_teks.length() teks_selesai_diketik = true print("📖 Semua teks telah ditampilkan. Menunggu klik pemain untuk lanjut...") # 🖱️ / 📱 INPUT DETECTION: Mendeteksi klik mouse atau TAP layar di mana saja func _input(event): var is_click = event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT var is_touch = event is InputEventScreenTouch and event.pressed if is_click or is_touch: # Kondisi 1: Teks masih mengetik, sudah lewat 2 detik, dan pemain klik untuk SKIP if sudah_mulai and not teks_selesai_diketik and boleh_klik_instan: paksa_tampilkan_semua = true print("⏭️ Teks dipaksa muncul semua!") get_viewport().set_input_as_handled() return # Kondisi 2: Semua teks sudah muncul di layar, klik sekali lagi untuk PINDAH SCENE if teks_selesai_diketik: teks_selesai_diketik = false # Kunci anti-spam klik saat fadeout get_viewport().set_input_as_handled() mulai_transisi_fadeout() # 🎬 SEKUENS FADE OUT DAN PERPINDAHAN LEVEL MANUAL func mulai_transisi_fadeout(): print("🎬 Memulai proses fadeout cutscene...") # Fade out untuk BGM if audio_bgm and audio_bgm.is_playing(): var tween_out = create_tween() tween_out.tween_property(audio_bgm, "volume_db", -80.0, DURASI_FADE_OUT)\ .set_trans(Tween.TRANS_QUAD)\ .set_ease(Tween.EASE_IN) # Lakukan juga Fade Out untuk Audio Ketiga agar tidak mati mendadak if audio_tambahan and audio_tambahan.is_playing(): var tween_out_3 = create_tween() tween_out_3.tween_property(audio_tambahan, "volume_db", -80.0, DURASI_FADE_OUT)\ .set_trans(Tween.TRANS_QUAD)\ .set_ease(Tween.EASE_IN) if anim_player and anim_player.has_animation("fadeout"): anim_player.play("fadeout") await anim_player.animation_finished # 🔒 MATIKAN TOTAL KEDUA AUDIO SAAT PINDAH SCENE if audio_bgm: audio_bgm.stop() if audio_tambahan: audio_tambahan.stop() if target_scene != "": get_tree().change_scene_to_file(target_scene) # 🛠️ FUNGSI UNTUK DUPLIKASI AUDIO SECARA REAL-TIME func bunyikan_sfx_ketikan_bebas(): if audio_ketikan and audio_ketikan.stream: var audio_baru = AudioStreamPlayer.new() audio_baru.stream = audio_ketikan.stream audio_baru.volume_db = VOLUME_KETIKAN add_child(audio_baru) audio_baru.play() audio_baru.finished.connect(func(): audio_baru.queue_free()) func _on_timer_jeda_timeout(): pass