207 lines
7.0 KiB
GDScript
207 lines
7.0 KiB
GDScript
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.")
|