Files
Genarrative/src/data/affinityLevels.ts
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

131 lines
3.7 KiB
TypeScript

import type { RoleRelationState } from '../types';
export type AffinityLevelId =
| 'hostile'
| 'guarded'
| 'eased'
| 'friendly'
| 'trusted'
| 'close';
export type AffinityLevelMeta = {
id: AffinityLevelId;
label: string;
minAffinity: number;
markerAffinity: number;
nextAffinity: number | null;
description: string;
accentClassName: string;
relationStance: RoleRelationState['stance'];
};
export const AFFINITY_PROGRESS_MIN = -40;
export const AFFINITY_PROGRESS_MAX = 90;
export const AFFINITY_LEVELS: AffinityLevelMeta[] = [
{
id: 'hostile',
label: '敌对',
minAffinity: Number.NEGATIVE_INFINITY,
markerAffinity: AFFINITY_PROGRESS_MIN,
nextAffinity: 0,
description:
'好感落入负值区间后,会按敌对关系处理,靠近时通常直接进入对战。',
accentClassName: 'border-rose-300/28 bg-rose-500/14 text-rose-100',
relationStance: 'hostile',
},
{
id: 'guarded',
label: '戒备',
minAffinity: 0,
markerAffinity: 0,
nextAffinity: 15,
description: '对方仍保持明显距离,只会给出谨慎而有限的回应。',
accentClassName: 'border-white/12 bg-white/8 text-zinc-100',
relationStance: 'guarded',
},
{
id: 'eased',
label: '缓和',
minAffinity: 15,
markerAffinity: 15,
nextAffinity: 30,
description:
'戒备已经开始松动,愿意正常交流,也会试探性配合你的节奏。',
accentClassName: 'border-sky-300/20 bg-sky-500/10 text-sky-100',
relationStance: 'neutral',
},
{
id: 'friendly',
label: '友善',
minAffinity: 30,
markerAffinity: 30,
nextAffinity: 60,
description:
'态度明显友善了许多,愿意配合行动,也会给出更真诚的反馈。',
accentClassName:
'border-emerald-300/20 bg-emerald-500/10 text-emerald-100',
relationStance: 'cooperative',
},
{
id: 'trusted',
label: '信任',
minAffinity: 60,
markerAffinity: 60,
nextAffinity: 90,
description: '双方已经建立稳定信任,对方更愿意分享想法、资源和立场。',
accentClassName: 'border-amber-300/20 bg-amber-500/10 text-amber-100',
relationStance: 'bonded',
},
{
id: 'close',
label: '深交',
minAffinity: 90,
markerAffinity: 90,
nextAffinity: null,
description:
'关系已经非常亲近,对方几乎把你视作可以托付后背的自己人。',
accentClassName: 'border-rose-300/22 bg-rose-500/12 text-rose-100',
relationStance: 'bonded',
},
];
export const DEFAULT_AFFINITY_LEVEL = AFFINITY_LEVELS[0]!;
export const AFFINITY_PROGRESS_MARKERS = AFFINITY_LEVELS.map((level) => ({
value: level.markerAffinity,
label: level.label,
}));
export const AFFINITY_BACKSTORY_CHAPTER_THRESHOLDS = [
getAffinityLevelMetaById('eased').minAffinity,
getAffinityLevelMetaById('friendly').minAffinity,
getAffinityLevelMetaById('trusted').minAffinity,
getAffinityLevelMetaById('close').minAffinity,
] as const satisfies readonly [number, number, number, number];
export const DEFAULT_PRIVATE_CHAT_UNLOCK_AFFINITY =
getAffinityLevelMetaById('trusted').minAffinity;
export function getAffinityLevelMetaById(levelId: AffinityLevelId) {
const level = AFFINITY_LEVELS.find((entry) => entry.id === levelId);
if (!level) {
throw new Error(`Unknown affinity level id: ${levelId}`);
}
return level;
}
export function getAffinityLevelMeta(affinity: number) {
return (
[...AFFINITY_LEVELS]
.reverse()
.find((level) => affinity >= level.minAffinity) ?? DEFAULT_AFFINITY_LEVEL
);
}
export function resolveRelationStanceFromAffinity(
affinity: number,
): RoleRelationState['stance'] {
return getAffinityLevelMeta(affinity).relationStance;
}