diff --git a/_saves/Eustia_autoSave b/_saves/Eustia_autoSave index 2b31cdd..2a3d507 100644 --- a/_saves/Eustia_autoSave +++ b/_saves/Eustia_autoSave @@ -1 +1 @@ -N4IgZgNg9lBOCSATEAuEBnALrApjzADAIwgA0IAFjrFKqAJYC2AhgOY6qXVQB0A7jgBGABzIhmAOybNMHFGGYR0OchOaM5IAMLMmYiADdURchWEsAHqgBMBO6dEoidguRZrLN1yHfNU3mQBrL3JEHDAQnzCIlFs3KAkcAE9/chwLR28cAEcAV3pzHAlMVABtAF1yellGdDoQAGMErElMOpRQQRhgp3JIFKcAX3JMGCU6YZAc/OF20HQ+OEQSIcnoBvrEelwGzHoEzlzRcisUAFZyAedJyDY5kGqcRgARfF0IVGxclRBc5QAhfACIoAGXorAoJRQXx+AH1YcocIh4cYACxEVEADgIAGYAGyYkwgeGwSTI2FojHY/GE8jMXDMdqlUAeTQAHVyqJwBEEHLODVR1jELFmZW0ukYFCgGhA5WGLPU7M53N5uX5guFzFFKFKIAAVvRcqxcpJZfKQKzOByuTy+QKhW4tUyQABpAAqADUzaQFTK0ByAOzWPHWQPWMAkR3a3Xuj0AMSFcp9FsVVtyQZDYYjmujIFYUAgiECyW9vqVNtV6odPidYsE9Aof1ytCTZbTNuYdo1UedBuYUEwklYpZTfpA1u5nbV9pzzvQVAkrAkBilMtbo/Lk671ZFc4o9GXq4468t/vTwdD5+zPbFBqR/ZHp/H58zV8jNdzWFw+GIYi/eEIatjSHBoKCHMRgMXUCh2rCx6H7OCh1/E54KgRDFwIWDUPQ1gcUfVMUAkXIIAgG8dRAJJ6DyEgTwIoiSLI3VKLyRNzVPejSI/Z1mNyPDaL9DjGIoqjOXwgTiM43cxR4s4xLkQSuOkkS8Tk1AFKk8ieIDVTCIkoSeMxHT1NrTSRIATiMvTFNM6iCDNSoMECApUAUJQfjA2BkBQbx4Q8xAtALOAUTQXBkHIeFHkYAAJA9MGCipwthCAcGYAwcBBKAGmC4BJnhAx6HQR5yXqf8fxWGFJkEIE8AkABBTBBwaQJnhkPxdIY4lYWYHB6Hihz4S2dBmEEZLio6DBsAA38dVKIgcTOSoKjlG4C2gARYCZBysBwbUiRYTAem8dBhDwLy4gm2BjACVh6CuqpijuxoDicbwWBujZ5EUZRyBaPYsHoBp7lGQcIDdJg5Dm6wzOsM5MQDMyeDMsy8TxIgzKIC5GlyWBYDBv0MdRBGzJcEmScxsxUhAQQZEwZLKcYBIS28tIMkp6mGuSlq3rkbxhCgAqEi5tgeZZ7BmCF9h6agNLnm2HBdggAZvHBCQ4BwABlWRtSOwdYChaG8TOHg8UGSY/M4WcSsmsr6hFMpZucIhrBMIhHesaxSFsOxUVcL33YuL25sxT27HdgMQ8w6xg79mGI5h33Q+scOY+jlxUTxUg5rsYnFtd4hrFRUgE+91PMJxHEI4xDO/fmuP8Triu/bOYvrFrlOI4IM5C6zggzMxXO3cL4vO+ruwMcbseu8rs4A9D1FC79oNp4X0OA1HyPS877ucTsPECAH/Oh7jpfA9RyuQ3PifMLX8/S6IAMTEDpfXADXEk8zneCAJA+naP0+77xGZSur9gGPzHifFw2IO60i9gQTEGcX64lrnnV2qIf4FyLsA9e99k7gLvvDUgD8AzwNIMTL+hcyF4gobvI+lDu5uwWqQB2h9MGwMxJvTEQDYEEBXi4WeLgM5ELMhcVwojMEoJhj/IgwcUHSI7sjTOjsiAZzzphMRsCMSYLEWIiRjDSjQy0YolhCcTA6IYUYp2HsJFWLdjY/O1hFraIsXI2Rqc7GuxkUozx9jDGuDMmgphWd75OJfqY3x4T96BJ3vfYOISImGKCQSD+xBAG5x3v7ZJyigGJOydEwBmT8lBJAdYix7sCkyLybklJVT3bb2IAGAM5RlrkBoLkWQnAXRQATFoVErAAC8YgDCKG+HMSYaUNr7AkGmMyOAcSID5IIZugZmBmXmbkYmtg+SYi7naAgYAOTIyIBEcgxp6BeRAHiQQXJBCIGIbCIgzBUTMFhKiMA8NYSYkQDiQQsIzjzWsOEVEiBEBgH8WIPY+MAw0kFEjchmJBhAA=== \ No newline at end of file +N4IgZgNg9lBOCSATEAuEBnALrApjzADAIwgA0IAFjrFKqAJYC2AhgOY6qXVQB0A7jgBGABzIhmAOybNMHFGGYR0OchOaM5IAMLMmYiADdURchWEsAHqgCct66dEoiBF+RZrLqALQmQ75qgE5DIA1sauIIg4YOFBflExKHHownjISW5QEjgAnoHkOBaOcTgAjgCu9OY4EpioANoAuuT0sozodCAAxllYkpgdKKCCMGFO5JB5TgC+5JgwSnSzIGWVwoOg6HxwiCQzy9BdnYj0uF2Y9Fmc5aLkVigArORTzsuQbBsgrTiMACL4uggqGw5RUIHKygAQvgBDUADL0VgUOooEFggD66OUOEQmOMAHYAGwEawADgATOSACxxTGwSS49EE4lkyk04K4ZiDeqgDyaAA65SpOAIgkFDy6VPJYhY6wa2l0jAoUA0IEas156gFQpFYvKEqlMuYcpQ9RAACt6OVWOVJGqNSA+ZxBcLReLJdK3MbuSAANIAFQAavbSJrVWhBfjyYTyZHyWASF6TWaA4GAGLS9Whx1a53lKMxuMJo3JkCsKAQRAhXIhsPa116g2evze+WCegUCHlWhZut513Md2GpM+y3MKCYSSsWs58MgF0iwf6j0ln3oKgSVgSAzK1W92f1xdD5uytcUejb3ccfdOiP56Ox+/Fkfyy048cz2/z++Fp+JlullguD4MQYhAXghDNjaU5dBQU5iNBm6wVOzYWPQ45oVOoF3OhUCYZuBCobh+GsAAzJ+uYoBI5QQBAL6miAOT0BUJA3pR1G0fRZpMRUmYOreHF0QBPo8eU5FseGglcYxzFChRkk0UJp7yqJDzyXIUnCSpsmEupqCacpDGifielUYp0miaSpkGa2RmydY1nmVpdksQQ9rNBgIRVKgChKGCcGwOktLogFiBaBWcB4mguDIOQmLfIwAASF6YFFTRxeiEA4MwBg4HCUBdFFwDLJiBj0Og3yMp04EgXsaLLIIMJ4BIACCmCTl0IS/DIARmZxICYswOD0GlHmYic6DMIIWVVUMGDYBBoGmvURCkQ8zRNOqbwVtAAiwNyHlYDgJq+H0FxYPQXSfPMk4QP6TByOSDxEEQUpRjwhJ2C91hPN05SwLA93hkQDxUtYPCkXYUNQ4SDj5CAggyJgWXw4wWQ1hkKxFPDiPtVl3UsOw8PCFA5VZATbByCUFjYMwFNE5jaO5b8pw4OcEBTHEiISHAOAAMqyCaySTrAKKElSDykTwVLTMsoWcKu1ULbVnSyg0QQa6Qmva1ruv1D4pAG0bJgG0ExuGyblsWxbzQa1t5A0OUsicL6UAZloVKsAAvGIBiKKCGzLLl+2XBIebWDgpGIOKggPAQkbMNY0flNYhHx/qpKg+6BBgIK1iEkQMTkDa9DpCA5LMIghKEog1iFYgYBgFS6JUqSifooIVKEmA6JgIgU0EDgNJV3gYgXMD+KkficfWKRBBSi40xAA== \ No newline at end of file diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index b313060..10b3ad5 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -2243,12 +2243,12 @@ setanimate_s tooltip : setanimate:设置帧动画/特效(此项仅储存,不播放) helpUrl : /_docs/#/instruction default : ["sword","","",192,192,60] -colour : this.soundColor +colour : this.imageColor IntString_0 = IntString_0 ? (', "px": '+IntString_0+'') : ''; IntString_1 = IntString_1 ? (', "py": '+IntString_1+'') : ''; var imageList=animateDrawableimage_0?',"imageList": [\n'+animateDrawableimage_0.slice(0,-1)+'\n]':'' var soundList=animateDrawablesound_0?',"soundList": [\n'+animateDrawablesound_0.slice(0,-1)+'\n]':'' -var code = '{"type": "setanimate", "name": "'+EvalString_0+'",'+IntString_0+IntString_1+' "width": '+IntString_2+', "height": '+IntString_3+', "allFarme": '+IntString_4+imageList+soundList+'},\n'; +var code = '{"type": "setanimate", "name": "'+EvalString_0+'"'+IntString_0+IntString_1+' ,"width": '+IntString_2+', "height": '+IntString_3+', "allFarme": '+IntString_4+imageList+soundList+'},\n'; return code; */; @@ -2322,9 +2322,9 @@ deleteanimate_s /* deleteanimate_s tooltip : deleteanimate:删除储存的帧动画 helpUrl : /_docs/#/instruction -default : [""] +default : ["zone"] -colour : this.soundColor +colour : this.imageColor var code = '{"type": "deleteanimate", "name": "'+EvalString_0+'"},\n'; return code; @@ -2338,7 +2338,7 @@ tooltip : playanimate:播放帧动画,选择跟随勇士后x、y将失效改 helpUrl : /_docs/#/instruction default : ["zone","","",false,"",""] -colour : this.soundColor +colour : this.imageColor IntString_0 = IntString_0 ? (', "x": '+IntString_0+'') : ''; IntString_1 = IntString_1 ? (', "y": '+IntString_1+'') : ''; if(EvalString_1&&!/^(0|([1-9][0-9]*))(\.[\d]+)?$/.test(EvalString_1))throw new Error("此项仅能填写小数、整数或不填"); @@ -2356,7 +2356,7 @@ clearanimate_s tooltip : clearanimate:清空正在播放的帧动画 helpUrl : /_docs/#/instruction -colour : this.soundColor +colour : this.imageColor var code = '{"type": "clearanimate"},\n'; return code; @@ -2404,7 +2404,7 @@ stopAnimate_s tooltip : stopAnimate:停止所有动画 helpUrl : /_docs/#/instruction default : [false] -colour : this.soundColor +colour : this.imageColor Bool_0 = Bool_0?', "doCallback": true':''; var code = '{"type": "stopAnimate"'+Bool_0+'},\n'; return code; diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js index 829580d..06c2081 100644 --- a/_server/MotaActionParser.js +++ b/_server/MotaActionParser.js @@ -777,18 +777,18 @@ MotaActionParser = function () { break; case "deleteanimate": this.next = MotaActionBlocks["deleteanimate_s"].xmlText([ - this.name, + data.name, this.next, ]); break; case "playanimate": this.next = MotaActionBlocks["playanimate_s"].xmlText([ - this.name, - this.x, - this.y, - this.hero, - this.scalex, - this.scaley, + data.name, + data.x, + data.y, + data.hero, + data.scalex, + data.scaley, this.next, ]); break; diff --git a/_server/config.json b/_server/config.json index e97cbbe..bdcb574 100644 --- a/_server/config.json +++ b/_server/config.json @@ -1 +1 @@ -{"viewportLoc":[0,0],"editorLastFloorId":"KTV"} \ No newline at end of file +{"viewportLoc":[0,0],"editorLastFloorId":"jiedao"} \ No newline at end of file diff --git a/project/data.js b/project/data.js index 04a1131..5b8e9ed 100644 --- a/project/data.js +++ b/project/data.js @@ -904,118 +904,118 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "theme.opus" ], "sounds": [ - "aiy010000010.mp3", - "aiy010000020.mp3", - "aiy010000030.mp3", - "aiy020000005.mp3", - "aiy020000010.mp3", - "aiy020000020.mp3", - "aiy020000030.mp3", - "aiy020000040.mp3", - "aiy020000050.mp3", - "aiy020000060.mp3", - "aiy020000070.mp3", - "aiy020000080.mp3", - "aiy020000090.mp3", - "aiy020000100.mp3", - "aiy020000110.mp3", - "aiy020000120.mp3", - "aiy020000130.mp3", - "aiy020000140.mp3", - "aiy020000150.mp3", - "aiy020000160.mp3", - "aiy020000170.mp3", - "aiy020000180.mp3", - "aiy310000010.mp3", - "aiy310000020.mp3", - "aiy310000030.mp3", - "aiy310000040.mp3", - "aiy310000050.mp3", - "aiy310000060.mp3", - "aiy310000070.mp3", - "aiy310000080.mp3", - "aiy310000090.mp3", - "aiy310000100.mp3", - "aiy310000110.mp3", - "aiy310000120.mp3", - "aiy310000130.mp3", - "aiy310000140.mp3", - "aiy310000150.mp3", - "aiy310000160.mp3", - "aiy310000170.mp3", - "aiy310000180.mp3", - "aiy310000190.mp3", - "aiy310000200.mp3", - "aiy310000210.mp3", - "aiy310000220.mp3", - "aiy310000230.mp3", - "aiy310000240.mp3", - "aiy310000250.mp3", - "aiy310000260.mp3", - "aiy310000280.mp3", - "aiy310000290.mp3", - "aiy310000300.mp3", - "aiy350000010.mp3", - "aiy350000020.mp3", - "aiy350000030.mp3", - "aiy350000040.mp3", - "aiy350000050.mp3", - "aiy350000060.mp3", - "aiy350000070.mp3", - "aiy350000080.mp3", - "aiy350000090.mp3", - "aiy350000100.mp3", - "aiy350000110.mp3", - "aiy350000120.mp3", - "aiy350000130.mp3", - "aiy350000140.mp3", - "aiy350000150.mp3", - "aiy350000160.mp3", - "aiy350000170.mp3", - "aiy350000180.mp3", - "aiy350000190.mp3", - "aiy350000200.mp3", - "aiy350000210.mp3", - "aiy350000220.mp3", - "aiy350000230.mp3", - "aiy710000010.mp3", - "aiy710000020.mp3", - "aiy710000030.mp3", - "aiy710000040.mp3", - "aiy710000050.mp3", - "aiy710000060.mp3", - "aiy710000070.mp3", - "aiy710000080.mp3", - "aiy710000090.mp3", - "aiy710000100.mp3", - "aiy710000110.mp3", - "aiy710000120.mp3", - "aiy710000130.mp3", - "aiy820000010.mp3", - "aiy820000020.mp3", - "attack.mp3", + "aiy010000010.opus", + "aiy010000020.opus", + "aiy010000030.opus", + "aiy020000005.opus", + "aiy020000010.opus", + "aiy020000020.opus", + "aiy020000030.opus", + "aiy020000040.opus", + "aiy020000050.opus", + "aiy020000060.opus", + "aiy020000070.opus", + "aiy020000080.opus", + "aiy020000090.opus", + "aiy020000100.opus", + "aiy020000110.opus", + "aiy020000120.opus", + "aiy020000130.opus", + "aiy020000140.opus", + "aiy020000150.opus", + "aiy020000160.opus", + "aiy020000170.opus", + "aiy020000180.opus", + "aiy310000010.opus", + "aiy310000020.opus", + "aiy310000030.opus", + "aiy310000040.opus", + "aiy310000050.opus", + "aiy310000060.opus", + "aiy310000070.opus", + "aiy310000080.opus", + "aiy310000090.opus", + "aiy310000100.opus", + "aiy310000110.opus", + "aiy310000120.opus", + "aiy310000130.opus", + "aiy310000140.opus", + "aiy310000150.opus", + "aiy310000160.opus", + "aiy310000170.opus", + "aiy310000180.opus", + "aiy310000190.opus", + "aiy310000200.opus", + "aiy310000210.opus", + "aiy310000220.opus", + "aiy310000230.opus", + "aiy310000240.opus", + "aiy310000250.opus", + "aiy310000260.opus", + "aiy310000280.opus", + "aiy310000290.opus", + "aiy310000300.opus", + "aiy350000010.opus", + "aiy350000020.opus", + "aiy350000030.opus", + "aiy350000040.opus", + "aiy350000050.opus", + "aiy350000060.opus", + "aiy350000070.opus", + "aiy350000080.opus", + "aiy350000090.opus", + "aiy350000100.opus", + "aiy350000110.opus", + "aiy350000120.opus", + "aiy350000130.opus", + "aiy350000140.opus", + "aiy350000150.opus", + "aiy350000160.opus", + "aiy350000170.opus", + "aiy350000180.opus", + "aiy350000190.opus", + "aiy350000200.opus", + "aiy350000210.opus", + "aiy350000220.opus", + "aiy350000230.opus", + "aiy710000010.opus", + "aiy710000020.opus", + "aiy710000030.opus", + "aiy710000040.opus", + "aiy710000050.opus", + "aiy710000060.opus", + "aiy710000070.opus", + "aiy710000080.opus", + "aiy710000090.opus", + "aiy710000100.opus", + "aiy710000110.opus", + "aiy710000120.opus", + "aiy710000130.opus", + "aiy820000010.opus", + "aiy820000020.opus", "attack.opus", - "bomb.mp3", - "cancel.mp3", - "centerFly.mp3", - "confirm.mp3", - "cursor.mp3", - "door.mp3", - "equip.mp3", - "error.mp3", - "floor.mp3", - "gem.mp3", - "icePickaxe.mp3", - "item.mp3", - "jingbao.mp3", - "jump.mp3", - "load.mp3", - "open_ui.mp3", - "pickaxe.mp3", - "recovery.mp3", - "save.mp3", - "shop.mp3", - "zone.mp3" + "attack.opus", + "bomb.opus", + "cancel.opus", + "centerFly.opus", + "confirm.opus", + "cursor.opus", + "door.opus", + "equip.opus", + "error.opus", + "floor.opus", + "gem.opus", + "icePickaxe.opus", + "item.opus", + "jingbao.opus", + "jump.opus", + "load.opus", + "open_ui.opus", + "pickaxe.opus", + "recovery.opus", + "save.opus", + "shop.opus", + "zone.opus" ], "fonts": [ "HATTEN", @@ -1024,29 +1024,29 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "simhei" ], "nameMap": { - "确定": "confirm.mp3", - "取消": "cancel.mp3", - "操作失败": "error.mp3", - "光标移动": "cursor.mp3", - "打开界面": "open_ui.mp3", - "读档": "load.mp3", - "存档": "save.mp3", - "获得道具": "item.mp3", - "回血": "recovery.mp3", - "炸弹": "bomb.mp3", - "飞行器": "centerFly.mp3", - "开关门": "door.mp3", - "上下楼": "floor.mp3", - "跳跃": "jump.mp3", - "破墙镐": "pickaxe.mp3", - "破冰镐": "icePickaxe.mp3", - "宝石": "gem.mp3", - "阻激夹域": "zone.mp3", - "穿脱装备": "equip.mp3", - "背景音乐": "bgm.mp3", - "攻击": "attack.mp3", + "确定": "confirm.opus", + "取消": "cancel.opus", + "操作失败": "error.opus", + "光标移动": "cursor.opus", + "打开界面": "open_ui.opus", + "读档": "load.opus", + "存档": "save.opus", + "获得道具": "item.opus", + "回血": "recovery.opus", + "炸弹": "bomb.opus", + "飞行器": "centerFly.opus", + "开关门": "door.opus", + "上下楼": "floor.opus", + "跳跃": "jump.opus", + "破墙镐": "pickaxe.opus", + "破冰镐": "icePickaxe.opus", + "宝石": "gem.opus", + "阻激夹域": "zone.opus", + "穿脱装备": "equip.opus", + "背景音乐": "bgm.opus", + "攻击": "attack.opus", "背景图": "bg.webp", - "商店": "shop.mp3", + "商店": "shop.opus", "领域": "zone" }, "levelChoose": null, diff --git a/project/events.js b/project/events.js index 3ec338a..1e860d2 100644 --- a/project/events.js +++ b/project/events.js @@ -146,7 +146,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "chapter0": [ { "type": "playBgm", - "name": "Crawler.mp3", + "name": "Crawler.opus", "keep": true }, { @@ -892,7 +892,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "少女", "time": 30, "wait": 1000, - "sound": "aiy010000010.mp3", + "sound": "aiy010000010.opus", "text": "「呃······!?」", "bodyList": [ { @@ -1104,7 +1104,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "少女", "time": 30, "wait": 2000, - "sound": "aiy010000020.mp3", + "sound": "aiy010000020.opus", "text": "「······被杀」", "bodyList": [ { @@ -1192,7 +1192,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "少女", "time": 30, "wait": 1000, - "sound": "aiy010000030.mp3", + "sound": "aiy010000030.opus", "text": "「不,不要······」", "bodyList": [ { @@ -1204,7 +1204,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = }, { "type": "playBgm", - "name": "Blind_Alley.mp3", + "name": "Blind_Alley.opus", "keep": true }, { @@ -1316,7 +1316,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000010.mp3", + "sound": "aiy710000010.opus", "text": "「放开我!」", "bodyList": [ { @@ -1338,7 +1338,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000020.mp3", + "sound": "aiy710000020.opus", "text": "「我只是在帮那个女人而已!」", "bodyList": [ { @@ -1369,7 +1369,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000030.mp3", + "sound": "aiy710000030.opus", "text": "「你们没听到吗!?」", "bodyList": [ { @@ -1391,7 +1391,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000040.mp3", + "sound": "aiy710000040.opus", "text": "「她是被受骗才会被卖到娼馆来的」", "bodyList": [ { @@ -1413,7 +1413,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000050.mp3", + "sound": "aiy710000050.opus", "text": "「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」", "bodyList": [ { @@ -1444,7 +1444,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000060.mp3", + "sound": "aiy710000060.opus", "text": "「给我说些什么啊」", "bodyList": [ { @@ -1466,7 +1466,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000010.mp3", + "sound": "aiy310000010.opus", "text": "「这些话等到了娼馆再说吧」", "bodyList": [ { @@ -1488,7 +1488,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000020.mp3", + "sound": "aiy310000020.opus", "text": "「我来抓你,只是受雇于人而已」", "bodyList": [ { @@ -1563,7 +1563,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000010.mp3", + "sound": "aiy350000010.opus", "text": "「这不是凯伊姆先生吗,辛苦了」", "bodyList": [ { @@ -1585,7 +1585,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000020.mp3", + "sound": "aiy350000020.opus", "text": "「委托已经完成了吗?」", "bodyList": [ { @@ -1607,7 +1607,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000030.mp3", + "sound": "aiy310000030.opus", "text": "「啊啊,是这家伙没错吧」", "bodyList": [ { @@ -1651,7 +1651,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000030.mp3", + "sound": "aiy350000030.opus", "text": "「没错,就是这个人」", "bodyList": [ { @@ -1673,7 +1673,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000040.mp3", + "sound": "aiy310000040.opus", "text": "「是么」", "bodyList": [ { @@ -1695,7 +1695,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000070.mp3", + "sound": "aiy710000070.opus", "text": "「你,你们要对我做什么」", "bodyList": [ { @@ -1717,7 +1717,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000040.mp3", + "sound": "aiy350000040.opus", "text": "「······」", "bodyList": [ { @@ -1783,7 +1783,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000050.mp3", + "sound": "aiy350000050.opus", "text": "「抱歉啊,总是麻烦你去做这些无聊的事」", "bodyList": [ { @@ -1805,7 +1805,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000060.mp3", + "sound": "aiy350000060.opus", "text": "「都怪我们这边的年轻人太没用」", "bodyList": [ { @@ -1827,7 +1827,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000050.mp3", + "sound": "aiy310000050.opus", "text": "「客套话就免了」", "bodyList": [ { @@ -1849,7 +1849,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000070.mp3", + "sound": "aiy350000070.opus", "text": "「这还真是失礼了」", "bodyList": [ { @@ -1871,7 +1871,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000080.mp3", + "sound": "aiy350000080.opus", "text": "「喂,来个人」", "bodyList": [ { @@ -1893,7 +1893,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "光头男人", "time": 30, "wait": 1000, - "sound": "aiy820000010.mp3", + "sound": "aiy820000010.opus", "text": "「是」", "bodyList": [ { @@ -1915,7 +1915,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000090.mp3", + "sound": "aiy350000090.opus", "text": "「凯伊姆先生做完工作回来了」", "bodyList": [ { @@ -1937,7 +1937,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "光头男人", "time": 30, "wait": 1000, - "sound": "aiy820000020.mp3", + "sound": "aiy820000020.opus", "text": "「是,是,那个······」", "bodyList": [ { @@ -1959,7 +1959,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000100.mp3", + "sound": "aiy350000100.opus", "text": "「我是要你拿些酒来,这个蠢材!」", "bodyList": [ { @@ -2047,7 +2047,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000060.mp3", + "sound": "aiy310000060.opus", "text": "「不用这么麻烦」", "bodyList": [ { @@ -2069,7 +2069,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000070.mp3", + "sound": "aiy310000070.opus", "text": "「我接下来要去《菲诺列塔》」", "bodyList": [ { @@ -2091,7 +2091,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000110.mp3", + "sound": "aiy350000110.opus", "text": "「喔唷」", "bodyList": [ { @@ -2113,7 +2113,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000120.mp3", + "sound": "aiy350000120.opus", "text": "「既然如此,我就不留您在这里喝难饮的劣质酒了」", "bodyList": [ { @@ -2157,7 +2157,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000080.mp3", + "sound": "aiy310000080.opus", "text": "「用这些钱去买药」", "bodyList": [ { @@ -2201,7 +2201,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000130.mp3", + "sound": "aiy350000130.opus", "text": "「凯伊姆先生,不用对他们这么好」", "bodyList": [ { @@ -2223,7 +2223,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000090.mp3", + "sound": "aiy310000090.opus", "text": "「无妨」", "bodyList": [ { @@ -2245,7 +2245,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000100.mp3", + "sound": "aiy310000100.opus", "text": "「话说回来,那个要落跑的女人呢?」", "bodyList": [ { @@ -2267,7 +2267,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000140.mp3", + "sound": "aiy350000140.opus", "text": "「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」", "bodyList": [ { @@ -2289,7 +2289,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000150.mp3", + "sound": "aiy350000150.opus", "text": "「正好,趁此机会凯伊姆先生也来享受一番如何?」", "bodyList": [ { @@ -2311,7 +2311,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000080.mp3", + "sound": "aiy710000080.opus", "text": "「你,你们这些家伙,要对她做什么!?」", "bodyList": [ { @@ -2443,7 +2443,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000090.mp3", + "sound": "aiy710000090.opus", "text": "「咕······呃咳······」", "bodyList": [ { @@ -2509,7 +2509,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000100.mp3", + "sound": "aiy710000100.opus", "text": "「你们以为做出这种事······卫兵会坐视不理吗······」", "bodyList": [ { @@ -2531,7 +2531,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000160.mp3", + "sound": "aiy350000160.opus", "text": "「啊啊,不会坐视不理的」", "bodyList": [ { @@ -2553,7 +2553,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000170.mp3", + "sound": "aiy350000170.opus", "text": "「应该会拿出你的钱包,和我们商量如何瓜分吧」", "bodyList": [ { @@ -2575,7 +2575,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000110.mp3", + "sound": "aiy710000110.opus", "text": "「那,那种事······」", "bodyList": [ { @@ -2619,7 +2619,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 2000, - "sound": "aiy350000180.mp3", + "sound": "aiy350000180.opus", "text": "「怎么,头一回来牢狱么?」", "bodyList": [ { @@ -2663,7 +2663,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000190.mp3", + "sound": "aiy350000190.opus", "text": "「为了被骗的女人而来到牢狱,真是个规矩人啊」", "bodyList": [ { @@ -2685,7 +2685,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000200.mp3", + "sound": "aiy350000200.opus", "text": "「······前提是,被骗的人不是你」", "bodyList": [ { @@ -2707,7 +2707,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000120.mp3", + "sound": "aiy710000120.opus", "text": "「你说······我被骗了?」", "bodyList": [ { @@ -2729,7 +2729,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "年轻人", "time": 30, "wait": 1000, - "sound": "aiy710000130.mp3", + "sound": "aiy710000130.opus", "text": "「那,那是怎么回事!?」", "bodyList": [ { @@ -2751,7 +2751,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000210.mp3", + "sound": "aiy350000210.opus", "text": "「不用急,今天晚上会好好告诉你的」", "bodyList": [ { @@ -2971,7 +2971,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000110.mp3", + "sound": "aiy310000110.opus", "text": "「我要走了」", "bodyList": [ { @@ -2993,7 +2993,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000220.mp3", + "sound": "aiy350000220.opus", "text": "「好的,下次再麻烦您」", "bodyList": [ { @@ -3015,7 +3015,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "奥兹", "time": 30, "wait": 1000, - "sound": "aiy350000230.mp3", + "sound": "aiy350000230.opus", "text": "「之后吉克先生会将谢礼交给您的」", "bodyList": [ { @@ -3037,7 +3037,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000120.mp3", + "sound": "aiy310000120.opus", "text": "「啊啊」", "bodyList": [ { @@ -3081,7 +3081,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000130.mp3", + "sound": "aiy310000130.opus", "text": "「······?」", "bodyList": [ { @@ -3341,7 +3341,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000140.mp3", + "sound": "aiy310000140.opus", "text": "「艾莉斯」", "bodyList": [ { @@ -3387,7 +3387,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000005.mp3", + "sound": "aiy020000005.opus", "text": "「啊,凯伊姆」", "bodyList": [ { @@ -3409,7 +3409,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000010.mp3", + "sound": "aiy020000010.opus", "text": "「正好,我还想要去找你呢」", "bodyList": [ { @@ -3431,7 +3431,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000020.mp3", + "sound": "aiy020000020.opus", "text": "「没想到凯伊姆会主动出现······这是命运吗?」", "bodyList": [ { @@ -3453,7 +3453,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000150.mp3", + "sound": "aiy310000150.opus", "text": "「显然不是吧」", "bodyList": [ { @@ -3475,7 +3475,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000030.mp3", + "sound": "aiy020000030.opus", "text": "「啊,是么」", "bodyList": [ { @@ -3585,7 +3585,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000040.mp3", + "sound": "aiy020000040.opus", "text": "「喜欢我的眼睛吗?」", "bodyList": [ { @@ -3607,7 +3607,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000050.mp3", + "sound": "aiy020000050.opus", "text": "「如果想要的话就给你吧?」", "bodyList": [ { @@ -3629,7 +3629,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000160.mp3", + "sound": "aiy310000160.opus", "text": "「用不着」", "bodyList": [ { @@ -3651,7 +3651,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000060.mp3", + "sound": "aiy020000060.opus", "text": "「阿拉,可惜」", "bodyList": [ { @@ -3673,7 +3673,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000170.mp3", + "sound": "aiy310000170.opus", "text": "「那么,找我有什么事」", "bodyList": [ { @@ -3695,7 +3695,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000070.mp3", + "sound": "aiy020000070.opus", "text": "「梅尔特的钱好像被偷了」", "bodyList": [ { @@ -3717,7 +3717,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000180.mp3", + "sound": "aiy310000180.opus", "text": "「钱被偷了?都几岁了还这么没用」", "bodyList": [ { @@ -3739,7 +3739,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000080.mp3", + "sound": "aiy020000080.opus", "text": "「不要对我说啊」", "bodyList": [ { @@ -3761,7 +3761,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000190.mp3", + "sound": "aiy310000190.opus", "text": "「那家伙,该不会说要让我去抓那个小偷吧?」", "bodyList": [ { @@ -3783,7 +3783,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000090.mp3", + "sound": "aiy020000090.opus", "text": "「就是这样」", "bodyList": [ { @@ -3805,7 +3805,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000200.mp3", + "sound": "aiy310000200.opus", "text": "「笨蛋吗」", "bodyList": [ { @@ -3827,7 +3827,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000210.mp3", + "sound": "aiy310000210.opus", "text": "「如果是小钱的话,就当做是买个教训吧」", "bodyList": [ { @@ -3849,7 +3849,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000100.mp3", + "sound": "aiy020000100.opus", "text": "「说起来,被盗的是这个月的上纳金」", "bodyList": [ { @@ -3871,7 +3871,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000220.mp3", + "sound": "aiy310000220.opus", "text": "「你说什么?」", "bodyList": [ { @@ -3893,7 +3893,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000110.mp3", + "sound": "aiy020000110.opus", "text": "「用这些钱买教训,也太过奢侈了呢」", "bodyList": [ { @@ -3915,7 +3915,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000230.mp3", + "sound": "aiy310000230.opus", "text": "「知道了,我去找」", "bodyList": [ { @@ -3937,7 +3937,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000240.mp3", + "sound": "aiy310000240.opus", "text": "「小偷的特征呢」", "bodyList": [ { @@ -3959,7 +3959,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000120.mp3", + "sound": "aiy020000120.opus", "text": "「男孩子」", "bodyList": [ { @@ -3981,7 +3981,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000130.mp3", + "sound": "aiy020000130.opus", "text": "「······而且,背后有翅膀」", "bodyList": [ { @@ -4003,7 +4003,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000140.mp3", + "sound": "aiy020000140.opus", "text": "「虽然姑且是藏在身后,但是仔细观察的话是很明显的」", "bodyList": [ { @@ -4025,7 +4025,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000250.mp3", + "sound": "aiy310000250.opus", "text": "「羽化病吗」", "bodyList": [ { @@ -4047,7 +4047,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000150.mp3", + "sound": "aiy020000150.opus", "text": "「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」", "bodyList": [ { @@ -4069,7 +4069,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000260.mp3", + "sound": "aiy310000260.opus", "text": "「注意到他逃窜的方向了吗?」", "bodyList": [ { @@ -4091,7 +4091,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000160.mp3", + "sound": "aiy020000160.opus", "text": "「广场那边」", "bodyList": [ { @@ -4113,7 +4113,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000170.mp3", + "sound": "aiy020000170.opus", "text": "「虽然刚才《不蚀金锁》的人去追了,不过多半是······」", "bodyList": [ { @@ -4135,7 +4135,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000280.mp3", + "sound": "aiy310000280.opus", "text": "「偏偏还是广场吗」", "bodyList": [ { @@ -4157,7 +4157,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "艾莉斯", "time": 30, "wait": 1000, - "sound": "aiy020000180.mp3", + "sound": "aiy020000180.opus", "text": "「今天是觐见圣女大人的日子」", "bodyList": [ { @@ -4179,7 +4179,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000290.mp3", + "sound": "aiy310000290.opus", "text": "「我知道」", "bodyList": [ { @@ -4201,7 +4201,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "name": "凯伊姆", "time": 30, "wait": 1000, - "sound": "aiy310000300.mp3", + "sound": "aiy310000300.opus", "text": "「尽量找找看就好」", "bodyList": [ { diff --git a/project/maps.js b/project/maps.js index 6367155..fc722c6 100644 --- a/project/maps.js +++ b/project/maps.js @@ -1,11 +1,11 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = { - "1": {"cls":"animates","id":"yellowWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, - "2": {"cls":"animates","id":"whiteWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, - "3": {"cls":"animates","id":"blueWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "1": {"cls":"animates","id":"yellowWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, + "2": {"cls":"animates","id":"whiteWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, + "3": {"cls":"animates","id":"blueWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, "4": {"cls":"animates","id":"star","name":"星空"}, "5": {"cls":"animates","id":"lava","name":"岩浆"}, - "6": {"cls":"animates","id":"ice","doorInfo":{"time":160,"openSound":"破冰镐","closeSound":"door.mp3","keys":{"icePickaxe":1}},"animate":1}, + "6": {"cls":"animates","id":"ice","doorInfo":{"time":160,"openSound":"破冰镐","closeSound":"door.opus","keys":{"icePickaxe":1}},"animate":1}, "7": {"cls":"terrains","id":"blueShopLeft"}, "8": {"cls":"terrains","id":"blueShopRight"}, "9": {"cls":"terrains","id":"pinkShopLeft"}, @@ -68,12 +68,12 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "71": {"cls":"items","id":"shield0"}, "72": {"cls":"items","id":"skill1"}, "73": {"cls":"items","id":"wand"}, - "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"yellowKey":1}},"name":"黄门"}, - "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"blueKey":1}},"name":"蓝门"}, - "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}},"name":"红门"}, - "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}},"name":"绿门"}, - "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}},"name":"机关门"}, - "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门"}, + "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"yellowKey":1}},"name":"黄门"}, + "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"blueKey":1}},"name":"蓝门"}, + "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"redKey":1}},"name":"红门"}, + "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"greenKey":1}},"name":"绿门"}, + "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"specialKey":1}},"name":"机关门"}, + "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"steelKey":1}},"name":"铁门"}, "87": {"cls":"terrains","id":"upFloor","canPass":true}, "88": {"cls":"terrains","id":"downFloor","canPass":true}, "89": {"cls":"animates","id":"portal","canPass":true}, @@ -86,7 +86,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "102": {"cls":"animates","id":"crystalBottom"}, "103": {"cls":"animates","id":"fire"}, "104": {"cls":"animates","id":"switch"}, - "109": {"cls":"animates","id":"magentaWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "109": {"cls":"animates","id":"magentaWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, "121": {"cls":"npcs","id":"man"}, "122": {"cls":"npcs","id":"trader"}, "123": {"cls":"npcs","id":"thief"}, @@ -217,12 +217,12 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "316": {"cls":"terrains","id":"sWallTLR","name":"薄墙-上左右","cannotOut":["up","left","right"],"cannotIn":["up","left","right"]}, "317": {"cls":"terrains","id":"sWallTBR","name":"薄墙-上下右","cannotOut":["up","down","right"],"cannotIn":["up","down","right"]}, "318": {"cls":"terrains","id":"sWallTBL","name":"薄墙-上下左","cannotOut":["up","down","left"],"cannotIn":["up","down","left"]}, - "319": {"cls":"npc48","id":"tallYellowDoor","trigger":"openDoor","name":"高黄门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"yellowKey":1}}}, - "320": {"cls":"npc48","id":"tallBlueDoor","trigger":"openDoor","name":"高蓝门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"blueKey":1}}}, - "321": {"cls":"npc48","id":"tallRedDoor","trigger":"openDoor","name":"高红门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}}}, - "322": {"cls":"npc48","id":"tallGreenDoor","trigger":"openDoor","name":"高绿门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}}}, - "323": {"cls":"npc48","id":"tallSpecialDoor","trigger":"openDoor","name":"高机关门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}}}, - "324": {"cls":"npc48","id":"tallSteelDoor","trigger":"openDoor","name":"高铁门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}}}, + "319": {"cls":"npc48","id":"tallYellowDoor","trigger":"openDoor","name":"高黄门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"yellowKey":1}}}, + "320": {"cls":"npc48","id":"tallBlueDoor","trigger":"openDoor","name":"高蓝门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"blueKey":1}}}, + "321": {"cls":"npc48","id":"tallRedDoor","trigger":"openDoor","name":"高红门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"redKey":1}}}, + "322": {"cls":"npc48","id":"tallGreenDoor","trigger":"openDoor","name":"高绿门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"greenKey":1}}}, + "323": {"cls":"npc48","id":"tallSpecialDoor","trigger":"openDoor","name":"高机关门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"specialKey":1}}}, + "324": {"cls":"npc48","id":"tallSteelDoor","trigger":"openDoor","name":"高铁门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"steelKey":1}}}, "325": {"cls":"enemys","id":"keiskeiFairy"}, "326": {"cls":"enemys","id":"tulipFairy"}, "327": {"cls":"enemy48","id":"bearDown"}, diff --git a/project/plugins.js b/project/plugins.js index 317ee03..009ae14 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -4196,278 +4196,279 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } }, "编辑器显伤": function () { - // 在此增加新插件 - /////// 用户设置 /////// - // 将__enable置为false将关闭插件 - var __enable = true; - // 魔防攻速之类的属性可以在这里加 ['atk', 'def', 'mdef'] - var heroStatus = ["atk", "def", "mdef", "hp"]; - // saveHero为true 将会把每次造塔测试时的角色数据存下来 否则会读取初始属性 - // 用不着可以关了 节约缓存空间 (虽然根本没多少 还没一个存档大 - // 也可以手动清理 控制台输入core.removeLocalStorage('editorHero')即可 - var saveHero = true; + // 在此增加新插件 + /////// 用户设置 /////// + // 将__enable置为false将关闭插件 + var __enable = true; + // 魔防攻速之类的属性可以在这里加 ['atk', 'def', 'mdef'] + var heroStatus = ["atk", "def", "mdef", "hp"]; + // saveHero为true 将会把每次造塔测试时的角色数据存下来 否则会读取初始属性 + // 用不着可以关了 节约缓存空间 (虽然根本没多少 还没一个存档大 + // 也可以手动清理 控制台输入core.removeLocalStorage('editorHero')即可 + var saveHero = true; - // 下为具体实现 懒得写注释了 大概就是写HTML然后注册交互 - if (!__enable || main.mode != "editor") return; - core.plugin.initEditorDamage = false; - if (heroStatus.length >= 4 && !editor.isMobile) - editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + "px"; - editor.statusRatio = core.getLocalStorage("statusRatio", 1); - editor.saveHero = saveHero; - editor._heroStatus = heroStatus; - editor.dom.mapEdit.appendChild(core.canvas.damage.canvas); - var HTML = - ""; + // 下为具体实现 懒得写注释了 大概就是写HTML然后注册交互 + if (!__enable || main.mode != "editor") return; + core.plugin.initEditorDamage = false; + if (heroStatus.length >= 4 && !editor.isMobile) + editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + "px"; + editor.statusRatio = core.getLocalStorage("statusRatio", 1); + editor.saveHero = saveHero; + editor._heroStatus = heroStatus; + editor.dom.mapEdit.appendChild(core.canvas.damage.canvas); + var HTML = + ""; - //if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px'; - heroStatus.forEach(function (status) { - var id = status + "set", - id2 = status + "add", - id3 = status + "rec", - id4 = status + "help"; - HTML += - "
"; - }); - document.getElementById("viewportButtons").innerHTML = HTML; - ["set", "add", "rec", "help"].forEach(function (e) { - heroStatus.forEach(function (status) { - editor.dom[status + e] = document.getElementById(status + e); - }); - }); - var _hasItem = core.items.hasItem; - core.items.hasItem = function (itemId) { - if (itemId == "book" && main.mode == "editor") return true; - return _hasItem.call(core.items, itemId); - }; - if (main.mode == "editor") { - var applyList = [ - "getDamageString", - "nextCriticals", - "getEnemyInfo", - "getEnemyValue", - ]; - applyList.forEach(function (name) { - var func = core.enemys[name]; - core.enemys[name] = function () { - var args = - arguments.length === 1 ? [arguments[0]] : - Array.apply(null, arguments); - if (typeof args[0] == "string") args[0] = core.enemys.enemys[args[0]]; - return func.apply(core.enemys, args); - }; - }); - } + //if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px'; + heroStatus.forEach(function (status) { + var id = status + "set", + id2 = status + "add", + id3 = status + "rec", + id4 = status + "help"; + HTML += + "
"; + }); + document.getElementById("viewportButtons").innerHTML = HTML; + ["set", "add", "rec", "help"].forEach(function (e) { + heroStatus.forEach(function (status) { + editor.dom[status + e] = document.getElementById(status + e); + }); + }); + var _hasItem = core.items.hasItem; + core.items.hasItem = function (itemId) { + if (itemId == "book" && main.mode == "editor") return true; + return _hasItem.call(core.items, itemId); + }; + if (main.mode == "editor") { + var applyList = [ + "getDamageString", + "nextCriticals", + "getEnemyInfo", + "getEnemyValue", + ]; + applyList.forEach(function (name) { + var func = core.enemys[name]; + core.enemys[name] = function () { + var args = + arguments.length === 1 + ? [arguments[0]] + : Array.apply(null, arguments); + if (typeof args[0] == "string") args[0] = core.enemys.enemys[args[0]]; + return func.apply(core.enemys, args); + }; + }); + } - ////// 获得勇士属性 ////// - core.control.getStatus = function (name) { - if (!core.status.hero) return null; - if (name == "x" || name == "y" || name == "direction") - return this.getHeroLoc(name); - /*if ( main.mode == 'editor' && !core.hasFlag('__statistics__')) { + ////// 获得勇士属性 ////// + core.control.getStatus = function (name) { + if (!core.status.hero) return null; + if (name == "x" || name == "y" || name == "direction") + return this.getHeroLoc(name); + /*if ( main.mode == 'editor' && !core.hasFlag('__statistics__')) { return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name]; }*/ - return core.status.hero[name]; - }; + return core.status.hero[name]; + }; - core.control.updateDamage = function (floorId, ctx) { - floorId = floorId || core.status.floorId; - if (!floorId || core.status.gameOver) return; - var onMap = ctx == null; - if (main.mode == "editor") { - ctx = core.canvas.damage; - core.updateCheckBlock(); - core.clearMap(ctx); - if (editor.uivalues.bigmap) return; - } + core.control.updateDamage = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + if (!floorId || core.status.gameOver) return; + var onMap = ctx == null; + if (main.mode == "editor") { + ctx = core.canvas.damage; + core.updateCheckBlock(); + core.clearMap(ctx); + if (editor.uivalues.bigmap) return; + } - // 没有怪物手册 - if (!core.hasItem("book")) return; - core.status.damage.posX = core.bigmap.posX; - core.status.damage.posY = core.bigmap.posY; - if (!onMap) { - var width = core.floors[floorId].width, - height = core.floors[floorId].height; - // 地图过大的缩略图不绘制显伤 - if (width * height > core.bigmap.threshold) return; - } - this._updateDamage_damage(floorId, onMap); - this._updateDamage_extraDamage(floorId, onMap); - this.drawDamage(ctx); - }; + // 没有怪物手册 + if (!core.hasItem("book")) return; + core.status.damage.posX = core.bigmap.posX; + core.status.damage.posY = core.bigmap.posY; + if (!onMap) { + var width = core.floors[floorId].width, + height = core.floors[floorId].height; + // 地图过大的缩略图不绘制显伤 + if (width * height > core.bigmap.threshold) return; + } + this._updateDamage_damage(floorId, onMap); + this._updateDamage_extraDamage(floorId, onMap); + this.drawDamage(ctx); + }; - core.control.drawDamage = function (ctx) { - if ( - core.status.gameOver || - !core.status.damage /* || main.mode != 'play'*/ - ) - return; - var onMap = false; - if (ctx == null) { - ctx = core.canvas.damage; - core.clearMap("damage"); - onMap = true; - } + core.control.drawDamage = function (ctx) { + if ( + core.status.gameOver || + !core.status.damage /* || main.mode != 'play'*/ + ) + return; + var onMap = false; + if (ctx == null) { + ctx = core.canvas.damage; + core.clearMap("damage"); + onMap = true; + } - if (onMap && core.bigmap.v2) { - // 检查是否需要重算... - if ( - Math.abs(core.bigmap.posX - core.status.damage.posX) >= - core.bigmap.extend - 1 || - Math.abs(core.bigmap.posY - core.status.damage.posY) >= - core.bigmap.extend - 1 - ) { - return this.updateDamage(); - } - } - return this._drawDamage_draw(ctx, onMap); - }; + if (onMap && core.bigmap.v2) { + // 检查是否需要重算... + if ( + Math.abs(core.bigmap.posX - core.status.damage.posX) >= + core.bigmap.extend - 1 || + Math.abs(core.bigmap.posY - core.status.damage.posY) >= + core.bigmap.extend - 1 + ) { + return this.updateDamage(); + } + } + return this._drawDamage_draw(ctx, onMap); + }; - ////// 以x,y的形式返回每个点的事件 ////// - core.maps.getMapBlocksObj = function (floorId, noCache) { - floorId = floorId || core.status.floorId; - if ( - core.status.mapBlockObjs[floorId] && - !noCache && - main.mode != "editor" - ) - return core.status.mapBlockObjs[floorId]; + ////// 以x,y的形式返回每个点的事件 ////// + core.maps.getMapBlocksObj = function (floorId, noCache) { + floorId = floorId || core.status.floorId; + if ( + core.status.mapBlockObjs[floorId] && + !noCache && + main.mode != "editor" + ) + return core.status.mapBlockObjs[floorId]; - var obj = {}; - core.extractBlocks(floorId); - core.status.maps[floorId].blocks.forEach(function (block) { - obj[block.x + "," + block.y] = block; - }); - core.status.mapBlockObjs[floorId] = obj; - return obj; - }; + var obj = {}; + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + obj[block.x + "," + block.y] = block; + }); + core.status.mapBlockObjs[floorId] = obj; + return obj; + }; - this.bignum = function (num, defaultValue) { - if (num == null || num == "") return defaultValue; - num = num + ""; - var list = { - w: 1e4, - e: 1e8, - z: 1e12, - j: 1e16, - g: 1e20, - }; - // 浮点数问题 - function checkFloat(num) { - if (!core.isset(num)) return 0; - num = num + ""; - var index = num.indexOf("."); - if (index < 0) return 0; - else return num.slice(index + 1).length; - } - var index = num.search(/w|e|z|j|g/); - if (index <= 0) { - num = parseInt(num); - if (core.isset(num)) return num; - else { - alert("不正确的输入"); - return defaultValue; - } - } - for (; index > 0; index = num.search(/w|e|z|j|g/)) { - var p = num[index], - q = list[p], - n = num.slice(0, index), - m = Math.pow(10, checkFloat(n)); - num = (n * m * q) / m + num.slice(index + 1); - } - return parseInt(num); - }; + this.bignum = function (num, defaultValue) { + if (num == null || num == "") return defaultValue; + num = num + ""; + var list = { + w: 1e4, + e: 1e8, + z: 1e12, + j: 1e16, + g: 1e20, + }; + // 浮点数问题 + function checkFloat(num) { + if (!core.isset(num)) return 0; + num = num + ""; + var index = num.indexOf("."); + if (index < 0) return 0; + else return num.slice(index + 1).length; + } + var index = num.search(/w|e|z|j|g/); + if (index <= 0) { + num = parseInt(num); + if (core.isset(num)) return num; + else { + alert("不正确的输入"); + return defaultValue; + } + } + for (; index > 0; index = num.search(/w|e|z|j|g/)) { + var p = num[index], + q = list[p], + n = num.slice(0, index), + m = Math.pow(10, checkFloat(n)); + num = (n * m * q) / m + num.slice(index + 1); + } + return parseInt(num); + }; - this.updateEditorDamage = function (noSave) { - core.updateDamage(); - heroStatus.forEach(function (status) { - editor.dom[status + "set"].value = core.status.hero[status]; - }); - if (!noSave && editor.saveHero) - core.setLocalStorage("editorHero", core.status.hero); - }; + this.updateEditorDamage = function (noSave) { + core.updateDamage(); + heroStatus.forEach(function (status) { + editor.dom[status + "set"].value = core.status.hero[status]; + }); + if (!noSave && editor.saveHero) + core.setLocalStorage("editorHero", core.status.hero); + }; - var _resizeMap = core.maps.resizeMap; - core.maps.resizeMap = function (floorId) { - _resizeMap.call(core.maps, floorId); - if (!core.plugin.initEditorDamage && main.mode == "editor") { - core.plugin.initEditorDamage = true; - var editorHero = core.getLocalStorage("editorHero"); - if (editorHero && saveHero) core.status.hero = editorHero; - else core.removeLocalStorage("editorHero"); - editor._heroStatus.forEach(function (e) { - editor.dom[e + "set"].onchange = function () { - var status = this.id.slice(0, -3); - core.status.hero[status] = core.bignum( - this.value, - core.status.hero[status] - ); - core.updateEditorDamage(); - }; - editor.dom[e + "add"].onclick = function () { - var status = this.id.slice(0, -3); - core.status.hero[status] += editor.statusRatio; - core.updateEditorDamage(); - }; - editor.dom[e + "rec"].onclick = function () { - var status = this.id.slice(0, -3); - core.status.hero[status] -= editor.statusRatio; - core.updateEditorDamage(); - }; - editor.dom[e + "help"].onclick = function () { - var status = this.id.slice(0, -4), - name = core.getStatusLabel(status); - var ratio = parseInt( - prompt( - "当前属性:" + - name + - "\n现在的点击按钮变化值:" + - editor.statusRatio + - ",请输入按下一次+/-按钮的属性变化量,可以写4w 10.2e这种字母缩写" - ) - ); - if (!core.isset(ratio)) { - printe("不合法的输入"); - return; - } - editor.statusRatio = ratio; - core.setLocalStorage("statusRatio", ratio); - }; - }); - var _updateMap = editor.updateMap; - editor.updateMap = function () { - _updateMap.call(editor); - core.updateEditorDamage(true); - }; - editor.mode.onmode = function (mode, callback) { - if (editor_mode.mode != mode) { - if (mode === "save") { - editor_mode.doActionList( - editor_mode.mode, - editor_mode.actionList, - function () { - if (callback) callback(); - core.updateEditorDamage(); - } - ); - } - if (editor_mode.mode === "nextChange" && mode) - editor_mode.showMode(mode); - if (mode !== "save") editor_mode.mode = mode; - editor_mode.actionList = []; - } - }; - } - }; -}, + var _resizeMap = core.maps.resizeMap; + core.maps.resizeMap = function (floorId) { + _resizeMap.call(core.maps, floorId); + if (!core.plugin.initEditorDamage && main.mode == "editor") { + core.plugin.initEditorDamage = true; + var editorHero = core.getLocalStorage("editorHero"); + if (editorHero && saveHero) core.status.hero = editorHero; + else core.removeLocalStorage("editorHero"); + editor._heroStatus.forEach(function (e) { + editor.dom[e + "set"].onchange = function () { + var status = this.id.slice(0, -3); + core.status.hero[status] = core.bignum( + this.value, + core.status.hero[status] + ); + core.updateEditorDamage(); + }; + editor.dom[e + "add"].onclick = function () { + var status = this.id.slice(0, -3); + core.status.hero[status] += editor.statusRatio; + core.updateEditorDamage(); + }; + editor.dom[e + "rec"].onclick = function () { + var status = this.id.slice(0, -3); + core.status.hero[status] -= editor.statusRatio; + core.updateEditorDamage(); + }; + editor.dom[e + "help"].onclick = function () { + var status = this.id.slice(0, -4), + name = core.getStatusLabel(status); + var ratio = parseInt( + prompt( + "当前属性:" + + name + + "\n现在的点击按钮变化值:" + + editor.statusRatio + + ",请输入按下一次+/-按钮的属性变化量,可以写4w 10.2e这种字母缩写" + ) + ); + if (!core.isset(ratio)) { + printe("不合法的输入"); + return; + } + editor.statusRatio = ratio; + core.setLocalStorage("statusRatio", ratio); + }; + }); + var _updateMap = editor.updateMap; + editor.updateMap = function () { + _updateMap.call(editor); + core.updateEditorDamage(true); + }; + editor.mode.onmode = function (mode, callback) { + if (editor_mode.mode != mode) { + if (mode === "save") { + editor_mode.doActionList( + editor_mode.mode, + editor_mode.actionList, + function () { + if (callback) callback(); + core.updateEditorDamage(); + } + ); + } + if (editor_mode.mode === "nextChange" && mode) + editor_mode.showMode(mode); + if (mode !== "save") editor_mode.mode = mode; + editor_mode.actionList = []; + } + }; + } + }; + }, "手册区分特殊属性": function () { // 在此增加新插件 this.arrsame = function (Arraya, Arrayb) { @@ -8447,6 +8448,2106 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } }; }, + "音频系统": function () { + // 在此增加新插件 + /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: + + + + + + + */ + // 将__enable置为false将关闭插件 + let __enable = true; + if (!__enable || main.mode === "editor") return; + const { OggOpusDecoderWebWorker } = window["ogg-opus-decoder"]; + const { OggVorbisDecoderWebWorker } = window["ogg-vorbis-decoder"]; + const { CodecParser } = window.CodecParser; + const { Transition, linear } = core.plugin.animate; + + const audio = new Audio(); + const AudioStatus = { + Playing: 0, + Pausing: 1, + Paused: 2, + Stoping: 3, + Stoped: 4, + }; + const supportMap = new Map(); + const AudioType = { + Mp3: "audio/mpeg", + Wav: 'audio/wav; codecs="1"', + Flac: "audio/flac", + Opus: 'audio/ogg; codecs="opus"', + Ogg: 'audio/ogg; codecs="vorbis"', + Aac: "audio/aac", + }; + /** + * 检查一种音频类型是否能被播放 + * @param type 音频类型 AudioType + */ + function isAudioSupport(type) { + if (supportMap.has(type)) return supportMap.get(type); + else { + const support = audio.canPlayType(type); + const canPlay = support === "maybe" || support === "probably"; + supportMap.set(type, canPlay); + return canPlay; + } + } + + const typeMap = new Map([ + ["ogg", AudioType.Ogg], + ["mp3", AudioType.Mp3], + ["wav", AudioType.Wav], + ["flac", AudioType.Flac], + ["opus", AudioType.Opus], + ["aac", AudioType.Aac], + ]); + + /** + * 根据文件名拓展猜测其类型 + * @param file 文件名 string + */ + function guessTypeByExt(file) { + const ext = /\.[a-zA-Z\d]+$/.exec(file); + if (!ext?.[0]) return ""; + const type = ext[0].slice(1); + return typeMap.get(type.toLocaleLowerCase()) ?? ""; + } + + isAudioSupport(AudioType.Ogg); + isAudioSupport(AudioType.Mp3); + isAudioSupport(AudioType.Wav); + isAudioSupport(AudioType.Flac); + isAudioSupport(AudioType.Opus); + isAudioSupport(AudioType.Aac); + + function isNil(value) { + return value === void 0 || value === null; + } + + function sleep(time) { + return new Promise((res) => setTimeout(res, time)); + } + class AudioEffect { + constructor(ac) {} + /** + * 连接至其他效果器 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + connect(target, output, input) { + this.output.connect(target.input, output, input); + } + + /** + * 与其他效果器取消连接 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + disconnect(target, output, input) { + if (!target) { + if (!isNil(output)) { + this.output.disconnect(output); + } else { + this.output.disconnect(); + } + } else { + if (!isNil(output)) { + if (!isNil(input)) { + this.output.disconnect(target.input, output, input); + } else { + this.output.disconnect(target.input, output); + } + } else { + this.output.disconnect(target.input); + } + } + } + } + + class StereoEffect extends AudioEffect { + constructor(ac) { + super(ac); + const panner = ac.createPanner(); + this.input = panner; + this.output = panner; + } + + /** + * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 Number + * @param y 朝向y坐标 Number + * @param z 朝向z坐标 Number + */ + setOrientation(x, y, z) { + this.output.orientationX.value = x; + this.output.orientationY.value = y; + this.output.orientationZ.value = z; + } + /** + * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 Number + * @param y 位置y坐标 Number + * @param z 位置z坐标 Number + */ + setPosition(x, y, z) { + this.output.positionX.value = x; + this.output.positionY.value = y; + this.output.positionZ.value = z; + } + end() {} + + start() {} + } + class VolumeEffect extends AudioEffect { + constructor(ac) { + super(ac); + const gain = ac.createGain(); + this.input = gain; + this.output = gain; + } + + /** + * 设置音量大小 + * @param volume 音量大小 Number + */ + setVolume(volume) { + this.output.gain.value = volume; + } + + /** + * 获取音量大小 Number + */ + getVolume() { + return this.output.gain.value; + } + + end() {} + + start() {} + } + class ChannelVolumeEffect extends AudioEffect { + /** 所有的音量控制节点 */ + + constructor(ac) { + super(ac); + /** 所有的音量控制节点 */ + this.gain = []; + const splitter = ac.createChannelSplitter(); + const merger = ac.createChannelMerger(); + this.output = merger; + this.input = splitter; + for (let i = 0; i < 6; i++) { + const gain = ac.createGain(); + splitter.connect(gain, i); + gain.connect(merger, 0, i); + this.gain.push(gain); + } + } + + /** + * 设置某个声道的音量大小 + * @param channel 要设置的声道,可填0-5 Number + * @param volume 这个声道的音量大小 Number + */ + setVolume(channel, volume) { + if (!this.gain[channel]) return; + this.gain[channel].gain.value = volume; + } + + /** + * 获取某个声道的音量大小,可填0-5 + * @param channel 要获取的声道 Number + */ + getVolume(channel) { + if (!this.gain[channel]) return 0; + return this.gain[channel].gain.value; + } + + end() {} + + start() {} + } + class DelayEffect extends AudioEffect { + constructor(ac) { + super(ac); + + const delay = ac.createDelay(); + this.input = delay; + this.output = delay; + } + + /** + * 设置延迟时长 + * @param delay 延迟时长,单位秒 Number + */ + setDelay(delay) { + this.output.delayTime.value = delay; + } + + /** + * 获取延迟时长 + */ + getDelay() { + return this.output.delayTime.value; + } + + end() {} + + start() {} + } + class EchoEffect extends AudioEffect { + constructor(ac) { + super(ac); + /** 当前增益 */ + this.gain = 0.5; + /** 是否正在播放 */ + this.playing = false; + const delay = ac.createDelay(); + const gain = ac.createGain(); + gain.gain.value = 0.5; + delay.delayTime.value = 0.05; + delay.connect(gain); + gain.connect(delay); + /** 延迟节点 */ + this.delay = delay; + /** 反馈增益节点 */ + this.gainNode = gain; + + this.input = gain; + this.output = gain; + } + + /** + * 设置回声反馈增益大小 + * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number + */ + setFeedbackGain(gain) { + const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; + this.gain = resolved; + if (this.playing) this.gainNode.gain.value = resolved; + } + + /** + * 设置回声间隔时长 + * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number + */ + setEchoDelay(delay) { + const resolved = delay < 0.01 ? 0.01 : delay; + this.delay.delayTime.value = resolved; + } + + /** + * 获取反馈节点增益 + */ + getFeedbackGain() { + return this.gain; + } + + /** + * 获取回声间隔时长 + */ + getEchoDelay() { + return this.delay.delayTime.value; + } + + end() { + this.playing = false; + const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; + sleep(this.delay.delayTime.value * echoTime).then(() => { + if (!this.playing) this.gainNode.gain.value = 0; + }); + } + + start() { + this.playing = true; + this.gainNode.gain.value = this.gain; + } + } + + class StreamLoader { + constructor(url) { + /** 传输目标 Set*/ + this.target = new Set(); + this.loading = false; + } + + /** + * 将加载流传递给字节流读取对象 + * @param reader 字节流读取对象 IStreamReader + */ + pipe(reader) { + if (this.loading) { + console.warn( + "Cannot pipe new StreamReader object when stream is loading." + ); + return; + } + this.target.add(reader); + reader.piped(this); + return this; + } + + async start() { + if (this.loading) return; + this.loading = true; + const response = await window.fetch(this.url); + const stream = response.body; + if (!stream) { + console.error("Cannot get reader when fetching '" + this.url + "'."); + return; + } + // 获取读取器 + /** 读取流对象 */ + this.stream = stream; + const reader = response.body?.getReader(); + const targets = [...this.target]; + + await Promise.all(targets.map((v) => v.start(stream, this, response))); + if (reader && reader.read) { + // 开始流传输 + while (true) { + const { value, done } = await reader.read(); + await Promise.all( + targets.map((v) => v.pump(value, done, response)) + ); + if (done) break; + } + } else { + // 如果不支持流传输 + const buffer = await response.arrayBuffer(); + const data = new Uint8Array(buffer); + await Promise.all(targets.map((v) => v.pump(data, true, response))); + } + // 开始流传输 + while (true) { + const { value, done } = await reader.read(); + await Promise.all(targets.map((v) => v.pump(value, done, response))); + if (done) break; + } + + this.loading = false; + targets.forEach((v) => v.end(true)); + + // + } + + cancel(reason) { + if (!this.stream) return; + this.stream.cancel(reason); + this.loading = false; + this.target.forEach((v) => v.end(false, reason)); + } + } + const fileSignatures = [ + [AudioType.Mp3, [0x49, 0x44, 0x33]], + [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], + [AudioType.Wav, [0x52, 0x49, 0x46, 0x46]], + [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], + [AudioType.Aac, [0xff, 0xf1]], + [AudioType.Aac, [0xff, 0xf9]], + ]; + const oggHeaders = [ + [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]], + ]; + + function checkAudioType(data) { + let audioType = ""; + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + for (const [type, value] of fileSignatures) { + if (value.every((v, i) => toCheck[i] === v)) { + audioType = type; + break; + } + } + if (audioType === AudioType.Ogg) { + // 如果是ogg的话,进一步判断是不是opus + for (const [key, value] of oggHeaders) { + const has = toCheck.some((_, i) => { + return value.every((v, ii) => toCheck[i + ii] === v); + }); + if (has) { + audioType = key; + break; + } + } + } + + return audioType; + } + class AudioDecoder { + /** + * 注册一个解码器 + * @param type 要注册的解码器允许解码的类型 + * @param decoder 解码器对象 + */ + static registerDecoder(type, decoder) { + if (!this.decoderMap) this.decoderMap = new Map(); + if (this.decoderMap.has(type)) { + console.warn( + "Audio stream decoder for audio type '" + + type + + "' has already existed." + ); + return; + } + + this.decoderMap.set(type, decoder); + } + + /** + * 解码音频数据 + * @param data 音频文件数据 + * @param player AudioPlayer实例 + */ + static async decodeAudioData(data, player) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + const type = checkAudioType(data); + if (type === "") { + console.error( + "Unknown audio type. Header: '" + [...toCheck] + .map((v) => v.toString().padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return null; + } + if (isAudioSupport(type)) { + if (data.buffer instanceof ArrayBuffer) { + return player.ac.decodeAudioData(data.buffer); + } else { + return null; + } + } else { + const Decoder = this.decoderMap.get(type); + if (!Decoder) { + return null; + } else { + const decoder = new Decoder(); + await decoder.create(); + const decodedData = await decoder.decode(data); + if (!decodedData) return null; + const buffer = player.ac.createBuffer( + decodedData.channelData.length, + decodedData.channelData[0].length, + decodedData.sampleRate + ); + decodedData.channelData.forEach((v, i) => { + buffer.copyToChannel(v, i); + }); + decoder.destroy(); + return buffer; + } + } + } + } + + class VorbisDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggVorbisDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return this.decoder?.flush(); + } + } + + class OpusDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggOpusDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return await this.decoder?.flush(); + } + } + const mimeTypeMap = { + [AudioType.Aac]: "audio/aac", + [AudioType.Flac]: "audio/flac", + [AudioType.Mp3]: "audio/mpeg", + [AudioType.Ogg]: "application/ogg", + [AudioType.Opus]: "application/ogg", + [AudioType.Wav]: "application/ogg", + }; + + function isOggPage(data) { + return !isNil(data.isFirstPage); + } + class AudioStreamSource { + constructor(context) { + this.output = context.createBufferSource(); + /** 是否已经完全加载完毕 */ + this.loaded = false; + /** 是否正在播放 */ + this.playing = false; + /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ + this.buffered = 0; + /** 已经缓冲的采样点数量 */ + this.bufferedSamples = 0; + /** 歌曲时长,加载完毕之前保持为 0 */ + this.duration = 0; + /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ + this.bufferPlayDuration = 1; + /** 音频的采样率,未成功解析出之前保持为 0 */ + this.sampleRate = 0; + //是否循环播放 + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 开始播放时刻 */ + this.lastStartTime = 0; + /** 上一次播放的缓存长度 */ + this.lastBufferSamples = 0; + + /** 是否已经获取到头文件 */ + this.headerRecieved = false; + /** 音频类型 */ + this.audioType = ""; + /** 每多长时间组成一个缓存 Float32Array */ + this.bufferChunkSize = 10; + /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ + this.audioData = []; + + this.errored = false; + this.ac = context; + } + /** 当前已经播放了多长时间 */ + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + /** + * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 + * @param size 每个缓存数据的时长,单位秒 + */ + setChunkSize(size) { + if (this.controller?.loading || this.loaded) return; + this.bufferChunkSize = size; + } + on(event, fn, context) {} + piped(controller) { + this.controller = controller; + } + + async pump(data, done) { + if (!data || this.errored) return; + if (!this.headerRecieved) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + this.audioType = checkAudioType(data); + if (!this.audioType) { + console.error( + "Unknown audio type. Header: '" + [...toCheck] + .map((v) => v.toString(16).padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return; + } + // 创建解码器 + const Decoder = AudioDecoder.decoderMap.get(this.audioType); + if (!Decoder) { + this.errored = true; + console.error( + "Cannot decode stream source type of '" + + this.audioType + + "', since there is no registered decoder for that type." + ); + return Promise.reject( + `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` + ); + } + this.decoder = new Decoder(); + // 创建数据解析器 + const mime = mimeTypeMap[this.audioType]; + const parser = new CodecParser(mime); + this.parser = parser; + await this.decoder.create(); + this.headerRecieved = true; + } + + const decoder = this.decoder; + const parser = this.parser; + if (!decoder || !parser) { + this.errored = true; + return Promise.reject( + "No parser or decoder attached in this AudioStreamSource" + ); + } + + await this.decodeData(data, decoder, parser); + if (done) await this.decodeFlushData(decoder, parser); + this.checkBufferedPlay(); + } + + /** + * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 + */ + checkSampleRate(info) { + for (const one of info) { + const frame = isOggPage(one) ? one.codecFrames[0] : one; + if (frame) { + const rate = frame.header.sampleRate; + if (this.sampleRate === 0) { + this.sampleRate = rate; + break; + } else { + if (rate !== this.sampleRate) { + console.warn("Sample rate in stream audio must be constant."); + } + } + } + } + } + + /** + * 解析音频数据 + */ + async decodeData(data, decoder, parser) { + // 解析音频数据 + const audioData = await decoder.decode(data); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.parseChunk(data)]; + + // 检查采样率 + this.checkSampleRate(audioInfo); + // 追加音频数据 + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 解码剩余数据 + */ + async decodeFlushData(decoder, parser) { + const audioData = await decoder.flush(); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.flush()]; + + this.checkSampleRate(audioInfo); + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 追加音频数据 + */ + appendDecodedData(data, info) { + const channels = data.channelData.length; + if (channels === 0) return; + if (this.audioData.length !== channels) { + this.audioData = []; + for (let i = 0; i < channels; i++) { + this.audioData.push([]); + } + } + // 计算出应该放在哪 + const chunk = this.sampleRate * this.bufferChunkSize; + const sampled = this.bufferedSamples; + const pushIndex = Math.floor(sampled / chunk); + const bufferIndex = sampled % chunk; + const dataLength = data.channelData[0].length; + let buffered = 0; + let nowIndex = pushIndex; + let toBuffer = bufferIndex; + while (buffered < dataLength) { + const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; + + for (let i = 0; i < channels; i++) { + const audioData = this.audioData[i]; + if (!audioData[nowIndex]) { + audioData.push(new Float32Array(chunk)); + } + const toPush = data.channelData[i].slice(buffered, buffered + rest); + + audioData[nowIndex].set(toPush, toBuffer); + } + buffered += rest; + nowIndex++; + toBuffer = 0; + } + + this.buffered += + info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; + this.bufferedSamples += info.reduce( + (prev, curr) => prev + curr.samples, + 0 + ); + } + + /** + * 检查已缓冲内容,并在未开始播放时播放 + */ + checkBufferedPlay() { + if (this.playing || this.sampleRate === 0) return; + const played = this.lastBufferSamples / this.sampleRate; + const dt = this.buffered - played; + if (this.loaded) { + this.playAudio(played); + return; + } + if (dt < this.bufferPlayDuration) return; + + this.lastBufferSamples = this.bufferedSamples; + // 需要播放 + this.mergeBuffers(); + if (!this.buffer) return; + if (this.playing) this.output.stop(); + this.createSourceNode(this.buffer); + this.output.loop = false; + this.output.start(0, played); + this.lastStartTime = this.ac.currentTime; + this.playing = true; + this.output.addEventListener("ended", () => { + this.playing = false; + this.checkBufferedPlay(); + }); + } + + mergeBuffers() { + const buffer = this.ac.createBuffer( + this.audioData.length, + this.bufferedSamples, + this.sampleRate + ); + const chunk = this.sampleRate * this.bufferChunkSize; + const bufferedChunks = Math.floor(this.bufferedSamples / chunk); + const restLength = this.bufferedSamples % chunk; + for (let i = 0; i < this.audioData.length; i++) { + const audio = this.audioData[i]; + const data = new Float32Array(this.bufferedSamples); + for (let j = 0; j < bufferedChunks; j++) { + data.set(audio[j], chunk * j); + } + if (restLength !== 0) { + data.set( + audio[bufferedChunks].slice(0, restLength), + chunk * bufferedChunks + ); + } + + buffer.copyToChannel(data, i, 0); + } + this.buffer = buffer; + } + + async start() { + delete this.buffer; + this.headerRecieved = false; + this.audioType = ""; + this.errored = false; + this.buffered = 0; + this.sampleRate = 0; + this.bufferedSamples = 0; + this.duration = 0; + this.loaded = false; + if (this.playing) this.output.stop(); + this.playing = false; + this.lastStartTime = this.ac.currentTime; + } + + end(done, reason) { + if (done && this.buffer) { + this.loaded = true; + delete this.controller; + this.mergeBuffers(); + + this.duration = this.buffered; + this.audioData = []; + this.decoder?.destroy(); + delete this.decoder; + delete this.parser; + } else { + console.warn( + "Unexpected end when loading stream audio, reason: '" + + (reason ?? "") + + "'" + ); + } + } + + playAudio(when) { + if (!this.buffer) return; + this.lastStartTime = this.ac.currentTime; + if (this.playing) this.output.stop(); + if (this.player.status !== AudioStatus.Playing) { + this.player.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.playing = true; + + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.player.status === AudioStatus.Playing) { + this.player.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + /** + * 开始播放这个音频源 + */ + play(when) { + if (this.playing || this.errored) return; + if (this.loaded && this.buffer) { + this.playing = true; + this.playAudio(when); + } else { + this.controller?.start(); + } + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + if (this.playing) this.output.stop(); + this.playing = false; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + /** + * 停止播放这个音频源 + * @returns 音频暂停的时刻 number + */ + stop() { + if (this.playing) this.output.stop(); + this.playing = false; + return this.ac.currentTime - this.lastStartTime; + } + /** + * 连接到音频路由图上,每次调用播放的时候都会执行一次 + * @param target 连接至的目标 IAudioInput + */ + connect(target) { + this.target = target; + } + /** + * 设置是否循环播放 + * @param loop 是否循环 boolean) + */ + setLoop(loop) { + this.loop = loop; + } + } + class AudioElementSource { + constructor(context) { + const audio = new Audio(); + audio.preload = "none"; + this.output = context.createMediaElementSource(audio); + this.audio = audio; + this.ac = context; + audio.addEventListener("play", () => { + this.playing = true; + if (this.player.status !== AudioStatus.Playing) { + this.player.status = AudioStatus.Playing; + } + }); + audio.addEventListener("ended", () => { + this.playing = false; + if (this.player.status === AudioStatus.Playing) { + this.player.status = AudioStatus.Stoped; + } + }); + } + get duration() { + return this.audio.duration; + } + get currentTime() { + return this.audio.currentTime; + } + /** + * 设置音频源的路径 + * @param url 音频路径 + */ + setSource(url) { + this.audio.src = url; + } + + play(when = 0) { + if (this.playing) return; + this.audio.currentTime = when; + this.audio.play(); + } + + stop() { + this.audio.pause(); + this.playing = false; + if (this.player.status === AudioStatus.Playing) { + this.player.status = AudioStatus.Stoped; + } + return this.audio.currentTime; + } + + connect(target) { + this.output.connect(target.input); + } + + setLoop(loop) { + this.audio.loop = loop; + } + } + class AudioBufferSource { + constructor(context) { + this.output = context.createBufferSource(); + /** 是否循环 */ + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 播放开始时刻 */ + this.lastStartTime = 0; + this.duration = 0; + this.ac = context; + } + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + + /** + * 设置音频源数据 + * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer + */ + async setBuffer(buffer) { + if (buffer instanceof ArrayBuffer) { + this.buffer = await this.ac.decodeAudioData(buffer); + } else { + this.buffer = buffer; + } + this.duration = this.buffer.duration; + } + + play(when) { + if (this.playing || !this.buffer) return; + this.playing = true; + this.lastStartTime = this.ac.currentTime; + if (this.player.status !== AudioStatus.Playing) { + this.player.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.player.status === AudioStatus.Playing) { + this.player.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + + stop() { + this.output.stop(); + return this.ac.currentTime - this.lastStartTime; + } + + connect(target) { + this.target = target; + } + + setLoop(loop) { + this.loop = loop; + } + } + class AudioPlayer { + constructor() { + /** 音频播放上下文 */ + this.ac = new AudioContext(); + /** 音量节点 */ + this.gain = this.ac.createGain(); + this.gain.connect(this.ac.destination); + this.audioRoutes = new Map(); + } + /** + * 解码音频数据 + * @param data 音频数据 + */ + decodeAudioData(data) { + return AudioDecoder.decodeAudioData(data, this); + } + /** + * 设置音量 + * @param volume 音量 + */ + setVolume(volume) { + this.gain.gain.value = volume; + } + + /** + * 获取音量 + */ + getVolume() { + return this.gain.gain.value; + } + + /** + * 创建一个音频源 + * @param Source 音频源类 + */ + createSource(Source) { + return new Source(this.ac); + } + + /** + * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 + */ + createStreamSource() { + return new AudioStreamSource(this.ac); + } + + /** + * 创建一个通过 audio 元素播放的音频源 + */ + createElementSource() { + return new AudioElementSource(this.ac); + } + + /** + * 创建一个通过 AudioBuffer 播放的音频源 + */ + createBufferSource() { + return new AudioBufferSource(this.ac); + } + + /** + * 获取音频目的地 + */ + getDestination() { + return this.gain; + } + + /** + * 创建一个音频效果器 + * @param Effect 效果器类 + */ + createEffect(Effect) { + return new Effect(this.ac); + } + + /** + * 创建一个修改音量的效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * |----------| + * ``` + */ + createVolumeEffect() { + return new VolumeEffect(this.ac); + } + + /** + * 创建一个立体声效果器 + * ```txt + * |------------| + * Input ----> | PannerNode | ----> Output + * |------------| + * ``` + */ + createStereoEffect() { + return new StereoEffect(this.ac); + } + + /** + * 创建一个修改单个声道音量的效果器 + * ```txt + * |----------| + * -> | GainNode | \ + * |--------------| / |----------| -> |------------| + * Input ----> | SplitterNode | ...... | MergerNode | ----> Output + * |--------------| \ |----------| -> |------------| + * -> | GainNode | / + * |----------| + * ``` + */ + createChannelVolumeEffect() { + return new ChannelVolumeEffect(this.ac); + } + + /** + * 创建一个延迟效果器 + * |-----------| + * Input ----> | DelayNode | ----> Output + * |-----------| + */ + createDelay() { + return new DelayEffect(this.ac); + } + + /** + * 创建一个回声效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * ^ |----------| | + * | | + * | |------------| ↓ + * |-- | Delay Node | <-- + * |------------| + * ``` + */ + createEchoEffect() { + return new EchoEffect(this.ac); + } + + /** + * 创建一个音频播放路由 + * @param source 音频源 + */ + createRoute(source) { + return new AudioRoute(source, this); + } + + /** + * 添加一个音频播放路由,可以直接被播放 + * @param id 这个音频播放路由的名称 + * @param route 音频播放路由对象 + */ + addRoute(id, route) { + if (!this.audioRoutes) this.audioRoutes = new Map(); + if (this.audioRoutes.has(id)) { + console.warn( + "Audio route with id of '" + + id + + "' has already existed. New route will override old route." + ); + } + this.audioRoutes.set(id, route); + } + + /** + * 根据名称获取音频播放路由对象 + * @param id 音频播放路由的名称 + */ + getRoute(id) { + return this.audioRoutes.get(id); + } + /** + * 移除一个音频播放路由 + * @param id 要移除的播放路由的名称 + */ + removeRoute(id) { + this.audioRoutes.delete(id); + } + /** + * 播放音频 + * @param id 音频名称 + * @param when 从音频的哪个位置开始播放,单位秒 + */ + play(id, when) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot play audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + + route.play(when); + } + + /** + * 暂停音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + pause(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.pause(); + } + + /** + * 停止音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + stop(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot stop audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.stop(); + } + + /** + * 继续音频播放 + * @param id 音频名称 + */ + resume(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + route.resume(); + } + + /** + * 设置听者位置,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 + * @param y 位置y坐标 + * @param z 位置z坐标 + */ + setListenerPosition(x, y, z) { + const listener = this.ac.listener; + listener.positionX.value = x; + listener.positionY.value = y; + listener.positionZ.value = z; + } + + /** + * 设置听者朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 + * @param y 朝向y坐标 + * @param z 朝向z坐标 + */ + setListenerOrientation(x, y, z) { + const listener = this.ac.listener; + listener.forwardX.value = x; + listener.forwardY.value = y; + listener.forwardZ.value = z; + } + + /** + * 设置听者头顶朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 头顶朝向x坐标 + * @param y 头顶朝向y坐标 + * @param z 头顶朝向z坐标 + */ + setListenerUp(x, y, z) { + const listener = this.ac.listener; + listener.upX.value = x; + listener.upY.value = y; + listener.upZ.value = z; + } + } + class AudioRoute { + constructor(source, player) { + this.output = source.output; + + /** 效果器路由图 */ + this.effectRoute = []; + + /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ + this.endTime = 0; + /** 暂停时播放了多长时间 */ + this.pauseCurrentTime = 0; + /** 当前播放状态 */ + this.player = player; + this.player.status = AudioStatus.Stoped; + this.shouldStop = false; + /** + * 每次暂停或停止时自增,用于判断当前正在处理的情况。 + * 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作 + */ + this.stopIdentifier = 0; + /** 暂停时刻 */ + this.pauseTime = 0; + this.source = source; + this.source.player = player; + } + /** 音频时长,单位秒 */ + get duration() { + return this.source.duration; + } + /** 当前播放了多长时间,单位秒 */ + get currentTime() { + if (this.player.status === AudioStatus.Paused) { + return this.pauseCurrentTime; + } else { + return this.source.currentTime; + } + } + set currentTime(time) { + this.source.stop(); + this.source.play(time); + } + /** + * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 + * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 + */ + setEndTime(time) { + this.endTime = time; + } + + /** + * 当音频播放时执行的函数,可以用于音频淡入效果 + * @param fn 音频开始播放时执行的函数 + */ + onStart(fn) { + this.audioStartHook = fn; + } + + /** + * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 + * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 + * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 + */ + onEnd(fn) { + this.audioEndHook = fn; + } + + /** + * 开始播放这个音频 + * @param when 从音频的什么时候开始播放,单位秒 + */ + play(when = 0) { + if (this.player.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.player.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + if (this.source.player.status !== AudioStatus.Playing) { + this.source.player.status = AudioStatus.Playing; + } + } + + /** + * 暂停音频播放 + */ + async pause() { + if (this.player.status !== AudioStatus.Playing) return; + this.player.status = AudioStatus.Pausing; + this.stopIdentifier++; + const identifier = this.stopIdentifier; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + if ( + this.player.status !== AudioStatus.Pausing || + this.stopIdentifier !== identifier + ) { + return; + } + this.pauseCurrentTime = this.source.currentTime; + const time = this.source.stop(); + this.pauseTime = time; + if (this.shouldStop) { + this.player.status = AudioStatus.Stoped; + this.endAllEffect(); + + this.shouldStop = false; + } else { + this.player.status = AudioStatus.Paused; + this.endAllEffect(); + } + this.endAllEffect(); + } + + /** + * 继续音频播放 + */ + resume() { + if (this.player.status === AudioStatus.Playing) return; + if ( + this.player.status === AudioStatus.Pausing || + this.player.status === AudioStatus.Stoping + ) { + this.audioStartHook?.(this); + + return; + } + if (this.player.status === AudioStatus.Paused) { + this.play(this.pauseTime); + } else { + this.play(0); + } + this.player.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + } + + /** + * 停止音频播放 + */ + async stop() { + 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.status = AudioStatus.Stoped; + this.pauseTime = 0; + this.endAllEffect(); + } + + /** + * 添加效果器 + * @param effect 要添加的效果,可以是数组,表示一次添加多个 + * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 + */ + addEffect(effect, index) { + if (isNil(index)) { + if (effect instanceof Array) { + this.effectRoute.push(...effect); + } else { + this.effectRoute.push(effect); + } + } else { + if (effect instanceof Array) { + this.effectRoute.splice(index, 0, ...effect); + } else { + this.effectRoute.splice(index, 0, effect); + } + } + this.setOutput(); + if (this.source.playing) this.link(); + } + + /** + * 移除一个效果器 + * @param effect 要移除的效果 + */ + removeEffect(effect) { + const index = this.effectRoute.indexOf(effect); + if (index === -1) return; + this.effectRoute.splice(index, 1); + effect.disconnect(); + this.setOutput(); + if (this.source.playing) this.link(); + } + + setOutput() { + const effect = this.effectRoute.at(-1); + if (!effect) this.output = this.source.output; + else this.output = effect.output; + } + + /** + * 连接音频路由图 + */ + link() { + this.effectRoute.forEach((v) => v.disconnect()); + this.effectRoute.forEach((v, i) => { + const next = this.effectRoute[i + 1]; + if (next) { + v.connect(next); + } + }); + } + + startAllEffect() { + this.effectRoute.forEach((v) => v.start()); + } + + endAllEffect() { + this.effectRoute.forEach((v) => v.end()); + } + } + + const audioPlayer = new AudioPlayer(); + + class BgmController { + constructor(player) { + this.mainGain = player.createVolumeEffect(); + this.player = player; + /** bgm音频名称的前缀 */ + this.prefix = "bgms."; + /** 每个 bgm 的音量控制器 */ + this.gain = new Map(); + + /** 正在播放的 bgm */ + this.playingBgm = ""; + /** 是否正在播放 */ + this.playing = false; + + /** 是否已经启用 */ + this.enabled = true; + /** 是否屏蔽所有的音乐切换 */ + this.blocking = false; + /** 渐变时长 */ + this.transitionTime = 2000; + } + + /** + * 设置音频渐变时长 + * @param time 渐变时长 + */ + setTransitionTime(time) { + 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) { + this.mainGain.setVolume(volume); + this._volume = volume; + } + /** + * 获取总音量大小 + */ + getVolume() { + return this.mainGain.getVolume(); + } + /** + * 设置是否启用 + * @param enabled 是否启用 + */ + setEnabled(enabled) { + if (enabled) this.resume(); + else this.stop(); + this.enabled = enabled; + } + + /** + * 设置 bgm 音频名称的前缀 + */ + setPrefix(prefix) { + this.prefix = prefix; + } + + getId(name) { + return `${this.prefix}${name}`; + } + + /** + * 根据 bgm 名称获取其 AudioRoute 实例 + * @param id 音频名称 + */ + get(id) { + return this.player.getRoute(this.getId(id)); + } + + /** + * 添加一个 bgm + * @param id 要添加的 bgm 的名称 + * @param url 指定 bgm 的加载地址 + */ + addBgm(id, url = `project/bgms/${id}`) { + const type = guessTypeByExt(id); + if (!type) { + console.warn( + "Unknown audio extension name: '" + + 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) { + this.player.removeRoute(this.getId(id)); + const gain = this.gain.get(id); + gain?.transition.ticker.destroy(); + this.gain.delete(id); + } + + setTransition(id, route, gain) { + 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) => { + 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, when) { + 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; + if (this.player.status !== AudioStatus.Playing) { + this.player.status = AudioStatus.Playing; + } + } + + /** + * 继续当前的 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; + } + } + const bgmController = new BgmController(audioPlayer); + + class SoundPlayer { + constructor(player) { + /** 每个音效的唯一标识符 */ + this.num = 0; + this.enabled = true; + this.gain = player.createVolumeEffect(); + /** 每个音效的数据 */ + this.buffer = new Map(); + /** 所有正在播放的音乐 */ + this.playing = new Set(); + this.player = player; + } + /** + * 设置是否启用音效 + * @param enabled 是否启用音效 + */ + setEnabled(enabled) { + if (!enabled) this.stopAllSounds(); + this.enabled = enabled; + } + + /** + * 设置音量大小 + * @param volume 音量大小 + */ + setVolume(volume) { + this.gain.setVolume(volume); + } + /** + * 获取音量大小 + */ + getVolume() { + return this.gain.getVolume(); + } + /** + * 添加一个音效 + * @param id 音效名称 + * @param data 音效的Uint8Array数据 + */ + async add(id, data) { + const buffer = await this.player.decodeAudioData(data); + if (!buffer) { + console.warn( + "Cannot decode sound '" + + id + + "', since audio file may not supported by 2.b." + ); + return; + } + this.buffer.set(id, buffer); + } + + /** + * 播放一个音效 + * @param id 音效名称 + * @param position 音频位置,[0, 0, 0]表示正中心,x轴指向水平向右,y轴指向水平向上,z轴指向竖直向上 + * @param orientation 音频朝向,[0, 1, 0]表示朝向前方 + */ + play(id, position = [0, 0, 0], orientation = [1, 0, 0]) { + if (!this.enabled) return -1; + const buffer = this.buffer.get(id); + if (!buffer) { + console.warn( + "Cannot play sound '" + + id + + "', since there is no added data named it." + ); + return -1; + } + const soundNum = this.num++; + + const source = this.player.createBufferSource(); + source.setBuffer(buffer); + const route = this.player.createRoute(source); + const stereo = this.player.createStereoEffect(); + stereo.setPosition(position[0], position[1], position[2]); + stereo.setOrientation(orientation[0], orientation[1], orientation[2]); + route.addEffect([stereo, this.gain]); + this.player.addRoute(`sounds.${soundNum}`, route); + route.play(); + source.output.addEventListener("ended", () => { + this.playing.delete(soundNum); + }); + this.playing.add(soundNum); + return soundNum; + } + + /** + * 停止一个音效 + * @param num 音效的唯一 id + */ + stop(num) { + const id = `sounds.${num}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + this.playing.delete(num); + } + } + + /** + * 停止播放所有音效 + */ + stopAllSounds() { + this.playing.forEach((v) => { + const id = `sounds.${v}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + } + }); + this.playing.clear(); + } + } + const soundPlayer = new SoundPlayer(audioPlayer); + + function loadAllBgm() { + const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + for (const bgm of data.main.bgms) { + bgmController.addBgm(bgm); + } + } + loadAllBgm(); + AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder); + AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder); + + core.plugin.audioSystem = { + AudioType, + AudioDecoder, + AudioStatus, + checkAudioType, + isAudioSupport, + audioPlayer, + soundPlayer, + bgmController, + guessTypeByExt, + BgmController, + SoundPlayer, + EchoEffect, + DelayEffect, + ChannelVolumeEffect, + VolumeEffect, + StereoEffect, + AudioEffect, + AudioPlayer, + AudioRoute, + AudioStreamSource, + AudioElementSource, + AudioBufferSource, + loadAllBgm, + StreamLoader, + }; + //bgm相关复写 + control.prototype.playBgm = (bgm, when) => { + bgmController.play(bgm, when); + core.setMusicBtn(); + }; + control.prototype.pauseBgm = () => { + bgmController.pause(); + core.setMusicBtn(); + }; + + control.prototype.resumeBgm = function () { + bgmController.resume(); + core.setMusicBtn(); + }; + control.prototype.checkBgm = function () { + if (bgmController.playing) return; + if (core.musicStatus.bgmStatus) { + if (bgmController.playingBgm) { + bgmController.play(bgmController.playingBgm); + } else { + play(main.startBgm, 0); + } + } else { + pause(); + } + }; + control.prototype.triggerBgm = function () { + core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; + if (bgmController.playing) bgmController.pause(); + else bgmController.resume(); + core.setMusicBtn(); + core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus); + }; + //sound相关复写 + control.prototype.playSound = function ( + sound, + _pitch, + callback, + position, + orientation + ) { + if (main.mode != "play" || !core.musicStatus.soundStatus) return; + const name = core.getMappedName(sound); + const num = soundPlayer.play(name, position, orientation); + const route = audioPlayer.getRoute(`sounds.${num}`); + if (!route) { + callback?.(); + return -1; + } else { + sleep(route.duration).then(() => callback?.()); + return num; + } + }; + control.prototype.stopSound = function (id) { + if (isNil(id)) { + soundPlayer.stopAllSounds(); + } else { + soundPlayer.stop(id); + } + }; + control.prototype.getPlayingSounds = function () { + return [...soundPlayer.playing]; + }; + //sound加载复写 + loader.prototype._loadOneSound_decodeData = function (name, data) { + if (data instanceof Blob) { + var blobReader = new zip.BlobReader(data); + blobReader.init(function () { + blobReader.readUint8Array(0, blobReader.size, function (uint8) { + //core.loader._loadOneSound_decodeData(name, uint8.buffer); + soundPlayer.add(name, uint8); + }); + }); + return; + } + if (data instanceof ArrayBuffer) { + const uint8 = new Uint8Array(data); + soundPlayer.add(name, uint8); + } + }; + //音量控制复写 + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); + if (value == 0 && delta < 0) return; + core.musicStatus.userVolume = core.clamp( + Math.pow(value + delta, 2) / 100, + 0, + 1 + ); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) + core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + core.setLocalStorage("userVolume", core.musicStatus.userVolume); + core.playSound("确定"); + core.ui._drawSwitchs_sounds(); + }; +}, "怪物碎裂特效": function () { // 在此增加新插件 // -------------------- 安装说明 -------------------- // @@ -8580,6 +10681,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = return { animation: ani, + onEnd: promise.then(() => { ani.ticker.destroy(); onEnd(); @@ -9308,8 +11410,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = MotaActionFunctions.actionParser.parse( { time: 160, - openSound: "door.mp3", - closeSound: "door.mp3", + openSound: "door.opus", + closeSound: "door.opus", keys: { yellowKey: 1, orangeKey: 1 }, }, "doorInfo" @@ -9318,9 +11420,9 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = MotaActionBlocks["mainStyle_m"].xmlText(), MotaActionFunctions.actionParser.parse( { - 背景音乐: "bgm.mp3", - 确定: "confirm.mp3", - 攻击: "attack.mp3", + 背景音乐: "bgm.opus", + 确定: "confirm.opus", + 攻击: "attack.opus", 背景图: "bg.webp", 领域: "zone", 文件名: "file.jpg", @@ -12491,7 +14593,14 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }); if (count > 0) list.splice(0, count); } - if (!main.replayChecking) core.registerAnimationFrame("pop", true, pop); + let now = 0; + if (!main.replayChecking) + core.registerAnimationFrame("pop", true, (temptime) => { + if (temptime - now > 1000 / 60) { + now = temptime; + pop(); + } + }); /** 添加弹出内容 */ this.addPop = function ( @@ -12528,7 +14637,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = "warning": function () { // 在此增加新插件 // 默认音效名 - var defaultSound = "jingbao.mp3"; + var defaultSound = "jingbao.opus"; // 默认字体名 var defaultFont = "Verdana"; @@ -14123,7 +16232,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } }); bgm = core.musicStatus.playingBgm; - core.playBgm("op.mp3"); + core.playBgm("op.opus"); a = setTimeout(() => { video.remove(); video1.remove(); @@ -14256,1097 +16365,941 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }; }, "musicMode": function () { - // 在此增加新插件 - const music = document.createElement("canvas"); - music.style.position = "absolute"; - music.style.zIndex = 300; - music.style.display = "none"; - music.id = "music"; - main.dom.gameGroup.insertAdjacentElement("afterend", music); - music.style.top = "50%"; - music.style.left = "50%"; - music.style.transform = "translate(-50%,-50%)"; - const ctx = music.getContext("2d"); - main.dom.music = music; + // 在此增加新插件 + const music = document.createElement("canvas"); + music.style.position = "absolute"; + music.style.zIndex = 300; + music.style.display = "none"; + music.id = "music"; + main.dom.gameGroup.insertAdjacentElement("afterend", music); + music.style.top = "50%"; + music.style.left = "50%"; + music.style.transform = "translate(-50%,-50%)"; + const ctx = music.getContext("2d"); + main.dom.music = music; - const audio = document.createElement("audio"); - audio.autoplay = true; - audio.preload = "auto"; + const audio = core.plugin.audioSystem.bgmController; - function getRandomInt(min, max) { - const minCeiled = Math.ceil(min); - const maxFloored = Math.floor(max); - return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); - } - let page = 0; //初始页面 - let ischange = false; - let isvolume = false; + let page = 0; //初始页面 - function shuffle(arr) { - let n = arr.length, - random; - while (n) { - random = (Math.random() * n--) >>> 0; - [arr[n], arr[random]] = [arr[random], arr[n]]; - } - return arr; - } - music.addEventListener("mousedown", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor((e.clientX - left) / core.domStyle.scale), - py = Math.floor((e.clientY - top) / core.domStyle.scale); - core.ui.music.mousedown(px * 3, py * 3); - }); - music.addEventListener("mousemove", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor((e.clientX - left) / core.domStyle.scale), - py = Math.floor((e.clientY - top) / core.domStyle.scale); - core.ui.music.mousemove(px * 3, py * 3); - }); - music.addEventListener("mouseup", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("mouseleave", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("touchstart", function (e) { - e.preventDefault(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor( - (e.touches[0].clientX - left) / core.domStyle.scale - ), - py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); - core.ui.music.mousedown(px * 3, py * 3); - }); - music.addEventListener("touchmove", function (e) { - e.stopPropagation(); - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor( - (e.touches[0].clientX - left) / core.domStyle.scale - ), - py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); - core.ui.music.mousemove(px * 3, py * 3); - }); - music.addEventListener("touchend", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - if (!main.core.ui.music.stop) audio.play(); - }); - music.addEventListener("touchcancel", function (e) { - e.stopPropagation(); - ischange = false; - isvolume = false; - }); + let isvolume = false; - audio.addEventListener("ended", function () { - switch (main.core.ui.music.type) { - case "danqu": - audio.currentTime = 0; - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - page = main.core.ui.music.selection[0]; + function shuffle(arr) { + let n = arr.length, + random; + while (n) { + random = (Math.random() * n--) >>> 0; + [arr[n], arr[random]] = [arr[random], arr[n]]; + } + return arr; + } + music.addEventListener("mousedown", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor((e.clientX - left) / core.domStyle.scale), + py = Math.floor((e.clientY - top) / core.domStyle.scale); + core.ui.music.mousedown(px * 3, py * 3); + }); + music.addEventListener("mousemove", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor((e.clientX - left) / core.domStyle.scale), + py = Math.floor((e.clientY - top) / core.domStyle.scale); + core.ui.music.mousemove(px * 3, py * 3); + }); + music.addEventListener("mouseup", function (e) { + e.stopPropagation(); - break; - case "xunhuan": - if ( - main.core.ui.music.selection[1] === - main.core.ui.music.musicMx[main.core.ui.music.selection[0]].length - - 1 - ) { - if ( - main.core.ui.music.selection[0] === - main.core.ui.music.musicMx.length - 1 - ) { - main.core.ui.music.selection[0] = 0; - main.core.ui.music.selection[1] = 0; - } else { - main.core.ui.music.selection[0] += 1; - main.core.ui.music.selection[1] = 0; - } - } else { - main.core.ui.music.selection[1] += 1; - } - main.core.ui.music.randomList.indexOf( - (v) => - v === - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ] - ); - page = main.core.ui.music.selection[0]; - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; + isvolume = false; + }); + music.addEventListener("mouseleave", function (e) { + e.stopPropagation(); - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - break; - case "suiji": - if ( - main.core.ui.music.random < - main.core.ui.music.randomList.length - 1 - ) { - main.core.ui.music.random += 1; - } else { - main.core.ui.music.random = 0; - } - main.core.ui.music.selection[0] = - main.core.ui.music.musicMx.findIndex((v) => - v.includes( - main.core.ui.music.randomList[main.core.ui.music.random] - ) - ); - main.core.ui.music.selection[1] = main.core.ui.music.musicMx[ - main.core.ui.music.selection[0] - ].indexOf(main.core.ui.music.randomList[main.core.ui.music.random]); - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; - page = main.core.ui.music.selection[0]; + isvolume = false; + }); + music.addEventListener("touchstart", function (e) { + e.preventDefault(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor( + (e.touches[0].clientX - left) / core.domStyle.scale + ), + py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); + core.ui.music.mousedown(px * 3, py * 3); + }); + music.addEventListener("touchmove", function (e) { + e.stopPropagation(); + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor( + (e.touches[0].clientX - left) / core.domStyle.scale + ), + py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale); + core.ui.music.mousemove(px * 3, py * 3); + }); + music.addEventListener("touchend", function (e) { + e.stopPropagation(); - if (!main.core.ui.music.stop) audio.play(); - main.core.ui.music.stop = false; - break; - } - }); + isvolume = false; + }); + music.addEventListener("touchcancel", function (e) { + e.stopPropagation(); - class musicclass { - constructor() { - //music列表 - //需全塔属性注册并保存在bgms文件夹,每个数组为显示的一页内容 - this.musicMx = [ - ["Asphodelus_Ceui.mp3", "Blind_Alley.mp3"], - ["Crawler.mp3", "op.mp3", "theme.mp3"], - ]; - //音乐别名(将在播放器内显示的音乐名,music列表内的都要有对应歌名) - this.musicname = { - "Asphodelus_Ceui.mp3": "Asphodelus", - "Blind_Alley.mp3": "Blind", - "Crawler.mp3": "Crawler", - "op.mp3": "op", - "theme.mp3": "theme", - }; - this.selection = [0, 0]; - this.stop = false; - this.type = "xunhuan"; - this.randomList = []; - this.random = 0; - } + isvolume = false; + }); - //更新 - update() { - this.background(); - this.drawUI(); - } - background() { - //画布大小设置 - if (core.domStyle.isVertical) { - music.width = 1248; - music.height = 2028; - } else { - music.width = 2028; - music.height = 1248; - } - } - mousedown(px, py) { - //鼠标按下时 - //console.log(px, py) - const makeBox = ([x, y], [w, h]) => { - return [ - [x, y], - [x + w, y + h], - ]; - }; - const inRect = ([x, y], [[sx, sy], [dx, dy]]) => { - return sx <= x && x <= dx && sy <= y && y <= dy; - }; - const pos = [px, py]; - const backbox = makeBox([15, 35], [210, 90]); - if (inRect(pos, backbox)) { - //离开按钮是一致的,其余的记区分横竖屏 - music.style.display = "none"; - core.clearMap(ctx); - core.unregisterAnimationFrame("music"); - audio.src = ""; - core.restart(); + class musicclass { + constructor() { + //music列表 + //需全塔属性注册并保存在bgms文件夹,每个数组为显示的一页内容 + this.musicMx = [ + ["Asphodelus_Ceui.opus", "Blind_Alley.opus"], + ["Crawler.opus", "op.opus", "theme.opus"], + ]; + //音乐别名(将在播放器内显示的音乐名,music列表内的都要有对应歌名) + this.musicname = { + "Asphodelus_Ceui.opus": "Asphodelus", + "Blind_Alley.opus": "Blind", + "Crawler.opus": "Crawler", + "op.opus": "op", + "theme.opus": "theme", + }; + this.selection = [0, 0]; + this.stop = false; + this.type = "xunhuan"; + this.randomList = []; + this.random = 0; + } - return; - } - if (core.domStyle.isVertical) { - //竖屏 + //更新 + update() { + this.background(); + this.drawUI(); + } + background() { + //画布大小设置 + if (core.domStyle.isVertical) { + music.width = 1248; + music.height = 2028; + } else { + music.width = 2028; + music.height = 1248; + } + } + mousedown(px, py) { + //鼠标按下时 - const pageupbox = makeBox([100, 1230], [200, 100]); - const pagedownbox = makeBox([950, 1230], [200, 100]); - const musicbox = makeBox( - [100, 200], - [1048, this.musicMx[page].length * 100] - ); - const beforebox = makeBox([120, 1720], [100, 100]); - const afterbox = makeBox([780, 1720], [100, 100]); - const playbox = makeBox([420, 1680], [200, 200]); - const typebox = makeBox([1040, 1700], [120, 120]); - const changebox = makeBox([100, 1590], [1048, 20]); - const volumebox = makeBox([250, 1940], [1050, 20]); - if (inRect(pos, pageupbox)) { - if (page !== 0) page -= 1; - return; - } - if (inRect(pos, pagedownbox)) { - if (page !== this.musicMx.length - 1) page += 1; - return; - } - if (inRect(pos, playbox)) { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - return; - } - if (inRect(pos, beforebox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; + const makeBox = ([x, y], [w, h]) => { + return [ + [x, y], + [x + w, y + h], + ]; + }; + const inRect = ([x, y], [ + [sx, sy], + [dx, dy] + ]) => { + return sx <= x && x <= dx && sy <= y && y <= dy; + }; + const pos = [px, py]; + const backbox = makeBox([15, 35], [210, 90]); + if (inRect(pos, backbox)) { + //离开按钮是一致的,其余的记区分横竖屏 + music.style.display = "none"; + core.clearMap(ctx); - break; - case "xunhuan": - if (this.selection[1] === 0) { - if (this.selection[0] === 0) { - this.selection[0] = this.musicMx.length - 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } else { - this.selection[0] -= 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } - } else { - this.selection[1] -= 1; - } - this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + core.unregisterAnimationFrame("music"); + core.restart(); - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random > 0) { - this.random -= 1; - } else { - this.random = this.randomList.length - 1; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; + return; + } + if (core.domStyle.isVertical) { + //竖屏 - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, afterbox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; - break; - case "xunhuan": - if ( - this.selection[1] === - this.musicMx[this.selection[0]].length - 1 - ) { - if (this.selection[0] === this.musicMx.length - 1) { - this.selection[0] = 0; - this.selection[1] = 0; - } else { - this.selection[0] += 1; - this.selection[1] = 0; - } - } else { - this.selection[1] += 1; - } - this.random = this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + const pageupbox = makeBox([100, 1230], [200, 100]); + const pagedownbox = makeBox([950, 1230], [200, 100]); + const musicbox = makeBox( + [100, 200], + [1048, this.musicMx[page].length * 100] + ); + const beforebox = makeBox([120, 1620], [100, 100]); + const afterbox = makeBox([780, 1620], [100, 100]); + const playbox = makeBox([420, 1580], [200, 200]); + const typebox = makeBox([1040, 1600], [120, 120]); - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random < this.randomList.length - 1) { - this.random += 1; - } else { - this.random = 0; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + const volumebox = makeBox([250, 1940], [1050, 20]); + if (inRect(pos, pageupbox)) { + if (page !== 0) page -= 1; + return; + } + if (inRect(pos, pagedownbox)) { + if (page !== this.musicMx.length - 1) page += 1; + return; + } + if (inRect(pos, playbox)) { + if (this.stop) { + this.stop = !this.stop; - page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, typebox)) { - switch (this.type) { - case "danqu": - this.type = "xunhuan"; - break; - case "xunhuan": - this.type = "suiji"; - break; - case "suiji": - this.type = "danqu"; - break; - } - return; - } - if (inRect(pos, musicbox)) { - const index = Math.floor((py - 200) / 100); - if (page !== this.selection[0] || index !== this.selection[1]) { - this.selection[0] = page; - this.selection[1] = index; - this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - audio.src = "project/bgms/" + this.musicMx[page][index]; + core.resumeBgm(); + } else { + this.stop = !this.stop; + core.pauseBgm(); + } + return; + } + if (inRect(pos, beforebox)) { + this.stop = false; + switch (this.type) { + case "danqu": + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - if (!this.stop) audio.play(); - this.stop = false; - } else { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - } - return; - } - if (inRect(pos, changebox)) { - const time = Math.floor(((px - 100) / 1000) * audio.duration); + page = this.selection[0]; - audio.pause(); - audio.currentTime = time; + break; + case "xunhuan": + if (this.selection[1] === 0) { + if (this.selection[0] === 0) { + this.selection[0] = this.musicMx.length - 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } else { + this.selection[0] -= 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } + } else { + this.selection[1] -= 1; + } + this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; - ischange = true; - } - if (inRect(pos, volumebox)) { - const time = Math.min(Math.max((px - 250) / 800, 0), 1); - audio.volume = time; - isvolume = true; - } - } else { - //横屏 - const pageupbox = makeBox([1050, 1100], [200, 100]); - const pagedownbox = makeBox([1550, 1100], [200, 100]); - const musicbox = makeBox( - [900, 100], - [1000, this.musicMx[page].length * 100] - ); - const beforebox = makeBox([135, 740], [50, 50]); - const afterbox = makeBox([450, 740], [50, 50]); - const playbox = makeBox([250, 700], [200, 200]); - const typebox = makeBox([600, 700], [100, 100]); - const changebox = makeBox([100, 590], [600, 20]); - const volumebox = makeBox([100, 990], [600, 20]); - if (inRect(pos, pageupbox)) { - if (page !== 0) page -= 1; - return; - } - if (inRect(pos, pagedownbox)) { - if (page !== this.musicMx.length - 1) page += 1; - return; - } - if (inRect(pos, playbox)) { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - return; - } - if (inRect(pos, beforebox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - break; - case "xunhuan": - if (this.selection[1] === 0) { - if (this.selection[0] === 0) { - this.selection[0] = this.musicMx.length - 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } else { - this.selection[0] -= 1; - this.selection[1] = - this.musicMx[this.selection[0]].length - 1; - } - } else { - this.selection[1] -= 1; - } - this.random = this.randomList.indexOf( - this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + break; + case "suiji": + if (this.random > 0) { + this.random -= 1; + } else { + this.random = this.randomList.length - 1; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random > 0) { - this.random -= 1; - } else { - this.random = this.randomList.length - 1; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - this.randomList[this.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; - page = this.selection[0]; + page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, afterbox)) { - switch (this.type) { - case "danqu": - audio.currentTime = 0; - if (!this.stop) audio.play(); - this.stop = false; - page = this.selection[0]; - break; - case "xunhuan": - if ( - this.selection[1] === - this.musicMx[this.selection[0]].length - 1 - ) { - if (this.selection[0] === this.musicMx.length - 1) { - this.selection[0] = 0; - this.selection[1] = 0; - } else { - this.selection[0] += 1; - this.selection[1] = 0; - } - } else { - this.selection[1] += 1; - } - this.randomList.findIndex( - (v) => - v === this.musicMx[this.selection[0]][this.selection[1]] - ); - page = this.selection[0]; - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - if (!this.stop) audio.play(); - this.stop = false; - break; - case "suiji": - if (this.random < this.randomList.length - 1) { - this.random += 1; - } else { - this.random = 0; - } - this.selection[0] = this.musicMx.findIndex((v) => - v.includes(this.randomList[this.random]) - ); - this.selection[1] = this.musicMx[this.selection[0]].indexOf( - main.core.ui.music.randomList[main.core.ui.music.random] - ); - audio.src = - "project/bgms/" + - this.musicMx[this.selection[0]][this.selection[1]]; + break; + } + return; + } + if (inRect(pos, afterbox)) { + this.stop = false; + switch (this.type) { + case "danqu": + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - break; - } - return; - } - if (inRect(pos, typebox)) { - switch (this.type) { - case "danqu": - this.type = "xunhuan"; - break; - case "xunhuan": - this.type = "suiji"; - break; - case "suiji": - this.type = "danqu"; - break; - } - return; - } - if (inRect(pos, musicbox)) { - const index = Math.floor((py - 100) / 100); - if (page !== this.selection[0] || index !== this.selection[1]) { - this.selection[0] = page; - this.selection[1] = index; - this.randomList.indexOf( - (v) => v === this.musicMx[this.selection[0]][this.selection[1]] - ); - audio.src = "project/bgms/" + this.musicMx[page][index]; + page = this.selection[0]; + break; + case "xunhuan": + if ( + this.selection[1] === + this.musicMx[this.selection[0]].length - 1 + ) { + if (this.selection[0] === this.musicMx.length - 1) { + this.selection[0] = 0; + this.selection[1] = 0; + } else { + this.selection[0] += 1; + this.selection[1] = 0; + } + } else { + this.selection[1] += 1; + } + this.random = this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; - if (!this.stop) audio.play(); - this.stop = false; - } else { - if (this.stop) { - this.stop = !this.stop; - audio.play(); - } else { - this.stop = !this.stop; - audio.pause(); - } - } - return; - } - if (inRect(pos, changebox)) { - const time = Math.floor(((px - 100) / 600) * audio.duration); + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - audio.pause(); - audio.currentTime = time; + break; + case "suiji": + if (this.random < this.randomList.length - 1) { + this.random += 1; + } else { + this.random = 0; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); - ischange = true; - } - if (inRect(pos, volumebox)) { - const time = Math.min(Math.max((px - 100) / 600, 0), 1); - audio.volume = time; - isvolume = true; - } - } - } - mousemove(px, py) { - if (ischange) { - if (core.domStyle.isVertical) { - const time = Math.min( - Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), - audio.duration - ); + page = this.selection[0]; + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - audio.currentTime = time; - } else { - const time = Math.min( - Math.max(Math.floor(((px - 100) / 600) * audio.duration), 0), - audio.duration - ); + break; + } + return; + } + if (inRect(pos, typebox)) { + switch (this.type) { + case "danqu": + this.type = "xunhuan"; + break; + case "xunhuan": + this.type = "suiji"; + break; + case "suiji": + this.type = "danqu"; + break; + } + return; + } + if (inRect(pos, musicbox)) { + const index = Math.floor((py - 200) / 100); + if (page !== this.selection[0] || index !== this.selection[1]) { + this.selection[0] = page; + this.selection[1] = index; + this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); - audio.currentTime = time; - } - } - if (isvolume) { - if (core.domStyle.isVertical) { - const time = Math.min(Math.max((px - 250) / 800, 0), 1); - audio.volume = time; - } else { - const time = Math.min(Math.max((px - 100) / 600, 0), 1); - audio.volume = time; - } - } - } + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - drawUI() { - //绘制页面 - core.clearMap(music); - const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景 - const bg = core.material.images.images["bg_5043.webp"]; //竖屏背景 - if (core.domStyle.isVertical) { - //竖屏 + this.stop = false; + } else { + if (this.stop) { + this.stop = !this.stop; + core.resumeBgm(); + } else { + this.stop = !this.stop; + core.pauseBgm(); + } + } + return; + } - core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景 - ctx.globalAlpha = 0.3; //透明度 - if (bgVertical) - ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片 - ctx.globalAlpha = 1; //恢复为不透明 + if (inRect(pos, volumebox)) { + const time = Math.min(Math.max((px - 250) / 800, 0), 1); + audio.setVolume(time); + core.plugin.audioSystem.soundPlayer.setVolume(time); + isvolume = true; + } + } else { + //横屏 + const pageupbox = makeBox([1050, 1100], [200, 100]); + const pagedownbox = makeBox([1550, 1100], [200, 100]); + const musicbox = makeBox( + [900, 100], + [1000, this.musicMx[page].length * 100] + ); + const beforebox = makeBox([60, 620], [100, 100]); + const afterbox = makeBox([450, 620], [100, 100]); + const playbox = makeBox([200, 570], [200, 200]); + const typebox = makeBox([620, 600], [120, 120]); - core.setTextAlign(ctx, "center"); - core.fillBoldText1( - ctx, - "◀离开", - 110, - 100, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + const volumebox = makeBox([100, 990], [600, 20]); + if (inRect(pos, pageupbox)) { + if (page !== 0) page -= 1; + return; + } + if (inRect(pos, pagedownbox)) { + if (page !== this.musicMx.length - 1) page += 1; + return; + } + if (inRect(pos, playbox)) { + if (this.stop) { + this.stop = !this.stop; + core.resumeBgm(); + } else { + this.stop = !this.stop; + core.pauseBgm(); + } + return; + } + if (inRect(pos, beforebox)) { + this.stop = false; + switch (this.type) { + case "danqu": + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 200); - ctx.lineTo(1148, 200); + page = this.selection[0]; - ctx.stroke(); - let posy = 300; - const indexList = this.musicMx[page]; - core.setTextAlign(ctx, "left"); - for (let i = 0; i < indexList.length; i++) { - const text = this.musicname[indexList[i]]; - core.fillBoldText1( - ctx, - text, - 150, - posy - 30, - page === this.selection[0] && i === this.selection[1] - ? "#FFFFFF" - : "#444444", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, posy); - ctx.lineTo(1148, posy); - ctx.stroke(); - posy += 100; - } - ctx.beginPath(); - ctx.moveTo(100, 1210); - ctx.lineTo(1148, 1210); - ctx.moveTo(100, 1200); - ctx.lineTo(1148, 1200); - ctx.stroke(); + break; + case "xunhuan": + if (this.selection[1] === 0) { + if (this.selection[0] === 0) { + this.selection[0] = this.musicMx.length - 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } else { + this.selection[0] -= 1; + this.selection[1] = + this.musicMx[this.selection[0]].length - 1; + } + } else { + this.selection[1] -= 1; + } + this.random = this.randomList.indexOf( + this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; - core.fillBoldText1( - ctx, - "上一页", - 100, - 1300, - page === 0 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - core.fillBoldText1( - ctx, - page + 1 + "/" + this.musicMx.length, - 580, - 1300, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - core.fillBoldText1( - ctx, - "下一页", - 950, - 1300, - page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + break; + case "suiji": + if (this.random > 0) { + this.random -= 1; + } else { + this.random = this.randomList.length - 1; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + this.randomList[this.random] + ); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 1600); - ctx.lineTo(1148, 1600); - ctx.stroke(); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 96px Verdana"; - ctx.fillText("|", 100, 1797); - ctx.fillText("◀", 115, 1800); - ctx.beginPath(); - ctx.arc(505, 1770, 80, 0, 3 * Math.PI); - ctx.stroke(); - ctx.fillText("|", 835, 1797); - ctx.fillText("▶", 785, 1800); - if (this.stop) { - ctx.fillText("▶", 473, 1797); - } else { - ctx.fillText("||", 453, 1794); - } + page = this.selection[0]; - const img = core.material.images.images[this.type + ".webp"]; - if (img) ctx.drawImage(img, 1000, 1655, 200, 200); - core.setTextAlign(ctx, "center"); - ctx.font = "bold 52px Verdana"; - ctx.fillText("当前歌曲", 625, 1397); - ctx.fillText( - this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], - 625, - 1507 - ); + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - ctx.font = "bold 36px Verdana"; - const thistime = audio.currentTime; + break; + } + return; + } + if (inRect(pos, afterbox)) { + this.stop = false; + switch (this.type) { + case "danqu": + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - if (thistime) { - const timetext = - Math.floor(thistime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(thistime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 960, 1650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 960, 1650); - } - ctx.fillText("/", 1030, 1650); - const fulltime = audio.duration; + page = this.selection[0]; + break; + case "xunhuan": + if ( + this.selection[1] === + this.musicMx[this.selection[0]].length - 1 + ) { + if (this.selection[0] === this.musicMx.length - 1) { + this.selection[0] = 0; + this.selection[1] = 0; + } else { + this.selection[0] += 1; + this.selection[1] = 0; + } + } else { + this.selection[1] += 1; + } + this.randomList.findIndex( + (v) => + v === this.musicMx[this.selection[0]][this.selection[1]] + ); + page = this.selection[0]; - if (fulltime) { - const timetext = - Math.floor(fulltime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(fulltime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 1100, 1650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 1100, 1650); - } - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; - const pointx = (1048 * thistime) / fulltime + 100; - if (fulltime && thistime) { - ctx.beginPath(); - ctx.moveTo(100, 1600); - ctx.lineTo(pointx, 1600); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(pointx, 1600, 10, 0, 2 * Math.PI); - ctx.fill(); - } else { - ctx.beginPath(); - ctx.arc(100, 1600, 10, 0, 2 * Math.PI); - ctx.fill(); - } + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 48px Verdana"; - ctx.fillText("音量", 150, 1970); - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(250, 1950); - ctx.lineTo(1050, 1950); - ctx.stroke(); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; + break; + case "suiji": + if (this.random < this.randomList.length - 1) { + this.random += 1; + } else { + this.random = 0; + } + this.selection[0] = this.musicMx.findIndex((v) => + v.includes(this.randomList[this.random]) + ); + this.selection[1] = this.musicMx[this.selection[0]].indexOf( + main.core.ui.music.randomList[main.core.ui.music.random] + ); - ctx.beginPath(); - ctx.moveTo(250, 1950); - ctx.lineTo(800 * audio.volume + 250, 1950); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(800 * audio.volume + 250, 1950, 10, 0, 2 * Math.PI); - ctx.fill(); - core.fillBoldText1( - ctx, - Math.floor(100 * audio.volume), - 1120, - 1970, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(48, true) - ); - } else { - //横屏 - core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景 - ctx.globalAlpha = 0.5; //透明度 - if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片 - ctx.globalAlpha = 1; //恢复为不透明 - core.setTextAlign(ctx, "center"); + page = this.selection[0]; + if (!this.stop) + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); - core.fillBoldText1( - ctx, - "◀离开", - 110, - 100, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - //core.fillRect(ctx, 440, 760, 50, 50) - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(800, 100); - ctx.lineTo(800, 1148); - ctx.moveTo(900, 100); - ctx.lineTo(1900, 100); - ctx.stroke(); - let posy = 200; - const indexList = this.musicMx[page]; - core.setTextAlign(ctx, "left"); - for (let i = 0; i < indexList.length; i++) { - const text = this.musicname[indexList[i]]; - core.fillBoldText1( - ctx, - text, - 950, - posy - 30, - page === this.selection[0] && i === this.selection[1] - ? "#FFFFFF" - : "#444444", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#FFFFFF"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(900, posy); - ctx.lineTo(1900, posy); - ctx.stroke(); - posy += 100; - } - core.fillBoldText1( - ctx, - "上一页", - 1050, - 1200 - 30, - page === 0 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); + break; + } + return; + } + if (inRect(pos, typebox)) { + switch (this.type) { + case "danqu": + this.type = "xunhuan"; + break; + case "xunhuan": + this.type = "suiji"; + break; + case "suiji": + this.type = "danqu"; + break; + } + return; + } + if (inRect(pos, musicbox)) { + const index = Math.floor((py - 100) / 100); + if (page !== this.selection[0] || index !== this.selection[1]) { + this.selection[0] = page; + this.selection[1] = index; + this.randomList.indexOf( + (v) => v === this.musicMx[this.selection[0]][this.selection[1]] + ); - core.fillBoldText1( - ctx, - page + 1 + "/" + this.musicMx.length, - 1350, - 1200 - 30, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - core.fillBoldText1( - ctx, - "下一页", - 1550, - 1200 - 30, - page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(66, true) - ); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(100, 600); - ctx.lineTo(700, 600); - ctx.stroke(); - ctx.fillStyle = "#ffffff"; - ctx.font = "bold 48px Verdana"; - ctx.fillText("|", 130, 797); - ctx.fillText("◀", 140, 800); - ctx.beginPath(); - ctx.arc(310, 780, 50, 0, 2 * Math.PI); - ctx.stroke(); - if (this.stop) { - ctx.fillText("▶", 295, 797); - } else { - ctx.fillText("||", 285, 794); - } + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); + this.stop = false; + } else { + if (this.stop) { + this.stop = !this.stop; + core.resumeBgm(); + } else { + this.stop = !this.stop; + core.pauseBgm(); + } + } + return; + } - ctx.fillText("|", 470, 797); - ctx.fillText("▶", 450, 800); - ctx.fillText("音量", 350, 900); - ctx.beginPath(); - ctx.moveTo(100, 1000); - ctx.lineTo(700, 1000); - ctx.stroke(); - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; + if (inRect(pos, volumebox)) { + const time = Math.min(Math.max((px - 100) / 600, 0), 1); + audio.setVolume(time); + core.plugin.audioSystem.soundPlayer.setVolume(time); + isvolume = true; + } + } + } + mousemove(px, py) { + if (isvolume) { + if (core.domStyle.isVertical) { + const time = Math.min(Math.max((px - 250) / 800, 0), 1); + audio.setVolume(time); + core.plugin.audioSystem.soundPlayer.setVolume(time); + } else { + const time = Math.min(Math.max((px - 100) / 600, 0), 1); + audio.setVolume(time); + core.plugin.audioSystem.soundPlayer.setVolume(time); + } + } + } - ctx.beginPath(); - ctx.moveTo(100, 1000); - ctx.lineTo(600 * audio.volume + 100, 1000); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(600 * audio.volume + 100, 1000, 10, 0, 2 * Math.PI); - ctx.fill(); - core.fillBoldText1( - ctx, - Math.floor(100 * audio.volume), - 720, - 1010, - "#FFFFFF", - "#000000", - 6, - core.ui._buildFont(32, true) - ); - const img = core.material.images.images[this.type + ".webp"]; - if (img) ctx.drawImage(img, 580, 730, 100, 100); - core.setTextAlign(ctx, "center"); - ctx.font = "bold 48px Verdana"; - ctx.fillText("当前歌曲", 400, 297); - ctx.fillText( - this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], - 400, - 397 - ); + drawUI() { + //绘制页面 + core.clearMap(music); + const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景 + const bg = core.material.images.images["bg_5043.webp"]; //竖屏背景 + if (core.domStyle.isVertical) { + //竖屏 - ctx.font = "bold 36px Verdana"; - const thistime = audio.currentTime; + core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景 + ctx.globalAlpha = 0.3; //透明度 + if (bgVertical) + ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片 + ctx.globalAlpha = 1; //恢复为不透明 - if (thistime) { - const timetext = - Math.floor(thistime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(thistime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 510, 650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 510, 650); - } - ctx.fillText("/", 580, 650); - const fulltime = audio.duration; + core.setTextAlign(ctx, "center"); + core.fillBoldText1( + ctx, + "◀离开", + 110, + 100, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); - if (fulltime) { - const timetext = - Math.floor(fulltime / 60) - .toString() - .padStart(2, "0") + - ":" + - Math.floor(fulltime % 60) - .toString() - .padStart(2, "0"); - ctx.fillText(timetext, 650, 650); - } else { - const timetext = "00:00"; - ctx.fillText(timetext, 650, 650); - } - ctx.strokeStyle = "#ffffff"; - ctx.lineWidth = 9; - ctx.fillStyle = "rgba(255,255,255,0.5)"; - const pointx = (600 * thistime) / fulltime + 100; - if (fulltime && thistime) { - ctx.beginPath(); - ctx.moveTo(100, 600); - ctx.lineTo(pointx, 600); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(pointx, 600, 10, 0, 2 * Math.PI); - ctx.fill(); - } else { - ctx.beginPath(); - ctx.arc(100, 600, 10, 0, 2 * Math.PI); - ctx.fill(); - } - } - } - } - core.ui.music = new musicclass(); - main.dom.musicMode.onclick = function () { - //点击开始页面的CG MODE进入cg回廊 - main.core.control.checkBgm(); - main.core.control.pauseBgm(); - audio.src = - "project/bgms/" + - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ]; - const arr = main.core.ui.music.musicMx.flat(Infinity); - main.core.ui.music.randomList = shuffle(arr); - main.core.ui.music.random = main.core.ui.music.randomList.indexOf( - main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ - main.core.ui.music.selection[1] - ] - ); - page = 0; - music.style.display = "block"; - let time = 0; - core.registerAnimationFrame("music", null, (temptime) => { - if (temptime > time + 1000 / 60) { - time = temptime; - main.core.ui.music.update(); - } - }); - }; - }, + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, 200); + ctx.lineTo(1148, 200); + + ctx.stroke(); + let posy = 300; + const indexList = this.musicMx[page]; + core.setTextAlign(ctx, "left"); + for (let i = 0; i < indexList.length; i++) { + const text = this.musicname[indexList[i]]; + core.fillBoldText1( + ctx, + text, + 150, + posy - 30, + page === this.selection[0] && i === this.selection[1] ? + "#FFFFFF" : + "#444444", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(100, posy); + ctx.lineTo(1148, posy); + ctx.stroke(); + posy += 100; + } + ctx.beginPath(); + ctx.moveTo(100, 1210); + ctx.lineTo(1148, 1210); + ctx.moveTo(100, 1200); + ctx.lineTo(1148, 1200); + ctx.stroke(); + + core.fillBoldText1( + ctx, + "上一页", + 100, + 1300, + page === 0 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + + core.fillBoldText1( + ctx, + page + 1 + "/" + this.musicMx.length, + 580, + 1300, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + core.fillBoldText1( + ctx, + "下一页", + 950, + 1300, + page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 3; + + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 96px Verdana"; + ctx.fillText("|", 100, 1697); + ctx.fillText("◀", 115, 1700); + ctx.beginPath(); + ctx.arc(505, 1670, 80, 0, 3 * Math.PI); + ctx.stroke(); + ctx.fillText("|", 835, 1697); + ctx.fillText("▶", 785, 1700); + if (this.stop) { + ctx.fillText("▶", 473, 1697); + } else { + ctx.fillText("||", 453, 1694); + } + + const img = core.material.images.images[this.type + ".webp"]; + if (img) ctx.drawImage(img, 1000, 1555, 200, 200); + core.setTextAlign(ctx, "center"); + ctx.font = "bold 52px Verdana"; + ctx.fillText("当前歌曲", 625, 1397); + ctx.fillText( + this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], + 625, + 1507 + ); + + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 48px Verdana"; + ctx.fillText("音量", 150, 1970); + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(250, 1950); + ctx.lineTo(1050, 1950); + ctx.stroke(); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; + + ctx.beginPath(); + ctx.moveTo(250, 1950); + ctx.lineTo(800 * audio.getVolume() + 250, 1950); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(800 * audio.getVolume() + 250, 1950, 10, 0, 2 * Math.PI); + ctx.fill(); + core.fillBoldText1( + ctx, + Math.floor(100 * audio.getVolume()), + 1120, + 1970, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(56, true) + ); + } else { + //横屏 + + core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景 + ctx.globalAlpha = 0.5; //透明度 + if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片 + ctx.globalAlpha = 1; //恢复为不透明 + core.setTextAlign(ctx, "center"); + + core.fillBoldText1( + ctx, + "◀离开", + 110, + 100, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(800, 100); + ctx.lineTo(800, 1148); + ctx.moveTo(900, 100); + ctx.lineTo(1900, 100); + ctx.stroke(); + let posy = 200; + const indexList = this.musicMx[page]; + core.setTextAlign(ctx, "left"); + for (let i = 0; i < indexList.length; i++) { + const text = this.musicname[indexList[i]]; + core.fillBoldText1( + ctx, + text, + 950, + posy - 30, + page === this.selection[0] && i === this.selection[1] ? + "#FFFFFF" : + "#444444", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(900, posy); + ctx.lineTo(1900, posy); + ctx.stroke(); + posy += 100; + } + core.fillBoldText1( + ctx, + "上一页", + 1050, + 1200 - 30, + page === 0 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + + core.fillBoldText1( + ctx, + page + 1 + "/" + this.musicMx.length, + 1350, + 1200 - 30, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + core.fillBoldText1( + ctx, + "下一页", + 1550, + 1200 - 30, + page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(66, true) + ); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 3; + + ctx.fillStyle = "#ffffff"; + ctx.font = "bold 96px Verdana"; + ctx.fillText("|", 60, 697); + ctx.fillText("◀", 70, 700); + ctx.beginPath(); + ctx.arc(295, 670, 80, 0, 2 * Math.PI); + ctx.stroke(); + if (this.stop) { + ctx.fillText("▶", 245, 697); + } else { + ctx.fillText("||", 245, 694); + } + + ctx.fillText("|", 490, 697); + ctx.fillText("▶", 450, 700); + ctx.font = "bold 48px Verdana"; + ctx.fillText("音量", 350, 900); + ctx.beginPath(); + ctx.moveTo(100, 1000); + ctx.lineTo(700, 1000); + ctx.stroke(); + ctx.strokeStyle = "#ffffff"; + ctx.lineWidth = 9; + ctx.fillStyle = "rgba(255,255,255,0.5)"; + + ctx.beginPath(); + ctx.moveTo(100, 1000); + ctx.lineTo(600 * audio.getVolume() + 100, 1000); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(600 * audio.getVolume() + 100, 1000, 10, 0, 2 * Math.PI); + ctx.fill(); + core.fillBoldText1( + ctx, + Math.floor(100 * audio.getVolume()), + 720, + 1010, + "#FFFFFF", + "#000000", + 6, + core.ui._buildFont(56, true) + ); + const img = core.material.images.images[this.type + ".webp"]; + if (img) ctx.drawImage(img, 580, 560, 200, 200); + core.setTextAlign(ctx, "center"); + ctx.font = "bold 48px Verdana"; + ctx.fillText("当前歌曲", 400, 297); + ctx.fillText( + this.musicname[this.musicMx[this.selection[0]][this.selection[1]]], + 400, + 397 + ); + } + } + } + core.ui.music = new musicclass(); + main.dom.musicMode.onclick = function () { + //点击开始页面的CG MODE进入cg回廊 + + core.playBgm( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); + + const arr = main.core.ui.music.musicMx.flat(Infinity); + main.core.ui.music.randomList = shuffle(arr); + main.core.ui.music.random = main.core.ui.music.randomList.indexOf( + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ); + page = 0; + music.style.display = "block"; + let time = 0; + core.registerAnimationFrame("music", null, (temptime) => { + if (temptime > time + 1000 / 60) { + time = temptime; + main.core.ui.music.update(); + const duration = + core.plugin.audioSystem.bgmController.player.getRoute( + "bgms." + + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ).duration; + + const currentTime = + core.plugin.audioSystem.bgmController.player.getRoute( + "bgms." + + main.core.ui.music.musicMx[main.core.ui.music.selection[0]][ + main.core.ui.music.selection[1] + ] + ).currentTime; + if (currentTime && duration && duration - currentTime < 0.05) { + if (core.domStyle.isVertical) { + core.ui.music.mousedown(830, 1770); + } else { + core.ui.music.mousedown(475, 765); + } + } + } + }); + }; +}, "横屏切换": function () { // 在此增加新插件 this.triggerFullscreen = async function (full) { @@ -15540,11 +17493,13 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), angle = - (Math.PI * (image.angel ?? 0)) / 180 + - ((Math.PI * (image.aangel ?? 0)) / 180 - - ((Math.PI * (image.angel ?? 0)) / 180) * - (one.farme - beforefarme)) / - (afterfarme - beforefarme || 1); + (Math.PI * + ((image.image.angle ?? 0) + + (((image.aangle ?? 0) - (image.image.angle ?? 0)) * + (one.farme - beforefarme)) / + (afterfarme - beforefarme || 1))) / + 180; + if (one.hero) { let sx, sy; if (core.status.heroMoving < 0) { @@ -15609,2062 +17564,5 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }); } }); - }, - "音频系统": function () { - // 在此增加新插件 - /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: - - - - - - - */ - // 将__enable置为false将关闭插件 - let __enable = true; - if (!__enable || main.mode === "editor") return; - const { OggOpusDecoderWebWorker } = window["ogg-opus-decoder"]; - const { OggVorbisDecoderWebWorker } = window["ogg-vorbis-decoder"]; - const { CodecParser } = window.CodecParser; - const { Transition, linear } = core.plugin.animate; - - const audio = new Audio(); - - const supportMap = new Map(); - const AudioType = { - Mp3: "audio/mpeg", - Wav: 'audio/wav; codecs="1"', - Flac: "audio/flac", - Opus: 'audio/ogg; codecs="opus"', - Ogg: 'audio/ogg; codecs="vorbis"', - Aac: "audio/aac", - }; - /** - * 检查一种音频类型是否能被播放 - * @param type 音频类型 AudioType - */ - function isAudioSupport(type) { - if (supportMap.has(type)) return supportMap.get(type); - else { - const support = audio.canPlayType(type); - const canPlay = support === "maybe" || support === "probably"; - supportMap.set(type, canPlay); - return canPlay; - } - } - - const typeMap = new Map([ - ["ogg", AudioType.Ogg], - ["mp3", AudioType.Mp3], - ["wav", AudioType.Wav], - ["flac", AudioType.Flac], - ["opus", AudioType.Opus], - ["aac", AudioType.Aac], - ]); - - /** - * 根据文件名拓展猜测其类型 - * @param file 文件名 string - */ - function guessTypeByExt(file) { - const ext = /\.[a-zA-Z\d]+$/.exec(file); - if (!ext?.[0]) return ""; - const type = ext[0].slice(1); - return typeMap.get(type.toLocaleLowerCase()) ?? ""; - } - - isAudioSupport(AudioType.Ogg); - isAudioSupport(AudioType.Mp3); - isAudioSupport(AudioType.Wav); - isAudioSupport(AudioType.Flac); - isAudioSupport(AudioType.Opus); - isAudioSupport(AudioType.Aac); - - function isNil(value) { - return value === void 0 || value === null; - } - - function sleep(time) { - return new Promise((res) => setTimeout(res, time)); - } - class AudioEffect { - constructor(ac) {} - /** - * 连接至其他效果器 - * @param target 目标输入 IAudioInput - * @param output 当前效果器输出通道 Number - * @param input 目标效果器的输入通道 Number - */ - connect(target, output, input) { - this.output.connect(target.input, output, input); - } - - /** - * 与其他效果器取消连接 - * @param target 目标输入 IAudioInput - * @param output 当前效果器输出通道 Number - * @param input 目标效果器的输入通道 Number - */ - disconnect(target, output, input) { - if (!target) { - if (!isNil(output)) { - this.output.disconnect(output); - } else { - this.output.disconnect(); - } - } else { - if (!isNil(output)) { - if (!isNil(input)) { - this.output.disconnect(target.input, output, input); - } else { - this.output.disconnect(target.input, output); - } - } else { - this.output.disconnect(target.input); - } - } - } - } - - class StereoEffect extends AudioEffect { - constructor(ac) { - super(ac); - const panner = ac.createPanner(); - this.input = panner; - this.output = panner; - } - - /** - * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 朝向x坐标 Number - * @param y 朝向y坐标 Number - * @param z 朝向z坐标 Number - */ - setOrientation(x, y, z) { - this.output.orientationX.value = x; - this.output.orientationY.value = y; - this.output.orientationZ.value = z; - } - /** - * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 位置x坐标 Number - * @param y 位置y坐标 Number - * @param z 位置z坐标 Number - */ - setPosition(x, y, z) { - this.output.positionX.value = x; - this.output.positionY.value = y; - this.output.positionZ.value = z; - } - end() {} - - start() {} - } - class VolumeEffect extends AudioEffect { - constructor(ac) { - super(ac); - const gain = ac.createGain(); - this.input = gain; - this.output = gain; - } - - /** - * 设置音量大小 - * @param volume 音量大小 Number - */ - setVolume(volume) { - this.output.gain.value = volume; - } - - /** - * 获取音量大小 Number - */ - getVolume() { - return this.output.gain.value; - } - - end() {} - - start() {} - } - class ChannelVolumeEffect extends AudioEffect { - /** 所有的音量控制节点 */ - - constructor(ac) { - super(ac); - /** 所有的音量控制节点 */ - this.gain = []; - const splitter = ac.createChannelSplitter(); - const merger = ac.createChannelMerger(); - this.output = merger; - this.input = splitter; - for (let i = 0; i < 6; i++) { - const gain = ac.createGain(); - splitter.connect(gain, i); - gain.connect(merger, 0, i); - this.gain.push(gain); - } - } - - /** - * 设置某个声道的音量大小 - * @param channel 要设置的声道,可填0-5 Number - * @param volume 这个声道的音量大小 Number - */ - setVolume(channel, volume) { - if (!this.gain[channel]) return; - this.gain[channel].gain.value = volume; - } - - /** - * 获取某个声道的音量大小,可填0-5 - * @param channel 要获取的声道 Number - */ - getVolume(channel) { - if (!this.gain[channel]) return 0; - return this.gain[channel].gain.value; - } - - end() {} - - start() {} - } - class DelayEffect extends AudioEffect { - constructor(ac) { - super(ac); - - const delay = ac.createDelay(); - this.input = delay; - this.output = delay; - } - - /** - * 设置延迟时长 - * @param delay 延迟时长,单位秒 Number - */ - setDelay(delay) { - this.output.delayTime.value = delay; - } - - /** - * 获取延迟时长 - */ - getDelay() { - return this.output.delayTime.value; - } - - end() {} - - start() {} - } - class EchoEffect extends AudioEffect { - constructor(ac) { - super(ac); - const delay = ac.createDelay(); - const gain = ac.createGain(); - gain.gain.value = 0.5; - delay.delayTime.value = 0.05; - delay.connect(gain); - gain.connect(delay); - /** 延迟节点 */ - this.delay = delay; - /** 反馈增益节点 */ - this.gainNode = gain; - /** 当前增益 */ - this.gain = 0.5; - /** 是否正在播放 */ - this.playing = false; - this.input = gain; - this.output = gain; - } - - /** - * 设置回声反馈增益大小 - * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number - */ - setFeedbackGain(gain) { - const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; - this.gain = resolved; - if (this.playing) this.gainNode.gain.value = resolved; - } - - /** - * 设置回声间隔时长 - * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number - */ - setEchoDelay(delay) { - const resolved = delay < 0.01 ? 0.01 : delay; - this.delay.delayTime.value = resolved; - } - - /** - * 获取反馈节点增益 - */ - getFeedbackGain() { - return this.gain; - } - - /** - * 获取回声间隔时长 - */ - getEchoDelay() { - return this.delay.delayTime.value; - } - - end() { - this.playing = false; - const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; - sleep(this.delay.delayTime.value * echoTime).then(() => { - if (!this.playing) this.gainNode.gain.value = 0; - }); - } - - start() { - this.playing = true; - this.gainNode.gain.value = this.gain; - } - } - - class StreamLoader { - constructor(url) { - /** 传输目标 Set*/ - this.target = new Set(); - this.loading = false; - } - - /** - * 将加载流传递给字节流读取对象 - * @param reader 字节流读取对象 IStreamReader - */ - pipe(reader) { - if (this.loading) { - console.warn( - "Cannot pipe new StreamReader object when stream is loading." - ); - return; - } - this.target.add(reader); - reader.piped(this); - return this; - } - - async start() { - if (this.loading) return; - this.loading = true; - const response = await window.fetch(this.url); - const stream = response.body; - if (!stream) { - console.error("Cannot get reader when fetching '" + this.url + "'."); - return; - } - // 获取读取器 - /** 读取流对象 */ - this.stream = stream; - const reader = response.body?.getReader(); - const targets = [...this.target]; - - - await Promise.all(targets.map((v) => v.start(stream, this, response))); - if (reader && reader.read) { - // 开始流传输 - while (true) { - const { value, done } = await reader.read(); - await Promise.all( - targets.map(v => v.pump(value, done, response)) - ); - if (done) break; - } - } else { - // 如果不支持流传输 - const buffer = await response.arrayBuffer(); - const data = new Uint8Array(buffer); - await Promise.all(targets.map(v => v.pump(data, true, response))); - } - // 开始流传输 - while (true) { - const { value, done } = await reader.read(); - await Promise.all(targets.map((v) => v.pump(value, done, response))); - if (done) break; - } - - this.loading = false; - targets.forEach((v) => v.end(true)); - - // - } - - cancel(reason) { - if (!this.stream) return; - this.stream.cancel(reason); - this.loading = false; - this.target.forEach((v) => v.end(false, reason)); - } - } - const fileSignatures = [ - [AudioType.Mp3, [0x49, 0x44, 0x33]], - [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], - [AudioType.Wav, [52, 0x49, 0x46, 0x46]], - [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], - [AudioType.Aac, [0xff, 0xf1]], - [AudioType.Aac, [0xff, 0xf9]] - ]; - const oggHeaders = [ - [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]] - ]; - - function checkAudioType(data) { - let audioType = ''; - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - for (const [type, value] of fileSignatures) { - if (value.every((v, i) => toCheck[i] === v)) { - audioType = type; - break; - } - } - if (audioType === AudioType.Ogg) { - // 如果是ogg的话,进一步判断是不是opus - for (const [key, value] of oggHeaders) { - const has = toCheck.some((_, i) => { - return value.every((v, ii) => toCheck[i + ii] === v); - }); - if (has) { - audioType = key; - break; - } - } - } - - return audioType; - } - - const mimeTypeMap = { - [AudioType.Aac]: "audio/aac", - [AudioType.Flac]: "audio/flac", - [AudioType.Mp3]: "audio/mpeg", - [AudioType.Ogg]: "application/ogg", - [AudioType.Opus]: "application/ogg", - [AudioType.Wav]: "application/ogg", - }; - - function isOggPage(data) { - return !isNil(data.isFirstPage); - } - class AudioStreamSource { - constructor(context) { - this.output = context.createBufferSource(); - /** 是否已经完全加载完毕 */ - this.loaded = false; - - /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ - this.buffered = 0; - /** 已经缓冲的采样点数量 */ - this.bufferedSamples = 0; - /** 歌曲时长,加载完毕之前保持为 0 */ - this.duration = 0; - /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ - this.bufferPlayDuration = 1; - /** 音频的采样率,未成功解析出之前保持为 0 */ - this.sampleRate = 0; - //是否循环播放 - this.loop = false; - /** 上一次播放是从何时开始的 */ - this.lastStartWhen = 0; - /** 开始播放时刻 */ - this.lastStartTime = 0; - /** 上一次播放的缓存长度 */ - this.lastBufferSamples = 0; - - /** 是否已经获取到头文件 */ - this.headerRecieved = false; - /** 音频类型 */ - this.audioType = ""; - /** 每多长时间组成一个缓存 Float32Array */ - this.bufferChunkSize = 10; - /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ - this.audioData = []; - - this.errored = false; - this.ac = context; - } - /** 当前已经播放了多长时间 */ - get currentTime() { - return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; - } - /** - * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 - * @param size 每个缓存数据的时长,单位秒 - */ - setChunkSize(size) { - if (this.controller?.loading || this.loaded) return; - this.bufferChunkSize = size; - } - - piped(controller) { - this.controller = controller; - } - - async pump(data, done) { - if (!data || this.errored) return; - if (!this.headerRecieved) { - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - this.audioType = checkAudioType(data); - if (!this.audioType) { - console.error( - "Unknown audio type. Header: '" + [...toCheck] - .map((v) => v.toString().padStart(2, "0")) - .join(" ") - .toUpperCase() + - "'" - ); - return; - } - // 创建解码器 - const Decoder = AudioDecoder.decoderMap.get(this.audioType); - if (!Decoder) { - this.errored = true; - console.error( - "Cannot decode stream source type of '" + - this.audioType + - "', since there is no registered decoder for that type." - ); - return Promise.reject( - `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` - ); - } - this.decoder = new Decoder(); - // 创建数据解析器 - const mime = mimeTypeMap[this.audioType]; - const parser = new CodecParser(mime); - this.parser = parser; - await this.decoder.create(); - this.headerRecieved = true; - } - - const decoder = this.decoder; - const parser = this.parser; - if (!decoder || !parser) { - this.errored = true; - return Promise.reject( - "No parser or decoder attached in this AudioStreamSource" - ); - } - - await this.decodeData(data, decoder, parser); - if (done) await this.decodeFlushData(decoder, parser); - this.checkBufferedPlay(); - } - - /** - * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 - */ - checkSampleRate(info) { - for (const one of info) { - const frame = isOggPage(one) ? one.codecFrames[0] : one; - if (frame) { - const rate = frame.header.sampleRate; - if (this.sampleRate === 0) { - this.sampleRate = rate; - break; - } else { - if (rate !== this.sampleRate) { - console.warn("Sample rate in stream audio must be constant."); - } - } - } - } - } - - /** - * 解析音频数据 - */ - async decodeData(data, decoder, parser) { - // 解析音频数据 - const audioData = await decoder.decode(data); - if (!audioData) return; - // @ts-expect-error 库类型声明错误 - const audioInfo = [...parser.parseChunk(data)]; - - // 检查采样率 - this.checkSampleRate(audioInfo); - // 追加音频数据 - this.appendDecodedData(audioData, audioInfo); - } - - /** - * 解码剩余数据 - */ - async decodeFlushData(decoder, parser) { - const audioData = await decoder.flush(); - if (!audioData) return; - // @ts-expect-error 库类型声明错误 - const audioInfo = [...parser.flush()]; - - this.checkSampleRate(audioInfo); - this.appendDecodedData(audioData, audioInfo); - } - - /** - * 追加音频数据 - */ - appendDecodedData(data, info) { - const channels = data.channelData.length; - if (channels === 0) return; - if (this.audioData.length !== channels) { - this.audioData = []; - for (let i = 0; i < channels; i++) { - this.audioData.push([]); - } - } - // 计算出应该放在哪 - const chunk = this.sampleRate * this.bufferChunkSize; - const sampled = this.bufferedSamples; - const pushIndex = Math.floor(sampled / chunk); - const bufferIndex = sampled % chunk; - const dataLength = data.channelData[0].length; - let buffered = 0; - let nowIndex = pushIndex; - let toBuffer = bufferIndex; - while (buffered < dataLength) { - const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; - - for (let i = 0; i < channels; i++) { - const audioData = this.audioData[i]; - if (!audioData[nowIndex]) { - audioData.push(new Float32Array(chunk)); - } - const toPush = data.channelData[i].slice(buffered, buffered + rest); - - audioData[nowIndex].set(toPush, toBuffer); - } - buffered += rest; - nowIndex++; - toBuffer = 0; - } - - this.buffered += - info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; - this.bufferedSamples += info.reduce( - (prev, curr) => prev + curr.samples, - 0 - ); - } - - /** - * 检查已缓冲内容,并在未开始播放时播放 - */ - checkBufferedPlay() { - if (this.playing || this.sampleRate === 0) return; - const played = this.lastBufferSamples / this.sampleRate; - const dt = this.buffered - played; - if (this.loaded) { - this.playAudio(played); - return; - } - if (dt < this.bufferPlayDuration) return; - - this.lastBufferSamples = this.bufferedSamples; - // 需要播放 - this.mergeBuffers(); - if (!this.buffer) return; - if (this.playing) this.output.stop(); - this.createSourceNode(this.buffer); - this.output.loop = false; - this.output.start(0, played); - this.lastStartTime = this.ac.currentTime; - this.playing = true; - this.output.addEventListener("ended", () => { - this.playing = false; - this.checkBufferedPlay(); - }); - } - - mergeBuffers() { - const buffer = this.ac.createBuffer( - this.audioData.length, - this.bufferedSamples, - this.sampleRate - ); - const chunk = this.sampleRate * this.bufferChunkSize; - const bufferedChunks = Math.floor(this.bufferedSamples / chunk); - const restLength = this.bufferedSamples % chunk; - for (let i = 0; i < this.audioData.length; i++) { - const audio = this.audioData[i]; - const data = new Float32Array(this.bufferedSamples); - for (let j = 0; j < bufferedChunks; j++) { - data.set(audio[j], chunk * j); - } - if (restLength !== 0) { - data.set( - audio[bufferedChunks].slice(0, restLength), - chunk * bufferedChunks - ); - } - - buffer.copyToChannel(data, i, 0); - } - this.buffer = buffer; - } - - async start() { - delete this.buffer; - this.headerRecieved = false; - this.audioType = ""; - this.errored = false; - this.buffered = 0; - this.sampleRate = 0; - this.bufferedSamples = 0; - this.duration = 0; - this.loaded = false; - if (this.playing) this.output.stop(); - this.playing = false; - this.lastStartTime = this.ac.currentTime; - } - - end(done, reason) { - if (done && this.buffer) { - this.loaded = true; - delete this.controller; - this.mergeBuffers(); - - this.duration = this.buffered; - this.audioData = []; - this.decoder?.destroy(); - delete this.decoder; - delete this.parser; - } else { - console.warn( - "Unexpected end when loading stream audio, reason: '" + - (reason ?? "") + - "'" - ); - } - } - - playAudio(when) { - if (!this.buffer) return; - this.lastStartTime = this.ac.currentTime; - if (this.playing) this.output.stop(); - - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.playing = true; - - - this.output.addEventListener("ended", () => { - this.playing = false; - - if (this.loop && !this.output.loop) this.play(0); - }); - } - /** - * 开始播放这个音频源 - */ - play(when) { - if (this.playing || this.errored) return; - if (this.loaded && this.buffer) { - this.playing = true; - this.playAudio(when); - } else { - this.controller?.start(); - } - } - - createSourceNode(buffer) { - if (!this.target) return; - const node = this.ac.createBufferSource(); - node.buffer = buffer; - if (this.playing) this.output.stop(); - this.playing = false; - this.output = node; - node.connect(this.target.input); - node.loop = this.loop; - } - /** - * 停止播放这个音频源 - * @returns 音频暂停的时刻 number - */ - stop() { - if (this.playing) this.output.stop(); - this.playing = false; - return this.ac.currentTime - this.lastStartTime; - } - /** - * 连接到音频路由图上,每次调用播放的时候都会执行一次 - * @param target 连接至的目标 IAudioInput - */ - connect(target) { - this.target = target; - } - /** - * 设置是否循环播放 - * @param loop 是否循环 boolean) - */ - setLoop(loop) { - this.loop = loop; - } - } - class AudioElementSource { - constructor(context) { - const audio = new Audio(); - audio.preload = "none"; - this.output = context.createMediaElementSource(audio); - this.audio = audio; - audio.addEventListener("play", () => { - this.playing = true; - }); - audio.addEventListener("ended", () => { - this.playing = false; - this.ac = context; - }); - } - get duration() { - return this.audio.duration; - } - get currentTime() { - return this.audio.currentTime; - } - /** - * 设置音频源的路径 - * @param url 音频路径 - */ - setSource(url) { - this.audio.src = url; - } - - play(when = 0) { - if (this.playing) return; - this.audio.currentTime = when; - this.audio.play(); - } - - stop() { - this.audio.pause(); - this.playing = false; - - return this.audio.currentTime; - } - - connect(target) { - this.output.connect(target.input); - } - - setLoop(loop) { - this.audio.loop = loop; - } - } - class AudioBufferSource { - constructor(context) { - this.output = context.createBufferSource(); - /** 是否循环 */ - this.loop = false; - /** 上一次播放是从何时开始的 */ - this.lastStartWhen = 0; - /** 播放开始时刻 */ - this.lastStartTime = 0; - this.duration = 0; - this.ac = context; - } - get currentTime() { - return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; - } - - /** - * 设置音频源数据 - * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer - */ - async setBuffer(buffer) { - if (buffer instanceof ArrayBuffer) { - this.buffer = await this.ac.decodeAudioData(buffer); - } else { - this.buffer = buffer; - } - this.duration = this.buffer.duration; - } - - play(when) { - if (this.playing || !this.buffer) return; - this.playing = true; - this.lastStartTime = this.ac.currentTime; - - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.output.addEventListener("ended", () => { - this.playing = false; - if (this.loop && !this.output.loop) this.play(0); - }); - } - - createSourceNode(buffer) { - if (!this.target) return; - const node = this.ac.createBufferSource(); - node.buffer = buffer; - this.output = node; - node.connect(this.target.input); - node.loop = this.loop; - } - - stop() { - this.output.stop(); - return this.ac.currentTime - this.lastStartTime; - } - - connect(target) { - this.target = target; - } - - setLoop(loop) { - this.loop = loop; - } - } - class AudioPlayer { - constructor() { - /** 音频播放上下文 */ - this.ac = new AudioContext(); - /** 音量节点 */ - this.gain = this.ac.createGain(); - this.gain.connect(this.ac.destination); - this.audioRoutes = new Map(); - } - /** - * 解码音频数据 - * @param data 音频数据 - */ - decodeAudioData(data) { - return AudioDecoder.decodeAudioData(data, this); - } - /** - * 设置音量 - * @param volume 音量 - */ - setVolume(volume) { - this.gain.gain.value = volume; - } - - /** - * 获取音量 - */ - getVolume() { - return this.gain.gain.value; - } - - /** - * 创建一个音频源 - * @param Source 音频源类 - */ - createSource(Source) { - return new Source(this.ac); - } - - /** - * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 - */ - createStreamSource() { - return new AudioStreamSource(this.ac); - } - - /** - * 创建一个通过 audio 元素播放的音频源 - */ - createElementSource() { - return new AudioElementSource(this.ac); - } - - /** - * 创建一个通过 AudioBuffer 播放的音频源 - */ - createBufferSource() { - return new AudioBufferSource(this.ac); - } - - /** - * 获取音频目的地 - */ - getDestination() { - return this.gain; - } - - /** - * 创建一个音频效果器 - * @param Effect 效果器类 - */ - createEffect(Effect) { - return new Effect(this.ac); - } - - /** - * 创建一个修改音量的效果器 - * ```txt - * |----------| - * Input ----> | GainNode | ----> Output - * |----------| - * ``` - */ - createVolumeEffect() { - return new VolumeEffect(this.ac); - } - - /** - * 创建一个立体声效果器 - * ```txt - * |------------| - * Input ----> | PannerNode | ----> Output - * |------------| - * ``` - */ - createStereoEffect() { - return new StereoEffect(this.ac); - } - - /** - * 创建一个修改单个声道音量的效果器 - * ```txt - * |----------| - * -> | GainNode | \ - * |--------------| / |----------| -> |------------| - * Input ----> | SplitterNode | ...... | MergerNode | ----> Output - * |--------------| \ |----------| -> |------------| - * -> | GainNode | / - * |----------| - * ``` - */ - createChannelVolumeEffect() { - return new ChannelVolumeEffect(this.ac); - } - - /** - * 创建一个延迟效果器 - * |-----------| - * Input ----> | DelayNode | ----> Output - * |-----------| - */ - createDelay() { - return new DelayEffect(this.ac); - } - - /** - * 创建一个回声效果器 - * ```txt - * |----------| - * Input ----> | GainNode | ----> Output - * ^ |----------| | - * | | - * | |------------| ↓ - * |-- | Delay Node | <-- - * |------------| - * ``` - */ - createEchoEffect() { - return new EchoEffect(this.ac); - } - - /** - * 创建一个音频播放路由 - * @param source 音频源 - */ - createRoute(source) { - return new AudioRoute(source, this); - } - - /** - * 添加一个音频播放路由,可以直接被播放 - * @param id 这个音频播放路由的名称 - * @param route 音频播放路由对象 - */ - addRoute(id, route) { - if (!this.audioRoutes) this.audioRoutes = new Map(); - if (this.audioRoutes.has(id)) { - console.warn( - "Audio route with id of '" + - id + - "' has already existed. New route will override old route." - ); - } - this.audioRoutes.set(id, route); - } - - /** - * 根据名称获取音频播放路由对象 - * @param id 音频播放路由的名称 - */ - getRoute(id) { - return this.audioRoutes.get(id); - } - /** - * 移除一个音频播放路由 - * @param id 要移除的播放路由的名称 - */ - removeRoute(id) { - this.audioRoutes.delete(id); - } - /** - * 播放音频 - * @param id 音频名称 - * @param when 从音频的哪个位置开始播放,单位秒 - */ - play(id, when) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot play audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - route.play(when); - } - - /** - * 暂停音频播放 - * @param id 音频名称 - * @returns 当音乐真正停止时兑现 - */ - pause(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot pause audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - return route.pause(); - } - - /** - * 停止音频播放 - * @param id 音频名称 - * @returns 当音乐真正停止时兑现 - */ - stop(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot stop audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - return route.stop(); - } - - /** - * 继续音频播放 - * @param id 音频名称 - */ - resume(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot pause audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - route.resume(); - } - - /** - * 设置听者位置,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 位置x坐标 - * @param y 位置y坐标 - * @param z 位置z坐标 - */ - setListenerPosition(x, y, z) { - const listener = this.ac.listener; - listener.positionX.value = x; - listener.positionY.value = y; - listener.positionZ.value = z; - } - - /** - * 设置听者朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 朝向x坐标 - * @param y 朝向y坐标 - * @param z 朝向z坐标 - */ - setListenerOrientation(x, y, z) { - const listener = this.ac.listener; - listener.forwardX.value = x; - listener.forwardY.value = y; - listener.forwardZ.value = z; - } - - /** - * 设置听者头顶朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 头顶朝向x坐标 - * @param y 头顶朝向y坐标 - * @param z 头顶朝向z坐标 - */ - setListenerUp(x, y, z) { - const listener = this.ac.listener; - listener.upX.value = x; - listener.upY.value = y; - listener.upZ.value = z; - } - } - const AudioStatus = { - Playing: 0, - Pausing: 1, - Paused: 2, - Stoping: 3, - Stoped: 4, - }; - const AudioRouteEvent = { - updateEffect: [], - play: [], - stop: [], - pause: [], - resume: [], - }; - class AudioRoute { - constructor(source, player) { - this.output = source.output; - /** 效果器路由图 */ - this.effectRoute = []; - - /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ - this.endTime = 0; - /** 暂停时播放了多长时间 */ - this.pauseCurrentTime = 0; - /** 当前播放状态 */ - this.status = AudioStatus.Stoped; - this.shouldStop = false; - /** - * 每次暂停或停止时自增,用于判断当前正在处理的情况。 - * 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作 - */ - this.stopIdentifier = 0; - /** 暂停时刻 */ - this.pauseTime = 0; - this.source = source; - this.player = player; - } - /** 音频时长,单位秒 */ - get duration() { - return this.source.duration; - } - /** 当前播放了多长时间,单位秒 */ - get currentTime() { - if (this.status === AudioStatus.Paused) { - return this.pauseCurrentTime; - } else { - return this.source.currentTime; - } - } - set currentTime(time) { - this.source.stop(); - this.source.play(time); - } - /** - * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 - * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 - */ - setEndTime(time) { - this.endTime = time; - } - - /** - * 当音频播放时执行的函数,可以用于音频淡入效果 - * @param fn 音频开始播放时执行的函数 - */ - onStart(fn) { - this.audioStartHook = fn; - } - - /** - * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 - * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 - * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 - */ - onEnd(fn) { - this.audioEndHook = fn; - } - - /** - * 开始播放这个音频 - * @param when 从音频的什么时候开始播放,单位秒 - */ - play(when = 0) { - 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.status = AudioStatus.Playing; - this.pauseTime = 0; - this.audioStartHook?.(this); - this.startAllEffect(); - } - - /** - * 暂停音频播放 - */ - async pause() { - 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; - } - this.pauseCurrentTime = this.source.currentTime; - const time = this.source.stop(); - this.pauseTime = time; - if (this.shouldStop) { - this.status = AudioStatus.Stoped; - this.endAllEffect(); - - this.shouldStop = false; - } else { - this.status = AudioStatus.Paused; - this.endAllEffect(); - } - this.endAllEffect(); - } - - /** - * 继续音频播放 - */ - resume() { - if (this.status === AudioStatus.Playing) return; - if ( - this.status === AudioStatus.Pausing || - this.status === AudioStatus.Stoping - ) { - - this.audioStartHook?.(this); - - return; - } - if (this.status === AudioStatus.Paused) { - this.play(this.pauseTime); - } else { - this.play(0); - } - this.status = AudioStatus.Playing; - this.pauseTime = 0; - this.audioStartHook?.(this); - this.startAllEffect(); - } - - /** - * 停止音频播放 - */ - async stop() { - 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.status = AudioStatus.Stoped; - this.pauseTime = 0; - this.endAllEffect(); - } - - /** - * 添加效果器 - * @param effect 要添加的效果,可以是数组,表示一次添加多个 - * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 - */ - addEffect(effect, index) { - if (isNil(index)) { - if (effect instanceof Array) { - this.effectRoute.push(...effect); - } else { - this.effectRoute.push(effect); - } - } else { - if (effect instanceof Array) { - this.effectRoute.splice(index, 0, ...effect); - } else { - this.effectRoute.splice(index, 0, effect); - } - } - this.setOutput(); - if (this.source.playing) this.link(); - } - - /** - * 移除一个效果器 - * @param effect 要移除的效果 - */ - removeEffect(effect) { - const index = this.effectRoute.indexOf(effect); - if (index === -1) return; - this.effectRoute.splice(index, 1); - effect.disconnect(); - this.setOutput(); - if (this.source.playing) this.link(); - } - - setOutput() { - const effect = this.effectRoute.at(-1); - if (!effect) this.output = this.source.output; - else this.output = effect.output; - } - - /** - * 连接音频路由图 - */ - link() { - this.effectRoute.forEach((v) => v.disconnect()); - this.effectRoute.forEach((v, i) => { - const next = this.effectRoute[i + 1]; - if (next) { - v.connect(next); - } - }); - } - - startAllEffect() { - this.effectRoute.forEach((v) => v.start()); - } - - endAllEffect() { - this.effectRoute.forEach((v) => v.end()); - } - } - - - const audioPlayer = new AudioPlayer() - - class AudioDecoder { - /** - * 注册一个解码器 - * @param type 要注册的解码器允许解码的类型 - * @param decoder 解码器对象 - */ - static registerDecoder(type, decoder) { - if (!this.decoderMap) this.decoderMap = new Map(); - if (this.decoderMap.has(type)) { - console.warn( - "Audio stream decoder for audio type '" + - type + - "' has already existed." - ); - return; - } - - this.decoderMap.set(type, decoder); - } - - /** - * 解码音频数据 - * @param data 音频文件数据 - * @param player AudioPlayer实例 - */ - static async decodeAudioData(data, player) { - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - const type = checkAudioType(data); - if (type === "") { - console.error( - "Unknown audio type. Header: '" + [...toCheck] - .map((v) => v.toString().padStart(2, "0")) - .join(" ") - .toUpperCase() + - "'" - ); - return null; - } - if (isAudioSupport(type)) { - if (data.buffer instanceof ArrayBuffer) { - return player.ac.decodeAudioData(data.buffer); - } else { - return null; - } - } else { - const Decoder = this.decoderMap.get(type); - if (!Decoder) { - return null; - } else { - const decoder = new Decoder(); - await decoder.create(); - const decodedData = await decoder.decode(data); - if (!decodedData) return null; - const buffer = player.ac.createBuffer( - decodedData.channelData.length, - decodedData.channelData[0].length, - decodedData.sampleRate - ); - decodedData.channelData.forEach((v, i) => { - buffer.copyToChannel(v, i); - }); - return buffer; - } - } - } - } - - class VorbisDecoder { - /** - * 创建音频解码器 - */ - async create() { - this.decoder = new OggVorbisDecoderWebWorker(); - await this.decoder.ready; - } - /** - * 摧毁这个解码器 - */ - destroy() { - this.decoder?.free(); - } - /** - * 解码流数据 - * @param data 流数据 - */ - - async decode(data) { - return this.decoder?.decode(data); - } - async decodeAll(data) { - return this.decoder?.decodeFile(data); - } - /** - * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 - */ - async flush() { - return this.decoder?.flush(); - } - } - class OpusDecoder { - /** - * 创建音频解码器 - */ - async create() { - this.decoder = new OggOpusDecoderWebWorker(); - await this.decoder.ready; - } - /** - * 摧毁这个解码器 - */ - destroy() { - this.decoder?.free(); - } - /** - * 解码流数据 - * @param data 流数据 - */ - async decode(data) { - return this.decoder?.decode(data); - } - - async decodeAll(data) { - return this.decoder?.decodeFile(data); - } - /** - * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 - */ - async flush() { - return await this.decoder?.flush(); - } - } - class BgmController { - constructor(player) { - this.mainGain = player.createVolumeEffect(); - this.player = player; - /** bgm音频名称的前缀 */ - this.prefix = "bgms."; - /** 每个 bgm 的音量控制器 */ - this.gain = new Map(); - - /** 正在播放的 bgm */ - this.playingBgm = ""; - /** 是否正在播放 */ - this.playing = false; - - /** 是否已经启用 */ - this.enabled = true; - /** 是否屏蔽所有的音乐切换 */ - this.blocking = false; - /** 渐变时长 */ - this.transitionTime = 2000; - } - - /** - * 设置音频渐变时长 - * @param time 渐变时长 - */ - setTransitionTime(time) { - 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) { - this.mainGain.setVolume(volume); - } - - /** - * 设置是否启用 - * @param enabled 是否启用 - */ - setEnabled(enabled) { - if (enabled) this.resume(); - else this.stop(); - this.enabled = enabled; - } - - /** - * 设置 bgm 音频名称的前缀 - */ - setPrefix(prefix) { - this.prefix = prefix; - } - - getId(name) { - return `${this.prefix}${name}`; - } - - /** - * 根据 bgm 名称获取其 AudioRoute 实例 - * @param id 音频名称 - */ - get(id) { - return this.player.getRoute(this.getId(id)); - } - - /** - * 添加一个 bgm - * @param id 要添加的 bgm 的名称 - * @param url 指定 bgm 的加载地址 - */ - addBgm(id, url = `project/bgms/${id}`) { - const type = guessTypeByExt(id); - if (!type) { - console.warn( - "Unknown audio extension name: '" + - 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) { - this.player.removeRoute(this.getId(id)); - const gain = this.gain.get(id); - gain?.transition.ticker.destroy(); - this.gain.delete(id); - } - - setTransition(id, route, gain) { - 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) => { - 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, when) { - 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; - } - } - const bgmController = new BgmController(audioPlayer); - - - class SoundPlayer { - constructor(player) { - /** 每个音效的唯一标识符 */ - this.num = 0; - this.enabled = true; - this.gain = player.createVolumeEffect(); - /** 每个音效的数据 */ - this.buffer = new Map(); - /** 所有正在播放的音乐 */ - this.playing = new Set(); - this.player = player; - } - /** - * 设置是否启用音效 - * @param enabled 是否启用音效 - */ - setEnabled(enabled) { - if (!enabled) this.stopAllSounds(); - this.enabled = enabled; - } - - /** - * 设置音量大小 - * @param volume 音量大小 - */ - setVolume(volume) { - this.gain.setVolume(volume); - } - /** - * 添加一个音效 - * @param id 音效名称 - * @param data 音效的Uint8Array数据 - */ - async add(id, data) { - const buffer = await this.player.decodeAudioData(data); - if (!buffer) { - console.warn( - "Cannot decode sound '" + - id + - "', since audio file may not supported by 2.b." - ); - return; - } - this.buffer.set(id, buffer); - } - - /** - * 播放一个音效 - * @param id 音效名称 - * @param position 音频位置,[0, 0, 0]表示正中心,x轴指向水平向右,y轴指向水平向上,z轴指向竖直向上 - * @param orientation 音频朝向,[0, 1, 0]表示朝向前方 - */ - play(id, position = [0, 0, 0], orientation = [1, 0, 0]) { - if (!this.enabled) return -1; - const buffer = this.buffer.get(id); - if (!buffer) { - console.warn( - "Cannot play sound '" + - id + - "', since there is no added data named it." - ); - return -1; - } - const soundNum = this.num++; - - const source = this.player.createBufferSource(); - source.setBuffer(buffer); - const route = this.player.createRoute(source); - const stereo = this.player.createStereoEffect(); - stereo.setPosition(position[0], position[1], position[2]); - stereo.setOrientation(orientation[0], orientation[1], orientation[2]); - route.addEffect([stereo, this.gain]); - this.player.addRoute(`sounds.${soundNum}`, route); - route.play(); - source.output.addEventListener("ended", () => { - this.playing.delete(soundNum); - }); - this.playing.add(soundNum); - return soundNum; - } - - /** - * 停止一个音效 - * @param num 音效的唯一 id - */ - stop(num) { - const id = `sounds.${num}`; - const route = this.player.getRoute(id); - if (route) { - route.stop(); - this.player.removeRoute(id); - this.playing.delete(num); - } - } - - /** - * 停止播放所有音效 - */ - stopAllSounds() { - this.playing.forEach((v) => { - const id = `sounds.${v}`; - const route = this.player.getRoute(id); - if (route) { - route.stop(); - this.player.removeRoute(id); - } - }); - this.playing.clear(); - } - } - const soundPlayer = new SoundPlayer(audioPlayer); - - - - function loadAllBgm() { - const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; - for (const bgm of data.main.bgms) { - bgmController.addBgm(bgm); - } - - } - loadAllBgm(); - AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder); - AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder); - - core.plugin.audioSystem = { - AudioType, - AudioDecoder, - AudioStatus, - checkAudioType, - isAudioSupport, - audioPlayer, - soundPlayer, - bgmController, - guessTypeByExt, - BgmController, - SoundPlayer, - EchoEffect, - DelayEffect, - ChannelVolumeEffect, - VolumeEffect, - StereoEffect, - AudioEffect, - AudioPlayer, - AudioRoute, - AudioStreamSource, - AudioElementSource, - AudioBufferSource, - loadAllBgm, - StreamLoader, - }; - //bgm相关复写 - control.prototype.playBgm = (bgm, when) => { - bgmController.play(bgm, when); - core.setMusicBtn(); - }; - control.prototype.pauseBgm = () => { - bgmController.pause(); - core.setMusicBtn(); - }; - - control.prototype.resumeBgm = function () { - bgmController.resume(); - core.setMusicBtn(); - }; - control.prototype.checkBgm = function () { - if (bgmController.playing) return; - if (core.musicStatus.bgmStatus) { - if (bgmController.playingBgm) { - bgmController.play(bgmController.playingBgm); - } else { - play(main.startBgm, 0); - } - } else { - pause(); - } - }; - control.prototype.triggerBgm = function () { - - - core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; - if (bgmController.playing) bgmController.pause(); - else bgmController.resume(); - core.setMusicBtn(); - core.setLocalStorage('bgmStatus', core.musicStatus.bgmStatus); - }; - //sound相关复写 - control.prototype.playSound = function ( - sound, - _pitch, - callback, - position, - orientation - ) { - if (main.mode != 'play' || !core.musicStatus.soundStatus) return - const name = core.getMappedName(sound); - const num = soundPlayer.play(name, position, orientation); - const route = audioPlayer.getRoute(`sounds.${num}`); - if (!route) { - callback?.(); - return -1; - } else { - sleep(route.duration).then(() => callback?.()); - return num; - } - }; - control.prototype.stopSound = function (id) { - if (isNil(id)) { - soundPlayer.stopAllSounds(); - } else { - soundPlayer.stop(id); - } - }; - control.prototype.getPlayingSounds = function () { - return [...soundPlayer.playing]; - }; - //sound加载复写 - loader.prototype._loadOneSound_decodeData = function (name, data) { - if (data instanceof Blob) { - var blobReader = new zip.BlobReader(data); - blobReader.init(function () { - blobReader.readUint8Array(0, blobReader.size, function (uint8) { - //core.loader._loadOneSound_decodeData(name, uint8.buffer); - soundPlayer.add(name, uint8) - }) - }); - return; - } - if (data instanceof ArrayBuffer) { - const uint8 = new Uint8Array(data) - soundPlayer.add(name, uint8) - } - } - //音量控制复写 - soundPlayer.setVolume(core.musicStatus.userVolume * core.musicStatus.designVolume) - bgmController.setVolume(core.musicStatus.userVolume * core.musicStatus.designVolume) - actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { - var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); - if (value == 0 && delta < 0) return; - core.musicStatus.userVolume = core.clamp(Math.pow(value + delta, 2) / 100, 0, 1); - //audioContext 音效 不受designVolume 影响 - if (core.musicStatus.gainNode != null) core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; - soundPlayer.setVolume(core.musicStatus.userVolume * core.musicStatus.designVolume) - bgmController.setVolume(core.musicStatus.userVolume * core.musicStatus.designVolume) - core.setLocalStorage('userVolume', core.musicStatus.userVolume); - core.playSound('确定'); - core.ui._drawSwitchs_sounds(); - } -} + } } \ No newline at end of file diff --git a/project/sounds/aiy010000010.mp3 b/project/sounds/aiy010000010.mp3 deleted file mode 100644 index 4389b53..0000000 Binary files a/project/sounds/aiy010000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy010000010.opus b/project/sounds/aiy010000010.opus new file mode 100644 index 0000000..0574c82 Binary files /dev/null and b/project/sounds/aiy010000010.opus differ diff --git a/project/sounds/aiy010000020.mp3 b/project/sounds/aiy010000020.mp3 deleted file mode 100644 index 77111c6..0000000 Binary files a/project/sounds/aiy010000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy010000020.opus b/project/sounds/aiy010000020.opus new file mode 100644 index 0000000..a5768d9 Binary files /dev/null and b/project/sounds/aiy010000020.opus differ diff --git a/project/sounds/aiy010000030.mp3 b/project/sounds/aiy010000030.mp3 deleted file mode 100644 index c2b47e7..0000000 Binary files a/project/sounds/aiy010000030.mp3 and /dev/null differ diff --git a/project/sounds/aiy010000030.opus b/project/sounds/aiy010000030.opus new file mode 100644 index 0000000..450cf99 Binary files /dev/null and b/project/sounds/aiy010000030.opus differ diff --git a/project/sounds/aiy020000005.mp3 b/project/sounds/aiy020000005.mp3 deleted file mode 100644 index c564818..0000000 Binary files a/project/sounds/aiy020000005.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000005.opus b/project/sounds/aiy020000005.opus new file mode 100644 index 0000000..5e05539 Binary files /dev/null and b/project/sounds/aiy020000005.opus differ diff --git a/project/sounds/aiy020000010.mp3 b/project/sounds/aiy020000010.mp3 deleted file mode 100644 index 850eefe..0000000 Binary files a/project/sounds/aiy020000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000010.opus b/project/sounds/aiy020000010.opus new file mode 100644 index 0000000..257f445 Binary files /dev/null and b/project/sounds/aiy020000010.opus differ diff --git a/project/sounds/aiy020000020.mp3 b/project/sounds/aiy020000020.mp3 deleted file mode 100644 index 0290dac..0000000 Binary files a/project/sounds/aiy020000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000020.opus b/project/sounds/aiy020000020.opus new file mode 100644 index 0000000..4ef0df5 Binary files /dev/null and b/project/sounds/aiy020000020.opus differ diff --git a/project/sounds/aiy020000030.mp3 b/project/sounds/aiy020000030.mp3 deleted file mode 100644 index a907ca7..0000000 Binary files a/project/sounds/aiy020000030.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000030.opus b/project/sounds/aiy020000030.opus new file mode 100644 index 0000000..680d345 Binary files /dev/null and b/project/sounds/aiy020000030.opus differ diff --git a/project/sounds/aiy020000040.mp3 b/project/sounds/aiy020000040.mp3 deleted file mode 100644 index 0b2b894..0000000 Binary files a/project/sounds/aiy020000040.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000040.opus b/project/sounds/aiy020000040.opus new file mode 100644 index 0000000..7d8b8dd Binary files /dev/null and b/project/sounds/aiy020000040.opus differ diff --git a/project/sounds/aiy020000050.mp3 b/project/sounds/aiy020000050.mp3 deleted file mode 100644 index dc53397..0000000 Binary files a/project/sounds/aiy020000050.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000050.opus b/project/sounds/aiy020000050.opus new file mode 100644 index 0000000..54e571a Binary files /dev/null and b/project/sounds/aiy020000050.opus differ diff --git a/project/sounds/aiy020000060.mp3 b/project/sounds/aiy020000060.mp3 deleted file mode 100644 index 64e8e2f..0000000 Binary files a/project/sounds/aiy020000060.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000060.opus b/project/sounds/aiy020000060.opus new file mode 100644 index 0000000..a574a48 Binary files /dev/null and b/project/sounds/aiy020000060.opus differ diff --git a/project/sounds/aiy020000070.mp3 b/project/sounds/aiy020000070.mp3 deleted file mode 100644 index a6dc7b2..0000000 Binary files a/project/sounds/aiy020000070.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000070.opus b/project/sounds/aiy020000070.opus new file mode 100644 index 0000000..c4250aa Binary files /dev/null and b/project/sounds/aiy020000070.opus differ diff --git a/project/sounds/aiy020000080.mp3 b/project/sounds/aiy020000080.mp3 deleted file mode 100644 index b3b7b81..0000000 Binary files a/project/sounds/aiy020000080.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000080.opus b/project/sounds/aiy020000080.opus new file mode 100644 index 0000000..5002cc3 Binary files /dev/null and b/project/sounds/aiy020000080.opus differ diff --git a/project/sounds/aiy020000090.mp3 b/project/sounds/aiy020000090.mp3 deleted file mode 100644 index 9680e84..0000000 Binary files a/project/sounds/aiy020000090.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000090.opus b/project/sounds/aiy020000090.opus new file mode 100644 index 0000000..cedb5f0 Binary files /dev/null and b/project/sounds/aiy020000090.opus differ diff --git a/project/sounds/aiy020000100.mp3 b/project/sounds/aiy020000100.mp3 deleted file mode 100644 index 74349d9..0000000 Binary files a/project/sounds/aiy020000100.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000100.opus b/project/sounds/aiy020000100.opus new file mode 100644 index 0000000..deded59 Binary files /dev/null and b/project/sounds/aiy020000100.opus differ diff --git a/project/sounds/aiy020000110.mp3 b/project/sounds/aiy020000110.mp3 deleted file mode 100644 index 9c1625b..0000000 Binary files a/project/sounds/aiy020000110.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000110.opus b/project/sounds/aiy020000110.opus new file mode 100644 index 0000000..574f59c Binary files /dev/null and b/project/sounds/aiy020000110.opus differ diff --git a/project/sounds/aiy020000120.mp3 b/project/sounds/aiy020000120.mp3 deleted file mode 100644 index e8accbe..0000000 Binary files a/project/sounds/aiy020000120.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000120.opus b/project/sounds/aiy020000120.opus new file mode 100644 index 0000000..0e982bc Binary files /dev/null and b/project/sounds/aiy020000120.opus differ diff --git a/project/sounds/aiy020000130.mp3 b/project/sounds/aiy020000130.mp3 deleted file mode 100644 index 4d33949..0000000 Binary files a/project/sounds/aiy020000130.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000130.opus b/project/sounds/aiy020000130.opus new file mode 100644 index 0000000..837d498 Binary files /dev/null and b/project/sounds/aiy020000130.opus differ diff --git a/project/sounds/aiy020000140.mp3 b/project/sounds/aiy020000140.mp3 deleted file mode 100644 index b207d11..0000000 Binary files a/project/sounds/aiy020000140.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000140.opus b/project/sounds/aiy020000140.opus new file mode 100644 index 0000000..b57af22 Binary files /dev/null and b/project/sounds/aiy020000140.opus differ diff --git a/project/sounds/aiy020000150.mp3 b/project/sounds/aiy020000150.mp3 deleted file mode 100644 index d652629..0000000 Binary files a/project/sounds/aiy020000150.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000150.opus b/project/sounds/aiy020000150.opus new file mode 100644 index 0000000..4ab93aa Binary files /dev/null and b/project/sounds/aiy020000150.opus differ diff --git a/project/sounds/aiy020000160.mp3 b/project/sounds/aiy020000160.mp3 deleted file mode 100644 index 317fd3c..0000000 Binary files a/project/sounds/aiy020000160.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000160.opus b/project/sounds/aiy020000160.opus new file mode 100644 index 0000000..14cf9f9 Binary files /dev/null and b/project/sounds/aiy020000160.opus differ diff --git a/project/sounds/aiy020000170.mp3 b/project/sounds/aiy020000170.mp3 deleted file mode 100644 index f82fb9e..0000000 Binary files a/project/sounds/aiy020000170.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000170.opus b/project/sounds/aiy020000170.opus new file mode 100644 index 0000000..fbf0b94 Binary files /dev/null and b/project/sounds/aiy020000170.opus differ diff --git a/project/sounds/aiy020000180.mp3 b/project/sounds/aiy020000180.mp3 deleted file mode 100644 index e41d74c..0000000 Binary files a/project/sounds/aiy020000180.mp3 and /dev/null differ diff --git a/project/sounds/aiy020000180.opus b/project/sounds/aiy020000180.opus new file mode 100644 index 0000000..2dfcfc2 Binary files /dev/null and b/project/sounds/aiy020000180.opus differ diff --git a/project/sounds/aiy310000010.mp3 b/project/sounds/aiy310000010.mp3 deleted file mode 100644 index d6bf306..0000000 Binary files a/project/sounds/aiy310000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000010.opus b/project/sounds/aiy310000010.opus new file mode 100644 index 0000000..9955fab Binary files /dev/null and b/project/sounds/aiy310000010.opus differ diff --git a/project/sounds/aiy310000020.mp3 b/project/sounds/aiy310000020.mp3 deleted file mode 100644 index 84ad00e..0000000 Binary files a/project/sounds/aiy310000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000020.opus b/project/sounds/aiy310000020.opus new file mode 100644 index 0000000..99fe711 Binary files /dev/null and b/project/sounds/aiy310000020.opus differ diff --git a/project/sounds/aiy310000030.mp3 b/project/sounds/aiy310000030.mp3 deleted file mode 100644 index ca51c6c..0000000 Binary files a/project/sounds/aiy310000030.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000030.opus b/project/sounds/aiy310000030.opus new file mode 100644 index 0000000..e62ade9 Binary files /dev/null and b/project/sounds/aiy310000030.opus differ diff --git a/project/sounds/aiy310000040.mp3 b/project/sounds/aiy310000040.mp3 deleted file mode 100644 index 139b9c4..0000000 Binary files a/project/sounds/aiy310000040.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000040.opus b/project/sounds/aiy310000040.opus new file mode 100644 index 0000000..ecd0f9b Binary files /dev/null and b/project/sounds/aiy310000040.opus differ diff --git a/project/sounds/aiy310000050.mp3 b/project/sounds/aiy310000050.mp3 deleted file mode 100644 index b0b68b9..0000000 Binary files a/project/sounds/aiy310000050.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000050.opus b/project/sounds/aiy310000050.opus new file mode 100644 index 0000000..12f5233 Binary files /dev/null and b/project/sounds/aiy310000050.opus differ diff --git a/project/sounds/aiy310000060.mp3 b/project/sounds/aiy310000060.mp3 deleted file mode 100644 index 9773718..0000000 Binary files a/project/sounds/aiy310000060.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000060.opus b/project/sounds/aiy310000060.opus new file mode 100644 index 0000000..4e36bcd Binary files /dev/null and b/project/sounds/aiy310000060.opus differ diff --git a/project/sounds/aiy310000070.mp3 b/project/sounds/aiy310000070.mp3 deleted file mode 100644 index 3b2667c..0000000 Binary files a/project/sounds/aiy310000070.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000070.opus b/project/sounds/aiy310000070.opus new file mode 100644 index 0000000..db9d0e7 Binary files /dev/null and b/project/sounds/aiy310000070.opus differ diff --git a/project/sounds/aiy310000080.mp3 b/project/sounds/aiy310000080.mp3 deleted file mode 100644 index 392969d..0000000 Binary files a/project/sounds/aiy310000080.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000080.opus b/project/sounds/aiy310000080.opus new file mode 100644 index 0000000..c303fb3 Binary files /dev/null and b/project/sounds/aiy310000080.opus differ diff --git a/project/sounds/aiy310000090.mp3 b/project/sounds/aiy310000090.mp3 deleted file mode 100644 index 7ace161..0000000 Binary files a/project/sounds/aiy310000090.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000090.opus b/project/sounds/aiy310000090.opus new file mode 100644 index 0000000..63289e6 Binary files /dev/null and b/project/sounds/aiy310000090.opus differ diff --git a/project/sounds/aiy310000100.mp3 b/project/sounds/aiy310000100.mp3 deleted file mode 100644 index 57907d8..0000000 Binary files a/project/sounds/aiy310000100.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000100.opus b/project/sounds/aiy310000100.opus new file mode 100644 index 0000000..e62de21 Binary files /dev/null and b/project/sounds/aiy310000100.opus differ diff --git a/project/sounds/aiy310000110.mp3 b/project/sounds/aiy310000110.mp3 deleted file mode 100644 index 955cda3..0000000 Binary files a/project/sounds/aiy310000110.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000110.opus b/project/sounds/aiy310000110.opus new file mode 100644 index 0000000..3c6a541 Binary files /dev/null and b/project/sounds/aiy310000110.opus differ diff --git a/project/sounds/aiy310000120.mp3 b/project/sounds/aiy310000120.mp3 deleted file mode 100644 index d387bdd..0000000 Binary files a/project/sounds/aiy310000120.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000120.opus b/project/sounds/aiy310000120.opus new file mode 100644 index 0000000..3961d24 Binary files /dev/null and b/project/sounds/aiy310000120.opus differ diff --git a/project/sounds/aiy310000130.mp3 b/project/sounds/aiy310000130.mp3 deleted file mode 100644 index e643d89..0000000 Binary files a/project/sounds/aiy310000130.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000130.opus b/project/sounds/aiy310000130.opus new file mode 100644 index 0000000..e5dab39 Binary files /dev/null and b/project/sounds/aiy310000130.opus differ diff --git a/project/sounds/aiy310000140.mp3 b/project/sounds/aiy310000140.mp3 deleted file mode 100644 index e647175..0000000 Binary files a/project/sounds/aiy310000140.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000140.opus b/project/sounds/aiy310000140.opus new file mode 100644 index 0000000..e7bf71f Binary files /dev/null and b/project/sounds/aiy310000140.opus differ diff --git a/project/sounds/aiy310000150.mp3 b/project/sounds/aiy310000150.mp3 deleted file mode 100644 index 5d64a1d..0000000 Binary files a/project/sounds/aiy310000150.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000150.opus b/project/sounds/aiy310000150.opus new file mode 100644 index 0000000..b6fe0bd Binary files /dev/null and b/project/sounds/aiy310000150.opus differ diff --git a/project/sounds/aiy310000160.mp3 b/project/sounds/aiy310000160.mp3 deleted file mode 100644 index 24ec45b..0000000 Binary files a/project/sounds/aiy310000160.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000160.opus b/project/sounds/aiy310000160.opus new file mode 100644 index 0000000..1de7696 Binary files /dev/null and b/project/sounds/aiy310000160.opus differ diff --git a/project/sounds/aiy310000170.mp3 b/project/sounds/aiy310000170.mp3 deleted file mode 100644 index 5c1950a..0000000 Binary files a/project/sounds/aiy310000170.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000170.opus b/project/sounds/aiy310000170.opus new file mode 100644 index 0000000..55df5b4 Binary files /dev/null and b/project/sounds/aiy310000170.opus differ diff --git a/project/sounds/aiy310000180.mp3 b/project/sounds/aiy310000180.mp3 deleted file mode 100644 index 6fbf8f1..0000000 Binary files a/project/sounds/aiy310000180.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000180.opus b/project/sounds/aiy310000180.opus new file mode 100644 index 0000000..dc9a85c Binary files /dev/null and b/project/sounds/aiy310000180.opus differ diff --git a/project/sounds/aiy310000190.mp3 b/project/sounds/aiy310000190.mp3 deleted file mode 100644 index 06e7906..0000000 Binary files a/project/sounds/aiy310000190.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000190.opus b/project/sounds/aiy310000190.opus new file mode 100644 index 0000000..821e276 Binary files /dev/null and b/project/sounds/aiy310000190.opus differ diff --git a/project/sounds/aiy310000200.mp3 b/project/sounds/aiy310000200.mp3 deleted file mode 100644 index 68d714d..0000000 Binary files a/project/sounds/aiy310000200.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000200.opus b/project/sounds/aiy310000200.opus new file mode 100644 index 0000000..51ba4fc Binary files /dev/null and b/project/sounds/aiy310000200.opus differ diff --git a/project/sounds/aiy310000210.mp3 b/project/sounds/aiy310000210.mp3 deleted file mode 100644 index f1d7e95..0000000 Binary files a/project/sounds/aiy310000210.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000210.opus b/project/sounds/aiy310000210.opus new file mode 100644 index 0000000..4fe0fa9 Binary files /dev/null and b/project/sounds/aiy310000210.opus differ diff --git a/project/sounds/aiy310000220.mp3 b/project/sounds/aiy310000220.mp3 deleted file mode 100644 index 4fc534d..0000000 Binary files a/project/sounds/aiy310000220.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000220.opus b/project/sounds/aiy310000220.opus new file mode 100644 index 0000000..169de30 Binary files /dev/null and b/project/sounds/aiy310000220.opus differ diff --git a/project/sounds/aiy310000230.mp3 b/project/sounds/aiy310000230.mp3 deleted file mode 100644 index 57f5059..0000000 Binary files a/project/sounds/aiy310000230.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000230.opus b/project/sounds/aiy310000230.opus new file mode 100644 index 0000000..db0f37d Binary files /dev/null and b/project/sounds/aiy310000230.opus differ diff --git a/project/sounds/aiy310000240.mp3 b/project/sounds/aiy310000240.mp3 deleted file mode 100644 index c9d29ee..0000000 Binary files a/project/sounds/aiy310000240.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000240.opus b/project/sounds/aiy310000240.opus new file mode 100644 index 0000000..597e491 Binary files /dev/null and b/project/sounds/aiy310000240.opus differ diff --git a/project/sounds/aiy310000250.mp3 b/project/sounds/aiy310000250.mp3 deleted file mode 100644 index 37346d0..0000000 Binary files a/project/sounds/aiy310000250.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000250.opus b/project/sounds/aiy310000250.opus new file mode 100644 index 0000000..e8ada96 Binary files /dev/null and b/project/sounds/aiy310000250.opus differ diff --git a/project/sounds/aiy310000260.mp3 b/project/sounds/aiy310000260.mp3 deleted file mode 100644 index b547f31..0000000 Binary files a/project/sounds/aiy310000260.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000260.opus b/project/sounds/aiy310000260.opus new file mode 100644 index 0000000..1d7bae9 Binary files /dev/null and b/project/sounds/aiy310000260.opus differ diff --git a/project/sounds/aiy310000280.mp3 b/project/sounds/aiy310000280.mp3 deleted file mode 100644 index d7ad3e9..0000000 Binary files a/project/sounds/aiy310000280.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000280.opus b/project/sounds/aiy310000280.opus new file mode 100644 index 0000000..649f1fd Binary files /dev/null and b/project/sounds/aiy310000280.opus differ diff --git a/project/sounds/aiy310000290.mp3 b/project/sounds/aiy310000290.mp3 deleted file mode 100644 index 2380de7..0000000 Binary files a/project/sounds/aiy310000290.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000290.opus b/project/sounds/aiy310000290.opus new file mode 100644 index 0000000..f4222de Binary files /dev/null and b/project/sounds/aiy310000290.opus differ diff --git a/project/sounds/aiy310000300.mp3 b/project/sounds/aiy310000300.mp3 deleted file mode 100644 index c171f56..0000000 Binary files a/project/sounds/aiy310000300.mp3 and /dev/null differ diff --git a/project/sounds/aiy310000300.opus b/project/sounds/aiy310000300.opus new file mode 100644 index 0000000..32c2f49 Binary files /dev/null and b/project/sounds/aiy310000300.opus differ diff --git a/project/sounds/aiy350000010.mp3 b/project/sounds/aiy350000010.mp3 deleted file mode 100644 index 57c17f9..0000000 Binary files a/project/sounds/aiy350000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000010.opus b/project/sounds/aiy350000010.opus new file mode 100644 index 0000000..0f41912 Binary files /dev/null and b/project/sounds/aiy350000010.opus differ diff --git a/project/sounds/aiy350000020.mp3 b/project/sounds/aiy350000020.mp3 deleted file mode 100644 index 6ebb9bc..0000000 Binary files a/project/sounds/aiy350000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000020.opus b/project/sounds/aiy350000020.opus new file mode 100644 index 0000000..b99c603 Binary files /dev/null and b/project/sounds/aiy350000020.opus differ diff --git a/project/sounds/aiy350000030.mp3 b/project/sounds/aiy350000030.mp3 deleted file mode 100644 index 2f57a03..0000000 Binary files a/project/sounds/aiy350000030.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000030.opus b/project/sounds/aiy350000030.opus new file mode 100644 index 0000000..773d947 Binary files /dev/null and b/project/sounds/aiy350000030.opus differ diff --git a/project/sounds/aiy350000040.mp3 b/project/sounds/aiy350000040.mp3 deleted file mode 100644 index 13d4523..0000000 Binary files a/project/sounds/aiy350000040.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000040.opus b/project/sounds/aiy350000040.opus new file mode 100644 index 0000000..df657bf Binary files /dev/null and b/project/sounds/aiy350000040.opus differ diff --git a/project/sounds/aiy350000050.mp3 b/project/sounds/aiy350000050.mp3 deleted file mode 100644 index 5c763e4..0000000 Binary files a/project/sounds/aiy350000050.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000050.opus b/project/sounds/aiy350000050.opus new file mode 100644 index 0000000..d520d2e Binary files /dev/null and b/project/sounds/aiy350000050.opus differ diff --git a/project/sounds/aiy350000060.mp3 b/project/sounds/aiy350000060.mp3 deleted file mode 100644 index 790d096..0000000 Binary files a/project/sounds/aiy350000060.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000060.opus b/project/sounds/aiy350000060.opus new file mode 100644 index 0000000..47f1642 Binary files /dev/null and b/project/sounds/aiy350000060.opus differ diff --git a/project/sounds/aiy350000070.mp3 b/project/sounds/aiy350000070.mp3 deleted file mode 100644 index 236c48a..0000000 Binary files a/project/sounds/aiy350000070.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000070.opus b/project/sounds/aiy350000070.opus new file mode 100644 index 0000000..b02685c Binary files /dev/null and b/project/sounds/aiy350000070.opus differ diff --git a/project/sounds/aiy350000080.mp3 b/project/sounds/aiy350000080.mp3 deleted file mode 100644 index f6753db..0000000 Binary files a/project/sounds/aiy350000080.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000080.opus b/project/sounds/aiy350000080.opus new file mode 100644 index 0000000..868a458 Binary files /dev/null and b/project/sounds/aiy350000080.opus differ diff --git a/project/sounds/aiy350000090.mp3 b/project/sounds/aiy350000090.mp3 deleted file mode 100644 index 869aea1..0000000 Binary files a/project/sounds/aiy350000090.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000090.opus b/project/sounds/aiy350000090.opus new file mode 100644 index 0000000..26a24b1 Binary files /dev/null and b/project/sounds/aiy350000090.opus differ diff --git a/project/sounds/aiy350000100.mp3 b/project/sounds/aiy350000100.mp3 deleted file mode 100644 index f87b58f..0000000 Binary files a/project/sounds/aiy350000100.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000100.opus b/project/sounds/aiy350000100.opus new file mode 100644 index 0000000..45199fe Binary files /dev/null and b/project/sounds/aiy350000100.opus differ diff --git a/project/sounds/aiy350000110.mp3 b/project/sounds/aiy350000110.mp3 deleted file mode 100644 index 553ac12..0000000 Binary files a/project/sounds/aiy350000110.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000110.opus b/project/sounds/aiy350000110.opus new file mode 100644 index 0000000..ef10116 Binary files /dev/null and b/project/sounds/aiy350000110.opus differ diff --git a/project/sounds/aiy350000120.mp3 b/project/sounds/aiy350000120.mp3 deleted file mode 100644 index 57f9899..0000000 Binary files a/project/sounds/aiy350000120.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000120.opus b/project/sounds/aiy350000120.opus new file mode 100644 index 0000000..5c9fd50 Binary files /dev/null and b/project/sounds/aiy350000120.opus differ diff --git a/project/sounds/aiy350000130.mp3 b/project/sounds/aiy350000130.mp3 deleted file mode 100644 index 62e68dc..0000000 Binary files a/project/sounds/aiy350000130.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000130.opus b/project/sounds/aiy350000130.opus new file mode 100644 index 0000000..c07b760 Binary files /dev/null and b/project/sounds/aiy350000130.opus differ diff --git a/project/sounds/aiy350000140.mp3 b/project/sounds/aiy350000140.mp3 deleted file mode 100644 index b4ff73c..0000000 Binary files a/project/sounds/aiy350000140.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000140.opus b/project/sounds/aiy350000140.opus new file mode 100644 index 0000000..50ddd6d Binary files /dev/null and b/project/sounds/aiy350000140.opus differ diff --git a/project/sounds/aiy350000150.mp3 b/project/sounds/aiy350000150.mp3 deleted file mode 100644 index b8d9ce2..0000000 Binary files a/project/sounds/aiy350000150.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000150.opus b/project/sounds/aiy350000150.opus new file mode 100644 index 0000000..7626306 Binary files /dev/null and b/project/sounds/aiy350000150.opus differ diff --git a/project/sounds/aiy350000160.mp3 b/project/sounds/aiy350000160.mp3 deleted file mode 100644 index 563717f..0000000 Binary files a/project/sounds/aiy350000160.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000160.opus b/project/sounds/aiy350000160.opus new file mode 100644 index 0000000..6a0e778 Binary files /dev/null and b/project/sounds/aiy350000160.opus differ diff --git a/project/sounds/aiy350000170.mp3 b/project/sounds/aiy350000170.mp3 deleted file mode 100644 index 23266c1..0000000 Binary files a/project/sounds/aiy350000170.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000170.opus b/project/sounds/aiy350000170.opus new file mode 100644 index 0000000..264c29b Binary files /dev/null and b/project/sounds/aiy350000170.opus differ diff --git a/project/sounds/aiy350000180.mp3 b/project/sounds/aiy350000180.mp3 deleted file mode 100644 index 0a52d64..0000000 Binary files a/project/sounds/aiy350000180.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000180.opus b/project/sounds/aiy350000180.opus new file mode 100644 index 0000000..f30c59e Binary files /dev/null and b/project/sounds/aiy350000180.opus differ diff --git a/project/sounds/aiy350000190.mp3 b/project/sounds/aiy350000190.mp3 deleted file mode 100644 index 5379fbb..0000000 Binary files a/project/sounds/aiy350000190.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000190.opus b/project/sounds/aiy350000190.opus new file mode 100644 index 0000000..c2a045a Binary files /dev/null and b/project/sounds/aiy350000190.opus differ diff --git a/project/sounds/aiy350000200.mp3 b/project/sounds/aiy350000200.mp3 deleted file mode 100644 index 7bf86fd..0000000 Binary files a/project/sounds/aiy350000200.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000200.opus b/project/sounds/aiy350000200.opus new file mode 100644 index 0000000..d964326 Binary files /dev/null and b/project/sounds/aiy350000200.opus differ diff --git a/project/sounds/aiy350000210.mp3 b/project/sounds/aiy350000210.mp3 deleted file mode 100644 index 01426ec..0000000 Binary files a/project/sounds/aiy350000210.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000210.opus b/project/sounds/aiy350000210.opus new file mode 100644 index 0000000..dcf95ec Binary files /dev/null and b/project/sounds/aiy350000210.opus differ diff --git a/project/sounds/aiy350000220.mp3 b/project/sounds/aiy350000220.mp3 deleted file mode 100644 index 7687c76..0000000 Binary files a/project/sounds/aiy350000220.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000220.opus b/project/sounds/aiy350000220.opus new file mode 100644 index 0000000..ba965cf Binary files /dev/null and b/project/sounds/aiy350000220.opus differ diff --git a/project/sounds/aiy350000230.mp3 b/project/sounds/aiy350000230.mp3 deleted file mode 100644 index ef72218..0000000 Binary files a/project/sounds/aiy350000230.mp3 and /dev/null differ diff --git a/project/sounds/aiy350000230.opus b/project/sounds/aiy350000230.opus new file mode 100644 index 0000000..88cd8a9 Binary files /dev/null and b/project/sounds/aiy350000230.opus differ diff --git a/project/sounds/aiy710000010.mp3 b/project/sounds/aiy710000010.mp3 deleted file mode 100644 index 6e08794..0000000 Binary files a/project/sounds/aiy710000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000010.opus b/project/sounds/aiy710000010.opus new file mode 100644 index 0000000..bcf6341 Binary files /dev/null and b/project/sounds/aiy710000010.opus differ diff --git a/project/sounds/aiy710000020.mp3 b/project/sounds/aiy710000020.mp3 deleted file mode 100644 index d025b23..0000000 Binary files a/project/sounds/aiy710000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000020.opus b/project/sounds/aiy710000020.opus new file mode 100644 index 0000000..f4de94a Binary files /dev/null and b/project/sounds/aiy710000020.opus differ diff --git a/project/sounds/aiy710000030.mp3 b/project/sounds/aiy710000030.mp3 deleted file mode 100644 index ad200f2..0000000 Binary files a/project/sounds/aiy710000030.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000030.opus b/project/sounds/aiy710000030.opus new file mode 100644 index 0000000..c6f5d74 Binary files /dev/null and b/project/sounds/aiy710000030.opus differ diff --git a/project/sounds/aiy710000040.mp3 b/project/sounds/aiy710000040.mp3 deleted file mode 100644 index dbdbbd2..0000000 Binary files a/project/sounds/aiy710000040.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000040.opus b/project/sounds/aiy710000040.opus new file mode 100644 index 0000000..02e9b14 Binary files /dev/null and b/project/sounds/aiy710000040.opus differ diff --git a/project/sounds/aiy710000050.mp3 b/project/sounds/aiy710000050.mp3 deleted file mode 100644 index f304d67..0000000 Binary files a/project/sounds/aiy710000050.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000050.opus b/project/sounds/aiy710000050.opus new file mode 100644 index 0000000..a37c230 Binary files /dev/null and b/project/sounds/aiy710000050.opus differ diff --git a/project/sounds/aiy710000060.mp3 b/project/sounds/aiy710000060.mp3 deleted file mode 100644 index 1366af4..0000000 Binary files a/project/sounds/aiy710000060.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000060.opus b/project/sounds/aiy710000060.opus new file mode 100644 index 0000000..2fae4fc Binary files /dev/null and b/project/sounds/aiy710000060.opus differ diff --git a/project/sounds/aiy710000070.mp3 b/project/sounds/aiy710000070.mp3 deleted file mode 100644 index 1528e54..0000000 Binary files a/project/sounds/aiy710000070.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000070.opus b/project/sounds/aiy710000070.opus new file mode 100644 index 0000000..ce2b71c Binary files /dev/null and b/project/sounds/aiy710000070.opus differ diff --git a/project/sounds/aiy710000080.mp3 b/project/sounds/aiy710000080.mp3 deleted file mode 100644 index 600011d..0000000 Binary files a/project/sounds/aiy710000080.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000080.opus b/project/sounds/aiy710000080.opus new file mode 100644 index 0000000..0673c92 Binary files /dev/null and b/project/sounds/aiy710000080.opus differ diff --git a/project/sounds/aiy710000090.mp3 b/project/sounds/aiy710000090.mp3 deleted file mode 100644 index b091686..0000000 Binary files a/project/sounds/aiy710000090.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000090.opus b/project/sounds/aiy710000090.opus new file mode 100644 index 0000000..9990f15 Binary files /dev/null and b/project/sounds/aiy710000090.opus differ diff --git a/project/sounds/aiy710000100.mp3 b/project/sounds/aiy710000100.mp3 deleted file mode 100644 index 3f84955..0000000 Binary files a/project/sounds/aiy710000100.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000100.opus b/project/sounds/aiy710000100.opus new file mode 100644 index 0000000..28b109b Binary files /dev/null and b/project/sounds/aiy710000100.opus differ diff --git a/project/sounds/aiy710000110.mp3 b/project/sounds/aiy710000110.mp3 deleted file mode 100644 index 1095ffa..0000000 Binary files a/project/sounds/aiy710000110.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000110.opus b/project/sounds/aiy710000110.opus new file mode 100644 index 0000000..ad02c45 Binary files /dev/null and b/project/sounds/aiy710000110.opus differ diff --git a/project/sounds/aiy710000120.mp3 b/project/sounds/aiy710000120.mp3 deleted file mode 100644 index 81be490..0000000 Binary files a/project/sounds/aiy710000120.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000120.opus b/project/sounds/aiy710000120.opus new file mode 100644 index 0000000..0985ea1 Binary files /dev/null and b/project/sounds/aiy710000120.opus differ diff --git a/project/sounds/aiy710000130.mp3 b/project/sounds/aiy710000130.mp3 deleted file mode 100644 index bde952f..0000000 Binary files a/project/sounds/aiy710000130.mp3 and /dev/null differ diff --git a/project/sounds/aiy710000130.opus b/project/sounds/aiy710000130.opus new file mode 100644 index 0000000..07881d1 Binary files /dev/null and b/project/sounds/aiy710000130.opus differ diff --git a/project/sounds/aiy820000010.mp3 b/project/sounds/aiy820000010.mp3 deleted file mode 100644 index 04b2978..0000000 Binary files a/project/sounds/aiy820000010.mp3 and /dev/null differ diff --git a/project/sounds/aiy820000010.opus b/project/sounds/aiy820000010.opus new file mode 100644 index 0000000..4503491 Binary files /dev/null and b/project/sounds/aiy820000010.opus differ diff --git a/project/sounds/aiy820000020.mp3 b/project/sounds/aiy820000020.mp3 deleted file mode 100644 index ef06278..0000000 Binary files a/project/sounds/aiy820000020.mp3 and /dev/null differ diff --git a/project/sounds/aiy820000020.opus b/project/sounds/aiy820000020.opus new file mode 100644 index 0000000..e405674 Binary files /dev/null and b/project/sounds/aiy820000020.opus differ diff --git a/project/sounds/attack.mp3 b/project/sounds/attack.mp3 deleted file mode 100644 index 16098f7..0000000 Binary files a/project/sounds/attack.mp3 and /dev/null differ diff --git a/project/sounds/bomb.mp3 b/project/sounds/bomb.mp3 deleted file mode 100644 index 8a98067..0000000 Binary files a/project/sounds/bomb.mp3 and /dev/null differ diff --git a/project/sounds/bomb.opus b/project/sounds/bomb.opus new file mode 100644 index 0000000..443e6af Binary files /dev/null and b/project/sounds/bomb.opus differ diff --git a/project/sounds/cancel.mp3 b/project/sounds/cancel.mp3 deleted file mode 100644 index 3842eee..0000000 Binary files a/project/sounds/cancel.mp3 and /dev/null differ diff --git a/project/sounds/cancel.opus b/project/sounds/cancel.opus new file mode 100644 index 0000000..da26056 Binary files /dev/null and b/project/sounds/cancel.opus differ diff --git a/project/sounds/centerFly.mp3 b/project/sounds/centerFly.mp3 deleted file mode 100644 index 8246763..0000000 Binary files a/project/sounds/centerFly.mp3 and /dev/null differ diff --git a/project/sounds/centerFly.opus b/project/sounds/centerFly.opus new file mode 100644 index 0000000..fd94f13 Binary files /dev/null and b/project/sounds/centerFly.opus differ diff --git a/project/sounds/confirm.mp3 b/project/sounds/confirm.mp3 deleted file mode 100644 index 138beeb..0000000 Binary files a/project/sounds/confirm.mp3 and /dev/null differ diff --git a/project/sounds/confirm.opus b/project/sounds/confirm.opus new file mode 100644 index 0000000..821a7dc Binary files /dev/null and b/project/sounds/confirm.opus differ diff --git a/project/sounds/cursor.mp3 b/project/sounds/cursor.mp3 deleted file mode 100644 index 652bf07..0000000 Binary files a/project/sounds/cursor.mp3 and /dev/null differ diff --git a/project/sounds/cursor.opus b/project/sounds/cursor.opus new file mode 100644 index 0000000..7eec58d Binary files /dev/null and b/project/sounds/cursor.opus differ diff --git a/project/sounds/door.mp3 b/project/sounds/door.mp3 deleted file mode 100644 index ea6706d..0000000 Binary files a/project/sounds/door.mp3 and /dev/null differ diff --git a/project/sounds/door.opus b/project/sounds/door.opus new file mode 100644 index 0000000..dce3824 Binary files /dev/null and b/project/sounds/door.opus differ diff --git a/project/sounds/equip.mp3 b/project/sounds/equip.mp3 deleted file mode 100644 index 36bbd02..0000000 Binary files a/project/sounds/equip.mp3 and /dev/null differ diff --git a/project/sounds/equip.opus b/project/sounds/equip.opus new file mode 100644 index 0000000..2bd2c63 Binary files /dev/null and b/project/sounds/equip.opus differ diff --git a/project/sounds/error.mp3 b/project/sounds/error.mp3 deleted file mode 100644 index 329cca4..0000000 Binary files a/project/sounds/error.mp3 and /dev/null differ diff --git a/project/sounds/error.opus b/project/sounds/error.opus new file mode 100644 index 0000000..973ee32 Binary files /dev/null and b/project/sounds/error.opus differ diff --git a/project/sounds/floor.mp3 b/project/sounds/floor.mp3 deleted file mode 100644 index 2b24efb..0000000 Binary files a/project/sounds/floor.mp3 and /dev/null differ diff --git a/project/sounds/floor.opus b/project/sounds/floor.opus new file mode 100644 index 0000000..0f603d3 Binary files /dev/null and b/project/sounds/floor.opus differ diff --git a/project/sounds/gem.mp3 b/project/sounds/gem.mp3 deleted file mode 100644 index c29b9df..0000000 Binary files a/project/sounds/gem.mp3 and /dev/null differ diff --git a/project/sounds/gem.opus b/project/sounds/gem.opus new file mode 100644 index 0000000..f7bbf6c Binary files /dev/null and b/project/sounds/gem.opus differ diff --git a/project/sounds/icePickaxe.mp3 b/project/sounds/icePickaxe.mp3 deleted file mode 100644 index 29ed9b0..0000000 Binary files a/project/sounds/icePickaxe.mp3 and /dev/null differ diff --git a/project/sounds/icePickaxe.opus b/project/sounds/icePickaxe.opus new file mode 100644 index 0000000..479b8e5 Binary files /dev/null and b/project/sounds/icePickaxe.opus differ diff --git a/project/sounds/item.mp3 b/project/sounds/item.mp3 deleted file mode 100644 index 5d82178..0000000 Binary files a/project/sounds/item.mp3 and /dev/null differ diff --git a/project/sounds/item.opus b/project/sounds/item.opus new file mode 100644 index 0000000..9a488f0 Binary files /dev/null and b/project/sounds/item.opus differ diff --git a/project/sounds/jingbao.mp3 b/project/sounds/jingbao.mp3 deleted file mode 100644 index c2c4aaa..0000000 Binary files a/project/sounds/jingbao.mp3 and /dev/null differ diff --git a/project/sounds/jingbao.opus b/project/sounds/jingbao.opus new file mode 100644 index 0000000..b2a57e1 Binary files /dev/null and b/project/sounds/jingbao.opus differ diff --git a/project/sounds/jump.mp3 b/project/sounds/jump.mp3 deleted file mode 100644 index b8ce8f7..0000000 Binary files a/project/sounds/jump.mp3 and /dev/null differ diff --git a/project/sounds/jump.opus b/project/sounds/jump.opus new file mode 100644 index 0000000..b1c8c01 Binary files /dev/null and b/project/sounds/jump.opus differ diff --git a/project/sounds/load.mp3 b/project/sounds/load.mp3 deleted file mode 100644 index e680f4d..0000000 Binary files a/project/sounds/load.mp3 and /dev/null differ diff --git a/project/sounds/load.opus b/project/sounds/load.opus new file mode 100644 index 0000000..215881f Binary files /dev/null and b/project/sounds/load.opus differ diff --git a/project/sounds/open_ui.mp3 b/project/sounds/open_ui.mp3 deleted file mode 100644 index 0282a08..0000000 Binary files a/project/sounds/open_ui.mp3 and /dev/null differ diff --git a/project/sounds/open_ui.opus b/project/sounds/open_ui.opus new file mode 100644 index 0000000..dbad758 Binary files /dev/null and b/project/sounds/open_ui.opus differ diff --git a/project/sounds/pickaxe.mp3 b/project/sounds/pickaxe.mp3 deleted file mode 100644 index f6dc258..0000000 Binary files a/project/sounds/pickaxe.mp3 and /dev/null differ diff --git a/project/sounds/pickaxe.opus b/project/sounds/pickaxe.opus new file mode 100644 index 0000000..2a9924d Binary files /dev/null and b/project/sounds/pickaxe.opus differ diff --git a/project/sounds/recovery.mp3 b/project/sounds/recovery.mp3 deleted file mode 100644 index 76b67f3..0000000 Binary files a/project/sounds/recovery.mp3 and /dev/null differ diff --git a/project/sounds/recovery.opus b/project/sounds/recovery.opus new file mode 100644 index 0000000..07091c8 Binary files /dev/null and b/project/sounds/recovery.opus differ diff --git a/project/sounds/save.mp3 b/project/sounds/save.mp3 deleted file mode 100644 index 295dbf2..0000000 Binary files a/project/sounds/save.mp3 and /dev/null differ diff --git a/project/sounds/save.opus b/project/sounds/save.opus new file mode 100644 index 0000000..58f2c9a Binary files /dev/null and b/project/sounds/save.opus differ diff --git a/project/sounds/shop.mp3 b/project/sounds/shop.mp3 deleted file mode 100644 index b654aa2..0000000 Binary files a/project/sounds/shop.mp3 and /dev/null differ diff --git a/project/sounds/shop.opus b/project/sounds/shop.opus new file mode 100644 index 0000000..1accbf1 Binary files /dev/null and b/project/sounds/shop.opus differ diff --git a/project/sounds/zone.mp3 b/project/sounds/zone.mp3 deleted file mode 100644 index 414b287..0000000 Binary files a/project/sounds/zone.mp3 and /dev/null differ diff --git a/project/sounds/zone.opus b/project/sounds/zone.opus new file mode 100644 index 0000000..ccdade8 Binary files /dev/null and b/project/sounds/zone.opus differ