extends Node2D signal mati # 👈 Sinyal agar stage bisa mendengar kematian TANK! # ============================================================================== # 🎛️ SETELAN AUDIO MANUAL PANZER (UBAH DI SINI) # ============================================================================== # Nilai dalam desibel (dB). Shiro set ke 10.0 dB agar suaranya lantang dan keras! @export var VOLUME_TEMBAKAN: float = 10.0 # ============================================================================== # --- ⚙️ PENGATURAN ROTASI RODA --- @export var kecepatan_rotasi_roda: float = 5.0 # --- 🔫 PENGATURAN TEMBAKAN PANZER --- @export var peluru_scene: PackedScene = preload("res://pelurumusuhfase_2.tscn") @export var jeda_tembak: float = 3.0 # Jeda antar tembakan (Fase Diam) @export var kecepatan_putar_laras: float = 2.0 # Semakin cepat laras berputar @export var damage_tembakan: float = 30.0 # DAMAGE YANG DILEMPAR PANZER KE PELURUNYA # --- 📐 BATAS ROTASI LARAS --- @export_group("Batas Rotasi Meriam") @export var batas_mendongak_keatas: float = 45.0 # Maksimal mendongak 45 derajat ke atas @export var batas_menunduk_kebawah: float = 10.0 # Maksimal menunduk 10 derajat ke bawah # --- 🖼️ TEXTURE UNTUK FASE KERUSAKAN TANK --- @export_group("Visual Kerusakan Bodi") @export var tekstur_body_hp_100: Texture2D # Gambar tank saat HP 100 kebawah @export var tekstur_body_hp_50: Texture2D # Gambar tank saat HP 50 kebawah @export var tekstur_body_hancur_0: Texture2D # Gambar tank jadi bangkai rongsokan saat HP 0 # --- 🛡️ PENGATURAN NYAWA PANZER --- @export var health = 200 var is_destroyed = false # 🎵 AMBIL REFERENSI NODE AUDIO UNTUK EFEK SUARA TEMBAKAN @onready var audio_tembakan = $AudioStreamPlayer # Daftar untuk menyimpan referensi ke-11 Sprite2D ban secara otomatis var daftar_ban: Array[Sprite2D] = [] # Referensi Node Laras, Titik Tembak, dan Bodi Tank var node_laras: Sprite2D = null var titik_tembak: Marker2D = null var node_body_sprite: Sprite2D = null # Variabel pembantu AI & Pergerakan var posisi_terakhir_x: float = 0.0 var player_target: Node2D = null # --- 🧭 VARIABEL KONTROL ROTASI --- var sudut_target_laras: float = 0.0 var sedang_membidik: bool = false func _ready() -> void: print("🪖 Final Boss: Tank Panzer (HP 200) bersiap!") posisi_terakhir_x = global_position.x player_target = get_tree().current_scene.get_node_or_null("Player") var node_static_body = get_node_or_null("StaticBody2D") if node_static_body: node_body_sprite = node_static_body.get_node_or_null("body") node_laras = node_static_body.get_node_or_null("laras") if node_laras: titik_tembak = node_laras.get_node_or_null("TitikTembak") sudut_target_laras = node_laras.rotation for i in range(1, 12): var nama_ban = "ban" + str(i) var node_ban = node_static_body.get_node_or_null(nama_ban) if node_ban and node_ban is Sprite2D: daftar_ban.append(node_ban) print("⚙️ Berhasil mendaftarkan ", daftar_ban.size(), " roda Panzer.") mulai_siklus_tembak_panzer() else: print("❌ Eror: Node induk 'StaticBody2D' tidak ditemukan!") func _physics_process(delta: float) -> void: if is_destroyed: return var pergerakan_x = global_position.x - posisi_terakhir_x if pergerakan_x < -0.01: putar_semua_roda(-kecepatan_rotasi_roda * delta) elif pergerakan_x > 0.01: putar_semua_roda(kecepatan_rotasi_roda * delta) posisi_terakhir_x = global_position.x if node_laras: if sedang_membidik and player_target: var arah_ke_player = node_laras.global_position.direction_to(player_target.global_position) var sudut_mentah = arah_ke_player.angle() var sudut_derajat = rad_to_deg(sudut_mentah) var selisih_sudut = sudut_derajat - 180.0 selisih_sudut = fposmod(selisih_sudut + 180.0, 360.0) - 180.0 selisih_sudut = clamp(selisih_sudut, -batas_menunduk_kebawah, batas_mendongak_keatas) sudut_target_laras = deg_to_rad(selisih_sudut) else: sudut_target_laras = 0.0 node_laras.rotation = rotate_toward(node_laras.rotation, sudut_target_laras, kecepatan_putar_laras * delta) func putar_semua_roda(jumlah_rotasi: float) -> void: for ban in daftar_ban: if is_instance_valid(ban): ban.rotation += jumlah_rotasi func mulai_siklus_tembak_panzer() -> void: while true: if is_destroyed: break sedang_membidik = false await get_tree().create_timer(jeda_tembak).timeout if is_destroyed: break if not player_target or ("is_hiding" in player_target and player_target.is_hiding): continue sedang_membidik = true await get_tree().create_timer(1.5).timeout if is_destroyed: break eksekusi_tembakan_meriam() await get_tree().create_timer(0.5).timeout func eksekusi_tembakan_meriam() -> void: if not peluru_scene or not titik_tembak or not node_laras or is_destroyed: return print("💥 BOOM! Tembakan Panzer meletus! Mengirim damage: ", damage_tembakan) # 🎵 MAINKAN AUDIO TEMBAKAN & ATUR VOLUMENYA AGAR LEBIH KERAS if audio_tembakan: audio_tembakan.volume_db = VOLUME_TEMBAKAN audio_tembakan.play() var p = peluru_scene.instantiate() p.global_position = titik_tembak.global_position if "damage" in p: p.damage = damage_tembakan var arah_tembakan = Vector2.LEFT.rotated(node_laras.global_rotation) if "direction" in p: p.direction = arah_tembakan p.rotation = arah_tembakan.angle() get_tree().current_scene.add_child(p) # --- 🛡️ LOGIKA MENERIMA DAMAGE --- func kurangi_nyawa(jumlah): if is_destroyed: return health -= jumlah print("🛡️ Tank Panzer Terhantam! HP Panzer: ", health) if node_body_sprite: if health <= 0: if tekstur_body_hancur_0: node_body_sprite.texture = tekstur_body_hancur_0 elif health <= 50: if tekstur_body_hp_50: node_body_sprite.texture = tekstur_body_hp_50 print("🚨 CRITICAL! Gambar berubah ke sprite HP 50 kebawah.") elif health <= 100: if tekstur_body_hp_100: node_body_sprite.texture = tekstur_body_hp_100 print("⚠️ WARNING! Gambar berubah ke sprite HP 100 kebawah.") if health <= 0: hancurkan_tank() # --- 💀 LOGIKA HANCUR --- func hancurkan_tank(): is_destroyed = true sedang_membidik = false print("💀 TANK PANZER BERHASIL DIHANCURKAN! STAGE 3 CLEAR!") # Pancarkan sinyal ke Stage (Tanpa memicu audio apa pun) mati.emit() # Panggil rem darurat di script Stage 3 var node_stage = get_parent() if node_stage and node_stage.has_method("hentikan_pergerakan_panzer"): node_stage.hentikan_pergerakan_panzer() # Atur posisi dan skala sprite bangkai secara presisi if node_body_sprite: node_body_sprite.position = Vector2(575.5, 269.0) node_body_sprite.scale = Vector2(2.135, 2.106) print("📐 Posisi & Skala bangkai Panzer berhasil disesuaikan!") # Hapus laras meriam beserta node anaknya if node_laras: node_laras.queue_free() print("🗑️ Node Laras dibersihkan.") # Matikan bentuk tabrakan agar player bisa lewat bebas var body = get_node_or_null("StaticBody2D") if body: for child in body.get_children(): if child is CollisionPolygon2D or child is CollisionShape2D: child.set_deferred("disabled", true) print("🛡️ Collision bodi tank dinonaktifkan.")