mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-05-09 18:03:24 +08:00
refactor: bgm控制器
This commit is contained in:
parent
a058dfda4a
commit
1ef551799b
@ -526,17 +526,20 @@ control.prototype.setHeroMoveInterval = function (callback) {
|
||||
// render.move(true);
|
||||
// });
|
||||
|
||||
core.interval.heroMoveInterval = window.setInterval(function () {
|
||||
// render.offset += toAdd * 4;
|
||||
core.status.heroMoving += toAdd;
|
||||
if (core.status.heroMoving >= 8) {
|
||||
clearInterval(core.interval.heroMoveInterval);
|
||||
core.status.heroMoving = 0;
|
||||
// render.offset = 0;
|
||||
// render.move(false);
|
||||
if (callback) callback();
|
||||
}
|
||||
}, ((core.values.moveSpeed / 8) * toAdd) / core.status.replay.speed);
|
||||
core.interval.heroMoveInterval = window.setInterval(
|
||||
function () {
|
||||
// render.offset += toAdd * 4;
|
||||
core.status.heroMoving += toAdd;
|
||||
if (core.status.heroMoving >= 8) {
|
||||
clearInterval(core.interval.heroMoveInterval);
|
||||
core.status.heroMoving = 0;
|
||||
// render.offset = 0;
|
||||
// render.move(false);
|
||||
if (callback) callback();
|
||||
}
|
||||
},
|
||||
((core.values.moveSpeed / 8) * toAdd) / core.status.replay.speed
|
||||
);
|
||||
};
|
||||
|
||||
////// 每移动一格后执行的事件 //////
|
||||
@ -2988,25 +2991,23 @@ control.prototype.screenFlash = function (
|
||||
// todo: deprecate playBgm, pauseBgm, resumeBgm, triggerBgm
|
||||
////// 播放背景音乐 //////
|
||||
control.prototype.playBgm = function (bgm, startTime) {
|
||||
bgm = core.getMappedName(bgm);
|
||||
if (main.mode !== 'play') return;
|
||||
Mota.require('var', 'bgm').changeTo(bgm, startTime);
|
||||
// see src/module/fallback/audio.ts
|
||||
};
|
||||
|
||||
////// 暂停背景音乐的播放 //////
|
||||
control.prototype.pauseBgm = function () {
|
||||
if (main.mode !== 'play') return;
|
||||
Mota.require('var', 'bgm').pause();
|
||||
// see src/module/fallback/audio.ts
|
||||
};
|
||||
|
||||
////// 恢复背景音乐的播放 //////
|
||||
control.prototype.resumeBgm = function (resumeTime) {
|
||||
if (main.mode !== 'play') return;
|
||||
Mota.require('var', 'bgm').resume();
|
||||
// see src/module/fallback/audio.ts
|
||||
};
|
||||
|
||||
////// 更改背景音乐的播放 //////
|
||||
control.prototype.triggerBgm = function () {
|
||||
// see src/module/fallback/audio.ts
|
||||
return;
|
||||
if (main.mode !== 'play') return;
|
||||
const bgm = Mota.require('var', 'bgm');
|
||||
bgm.disable = !bgm.disable;
|
||||
@ -3036,6 +3037,8 @@ control.prototype.getPlayingSounds = function (name) {
|
||||
|
||||
////// 检查bgm状态 //////
|
||||
control.prototype.checkBgm = function () {
|
||||
// see src/module/fallback/audio.ts
|
||||
return;
|
||||
const bgm = Mota.require('var', 'bgm');
|
||||
if (bgm.disable) {
|
||||
bgm.pause();
|
||||
|
@ -696,12 +696,12 @@ events.prototype.getItem = function (id, num, x, y, isGentleClick, callback) {
|
||||
(id.endsWith('Key')
|
||||
? '(钥匙类道具,遇到对应的门时自动打开)'
|
||||
: itemCls == 'tools'
|
||||
? '(消耗类道具,请按T在道具栏使用)'
|
||||
: itemCls == 'constants'
|
||||
? '(永久类道具,请按T在道具栏使用)'
|
||||
: itemCls == 'equips'
|
||||
? '(装备类道具,请按Q在装备栏进行装备)'
|
||||
: '')
|
||||
? '(消耗类道具,请按T在道具栏使用)'
|
||||
: itemCls == 'constants'
|
||||
? '(永久类道具,请按T在道具栏使用)'
|
||||
: itemCls == 'equips'
|
||||
? '(装备类道具,请按Q在装备栏进行装备)'
|
||||
: '')
|
||||
);
|
||||
}
|
||||
itemHint.push(id);
|
||||
|
128
src/common/patch.ts
Normal file
128
src/common/patch.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { logger } from '@/core/common/logger';
|
||||
|
||||
export const enum PatchClass {
|
||||
Actions,
|
||||
Control,
|
||||
Core,
|
||||
Data,
|
||||
Enemys,
|
||||
Events,
|
||||
Icons,
|
||||
Items,
|
||||
Loader,
|
||||
Maps,
|
||||
UI,
|
||||
Utils
|
||||
}
|
||||
|
||||
interface PatchList {
|
||||
[PatchClass.Actions]: Actions;
|
||||
[PatchClass.Control]: Control;
|
||||
[PatchClass.Core]: Core;
|
||||
[PatchClass.Data]: Omit<DataCore, 'main'>;
|
||||
[PatchClass.Enemys]: Enemys;
|
||||
[PatchClass.Events]: Events;
|
||||
[PatchClass.Icons]: Icons;
|
||||
[PatchClass.Items]: Items;
|
||||
[PatchClass.Loader]: Loader;
|
||||
[PatchClass.Maps]: Maps;
|
||||
[PatchClass.UI]: Ui;
|
||||
[PatchClass.Utils]: Utils;
|
||||
}
|
||||
|
||||
const patchName = {
|
||||
[PatchClass.Actions]: 'actions',
|
||||
[PatchClass.Control]: 'control',
|
||||
[PatchClass.Core]: 'core',
|
||||
[PatchClass.Data]: 'data',
|
||||
[PatchClass.Enemys]: 'enemys',
|
||||
[PatchClass.Events]: 'events',
|
||||
[PatchClass.Icons]: 'icons',
|
||||
[PatchClass.Items]: 'items',
|
||||
[PatchClass.Loader]: 'loader',
|
||||
[PatchClass.Maps]: 'maps',
|
||||
[PatchClass.UI]: 'ui',
|
||||
[PatchClass.Utils]: 'utils'
|
||||
};
|
||||
|
||||
export class Patch<T extends PatchClass> {
|
||||
private static patchList: Set<Patch<PatchClass>> = new Set();
|
||||
private static patched: Partial<Record<PatchClass, Set<string>>> = {};
|
||||
|
||||
private patches: Map<string, (...params: any[]) => any> = new Map();
|
||||
|
||||
constructor(public readonly patchClass: T) {
|
||||
Patch.patchList.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加函数修改
|
||||
* @param key 要修改的函数名
|
||||
* @param patch 修改为的函数内容
|
||||
*/
|
||||
add<
|
||||
K extends Exclude<
|
||||
SelectKey<PatchList[T], (...params: any[]) => any>,
|
||||
symbol | number
|
||||
>
|
||||
>(key: K, patch: PatchList[T][K]) {
|
||||
if (this.patches.has(key)) {
|
||||
logger.warn(49, patchName[this.patchClass], key);
|
||||
}
|
||||
this.patches.set(key, patch);
|
||||
}
|
||||
|
||||
private static getPatchClass(patch: PatchClass): any {
|
||||
switch (patch) {
|
||||
case PatchClass.Actions:
|
||||
return actions.prototype;
|
||||
case PatchClass.Control:
|
||||
return control.prototype;
|
||||
case PatchClass.Core:
|
||||
return core;
|
||||
case PatchClass.Data:
|
||||
return data.prototype;
|
||||
case PatchClass.Enemys:
|
||||
return enemys.prototype;
|
||||
case PatchClass.Events:
|
||||
return events.prototype;
|
||||
case PatchClass.Icons:
|
||||
return icons.prototype;
|
||||
case PatchClass.Items:
|
||||
return items.prototype;
|
||||
case PatchClass.Loader:
|
||||
return loader.prototype;
|
||||
case PatchClass.Maps:
|
||||
return maps.prototype;
|
||||
case PatchClass.UI:
|
||||
return ui.prototype;
|
||||
case PatchClass.Utils:
|
||||
return utils.prototype;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改添加的所有函数
|
||||
*/
|
||||
static patchAll() {
|
||||
this.patchList.forEach(v => this.patch(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改某个实例添加的所有函数
|
||||
* @param patch 要修改的函数实例
|
||||
*/
|
||||
static patch(patch: Patch<PatchClass>) {
|
||||
const patchClass = patch.patchClass;
|
||||
this.patched[patchClass] ??= new Set();
|
||||
const set = this.patched[patchClass];
|
||||
const obj = this.getPatchClass(patchClass);
|
||||
for (const [key, func] of patch.patches) {
|
||||
if (set.has(key)) {
|
||||
logger.warn(49, patchName[patchClass], key);
|
||||
}
|
||||
obj[key] = func;
|
||||
}
|
||||
this.patchList.delete(patch);
|
||||
}
|
||||
}
|
@ -1,306 +0,0 @@
|
||||
import { Animation, TimingFn, Transition, linear, sleep } from 'mutate-animate';
|
||||
import { Undoable } from '../interface';
|
||||
import { ResourceController } from '../loader/controller';
|
||||
import { has } from '@/plugin/utils';
|
||||
|
||||
interface AnimatingBgm {
|
||||
end: () => void;
|
||||
ani: Transition;
|
||||
timeout: number;
|
||||
currentTime: number;
|
||||
endVolume: number;
|
||||
}
|
||||
|
||||
export class BgmController
|
||||
extends ResourceController<HTMLAudioElement>
|
||||
implements Undoable<BgmIds>
|
||||
{
|
||||
/** Bgm播放栈,可以undo,最多存放10个 */
|
||||
stack: BgmIds[] = [];
|
||||
/** Bgm的redo栈,最多存放10个 */
|
||||
redoStack: BgmIds[] = [];
|
||||
/** 当前播放的bgm */
|
||||
now?: BgmIds;
|
||||
|
||||
/** 渐变切歌时长 */
|
||||
transitionTime: number = 2000;
|
||||
/** 渐变切歌的音量曲线 */
|
||||
transitionCurve: TimingFn = linear();
|
||||
|
||||
/** 音量 */
|
||||
volume: number = 1;
|
||||
/** 是否关闭了bgm */
|
||||
disable: boolean = false;
|
||||
|
||||
/** 是否正在播放bgm */
|
||||
playing: boolean = false;
|
||||
|
||||
private transitionData: Map<BgmIds, AnimatingBgm> = new Map();
|
||||
|
||||
private canChange: boolean = true;
|
||||
|
||||
/**
|
||||
* 屏蔽音乐修改
|
||||
*/
|
||||
blockChange() {
|
||||
this.canChange = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消屏蔽音乐修改
|
||||
*/
|
||||
unblockChange() {
|
||||
this.canChange = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个bgm
|
||||
* @param uri bgm的`uri`,由于bgm是一类资源,因此`uri`为`bgms.xxx`的形式
|
||||
* @param data bgm音频元素
|
||||
*/
|
||||
add(uri: string, data: HTMLAudioElement) {
|
||||
if (this.list[uri]) {
|
||||
console.warn(`Repeated bgm: '${uri}'.`);
|
||||
}
|
||||
this.list[uri] = data;
|
||||
data.loop = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个bgm
|
||||
* @param id 要加载的bgm
|
||||
*/
|
||||
load(id: BgmIds) {
|
||||
const bgm = this.get(id);
|
||||
bgm.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换bgm,具有渐变效果,可以通过监听切换事件,同时调用preventDefault来阻止渐变,
|
||||
* 并使用自己的切歌程序。阻止后,不会将切换的歌曲加入播放栈,也不会进行切歌,
|
||||
* 所有的切歌操作均由你自己的程序执行
|
||||
* @param id 要切换至的bgm
|
||||
* @param when 切换至的歌从什么时候开始播放,默认-1,表示不改变,整数表示设置为目标值
|
||||
*/
|
||||
changeTo(id: BgmIds, when: number = -1, noStack: boolean = false) {
|
||||
if (!this.canChange) return;
|
||||
if (id === this.now) return this.resume();
|
||||
let prevent = false;
|
||||
const preventDefault = () => {
|
||||
prevent = true;
|
||||
};
|
||||
const ev = { preventDefault };
|
||||
|
||||
this.emit('changeBgm', ev, id, this.now);
|
||||
|
||||
if (prevent) return;
|
||||
|
||||
this.playing = true;
|
||||
if (!this.disable) {
|
||||
this.setTransitionAnimate(id, 1, when);
|
||||
if (this.now) this.setTransitionAnimate(this.now, 0);
|
||||
} else {
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
if (!noStack) {
|
||||
if (this.now) this.stack.push(this.now);
|
||||
this.redoStack = [];
|
||||
}
|
||||
this.now = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停当前bgm的播放,继续播放时将会延续暂停的时刻,同样可以使用preventDefault使用自己的暂停程序
|
||||
* @param transition 是否使用渐变效果,默认使用
|
||||
*/
|
||||
pause(transition: boolean = true) {
|
||||
if (!this.canChange) return;
|
||||
if (!this.now) return;
|
||||
let prevent = false;
|
||||
const preventDefault = () => {
|
||||
prevent = true;
|
||||
};
|
||||
const ev = { preventDefault };
|
||||
|
||||
this.emit('pause', ev, this.now);
|
||||
|
||||
if (prevent) return;
|
||||
|
||||
this.playing = false;
|
||||
|
||||
if (transition) this.setTransitionAnimate(this.now, 0);
|
||||
else this.get(this.now).pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续当前bgm的播放,从上一次暂停的时刻开始播放,同样可以使用preventDefault使用自己的播放程序
|
||||
* @param transition 是否使用渐变效果,默认使用
|
||||
*/
|
||||
resume(transition: boolean = true) {
|
||||
if (!this.canChange) return;
|
||||
if (!this.now) return;
|
||||
let prevent = false;
|
||||
const preventDefault = () => {
|
||||
prevent = true;
|
||||
};
|
||||
const ev = { preventDefault };
|
||||
|
||||
this.emit('resume', ev, this.now);
|
||||
|
||||
if (prevent) return;
|
||||
|
||||
this.playing = true;
|
||||
|
||||
if (!this.disable) {
|
||||
if (transition) this.setTransitionAnimate(this.now, 1);
|
||||
else this.get(this.now).play();
|
||||
} else {
|
||||
this.playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放bgm,不进行渐变操作,效果为没有渐变的切歌,也会触发changeBgm事件,可以被preventDefault
|
||||
* @param id 要播放的bgm
|
||||
* @param when 从bgm的何时开始播放
|
||||
*/
|
||||
play(id: BgmIds, when: number = 0, noStack: boolean = false) {
|
||||
if (!this.canChange) return;
|
||||
if (id === this.now) return;
|
||||
let prevent = false;
|
||||
const preventDefault = () => {
|
||||
prevent = true;
|
||||
};
|
||||
const ev = { preventDefault };
|
||||
|
||||
this.emit('changeBgm', ev, id, this.now);
|
||||
|
||||
if (prevent) return;
|
||||
|
||||
this.playing = true;
|
||||
|
||||
const before = this.now ? null : this.get(this.now!);
|
||||
const to = this.get(id);
|
||||
if (before) {
|
||||
before.pause();
|
||||
}
|
||||
to.currentTime = when;
|
||||
to.volume = this.volume;
|
||||
to.play();
|
||||
|
||||
if (!this.disable) {
|
||||
if (!noStack) {
|
||||
if (this.now) this.stack.push(this.now);
|
||||
this.redoStack = [];
|
||||
}
|
||||
this.now = id;
|
||||
} else {
|
||||
this.playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销当前播放,改为播放前一个bgm
|
||||
*/
|
||||
undo(transition: boolean = true, when: number = 0) {
|
||||
if (!this.canChange) return;
|
||||
if (this.stack.length === 0) return;
|
||||
else {
|
||||
const to = this.stack.pop()!;
|
||||
if (this.now) this.redoStack.push(this.now);
|
||||
else return;
|
||||
|
||||
if (transition) this.changeTo(to, when, true);
|
||||
else this.play(to, when, true);
|
||||
return this.now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消上一次的撤销,改为播放上一次撤销的bgm
|
||||
*/
|
||||
redo(transition: boolean = true, when: number = 0) {
|
||||
if (!this.canChange) return;
|
||||
if (this.redoStack.length === 0) return;
|
||||
else {
|
||||
const to = this.redoStack.pop()!;
|
||||
if (this.now) this.stack.push(this.now);
|
||||
else return;
|
||||
|
||||
if (transition) this.changeTo(to, when, true);
|
||||
else this.play(to, when, true);
|
||||
return this.now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置渐变切歌信息
|
||||
* @param time 渐变时长
|
||||
* @param curve 渐变的音量曲线
|
||||
*/
|
||||
setTransition(time?: number, curve?: TimingFn) {
|
||||
has(time) && (this.transitionTime = time);
|
||||
has(curve) && (this.transitionCurve = curve);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id获取bgm
|
||||
* @param id 要获取的bgm的id
|
||||
*/
|
||||
get(id: BgmIds) {
|
||||
return this.list[`bgms.${id}`];
|
||||
}
|
||||
|
||||
private setTransitionAnimate(id: BgmIds, to: number, when: number = -1) {
|
||||
const bgm = this.get(id);
|
||||
|
||||
let tran = this.transitionData.get(id);
|
||||
if (!tran) {
|
||||
const ani = new Transition();
|
||||
ani.value.volume = bgm.paused ? 0 : 1;
|
||||
const end = () => {
|
||||
ani.ticker.destroy();
|
||||
if (tran!.endVolume === 0) {
|
||||
bgm.pause();
|
||||
} else {
|
||||
bgm.volume = tran!.endVolume * this.volume;
|
||||
}
|
||||
this.transitionData.delete(id);
|
||||
};
|
||||
tran = {
|
||||
end,
|
||||
ani: ani,
|
||||
timeout: -1,
|
||||
currentTime: bgm.currentTime,
|
||||
endVolume: to
|
||||
};
|
||||
this.transitionData.set(id, tran);
|
||||
ani.ticker.add(() => {
|
||||
bgm.volume = ani.value.volume * this.volume;
|
||||
});
|
||||
}
|
||||
|
||||
if (to !== 0) {
|
||||
bgm.volume = tran.ani.value.volume * this.volume;
|
||||
if (bgm.paused) bgm.play();
|
||||
}
|
||||
if (when !== -1) {
|
||||
bgm.currentTime = when;
|
||||
}
|
||||
tran.endVolume = to;
|
||||
|
||||
tran.ani
|
||||
.time(this.transitionTime)
|
||||
.mode(this.transitionCurve)
|
||||
.absolute()
|
||||
.transition('volume', to);
|
||||
|
||||
if (tran.timeout !== -1) {
|
||||
clearTimeout(tran.timeout);
|
||||
}
|
||||
tran.timeout = window.setTimeout(tran.end, this.transitionTime);
|
||||
}
|
||||
}
|
||||
|
||||
export const bgm = new BgmController();
|
@ -475,14 +475,14 @@ export function loadDefaultResource() {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
const icon = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
// bgm
|
||||
data.main.bgms.forEach(v => {
|
||||
const res = LoadTask.add('audio', `audio/${v}`);
|
||||
Mota.r(() => {
|
||||
res.once('loadStart', res => {
|
||||
Mota.require('var', 'bgm').add(`bgms.${v}`, res.resource!);
|
||||
});
|
||||
});
|
||||
});
|
||||
// data.main.bgms.forEach(v => {
|
||||
// const res = LoadTask.add('audio', `audio/${v}`);
|
||||
// Mota.r(() => {
|
||||
// res.once('loadStart', res => {
|
||||
// Mota.require('var', 'bgm').add(`bgms.${v}`, res.resource!);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// fonts
|
||||
data.main.fonts.forEach(v => {
|
||||
const res = LoadTask.add('buffer', `buffer/project/fonts/${v}.ttf`);
|
||||
@ -584,16 +584,16 @@ export async function loadCompressedResource() {
|
||||
});
|
||||
const list: CompressedLoadList = JSON.parse(data.data);
|
||||
|
||||
const d = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
// const d = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
// 对于bgm,直接按照原来的方式加载即可
|
||||
d.main.bgms.forEach(v => {
|
||||
const res = LoadTask.add('audio', `audio/${v}`);
|
||||
Mota.r(() => {
|
||||
res.once('loadStart', res => {
|
||||
Mota.require('var', 'bgm').add(`bgms.${v}`, res.resource!);
|
||||
});
|
||||
});
|
||||
});
|
||||
// d.main.bgms.forEach(v => {
|
||||
// const res = LoadTask.add('audio', `audio/${v}`);
|
||||
// Mota.r(() => {
|
||||
// res.once('loadStart', res => {
|
||||
// Mota.require('var', 'bgm').add(`bgms.${v}`, res.resource!);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// 对于区域内容,按照zip格式进行加载,然后解压处理
|
||||
const autotiles: Partial<Record<AllIdsOf<'autotile'>, HTMLImageElement>> =
|
||||
{};
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { BgmController, bgm } from './audio/bgm';
|
||||
import { SoundController, SoundEffect, sound } from './audio/sound';
|
||||
import { Focus, GameUi, UiController } from './main/custom/ui';
|
||||
import { GameStorage } from './main/storage';
|
||||
@ -11,7 +10,7 @@ import {
|
||||
mainSetting,
|
||||
settingStorage
|
||||
} from './main/setting';
|
||||
import { KeyCode, ScanCode } from '@/plugin/keyCodes';
|
||||
import { KeyCode } from '@/plugin/keyCodes';
|
||||
import { status } from '@/plugin/ui/statusBar';
|
||||
import '@/plugin';
|
||||
import './package';
|
||||
@ -80,7 +79,6 @@ import { TextboxStore } from './render/index';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'AudioPlayer', AudioPlayer);
|
||||
Mota.register('class', 'BgmController', BgmController);
|
||||
Mota.register('class', 'CustomToolbar', CustomToolbar);
|
||||
Mota.register('class', 'Focus', Focus);
|
||||
Mota.register('class', 'GameStorage', GameStorage);
|
||||
@ -106,7 +104,6 @@ Mota.register('fn', 'removeAnimate', removeAnimate);
|
||||
// ----- 变量注册
|
||||
Mota.register('var', 'mainUi', mainUi);
|
||||
Mota.register('var', 'fixedUi', fixedUi);
|
||||
Mota.register('var', 'bgm', bgm);
|
||||
Mota.register('var', 'sound', sound);
|
||||
Mota.register('var', 'gameKey', gameKey);
|
||||
Mota.register('var', 'mainSetting', mainSetting);
|
||||
|
@ -3,13 +3,13 @@ import { EventEmitter } from '../common/eventEmitter';
|
||||
import { GameStorage } from './storage';
|
||||
import { has, triggerFullscreen } from '@/plugin/utils';
|
||||
import { createSettingComponents } from './init/settings';
|
||||
import { bgm } from '../audio/bgm';
|
||||
import { SoundEffect } from '../audio/sound';
|
||||
import settingsText from '@/data/settings.json';
|
||||
import { isMobile } from '@/plugin/use';
|
||||
import { fontSize } from '@/plugin/ui/statusBar';
|
||||
import { CustomToolbar } from './custom/toolbar';
|
||||
import { fixedUi } from './init/ui';
|
||||
import { bgmController } from '@/module';
|
||||
|
||||
export interface SettingComponentProps {
|
||||
item: MotaSettingItem;
|
||||
@ -347,7 +347,7 @@ const root = document.getElementById('root') as HTMLDivElement;
|
||||
function handleScreenSetting<T extends number | boolean>(
|
||||
key: string,
|
||||
n: T,
|
||||
o: T
|
||||
_o: T
|
||||
) {
|
||||
if (key === 'fullscreen') {
|
||||
// 全屏
|
||||
@ -369,7 +369,7 @@ function handleScreenSetting<T extends number | boolean>(
|
||||
function handleActionSetting<T extends number | boolean>(
|
||||
key: string,
|
||||
n: T,
|
||||
o: T
|
||||
_o: T
|
||||
) {
|
||||
if (key === 'autoSkill') {
|
||||
// 自动切换技能
|
||||
@ -382,13 +382,13 @@ function handleActionSetting<T extends number | boolean>(
|
||||
function handleAudioSetting<T extends number | boolean>(
|
||||
key: string,
|
||||
n: T,
|
||||
o: T
|
||||
_o: T
|
||||
) {
|
||||
if (key === 'bgmEnabled') {
|
||||
bgm.disable = !n;
|
||||
if (core.isPlaying()) core.checkBgm();
|
||||
if (key === 'bgmEnabled') {
|
||||
bgmController.setEnabled(n as boolean);
|
||||
core.checkBgm();
|
||||
} else if (key === 'bgmVolume') {
|
||||
bgm.volume = (n as number) / 100;
|
||||
bgmController.setVolume((n as number) / 100);
|
||||
} else if (key === 'soundEnabled') {
|
||||
SoundEffect.disable = !n;
|
||||
} else if (key === 'soundVolume') {
|
||||
|
@ -80,6 +80,8 @@
|
||||
"46": "Cannot pipe new StreamReader object when stream is loading.",
|
||||
"47": "Audio stream decoder for audio type '$1' has already existed.",
|
||||
"48": "Sample rate in stream audio must be constant.",
|
||||
"49": "Repeated patch for '$1', key: '$2'.",
|
||||
"50": "Unknown audio extension name: '$1'",
|
||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
||||
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { AudioPlayer } from '@/core/audio/audio';
|
||||
import type { BgmController } from '@/core/audio/bgm';
|
||||
import type { SoundController, SoundEffect } from '@/core/audio/sound';
|
||||
import type { Disposable } from '@/core/common/disposable';
|
||||
import type {
|
||||
@ -61,7 +60,6 @@ interface ClassInterface {
|
||||
AudioPlayer: typeof AudioPlayer;
|
||||
SoundEffect: typeof SoundEffect;
|
||||
SoundController: typeof SoundController;
|
||||
BgmController: typeof BgmController;
|
||||
Danmaku: typeof Danmaku;
|
||||
// todo: 放到插件 ShaderEffect: typeof ShaderEffect;
|
||||
// 定义于游戏进程,渲染进程依然可用
|
||||
@ -92,7 +90,6 @@ interface VariableInterface {
|
||||
fixedUi: UiController;
|
||||
KeyCode: typeof KeyCode;
|
||||
// isMobile: boolean;
|
||||
bgm: BgmController;
|
||||
sound: SoundController;
|
||||
settingStorage: GameStorage;
|
||||
status: Ref<boolean>;
|
||||
@ -431,7 +428,7 @@ class Mota {
|
||||
|
||||
static require(type: InterfaceType, key: string): any {
|
||||
const data = this.getByType(type)[key];
|
||||
if (!!data) return data;
|
||||
if (data) return data;
|
||||
else {
|
||||
throw new Error(
|
||||
`Cannot resolve require: type='${type}',key='${key}'`
|
||||
@ -457,10 +454,10 @@ class Mota {
|
||||
return type === 'class'
|
||||
? this.classes
|
||||
: type === 'fn'
|
||||
? this.functions
|
||||
: type === 'var'
|
||||
? this.variables
|
||||
: this.modules;
|
||||
? this.functions
|
||||
: type === 'var'
|
||||
? this.variables
|
||||
: this.modules;
|
||||
}
|
||||
}
|
||||
|
||||
|
252
src/module/audio/bgm.ts
Normal file
252
src/module/audio/bgm.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { audioPlayer, AudioPlayer, AudioRoute, AudioStatus } from './player';
|
||||
import { guessTypeByExt, isAudioSupport } from './support';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { StreamLoader } from '../loader';
|
||||
import { linear, sleep, Transition } from 'mutate-animate';
|
||||
import { VolumeEffect } from './effect';
|
||||
|
||||
interface BgmVolume {
|
||||
effect: VolumeEffect;
|
||||
transition: Transition;
|
||||
}
|
||||
|
||||
interface BgmControllerEvent {}
|
||||
|
||||
export class BgmController<
|
||||
T extends string = BgmIds
|
||||
> extends EventEmitter<BgmControllerEvent> {
|
||||
/** bgm音频名称的前缀 */
|
||||
prefix: string = 'bgms.';
|
||||
/** 每个 bgm 的音量控制器 */
|
||||
readonly gain: Map<T, BgmVolume> = new Map();
|
||||
|
||||
/** 正在播放的 bgm */
|
||||
playingBgm?: T;
|
||||
/** 是否正在播放 */
|
||||
playing: boolean = false;
|
||||
|
||||
/** 是否已经启用 */
|
||||
enabled: boolean = true;
|
||||
/** 主音量控制器 */
|
||||
private readonly mainGain: VolumeEffect;
|
||||
/** 是否屏蔽所有的音乐切换 */
|
||||
private blocking: boolean = false;
|
||||
/** 渐变时长 */
|
||||
private transitionTime: number = 2000;
|
||||
|
||||
constructor(public readonly player: AudioPlayer) {
|
||||
super();
|
||||
this.mainGain = player.createVolumeEffect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音频渐变时长
|
||||
* @param time 渐变时长
|
||||
*/
|
||||
setTransitionTime(time: number) {
|
||||
this.transitionTime = time;
|
||||
for (const [, value] of this.gain) {
|
||||
value.transition.time(time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 屏蔽音乐切换
|
||||
*/
|
||||
blockChange() {
|
||||
this.blocking = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消屏蔽音乐切换
|
||||
*/
|
||||
unblockChange() {
|
||||
this.blocking = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置总音量大小
|
||||
* @param volume 音量大小
|
||||
*/
|
||||
setVolume(volume: number) {
|
||||
this.mainGain.setVolume(volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
setEnabled(enabled: boolean) {
|
||||
if (enabled) this.resume();
|
||||
else this.stop();
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 bgm 音频名称的前缀
|
||||
*/
|
||||
setPrefix(prefix: string) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
private getId(name: T) {
|
||||
return `${this.prefix}${name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 bgm 名称获取其 AudioRoute 实例
|
||||
* @param id 音频名称
|
||||
*/
|
||||
get(id: T) {
|
||||
return this.player.getRoute(this.getId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个 bgm
|
||||
* @param id 要添加的 bgm 的名称
|
||||
* @param url 指定 bgm 的加载地址
|
||||
*/
|
||||
addBgm(id: T, url: string = `project/bgms/${id}`) {
|
||||
const type = guessTypeByExt(id);
|
||||
if (!type) {
|
||||
logger.warn(50, id.split('.').slice(0, -1).join('.'));
|
||||
return;
|
||||
}
|
||||
const gain = this.player.createVolumeEffect();
|
||||
if (isAudioSupport(type)) {
|
||||
const source = audioPlayer.createElementSource();
|
||||
source.setSource(url);
|
||||
source.setLoop(true);
|
||||
const route = new AudioRoute(source, audioPlayer);
|
||||
route.addEffect([gain, this.mainGain]);
|
||||
audioPlayer.addRoute(this.getId(id), route);
|
||||
this.setTransition(id, route, gain);
|
||||
} else {
|
||||
const source = audioPlayer.createStreamSource();
|
||||
const stream = new StreamLoader(url);
|
||||
stream.pipe(source);
|
||||
source.setLoop(true);
|
||||
const route = new AudioRoute(source, audioPlayer);
|
||||
route.addEffect([gain, this.mainGain]);
|
||||
audioPlayer.addRoute(this.getId(id), route);
|
||||
this.setTransition(id, route, gain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个 bgm
|
||||
* @param id 要移除的 bgm 的名称
|
||||
*/
|
||||
removeBgm(id: T) {
|
||||
this.player.removeRoute(this.getId(id));
|
||||
const gain = this.gain.get(id);
|
||||
gain?.transition.ticker.destroy();
|
||||
this.gain.delete(id);
|
||||
}
|
||||
|
||||
private setTransition(id: T, route: AudioRoute, gain: VolumeEffect) {
|
||||
const transition = new Transition();
|
||||
transition
|
||||
.time(this.transitionTime)
|
||||
.mode(linear())
|
||||
.transition('volume', 0);
|
||||
|
||||
const tick = () => {
|
||||
gain.setVolume(transition.value.volume);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param expect 在结束时应该是正在播放还是停止
|
||||
*/
|
||||
const setTick = async (expect: AudioStatus) => {
|
||||
transition.ticker.remove(tick);
|
||||
transition.ticker.add(tick);
|
||||
const identifier = route.stopIdentifier;
|
||||
await sleep(this.transitionTime + 500);
|
||||
if (
|
||||
route.status === expect &&
|
||||
identifier === route.stopIdentifier
|
||||
) {
|
||||
transition.ticker.remove(tick);
|
||||
if (route.status === AudioStatus.Playing) {
|
||||
gain.setVolume(1);
|
||||
} else {
|
||||
gain.setVolume(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
route.onStart(async () => {
|
||||
transition.transition('volume', 1);
|
||||
setTick(AudioStatus.Playing);
|
||||
});
|
||||
route.onEnd(() => {
|
||||
transition.transition('volume', 0);
|
||||
setTick(AudioStatus.Paused);
|
||||
});
|
||||
route.setEndTime(this.transitionTime);
|
||||
|
||||
this.gain.set(id, { effect: gain, transition });
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放一个 bgm
|
||||
* @param id 要播放的 bgm 名称
|
||||
*/
|
||||
play(id: T, when?: number) {
|
||||
if (this.blocking) return;
|
||||
if (id !== this.playingBgm && this.playingBgm) {
|
||||
this.player.pause(this.getId(this.playingBgm));
|
||||
}
|
||||
this.playingBgm = id;
|
||||
if (!this.enabled) return;
|
||||
this.player.play(this.getId(id), when);
|
||||
this.playing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续当前的 bgm
|
||||
*/
|
||||
resume() {
|
||||
if (this.blocking || !this.enabled || this.playing) return;
|
||||
if (this.playingBgm) {
|
||||
this.player.resume(this.getId(this.playingBgm));
|
||||
}
|
||||
this.playing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停当前的 bgm
|
||||
*/
|
||||
pause() {
|
||||
if (this.blocking || !this.enabled) return;
|
||||
if (this.playingBgm) {
|
||||
this.player.pause(this.getId(this.playingBgm));
|
||||
}
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前的 bgm
|
||||
*/
|
||||
stop() {
|
||||
if (this.blocking || !this.enabled) return;
|
||||
if (this.playingBgm) {
|
||||
this.player.stop(this.getId(this.playingBgm));
|
||||
}
|
||||
this.playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
export const bgmController = new BgmController<BgmIds>(audioPlayer);
|
||||
|
||||
export function loadAllBgm() {
|
||||
const loading = Mota.require('var', 'loading');
|
||||
loading.once('coreInit', () => {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
for (const bgm of data.main.bgms) {
|
||||
bgmController.addBgm(bgm);
|
||||
}
|
||||
});
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { StreamLoader } from '../loader';
|
||||
import { audioPlayer, AudioRoute } from './player';
|
||||
import { guessTypeByExt, isAudioSupport } from './support';
|
||||
|
||||
export function loadAllBgm() {
|
||||
const loading = Mota.require('var', 'loading');
|
||||
loading.once('coreInit', () => {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
for (const bgm of data.main.bgms) {
|
||||
const type = guessTypeByExt(bgm);
|
||||
if (!type) continue;
|
||||
if (isAudioSupport(type)) {
|
||||
const source = audioPlayer.createElementSource();
|
||||
source.setSource(`project/bgms/${bgm}`);
|
||||
source.setLoop(true);
|
||||
const route = new AudioRoute(source, audioPlayer);
|
||||
audioPlayer.addRoute(`bgms.${bgm}`, route);
|
||||
} else {
|
||||
const source = audioPlayer.createStreamSource();
|
||||
const stream = new StreamLoader(`project/bgms/${bgm}`);
|
||||
stream.pipe(source);
|
||||
source.setLoop(true);
|
||||
const route = new AudioRoute(source, audioPlayer);
|
||||
audioPlayer.addRoute(`bgms.${bgm}`, route);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { OggVorbisDecoder } from '@wasm-audio-decoders/ogg-vorbis';
|
||||
import { OggVorbisDecoderWebWorker } from '@wasm-audio-decoders/ogg-vorbis';
|
||||
import { IAudioDecodeData, IAudioDecoder } from './source';
|
||||
import { OggOpusDecoder } from 'ogg-opus-decoder';
|
||||
import { OggOpusDecoderWebWorker } from 'ogg-opus-decoder';
|
||||
|
||||
export class VorbisDecoder implements IAudioDecoder {
|
||||
decoder?: OggVorbisDecoder;
|
||||
decoder?: OggVorbisDecoderWebWorker;
|
||||
|
||||
async create(): Promise<void> {
|
||||
this.decoder = new OggVorbisDecoder();
|
||||
this.decoder = new OggVorbisDecoderWebWorker();
|
||||
await this.decoder.ready;
|
||||
}
|
||||
|
||||
@ -19,15 +19,15 @@ export class VorbisDecoder implements IAudioDecoder {
|
||||
}
|
||||
|
||||
async flush(): Promise<IAudioDecodeData | undefined> {
|
||||
return await this.decoder?.flush();
|
||||
return this.decoder?.flush();
|
||||
}
|
||||
}
|
||||
|
||||
export class OpusDecoder implements IAudioDecoder {
|
||||
decoder?: OggOpusDecoder;
|
||||
decoder?: OggOpusDecoderWebWorker;
|
||||
|
||||
async create(): Promise<void> {
|
||||
this.decoder = new OggOpusDecoder();
|
||||
this.decoder = new OggOpusDecoderWebWorker();
|
||||
await this.decoder.ready;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { loadAllBgm } from './bgmLoader';
|
||||
import { loadAllBgm } from './bgm';
|
||||
import { OpusDecoder, VorbisDecoder } from './decoder';
|
||||
import { AudioStreamSource } from './source';
|
||||
import { AudioType } from './support';
|
||||
@ -11,4 +11,5 @@ export * from './support';
|
||||
export * from './effect';
|
||||
export * from './player';
|
||||
export * from './source';
|
||||
export * from './bgmLoader';
|
||||
export * from './bgm';
|
||||
export * from './decoder';
|
||||
|
@ -193,6 +193,14 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
||||
return this.audioRoutes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个音频播放路由
|
||||
* @param id 要移除的播放路由的名称
|
||||
*/
|
||||
removeRoute(id: string) {
|
||||
this.audioRoutes.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音频
|
||||
* @param id 音频名称
|
||||
@ -272,6 +280,14 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
export const enum AudioStatus {
|
||||
Playing,
|
||||
Pausing,
|
||||
Paused,
|
||||
Stoping,
|
||||
Stoped
|
||||
}
|
||||
|
||||
type AudioStartHook = (route: AudioRoute) => void;
|
||||
type AudioEndHook = (time: number, route: AudioRoute) => void;
|
||||
|
||||
@ -295,11 +311,18 @@ export class AudioRoute
|
||||
/** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */
|
||||
endTime: number = 0;
|
||||
|
||||
/** 是否已暂停,注意停止播放是不算暂停的 */
|
||||
paused: boolean = false;
|
||||
/** 当前播放状态 */
|
||||
status: AudioStatus = AudioStatus.Stoped;
|
||||
/** 暂停时刻 */
|
||||
private pauseTime: number = 0;
|
||||
|
||||
private shouldStop: boolean = false;
|
||||
/**
|
||||
* 每次暂停或停止时自增,用于判断当前正在处理的情况。
|
||||
* 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作
|
||||
*/
|
||||
stopIdentifier: number = 0;
|
||||
|
||||
private audioStartHook?: AudioStartHook;
|
||||
private audioEndHook?: AudioEndHook;
|
||||
|
||||
@ -341,16 +364,18 @@ export class AudioRoute
|
||||
* @param when 从音频的什么时候开始播放,单位秒
|
||||
*/
|
||||
play(when: number = 0) {
|
||||
if (this.source.playing) return;
|
||||
if (this.status === AudioStatus.Playing) return;
|
||||
this.link();
|
||||
if (this.effectRoute.length > 0) {
|
||||
const first = this.effectRoute[0];
|
||||
this.source.connect(first);
|
||||
const last = this.effectRoute.at(-1)!;
|
||||
last.connect({ input: this.player.getDestination() });
|
||||
} else {
|
||||
this.source.connect({ input: this.player.getDestination() });
|
||||
}
|
||||
this.source.play(when);
|
||||
this.paused = false;
|
||||
this.status = AudioStatus.Playing;
|
||||
this.pauseTime = 0;
|
||||
this.audioStartHook?.(this);
|
||||
this.startAllEffect();
|
||||
@ -361,29 +386,55 @@ export class AudioRoute
|
||||
* 暂停音频播放
|
||||
*/
|
||||
async pause() {
|
||||
if (this.paused || !this.source.playing) return;
|
||||
if (this.status !== AudioStatus.Playing) return;
|
||||
this.status = AudioStatus.Pausing;
|
||||
this.stopIdentifier++;
|
||||
const identifier = this.stopIdentifier;
|
||||
if (this.audioEndHook) {
|
||||
this.audioEndHook(this.endTime, this);
|
||||
await sleep(this.endTime);
|
||||
}
|
||||
if (
|
||||
this.status !== AudioStatus.Pausing ||
|
||||
this.stopIdentifier !== identifier
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const time = this.source.stop();
|
||||
this.pauseTime = time;
|
||||
this.paused = true;
|
||||
this.endAllEffect();
|
||||
this.emit('pause');
|
||||
if (this.shouldStop) {
|
||||
this.status = AudioStatus.Stoped;
|
||||
this.endAllEffect();
|
||||
this.emit('stop');
|
||||
this.shouldStop = false;
|
||||
} else {
|
||||
this.status = AudioStatus.Paused;
|
||||
this.endAllEffect();
|
||||
this.emit('pause');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续音频播放
|
||||
*/
|
||||
resume() {
|
||||
if (this.source.playing) return;
|
||||
if (this.paused) {
|
||||
if (this.status === AudioStatus.Playing) return;
|
||||
if (
|
||||
this.status === AudioStatus.Pausing ||
|
||||
this.status === AudioStatus.Stoping
|
||||
) {
|
||||
console.log(1);
|
||||
|
||||
this.audioStartHook?.(this);
|
||||
this.emit('resume');
|
||||
return;
|
||||
}
|
||||
if (this.status === AudioStatus.Paused) {
|
||||
this.play(this.pauseTime);
|
||||
} else {
|
||||
this.play(0);
|
||||
}
|
||||
this.paused = false;
|
||||
this.status = AudioStatus.Playing;
|
||||
this.pauseTime = 0;
|
||||
this.audioStartHook?.(this);
|
||||
this.startAllEffect();
|
||||
@ -394,13 +445,27 @@ export class AudioRoute
|
||||
* 停止音频播放
|
||||
*/
|
||||
async stop() {
|
||||
if (!this.source.playing) return;
|
||||
if (this.status !== AudioStatus.Playing) {
|
||||
if (this.status === AudioStatus.Pausing) {
|
||||
this.shouldStop = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.status = AudioStatus.Stoping;
|
||||
this.stopIdentifier++;
|
||||
const identifier = this.stopIdentifier;
|
||||
if (this.audioEndHook) {
|
||||
this.audioEndHook(this.endTime, this);
|
||||
await sleep(this.endTime);
|
||||
}
|
||||
if (
|
||||
this.status !== AudioStatus.Stoping ||
|
||||
this.stopIdentifier !== identifier
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.source.stop();
|
||||
this.paused = false;
|
||||
this.status = AudioStatus.Stoped;
|
||||
this.pauseTime = 0;
|
||||
this.endAllEffect();
|
||||
this.emit('stop');
|
||||
@ -473,3 +538,4 @@ export class AudioRoute
|
||||
}
|
||||
|
||||
export const audioPlayer = new AudioPlayer();
|
||||
// window.audioPlayer = audioPlayer;
|
||||
|
40
src/module/fallback/audio.ts
Normal file
40
src/module/fallback/audio.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Patch, PatchClass } from '@/common/patch';
|
||||
import { bgmController } from '../audio';
|
||||
import { mainSetting } from '@/core/main/setting';
|
||||
|
||||
export function patchAudio() {
|
||||
const patch = new Patch(PatchClass.Control);
|
||||
|
||||
const play = (bgm: BgmIds, when?: number) => {
|
||||
bgmController.play(bgm, when);
|
||||
};
|
||||
const pause = () => {
|
||||
bgmController.pause();
|
||||
};
|
||||
|
||||
patch.add('playBgm', function (bgm, startTime) {
|
||||
play(bgm, startTime);
|
||||
});
|
||||
patch.add('pauseBgm', function () {
|
||||
pause();
|
||||
});
|
||||
patch.add('resumeBgm', function () {
|
||||
bgmController.resume();
|
||||
});
|
||||
patch.add('checkBgm', function () {
|
||||
if (bgmController.playing) return;
|
||||
if (mainSetting.getValue('audio.bgmEnabled')) {
|
||||
if (bgmController.playingBgm) {
|
||||
bgmController.play(bgmController.playingBgm);
|
||||
} else {
|
||||
play(main.startBgm, 0);
|
||||
}
|
||||
} else {
|
||||
pause();
|
||||
}
|
||||
});
|
||||
patch.add('triggerBgm', function () {
|
||||
if (bgmController.playing) bgmController.pause();
|
||||
else bgmController.resume();
|
||||
});
|
||||
}
|
11
src/module/fallback/index.ts
Normal file
11
src/module/fallback/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Patch } from '@/common/patch';
|
||||
import { patchAudio } from './audio';
|
||||
|
||||
patchAudio();
|
||||
|
||||
export function patchAll() {
|
||||
const loading = Mota.require('var', 'loading');
|
||||
loading.once('coreInit', () => {
|
||||
Patch.patchAll();
|
||||
});
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import { patchAll } from './fallback';
|
||||
import { controller } from './weather';
|
||||
import { RainWeather } from './weather/rain';
|
||||
import { WeatherController } from './weather/weather';
|
||||
|
||||
patchAll();
|
||||
Mota.register('module', 'Weather', {
|
||||
controller,
|
||||
WeatherController,
|
||||
|
@ -5,8 +5,8 @@ import { Camera, CameraAnimation, ICameraScale } from '@/core/render/camera';
|
||||
import { LayerGroup } from '@/core/render/preset/layer';
|
||||
import { MotaRenderer } from '@/core/render/render';
|
||||
import { Sprite } from '@/core/render/sprite';
|
||||
import { bgm } from '@/core/audio/bgm';
|
||||
import { PointEffect, PointEffectType } from '../fx/pointShader';
|
||||
import { bgmController } from '@/module';
|
||||
|
||||
const path: Partial<Record<FloorIds, LocArr[]>> = {
|
||||
MT16: [
|
||||
@ -261,11 +261,12 @@ function initFromSave(chase: Chase) {
|
||||
}
|
||||
|
||||
function playAudio(from: number, chase: Chase) {
|
||||
bgm.changeTo('escape.mp3', from);
|
||||
bgm.blockChange();
|
||||
const playing = bgmController.playingBgm;
|
||||
bgmController.play('escape.mp3', from);
|
||||
bgmController.blockChange();
|
||||
chase.on('end', () => {
|
||||
bgm.unblockChange();
|
||||
bgm.undo();
|
||||
bgmController.unblockChange();
|
||||
if (playing) bgmController.play(playing);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ class Change extends RenderItem {
|
||||
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
_transform: Transform
|
||||
): void {
|
||||
if (this.backAlpha === 0) return;
|
||||
const ctx = canvas.ctx;
|
||||
|
@ -72,8 +72,8 @@ import { gameKey } from '@/core/main/custom/hotkey';
|
||||
import { mainUi } from '@/core/main/init/ui';
|
||||
import { CustomToolbar } from '@/core/main/custom/toolbar';
|
||||
import { mainSetting } from '@/core/main/setting';
|
||||
import { bgm as mainBgm } from '@/core/audio/bgm';
|
||||
import { mat4 } from 'gl-matrix';
|
||||
import { bgmController } from '@/module';
|
||||
|
||||
const props = defineProps<{
|
||||
num: number;
|
||||
@ -328,7 +328,7 @@ onMounted(async () => {
|
||||
resize();
|
||||
|
||||
soundChecked.value = mainSetting.getValue('audio.bgmEnabled', true);
|
||||
mainBgm.changeTo('title.mp3');
|
||||
bgmController.play('title.mp3');
|
||||
|
||||
start.style.opacity = '1';
|
||||
if (played) {
|
||||
@ -426,7 +426,8 @@ onUnmounted(() => {
|
||||
);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5),
|
||||
text-shadow:
|
||||
1px 1px 4px rgba(0, 0, 0, 0.5),
|
||||
-1px -1px 3px rgba(255, 255, 255, 0.3),
|
||||
5px 5px 5px rgba(0, 0, 0, 0.4);
|
||||
filter: brightness(1.8);
|
||||
@ -449,14 +450,17 @@ onUnmounted(() => {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
animation: cursor 2.5s linear 0s infinite normal running;
|
||||
transition: left 0.4s ease-out, top 0.4s ease-out,
|
||||
transition:
|
||||
left 0.4s ease-out,
|
||||
top 0.4s ease-out,
|
||||
opacity 1.5s ease-out;
|
||||
}
|
||||
|
||||
.start-button {
|
||||
position: relative;
|
||||
font: bold 1.5em 'normal';
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4),
|
||||
text-shadow:
|
||||
1px 1px 2px rgba(0, 0, 0, 0.4),
|
||||
0px 0px 1px rgba(255, 255, 255, 0.3);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
|
Loading…
Reference in New Issue
Block a user