@tool class_name Player extends CharacterBody2D # --- VARIABEL --- # 🎛️ SETELAN AUDIO MANUAL TEMBAKAN var VOLUME_TEMBAKAN_MANUAL: float = -8.0 @onready var sfx_tembakan = $AudioStreamPlayer @export var bisa_menunduk : bool = false @export var hanya_boleh_menunduk : bool = false @export var peluru_scene : PackedScene # Drag file pelururifle.tscn ke sini di Inspector # --- VARIABEL UNTUK SKIN CUSTOM --- @export var kostum_badan_custom: Texture2D @export var kostum_tangan_kanan_custom: Texture2D @export var kostum_tangan_kiri_custom: Texture2D @export var kostum_kepala_custom: Texture2D # Tombol utama di Inspector @export var is_pegang_senjata : bool = false: set(value): is_pegang_senjata = value if is_node_ready(): proses_senjata(value) update_animation_state() signal hp_berubah(hp_sekarang) # --- VARIABEL POWER UP TRIPLE SHOT --- var is_triple_shot : bool = false var is_menunduk = false var sedang_di_tangga = false var health = 100 var is_dead = false @export var SPEED : float = 600 const JUMP_VELOCITY = -600.0 const CLIMB_SPEED = 400.0 var is_hiding = false var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") @onready var visual = $Visual @onready var anim_player = $AnimasiIdle @onready var collision = $CollisionShape2D @onready var tangan_kanan = $Visual/tangankanan @onready var tangan_kiri = $Visual/tangankiri @onready var senjata = $Visual/senjata @onready var muzzle = $Visual/senjata/muzzle # Titik peluru keluar # 🔪 FIX 1: AMBIL REFERENSI SPRITE PISAU (Letakkan di bawah muzzle) @onready var sprite_pisau = $Visual/pisau func terima_damage(jumlah): if is_dead: return health -= jumlah health = clamp(health, 0, 100) # "Terpancar" atau kirim signal setiap kali HP berubah emit_signal("hp_berubah", health) print("HP Player: ", health) if health <= 0: mati() func mati(): if is_dead: return is_dead = true print("Robot Biru Hancur! Mengalihkan ke Layar Game Over...") set_physics_process(false) # Catat lokasi terakhir Globaltimer.scene_terakhir_player = get_tree().current_scene.scene_file_path call_deferred("pindah_ke_gameover") func pindah_ke_gameover(): get_tree().change_scene_to_file("res://gameover.tscn") func _ready(): if not Engine.is_editor_hint(): proses_senjata(is_pegang_senjata) # 🥷 Pastikan pisau tersembunyi di awal permainan if sprite_pisau: sprite_pisau.visible = false if kostum_badan_custom: $Visual/badan.texture = kostum_badan_custom if kostum_tangan_kanan_custom: $Visual/tangankanan.texture = kostum_tangan_kanan_custom if kostum_tangan_kiri_custom: $Visual/tangankiri.texture = kostum_tangan_kiri_custom $Visual/tangankiri.offset.x = -23 if kostum_kepala_custom: $Visual/kepala.texture = kostum_kepala_custom func _physics_process(delta): if Engine.is_editor_hint(): return if is_hiding: velocity = Vector2.ZERO return # LOGIKA TOGGLE SENJATA (Tab) if Input.is_action_just_pressed("ui_focus_next"): is_pegang_senjata = !is_pegang_senjata # LOGIKA BIDIK & TEMBAK (Hanya saat pegang senjata) if is_pegang_senjata: var crosshair = get_tree().current_scene.find_child("Crosshair", true, false) if crosshair and crosshair.visible: senjata.look_at(get_global_mouse_position()) senjata.flip_v = (get_global_mouse_position().x < global_position.x) else: senjata.rotation_degrees = 0 senjata.flip_v = false # SINKRON: Menggunakan "click_kiri" sesuai setup Input Map kamu if Input.is_action_just_pressed("click_kiri"): tembak() if hanya_boleh_menunduk: velocity.x = 0 if bisa_menunduk and Input.is_action_just_pressed("gerak_bawah"): proses_menunduk(!is_menunduk) if not is_on_floor(): velocity.y += gravity * delta move_and_slide() update_animation_state() return # 1. Gravitasi & Tangga if not is_on_floor() and not sedang_di_tangga: velocity.y += gravity * delta if sedang_di_tangga: if Input.is_action_pressed("gerak_atas"): velocity.y = -CLIMB_SPEED elif Input.is_action_pressed("gerak_bawah"): velocity.y = CLIMB_SPEED else: velocity.y = 0 # 2. Lompat & Menunduk if Input.is_action_just_pressed("lompat") and is_on_floor(): velocity.y = JUMP_VELOCITY if bisa_menunduk and is_on_floor() and not sedang_di_tangga: if Input.is_action_just_pressed("gerak_bawah"): proses_menunduk(!is_menunduk) # 3. Gerak Kiri-Kanan var direction = Input.get_axis("gerak_kiri", "gerak_kanan") if direction: var current_speed = SPEED * 0.5 if is_menunduk else SPEED velocity.x = direction * current_speed visual.scale.x = direction else: velocity.x = move_toward(velocity.x, 0, SPEED) move_and_slide() update_animation_state() func tembak(): if !peluru_scene: return if is_menunduk: print("❌ Player tidak bisa menembak sambil menunduk/berjongkok!") return if is_triple_shot: for i in range(3): if is_dead: break _eksekusi_instansiasi_peluru() await get_tree().create_timer(0.1).timeout else: _eksekusi_instansiasi_peluru() func _eksekusi_instansiasi_peluru(): var p = peluru_scene.instantiate() var arah_tembakan = Vector2.ZERO var crosshair = get_tree().current_scene.find_child("Crosshair", true, false) if crosshair and crosshair.visible: arah_tembakan = (get_global_mouse_position() - muzzle.global_position).normalized() else: arah_tembakan = Vector2(visual.scale.x, 0) p.global_position = muzzle.global_position p.direction = arah_tembakan p.rotation = arah_tembakan.angle() get_tree().current_scene.add_child(p) if sfx_tembakan: sfx_tembakan.stop() sfx_tembakan.volume_db = VOLUME_TEMBAKAN_MANUAL sfx_tembakan.play() func update_animation_state(): if Engine.is_editor_hint(): return # 🚷 FIX UTAMA: Pengaman ini HARUS ditaruh paling atas sebelum pengecekan menunduk/tangga! if anim_player and anim_player.current_animation == "tusuk": return if is_menunduk or sedang_di_tangga: return var anim_suffix = "_weapon" if is_pegang_senjata else "" if velocity.x != 0 and is_on_floor(): var anim_walk = "walk" + anim_suffix if anim_player.has_animation(anim_walk): anim_player.play(anim_walk) elif is_on_floor(): var anim_idle = "idle" + anim_suffix if anim_player.has_animation(anim_idle): # Reset posisi visual for child in visual.get_children(): if child is Sprite2D: child.position = Vector2.ZERO if not (is_pegang_senjata and (child == tangan_kanan or child == tangan_kiri)): child.rotation_degrees = 0 anim_player.play(anim_idle) if is_pegang_senjata: tangan_kanan.position = Vector2(-26, 4) tangan_kanan.rotation_degrees = -68 tangan_kiri.position = Vector2(17, 4) tangan_kiri.rotation_degrees = -80 senjata.position = Vector2(46, 12) else: tangan_kanan.position = Vector2.ZERO tangan_kanan.rotation_degrees = 0 tangan_kiri.position = Vector2.ZERO tangan_kiri.rotation_degrees = 0 func proses_senjata(status: bool): if !tangan_kanan or !tangan_kiri or !senjata: return senjata.visible = status if status: tangan_kanan.position = Vector2(-26, 4) tangan_kanan.rotation_degrees = -68 tangan_kiri.position = Vector2(17, 4) tangan_kiri.rotation_degrees = -80 senjata.position = Vector2(46, 12) senjata.rotation_degrees = 0 else: tangan_kanan.position = Vector2.ZERO tangan_kanan.rotation_degrees = 0 tangan_kiri.position = Vector2.ZERO tangan_kiri.rotation_degrees = 0 senjata.position = Vector2.ZERO senjata.rotation_degrees = 0 func proses_menunduk(status: bool): is_menunduk = status if status: visual.position.y = 15 collision.scale.y = 0.5 anim_player.stop() else: visual.position.y = 0 collision.scale.y = 1.0 func reset_posisi_visual(): if is_pegang_senjata: return for child in visual.get_children(): if child is Sprite2D: child.position = Vector2.ZERO func set_di_tangga(status: bool): sedang_di_tangga = status if status and is_menunduk: proses_menunduk(false) func _on_speed_slider_value_changed(value: float): SPEED = value func eksekusi_kematian_player(): print("Player mati!") set_physics_process(false) Globaltimer.scene_terakhir_player = get_tree().current_scene.scene_file_path get_tree().change_scene_to_file("res://gameover.tscn") func tambah_hp(jumlah): if is_dead: return health += jumlah health = clamp(health, 0, 100) emit_signal("hp_berubah", health) print("💊 Darah berhasil dipulihkan! HP Player Sekarang: ", health) func aktifkan_triple_shot(durasi: float = 15.0): if is_dead: return is_triple_shot = true print("🔥 Power-Up Aktif: Tembakan 3 Peluru Beruntun!") await get_tree().create_timer(durasi).timeout is_triple_shot = false print("🔋 Durasi Triple Shot habis. Senjata kembali ke mode normal.") # --- 🥷 FUNGSI UTAMA STEALTH KILL PLAYER (TARUH DI PLAYER.GD) --- func eksekusi_stealth_kill(): if is_dead or is_menunduk or sedang_di_tangga: return set_physics_process(false) print("🥷 Player: Memulai Stealth Kill. Mengeluarkan pisau...") if sprite_pisau: sprite_pisau.visible = true if anim_player and anim_player.has_animation("tusuk"): anim_player.play("tusuk") await anim_player.animation_finished # Menunggu animasi tusuk kelar else: print("⚠️ Peringatan: Animasi bernama 'tusuk' tidak ditemukan!") if sprite_pisau: sprite_pisau.visible = false set_physics_process(true) print("🥷 Player: Stealth Kill selesai.")