diff --git a/_docs/api.md b/_docs/api.md
index a7c27925..becdd230 100644
--- a/_docs/api.md
+++ b/_docs/api.md
@@ -478,8 +478,12 @@ playBgm: fn(bgm: string, startTime?: number)
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过
-playSound: fn(sound: string)
+playSound: fn(sound: string, pitch?: number, callback?: fn()) -> number
播放一个音效
+sound: 音效名;可以使用文件别名。
+pitch: 播放的音调;可选,如果设置则为30-300之间的数值;100为正常音调。
+callback: 可选,播放完毕后执行的回调函数。
+返回:一个数字,可用于core.stopSound的参数来只停止该音效。
registerAnimationFrame: fn(name: string, needPlaying: bool, func?: fn(timestamp: number))
注册一个 animationFrame
@@ -550,6 +554,11 @@ destX: 鼠标或手指的起拖点横坐标
destY: 鼠标或手指的起拖点纵坐标
stepPostfix: 拖动轨迹的数组表示,每项为一步的方向和目标点。
+setBgmSpeed: fn(speed: number, usePitch?: bool)
+设置背景音乐的播放速度和音调
+speed: 播放速度,必须为30-300中间的值。100为正常速度。
+usePitch: 是否同时改变音调(部分设备可能不支持)
+
setBuff: fn(name: string, value: number)
设置主角某个属性的百分比修正倍率,初始值为1,
倍率存放在flag: '__'+name+'_buff__' 中
@@ -640,8 +649,8 @@ stopAutomaticRoute: fn()
stopReplay: fn(force?: bool)
停止播放
-stopSound: fn()
-停止所有SE
+stopSound: fn(id?: number)
+停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。
syncLoad: fn()
从服务器加载存档
diff --git a/_server/CodeMirror/defs.js b/_server/CodeMirror/defs.js
index dd4ee119..c69b4130 100644
--- a/_server/CodeMirror/defs.js
+++ b/_server/CodeMirror/defs.js
@@ -2189,12 +2189,12 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [
"!type": "fn(name: string, value: ?)"
},
"playSound": {
- "!doc": "播放一个音效",
- "!type": "fn(sound: string)"
+ "!doc": "播放一个音效
sound: 音效名;可以使用文件别名。
pitch: 播放的音调;可选,如果设置则为30-300之间的数值。
callback: 可选,播放完毕后执行的回调函数。
返回:一个数字,可用于core.stopSound的参数来只停止该音效。",
+ "!type": "fn(sound: string, pitch?: number, callback?: fn()) -> number"
},
"stopSound": {
- "!doc": "停止所有SE",
- "!type": "fn()"
+ "!doc": "停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。",
+ "!type": "fn(id?: number)"
},
"addGameCanvasTranslate": {
"!doc": "加减画布偏移",
diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4
index 3b4ee833..c9d4605f 100644
--- a/_server/MotaAction.g4
+++ b/_server/MotaAction.g4
@@ -823,6 +823,7 @@ action
| playSound_1_s
| stopSound_s
| setVolume_s
+ | setBgmSpeed_s
| win_s
| lose_s
| restart_s
@@ -2462,32 +2463,42 @@ return code;
*/;
playSound_s
- : '播放音效' EvalString '停止之前音效' Bool? Newline
+ : '播放音效' EvalString '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline
/* playSound_s
tooltip : playSound: 播放音效
helpUrl : /_docs/#/instruction
-default : ["item.mp3",false]
+default : ["item.mp3",false,"",false]
colour : this.soundColor
allSounds : ['EvalString_0']
material : ["./project/sounds/", "EvalString_0"]
+if (IntString_0) {
+ if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。';
+ IntString_0 = ', "pitch": ' + IntString_0;
+} else IntString_0 = '';
Bool_0 = Bool_0 ? ', "stop": true' : '';
-var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+'},\n';
+Bool_1 = Bool_1 ? ', "sync": true' : '';
+var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+IntString_0+Bool_1+'},\n';
return code;
*/;
playSound_1_s
- : '播放系统音效' NameMap_List '停止之前音效' Bool? Newline
+ : '播放系统音效' NameMap_List '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline
/* playSound_1_s
tooltip : playSound: 播放系统音效
helpUrl : /_docs/#/instruction
-default : ["确认",false]
+default : ["确认",false,"",false]
colour : this.soundColor
+if (IntString_0) {
+ if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。';
+ IntString_0 = ', "pitch": ' + IntString_0;
+} else IntString_0 = '';
Bool_0 = Bool_0 ? ', "stop": true' : '';
-var code = '{"type": "playSound", "name": "'+NameMap_List_0+'"'+Bool_0+'},\n';
+Bool_1 = Bool_1 ? ', "sync": true' : '';
+var code = '{"type": "playSound", "name": "'+NameMap_List_0+'"'+Bool_0+IntString_0+Bool_1+'},\n';
return code;
*/;
@@ -2518,6 +2529,21 @@ var code = '{"type": "setVolume", "value": '+Int_0+IntString_0+async+'},\n';
return code;
*/;
+setBgmSpeed_s
+ : '设置背景音乐播放速度' Int '同时改变音调' Bool Newline
+
+
+/* setBgmSpeed_s
+tooltip : setSpeed: 设置背景音乐播放速度
+helpUrl : /_docs/#/instruction
+default : [100, true]
+colour : this.soundColor
+if (Int_0 < 30 || Int_0 > 300) throw '速度只能设置只能在30-300之间;100为正常速度。';
+Bool_0 = Bool_0?', "pitch": true':'';
+var code = '{"type": "setBgmSpeed", "value": '+Int_0+Bool_0+'},\n';
+return code;
+*/;
+
win_s
: '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool '不结束游戏' Bool Newline
diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js
index 7ba294f9..3b673eaa 100644
--- a/_server/MotaActionParser.js
+++ b/_server/MotaActionParser.js
@@ -642,10 +642,10 @@ ActionParser.prototype.parseAction = function() {
var knownItems = MotaActionBlocks['NameMap_List'].options.map(function (one) {return one[1];});
if (knownItems.indexOf(data.name) >= 0) {
this.next = MotaActionBlocks['playSound_1_s'].xmlText([
- data.name,data.stop,this.next]);
+ data.name,data.stop,data.pitch||"",data.sync,this.next]);
} else {
this.next = MotaActionBlocks['playSound_s'].xmlText([
- data.name,data.stop,this.next]);
+ data.name,data.stop,data.pitch||"",data.sync,this.next]);
}
break;
case "playBgm":
@@ -676,6 +676,10 @@ ActionParser.prototype.parseAction = function() {
this.next = MotaActionBlocks['setVolume_s'].xmlText([
data.value, data.time, data.async||false, this.next]);
break
+ case "setBgmSpeed":
+ this.next = MotaActionBlocks['setBgmSpeed_s'].xmlText([
+ data.value, data.pitch||false, this.next]);
+ break;
case "setValue":
this.next = MotaActionBlocks['setValue_s'].xmlText([
this.expandIdBlock([data.name]), data["operator"]||'=',
diff --git a/_server/editor_blocklyconfig.js b/_server/editor_blocklyconfig.js
index fea6aaa9..2e9ab074 100644
--- a/_server/editor_blocklyconfig.js
+++ b/_server/editor_blocklyconfig.js
@@ -213,6 +213,7 @@ editor_blocklyconfig=(function(){
MotaActionBlocks['playSound_1_s'].xmlText(),
MotaActionBlocks['stopSound_s'].xmlText(),
MotaActionBlocks['setVolume_s'].xmlText(),
+ MotaActionBlocks['setBgmSpeed_s'].xmlText(),
MotaActionBlocks['callBook_s'].xmlText(),
MotaActionBlocks['callSave_s'].xmlText(),
MotaActionBlocks['autoSave_s'].xmlText(),
diff --git a/libs/control.js b/libs/control.js
index 343404cd..f064cd1b 100644
--- a/libs/control.js
+++ b/libs/control.js
@@ -2695,6 +2695,25 @@ control.prototype._playBgm_play = function (bgm, startTime) {
core.material.bgms[bgm].play();
core.musicStatus.playingBgm = bgm;
core.musicStatus.lastBgm = bgm;
+ core.setBgmSpeed(100);
+}
+
+///// 设置当前背景音乐的播放速度 //////
+control.prototype.setBgmSpeed = function (speed, usePitch) {
+ var bgm = core.musicStatus.playingBgm;
+ if (main.mode!='play' || !core.material.bgms[bgm]) return;
+ bgm = core.material.bgms[bgm];
+ if (speed < 30 || speed > 300) return;
+ bgm.playbackRate = speed / 100;
+ core.musicStatus.bgmSpeed = speed;
+
+ if (bgm.preservesPitch != null) {
+ if (bgm.__preservesPitch == null) bgm.__preservesPitch = bgm.preservesPitch;
+ if (usePitch == null) bgm.preservesPitch = bgm.__preservesPitch;
+ else if (usePitch) bgm.preservesPitch = false;
+ else bgm.preservesPitch = true;
+ core.musicStatus.bgmUsePitch = usePitch;
+ }
}
////// 暂停背景音乐的播放 //////
@@ -2718,8 +2737,13 @@ control.prototype.pauseBgm = function () {
control.prototype.resumeBgm = function (resumeTime) {
if (main.mode!='play')return;
try {
+ var speed = core.musicStatus.bgmSpeed;
+ var usePitch = core.musicStatus.bgmUsePitch;
core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm || main.startBgm,
resumeTime ? core.musicStatus.pauseTime : 0);
+ if (resumeTime) {
+ core.setBgmSpeed(speed, usePitch);
+ }
}
catch (e) {
console.log("无法恢复BGM");
@@ -2748,7 +2772,7 @@ control.prototype.triggerBgm = function () {
}
////// 播放音频 //////
-control.prototype.playSound = function (sound) {
+control.prototype.playSound = function (sound, pitch, callback) {
sound = core.getMappedName(sound);
if (main.mode!='play' || !core.musicStatus.soundStatus || !core.material.sounds[sound]) return;
try {
@@ -2757,16 +2781,22 @@ control.prototype.playSound = function (sound) {
source.buffer = core.material.sounds[sound];
source.connect(core.musicStatus.gainNode);
var id = setTimeout(null);
+ if (pitch && pitch >= 30 && pitch <= 300) {
+ source.playbackRate.setValueAtTime(pitch / 100, 0);
+ }
source.onended = function () {
delete core.musicStatus.playingSounds[id];
+ if (callback) callback();
}
+ core.musicStatus.playingSounds[id] = source;
if (source.start) source.start(0);
else if (source.noteOn) source.noteOn(0);
- core.musicStatus.playingSounds[id] = source;
+ return id;
}
else {
core.material.sounds[sound].volume = core.musicStatus.userVolume;
core.material.sounds[sound].play();
+ if (callback) callback();
}
}
catch (e) {
@@ -2776,18 +2806,23 @@ control.prototype.playSound = function (sound) {
}
////// 停止所有音频 //////
-control.prototype.stopSound = function () {
- for (var i in core.musicStatus.playingSounds) {
- var source = core.musicStatus.playingSounds[i];
- try {
- if (source.stop) source.stop();
- else if (source.noteOff) source.noteOff();
- }
- catch (e) {
- main.log(e);
- }
+control.prototype.stopSound = function (id) {
+ if (id == null) {
+ Object.keys(core.musicStatus.playingSounds).forEach(function (id) {
+ core.control.stopSound(id);
+ });
+ return;
}
- core.musicStatus.playingSounds = {};
+ var source = core.musicStatus.playingSounds[id];
+ if (!source) return;
+ try {
+ if (source.stop) source.stop();
+ else if (source.noteOff) source.noteOff();
+ }
+ catch (e) {
+ main.log(e);
+ }
+ delete core.musicStatus.playingSounds[id];
}
////// 检查bgm状态 //////
diff --git a/libs/core.js b/libs/core.js
index 11ee0a3a..4b31fc3e 100644
--- a/libs/core.js
+++ b/libs/core.js
@@ -64,6 +64,8 @@ function core() {
'playingSounds': {}, // 正在播放的SE
'userVolume': 1.0, // 用户音量
'designVolume': 1.0, //设计音量
+ 'bgmSpeed': 100, // 背景音乐速度
+ 'bgmUsePitch': null, // 是否同时修改音调
'cachedBgms': [], // 缓存BGM内容
'cachedBgmCount': 8, // 缓存的bgm数量
}
diff --git a/libs/events.js b/libs/events.js
index 5eed254f..6e0c2966 100644
--- a/libs/events.js
+++ b/libs/events.js
@@ -1722,8 +1722,12 @@ events.prototype._action_freeBgm = function (data, x, y, prefix) {
events.prototype._action_playSound = function (data, x, y, prefix) {
if (data.stop) core.stopSound();
- core.playSound(data.name);
- core.doAction();
+ if (data.sync) {
+ core.playSound(data.name, data.pitch, core.doAction);
+ } else {
+ core.playSound(data.name, data.pitch);
+ core.doAction();
+ }
}
events.prototype._action_stopSound = function (data, x, y, prefix) {
@@ -1737,6 +1741,11 @@ events.prototype._action_setVolume = function (data, x, y, prefix) {
this.__action_doAsyncFunc(data.async, core.setVolume, data.value, data.time || 0);
}
+events.prototype._action_setBgmSpeed = function (data, x, y, prefix) {
+ core.setBgmSpeed(data.value, data.pitch || false);
+ core.doAction();
+}
+
events.prototype._action_setValue = function (data, x, y, prefix) {
this.setValue(data.name, data.operator, data.value, prefix);
if (!data.norefresh) {
diff --git a/runtime.d.ts b/runtime.d.ts
index 2ea4c36e..8f393565 100644
--- a/runtime.d.ts
+++ b/runtime.d.ts
@@ -774,6 +774,9 @@ declare class control {
/** 恢复背景音乐的播放 */
resumeBgm(resumeTime?: number): void
+ /** 设置背景音乐的播放速度和音调 */
+ setBgmSpeed(speed: number, usePitch?: bool): void
+
/** 设置音乐图标的显隐状态 */
setMusicBtn(): void
@@ -781,10 +784,10 @@ declare class control {
triggerBgm(): void
/** 播放一个音效 */
- playSound(sound: string): void
+ playSound(sound: string, pitch?: number, callback?: () => any): number
- /** 停止所有音频 */
- stopSound(): void
+ /** 停止(所有)音频 */
+ stopSound(id?: number): void
/** 检查bgm状态 */
checkBgm(): void