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.
113 lines
3.1 KiB
113 lines
3.1 KiB
|
7 days ago
|
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!")
|