73 lines
1.9 KiB
GDScript
73 lines
1.9 KiB
GDScript
extends Node
|
|
## Triggers reactive expressions based on user attention state.
|
|
## Only active during idle conversation state — conversation emotions take priority.
|
|
|
|
const REACTION_COOLDOWN: float = 5.0
|
|
const SUSTAINED_LOOK_THRESHOLD: float = 3.0
|
|
const WELCOME_BACK_THRESHOLD: float = 5.0
|
|
|
|
var _current_attention: String = "absent"
|
|
var _conversation_state: String = "idle"
|
|
var _cooldown_timer: float = 0.0
|
|
var _attention_timer: float = 0.0
|
|
var _absent_duration: float = 0.0
|
|
|
|
|
|
func setup() -> void:
|
|
EventBus.attention_changed.connect(_on_attention_changed)
|
|
EventBus.state_changed.connect(_on_state_changed)
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if _cooldown_timer > 0.0:
|
|
_cooldown_timer = maxf(0.0, _cooldown_timer - delta)
|
|
|
|
if _current_attention == "looking":
|
|
_attention_timer += delta
|
|
_check_sustained_look()
|
|
elif _current_attention == "absent":
|
|
_absent_duration += delta
|
|
|
|
|
|
func _on_state_changed(_from_state: String, to_state: String) -> void:
|
|
_conversation_state = to_state
|
|
|
|
|
|
func _on_attention_changed(state: String, _confidence: float) -> void:
|
|
var prev := _current_attention
|
|
_current_attention = state
|
|
|
|
if not _can_react():
|
|
return
|
|
|
|
# Transition reactions
|
|
if prev != "looking" and state == "looking":
|
|
if prev == "absent" and _absent_duration >= WELCOME_BACK_THRESHOLD:
|
|
_react("surprised")
|
|
else:
|
|
_react("happy")
|
|
_attention_timer = 0.0
|
|
|
|
elif prev == "looking" and state != "looking":
|
|
_react("neutral")
|
|
|
|
# Reset timers on state change
|
|
if state != "absent":
|
|
_absent_duration = 0.0
|
|
if state != "looking":
|
|
_attention_timer = 0.0
|
|
|
|
|
|
func _check_sustained_look() -> void:
|
|
if _attention_timer >= SUSTAINED_LOOK_THRESHOLD and _can_react():
|
|
_react("relaxed")
|
|
_attention_timer = 0.0
|
|
|
|
|
|
func _can_react() -> bool:
|
|
return _conversation_state == "idle" and _cooldown_timer <= 0.0
|
|
|
|
|
|
func _react(emotion: String) -> void:
|
|
EventBus.emotion_changed.emit(emotion)
|
|
_cooldown_timer = REACTION_COOLDOWN
|