tweak
This commit is contained in:
parent
e1bea4e125
commit
d125cc3aac
|
@ -1194,6 +1194,7 @@ export interface Locale {
|
||||||
"enableQuickAddMfmFunction": string;
|
"enableQuickAddMfmFunction": string;
|
||||||
"bubbleGame": string;
|
"bubbleGame": string;
|
||||||
"sfx": string;
|
"sfx": string;
|
||||||
|
"soundWillBePlayed": string;
|
||||||
"_announcement": {
|
"_announcement": {
|
||||||
"forExistingUsers": string;
|
"forExistingUsers": string;
|
||||||
"forExistingUsersDescription": string;
|
"forExistingUsersDescription": string;
|
||||||
|
|
|
@ -1191,6 +1191,7 @@ addMfmFunction: "装飾を追加"
|
||||||
enableQuickAddMfmFunction: "高度なMFMのピッカーを表示する"
|
enableQuickAddMfmFunction: "高度なMFMのピッカーを表示する"
|
||||||
bubbleGame: "バブルゲーム"
|
bubbleGame: "バブルゲーム"
|
||||||
sfx: "効果音"
|
sfx: "効果音"
|
||||||
|
soundWillBePlayed: "サウンドが再生されます"
|
||||||
|
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "既存ユーザーのみ"
|
forExistingUsers: "既存ユーザーのみ"
|
||||||
|
|
|
@ -271,7 +271,7 @@ export async function mainBoot() {
|
||||||
|
|
||||||
main.on('unreadAntenna', () => {
|
main.on('unreadAntenna', () => {
|
||||||
updateAccount({ hasUnreadAntenna: true });
|
updateAccount({ hasUnreadAntenna: true });
|
||||||
sound.play('antenna');
|
sound.playMisskeySfx('antenna');
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllAnnouncements', () => {
|
main.on('readAllAnnouncements', () => {
|
||||||
|
|
|
@ -345,7 +345,7 @@ function react(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
return;
|
return;
|
||||||
|
@ -365,7 +365,7 @@ function react(viaKeyboard = false): void {
|
||||||
} else {
|
} else {
|
||||||
blur();
|
blur();
|
||||||
reactionPicker.show(reactButton.value, reaction => {
|
reactionPicker.show(reactButton.value, reaction => {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
emit('reaction', reaction);
|
emit('reaction', reaction);
|
||||||
|
|
|
@ -370,7 +370,7 @@ function react(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
|
||||||
misskeyApi('notes/reactions/create', {
|
misskeyApi('notes/reactions/create', {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
|
@ -386,7 +386,7 @@ function react(viaKeyboard = false): void {
|
||||||
} else {
|
} else {
|
||||||
blur();
|
blur();
|
||||||
reactionPicker.show(reactButton.value, reaction => {
|
reactionPicker.show(reactButton.value, reaction => {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
|
||||||
misskeyApi('notes/reactions/create', {
|
misskeyApi('notes/reactions/create', {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
|
|
|
@ -62,7 +62,7 @@ async function toggleReaction() {
|
||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
|
|
||||||
if (oldReaction !== props.reaction) {
|
if (oldReaction !== props.reaction) {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mock) {
|
if (mock) {
|
||||||
|
@ -81,7 +81,7 @@ async function toggleReaction() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
|
||||||
if (mock) {
|
if (mock) {
|
||||||
emit('reactionToggled', props.reaction, (props.count + 1));
|
emit('reactionToggled', props.reaction, (props.count + 1));
|
||||||
|
|
|
@ -81,7 +81,7 @@ function prepend(note) {
|
||||||
emit('note');
|
emit('note');
|
||||||
|
|
||||||
if (props.sound) {
|
if (props.sound) {
|
||||||
sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ function onClick(ev: MouseEvent) {
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
action: () => {
|
action: () => {
|
||||||
react(`:${props.name}:`);
|
react(`:${props.name}:`);
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
},
|
},
|
||||||
}] : [])], ev.currentTarget ?? ev.target);
|
}] : [])], ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ function onClick(ev: MouseEvent) {
|
||||||
icon: 'ti ti-plus',
|
icon: 'ti ti-plus',
|
||||||
action: () => {
|
action: () => {
|
||||||
react(props.emoji);
|
react(props.emoji);
|
||||||
sound.play('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
},
|
},
|
||||||
}] : [])], ev.currentTarget ?? ev.target);
|
}] : [])], ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
|
<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div :class="$style.frameInner">
|
||||||
|
<div class="_gaps" style="padding: 16px;">
|
||||||
|
<div style="font-size: 90%;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div>
|
||||||
|
<MkSwitch v-model="mute">
|
||||||
|
<template #label>{{ i18n.ts.mute }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -159,6 +167,7 @@ import { $i } from '@/account.js';
|
||||||
import { DropAndFusionGame, Mono } from '@/scripts/drop-and-fusion-engine.js';
|
import { DropAndFusionGame, Mono } from '@/scripts/drop-and-fusion-engine.js';
|
||||||
import * as sound from '@/scripts/sound.js';
|
import * as sound from '@/scripts/sound.js';
|
||||||
import MkRange from '@/components/MkRange.vue';
|
import MkRange from '@/components/MkRange.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
|
||||||
const NORMAL_BASE_SIZE = 30;
|
const NORMAL_BASE_SIZE = 30;
|
||||||
const NORAML_MONOS: Mono[] = [{
|
const NORAML_MONOS: Mono[] = [{
|
||||||
|
@ -409,6 +418,7 @@ const gameOver = ref(false);
|
||||||
const gameStarted = ref(false);
|
const gameStarted = ref(false);
|
||||||
const highScore = ref<number | null>(null);
|
const highScore = ref<number | null>(null);
|
||||||
const showConfig = ref(false);
|
const showConfig = ref(false);
|
||||||
|
const mute = ref(false);
|
||||||
const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
|
const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
|
||||||
const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
|
const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
|
||||||
|
|
||||||
|
@ -545,7 +555,7 @@ async function start() {
|
||||||
width: GAME_WIDTH,
|
width: GAME_WIDTH,
|
||||||
height: GAME_HEIGHT,
|
height: GAME_HEIGHT,
|
||||||
canvas: canvasEl.value!,
|
canvas: canvasEl.value!,
|
||||||
sfxVolume: sfxVolume.value,
|
sfxVolume: mute.value ? 0 : sfxVolume.value,
|
||||||
...(
|
...(
|
||||||
gameMode.value === 'normal' ? {
|
gameMode.value === 'normal' ? {
|
||||||
monoDefinitions: NORAML_MONOS,
|
monoDefinitions: NORAML_MONOS,
|
||||||
|
@ -565,7 +575,9 @@ async function start() {
|
||||||
}
|
}
|
||||||
const bgmBuffer = await sound.loadAudio('/client-assets/drop-and-fusion/bgm_1.mp3');
|
const bgmBuffer = await sound.loadAudio('/client-assets/drop-and-fusion/bgm_1.mp3');
|
||||||
if (!bgmBuffer) return;
|
if (!bgmBuffer) return;
|
||||||
bgmNodes = sound.createSourceNode(bgmBuffer, bgmVolume.value);
|
bgmNodes = sound.createSourceNode(bgmBuffer, {
|
||||||
|
volume: mute.value ? 0 : bgmVolume.value,
|
||||||
|
});
|
||||||
if (!bgmNodes) return;
|
if (!bgmNodes) return;
|
||||||
bgmNodes.soundSource.loop = true;
|
bgmNodes.soundSource.loop = true;
|
||||||
bgmNodes.soundSource.start();
|
bgmNodes.soundSource.start();
|
||||||
|
@ -574,14 +586,14 @@ async function start() {
|
||||||
|
|
||||||
watch(bgmVolume, (newValue, oldValue) => {
|
watch(bgmVolume, (newValue, oldValue) => {
|
||||||
if (bgmNodes) {
|
if (bgmNodes) {
|
||||||
bgmNodes.gainNode.gain.value = newValue;
|
bgmNodes.gainNode.gain.value = mute.value ? 0 : newValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(sfxVolume, (newValue, oldValue) => {
|
watch(sfxVolume, (newValue, oldValue) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (game) {
|
if (game) {
|
||||||
game.setSfxVolume(newValue);
|
game.setSfxVolume(mute.value ? 0 : newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ import MkRange from '@/components/MkRange.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
|
import { playMisskeySfxFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
|
||||||
import { selectFile } from '@/scripts/select-file.js';
|
import { selectFile } from '@/scripts/select-file.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -119,7 +119,7 @@ function listen() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playFile(type.value === '_driveFile_' ? {
|
playMisskeySfxFile(type.value === '_driveFile_' ? {
|
||||||
type: '_driveFile_',
|
type: '_driveFile_',
|
||||||
fileId: fileId.value as string,
|
fileId: fileId.value as string,
|
||||||
fileUrl: fileUrl.value as string,
|
fileUrl: fileUrl.value as string,
|
||||||
|
|
|
@ -233,7 +233,11 @@ export class DropAndFusionGame extends EventEmitter<{
|
||||||
|
|
||||||
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
||||||
const pan = ((newX / this.gameWidth) - 0.5) * 2;
|
const pan = ((newX / this.gameWidth) - 0.5) * 2;
|
||||||
sound.playUrl('/client-assets/drop-and-fusion/bubble2.mp3', this.sfxVolume, pan, nextMono.sfxPitch);
|
sound.playUrl('/client-assets/drop-and-fusion/bubble2.mp3', {
|
||||||
|
volume: this.sfxVolume,
|
||||||
|
pan,
|
||||||
|
playbackRate: nextMono.sfxPitch,
|
||||||
|
});
|
||||||
|
|
||||||
this.emit('monoAdded', nextMono);
|
this.emit('monoAdded', nextMono);
|
||||||
this.emit('fusioned', newX, newY, additionalScore);
|
this.emit('fusioned', newX, newY, additionalScore);
|
||||||
|
@ -335,7 +339,11 @@ export class DropAndFusionGame extends EventEmitter<{
|
||||||
const vol = ((Math.min(maxCollisionEnergyForSound, energy - minCollisionEnergyForSound) / maxCollisionEnergyForSound) / 4) * this.sfxVolume;
|
const vol = ((Math.min(maxCollisionEnergyForSound, energy - minCollisionEnergyForSound) / maxCollisionEnergyForSound) / 4) * this.sfxVolume;
|
||||||
const pan = ((((bodyA.position.x + bodyB.position.x) / 2) / this.gameWidth) - 0.5) * 2;
|
const pan = ((((bodyA.position.x + bodyB.position.x) / 2) / this.gameWidth) - 0.5) * 2;
|
||||||
const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10)));
|
const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10)));
|
||||||
sound.playUrl('/client-assets/drop-and-fusion/poi1.mp3', vol, pan, pitch);
|
sound.playUrl('/client-assets/drop-and-fusion/poi1.mp3', {
|
||||||
|
volume: vol,
|
||||||
|
pan,
|
||||||
|
playbackRate: pitch,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,7 +408,10 @@ export class DropAndFusionGame extends EventEmitter<{
|
||||||
|
|
||||||
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
||||||
const pan = ((x / this.gameWidth) - 0.5) * 2;
|
const pan = ((x / this.gameWidth) - 0.5) * 2;
|
||||||
sound.playUrl('/client-assets/drop-and-fusion/poi2.mp3', this.sfxVolume, pan);
|
sound.playUrl('/client-assets/drop-and-fusion/poi2.mp3', {
|
||||||
|
volume: this.sfxVolume,
|
||||||
|
pan,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public hold() {
|
public hold() {
|
||||||
|
|
|
@ -126,13 +126,13 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
|
||||||
* 既定のスプライトを再生する
|
* 既定のスプライトを再生する
|
||||||
* @param type スプライトの種類を指定
|
* @param type スプライトの種類を指定
|
||||||
*/
|
*/
|
||||||
export function play(operationType: OperationType) {
|
export function playMisskeySfx(operationType: OperationType) {
|
||||||
const sound = defaultStore.state[`sound_${operationType}`];
|
const sound = defaultStore.state[`sound_${operationType}`];
|
||||||
if (_DEV_) console.log('play', operationType, sound);
|
if (_DEV_) console.log('play', operationType, sound);
|
||||||
if (sound.type == null || !canPlay) return;
|
if (sound.type == null || !canPlay) return;
|
||||||
|
|
||||||
canPlay = false;
|
canPlay = false;
|
||||||
playFile(sound).finally(() => {
|
playMisskeySfxFile(sound).finally(() => {
|
||||||
// ごく短時間に音が重複しないように
|
// ごく短時間に音が重複しないように
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
canPlay = true;
|
canPlay = true;
|
||||||
|
@ -144,7 +144,7 @@ export function play(operationType: OperationType) {
|
||||||
* サウンド設定形式で指定された音声を再生する
|
* サウンド設定形式で指定された音声を再生する
|
||||||
* @param soundStore サウンド設定
|
* @param soundStore サウンド設定
|
||||||
*/
|
*/
|
||||||
export async function playFile(soundStore: SoundStore) {
|
export async function playMisskeySfxFile(soundStore: SoundStore) {
|
||||||
if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
|
if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -155,35 +155,42 @@ export async function playFile(soundStore: SoundStore) {
|
||||||
const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
|
const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
|
||||||
const buffer = await loadAudio(url);
|
const buffer = await loadAudio(url);
|
||||||
if (!buffer) return;
|
if (!buffer) return;
|
||||||
createSourceNode(buffer, soundStore.volume).soundSource.start();
|
const volume = soundStore.volume * masterVolume;
|
||||||
|
createSourceNode(buffer, { volume }).soundSource.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function playUrl(url: string, volume = 1, pan = 0, playbackRate = 1) {
|
export async function playUrl(url: string, opts: {
|
||||||
const masterVolume = defaultStore.state.sound_masterVolume;
|
volume?: number;
|
||||||
if (isMute() || masterVolume === 0 || volume === 0) {
|
pan?: number;
|
||||||
|
playbackRate?: number;
|
||||||
|
}) {
|
||||||
|
if (opts.volume === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const buffer = await loadAudio(url);
|
const buffer = await loadAudio(url);
|
||||||
if (!buffer) return;
|
if (!buffer) return;
|
||||||
createSourceNode(buffer, volume, pan, playbackRate).soundSource.start();
|
createSourceNode(buffer, opts).soundSource.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, playbackRate = 1): {
|
export function createSourceNode(buffer: AudioBuffer, opts: {
|
||||||
|
volume?: number;
|
||||||
|
pan?: number;
|
||||||
|
playbackRate?: number;
|
||||||
|
}): {
|
||||||
soundSource: AudioBufferSourceNode;
|
soundSource: AudioBufferSourceNode;
|
||||||
panNode: StereoPannerNode;
|
panNode: StereoPannerNode;
|
||||||
gainNode: GainNode;
|
gainNode: GainNode;
|
||||||
} {
|
} {
|
||||||
const masterVolume = defaultStore.state.sound_masterVolume;
|
|
||||||
|
|
||||||
const panNode = ctx.createStereoPanner();
|
const panNode = ctx.createStereoPanner();
|
||||||
panNode.pan.value = pan;
|
panNode.pan.value = opts.pan ?? 0;
|
||||||
|
|
||||||
const gainNode = ctx.createGain();
|
const gainNode = ctx.createGain();
|
||||||
gainNode.gain.value = masterVolume * volume;
|
|
||||||
|
gainNode.gain.value = opts.volume ?? 1;
|
||||||
|
|
||||||
const soundSource = ctx.createBufferSource();
|
const soundSource = ctx.createBufferSource();
|
||||||
soundSource.buffer = buffer;
|
soundSource.buffer = buffer;
|
||||||
soundSource.playbackRate.value = playbackRate;
|
soundSource.playbackRate.value = opts.playbackRate ?? 1;
|
||||||
soundSource
|
soundSource
|
||||||
.connect(panNode)
|
.connect(panNode)
|
||||||
.connect(gainNode)
|
.connect(gainNode)
|
||||||
|
|
|
@ -83,7 +83,7 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
|
||||||
}, 6000);
|
}, 6000);
|
||||||
}
|
}
|
||||||
|
|
||||||
sound.play('notification');
|
sound.playMisskeySfx('notification');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($i) {
|
if ($i) {
|
||||||
|
|
|
@ -123,7 +123,7 @@ const onStats = (stats) => {
|
||||||
current[domain].delayed = stats[domain].delayed;
|
current[domain].delayed = stats[domain].delayed;
|
||||||
|
|
||||||
if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
|
if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
|
||||||
const soundNode = sound.createSourceNode(jammedAudioBuffer.value, 1)?.soundSource;
|
const soundNode = sound.createSourceNode(jammedAudioBuffer.value, {}).soundSource;
|
||||||
if (soundNode) {
|
if (soundNode) {
|
||||||
jammedSoundNodePlaying.value = true;
|
jammedSoundNodePlaying.value = true;
|
||||||
soundNode.onended = () => jammedSoundNodePlaying.value = false;
|
soundNode.onended = () => jammedSoundNodePlaying.value = false;
|
||||||
|
|
Loading…
Reference in New Issue