chobit/shared/godot/avatar/attention_reactor.gd
Claude Code 7067b6dded refactor(shared): ♻️ Improve shared utility structure for better maintainability
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-03-28 14:55:37 -07:00

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