Replace E effect cycling with effect selection menu

This commit is contained in:
Jage9
2026-02-21 02:06:32 -05:00
parent 5a3f3d3044
commit e3d2fe6404
5 changed files with 60 additions and 4 deletions

View File

@@ -63,7 +63,7 @@
<p><b>M:</b> Mute/unmute</p> <p><b>M:</b> Mute/unmute</p>
<p><b>Shift+M:</b> Toggle stereo/mono output</p> <p><b>Shift+M:</b> Toggle stereo/mono output</p>
<p><b>! (Shift+1):</b> Toggle loopback monitor</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> <p><b>Dash or Equals:</b> Lower/raise active effect value</p>
</div> </div>

View File

@@ -1,3 +1,3 @@
// Maintainer-controlled web client version. // Maintainer-controlled web client version.
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) // 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";

View File

@@ -111,6 +111,13 @@ export class AudioEngine {
return EFFECT_SEQUENCE[this.effectIndex]; 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 } { getCurrentEffect(): { id: EffectId; label: string; value: number; defaultValue: number } {
const effect = EFFECT_SEQUENCE[this.effectIndex]; const effect = EFFECT_SEQUENCE[this.effectIndex];
return { return {

View File

@@ -1092,6 +1092,7 @@ function disconnect(): void {
state.editingPropertyKey = null; state.editingPropertyKey = null;
state.itemPropertyOptionValues = []; state.itemPropertyOptionValues = [];
state.itemPropertyOptionIndex = 0; state.itemPropertyOptionIndex = 0;
state.effectSelectIndex = 0;
pendingEscapeDisconnect = false; pendingEscapeDisconnect = false;
connecting = false; connecting = false;
@@ -1327,8 +1328,11 @@ function handleNormalModeInput(code: string, shiftKey: boolean): void {
} }
if (code === 'KeyE') { if (code === 'KeyE') {
const effect = audio.cycleOutboundEffect(); const currentEffect = audio.getCurrentEffect();
updateStatus(effect.label); 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(); audio.sfxUiBlip();
return; return;
} }
@@ -1625,6 +1629,46 @@ function handleChatModeInput(code: string, key: string): void {
applyTextInputEdit(code, key, 500); 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 { function handleListModeInput(code: string, key: string): void {
if (state.sortedPeerIds.length === 0) { if (state.sortedPeerIds.length === 0) {
state.mode = 'normal'; state.mode = 'normal';
@@ -2164,6 +2208,8 @@ function setupInputHandlers(): void {
handleNicknameModeInput(code, event.key); handleNicknameModeInput(code, event.key);
} else if (state.mode === 'chat') { } else if (state.mode === 'chat') {
handleChatModeInput(code, event.key); handleChatModeInput(code, event.key);
} else if (state.mode === 'effectSelect') {
handleEffectSelectModeInput(code, event.key);
} else if (state.mode === 'listUsers') { } else if (state.mode === 'listUsers') {
handleListModeInput(code, event.key); handleListModeInput(code, event.key);
} else if (state.mode === 'listItems') { } else if (state.mode === 'listItems') {

View File

@@ -26,6 +26,7 @@ export type GameMode =
| 'normal' | 'normal'
| 'nickname' | 'nickname'
| 'chat' | 'chat'
| 'effectSelect'
| 'listUsers' | 'listUsers'
| 'listItems' | 'listItems'
| 'addItem' | 'addItem'
@@ -69,6 +70,7 @@ export type GameState = {
editingPropertyKey: string | null; editingPropertyKey: string | null;
itemPropertyOptionValues: string[]; itemPropertyOptionValues: string[];
itemPropertyOptionIndex: number; itemPropertyOptionIndex: number;
effectSelectIndex: number;
addItemTypeIndex: number; addItemTypeIndex: number;
isMuted: boolean; isMuted: boolean;
player: Player; player: Player;
@@ -98,6 +100,7 @@ export function createInitialState(): GameState {
editingPropertyKey: null, editingPropertyKey: null,
itemPropertyOptionValues: [], itemPropertyOptionValues: [],
itemPropertyOptionIndex: 0, itemPropertyOptionIndex: 0,
effectSelectIndex: 0,
addItemTypeIndex: 0, addItemTypeIndex: 0,
isMuted: false, isMuted: false,
player: { player: {