Files
jsc-marketplace/plugins/cockpit/hooks/companion/mood.ts
T

61 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Pad } from './types'
export const clamp = (x: number, lo = -1, hi = 1) => Math.max(lo, Math.min(hi, x))
const HOUR = 3_600_000
const MOOD_HALF_LIFE_H = 8
// 高神經質的角色情緒消退較慢:50 為基準,0→0.5 倍,100→1.5 倍
export const halfLife = (neuroticism: number) => MOOD_HALF_LIFE_H * HOUR * (0.5 + neuroticism / 100)
// 讀檔時才計算:x = B + (x − B)·0.5^(Δ/τ)
export function decay(mood: Pad, baseline: Pad, elapsedMs: number, neuroticism: number): Pad {
if (elapsedMs <= 0) return { p: mood.p, a: mood.a, d: mood.d }
const k = Math.pow(0.5, elapsedMs / halfLife(neuroticism))
return {
p: baseline.p + (mood.p - baseline.p) * k,
a: baseline.a + (mood.a - baseline.a) * k,
d: baseline.d + (mood.d - baseline.d) * k,
}
}
// 單軸每回合變化上限 ±0.3
export const TURN_CAP = 0.3
export function applyDelta(mood: Pad, delta: Pad): Pad {
const step = (x: number, dx: number) => clamp(x + clamp(dx, -TURN_CAP, TURN_CAP))
return { p: step(mood.p, delta.p), a: step(mood.a, delta.a), d: step(mood.d, delta.d) }
}
export const MOOD_EVENTS = {
thanks: { p: 0.15, a: 0.05, d: 0.05 },
scold: { p: -0.2, a: 0.15, d: -0.1 },
success: { p: 0.08, a: 0.03, d: 0.05 },
toolFail: { p: -0.08, a: 0.1, d: -0.08 },
return: { p: 0.1, a: 0.08, d: 0 },
lateNight: { p: -0.05, a: -0.1, d: 0 },
favorite: { p: 0.12, a: 0.08, d: 0.03 },
ignored: { p: -0.05, a: -0.03, d: -0.03 },
} as const satisfies Record<string, Pad>
export type MoodEvent = keyof typeof MOOD_EVENTS
// 八分區(Mehrabian 的 PAD 八分類)→ 情緒標籤與顏文字
const OCTANTS: Record<string, { label: string; kaomoji: string }> = {
'+++': { label: '興奮', kaomoji: '(≧▽≦)' },
'++-': { label: '撒嬌', kaomoji: '(๑>ᴗ<๑)' },
'+-+': { label: '放鬆', kaomoji: '(´ᴗ`)' },
'+--': { label: '溫順', kaomoji: '(︶ᴗ︶)' },
'---': { label: '無聊', kaomoji: '(-_-)' },
'--+': { label: '不屑', kaomoji: '(¬_¬)' },
'-++': { label: '生氣', kaomoji: '(╬`益´)' },
'-+-': { label: '焦慮', kaomoji: '(;´Д`)' },
}
const CALM = { label: '平靜', kaomoji: '(・ω・)' }
const CALM_RADIUS = 0.1
export function moodLabel(m: Pad): { label: string; kaomoji: string } {
if (Math.abs(m.p) < CALM_RADIUS && Math.abs(m.a) < CALM_RADIUS && Math.abs(m.d) < CALM_RADIUS) return CALM
const s = (x: number) => (x >= 0 ? '+' : '-')
return OCTANTS[s(m.p) + s(m.a) + s(m.d)]!
}