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!")