You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
3.1 KiB
112 lines
3.1 KiB
extends BaseEnemy |
|
class_name PracticeDummy |
|
|
|
## A stationary practice dummy for testing combat |
|
## Cannot move or attack - just takes damage and shows health |
|
|
|
## Visual mesh reference |
|
@onready var _mesh: MeshInstance3D = null |
|
## Health label above dummy |
|
@onready var _health_label: Label3D = null |
|
## Original material for hit flash effect |
|
var _original_material: Material = null |
|
## Hit flash effect |
|
var _hit_flash_timer: float = 0.0 |
|
const HIT_FLASH_DURATION: float = 0.2 |
|
|
|
func _ready(): |
|
super._ready() |
|
|
|
# Practice dummy should not be aggressive |
|
is_aggressive = false |
|
|
|
# Auto-find mesh and health label |
|
_mesh = get_node_or_null("Mesh") |
|
_health_label = get_node_or_null("HealthLabel") |
|
|
|
# Store original material for flash effect |
|
if _mesh: |
|
_original_material = _mesh.get_surface_override_material(0) |
|
if not _original_material and _mesh.mesh: |
|
_original_material = _mesh.mesh.surface_get_material(0) |
|
|
|
# Update initial health display |
|
_update_health_display() |
|
|
|
# Connect health change to update display |
|
health_changed.connect(_on_health_display_changed) |
|
|
|
func _process(delta): |
|
# Handle hit flash timer |
|
if _hit_flash_timer > 0: |
|
_hit_flash_timer -= delta |
|
if _hit_flash_timer <= 0: |
|
_reset_material() |
|
|
|
func _physics_process(delta): |
|
# Don't call super._physics_process since we don't move |
|
# Just apply gravity |
|
if not is_on_floor(): |
|
velocity.y -= ProjectSettings.get_setting("physics/3d/default_gravity") * delta |
|
move_and_slide() |
|
|
|
## Update health display |
|
func _update_health_display(): |
|
if _health_label: |
|
_health_label.text = "HP: %d/%d" % [int(current_health), int(max_health)] |
|
|
|
func _on_health_display_changed(_old_health: float, _new_health: float): |
|
_update_health_display() |
|
|
|
## Override hurt animation to flash red |
|
@rpc("any_peer", "call_local", "reliable") |
|
func _play_hurt_animation(): |
|
if _mesh: |
|
_flash_red() |
|
|
|
## Flash the dummy red when hit |
|
func _flash_red(): |
|
if not _mesh: |
|
return |
|
|
|
_hit_flash_timer = HIT_FLASH_DURATION |
|
|
|
# Create red material |
|
var red_material = StandardMaterial3D.new() |
|
red_material.albedo_color = Color(1.5, 0.3, 0.3) # Bright red |
|
|
|
# Copy properties from original if it exists |
|
if _original_material and _original_material is StandardMaterial3D: |
|
var orig = _original_material as StandardMaterial3D |
|
red_material.metallic = orig.metallic |
|
red_material.roughness = orig.roughness |
|
red_material.albedo_texture = orig.albedo_texture |
|
|
|
_mesh.set_surface_override_material(0, red_material) |
|
|
|
## Reset to original material |
|
func _reset_material(): |
|
if _mesh and _original_material: |
|
_mesh.set_surface_override_material(0, _original_material.duplicate()) |
|
|
|
## Override death to just hide the mesh |
|
func _on_enemy_died(killer_id: int): |
|
super._on_enemy_died(killer_id) |
|
|
|
# Hide mesh when dead |
|
if _mesh: |
|
_mesh.visible = false |
|
|
|
print("[PracticeDummy] Killed by player ", killer_id, ". Respawning in ", respawn_delay, " seconds...") |
|
|
|
## Override respawn to show mesh again |
|
func _on_enemy_respawned(): |
|
super._on_enemy_respawned() |
|
|
|
# Show mesh when respawned |
|
if _mesh: |
|
_mesh.visible = true |
|
_reset_material() |
|
|
|
_update_health_display() |
|
print("[PracticeDummy] Respawned!")
|
|
|