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.
110 lines
3.4 KiB
110 lines
3.4 KiB
extends Node3D |
|
class_name LilguyBody |
|
|
|
const LERP_VELOCITY: float = 0.15 |
|
|
|
@export_category("Objects") |
|
@export var _character: CharacterBody3D = null |
|
@export var animation_player: AnimationPlayer = null |
|
|
|
func apply_rotation(_velocity: Vector3) -> void: |
|
var new_rotation_y = lerp_angle(rotation.y, atan2(_velocity.x, _velocity.z), LERP_VELOCITY) |
|
rotation.y = new_rotation_y |
|
|
|
func animate(_velocity: Vector3) -> void: |
|
if not animation_player: |
|
return |
|
|
|
# Don't override attack animation if it's playing |
|
if animation_player.is_playing() and animation_player.current_animation.begins_with("Attack"): |
|
return |
|
|
|
# Check if we're dashing |
|
if _character._is_dashing: |
|
if animation_player.current_animation != "Jump": |
|
_play_animation("Jump") |
|
return |
|
|
|
if not _character.is_on_floor(): |
|
if _velocity.y < 0: |
|
# Falling - use FallIdle animation |
|
_play_animation("FallIdle") |
|
else: |
|
_play_animation("Jump") |
|
return |
|
|
|
if _velocity: |
|
if _character.is_running() and _character.is_on_floor(): |
|
# Sprint animation = Run for Lilguy |
|
_play_animation("Run") |
|
return |
|
|
|
# Walk animation for normal movement |
|
_play_animation("Walk") |
|
return |
|
|
|
# Idle - use FallIdle or Idle if it exists |
|
if animation_player.has_animation("Idle"): |
|
_play_animation("Idle") |
|
else: |
|
_play_animation("FallIdle") |
|
|
|
func _play_animation(anim_name: String): |
|
if animation_player and animation_player.has_animation(anim_name): |
|
animation_player.play(anim_name) |
|
# Silently ignore if animation doesn't exist |
|
|
|
func play_attack(anim_name: String = "Attack_OneHand") -> void: |
|
if animation_player: |
|
# Play attack animation once (don't loop) |
|
if animation_player.has_animation(anim_name): |
|
animation_player.play(anim_name, -1, 1.0) |
|
animation_player.animation_set_next(anim_name, "") |
|
else: |
|
# Fallback to default if animation doesn't exist |
|
#push_warning("Animation '%s' not found, using Attack_OneHand" % anim_name) |
|
animation_player.play("Attack_OneHand", -1, 1.0) |
|
animation_player.animation_set_next("Attack_OneHand", "") |
|
|
|
## Apply hue shift to all mesh instances in the character |
|
func set_character_color(hue_shift: float) -> void: |
|
# Find all MeshInstance3D children recursively |
|
var mesh_instances = _find_mesh_instances(self) |
|
|
|
for mesh in mesh_instances: |
|
_apply_hue_to_mesh(mesh, hue_shift) |
|
|
|
## Recursively find all MeshInstance3D nodes |
|
func _find_mesh_instances(node: Node) -> Array[MeshInstance3D]: |
|
var meshes: Array[MeshInstance3D] = [] |
|
|
|
if node is MeshInstance3D: |
|
meshes.append(node) |
|
|
|
for child in node.get_children(): |
|
meshes.append_array(_find_mesh_instances(child)) |
|
|
|
return meshes |
|
|
|
## Apply hue shift to a mesh instance |
|
func _apply_hue_to_mesh(mesh_instance: MeshInstance3D, hue_shift: float) -> void: |
|
if not mesh_instance: |
|
return |
|
|
|
# Get or create material for each surface |
|
for i in range(mesh_instance.get_surface_override_material_count()): |
|
var material = mesh_instance.get_surface_override_material(i) |
|
|
|
# If no override material, get the base material and duplicate it |
|
if not material: |
|
material = mesh_instance.mesh.surface_get_material(i) |
|
if material: |
|
material = material.duplicate() |
|
mesh_instance.set_surface_override_material(i, material) |
|
|
|
# Apply hue shift if it's a StandardMaterial3D |
|
if material and material is StandardMaterial3D: |
|
var std_mat = material as StandardMaterial3D |
|
# Create a modulate color from hue |
|
var color = Color.from_hsv(hue_shift, 0.6, 1.0) |
|
std_mat.albedo_color = color
|
|
|