feat(idle_companion): Aparición aleatoria 45s-3min

- Tiempo de aparición ahora es aleatorio entre 45s y 3 minutos
- Cada aparición genera nuevo umbral para la siguiente
- Mantiene el efecto sorpresa

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2025-12-31 21:02:16 +01:00
parent 092671adda
commit 0125e2ca3e

View file

@ -32,12 +32,18 @@
//! //!
//! ## Personalización //! ## Personalización
//! Los tiempos son configurables mediante constantes en State: //! Los tiempos son configurables mediante constantes en State:
//! - IDLE_THRESHOLD_MS: Tiempo de inactividad para aparecer (15s) //! - IDLE_MIN_MS: Tiempo MÍNIMO de inactividad para aparecer (45s)
//! - IDLE_RANDOM_MAX_MS: Tiempo adicional aleatorio (0-135s, total 45s-3min)
//! - PEEK_DURATION_MS: Duración del asomarse (4s) //! - PEEK_DURATION_MS: Duración del asomarse (4s)
//! - WATCH_DURATION_MS: Duración mirando alrededor (3s) //! - WATCH_DURATION_MS: Duración mirando alrededor (3s)
//! - HIDE_DURATION_MS: Duración al esconderse (1s) //! - HIDE_DURATION_MS: Duración al esconderse (1s)
//! - PANIC_HIDE_DURATION_MS: Duración del salto de pánico (0.2s) //! - PANIC_HIDE_DURATION_MS: Duración del salto de pánico (0.2s)
//! - RELOCATE_DELAY_MS: Pausa antes de reaparecer (5s) //! - RELOCATE_DELAY_MS: Pausa antes de reaparecer (5s)
//!
//! ## Comportamiento de sorpresa
//! El gatito aparece de forma impredecible entre 45 segundos y 3 minutos
//! de inactividad. Cada aparición genera un nuevo tiempo aleatorio para
//! la siguiente, manteniendo el efecto sorpresa.
const std = @import("std"); const std = @import("std");
const Context = @import("../core/context.zig").Context; const Context = @import("../core/context.zig").Context;
@ -96,11 +102,18 @@ pub const State = struct {
/// Semilla para números pseudo-aleatorios (LCG simple) /// Semilla para números pseudo-aleatorios (LCG simple)
random_seed: u32 = 12345, random_seed: u32 = 12345,
/// Tiempo de inactividad necesario para la próxima aparición (aleatorio)
next_appear_threshold: i64 = 45_000,
// ========================================================================= // =========================================================================
// Constantes de tiempo (personalizables) // Constantes de tiempo (personalizables)
// ========================================================================= // =========================================================================
/// Tiempo de inactividad para aparecer (ms) /// Tiempo MÍNIMO de inactividad para aparecer (ms)
pub const IDLE_MIN_MS: i64 = 45_000; // 45 segundos
/// Tiempo MÁXIMO adicional aleatorio (ms)
pub const IDLE_RANDOM_MAX_MS: i64 = 135_000; // +0 a 135s = total 45s-3min
/// Tiempo de inactividad para aparecer (ms) - LEGACY, ahora aleatorio
pub const IDLE_THRESHOLD_MS: i64 = 15_000; pub const IDLE_THRESHOLD_MS: i64 = 15_000;
/// Duración del estado peeking (ms) /// Duración del estado peeking (ms)
pub const PEEK_DURATION_MS: i64 = 4_000; pub const PEEK_DURATION_MS: i64 = 4_000;
@ -124,6 +137,13 @@ pub const State = struct {
return @as(f32, @floatFromInt(self.nextRandom() % 10000)) / 10000.0; return @as(f32, @floatFromInt(self.nextRandom() % 10000)) / 10000.0;
} }
/// Genera el próximo tiempo de inactividad necesario para aparecer
/// Resultado: entre IDLE_MIN_MS y IDLE_MIN_MS + IDLE_RANDOM_MAX_MS
fn generateNextAppearThreshold(self: *State) void {
const random_extra = self.nextRandom() % @as(u32, @intCast(IDLE_RANDOM_MAX_MS));
self.next_appear_threshold = IDLE_MIN_MS + @as(i64, random_extra);
}
/// Elige un panel y posición aleatorios /// Elige un panel y posición aleatorios
fn pickRandomLocation(self: *State, num_panels: usize) void { fn pickRandomLocation(self: *State, num_panels: usize) void {
if (num_panels == 0) return; if (num_panels == 0) return;
@ -164,7 +184,8 @@ pub const State = struct {
// Máquina de estados // Máquina de estados
switch (self.anim_state) { switch (self.anim_state) {
.hidden => { .hidden => {
if (idle_time >= IDLE_THRESHOLD_MS and num_panels > 0) { // Aparición aleatoria: usa threshold variable (45s - 3min)
if (idle_time >= self.next_appear_threshold and num_panels > 0) {
self.pickRandomLocation(num_panels); self.pickRandomLocation(num_panels);
self.anim_state = .peeking; self.anim_state = .peeking;
self.state_start_time = now; self.state_start_time = now;
@ -231,6 +252,8 @@ pub const State = struct {
self.state_start_time = now + RELOCATE_DELAY_MS; self.state_start_time = now + RELOCATE_DELAY_MS;
self.appear_progress = 0; self.appear_progress = 0;
self.panic_jump = false; self.panic_jump = false;
// Generar nuevo tiempo aleatorio para próxima aparición
self.generateNextAppearThreshold();
} }
}, },
} }