Skip to content

Commit 020ba16

Browse files
committed
feat(li-mode): 引入里模式阶段系统
引入了与“强度”正交的“阶段”(平然/羞耻/欲望)维度,用于控制 NPC 在亲密场景下的心理态度和行为倾向。 主要变更: - **数据模型**: 在游戏设置和 NPC 结构中增加了 `子纪元里模式阶段` 字段。 - **Prompt**: 新增 `构建里模式阶段注入` 函数,根据当前阶段(平然/羞耻/欲望)向系统提示词注入不同的 NPC 行为引导规则。 - **UI**: 在新建游戏向导和游戏设置中添加了阶段选择器。 - **显示**: TopBar 上的里模式徽章现在会显示当前阶段和强度 (如 "羞耻·暧昧")。 - **设计文档**: 添加了 `li-mode-stages.md` 方案文档。
1 parent b066db3 commit 020ba16

12 files changed

Lines changed: 375 additions & 11 deletions

File tree

App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ const App: React.FC = () => {
768768
eraId={state.currentEra}
769769
启用子纪元里模式={state.gameConfig?.启用子纪元里模式}
770770
子纪元里模式强度={state.gameConfig?.子纪元里模式强度}
771+
子纪元里模式阶段={state.gameConfig?.子纪元里模式阶段}
771772
onLiModeIntensityChange={(eraId, intensity) => {
772773
const prev = state.gameConfig?.子纪元里模式强度 || {};
773774
actions.saveGameSettings({ ...state.gameConfig, 子纪元里模式强度: { ...prev, [eraId]: intensity } } as any);

components/features/NewGame/NewGameWizardContent.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export const NewGameWizardContent: React.FC<NewGameWizardContentProps> = ({ wiza
233233
setShowCustomBackground, setShowCustomTalent, setShowCustomPresetEditor,
234234
子纪元里模式开启, 设置子纪元里模式开启,
235235
子纪元里模式强度, 设置子纪元里模式强度,
236+
子纪元里模式阶段, 设置子纪元里模式阶段,
236237
古代体系选择, 设置古代体系选择,
237238
} = wizard;
238239

@@ -435,6 +436,37 @@ export const NewGameWizardContent: React.FC<NewGameWizardContentProps> = ({ wiza
435436
})}
436437
</div>
437438
)}
439+
440+
{/* 阶段选择器 — 开启时展示 */}
441+
{子纪元里模式开启 && 有里模式数据 && (
442+
<div className="mt-2 flex gap-2">
443+
<div className="text-[11px] text-gray-500 self-center mr-1">阶段:</div>
444+
{(['平然', '羞耻', '欲望'] as const).map((s) => {
445+
const isActive = 子纪元里模式阶段 === s;
446+
const colors = {
447+
'平然': isActive ? 'border-green-500/60 bg-green-500/15 text-green-400' : 'border-gray-700 bg-black/25 text-gray-500 hover:border-gray-500',
448+
'羞耻': isActive ? 'border-yellow-500/60 bg-yellow-500/15 text-yellow-400' : 'border-gray-700 bg-black/25 text-gray-500 hover:border-gray-500',
449+
'欲望': isActive ? 'border-purple-500/60 bg-purple-500/15 text-purple-400' : 'border-gray-700 bg-black/25 text-gray-500 hover:border-gray-500',
450+
};
451+
const descs = {
452+
'平然': '视作日常,自然接受',
453+
'羞耻': '害羞但不抗拒',
454+
'欲望': '主动引导,渴望亲密',
455+
};
456+
return (
457+
<button
458+
key={s}
459+
type="button"
460+
title={descs[s]}
461+
onClick={() => 设置子纪元里模式阶段(s)}
462+
className={`flex-1 rounded-md border px-3 py-2 text-xs font-bold transition-all ${colors[s]}`}
463+
>
464+
{s}
465+
</button>
466+
);
467+
})}
468+
</div>
469+
)}
438470
</div>
439471

440472
{/* 世界观预设 - moved up for quick-start */}

components/features/NewGame/useNewGameWizardState.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from '../../../utils/openingConfig';
2727
import { 默认境界母板提示词 } from '../../../prompts/runtime/fandom';
2828
import { LiModeIntensity } from '../../../prompts/runtime/eraLiMode';
29+
import type { LiModeStage } from '../../../models/eraTheme/types';
2930
import { 设置键 } from '../../../utils/settingsSchema';
3031
import { 内置时代配置, 获取时代背景 } from '../../../models/system';
3132
import { 时代主题方案列表, 获取时代主题方案 } from '../../../models/eraTheme';
@@ -324,6 +325,7 @@ export function useNewGameWizardState({ onComplete, onCancel, loading, currentEr
324325
const [成人内容开启, 设置成人内容开启] = useState(false);
325326
const [子纪元里模式开启, 设置子纪元里模式开启] = useState(true);
326327
const [子纪元里模式强度, 设置子纪元里模式强度] = useState<LiModeIntensity>('暧昧');
328+
const [子纪元里模式阶段, 设置子纪元里模式阶段] = useState<LiModeStage>('羞耻');
327329
const [古代体系选择, 设置古代体系选择] = useState<体系类型>('武侠');
328330

329331
// Search & filter
@@ -1168,7 +1170,10 @@ export function useNewGameWizardState({ onComplete, onCancel, loading, currentEr
11681170
const prevIntensity = typeof savedGameSettings.子纪元里模式强度 === 'object'
11691171
? savedGameSettings.子纪元里模式强度
11701172
: {};
1171-
await dbService.保存设置(设置键.游戏设置, { ...savedGameSettings, 时代配置ID: savedEra, 启用子纪元里模式: { ...prev, [savedEra]: 子纪元里模式开启 }, 子纪元里模式强度: { ...prevIntensity, [savedEra]: 子纪元里模式强度 }, 古代体系选择 });
1173+
const prevStage = typeof savedGameSettings.子纪元里模式阶段 === 'object'
1174+
? savedGameSettings.子纪元里模式阶段
1175+
: {};
1176+
await dbService.保存设置(设置键.游戏设置, { ...savedGameSettings, 时代配置ID: savedEra, 启用子纪元里模式: { ...prev, [savedEra]: 子纪元里模式开启 }, 子纪元里模式强度: { ...prevIntensity, [savedEra]: 子纪元里模式强度 }, 子纪元里模式阶段: { ...prevStage, [savedEra]: 子纪元里模式阶段 }, 古代体系选择 });
11721177
} catch (error) {
11731178
console.error('保存游戏设置失败', error);
11741179
}
@@ -1196,6 +1201,7 @@ export function useNewGameWizardState({ onComplete, onCancel, loading, currentEr
11961201
成人内容开启, 设置成人内容开启,
11971202
子纪元里模式开启, 设置子纪元里模式开启,
11981203
子纪元里模式强度, 设置子纪元里模式强度,
1204+
子纪元里模式阶段, 设置子纪元里模式阶段,
11991205
古代体系选择, 设置古代体系选择,
12001206
customTalent, setCustomTalent, showCustomTalent, setShowCustomTalent,
12011207
正在编辑天赋名, set正在编辑天赋名,

components/features/Settings/GameSettings.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,39 @@ const GameSettings: React.FC<Props> = ({ settings, onSave, currentEra, onEraChan
538538
</button>
539539
))}
540540
</div>
541+
542+
{/* 阶段选择器 */}
543+
<div className="text-xs text-gray-400 mb-2 mt-3">里模式阶段 — 控制 NPC 对亲密行为的心理态度</div>
544+
<div className="flex gap-2">
545+
{(['平然', '羞耻', '欲望'] as const).map((stage) => {
546+
const currentStage = (form as any).子纪元里模式阶段?.[intensityKey] ?? '羞耻';
547+
const isActive = currentStage === stage;
548+
const colors = {
549+
'平然': isActive ? 'border-green-500/50 bg-green-500/10 text-green-400 font-bold' : 'border-gray-800 bg-black/25 text-gray-500 hover:border-gray-600',
550+
'羞耻': isActive ? 'border-yellow-500/50 bg-yellow-500/10 text-yellow-400 font-bold' : 'border-gray-800 bg-black/25 text-gray-500 hover:border-gray-600',
551+
'欲望': isActive ? 'border-purple-500/50 bg-purple-500/10 text-purple-400 font-bold' : 'border-gray-800 bg-black/25 text-gray-500 hover:border-gray-600',
552+
};
553+
const descs = {
554+
'平然': '视作日常,自然接受',
555+
'羞耻': '害羞但不抗拒',
556+
'欲望': '主动引导,渴望亲密',
557+
};
558+
return (
559+
<button
560+
key={stage}
561+
type="button"
562+
onClick={() => {
563+
const prev = (form as any).子纪元里模式阶段 || {};
564+
实时应用更新({ 子纪元里模式阶段: { ...prev, [intensityKey]: stage } } as any);
565+
}}
566+
className={`flex-1 rounded-lg border px-3 py-2 text-center text-sm transition-all ${colors[stage]}`}
567+
title={descs[stage]}
568+
>
569+
{stage}
570+
</button>
571+
);
572+
})}
573+
</div>
541574
</div>
542575
);
543576
})()}

components/layout/TopBar.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface Props {
1313
eraId?: string | null;
1414
启用子纪元里模式?: Record<string, boolean>;
1515
子纪元里模式强度?: Record<string, string>;
16+
子纪元里模式阶段?: Record<string, string>;
1617
onLiModeIntensityChange?: (eraId: string, intensity: '微暗' | '暧昧' | '露骨') => void;
1718
}
1819

@@ -182,7 +183,7 @@ const toGameMinuteValue = (time: { year: number; month: number; day: number; hou
182183
return (((time.year * 12 + time.month) * 31 + time.day) * 24 + time.hour) * 60 + time.minute;
183184
};
184185

185-
const TopBar: React.FC<Props> = ({ 环境, 游戏初始时间, timeFormat, festivals = [], visualConfig, eraId, 启用子纪元里模式, 子纪元里模式强度, onLiModeIntensityChange }) => {
186+
const TopBar: React.FC<Props> = ({ 环境, 游戏初始时间, timeFormat, festivals = [], visualConfig, eraId, 启用子纪元里模式, 子纪元里模式强度, 子纪元里模式阶段, onLiModeIntensityChange }) => {
186187
const 文案 = useUIText();
187188
const [mobileLeftMode, setMobileLeftMode] = useState<'weather' | 'environment'>('weather');
188189
const [mobileRightMode, setMobileRightMode] = useState<'journey' | 'festival'>('journey');
@@ -294,9 +295,9 @@ const TopBar: React.FC<Props> = ({ 环境, 游戏初始时间, timeFormat, festi
294295
const perEraEnabled = 启用子纪元里模式?.[eraId];
295296
if (perEraEnabled === false) return null;
296297
const intensity = 子纪元里模式强度?.[eraId] as '微暗' | '暧昧' | '露骨' | undefined;
297-
const displayIntensity = intensity || '露骨';
298-
return { eraId, intensity: displayIntensity };
299-
}, [eraId, 启用子纪元里模式, 子纪元里模式强度]);
298+
const stage = 子纪元里模式阶段?.[eraId] as '平然' | '羞耻' | '欲望' | undefined;
299+
return { eraId, intensity: intensity || '露骨', stage: stage || '羞耻' };
300+
}, [eraId, 启用子纪元里模式, 子纪元里模式强度, 子纪元里模式阶段]);
300301

301302
const toggleFullScreen = () => {
302303
if (!document.fullscreenElement) {
@@ -475,7 +476,7 @@ const TopBar: React.FC<Props> = ({ 环境, 游戏初始时间, timeFormat, festi
475476
<div className="relative">
476477
<TopItem
477478
label="里"
478-
value={里模式状态.intensity}
479+
value={`${里模式状态.stage}·${里模式状态.intensity}`}
479480
visualConfig={visualConfig}
480481
isExpanded={liIntensityOpen}
481482
onClick={() => {

0 commit comments

Comments
 (0)