Skip to content

Commit 31465a1

Browse files
committed
feat: 8ch 预设
1 parent 1a4512a commit 31465a1

4 files changed

Lines changed: 119 additions & 12 deletions

File tree

MaiChartManager/Front/src/views/ModManager/AquaMaiConfigurator.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ export default defineComponent({
130130
<ConfigSection key={section.path!} section={section}
131131
entryStates={props.config.entryStates!}
132132
isCommunity={communityList.value.includes(section.path!)}
133-
sectionState={props.config.sectionStates![section.path!]}/>)}
133+
sectionState={props.config.sectionStates![section.path!]}
134+
allSectionStates={props.config.sectionStates!}/>)}
134135
</div>
135136
</div>
136137
</div>

MaiChartManager/Front/src/views/ModManager/ConfigSection.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default defineComponent({
1414
section: { type: Object as PropType<Section>, required: true },
1515
entryStates: { type: Object as PropType<Record<string, IEntryState>>, required: true },
1616
sectionState: { type: Object as PropType<ISectionState>, required: true },
17+
allSectionStates: { type: Object as PropType<Record<string, ISectionState>> },
1718
isCommunity: Boolean,
1819
},
1920
setup(props, { emit }) {
@@ -55,14 +56,14 @@ export default defineComponent({
5556
</div>}
5657
<TransitionVertical>
5758
{props.sectionState.enabled && <div class="flex flex-col gap-2 mt-2">
58-
{customPanelPosition === 'top' && <CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section}/>}
59+
{customPanelPosition === 'top' && <CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section} allSectionStates={props.allSectionStates}/>}
5960
{(CustomPanel && customPanelPosition === 'override') ?
60-
<CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section}/> :
61+
<CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section} allSectionStates={props.allSectionStates}/> :
6162
!!props.section.entries?.length && <div class={["flex flex-col gap-2", ENTRY_GROUP_PADDING]}>
6263
{props.section.entries?.filter(it => !it.attribute?.hideWhenDefault || (it.attribute?.hideWhenDefault && !props.entryStates[it.path!].isDefault))
6364
.map((entry) => <ConfigEntry key={entry.path!} entry={entry} entryState={props.entryStates[entry.path!]}/>)}
6465
</div>}
65-
{customPanelPosition === 'bottom' && <CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section}/>}
66+
{customPanelPosition === 'bottom' && <CustomPanel entryStates={props.entryStates} sectionState={props.sectionState} section={props.section} allSectionStates={props.allSectionStates}/>}
6667
</div>}
6768
</TransitionVertical>
6869
</div>;

MaiChartManager/Front/src/views/ModManager/sectionPanelOverride/GameSystem.SoundRouting/index.tsx

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,82 @@
1-
import { defineComponent, PropType } from 'vue';
1+
import { computed, defineComponent, PropType } from 'vue';
22
import { IEntryState, ISectionState, Section } from "@/client/apiGen";
3-
import { Select, NumberInput } from '@munet/ui';
3+
import { Select, NumberInput, WhateverNaviBar } from '@munet/ui';
44
import { useI18n } from 'vue-i18n';
55
import { getNameForPath } from '../../utils';
66
import ConfigEntry from '../../ConfigEntry';
77
import { ENTRY_GROUP_PADDING, ENTRY_LABEL_CLASS } from '../../constants';
88

