135 lines
4.7 KiB
GDScript
135 lines
4.7 KiB
GDScript
extends Node3D
|
|
|
|
const HUNGER_CUE_BASE_Y := 2.05
|
|
const FED_RESPONSE_BASE_Y := 1.8
|
|
|
|
@onready var head_root: Node3D = $HeadRoot
|
|
@onready var tail_root: Node3D = $TailRoot
|
|
@onready var hunger_cue_root: Node3D = $HungerCueRoot
|
|
@onready var fed_response_root: Node3D = $FedResponseRoot
|
|
@onready var leg_front_left: Node3D = $LegFrontLeft
|
|
@onready var leg_front_right: Node3D = $LegFrontRight
|
|
@onready var leg_back_left: Node3D = $LegBackLeft
|
|
@onready var leg_back_right: Node3D = $LegBackRight
|
|
@onready var body: Node3D = $Body
|
|
@onready var fluff_front: Node3D = $FluffFront
|
|
@onready var fluff_back: Node3D = $FluffBack
|
|
@onready var horn_left: Node3D = $HeadRoot/HornLeft
|
|
@onready var horn_right: Node3D = $HeadRoot/HornRight
|
|
@onready var beard: Node3D = $HeadRoot/Beard
|
|
|
|
var idle_phase := 0.0
|
|
var is_hungry := false
|
|
var is_moving := false
|
|
var fed_tween: Tween
|
|
var head_base_position: Vector3
|
|
var visual_base_position: Vector3
|
|
var body_base_scale: Vector3
|
|
var fluff_front_base_scale: Vector3
|
|
var fluff_back_base_scale: Vector3
|
|
var _animal_definition: AnimalDefinition
|
|
var _presentation_parameters: Dictionary = {}
|
|
|
|
|
|
func _ready() -> void:
|
|
head_base_position = head_root.position
|
|
visual_base_position = position
|
|
body_base_scale = body.scale
|
|
fluff_front_base_scale = fluff_front.scale
|
|
fluff_back_base_scale = fluff_back.scale
|
|
_apply_definition_presentation()
|
|
reset_transient_feedback()
|
|
|
|
|
|
func apply_animal_definition(
|
|
definition: AnimalDefinition, presentation_parameters: Dictionary = {}
|
|
) -> bool:
|
|
if definition == null or not definition.validate().is_empty():
|
|
return false
|
|
_animal_definition = definition
|
|
_presentation_parameters = presentation_parameters.duplicate(true)
|
|
if is_node_ready():
|
|
_apply_definition_presentation()
|
|
return true
|
|
|
|
|
|
func _apply_definition_presentation() -> void:
|
|
if _animal_definition == null:
|
|
return
|
|
scale = _parameter_vector("visual_scale", Vector3.ONE)
|
|
body.scale = body_base_scale * _parameter_vector("body_scale", Vector3.ONE)
|
|
fluff_front.scale = fluff_front_base_scale * _parameter_vector("fluff_scale", Vector3.ONE)
|
|
fluff_back.scale = fluff_back_base_scale * _parameter_vector("fluff_scale", Vector3.ONE)
|
|
horn_left.visible = bool(_presentation_parameters.get("show_horns", true))
|
|
horn_right.visible = bool(_presentation_parameters.get("show_horns", true))
|
|
beard.visible = bool(_presentation_parameters.get("show_beard", true))
|
|
|
|
|
|
func _parameter_vector(key: String, fallback: Vector3) -> Vector3:
|
|
var value: Variant = _presentation_parameters.get(key, fallback)
|
|
return value as Vector3 if value is Vector3 else fallback
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
idle_phase = fmod(idle_phase + delta, TAU)
|
|
var breath := sin(idle_phase * 1.7) * 0.025
|
|
var hungry_drop := -0.12 if is_hungry else 0.0
|
|
var walking_bob := absf(sin(idle_phase * 5.4)) * 0.045 if is_moving else 0.0
|
|
position.y = visual_base_position.y + walking_bob
|
|
head_root.position.y = head_base_position.y + breath + hungry_drop
|
|
head_root.rotation_degrees.x = (
|
|
8.0 + sin(idle_phase * 1.2) * 2.0 if is_hungry else -3.0 + sin(idle_phase * 1.4) * 2.5
|
|
)
|
|
tail_root.rotation_degrees.z = sin(idle_phase * 3.1) * (4.0 if is_hungry else 13.0)
|
|
var stride := sin(idle_phase * 5.4) * 14.0 if is_moving else 0.0
|
|
leg_front_left.rotation_degrees.x = stride
|
|
leg_back_right.rotation_degrees.x = stride
|
|
leg_front_right.rotation_degrees.x = -stride
|
|
leg_back_left.rotation_degrees.x = -stride
|
|
if hunger_cue_root.visible:
|
|
hunger_cue_root.position.y = HUNGER_CUE_BASE_Y + sin(idle_phase * 2.2) * 0.07
|
|
|
|
|
|
func set_hungry(value: bool) -> void:
|
|
is_hungry = value
|
|
hunger_cue_root.visible = value
|
|
|
|
|
|
func set_moving(value: bool) -> void:
|
|
is_moving = value
|
|
if not value:
|
|
position = visual_base_position
|
|
leg_front_left.rotation_degrees.x = 0.0
|
|
leg_front_right.rotation_degrees.x = 0.0
|
|
leg_back_left.rotation_degrees.x = 0.0
|
|
leg_back_right.rotation_degrees.x = 0.0
|
|
|
|
|
|
func reset_transient_feedback() -> void:
|
|
if fed_tween != null:
|
|
fed_tween.kill()
|
|
fed_tween = null
|
|
fed_response_root.visible = false
|
|
fed_response_root.position.y = FED_RESPONSE_BASE_Y
|
|
fed_response_root.scale = Vector3(0.2, 0.2, 0.2)
|
|
|
|
|
|
func play_fed_response() -> void:
|
|
reset_transient_feedback()
|
|
fed_response_root.visible = true
|
|
fed_tween = create_tween()
|
|
fed_tween.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
fed_tween.tween_property(fed_response_root, "scale", Vector3.ONE, 0.34)
|
|
fed_tween.parallel().tween_property(
|
|
fed_response_root, "position:y", FED_RESPONSE_BASE_Y + 0.22, 0.34
|
|
)
|
|
fed_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
fed_tween.tween_interval(0.7)
|
|
fed_tween.tween_property(fed_response_root, "scale", Vector3(0.2, 0.2, 0.2), 0.42)
|
|
fed_tween.finished.connect(_on_fed_response_finished)
|
|
|
|
|
|
func _on_fed_response_finished() -> void:
|
|
fed_response_root.visible = false
|
|
fed_tween = null
|