Replace E effect cycling with effect selection menu
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
<p><b>M:</b> Mute/unmute</p>
|
||||
<p><b>Shift+M:</b> Toggle stereo/mono output</p>
|
||||
<p><b>! (Shift+1):</b> Toggle loopback monitor</p>
|
||||
<p><b>E:</b> Cycle voice effect</p>
|
||||
<p><b>E:</b> Select voice effect</p>
|
||||
<p><b>Dash or Equals:</b> Lower/raise active effect value</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Maintainer-controlled web client version.
|
||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||
window.CHGRID_WEB_VERSION = "2026.02.21 R80";
|
||||
window.CHGRID_WEB_VERSION = "2026.02.21 R81";
|
||||
|
||||
@@ -111,6 +111,13 @@ export class AudioEngine {
|
||||
return EFFECT_SEQUENCE[this.effectIndex];
|
||||
}
|
||||
|
||||
setOutboundEffect(effectId: EffectId): { id: EffectId; label: string } {
|
||||
const nextIndex = EFFECT_SEQUENCE.findIndex((effect) => effect.id === effectId);
|
||||
this.effectIndex = nextIndex >= 0 ? nextIndex : this.effectIndex;
|
||||
this.rebuildOutboundEffectGraph();
|
||||
return EFFECT_SEQUENCE[this.effectIndex];
|
||||
}
|
||||
|
||||
getCurrentEffect(): { id: EffectId; label: string; value: number; defaultValue: number } {
|
||||
const effect = EFFECT_SEQUENCE[this.effectIndex];
|
||||
return {
|
||||
|
||||
@@ -1092,6 +1092,7 @@ function disconnect(): void {
|
||||
state.editingPropertyKey = null;
|
||||
state.itemPropertyOptionValues = [];
|
||||
state.itemPropertyOptionIndex = 0;
|
||||
state.effectSelectIndex = 0;
|
||||
pendingEscapeDisconnect = false;
|
||||
|
||||
connecting = false;
|
||||
@@ -1327,8 +1328,11 @@ function handleNormalModeInput(code: string, shiftKey: boolean): void {
|
||||
}
|
||||
|
||||
if (code === 'KeyE') {
|
||||
const effect = audio.cycleOutboundEffect();
|
||||
updateStatus(effect.label);
|
||||
const currentEffect = audio.getCurrentEffect();
|
||||
const currentIndex = EFFECT_SEQUENCE.findIndex((effect) => effect.id === currentEffect.id);
|
||||
state.effectSelectIndex = currentIndex >= 0 ? currentIndex : 0;
|
||||
state.mode = 'effectSelect';
|
||||
updateStatus(`Select effect: ${EFFECT_SEQUENCE[state.effectSelectIndex].label}`);
|
||||
audio.sfxUiBlip();
|
||||
return;
|
||||
}
|
||||
@@ -1625,6 +1629,46 @@ function handleChatModeInput(code: string, key: string): void {
|
||||
applyTextInputEdit(code, key, 500);
|
||||
}
|
||||
|
||||
function handleEffectSelectModeInput(code: string, key: string): void {
|
||||
if (code === 'ArrowDown' || code === 'ArrowUp') {
|
||||
state.effectSelectIndex =
|
||||
code === 'ArrowDown'
|
||||
? (state.effectSelectIndex + 1) % EFFECT_SEQUENCE.length
|
||||
: (state.effectSelectIndex - 1 + EFFECT_SEQUENCE.length) % EFFECT_SEQUENCE.length;
|
||||
updateStatus(`Select effect: ${EFFECT_SEQUENCE[state.effectSelectIndex].label}`);
|
||||
audio.sfxUiBlip();
|
||||
return;
|
||||
}
|
||||
|
||||
const nextByInitial = findNextIndexByInitial(
|
||||
EFFECT_SEQUENCE,
|
||||
state.effectSelectIndex,
|
||||
key,
|
||||
(effect) => effect.label,
|
||||
);
|
||||
if (nextByInitial >= 0) {
|
||||
state.effectSelectIndex = nextByInitial;
|
||||
updateStatus(`Select effect: ${EFFECT_SEQUENCE[state.effectSelectIndex].label}`);
|
||||
audio.sfxUiBlip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (code === 'Enter') {
|
||||
const selected = EFFECT_SEQUENCE[state.effectSelectIndex];
|
||||
const effect = audio.setOutboundEffect(selected.id);
|
||||
state.mode = 'normal';
|
||||
updateStatus(effect.label);
|
||||
audio.sfxUiBlip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (code === 'Escape') {
|
||||
state.mode = 'normal';
|
||||
updateStatus('Cancelled.');
|
||||
audio.sfxUiCancel();
|
||||
}
|
||||
}
|
||||
|
||||
function handleListModeInput(code: string, key: string): void {
|
||||
if (state.sortedPeerIds.length === 0) {
|
||||
state.mode = 'normal';
|
||||
@@ -2164,6 +2208,8 @@ function setupInputHandlers(): void {
|
||||
handleNicknameModeInput(code, event.key);
|
||||
} else if (state.mode === 'chat') {
|
||||
handleChatModeInput(code, event.key);
|
||||
} else if (state.mode === 'effectSelect') {
|
||||
handleEffectSelectModeInput(code, event.key);
|
||||
} else if (state.mode === 'listUsers') {
|
||||
handleListModeInput(code, event.key);
|
||||
} else if (state.mode === 'listItems') {
|
||||
|
||||
@@ -26,6 +26,7 @@ export type GameMode =
|
||||
| 'normal'
|
||||
| 'nickname'
|
||||
| 'chat'
|
||||
| 'effectSelect'
|
||||
| 'listUsers'
|
||||
| 'listItems'
|
||||
| 'addItem'
|
||||
@@ -69,6 +70,7 @@ export type GameState = {
|
||||
editingPropertyKey: string | null;
|
||||
itemPropertyOptionValues: string[];
|
||||
itemPropertyOptionIndex: number;
|
||||
effectSelectIndex: number;
|
||||
addItemTypeIndex: number;
|
||||
isMuted: boolean;
|
||||
player: Player;
|
||||
@@ -98,6 +100,7 @@ export function createInitialState(): GameState {
|
||||
editingPropertyKey: null,
|
||||
itemPropertyOptionValues: [],
|
||||
itemPropertyOptionIndex: 0,
|
||||
effectSelectIndex: 0,
|
||||
addItemTypeIndex: 0,
|
||||
isMuted: false,
|
||||
player: {
|
||||
|
||||
Reference in New Issue
Block a user