9+
const ROUTING_PREFIX = 'GameSystem.SoundRouting.';
10+
const SOUND_PREFIX = 'GameSystem.Sound.';
11+
const ENABLE_8CH_PATH = SOUND_PREFIX + 'Enable8Channel';
12+
13+
const routeKeys = [
14+
'RouteP1SpeakerLeftTo', 'RouteP1SpeakerRightTo',
15+
'RouteP1HeadphoneLeftTo', 'RouteP1HeadphoneRightTo',
16+
'RouteP2SpeakerLeftTo', 'RouteP2SpeakerRightTo',
17+
'RouteP2HeadphoneLeftTo', 'RouteP2HeadphoneRightTo',
18+
] as const;
19+
20+
interface Preset {
21+
name: string;
22+
routes: Record<typeof routeKeys[number], string>;
23+
enable8Channel: boolean;
24+
}
25+
26+
const presets: Preset[] = [
27+
{
28+
name: '2ch外放',
29+
routes: {
30+
RouteP1SpeakerLeftTo: 'P1SpeakerLeft',
31+
RouteP1SpeakerRightTo: 'P1SpeakerRight',
32+
RouteP1HeadphoneLeftTo: 'None',
33+
RouteP1HeadphoneRightTo: 'None',
34+
RouteP2SpeakerLeftTo: 'None',
35+
RouteP2SpeakerRightTo: 'None',
36+
RouteP2HeadphoneLeftTo: 'None',
37+
RouteP2HeadphoneRightTo: 'None',
38+
},
39+
enable8Channel: false,
40+
},
41+
{
42+
name: '2ch耳机',
43+
routes: {
44+
RouteP1SpeakerLeftTo: 'None',
45+
RouteP1SpeakerRightTo: 'None',
46+
RouteP1HeadphoneLeftTo: 'P1SpeakerLeft',
47+
RouteP1HeadphoneRightTo: 'P1SpeakerRight',
48+
RouteP2SpeakerLeftTo: 'None',
49+
RouteP2SpeakerRightTo: 'None',
50+
RouteP2HeadphoneLeftTo: 'None',
51+
RouteP2HeadphoneRightTo: 'None',
52+
},
53+
enable8Channel: false,
54+
},
55+
{
56+
name: '8ch',
57+
routes: {
58+
RouteP1SpeakerLeftTo: 'P1SpeakerLeft',
59+
RouteP1SpeakerRightTo: 'P1SpeakerRight',
60+
RouteP1HeadphoneLeftTo: 'P1HeadphoneLeft',
61+
RouteP1HeadphoneRightTo: 'P1HeadphoneRight',
62+
RouteP2SpeakerLeftTo: 'P2SpeakerLeft',
63+
RouteP2SpeakerRightTo: 'P2SpeakerRight',
64+
RouteP2HeadphoneLeftTo: 'P2HeadphoneLeft',
65+
RouteP2HeadphoneRightTo: 'P2HeadphoneRight',
66+
},
67+
enable8Channel: true,
68+
},
69+
];
70+
971
export default defineComponent({
1072
props: {
1173
section: { type: Object as PropType<Section>, required: true },
1274
entryStates: { type: Object as PropType<Record<string, IEntryState>>, required: true },
1375
sectionState: { type: Object as PropType<ISectionState>, required: true },
76+
allSectionStates: { type: Object as PropType<Record<string, ISectionState>> },
1477
},
1578
setup(props) {
1679
const { t } = useI18n();
17-
const PREFIX = 'GameSystem.SoundRouting.';
1880

1981
const soundChannelOptions = ['None', 'P1SpeakerLeft', 'P1SpeakerRight', 'P1HeadphoneLeft', 'P1HeadphoneRight', 'P2SpeakerLeft', 'P2SpeakerRight', 'P2HeadphoneLeft', 'P2HeadphoneRight']
2082
.map(channel => ({ label: t('mod.soundChannel.' + channel), value: channel }));
@@ -23,16 +85,55 @@ export default defineComponent({
2385
const p2Routes = ['RouteP2SpeakerLeftTo', 'RouteP2SpeakerRightTo', 'RouteP2HeadphoneLeftTo', 'RouteP2HeadphoneRightTo'];
2486
const p1Volumes = ['VolumeP1Speaker', 'VolumeP1Headphone'];
2587
const p2Volumes = ['VolumeP2Speaker', 'VolumeP2Headphone'];
26-
const knownPaths = [...p1Routes, ...p2Routes, ...p1Volumes, ...p2Volumes].map(it => PREFIX + it);
88+
const knownPaths = [...p1Routes, ...p2Routes, ...p1Volumes, ...p2Volumes].map(it => ROUTING_PREFIX + it);
2789

28-
const findEntry = (key: string) => props.section.entries?.find(it => it.path === PREFIX + key);
90+
const findEntry = (key: string) => props.section.entries?.find(it => it.path === ROUTING_PREFIX + key);
91+
92+
// 检测当前配置匹配哪个预设
93+
const activePreset = computed(() => {
94+
const enable8ChEntry = props.entryStates[ENABLE_8CH_PATH];
95+
if (!enable8ChEntry) return null;
96+
97+
for (const preset of presets) {
98+
const routesMatch = routeKeys.every(key => {
99+
const entry = props.entryStates[ROUTING_PREFIX + key];
100+
return entry && entry.value === preset.routes[key];
101+
});
102+
const channelMatch = !!enable8ChEntry.value === preset.enable8Channel;
103+
if (routesMatch && channelMatch) return preset.name;
104+
}
105+
return null;
106+
});
107+
108+
// 应用预设:只修改路由和 Enable8Channel,其他选项不动
109+
const applyPreset = (preset: Preset) => {
110+
for (const key of routeKeys) {
111+
const entry = props.entryStates[ROUTING_PREFIX + key];
112+
if (entry) entry.value = preset.routes[key];
113+
}
114+
const enable8ChEntry = props.entryStates[ENABLE_8CH_PATH];
115+
if (enable8ChEntry) enable8ChEntry.value = preset.enable8Channel;
116+
117+
// 确保 GameSystem.Sound 和 GameSystem.SoundRouting 都开着
118+
if (props.allSectionStates) {
119+
const soundState = props.allSectionStates['GameSystem.Sound'];
120+
if (soundState) soundState.enabled = true;
121+
}
122+
props.sectionState.enabled = true;
123+
};
124+
125+
const naviItems = computed(() => presets.map(preset => ({
126+
name: preset.name,
127+
selected: activePreset.value === preset.name,
128+
onClick: () => applyPreset(preset),
129+
})));
29130

30131
const renderSelect = (key: string) => {
31132
const entry = findEntry(key);
32133
if (!entry) return null;
33134
return <div class="flex gap-2 items-start">
34135
<div class={ENTRY_LABEL_CLASS}>{getNameForPath(entry.path!, entry.name!, entry.attribute?.comment?.nameZh)}</div>
35-
<Select class="w-full" v-model:value={props.entryStates[PREFIX + key].value} options={soundChannelOptions}/>
136+
<Select class="w-full" v-model:value={props.entryStates[ROUTING_PREFIX + key].value} options={soundChannelOptions}/>
36137
</div>;
37138
};
38139

@@ -41,11 +142,15 @@ export default defineComponent({
41142
if (!entry) return null;
42143
return <div class="flex gap-2 items-start">
43144
<div class={ENTRY_LABEL_CLASS}>{getNameForPath(entry.path!, entry.name!, entry.attribute?.comment?.nameZh)}</div>
44-
<NumberInput innerClass="h-42px!" v-model:value={props.entryStates[PREFIX + key].value} step={0.1} decimal={2}/>
145+
<NumberInput innerClass="h-42px!" v-model:value={props.entryStates[ROUTING_PREFIX + key].value} step={0.1} decimal={2}/>
45146
</div>;
46147
};
47148

48149
return () => <div class="flex flex-col gap-2">
150+
<div class="pl-40 flex items-center gap-2">
151+
预设:
152+
<WhateverNaviBar items={naviItems.value}/>
153+
</div>
49154
<div class={["grid grid-cols-1 min-[500px]:grid-cols-2 gap-y-12px gap-x-16px", ENTRY_GROUP_PADDING]}>
50155
<div class="flex flex-col gap-2">
51156
{p1Routes.map(renderSelect)}

0 commit comments

Comments
 (0)