diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index c488ffd..eaa641e 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -1138,7 +1138,7 @@ return code; cgtext_s : '背景' EvalString? '回忆滤镜' Bool? '移除对话框' Bool? '剧情库序列' Int '头像' EvalString?'坐标PX' Number'打字间隔' Int? BGNL? Newline - '自动等待时长' Int '音频文件(需在全塔属性——使用音效注册)'EvalString? BGNL? Newline + '自动等待时长' Int BGNL? Newline textcgDrawingList+? Newline @@ -1146,11 +1146,10 @@ cgtext_s tooltip : cgtext:显示一段包含cg的文字(剧情) helpUrl : /_docs/#/instruction allImages : ['EvalString_0','EvalString_1'] -allSounds : ['EvalString_2'] default : ["bg_5043.webp",false,false,0,"face_050445.webp",-300,0,2000,"","这句话显示在对话框内",[{ "name":"tati_050145a.webp" , "px": 100,"filter":false }]] var head ='{ "name": "'+EvalString_1+'", "px": '+Number_0+' }' var list=',"bodyList": [\n'+textcgDrawingList_0.slice(0,-1)+'\n]' -var code = '{"type": "cgtext", "bg":"'+EvalString_0+'","memory":'+Bool_0+',"WindowSkin":'+Bool_1+',"head":'+head+' ,"index":"'+Int_0+'","time":'+Int_1+',"wait":'+Int_2+',"sound":"'+EvalString_2+'"'+list+' },\n'; +var code = '{"type": "cgtext", "bg":"'+EvalString_0+'","memory":'+Bool_0+',"WindowSkin":'+Bool_1+',"head":'+head+' ,"index":"'+Int_0+'","time":'+Int_1+',"wait":'+Int_2+list+' },\n'; return code; */; textcgDrawingList diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js index 85647da..db8cf0b 100644 --- a/_server/MotaActionParser.js +++ b/_server/MotaActionParser.js @@ -574,7 +574,6 @@ MotaActionParser = function () { data.head.px || -300, data.time, data.wait, - data.sound, buildcgDrawing(data.bodyList), this.next, ]); diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index 6698265..358881b 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -269,7 +269,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "textarea", "_data": "法强", }, - "spelldef": { + "mdef": { "_leaf": true, "_type": "textarea", "_data": "法抗百分比", @@ -280,7 +280,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_data": "100为法强转化为等值魔攻", "_docs": "魔攻比例" }, - "mdef": { + "mhp": { "_leaf": true, "_type": "textarea", "_data": "100为法强转化为等值护盾", diff --git a/project/data.js b/project/data.js index 0920725..cf5aaac 100644 --- a/project/data.js +++ b/project/data.js @@ -437,18 +437,21 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "face_440110.webp", "green.webp", "hero.webp", + "jianji.webp", "l.webp", "lane1.webp", "light.webp", "lock.webp", "lr.webp", "maba.webp", + "miwu.webp", "null.webp", "other_0001.webp", "other_0002.webp", "other_0003.webp", "other_0004.webp", "r.webp", + "sound.webp", "status.webp", "suiji.webp", "tati_020101.webp", @@ -1116,7 +1119,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "atk": 100, "def": 100, "mdef": 0, - "speed": 0, + "speed": 10, "money": 0, "exp": 0, "equipment": [], @@ -1138,7 +1141,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "steps": 0, "matk": 0, "spell": 0, - "spelldef": 0 + "spelldef": 0, + "mhp": 0 }, "startCanvas": [ { @@ -1494,14 +1498,12 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = ], "startText": [ { - "type": "setValue", - "name": "item:book", - "value": "1" + "type": "function", + "function": "function(){\ncore.getItem('book',1);core.getItem('fly',1)\n}" }, { - "type": "setValue", - "name": "item:fly", - "value": "1" + "type": "insert", + "name": "战斗动画特效注册" }, { "type": "insert", diff --git a/project/enemys.js b/project/enemys.js index f4da758..8c80082 100644 --- a/project/enemys.js +++ b/project/enemys.js @@ -4,7 +4,7 @@ var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = "redSlime": {"name":"红头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"value":10,"magic":false}, "blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, "slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[1,9]}, - "bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1],"mdef":0,"speed":10}, + "bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1],"mdef":0,"speed":15}, "bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, "redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, "vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"special":[6],"n":8}, diff --git a/project/events.js b/project/events.js index 5c6c1a0..71a864c 100644 --- a/project/events.js +++ b/project/events.js @@ -456,7 +456,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "0", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -477,7 +476,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "1", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -498,7 +496,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "2", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -528,7 +525,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "3", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -549,7 +545,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "4", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -570,7 +565,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "5", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -591,7 +585,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "6", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -612,7 +605,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "7", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -633,7 +625,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "8", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -654,7 +645,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "9", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -675,7 +665,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "10", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -705,7 +694,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "11", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -726,7 +714,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "12", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -747,7 +734,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "13", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -768,7 +754,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "14", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -789,7 +774,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "15", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -810,7 +794,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "16", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -831,7 +814,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "17", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -852,7 +834,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "18", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -882,7 +863,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "19", "time": 30, "wait": 1000, - "sound": "aiy010000010.opus", "bodyList": [ { "name": "", @@ -912,7 +892,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "20", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -933,7 +912,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "21", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -963,7 +941,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "22", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -993,7 +970,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "23", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1014,7 +990,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "24", "time": 50, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1044,7 +1019,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "25", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1065,7 +1039,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "26", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1086,7 +1059,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "27", "time": 30, "wait": 2000, - "sound": "aiy010000020.opus", "bodyList": [ { "name": "", @@ -1107,7 +1079,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "28", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1128,7 +1099,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "29", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1149,7 +1119,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "30", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1170,7 +1139,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "31", "time": 30, "wait": 1000, - "sound": "aiy010000030.opus", "bodyList": [ { "name": "", @@ -1205,7 +1173,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "32", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1226,7 +1193,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "33", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1247,7 +1213,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "34", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1268,7 +1233,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "35", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1289,7 +1253,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "36", "time": 30, "wait": 1000, - "sound": "aiy710000010.opus", "bodyList": [ { "name": "", @@ -1310,7 +1273,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "37", "time": 30, "wait": 1000, - "sound": "aiy710000020.opus", "bodyList": [ { "name": "", @@ -1340,7 +1302,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "38", "time": 30, "wait": 1000, - "sound": "aiy710000030.opus", "bodyList": [ { "name": "", @@ -1361,7 +1322,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "39", "time": 30, "wait": 1000, - "sound": "aiy710000040.opus", "bodyList": [ { "name": "", @@ -1382,7 +1342,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "40", "time": 30, "wait": 1000, - "sound": "aiy710000050.opus", "bodyList": [ { "name": "", @@ -1412,7 +1371,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "41", "time": 30, "wait": 1000, - "sound": "aiy710000060.opus", "bodyList": [ { "name": "", @@ -1433,7 +1391,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "42", "time": 30, "wait": 1000, - "sound": "aiy310000010.opus", "bodyList": [ { "name": "", @@ -1454,7 +1411,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "43", "time": 30, "wait": 1000, - "sound": "aiy310000020.opus", "bodyList": [ { "name": "", @@ -1484,7 +1440,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "44", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1505,7 +1460,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "45", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1526,7 +1480,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "46", "time": 30, "wait": 1000, - "sound": "aiy350000010.opus", "bodyList": [ { "name": "", @@ -1547,7 +1500,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "47", "time": 30, "wait": 1000, - "sound": "aiy350000020.opus", "bodyList": [ { "name": "", @@ -1568,7 +1520,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "48", "time": 30, "wait": 1000, - "sound": "aiy310000030.opus", "bodyList": [ { "name": "", @@ -1589,7 +1540,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "49", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1610,7 +1560,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "50", "time": 30, "wait": 1000, - "sound": "aiy350000030.opus", "bodyList": [ { "name": "", @@ -1631,7 +1580,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "51", "time": 30, "wait": 1000, - "sound": "aiy310000040.opus", "bodyList": [ { "name": "", @@ -1652,7 +1600,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "52", "time": 30, "wait": 1000, - "sound": "aiy710000070.opus", "bodyList": [ { "name": "", @@ -1673,7 +1620,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "53", "time": 30, "wait": 1000, - "sound": "aiy350000040.opus", "bodyList": [ { "name": "", @@ -1694,7 +1640,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "54", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1715,7 +1660,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "55", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1736,7 +1680,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "56", "time": 30, "wait": 1000, - "sound": "aiy350000050.opus", "bodyList": [ { "name": "", @@ -1757,7 +1700,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "57", "time": 30, "wait": 1000, - "sound": "aiy350000060.opus", "bodyList": [ { "name": "", @@ -1778,7 +1720,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "58", "time": 30, "wait": 1000, - "sound": "aiy310000050.opus", "bodyList": [ { "name": "", @@ -1799,7 +1740,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "59", "time": 30, "wait": 1000, - "sound": "aiy350000070.opus", "bodyList": [ { "name": "", @@ -1820,7 +1760,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "60", "time": 30, "wait": 1000, - "sound": "aiy350000080.opus", "bodyList": [ { "name": "", @@ -1841,7 +1780,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "61", "time": 30, "wait": 1000, - "sound": "aiy820000010.opus", "bodyList": [ { "name": "", @@ -1862,7 +1800,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "62", "time": 30, "wait": 1000, - "sound": "aiy350000090.opus", "bodyList": [ { "name": "", @@ -1883,7 +1820,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "63", "time": 30, "wait": 1000, - "sound": "aiy820000020.opus", "bodyList": [ { "name": "", @@ -1904,7 +1840,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "64", "time": 30, "wait": 1000, - "sound": "aiy350000100.opus", "bodyList": [ { "name": "", @@ -1925,7 +1860,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "65", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1946,7 +1880,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "66", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1967,7 +1900,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "67", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -1988,7 +1920,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "68", "time": 30, "wait": 1000, - "sound": "aiy310000060.opus", "bodyList": [ { "name": "", @@ -2009,7 +1940,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "69", "time": 30, "wait": 1000, - "sound": "aiy310000070.opus", "bodyList": [ { "name": "", @@ -2030,7 +1960,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "70", "time": 30, "wait": 1000, - "sound": "aiy350000110.opus", "bodyList": [ { "name": "", @@ -2051,7 +1980,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "71", "time": 30, "wait": 1000, - "sound": "aiy350000120.opus", "bodyList": [ { "name": "", @@ -2072,7 +2000,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "72", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2093,7 +2020,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "73", "time": 30, "wait": 1000, - "sound": "aiy310000080.opus", "bodyList": [ { "name": "", @@ -2114,7 +2040,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "74", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2135,7 +2060,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "75", "time": 30, "wait": 1000, - "sound": "aiy350000130.opus", "bodyList": [ { "name": "", @@ -2156,7 +2080,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "76", "time": 30, "wait": 1000, - "sound": "aiy310000090.opus", "bodyList": [ { "name": "", @@ -2177,7 +2100,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "77", "time": 30, "wait": 1000, - "sound": "aiy310000100.opus", "bodyList": [ { "name": "", @@ -2198,7 +2120,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "78", "time": 30, "wait": 1000, - "sound": "aiy350000140.opus", "bodyList": [ { "name": "", @@ -2219,7 +2140,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "79", "time": 30, "wait": 1000, - "sound": "aiy350000150.opus", "bodyList": [ { "name": "", @@ -2240,7 +2160,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "80", "time": 30, "wait": 1000, - "sound": "aiy710000080.opus", "bodyList": [ { "name": "", @@ -2261,7 +2180,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "81", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2282,7 +2200,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "82", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2303,7 +2220,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "83", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2324,7 +2240,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "84", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2345,7 +2260,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "85", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2366,7 +2280,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "86", "time": 30, "wait": 1000, - "sound": "aiy710000090.opus", "bodyList": [ { "name": "", @@ -2387,7 +2300,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "87", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2408,7 +2320,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "88", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2429,7 +2340,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "89", "time": 30, "wait": 1000, - "sound": "aiy710000100.opus", "bodyList": [ { "name": "", @@ -2450,7 +2360,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "90", "time": 30, "wait": 1000, - "sound": "aiy350000160.opus", "bodyList": [ { "name": "", @@ -2471,7 +2380,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "91", "time": 30, "wait": 1000, - "sound": "aiy350000170.opus", "bodyList": [ { "name": "", @@ -2492,7 +2400,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "92", "time": 30, "wait": 1000, - "sound": "aiy710000110.opus", "bodyList": [ { "name": "", @@ -2513,7 +2420,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "93", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2534,7 +2440,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "94", "time": 30, "wait": 2000, - "sound": "aiy350000180.opus", "bodyList": [ { "name": "", @@ -2555,7 +2460,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "95", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2576,7 +2480,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "96", "time": 30, "wait": 1000, - "sound": "aiy350000190.opus", "bodyList": [ { "name": "", @@ -2597,7 +2500,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "97", "time": 30, "wait": 1000, - "sound": "aiy350000200.opus", "bodyList": [ { "name": "", @@ -2618,7 +2520,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "98", "time": 30, "wait": 1000, - "sound": "aiy710000120.opus", "bodyList": [ { "name": "", @@ -2639,7 +2540,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "99", "time": 30, "wait": 1000, - "sound": "aiy710000130.opus", "bodyList": [ { "name": "", @@ -2660,7 +2560,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "100", "time": 30, "wait": 1000, - "sound": "aiy350000210.opus", "bodyList": [ { "name": "", @@ -2681,7 +2580,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "101", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2702,7 +2600,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "102", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2723,7 +2620,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "103", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2744,7 +2640,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "104", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2765,7 +2660,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "105", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2786,7 +2680,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "106", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2807,7 +2700,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "107", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2828,7 +2720,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "108", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2849,7 +2740,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "109", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2870,7 +2760,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "110", "time": 30, "wait": 1000, - "sound": "aiy310000110.opus", "bodyList": [ { "name": "", @@ -2891,7 +2780,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "111", "time": 30, "wait": 1000, - "sound": "aiy350000220.opus", "bodyList": [ { "name": "", @@ -2912,7 +2800,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "112", "time": 30, "wait": 1000, - "sound": "aiy350000230.opus", "bodyList": [ { "name": "", @@ -2933,7 +2820,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "113", "time": 30, "wait": 1000, - "sound": "aiy310000120.opus", "bodyList": [ { "name": "", @@ -2954,7 +2840,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "114", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -2975,7 +2860,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "115", "time": 30, "wait": 1000, - "sound": "aiy310000130.opus", "bodyList": [ { "name": "", @@ -3005,7 +2889,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "116", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3026,7 +2909,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "117", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3047,7 +2929,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "118", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3068,7 +2949,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "119", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3089,7 +2969,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "120", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3110,7 +2989,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "121", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3131,7 +3009,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "122", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3152,7 +3029,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "123", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3173,7 +3049,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "124", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3194,7 +3069,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "125", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -3224,7 +3098,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "126", "time": 30, "wait": 1000, - "sound": "aiy310000140.opus", "bodyList": [ { "name": "", @@ -3273,7 +3146,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "0", "time": 30, "wait": 1000, - "sound": "aiy020000005.opus", "bodyList": [ { "name": "", @@ -3298,7 +3170,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "1", "time": 30, "wait": 1000, - "sound": "aiy020000010.opus", "bodyList": [ { "name": "tati_020141.webp", @@ -3319,7 +3190,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "2", "time": 30, "wait": 1000, - "sound": "aiy020000020.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3340,7 +3210,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "3", "time": 30, "wait": 1000, - "sound": "aiy310000150.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3361,7 +3230,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "4", "time": 30, "wait": 1000, - "sound": "aiy020000030.opus", "bodyList": [ { "name": "tati_020107.webp", @@ -3382,7 +3250,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "5", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_020107.webp", @@ -3403,7 +3270,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "6", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_020107.webp", @@ -3424,7 +3290,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "7", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_020107.webp", @@ -3445,7 +3310,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "8", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_020107.webp", @@ -3466,7 +3330,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "9", "time": 30, "wait": 1000, - "sound": "aiy020000040.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3487,7 +3350,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "10", "time": 30, "wait": 1000, - "sound": "aiy020000050.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3508,7 +3370,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "11", "time": 30, "wait": 1000, - "sound": "aiy310000160.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3529,7 +3390,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "12", "time": 30, "wait": 1000, - "sound": "aiy020000060.opus", "bodyList": [ { "name": "tati_020111.webp", @@ -3550,7 +3410,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "13", "time": 30, "wait": 1000, - "sound": "aiy310000170.opus", "bodyList": [ { "name": "tati_020111.webp", @@ -3571,7 +3430,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "14", "time": 30, "wait": 1000, - "sound": "aiy020000070.opus", "bodyList": [ { "name": "tati_020101.webp", @@ -3592,7 +3450,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "15", "time": 30, "wait": 1000, - "sound": "aiy310000180.opus", "bodyList": [ { "name": "tati_020101.webp", @@ -3613,7 +3470,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "16", "time": 30, "wait": 1000, - "sound": "aiy020000080.opus", "bodyList": [ { "name": "tati_020107.webp", @@ -3634,7 +3490,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "17", "time": 30, "wait": 1000, - "sound": "aiy310000190.opus", "bodyList": [ { "name": "tati_020107.webp", @@ -3655,7 +3510,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "18", "time": 30, "wait": 1000, - "sound": "aiy020000090.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3676,7 +3530,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "19", "time": 30, "wait": 1000, - "sound": "aiy310000200.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3697,7 +3550,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "20", "time": 30, "wait": 1000, - "sound": "aiy310000210.opus", "bodyList": [ { "name": "tati_020157.webp", @@ -3718,7 +3570,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "21", "time": 30, "wait": 1000, - "sound": "aiy020000100.opus", "bodyList": [ { "name": "tati_020125.webp", @@ -3739,7 +3590,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "22", "time": 30, "wait": 1000, - "sound": "aiy310000220.opus", "bodyList": [ { "name": "tati_020125.webp", @@ -3760,7 +3610,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "23", "time": 30, "wait": 1000, - "sound": "aiy020000110.opus", "bodyList": [ { "name": "tati_020121.webp", @@ -3781,7 +3630,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "24", "time": 30, "wait": 1000, - "sound": "aiy310000230.opus", "bodyList": [ { "name": "tati_020121.webp", @@ -3802,7 +3650,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "25", "time": 30, "wait": 1000, - "sound": "aiy310000240.opus", "bodyList": [ { "name": "tati_020121.webp", @@ -3823,7 +3670,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "26", "time": 30, "wait": 1000, - "sound": "aiy020000120.opus", "bodyList": [ { "name": "tati_020105.webp", @@ -3844,7 +3690,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "27", "time": 30, "wait": 1000, - "sound": "aiy020000130.opus", "bodyList": [ { "name": "tati_020103.webp", @@ -3865,7 +3710,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "28", "time": 30, "wait": 1000, - "sound": "aiy020000140.opus", "bodyList": [ { "name": "tati_020141.webp", @@ -3886,7 +3730,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "29", "time": 30, "wait": 1000, - "sound": "aiy310000250.opus", "bodyList": [ { "name": "tati_020141.webp", @@ -3907,7 +3750,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "30", "time": 30, "wait": 1000, - "sound": "aiy020000150.opus", "bodyList": [ { "name": "tati_020105.webp", @@ -3928,7 +3770,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "31", "time": 30, "wait": 1000, - "sound": "aiy310000260.opus", "bodyList": [ { "name": "tati_020105.webp", @@ -3949,7 +3790,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "32", "time": 30, "wait": 1000, - "sound": "aiy020000160.opus", "bodyList": [ { "name": "tati_020105.webp", @@ -3970,7 +3810,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "33", "time": 30, "wait": 1000, - "sound": "aiy020000170.opus", "bodyList": [ { "name": "tati_020107.webp", @@ -3991,7 +3830,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "34", "time": 30, "wait": 1000, - "sound": "aiy310000280.opus", "bodyList": [ { "name": "tati_020107.webp", @@ -4012,7 +3850,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "35", "time": 30, "wait": 1000, - "sound": "aiy020000180.opus", "bodyList": [ { "name": "tati_020145.webp", @@ -4033,7 +3870,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "36", "time": 30, "wait": 1000, - "sound": "aiy310000290.opus", "bodyList": [ { "name": "tati_020145.webp", @@ -4054,7 +3890,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "37", "time": 30, "wait": 1000, - "sound": "aiy310000300.opus", "bodyList": [ { "name": "tati_020145.webp", @@ -4099,7 +3934,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "0", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4120,7 +3954,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "1", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4141,7 +3974,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "2", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4162,7 +3994,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "3", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4183,7 +4014,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "4", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4204,7 +4034,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "5", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4225,7 +4054,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "6", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4246,7 +4074,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "7", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4267,7 +4094,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "8", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4288,7 +4114,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "9", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4318,7 +4143,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "10", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4339,7 +4163,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "11", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4360,7 +4183,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "12", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4381,7 +4203,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "13", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4402,7 +4223,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "14", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4423,7 +4243,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "15", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4444,7 +4263,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "16", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4474,7 +4292,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "17", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4495,7 +4312,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "18", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4516,7 +4332,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "19", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4550,7 +4365,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "20", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_440101.webp", @@ -4571,7 +4385,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "21", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_440101.webp", @@ -4592,7 +4405,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "22", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "tati_440101.webp", @@ -4613,7 +4425,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "23", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4634,7 +4445,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "24", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4655,7 +4465,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "25", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4676,7 +4485,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "26", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4697,7 +4505,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "27", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4718,7 +4525,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "28", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4739,7 +4545,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "29", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4786,7 +4591,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "30", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4807,7 +4611,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "31", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4832,7 +4635,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "32", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4853,7 +4655,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "33", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4874,7 +4675,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "34", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4895,7 +4695,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "35", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4916,7 +4715,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "36", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4937,7 +4735,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "37", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4958,7 +4755,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "38", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -4979,7 +4775,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "39", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5000,7 +4795,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "40", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5021,7 +4815,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "41", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5046,7 +4839,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "42", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5076,7 +4868,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "43", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5097,7 +4888,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "44", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5118,7 +4908,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "45", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5139,7 +4928,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "46", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5160,7 +4948,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "47", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5190,7 +4977,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "48", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5211,7 +4997,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "49", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5232,7 +5017,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "50", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5253,7 +5037,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "51", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5274,7 +5057,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "52", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5295,7 +5077,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "53", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5316,7 +5097,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "54", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5337,7 +5117,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "55", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5358,7 +5137,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "56", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5379,7 +5157,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "57", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5400,7 +5177,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "58", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5421,7 +5197,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "59", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5442,7 +5217,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "60", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5463,7 +5237,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "61", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5484,7 +5257,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "62", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5505,7 +5277,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "63", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5526,7 +5297,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "64", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5547,7 +5317,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "65", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5568,7 +5337,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "66", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5589,7 +5357,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "67", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5610,7 +5377,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "68", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5631,7 +5397,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "69", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5652,7 +5417,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "70", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5673,7 +5437,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "71", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5694,7 +5457,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "72", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5715,7 +5477,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "73", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5736,7 +5497,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "74", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5757,7 +5517,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "75", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5778,7 +5537,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "76", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5799,7 +5557,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "77", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5820,7 +5577,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "78", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5841,7 +5597,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "index": "79", "time": 30, "wait": 1000, - "sound": "", "bodyList": [ { "name": "", @@ -5858,10 +5613,6 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "memory2": false, "time": 30, "style": "引出" - }, - { - "type": "comment", - "text": "下一场景是追捕羽化病少年...做个标记" } ], "chapter03": [ @@ -10883,6 +10634,136 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = } ] } + ], + "战斗动画特效注册": [ + { + "type": "setanimate", + "name": "sword", + "px": 48, + "py": 48, + "width": 192, + "height": 192, + "allFarme": 15, + "imageList": [ + { + "image": "jianji.webp", + "beforefarme": 0, + "globalAlpha": 100, + "cx": 192, + "cy": 2112, + "cw": 192, + "ch": 192, + "x": 0, + "y": 0, + "w": 96, + "h": 96, + "afterfarme": 3, + "acx": 192, + "acy": 2112, + "acw": 192, + "ach": 192, + "ax": 0, + "ay": 0, + "aw": 96, + "ah": 96 + }, + { + "image": "jianji.webp", + "beforefarme": 4, + "globalAlpha": 100, + "cx": 384, + "cy": 2112, + "cw": 192, + "ch": 192, + "x": 0, + "y": 0, + "w": 96, + "h": 96, + "afterfarme": 6, + "acx": 384, + "acy": 2112, + "acw": 192, + "ach": 192, + "ax": 0, + "ay": 0, + "aw": 96, + "ah": 96 + }, + { + "image": "jianji.webp", + "beforefarme": 7, + "globalAlpha": 100, + "cx": 576, + "cy": 2112, + "cw": 192, + "ch": 192, + "x": 0, + "y": 0, + "w": 96, + "h": 96, + "afterfarme": 9, + "acx": 576, + "acy": 2112, + "acw": 192, + "ach": 192, + "ax": 0, + "ay": 0, + "aw": 96, + "ah": 96 + }, + { + "image": "jianji.webp", + "beforefarme": 10, + "globalAlpha": 100, + "cx": 768, + "cy": 2112, + "cw": 192, + "ch": 192, + "x": 0, + "y": 0, + "w": 96, + "h": 96, + "afterfarme": 12, + "acx": 768, + "acy": 2112, + "acw": 192, + "ach": 192, + "ax": 0, + "ay": 0, + "aw": 96, + "ah": 96 + }, + { + "image": "jianji.webp", + "beforefarme": 13, + "globalAlpha": 100, + "cx": 0, + "cy": 2304, + "cw": 192, + "ch": 192, + "x": 0, + "y": 0, + "w": 96, + "h": 96, + "afterfarme": 15, + "acx": 0, + "acy": 2304, + "acw": 192, + "ach": 192, + "ax": 0, + "ay": 0, + "aw": 96, + "ah": 96 + } + ], + "soundList": [ + { + "sound": "", + "startfarme": 0, + "stopbefore": false + } + ] + } ] }, "CommonEventTemplate": { diff --git a/project/images/jianji.webp b/project/images/jianji.webp new file mode 100644 index 0000000..40504a4 Binary files /dev/null and b/project/images/jianji.webp differ diff --git a/project/images/miwu.webp b/project/images/miwu.webp new file mode 100644 index 0000000..84e72cc Binary files /dev/null and b/project/images/miwu.webp differ diff --git a/project/images/sound.webp b/project/images/sound.webp new file mode 100644 index 0000000..1fed120 Binary files /dev/null and b/project/images/sound.webp differ diff --git a/project/plugins.js b/project/plugins.js index 170978f..27fd1e2 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -207,7 +207,10 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.ui.cgText.time = data.time; core.ui.cgText.wait = data.wait; core.ui.cgText.WindowSkin = data.WindowSkin; - core.ui.cgText.sound = data.sound || ""; + core.ui.cgText.sound = + data.sound === "" ? + data.sound : + core.ui.cgText.textList[data.index][2] || ""; core.ui.cgText.bodyList = core.clone(data.bodyList); main.dom.cgText.style.display = "block"; core.ui.cgText.update(); @@ -2520,1799 +2523,1804 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = })(); }, "statusBar": function () { - main.dom.floorMsgGroup.style.display = "none"; - main.dom.statusBar.style.display = "none"; - main.dom.toolBar.style.display = "none"; - //所有数据*3是为了实现高清画布 - const GAMEVIEW_WIDTH = 676 * 3; //横屏画面宽度 - const GAMEVIEW_HEIGHT = 416 * 3; //横屏画面高度 + main.dom.floorMsgGroup.style.display = "none"; + main.dom.statusBar.style.display = "none"; + main.dom.toolBar.style.display = "none"; + //所有数据*3是为了实现高清画布 + const GAMEVIEW_WIDTH = 676 * 3; //横屏画面宽度 + const GAMEVIEW_HEIGHT = 416 * 3; //横屏画面高度 - const GAMEVIEW_WIDTH_VERTICAL = 416 * 3; //竖屏画面宽度 - const GAMEVIEW_HEIGHT_VERTICAL = 676 * 3; //竖屏画面高度 + const GAMEVIEW_WIDTH_VERTICAL = 416 * 3; //竖屏画面宽度 + const GAMEVIEW_HEIGHT_VERTICAL = 676 * 3; //竖屏画面高度 - const BAR_WIDTH = 130 * 3; //横屏左侧额外距离(即边栏宽度) - const BAR_HEIGHT_VERTICAL = 130 * 3; //竖屏上侧额外距离(即边栏高度) - const BORDER_WIDTH = 0; //游戏画面左侧偏移距离 - const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离 + const BAR_WIDTH = 130 * 3; //横屏左侧额外距离(即边栏宽度) + const BAR_HEIGHT_VERTICAL = 130 * 3; //竖屏上侧额外距离(即边栏高度) + const BORDER_WIDTH = 0; //游戏画面左侧偏移距离 + const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离 - const ITEM_BOX_LEFT = 549 * 3; //横屏道具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const ITEM_BOX_TOP = 155 * 3; //横屏道具栏上侧距离 - const ITEM_BOX_LEFT_VERTICAL = 160 * 3; //竖屏道具栏左侧距离 - const ITEM_BOX_TOP_VERTICAL = 549 * 3; //竖屏道具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const ITEM_BOX_LEFT = 549 * 3; //横屏道具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const ITEM_BOX_TOP = 155 * 3; //横屏道具栏上侧距离 + const ITEM_BOX_LEFT_VERTICAL = 160 * 3; //竖屏道具栏左侧距离 + const ITEM_BOX_TOP_VERTICAL = 549 * 3; //竖屏道具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const EQUIP_BLOCK_LEFT = 549 * 3; //横屏装备栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const EQUIP_BLOCK_TOP = 10 * 3; //横屏装备栏上侧距离 - const EQUIP_BLOCK_LEFT_VERTICAL = 10 * 3; //竖屏装备栏左侧距离 - const EQUIP_BLOCK_TOP_VERTICAL = 549 * 3; //竖屏装备栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const EQUIP_BLOCK_LEFT = 549 * 3; //横屏装备栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const EQUIP_BLOCK_TOP = 10 * 3; //横屏装备栏上侧距离 + const EQUIP_BLOCK_LEFT_VERTICAL = 10 * 3; //竖屏装备栏左侧距离 + const EQUIP_BLOCK_TOP_VERTICAL = 549 * 3; //竖屏装备栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const MAP_BLOCK_LEFT = 551 * 3; //横屏小地图左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const MAP_BLOCK_TOP = 0; //横屏小地图上侧距离 - const MAP_BLOCK_LEFT_VERTICAL = 0; //竖屏小地图左侧距离 - const MAP_BLOCK_TOP_VERTICAL = 551 * 3; //竖屏小地图上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const MAP_BLOCK_LEFT = 551 * 3; //横屏小地图左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const MAP_BLOCK_TOP = 0; //横屏小地图上侧距离 + const MAP_BLOCK_LEFT_VERTICAL = 0; //竖屏小地图左侧距离 + const MAP_BLOCK_TOP_VERTICAL = 551 * 3; //竖屏小地图上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT; //横屏钥匙栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const KEY_BLOCK_TOP = 110 * 3; //横屏钥匙栏上侧距离 - const KEY_BLOCK_LEFT_VERTICAL = 110 * 3; //竖屏钥匙栏左侧距离 - const KEY_BLOCK_TOP_VERTICAL = EQUIP_BLOCK_TOP_VERTICAL; //竖屏钥匙栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT; //横屏钥匙栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const KEY_BLOCK_TOP = 110 * 3; //横屏钥匙栏上侧距离 + const KEY_BLOCK_LEFT_VERTICAL = 110 * 3; //竖屏钥匙栏左侧距离 + const KEY_BLOCK_TOP_VERTICAL = EQUIP_BLOCK_TOP_VERTICAL; //竖屏钥匙栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const INFO_BLOCK_LEFT = 10 * 3; //横屏道具说明左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const INFO_BLOCK_TOP = 180 * 3; //横屏道具说明上侧距离 - const INFO_BLOCK_LEFT_VERTICAL = 113 * 3; //竖屏道具说明左侧距离 - const INFO_BLOCK_TOP_VERTICAL = 8 * 3; //竖屏道具说明上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const INFO_BLOCK_LEFT = 10 * 3; //横屏道具说明左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const INFO_BLOCK_TOP = 180 * 3; //横屏道具说明上侧距离 + const INFO_BLOCK_LEFT_VERTICAL = 113 * 3; //竖屏道具说明左侧距离 + const INFO_BLOCK_TOP_VERTICAL = 8 * 3; //竖屏道具说明上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT; //横屏工具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const TOOL_BOX_TOP = 348 * 3; //横屏工具栏上侧距离 - const TOOL_BOX_LEFT_VERTICAL = 348 * 3; //竖屏工具栏左侧距离 - const TOOL_BOX_TOP_VERTICAL = 549 * 3; //竖屏工具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT; //横屏工具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const TOOL_BOX_TOP = 348 * 3; //横屏工具栏上侧距离 + const TOOL_BOX_LEFT_VERTICAL = 348 * 3; //竖屏工具栏左侧距离 + const TOOL_BOX_TOP_VERTICAL = 549 * 3; //竖屏工具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const TOOL_ICON_OUTER_SIZE = 34 * 3; + const TOOL_ICON_OUTER_SIZE = 34 * 3; - const TEXT_COLOR = "#FFFFFF"; //默认文字颜色 - const globalAlpha = 0.7; //默认底框透明度 - const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具,如果道具不在此数组中,则只有道具多余1时显示数量 + const TEXT_COLOR = "#FFFFFF"; //默认文字颜色 + const globalAlpha = 0.7; //默认底框透明度 + const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具,如果道具不在此数组中,则只有道具多余1时显示数量 - const outerBackground = document.createElement("canvas"); //背景画布设置 - let globalAlphafloor = 0, - globalAlphafloorStatus = 4; - outerBackground.style.position = "absolute"; - outerBackground.style.zIndex = 5; - outerBackground.id = "outerBackground"; - main.dom.outerBackground = outerBackground; - main.dom.startPanel.insertAdjacentElement("afterend", outerBackground); + const outerBackground = document.createElement("canvas"); //背景画布设置 + let globalAlphafloor = 0, + globalAlphafloorStatus = 4; + outerBackground.style.position = "absolute"; + outerBackground.style.zIndex = 5; + outerBackground.id = "outerBackground"; + main.dom.outerBackground = outerBackground; + main.dom.startPanel.insertAdjacentElement("afterend", outerBackground); - const outerUI = document.createElement("canvas"); //额外ui画布设置(状态栏所有绘制、点击都在额外ui上) - outerUI.style.position = "absolute"; - outerUI.style.zIndex = 165; - outerUI.id = "outerUI"; + const outerUI = document.createElement("canvas"); //额外ui画布设置(状态栏所有绘制、点击都在额外ui上) + outerUI.style.position = "absolute"; + outerUI.style.zIndex = 165; + outerUI.id = "outerUI"; - main.dom.outerUI = outerUI; - outerBackground.insertAdjacentElement("afterend", outerUI); - setTimeout(function () { - // Should be executed immediately after init() - main.canvas.outerUI = outerUI.getContext("2d"); - }); - outerUI.onclick = function (e) { - try { - e.preventDefault(); - if (!core.isPlaying()) return false; - 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.statusBar.onclick(px * 3, py * 3); - } catch (ee) { - main.log(ee); - } - }; + main.dom.outerUI = outerUI; + outerBackground.insertAdjacentElement("afterend", outerUI); + setTimeout(function () { + // Should be executed immediately after init() + main.canvas.outerUI = outerUI.getContext("2d"); + }); + outerUI.onclick = function (e) { + try { + e.preventDefault(); + if (!core.isPlaying()) return false; + 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.statusBar.onclick(px * 3, py * 3); + } catch (ee) { + main.log(ee); + } + }; - const _resize_gameGroup = function (obj) { - //游戏画面自适应调节 - const gameGroup = core.dom.gameGroup; - gameGroup.style.width = obj.totalWidth + "px"; - gameGroup.style.height = obj.totalHeight + "px"; - gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px"; - gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px"; - //floorMsgGroup为切换楼层中生效,显示时间可通过‘全塔属性’——‘切换楼层时间’或游戏内设置调整 - //显示内容为游戏名/版本号/楼层名 - // floorMsgGroup - var floorMsgGroup = core.dom.floorMsgGroup; - var globalAttribute = - core.status.globalAttribute || core.initStatus.globalAttribute; - floorMsgGroup.style = globalAttribute.floorChangingStyle; - floorMsgGroup.style.height = floorMsgGroup.style.width = - (GAMEVIEW_HEIGHT / 3) * core.domStyle.scale + "px"; - floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; + const _resize_gameGroup = function (obj) { + //游戏画面自适应调节 + const gameGroup = core.dom.gameGroup; + gameGroup.style.width = obj.totalWidth + "px"; + gameGroup.style.height = obj.totalHeight + "px"; + gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px"; + //floorMsgGroup为切换楼层中生效,显示时间可通过‘全塔属性’——‘切换楼层时间’或游戏内设置调整 + //显示内容为游戏名/版本号/楼层名 + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + var globalAttribute = + core.status.globalAttribute || core.initStatus.globalAttribute; + floorMsgGroup.style = globalAttribute.floorChangingStyle; + floorMsgGroup.style.height = floorMsgGroup.style.width = + (GAMEVIEW_HEIGHT / 3) * core.domStyle.scale + "px"; + floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; - if (core.domStyle.isVertical) { - floorMsgGroup.style.left = "0px"; - floorMsgGroup.style.top = - ((GAMEVIEW_HEIGHT_VERTICAL / 3 - GAMEVIEW_WIDTH_VERTICAL / 3) * - core.domStyle.scale) / - 2 + - "px"; - } else { - floorMsgGroup.style.left = - ((GAMEVIEW_WIDTH / 3 - GAMEVIEW_HEIGHT / 3) * core.domStyle.scale) / - 2 + - "px"; - floorMsgGroup.style.top = "0px"; - } - core.dom.musicBtn.style.right = - (obj.clientWidth - obj.totalWidth) / 2 + "px"; - core.dom.musicBtn.style.bottom = - (obj.clientHeight - obj.totalHeight) / 2 - 27 + "px"; - let startBackground = core.domStyle.isVertical ? - main.styles.startVerticalBackground || main.styles.startBackground : - main.styles.startBackground; - if (main.dom.startBackground.getAttribute("__src__") != startBackground) { - main.dom.startBackground.setAttribute("__src__", startBackground); - main.dom.startBackground.src = startBackground; - } - const span = document - .getElementById("startButtons") - .getElementsByTagName("span"); - let font = (GAMEVIEW_WIDTH / 100) * core.domStyle.scale; - if (core.domStyle.isVertical) - font = ((GAMEVIEW_WIDTH_VERTICAL * 2) / 100) * core.domStyle.scale; + if (core.domStyle.isVertical) { + floorMsgGroup.style.left = "0px"; + floorMsgGroup.style.top = + ((GAMEVIEW_HEIGHT_VERTICAL / 3 - GAMEVIEW_WIDTH_VERTICAL / 3) * + core.domStyle.scale) / + 2 + + "px"; + } else { + floorMsgGroup.style.left = + ((GAMEVIEW_WIDTH / 3 - GAMEVIEW_HEIGHT / 3) * core.domStyle.scale) / + 2 + + "px"; + floorMsgGroup.style.top = "0px"; + } + core.dom.musicBtn.style.right = + (obj.clientWidth - obj.totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = + (obj.clientHeight - obj.totalHeight) / 2 - 27 + "px"; + let startBackground = core.domStyle.isVertical + ? main.styles.startVerticalBackground || main.styles.startBackground + : main.styles.startBackground; + if (main.dom.startBackground.getAttribute("__src__") != startBackground) { + main.dom.startBackground.setAttribute("__src__", startBackground); + main.dom.startBackground.src = startBackground; + } + const span = document + .getElementById("startButtons") + .getElementsByTagName("span"); + let font = (GAMEVIEW_WIDTH / 100) * core.domStyle.scale; + if (core.domStyle.isVertical) + font = ((GAMEVIEW_WIDTH_VERTICAL * 2) / 100) * core.domStyle.scale; - core.dom.playGame.style.fontSize = font + "px"; - core.dom.loadGame.style.fontSize = font + "px"; - core.dom.CGMode.style.fontSize = font + "px"; - core.dom.musicMode.style.fontSize = font + "px"; - core.dom.replayGame.style.fontSize = font + "px"; - core.dom.startButtonGroup.style.padding = font * 0.3 + "px 25px"; - }; - const _resize_canvas = function (obj) { - //自适应画布 - main.dom.outerBackground.style.width = obj.totalWidth + "px"; - main.dom.outerBackground.style.height = obj.totalHeight + "px"; - main.dom.outerUI.style.width = obj.totalWidth + "px"; - main.dom.outerUI.style.height = obj.totalHeight + "px"; - if (main.dom.CGUI) { - main.dom.CGUI.style.width = obj.totalWidth + 3 + "px"; - main.dom.CGUI.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.music) { - main.dom.music.style.width = obj.totalWidth + 3 + "px"; - main.dom.music.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.cgText) { - main.dom.cgText.style.width = obj.totalWidth + 3 + "px"; - main.dom.cgText.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.logcanvas) { - main.dom.logcanvas.style.width = obj.totalWidth + 3 + "px"; - main.dom.logcanvas.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.over) { - main.dom.over.style.width = obj.totalWidth + 3 + "px"; - main.dom.over.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.video) { - main.dom.video.style.width = obj.totalWidth + 3 + "px"; - main.dom.video.style.height = obj.totalHeight + 3 + "px"; - if (core.domStyle.isVertical) - main.dom.video.style.width = obj.totalHeight + 3 + "px"; - if (core.domStyle.isVertical) - main.dom.video.style.height = obj.totalWidth + 3 + "px"; - main.dom.video.style.top = "50%"; - main.dom.video.style.left = "50%"; + core.dom.playGame.style.fontSize = font + "px"; + core.dom.loadGame.style.fontSize = font + "px"; + core.dom.CGMode.style.fontSize = font + "px"; + core.dom.musicMode.style.fontSize = font + "px"; + core.dom.replayGame.style.fontSize = font + "px"; + core.dom.startButtonGroup.style.padding = font * 0.3 + "px 25px"; + }; + const _resize_canvas = function (obj) { + //自适应画布 + main.dom.outerBackground.style.width = obj.totalWidth + "px"; + main.dom.outerBackground.style.height = obj.totalHeight + "px"; + main.dom.outerUI.style.width = obj.totalWidth + "px"; + main.dom.outerUI.style.height = obj.totalHeight + "px"; + if (main.dom.CGUI) { + main.dom.CGUI.style.width = obj.totalWidth + 3 + "px"; + main.dom.CGUI.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.music) { + main.dom.music.style.width = obj.totalWidth + 3 + "px"; + main.dom.music.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.cgText) { + main.dom.cgText.style.width = obj.totalWidth + 3 + "px"; + main.dom.cgText.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.logcanvas) { + main.dom.logcanvas.style.width = obj.totalWidth + 3 + "px"; + main.dom.logcanvas.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.over) { + main.dom.over.style.width = obj.totalWidth + 3 + "px"; + main.dom.over.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.video) { + main.dom.video.style.width = obj.totalWidth + 3 + "px"; + main.dom.video.style.height = obj.totalHeight + 3 + "px"; + if (core.domStyle.isVertical) + main.dom.video.style.width = obj.totalHeight + 3 + "px"; + if (core.domStyle.isVertical) + main.dom.video.style.height = obj.totalWidth + 3 + "px"; + main.dom.video.style.top = "50%"; + main.dom.video.style.left = "50%"; - main.dom.video.style.transform = "translate(-50%,-50%)"; + main.dom.video.style.transform = "translate(-50%,-50%)"; - if (core.domStyle.isVertical) - main.dom.video.style.transform = "translate(-50%,-50%) rotate(90deg)"; - } - if (main.dom.video1) { - main.dom.video1.style.width = obj.totalWidth + 3 + "px"; - main.dom.video1.style.height = obj.totalHeight + 3 + "px"; - } + if (core.domStyle.isVertical) + main.dom.video.style.transform = "translate(-50%,-50%) rotate(90deg)"; + } + if (main.dom.video1) { + main.dom.video1.style.width = obj.totalWidth + 3 + "px"; + main.dom.video1.style.height = obj.totalHeight + 3 + "px"; + } - const innerSize = obj.canvasWidth * core.domStyle.scale + "px"; - for (let i = 0; i < core.dom.gameCanvas.length; ++i) - core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[ - i - ].style.height = innerSize; - core.dom.gif.style.width = core.dom.gif.style.height = innerSize; - core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; + const innerSize = obj.canvasWidth * core.domStyle.scale + "px"; + for (let i = 0; i < core.dom.gameCanvas.length; ++i) + core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[ + i + ].style.height = innerSize; + core.dom.gif.style.width = core.dom.gif.style.height = innerSize; + core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; - core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = - innerSize; - core.dom.gameDraw.style.top = - obj.gameDrawBox.top * core.domStyle.scale + "px"; - core.dom.gameDraw.style.left = - obj.gameDrawBox.left * core.domStyle.scale + "px"; - // resize bigmap - core.bigmap.canvas.forEach(function (cn) { - const ratio = core.canvas[cn].canvas.hasAttribute("isHD") ? - core.domStyle.ratio : - 1; - core.canvas[cn].canvas.style.width = - (innerSize / ratio) * core.domStyle.scale + "px"; - core.canvas[cn].canvas.style.height = - (innerSize / ratio) * core.domStyle.scale + "px"; - }); - // resize dynamic canvas - for (const name in core.dymCanvas) { - const ctx = core.dymCanvas[name], - canvas = ctx.canvas; - const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio : 1; - canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px"; - canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px"; - canvas.style.left = - parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; - canvas.style.top = - parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; - } - // resize next - main.dom.next.style.width = main.dom.next.style.height = - 5 * core.domStyle.scale + "px"; - main.dom.next.style.borderBottomWidth = - main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; - }; - const bgctx = main.dom.outerBackground.getContext("2d"); - const uictx = main.dom.outerUI.getContext("2d"); - let now = 0; - core.registerAnimationFrame("lightFloor", true, function (timestamp) { - if (timestamp - now > 1000 / 60) { - now = timestamp; - globalAlphafloor += globalAlphafloorStatus; - if (globalAlphafloor === 100) globalAlphafloorStatus = -2; - if (globalAlphafloor === 0) globalAlphafloorStatus = 2; + core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = + innerSize; + core.dom.gameDraw.style.top = + obj.gameDrawBox.top * core.domStyle.scale + "px"; + core.dom.gameDraw.style.left = + obj.gameDrawBox.left * core.domStyle.scale + "px"; + // resize bigmap + core.bigmap.canvas.forEach(function (cn) { + const ratio = core.canvas[cn].canvas.hasAttribute("isHD") + ? core.domStyle.ratio + : 1; + core.canvas[cn].canvas.style.width = + (innerSize / ratio) * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = + (innerSize / ratio) * core.domStyle.scale + "px"; + }); + // resize dynamic canvas + for (const name in core.dymCanvas) { + const ctx = core.dymCanvas[name], + canvas = ctx.canvas; + const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio : 1; + canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px"; + canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px"; + canvas.style.left = + parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = + parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + } + // resize next + main.dom.next.style.width = main.dom.next.style.height = + 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = + main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; + }; + const bgctx = main.dom.outerBackground.getContext("2d"); + const uictx = main.dom.outerUI.getContext("2d"); + let now = 0; + core.registerAnimationFrame("lightFloor", true, function (timestamp) { + if (timestamp - now > 1000 / 60) { + now = timestamp; + globalAlphafloor += globalAlphafloorStatus; + if (globalAlphafloor === 100) globalAlphafloorStatus = -2; + if (globalAlphafloor === 0) globalAlphafloorStatus = 2; - if (core.domStyle.isVertical) { - core.clearMap( - uictx, - MAP_BLOCK_LEFT_VERTICAL, - MAP_BLOCK_TOP_VERTICAL, - 340, - 360 - ); - if (core.status.event.id === "viewMaps") { - core.ui.statusBar._update_map(core.status.event.data.floorId); - } else { - core.ui.statusBar._update_map(); - } + if (core.domStyle.isVertical) { + core.clearMap( + uictx, + MAP_BLOCK_LEFT_VERTICAL, + MAP_BLOCK_TOP_VERTICAL, + 340, + 360 + ); + if (core.status.event.id === "viewMaps") { + core.ui.statusBar._update_map(core.status.event.data.floorId); + } else { + core.ui.statusBar._update_map(); + } - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT_VERTICAL + 135, - MAP_BLOCK_TOP_VERTICAL + 170 - ); - uictx.globalAlpha = 1; - } else { - core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); - if (core.status.event.id === "viewMaps") { - core.ui.statusBar._update_map(core.status.event.data.floorId); - } else { - core.ui.statusBar._update_map(); - } - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT + 150, - MAP_BLOCK_TOP + 180 - ); - uictx.globalAlpha = 1; - } - } - }); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT_VERTICAL + 135, + MAP_BLOCK_TOP_VERTICAL + 170 + ); + uictx.globalAlpha = 1; + } else { + core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); + if (core.status.event.id === "viewMaps") { + core.ui.statusBar._update_map(core.status.event.data.floorId); + } else { + core.ui.statusBar._update_map(); + } + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT + 150, + MAP_BLOCK_TOP + 180 + ); + uictx.globalAlpha = 1; + } + } + }); - core.control.resize = function () { - //自适应,可实现横竖屏切换 - if (main.mode == "editor") return; + core.control.resize = function () { + //自适应,可实现横竖屏切换 + if (main.mode == "editor") return; - const clientWidth = main.dom.body.clientWidth, - clientHeight = main.dom.body.clientHeight; - const canvasWidth = core.__PIXELS__; + const clientWidth = main.dom.body.clientWidth, + clientHeight = main.dom.body.clientHeight; + const canvasWidth = core.__PIXELS__; - const isVertical = clientHeight > clientWidth; - core.domStyle.isVertical = isVertical; + const isVertical = clientHeight > clientWidth; + core.domStyle.isVertical = isVertical; - const totalWidth = isVertical ? - GAMEVIEW_WIDTH_VERTICAL / 3 : - GAMEVIEW_WIDTH / 3, - totalHeight = isVertical ? - GAMEVIEW_HEIGHT_VERTICAL / 3 : - GAMEVIEW_HEIGHT / 3; + const totalWidth = isVertical + ? GAMEVIEW_WIDTH_VERTICAL / 3 + : GAMEVIEW_WIDTH / 3, + totalHeight = isVertical + ? GAMEVIEW_HEIGHT_VERTICAL / 3 + : GAMEVIEW_HEIGHT / 3; - const maxRatio = Math.min( - clientWidth / totalWidth, - clientHeight / totalHeight - ); + const maxRatio = Math.min( + clientWidth / totalWidth, + clientHeight / totalHeight + ); - core.domStyle.availableScale = []; - [1, 1.25, 1.5, 1.75, 2].forEach(function (v) { - if (maxRatio >= v) { - core.domStyle.availableScale.push(v); - } - }); + core.domStyle.availableScale = []; + [1, 1.25, 1.5, 1.75, 2].forEach(function (v) { + if (maxRatio >= v) { + core.domStyle.availableScale.push(v); + } + }); - if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { - core.domStyle.scale = Math.min(1, maxRatio); - } else if ( - core.getLocalStorage("scale") == null && - core.domStyle.availableScale.length >= 2 - ) { - core.domStyle.scale = - core.domStyle.availableScale[core.domStyle.availableScale.length - 2]; - core.setLocalStorage("scale", core.domStyle.scale); - } + if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { + core.domStyle.scale = Math.min(1, maxRatio); + } else if ( + core.getLocalStorage("scale") == null && + core.domStyle.availableScale.length >= 2 + ) { + core.domStyle.scale = + core.domStyle.availableScale[core.domStyle.availableScale.length - 2]; + core.setLocalStorage("scale", core.domStyle.scale); + } - const totalWidthScaled = totalWidth * core.domStyle.scale, - totalHeightScaled = totalHeight * core.domStyle.scale; + const totalWidthScaled = totalWidth * core.domStyle.scale, + totalHeightScaled = totalHeight * core.domStyle.scale; - const gameDrawBox = isVertical ? { - left: BORDER_WIDTH / 3, - top: BAR_HEIGHT_VERTICAL / 3 + BORDER_HEIGHT / 3, - } : { left: BAR_WIDTH / 3 + BORDER_WIDTH / 3, top: BORDER_HEIGHT / 3 }; + const gameDrawBox = isVertical + ? { + left: BORDER_WIDTH / 3, + top: BAR_HEIGHT_VERTICAL / 3 + BORDER_HEIGHT / 3, + } + : { left: BAR_WIDTH / 3 + BORDER_WIDTH / 3, top: BORDER_HEIGHT / 3 }; - const obj = { - clientWidth: clientWidth, - clientHeight: clientHeight, - canvasWidth: canvasWidth, - totalWidth: totalWidthScaled, - totalHeight: totalHeightScaled, - gameDrawBox: gameDrawBox, - globalAttribute: core.status.globalAttribute || core.initStatus.globalAttribute, - }; + const obj = { + clientWidth: clientWidth, + clientHeight: clientHeight, + canvasWidth: canvasWidth, + totalWidth: totalWidthScaled, + totalHeight: totalHeightScaled, + gameDrawBox: gameDrawBox, + globalAttribute: + core.status.globalAttribute || core.initStatus.globalAttribute, + }; - _resize_gameGroup(obj); - _resize_canvas(obj); + _resize_gameGroup(obj); + _resize_canvas(obj); - if (core.status.automaticRoute == null) core.status.automaticRoute = {}; - core.updateStatusBar(); - if (main.dom.CGUI && main.dom.CGUI.style.display === "block") - core.ui.CG.update(); - if (main.dom.music && main.dom.music.style.display === "block") - core.ui.music.update(); - if (main.dom.cgText && main.dom.cgText.style.display === "block") - core.ui.cgText.update(); - if (main.dom.logcanvas && main.dom.logcanvas.style.display === "block") - core.ui.cgText.update(); - }; + if (core.status.automaticRoute == null) core.status.automaticRoute = {}; + core.updateStatusBar(); + if (main.dom.CGUI && main.dom.CGUI.style.display === "block") + core.ui.CG.update(); + if (main.dom.music && main.dom.music.style.display === "block") + core.ui.music.update(); + if (main.dom.cgText && main.dom.cgText.style.display === "block") + core.ui.cgText.update(); + if (main.dom.logcanvas && main.dom.logcanvas.style.display === "block") + core.ui.cgText.update(); + }; - class StatusBar { - constructor() { - //道具栏列表 - this.itemMx = [ - //空位用‘none’填充,当前ui至多4列6行 - ["book", "wand", "none", "fly"], - ["cross", "superPotion", "pickaxe"], - ["bomb", "centerFly", "upFly"], - ["none", "none", "none"], - ["downFly", "knife", "snow"], - ["bigKey", "earthquake", "coin"], - ]; - } - //初始化内容(工具栏/录像操作执行函数) - init() { - this.toolbarAction = [ - [ - main.core.openKeyBoard, - main.core.openQuickShop, - core.openToolbox, - core.doSL, - ], - [main.core.openSettings, main.core.save, main.core.load, core.doSL], - ]; - this.replayAction = [ - [core.triggerReplay, core.stopReplay, core.rewindReplay], - [core.speedDownReplay, core.speedUpReplay, core.saveReplay], - ]; - } - //更新 - update() { - this._update_background(); //更新背景 - this._update_props(); //更新属性 - //this._update_items(); //更新道具 - //this._update_equips(); //更新装备 - //this._update_keys(); //更新钥匙 - //this._update_infoWindow(); //更新道具说明 - this._update_toolBox(); //更新工具栏 - this._redrawMap(); - } - _redrawMap() { - if (core.domStyle.isVertical) { - core.clearMap( - uictx, - MAP_BLOCK_LEFT_VERTICAL, - MAP_BLOCK_TOP_VERTICAL, - 340, - 360 - ); - this._update_map(); - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT_VERTICAL + 125, - MAP_BLOCK_TOP_VERTICAL + 170 - ); - uictx.globalAlpha = 1; - } else { - core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); - this._update_map(); - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT + 150, - MAP_BLOCK_TOP + 170 - ); - uictx.globalAlpha = 1; - } - } - //更新背景 - _update_background() { - if (core.domStyle.isVertical) { - bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; - bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; - uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; - uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; + class StatusBar { + constructor() { + //道具栏列表 + this.itemMx = [ + //空位用‘none’填充,当前ui至多4列6行 + ["book", "wand", "none", "fly"], + ["cross", "superPotion", "pickaxe"], + ["bomb", "centerFly", "upFly"], + ["none", "none", "none"], + ["downFly", "knife", "snow"], + ["bigKey", "earthquake", "coin"], + ]; + } + //初始化内容(工具栏/录像操作执行函数) + init() { + this.toolbarAction = [ + [ + main.core.openKeyBoard, + main.core.openQuickShop, + core.openToolbox, + core.doSL, + ], + [main.core.openSettings, main.core.save, main.core.load, core.doSL], + ]; + this.replayAction = [ + [core.triggerReplay, core.stopReplay, core.rewindReplay], + [core.speedDownReplay, core.speedUpReplay, core.saveReplay], + ]; + } + //更新 + update() { + this._update_background(); //更新背景 + this._update_props(); //更新属性 + //this._update_items(); //更新道具 + //this._update_equips(); //更新装备 + //this._update_keys(); //更新钥匙 + //this._update_infoWindow(); //更新道具说明 + this._update_toolBox(); //更新工具栏 + this._redrawMap(); + } + _redrawMap() { + if (core.domStyle.isVertical) { + core.clearMap( + uictx, + MAP_BLOCK_LEFT_VERTICAL, + MAP_BLOCK_TOP_VERTICAL, + 340, + 360 + ); + this._update_map(); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT_VERTICAL + 125, + MAP_BLOCK_TOP_VERTICAL + 170 + ); + uictx.globalAlpha = 1; + } else { + core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); + this._update_map(); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT + 150, + MAP_BLOCK_TOP + 170 + ); + uictx.globalAlpha = 1; + } + } + //更新背景 + _update_background() { + if (core.domStyle.isVertical) { + bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; + bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; + uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; + uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; - const bg = core.material.images.images["status.webp"]; //竖屏背景(上) - bgctx.drawImage( - bg, - 0, - 0, - GAMEVIEW_WIDTH_VERTICAL, - BAR_HEIGHT_VERTICAL - ); - const bg2 = core.material.images.images["status.webp"]; //竖屏背景(下) - bgctx.drawImage( - bg2, - 0, - BAR_HEIGHT_VERTICAL + GAMEVIEW_WIDTH_VERTICAL, - GAMEVIEW_WIDTH_VERTICAL, - BAR_HEIGHT_VERTICAL - ); - bgctx.globalAlpha = globalAlpha; - bgctx.globalAlpha = 1; - core.setTextAlign("outerUI", "center"); - } else { - bgctx.canvas.width = GAMEVIEW_WIDTH; - bgctx.canvas.height = GAMEVIEW_HEIGHT; - uictx.canvas.width = GAMEVIEW_WIDTH; - uictx.canvas.height = GAMEVIEW_HEIGHT; + const bg = core.material.images.images["status.webp"]; //竖屏背景(上) + bgctx.drawImage( + bg, + 0, + 0, + GAMEVIEW_WIDTH_VERTICAL, + BAR_HEIGHT_VERTICAL + ); + const bg2 = core.material.images.images["status.webp"]; //竖屏背景(下) + bgctx.drawImage( + bg2, + 0, + BAR_HEIGHT_VERTICAL + GAMEVIEW_WIDTH_VERTICAL, + GAMEVIEW_WIDTH_VERTICAL, + BAR_HEIGHT_VERTICAL + ); + bgctx.globalAlpha = globalAlpha; + bgctx.globalAlpha = 1; + core.setTextAlign("outerUI", "center"); + } else { + bgctx.canvas.width = GAMEVIEW_WIDTH; + bgctx.canvas.height = GAMEVIEW_HEIGHT; + uictx.canvas.width = GAMEVIEW_WIDTH; + uictx.canvas.height = GAMEVIEW_HEIGHT; - const bg = core.material.images.images["status.webp"]; //横屏背景(左) - bgctx.drawImage(bg, 0, 0, BAR_WIDTH, GAMEVIEW_HEIGHT); - const bg2 = core.material.images.images["status.webp"]; //横屏背景(右) - bgctx.drawImage( - bg2, - BAR_WIDTH + GAMEVIEW_HEIGHT, - 0, - BAR_WIDTH, - GAMEVIEW_HEIGHT - ); - bgctx.globalAlpha = globalAlpha; + const bg = core.material.images.images["status.webp"]; //横屏背景(左) + bgctx.drawImage(bg, 0, 0, BAR_WIDTH, GAMEVIEW_HEIGHT); + const bg2 = core.material.images.images["status.webp"]; //横屏背景(右) + bgctx.drawImage( + bg2, + BAR_WIDTH + GAMEVIEW_HEIGHT, + 0, + BAR_WIDTH, + GAMEVIEW_HEIGHT + ); + bgctx.globalAlpha = globalAlpha; - bgctx.globalAlpha = 1; - core.setTextAlign("outerUI", "center"); - } - } - // 更新属性 - _update_props(updatedFloorTitle) { - if (!updatedFloorTitle && core.status.floorId) { - updatedFloorTitle = core.status.maps[core.status.floorId].title; - } - const statusList = ["hp", "atk", "def", "money"]; //属性列表,图标在函数复写core.statusBar.icons中声明,数字为project\materials\icons.png中的图标序号(可使用便捷ps追加,第一个序号为0) - const drawStatusList = (baseX, baseY) => { - let curh = baseY; - core.setTextAlign("outerUI", "right"); - statusList.forEach((item) => { - // 绘制图标 - core.drawIcon( - "outerUI", - item, - baseX - 95 * 3, - curh - 18 * 3, - 22 * 3, - 22 * 3 - ); + bgctx.globalAlpha = 1; + core.setTextAlign("outerUI", "center"); + } + } + // 更新属性 + _update_props(updatedFloorTitle) { + if (!updatedFloorTitle && core.status.floorId) { + updatedFloorTitle = core.status.maps[core.status.floorId].title; + } + const statusList = ["hp", "atk", "def", "money"]; //属性列表,图标在函数复写core.statusBar.icons中声明,数字为project\materials\icons.png中的图标序号(可使用便捷ps追加,第一个序号为0) + const drawStatusList = (baseX, baseY) => { + let curh = baseY; + core.setTextAlign("outerUI", "right"); + statusList.forEach((item) => { + // 绘制图标 + core.drawIcon( + "outerUI", + item, + baseX - 95 * 3, + curh - 18 * 3, + 22 * 3, + 22 * 3 + ); - // 四舍五入 - core.status.hero[item] = Math.round(core.status.hero[item]); - // 大数据格式化 - core.fillBoldText1( - "outerUI", - core.getRealStatus(item), - baseX, - curh, - TEXT_COLOR, - "#000000", - 6 - ); - curh += 24 * 3; - if (curh > 130 * 3 && core.domStyle.isVertical) { - curh = 24 * 3; - baseX += 105 * 3; - } - }); - core.setTextAlign("outerUI", "center"); - }; - if (core.domStyle.isVertical) { - core.clearMap("outerUI", 10 * 3, 0, 210 * 3, 120 * 3); - core.setFont("outerUI", "bold 42px Verdana"); - if (updatedFloorTitle) { - core.fillBoldText1( - "outerUI", - updatedFloorTitle, - 60 * 3, - 22 * 3, - TEXT_COLOR, - "#000000", - 6 - ); - } - //drawStatusList(96 * 3, 46 * 3); - //core.drawImage("outerUI", "lane1.png", 0, 0) - core.drawImage("outerUI", "cao.webp", 0, 0); - } else { - core.clearMap("outerUI", 10 * 3, 40 * 3, 105 * 3, 250 * 3); - core.setFont("outerUI", "bold 48px Verdana"); - if (updatedFloorTitle) { - core.fillBoldText1( - "outerUI", - updatedFloorTitle, - 62 * 3, - 41 * 3, - TEXT_COLOR, - "#000000", - 6 - ); - } - //drawStatusList(110 * 3, 93 * 3); - //core.drawImage("outerUI", "lane1.png", 0, 30) - core.drawImage( - "outerUI", - "cao.webp", - 0, - 0, - 400, - 350, - 0, - 30, - 360, - 315 - ); - } - } - _update_items() { - //更新道具栏 - const drawItemMx = (drawFn) => { - for (let i = 0; i < this.itemMx.length; i++) { - for (let j = 0; j < this.itemMx[i].length; j++) { - var item = this.itemMx[i][j]; - drawFn(i, j, item); - } - } - }; - const drawItem = (item, posx, posy) => { - const icon = core.material.icons.items[item], - image = core.material.images.items; - core.drawImage( - "outerUI", - image, - 0, - 32 * icon, - 32, - 32, - posx, - posy, - 30 * 3, - 30 * 3 - ); - const cnt = core.itemCount(item); - if ( - (core.items.items[item].cls === "tools" && cnt > 1) || - FORCE_COUNTABLE_ITEMS.includes(item) - ) { - core.fillText( - "outerUI", - cnt, - posx + 25 * 3, - posy + 28 * 3, - "#FFFFFF", - "bold 36px Verdana" - ); - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - ITEM_BOX_LEFT_VERTICAL, - ITEM_BOX_TOP_VERTICAL, - 185 * 3, - 125 * 3 - ); + // 四舍五入 + core.status.hero[item] = Math.round(core.status.hero[item]); + // 大数据格式化 + core.fillBoldText1( + "outerUI", + core.getRealStatus(item), + baseX, + curh, + TEXT_COLOR, + "#000000", + 6 + ); + curh += 24 * 3; + if (curh > 130 * 3 && core.domStyle.isVertical) { + curh = 24 * 3; + baseX += 105 * 3; + } + }); + core.setTextAlign("outerUI", "center"); + }; + if (core.domStyle.isVertical) { + core.clearMap("outerUI", 10 * 3, 0, 210 * 3, 120 * 3); + core.setFont("outerUI", "bold 42px Verdana"); + if (updatedFloorTitle) { + core.fillBoldText1( + "outerUI", + updatedFloorTitle, + 60 * 3, + 22 * 3, + TEXT_COLOR, + "#000000", + 6 + ); + } + //drawStatusList(96 * 3, 46 * 3); + //core.drawImage("outerUI", "lane1.png", 0, 0) + core.drawImage("outerUI", "cao.webp", 0, 0); + } else { + core.clearMap("outerUI", 10 * 3, 40 * 3, 105 * 3, 250 * 3); + core.setFont("outerUI", "bold 48px Verdana"); + if (updatedFloorTitle) { + core.fillBoldText1( + "outerUI", + updatedFloorTitle, + 62 * 3, + 41 * 3, + TEXT_COLOR, + "#000000", + 6 + ); + } + //drawStatusList(110 * 3, 93 * 3); + //core.drawImage("outerUI", "lane1.png", 0, 30) + core.drawImage( + "outerUI", + "cao.webp", + 0, + 0, + 400, + 350, + 0, + 30, + 360, + 315 + ); + } + } + _update_items() { + //更新道具栏 + const drawItemMx = (drawFn) => { + for (let i = 0; i < this.itemMx.length; i++) { + for (let j = 0; j < this.itemMx[i].length; j++) { + var item = this.itemMx[i][j]; + drawFn(i, j, item); + } + } + }; + const drawItem = (item, posx, posy) => { + const icon = core.material.icons.items[item], + image = core.material.images.items; + core.drawImage( + "outerUI", + image, + 0, + 32 * icon, + 32, + 32, + posx, + posy, + 30 * 3, + 30 * 3 + ); + const cnt = core.itemCount(item); + if ( + (core.items.items[item].cls === "tools" && cnt > 1) || + FORCE_COUNTABLE_ITEMS.includes(item) + ) { + core.fillText( + "outerUI", + cnt, + posx + 25 * 3, + posy + 28 * 3, + "#FFFFFF", + "bold 36px Verdana" + ); + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + ITEM_BOX_LEFT_VERTICAL, + ITEM_BOX_TOP_VERTICAL, + 185 * 3, + 125 * 3 + ); - drawItemMx((i, j, item) => { - if (core.hasItem(item)) { - const posx = ITEM_BOX_LEFT_VERTICAL + i * 30 * 3, - posy = ITEM_BOX_TOP_VERTICAL + j * 31 * 3; - drawItem(item, posx, posy); - } - }); - } else { - core.clearMap( - "outerUI", - ITEM_BOX_LEFT, - ITEM_BOX_TOP, - 125 * 3, - 185 * 3 - ); + drawItemMx((i, j, item) => { + if (core.hasItem(item)) { + const posx = ITEM_BOX_LEFT_VERTICAL + i * 30 * 3, + posy = ITEM_BOX_TOP_VERTICAL + j * 31 * 3; + drawItem(item, posx, posy); + } + }); + } else { + core.clearMap( + "outerUI", + ITEM_BOX_LEFT, + ITEM_BOX_TOP, + 125 * 3, + 185 * 3 + ); - drawItemMx((i, j, item) => { - if (core.hasItem(item)) { - const posx = ITEM_BOX_LEFT + j * 30 * 3, - posy = ITEM_BOX_TOP + i * 31 * 3; - drawItem(item, posx, posy); - } - }); - } - } + drawItemMx((i, j, item) => { + if (core.hasItem(item)) { + const posx = ITEM_BOX_LEFT + j * 30 * 3, + posy = ITEM_BOX_TOP + i * 31 * 3; + drawItem(item, posx, posy); + } + }); + } + } - _update_map(floorId = core.status.floorId) { - const x = core.domStyle.isVertical ? - MAP_BLOCK_LEFT_VERTICAL : - MAP_BLOCK_LEFT; - const y = core.domStyle.isVertical ? - MAP_BLOCK_TOP_VERTICAL : - MAP_BLOCK_TOP; + _update_map(floorId = core.status.floorId) { + const x = core.domStyle.isVertical + ? MAP_BLOCK_LEFT_VERTICAL + : MAP_BLOCK_LEFT; + const y = core.domStyle.isVertical + ? MAP_BLOCK_TOP_VERTICAL + : MAP_BLOCK_TOP; - if (!floorId) return; - const info = core.plugin.getMapDrawInfo(floorId, Infinity, true); - core.setTextAlign("outerUI", "center"); + if (!floorId) return; + const info = core.plugin.getMapDrawInfo(floorId, Infinity, true); + core.setTextAlign("outerUI", "center"); - core.plugin.drawSmallMap(uictx, info, floorId, x, y, 300, 300); - } + core.plugin.drawSmallMap(uictx, info, floorId, x, y, 300, 300); + } - _update_equips() { - return; - core.setFont("outerUI", "bold 48px Verdana"); - const drawEquip = (baseX, baseY, id, color, back) => { - if (!id) - core.fillText( - "outerUI", - back, - baseX + 20 * 3, - baseY + 22 * 3, - color - ); - else { - var icon = core.material.icons.items[id]; - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - baseX + 5 * 3, - baseY, - 32 * 3, - 32 * 3 - ); - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL, - 90 * 3, - 130 * 3 - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL, - core.getEquip(0), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL, - core.getEquip(1), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, - core.getEquip(2), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, - core.getEquip(3), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, - core.getEquip(4), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, - core.getEquip(5), - "#D1CEFF", - "无" - ); - } else { - core.clearMap( - "outerUI", - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP, - 130 * 3, - 95 * 3 - ); - drawEquip( - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP, - core.getEquip(0), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 42 * 3, - EQUIP_BLOCK_TOP, - core.getEquip(1), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 85 * 3, - EQUIP_BLOCK_TOP, - core.getEquip(2), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(3), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 42 * 3, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(4), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 85 * 3, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(5), - "#D1CEFF", - "无" - ); - } - } - _update_keys() { - const drawKeyList = (baseX, baseY) => { - const todraw = [], - keyList = ["yellowKey", "blueKey", "redKey", "greenKey"]; - let total = 0; - keyList.forEach(function (key, i) { - todraw[i] = core.itemCount(key); - total += todraw[i]; - }); + _update_equips() { + return; + core.setFont("outerUI", "bold 48px Verdana"); + const drawEquip = (baseX, baseY, id, color, back) => { + if (!id) + core.fillText( + "outerUI", + back, + baseX + 20 * 3, + baseY + 22 * 3, + color + ); + else { + var icon = core.material.icons.items[id]; + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + baseX + 5 * 3, + baseY, + 32 * 3, + 32 * 3 + ); + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL, + 90 * 3, + 130 * 3 + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL, + core.getEquip(0), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL, + core.getEquip(1), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, + core.getEquip(2), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, + core.getEquip(3), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, + core.getEquip(4), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, + core.getEquip(5), + "#D1CEFF", + "无" + ); + } else { + core.clearMap( + "outerUI", + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP, + 130 * 3, + 95 * 3 + ); + drawEquip( + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP, + core.getEquip(0), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 42 * 3, + EQUIP_BLOCK_TOP, + core.getEquip(1), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 85 * 3, + EQUIP_BLOCK_TOP, + core.getEquip(2), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(3), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 42 * 3, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(4), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 85 * 3, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(5), + "#D1CEFF", + "无" + ); + } + } + _update_keys() { + const drawKeyList = (baseX, baseY) => { + const todraw = [], + keyList = ["yellowKey", "blueKey", "redKey", "greenKey"]; + let total = 0; + keyList.forEach(function (key, i) { + todraw[i] = core.itemCount(key); + total += todraw[i]; + }); - let dn = 3; - for (let i = 0; i <= dn; i++) { - let delta = i * 32 * 3; + let dn = 3; + for (let i = 0; i <= dn; i++) { + let delta = i * 32 * 3; - if (core.domStyle.isVertical) { - this.drawKey(keyList[i], baseX, baseY + delta); - } else { - this.drawKey(keyList[i], baseX + delta, baseY); - } + if (core.domStyle.isVertical) { + this.drawKey(keyList[i], baseX, baseY + delta); + } else { + this.drawKey(keyList[i], baseX + delta, baseY); + } - core.setFont("outerUI", "bold 48px Verdana"); - core.setTextAlign("outerUI", "left"); - if (core.domStyle.isVertical) { - core.fillText( - "outerUI", - todraw[i], - baseX + 20 * 3, - baseY + 14 * 3 + delta, - TEXT_COLOR - ); - } else { - core.fillText( - "outerUI", - todraw[i], - baseX + delta, - baseY + 32 * 3, - TEXT_COLOR - ); - } - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - KEY_BLOCK_LEFT_VERTICAL, - KEY_BLOCK_TOP_VERTICAL, - 45 * 3, - 130 * 3 - ); - drawKeyList( - KEY_BLOCK_LEFT_VERTICAL + 3 * 3, - KEY_BLOCK_TOP_VERTICAL + 5 * 3 - ); - } else { - core.clearMap( - "outerUI", - KEY_BLOCK_LEFT, - KEY_BLOCK_TOP, - 130 * 3, - 45 * 3 - ); - drawKeyList(KEY_BLOCK_LEFT + 10 * 3, KEY_BLOCK_TOP); - } - } - drawKey(key, x, y) { - let sx = 0, - sy = 0; + core.setFont("outerUI", "bold 48px Verdana"); + core.setTextAlign("outerUI", "left"); + if (core.domStyle.isVertical) { + core.fillText( + "outerUI", + todraw[i], + baseX + 20 * 3, + baseY + 14 * 3 + delta, + TEXT_COLOR + ); + } else { + core.fillText( + "outerUI", + todraw[i], + baseX + delta, + baseY + 32 * 3, + TEXT_COLOR + ); + } + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + KEY_BLOCK_LEFT_VERTICAL, + KEY_BLOCK_TOP_VERTICAL, + 45 * 3, + 130 * 3 + ); + drawKeyList( + KEY_BLOCK_LEFT_VERTICAL + 3 * 3, + KEY_BLOCK_TOP_VERTICAL + 5 * 3 + ); + } else { + core.clearMap( + "outerUI", + KEY_BLOCK_LEFT, + KEY_BLOCK_TOP, + 130 * 3, + 45 * 3 + ); + drawKeyList(KEY_BLOCK_LEFT + 10 * 3, KEY_BLOCK_TOP); + } + } + drawKey(key, x, y) { + let sx = 0, + sy = 0; - if (key == "yellowKey") sx += 13; - else if (key == "blueKey") sx += 26; - else if (key == "greenKey") sx += 39; + if (key == "yellowKey") sx += 13; + else if (key == "blueKey") sx += 26; + else if (key == "greenKey") sx += 39; - core.drawImage( - "outerUI", - "maba.webp", - sx, - sy, - 13, - 26, - x, - y, - 13 * 3, - 26 * 3 - ); - } - _update_infoWindow() { - const itemId = this.selectedItem; - let text = ""; - if (this.selectedItem) { - text = core.replaceText(core.material.items[itemId]?.text); - if (text[0] == "," || text[0] == ",") text = text.substring(1); - } - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - INFO_BLOCK_LEFT_VERTICAL, - INFO_BLOCK_TOP_VERTICAL, - 300 * 3, - 120 * 3 - ); + core.drawImage( + "outerUI", + "maba.webp", + sx, + sy, + 13, + 26, + x, + y, + 13 * 3, + 26 * 3 + ); + } + _update_infoWindow() { + const itemId = this.selectedItem; + let text = ""; + if (this.selectedItem) { + text = core.replaceText(core.material.items[itemId]?.text); + if (text[0] == "," || text[0] == ",") text = text.substring(1); + } + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + INFO_BLOCK_LEFT_VERTICAL, + INFO_BLOCK_TOP_VERTICAL, + 300 * 3, + 120 * 3 + ); - if (this.selectedItem) { - const icon = core.material.icons.items[itemId]; - core.setTextAlign("outerUI", "left"); - core.fillText( - "outerUI", - core.material.items[itemId].name, - INFO_BLOCK_LEFT_VERTICAL + 50 * 3, - INFO_BLOCK_TOP_VERTICAL + 27 * 3, - "#D1CEFF" - ); - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - INFO_BLOCK_LEFT_VERTICAL + 10 * 3, - INFO_BLOCK_TOP_VERTICAL + 8 * 3, - 32 * 3, - 32 * 3 - ); - core.ui.drawTextContent("outerUI", text, { - left: INFO_BLOCK_LEFT_VERTICAL + 10 * 3, - top: INFO_BLOCK_TOP_VERTICAL + 40 * 3, - maxWidth: 275 * 3, - color: "#D1CEFF", - fontSize: 36, - }); - } - } else { - core.clearMap( - "outerUI", - INFO_BLOCK_LEFT, - INFO_BLOCK_TOP, - 115 * 3, - 230 * 3 - ); + if (this.selectedItem) { + const icon = core.material.icons.items[itemId]; + core.setTextAlign("outerUI", "left"); + core.fillText( + "outerUI", + core.material.items[itemId].name, + INFO_BLOCK_LEFT_VERTICAL + 50 * 3, + INFO_BLOCK_TOP_VERTICAL + 27 * 3, + "#D1CEFF" + ); + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + INFO_BLOCK_LEFT_VERTICAL + 10 * 3, + INFO_BLOCK_TOP_VERTICAL + 8 * 3, + 32 * 3, + 32 * 3 + ); + core.ui.drawTextContent("outerUI", text, { + left: INFO_BLOCK_LEFT_VERTICAL + 10 * 3, + top: INFO_BLOCK_TOP_VERTICAL + 40 * 3, + maxWidth: 275 * 3, + color: "#D1CEFF", + fontSize: 36, + }); + } + } else { + core.clearMap( + "outerUI", + INFO_BLOCK_LEFT, + INFO_BLOCK_TOP, + 115 * 3, + 230 * 3 + ); - if (this.selectedItem) { - const icon = core.material.icons.items[itemId]; - core.setTextAlign("outerUI", "center"); - core.fillText( - "outerUI", - core.material.items[itemId].name, - INFO_BLOCK_LEFT + 60 * 3, - INFO_BLOCK_TOP + 25 * 3, - "#D1CEFF" - ); - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - INFO_BLOCK_LEFT + 45 * 3, - INFO_BLOCK_TOP + 30 * 3, - 32 * 3, - 32 * 3 - ); - core.ui.drawTextContent("outerUI", text, { - left: INFO_BLOCK_LEFT + 10 * 3, - top: INFO_BLOCK_TOP + 60 * 3, - maxWidth: 105 * 3, - color: "#D1CEFF", - fontSize: 36, - }); - } - } - } - showItemInfo(itemId) { - //展示道具说明 - this.selectedItem = itemId; - this._update_infoWindow(); - } - clearItemInfo() { - //清除道具说明 - this.selectedItem = null; - this._update_infoWindow(); - } - _update_toolBox() { - const tools = core.isReplaying() ? [ - [core.status.replay.pausing ? "play" : "pause", "stop", "rewind"], - ["speedDown", "speedUp", "save"], - ] : [ - ["keyboard", "shop", "pack", "T332"], - ["settings", "save", "load", "T331"], - ]; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - TOOL_BOX_LEFT_VERTICAL, - TOOL_BOX_TOP_VERTICAL, - 115, - 130 - ); + if (this.selectedItem) { + const icon = core.material.icons.items[itemId]; + core.setTextAlign("outerUI", "center"); + core.fillText( + "outerUI", + core.material.items[itemId].name, + INFO_BLOCK_LEFT + 60 * 3, + INFO_BLOCK_TOP + 25 * 3, + "#D1CEFF" + ); + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + INFO_BLOCK_LEFT + 45 * 3, + INFO_BLOCK_TOP + 30 * 3, + 32 * 3, + 32 * 3 + ); + core.ui.drawTextContent("outerUI", text, { + left: INFO_BLOCK_LEFT + 10 * 3, + top: INFO_BLOCK_TOP + 60 * 3, + maxWidth: 105 * 3, + color: "#D1CEFF", + fontSize: 36, + }); + } + } + } + showItemInfo(itemId) { + //展示道具说明 + this.selectedItem = itemId; + this._update_infoWindow(); + } + clearItemInfo() { + //清除道具说明 + this.selectedItem = null; + this._update_infoWindow(); + } + _update_toolBox() { + const tools = core.isReplaying() + ? [ + [core.status.replay.pausing ? "play" : "pause", "stop", "rewind"], + ["speedDown", "speedUp", "save"], + ] + : [ + ["keyboard", "shop", "pack", "T332"], + ["settings", "save", "load", "T331"], + ]; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + TOOL_BOX_LEFT_VERTICAL, + TOOL_BOX_TOP_VERTICAL, + 115, + 130 + ); - for (let i = 0; i < tools.length; i++) { - for (let j = 0; j < tools[i].length; j++) { - core.drawIcon( - "outerUI", - tools[i][j], - TOOL_BOX_LEFT_VERTICAL + i * 31 * 3, - TOOL_BOX_TOP_VERTICAL + j * 31 * 3, - 30 * 3, - 30 * 3 - ); - } - } - } else { - core.clearMap( - "outerUI", - TOOL_BOX_LEFT, - TOOL_BOX_TOP, - 130 * 3, - 80 * 3 - ); + for (let i = 0; i < tools.length; i++) { + for (let j = 0; j < tools[i].length; j++) { + core.drawIcon( + "outerUI", + tools[i][j], + TOOL_BOX_LEFT_VERTICAL + i * 31 * 3, + TOOL_BOX_TOP_VERTICAL + j * 31 * 3, + 30 * 3, + 30 * 3 + ); + } + } + } else { + core.clearMap( + "outerUI", + TOOL_BOX_LEFT, + TOOL_BOX_TOP, + 130 * 3, + 80 * 3 + ); - for (let i = 0; i < tools.length; i++) { - for (let j = 0; j < tools[i].length; j++) { - core.drawIcon( - "outerUI", - tools[i][j], - TOOL_BOX_LEFT + j * 31 * 3, - TOOL_BOX_TOP + i * 31 * 3, - 30 * 3, - 30 * 3 - ); - } - } - } - } - onclick(x, y) { - const makeBox = ([x, y], [w, h]) => { - return [ - [x, y], - [x + w, y + h], - ]; - }; - const gridify = ([x, y], [gw, gh]) => { - return [Math.floor(x / gw), Math.floor(y / gh)]; - }; - const useItem = (itemId) => { - if (!core.hasItem(itemId)) return; + for (let i = 0; i < tools.length; i++) { + for (let j = 0; j < tools[i].length; j++) { + core.drawIcon( + "outerUI", + tools[i][j], + TOOL_BOX_LEFT + j * 31 * 3, + TOOL_BOX_TOP + i * 31 * 3, + 30 * 3, + 30 * 3 + ); + } + } + } + } + onclick(x, y) { + const makeBox = ([x, y], [w, h]) => { + return [ + [x, y], + [x + w, y + h], + ]; + }; + const gridify = ([x, y], [gw, gh]) => { + return [Math.floor(x / gw), Math.floor(y / gh)]; + }; + const useItem = (itemId) => { + if (!core.hasItem(itemId)) return; - if (itemId != this.selectedItem) { - this.showItemInfo(itemId); - } else { - switch (itemId) { - case "centerFly": - core.ui._drawCenterFly(); - break; - case "book": - core.openBook(true); - break; - case "wand": - core.insertAction({ - type: "useItem", - id: itemId, - }); - break; - case "fly": - core.useItem(itemId); - break; - default: - core.useItem(itemId); - } - } - }; - const inRect = ([x, y], [ - [sx, sy], - [dx, dy] - ]) => { - return sx <= x && x <= dx && sy <= y && y <= dy; - }; - const relativeTo = ([x, y], [ax, ay]) => { - return [x - ax, y - ay]; - }; - const pos = [x, y]; - if (core.domStyle.isVertical) { - const itemBox = makeBox( - [ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL], - [30 * 6 * 3, 31 * 4 * 3] - ); - if (inRect(pos, itemBox)) { - const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ - 30 * 3, - 31 * 3, - ]); - const itemId = this.itemMx[gx][gy]; - if ( - (core.status.event.id == "viewMaps" || - core.status.event.id == "fly") && - itemId === "book" - ) - core.openBook(true); - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - useItem(itemId); - return; - } - const toolBox = makeBox( - [TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL], - [31 * 2 * 3, 31 * 4 * 3] - ); - if (inRect(pos, toolBox)) { - const [col, row] = gridify(relativeTo(pos, toolBox[0]), [ - 31 * 3, - 31 * 3, - ]); - if (core.status.lockControl || core.isMoving()) return; - if (core.isReplaying()) { - this.replayAction[col][row].call(core); - } else if (core.isPlaying()) { - if (col === 0 && row === 3) { - core.doSL("autoSave", "load"); - } else if (col === 1 && row === 3) { - core.doSL("autoSave", "reload"); - } else { - this.toolbarAction[col][row].call(core, true); - } - } - return; - } - const mapBox = makeBox( - [MAP_BLOCK_LEFT_VERTICAL, MAP_BLOCK_TOP_VERTICAL], - [350, 350] - ); - if (inRect(pos, mapBox)) { - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - core.useItem("fly"); - return; - } - /*const equipBox = makeBox([EQUIP_BLOCK_LEFT_VERTICAL, EQUIP_BLOCK_TOP_VERTICAL], [90 * 3, 130 * 3]) + if (itemId != this.selectedItem) { + this.showItemInfo(itemId); + } else { + switch (itemId) { + case "centerFly": + core.ui._drawCenterFly(); + break; + case "book": + core.openBook(true); + break; + case "wand": + core.insertAction({ + type: "useItem", + id: itemId, + }); + break; + case "fly": + core.useItem(itemId); + break; + default: + core.useItem(itemId); + } + } + }; + const inRect = ([x, y], [[sx, sy], [dx, dy]]) => { + return sx <= x && x <= dx && sy <= y && y <= dy; + }; + const relativeTo = ([x, y], [ax, ay]) => { + return [x - ax, y - ay]; + }; + const pos = [x, y]; + if (core.domStyle.isVertical) { + const itemBox = makeBox( + [ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL], + [30 * 6 * 3, 31 * 4 * 3] + ); + if (inRect(pos, itemBox)) { + const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ + 30 * 3, + 31 * 3, + ]); + const itemId = this.itemMx[gx][gy]; + if ( + (core.status.event.id == "viewMaps" || + core.status.event.id == "fly") && + itemId === "book" + ) + core.openBook(true); + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + useItem(itemId); + return; + } + const toolBox = makeBox( + [TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL], + [31 * 2 * 3, 31 * 4 * 3] + ); + if (inRect(pos, toolBox)) { + const [col, row] = gridify(relativeTo(pos, toolBox[0]), [ + 31 * 3, + 31 * 3, + ]); + if (core.status.lockControl || core.isMoving()) return; + if (core.isReplaying()) { + this.replayAction[col][row].call(core); + } else if (core.isPlaying()) { + if (col === 0 && row === 3) { + core.doSL("autoSave", "load"); + } else if (col === 1 && row === 3) { + core.doSL("autoSave", "reload"); + } else { + this.toolbarAction[col][row].call(core, true); + } + } + return; + } + const mapBox = makeBox( + [MAP_BLOCK_LEFT_VERTICAL, MAP_BLOCK_TOP_VERTICAL], + [350, 350] + ); + if (inRect(pos, mapBox)) { + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + core.useItem("fly"); + return; + } + /*const equipBox = makeBox([EQUIP_BLOCK_LEFT_VERTICAL, EQUIP_BLOCK_TOP_VERTICAL], [90 * 3, 130 * 3]) if (inRect(pos, equipBox)) { if (core.isReplaying() || core.status.lockControl || core.isMoving()) return; core.openEquipbox(true) return; }*/ - } else { - const mapBox = makeBox([MAP_BLOCK_LEFT, MAP_BLOCK_TOP], [350, 350]); - if (inRect(pos, mapBox)) { - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - core.useItem("fly"); - return; - } - /* + } else { + const mapBox = makeBox([MAP_BLOCK_LEFT, MAP_BLOCK_TOP], [350, 350]); + if (inRect(pos, mapBox)) { + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + core.useItem("fly"); + return; + } + /* const equipBox = makeBox([EQUIP_BLOCK_LEFT, EQUIP_BLOCK_TOP], [130, 95]) if (inRect(pos, equipBox)) { if (core.isReplaying() || core.status.lockControl || core.isMoving()) return; core.openEquipbox(true) return; }*/ - const itemBox = makeBox( - [ITEM_BOX_LEFT, ITEM_BOX_TOP], - [31 * 4 * 3, 30 * 6 * 3] - ); - if (inRect(pos, itemBox)) { - const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ - 31 * 3, - 30 * 3, - ]); - const itemId = this.itemMx[gy][gx]; - if ( - (core.status.event.id == "viewMaps" || - core.status.event.id == "fly") && - itemId === "book" - ) - core.openBook(true); - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - useItem(itemId); - return; - } - const toolBox = makeBox( - [TOOL_BOX_LEFT, TOOL_BOX_TOP], - [31 * 4 * 3, 31 * 2 * 3] - ); - if (inRect(pos, toolBox)) { - const [row, col] = gridify(relativeTo(pos, toolBox[0]), [ - 31 * 3, - 31 * 3, - ]); - if (core.status.lockControl || core.isMoving()) return; - if (core.isReplaying()) { - this.replayAction[col][row].call(core); - } else if (core.isPlaying()) { - if (col === 0 && row === 3) { - core.doSL("autoSave", "load"); - } else if (col === 1 && row === 3) { - core.doSL("autoSave", "reload"); - } else { - this.toolbarAction[col][row].call(core, true); - } - } - return; - } - } - } - } + const itemBox = makeBox( + [ITEM_BOX_LEFT, ITEM_BOX_TOP], + [31 * 4 * 3, 30 * 6 * 3] + ); + if (inRect(pos, itemBox)) { + const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ + 31 * 3, + 30 * 3, + ]); + const itemId = this.itemMx[gy][gx]; + if ( + (core.status.event.id == "viewMaps" || + core.status.event.id == "fly") && + itemId === "book" + ) + core.openBook(true); + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + useItem(itemId); + return; + } + const toolBox = makeBox( + [TOOL_BOX_LEFT, TOOL_BOX_TOP], + [31 * 4 * 3, 31 * 2 * 3] + ); + if (inRect(pos, toolBox)) { + const [row, col] = gridify(relativeTo(pos, toolBox[0]), [ + 31 * 3, + 31 * 3, + ]); + if (core.status.lockControl || core.isMoving()) return; + if (core.isReplaying()) { + this.replayAction[col][row].call(core); + } else if (core.isPlaying()) { + if (col === 0 && row === 3) { + core.doSL("autoSave", "load"); + } else if (col === 1 && row === 3) { + core.doSL("autoSave", "reload"); + } else { + this.toolbarAction[col][row].call(core, true); + } + } + return; + } + } + } + } - core.ui.statusBar = new StatusBar(); + core.ui.statusBar = new StatusBar(); - core.control.clearStatusBar = function () { - core.clearMap("outerUI"); - }; - // init() called in `afterLoadResources`. -}, + core.control.clearStatusBar = function () { + core.clearMap("outerUI"); + }; + // init() called in `afterLoadResources`. + }, "override": function () { - core.statusBar.icons = { - floor: 0, - name: null, - lv: 1, - hpmax: 2, - hp: 3, - atk: 4, - def: 5, - mdef: 6, - money: 7, - exp: 8, - up: 9, - book: 10, - fly: 11, - toolbox: 12, - keyboard: 13, - shop: 14, - save: 15, - load: 16, - settings: 17, - play: 18, - pause: 19, - stop: 20, - speedDown: 21, - speedUp: 22, - rewind: 23, - equipbox: 24, - mana: 25, - skill: 26, - exit: 27, - btn1: 28, - btn2: 29, - btn3: 30, - btn4: 31, - btn5: 32, - btn6: 33, - btn7: 34, - alt: 35, - keys: 36, - help: 37, - battle: 38, - }; - core.actions._getClickLoc = function (x, y) { - var size = 32 * core.domStyle.scale; - var left = main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft; - var top = main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop; - var loc = { - x: Math.max(x - left, 0), - y: Math.max(y - top, 0), - size: size, - }; - return loc; - }; - core.ui._drawWindowSelector = function (background, x, y, w, h) { - w = Math.round(w) + 48; - h = Math.round(h); - var ctx = core.ui.createCanvas("_selector", x - 24, y, w, h, 165); - ctx.canvas.id = ""; - this._drawSelector(ctx, background, w, h); - }; + core.statusBar.icons = { + floor: 0, + name: null, + lv: 1, + hpmax: 2, + hp: 3, + atk: 4, + def: 5, + mdef: 6, + money: 7, + exp: 8, + up: 9, + book: 10, + fly: 11, + toolbox: 12, + keyboard: 13, + shop: 14, + save: 15, + load: 16, + settings: 17, + play: 18, + pause: 19, + stop: 20, + speedDown: 21, + speedUp: 22, + rewind: 23, + equipbox: 24, + mana: 25, + skill: 26, + exit: 27, + btn1: 28, + btn2: 29, + btn3: 30, + btn4: 31, + btn5: 32, + btn6: 33, + btn7: 34, + alt: 35, + keys: 36, + help: 37, + battle: 38, + }; + core.actions._getClickLoc = function (x, y) { + var size = 32 * core.domStyle.scale; + var left = main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft; + var top = main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop; + var loc = { + x: Math.max(x - left, 0), + y: Math.max(y - top, 0), + size: size, + }; + return loc; + }; + core.ui._drawWindowSelector = function (background, x, y, w, h) { + w = Math.round(w) + 48; + h = Math.round(h); + var ctx = core.ui.createCanvas("_selector", x - 24, y, w, h, 165); + ctx.canvas.id = ""; + this._drawSelector(ctx, background, w, h); + }; - core.ui._drawSelector = function (ctx, background, w, h, left, top) { - left = left || 0; - top = top || 0; - ctx = this.getContextByName(ctx); - if (!ctx) return; - if (typeof background == "string") - background = core.material.images.images[background]; - if (!(background instanceof Image)) return; - // badge - ctx.drawImage(background, 132, 68, 24, 24, left + 4, top + 4, 24, 24); - ctx.drawImage( - background, - 132, - 68, - 24, - 24, - w - left - 28, - top + 4, - 24, - 24 - ); - }; + core.ui._drawSelector = function (ctx, background, w, h, left, top) { + left = left || 0; + top = top || 0; + ctx = this.getContextByName(ctx); + if (!ctx) return; + if (typeof background == "string") + background = core.material.images.images[background]; + if (!(background instanceof Image)) return; + // badge + ctx.drawImage(background, 132, 68, 24, 24, left + 4, top + 4, 24, 24); + ctx.drawImage( + background, + 132, + 68, + 24, + 24, + w - left - 28, + top + 4, + 24, + 24 + ); + }; - enemys.prototype._nextCriticals_useBinarySearch = function ( - enemy, - info, - number, - x, - y, - floorId - ) { - var mon_hp = info.mon_hp, - hero_atk = core.status.hero.atk, - mon_def = info.mon_def, - pre = info.damage; - var list = []; - var start_atk = hero_atk; - if (info.__over__) { - start_atk += info.__overAtk__; - list.push([info.__overAtk__, -info.damage]); - } - var calNext = function (currAtk, maxAtk) { - var start = Math.floor(currAtk), - end = Math.floor(maxAtk); - if (start > end) return null; + enemys.prototype._nextCriticals_useBinarySearch = function ( + enemy, + info, + number, + x, + y, + floorId + ) { + var mon_hp = info.mon_hp, + hero_atk = core.status.hero.atk, + mon_def = info.mon_def, + pre = info.damage; + var list = []; + var start_atk = hero_atk; + if (info.__over__) { + start_atk += info.__overAtk__; + list.push([info.__overAtk__, -info.damage]); + } + var calNext = function (currAtk, maxAtk) { + var start = Math.floor(currAtk), + end = Math.floor(maxAtk); + if (start > end) return null; - while (start < end) { - var mid = Math.floor((start + end) / 2); - if (mid - start > end - mid) mid--; - var nextInfo = core.enemys.getDamageInfo( - enemy, { atk: mid }, - x, - y, - floorId - ); - if (nextInfo == null || typeof nextInfo == "number") return null; - if (pre > nextInfo.damage) end = mid; - else start = mid + 1; - } - var nextInfo = core.enemys.getDamageInfo( - enemy, { atk: start }, - x, - y, - floorId - ); - return nextInfo == null || - typeof nextInfo == "number" || - nextInfo.damage >= pre ? - null : [start, nextInfo.damage]; - }; - var currAtk = start_atk; - while (true) { - var next = calNext(currAtk + 1, Number.MAX_SAFE_INTEGER, pre); - if (next == null) break; - currAtk = next[0]; - pre = next[1]; - list.push([currAtk - hero_atk, info.damage - pre]); - if (pre <= 0 && !core.flags.enableNegativeDamage) break; - if (list.length >= number) break; - } - if (list.length == 0) list.push([0, 0]); - return list; - }; - core.ui.clearMap = function (name, x, y, width, height) { - if (name == "all") { - for (var m in core.canvas) { - core.canvas[m].clearRect( - -32, - -32, - core.canvas[m].canvas.width + 32, - core.canvas[m].canvas.height + 32 - ); - } - core.clearMap("outerUI"); - core.dom.gif.innerHTML = ""; - core.removeGlobalAnimate(); - core.deleteCanvas(function (one) { - return one.startsWith("_bigImage_"); - }); - core.setWeather(null); - } else { - var ctx = this.getContextByName(name); - if (ctx) - ctx.clearRect( - x || 0, - y || 0, - width || ctx.canvas.width, - height || ctx.canvas.height - ); - } - }; - events.prototype.openBook = function (fromUserAction) { - if (core.isReplaying()) return; - // 如果能恢复事件(从callBook事件触发) - if ( - core.status.event.id == "book" && - core.events.recoverEvents(core.status.event.interval) - ) - return; - // 当前是book,且从“浏览地图”打开 - if (core.status.event.id == "book" && core.status.event.ui) { - core.status.boxAnimateObjs = []; - core.ui._drawViewMaps(core.status.event.ui); - return; - } - // 从“浏览地图”页面打开 - if (core.status.event.id == "viewMaps" || core.status.event.id == "fly") { - fromUserAction = false; - core.status.event.ui = core.status.event.data; - } - if (!this._checkStatus("book", fromUserAction, true)) return; - core.playSound("打开界面"); - core.useItem("book", true); - }; - ////// 怪物手册界面时,放开某个键的操作 ////// - core.actions._keyUpBook = function (keycode) { - if (keycode == 27 || keycode == 88) { - core.playSound("取消"); - if (core.events.recoverEvents(core.status.event.interval)) { - return; - } else if (core.status.event.ui != null) { - core.status.boxAnimateObjs = []; - if (typeof core.status.event.ui === "number") { - core.status.event.id = "fly"; - core.ui.drawFly(core.status.event.ui); - } else { - core.ui._drawViewMaps(core.status.event.ui); - } - } else core.ui.closePanel(); - return; - } - if (keycode == 13 || keycode == 32 || keycode == 67) { - var data = core.status.event.data; - if (data != null) { - core.ui._drawBookDetail(data); - } - return; - } - }; - ////// 怪物手册界面的点击操作 ////// - actions.prototype._clickBook = function (x, y) { - var pageinfo = core.ui._drawBook_pageinfo(); - // 上一页 - if ( - (x == this._HX_ - 2 || x == this._HX_ - 3) && - y === core._HEIGHT_ - 1 - ) { - core.playSound("光标移动"); - core.ui.drawBook(core.status.event.data - pageinfo.per_page); - return; - } - // 下一页 - if ( - (x == this._HX_ + 2 || x == this._HX_ + 3) && - y === core._HEIGHT_ - 1 - ) { - core.playSound("光标移动"); - core.ui.drawBook(core.status.event.data + pageinfo.per_page); - return; - } - // 返回 - if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { - core.playSound("取消"); - if (core.events.recoverEvents(core.status.event.interval)) { - return; - } else if (core.status.event.ui != null) { - core.status.boxAnimateObjs = []; - if (typeof core.status.event.ui === "number") { - core.status.event.id = "fly"; - core.ui.drawFly(core.status.event.ui); - } else { - core.ui._drawViewMaps(core.status.event.ui); - } - } else core.ui.closePanel(); - return; - } - // 怪物信息 - var data = core.status.event.data; - if (data != null && y < core._HEIGHT_ - 1) { - var per_page = pageinfo.per_page, - page = parseInt(data / per_page); - var u = (core._HEIGHT_ - 1) / per_page; - for (var i = 0; i < per_page; ++i) { - if (y >= u * i && y < u * (i + 1)) { - var index = per_page * page + i; - core.ui.drawBook(index); - core.ui._drawBookDetail(index); - break; - } - } - return; - } - return; - }; + while (start < end) { + var mid = Math.floor((start + end) / 2); + if (mid - start > end - mid) mid--; + var nextInfo = core.enemys.getDamageInfo( + enemy, + { atk: mid }, + x, + y, + floorId + ); + if (nextInfo == null || typeof nextInfo == "number") return null; + if (pre > nextInfo.damage) end = mid; + else start = mid + 1; + } + var nextInfo = core.enemys.getDamageInfo( + enemy, + { atk: start }, + x, + y, + floorId + ); + return nextInfo == null || + typeof nextInfo == "number" || + nextInfo.damage >= pre + ? null + : [start, nextInfo.damage]; + }; + var currAtk = start_atk; + while (true) { + var next = calNext(currAtk + 1, Number.MAX_SAFE_INTEGER, pre); + if (next == null) break; + currAtk = next[0]; + pre = next[1]; + list.push([currAtk - hero_atk, info.damage - pre]); + if (pre <= 0 && !core.flags.enableNegativeDamage) break; + if (list.length >= number) break; + } + if (list.length == 0) list.push([0, 0]); + return list; + }; + core.ui.clearMap = function (name, x, y, width, height) { + if (name == "all") { + for (var m in core.canvas) { + core.canvas[m].clearRect( + -32, + -32, + core.canvas[m].canvas.width + 32, + core.canvas[m].canvas.height + 32 + ); + } + core.clearMap("outerUI"); + core.dom.gif.innerHTML = ""; + core.removeGlobalAnimate(); + core.deleteCanvas(function (one) { + return one.startsWith("_bigImage_"); + }); + core.setWeather(null); + } else { + var ctx = this.getContextByName(name); + if (ctx) + ctx.clearRect( + x || 0, + y || 0, + width || ctx.canvas.width, + height || ctx.canvas.height + ); + } + }; + events.prototype.openBook = function (fromUserAction) { + if (core.isReplaying()) return; + // 如果能恢复事件(从callBook事件触发) + if ( + core.status.event.id == "book" && + core.events.recoverEvents(core.status.event.interval) + ) + return; + // 当前是book,且从“浏览地图”打开 + if (core.status.event.id == "book" && core.status.event.ui) { + core.status.boxAnimateObjs = []; + core.ui._drawViewMaps(core.status.event.ui); + return; + } + // 从“浏览地图”页面打开 + if (core.status.event.id == "viewMaps" || core.status.event.id == "fly") { + fromUserAction = false; + core.status.event.ui = core.status.event.data; + } + if (!this._checkStatus("book", fromUserAction, true)) return; + core.playSound("打开界面"); + core.useItem("book", true); + }; + ////// 怪物手册界面时,放开某个键的操作 ////// + core.actions._keyUpBook = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound("取消"); + if (core.events.recoverEvents(core.status.event.interval)) { + return; + } else if (core.status.event.ui != null) { + core.status.boxAnimateObjs = []; + if (typeof core.status.event.ui === "number") { + core.status.event.id = "fly"; + core.ui.drawFly(core.status.event.ui); + } else { + core.ui._drawViewMaps(core.status.event.ui); + } + } else core.ui.closePanel(); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + var data = core.status.event.data; + if (data != null) { + core.ui._drawBookDetail(data); + } + return; + } + }; + ////// 怪物手册界面的点击操作 ////// + actions.prototype._clickBook = function (x, y) { + var pageinfo = core.ui._drawBook_pageinfo(); + // 上一页 + if ( + (x == this._HX_ - 2 || x == this._HX_ - 3) && + y === core._HEIGHT_ - 1 + ) { + core.playSound("光标移动"); + core.ui.drawBook(core.status.event.data - pageinfo.per_page); + return; + } + // 下一页 + if ( + (x == this._HX_ + 2 || x == this._HX_ + 3) && + y === core._HEIGHT_ - 1 + ) { + core.playSound("光标移动"); + core.ui.drawBook(core.status.event.data + pageinfo.per_page); + return; + } + // 返回 + if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { + core.playSound("取消"); + if (core.events.recoverEvents(core.status.event.interval)) { + return; + } else if (core.status.event.ui != null) { + core.status.boxAnimateObjs = []; + if (typeof core.status.event.ui === "number") { + core.status.event.id = "fly"; + core.ui.drawFly(core.status.event.ui); + } else { + core.ui._drawViewMaps(core.status.event.ui); + } + } else core.ui.closePanel(); + return; + } + // 怪物信息 + var data = core.status.event.data; + if (data != null && y < core._HEIGHT_ - 1) { + var per_page = pageinfo.per_page, + page = parseInt(data / per_page); + var u = (core._HEIGHT_ - 1) / per_page; + for (var i = 0; i < per_page; ++i) { + if (y >= u * i && y < u * (i + 1)) { + var index = per_page * page + i; + core.ui.drawBook(index); + core.ui._drawBookDetail(index); + break; + } + } + return; + } + return; + }; - ////// 执行当前自定义事件列表中的下一个事件 ////// - events.prototype.doAction = function () { - // 清空boxAnimate和UI层 - clearInterval(core.status.event.interval); - clearTimeout(core.status.event.interval); - clearInterval(core.status.event.animateUI); - core.status.event.interval = null; - delete core.status.event.aniamteUI; - if (core.status.gameOver || core.status.replay.failed) return; - // 判定是否执行完毕 - if (this._doAction_finishEvents()) return; - core.clearUI(); - var floorId = core.status.event.data.floorId || core.status.floorId; - // 当前点坐标和前缀 - var x = core.status.event.data.x, - y = core.status.event.data.y; - var prefix = [ - floorId || ":f", - x != null ? x : "x", - y != null ? y : "y", - ].join("@"); - var current = core.status.event.data.list[0]; - if (this._popEvents(current, prefix)) return; - // 当前要执行的事件 - var data = current.todo.shift(); - core.status.event.data.current = data; - if (typeof data == "string") data = { type: "text", text: data }; - // 该事件块已经被禁用 - if (data._disabled) return core.doAction(); - if (data.type !== "cgtext") { - core.unregisterAnimationFrame("skip"); - core.setFlag("skip", false); - } - data.floorId = data.floorId || floorId; - core.status.event.data.type = data.type; - this.doEvent(data, x, y, prefix); - return; - }; + ////// 执行当前自定义事件列表中的下一个事件 ////// + events.prototype.doAction = function () { + // 清空boxAnimate和UI层 + clearInterval(core.status.event.interval); + clearTimeout(core.status.event.interval); + clearInterval(core.status.event.animateUI); + core.status.event.interval = null; + delete core.status.event.aniamteUI; + if (core.status.gameOver || core.status.replay.failed) return; + // 判定是否执行完毕 + if (this._doAction_finishEvents()) return; + core.clearUI(); + var floorId = core.status.event.data.floorId || core.status.floorId; + // 当前点坐标和前缀 + var x = core.status.event.data.x, + y = core.status.event.data.y; + var prefix = [ + floorId || ":f", + x != null ? x : "x", + y != null ? y : "y", + ].join("@"); + var current = core.status.event.data.list[0]; + if (this._popEvents(current, prefix)) return; + // 当前要执行的事件 + var data = current.todo.shift(); + core.status.event.data.current = data; + if (typeof data == "string") data = { type: "text", text: data }; + // 该事件块已经被禁用 + if (data._disabled) return core.doAction(); + if (data.type !== "cgtext") { + core.unregisterAnimationFrame("skip"); + core.setFlag("skip", false); + } + data.floorId = data.floorId || floorId; + core.status.event.data.type = data.type; + this.doEvent(data, x, y, prefix); + return; + }; - ////// 在某个canvas上绘制粗体 ////// - core.fillBoldText1 = function ( - name, - text, - x, - y, - style, - strokeStyle, - lineWidth, - font, - maxWidth - ) { - var ctx = this.getContextByName(name); - if (!ctx) return; - if (font) ctx.font = font; - if (!style) style = ctx.fillStyle; - style = core.arrayToRGBA(style); - if (!strokeStyle) strokeStyle = "#000000"; - strokeStyle = core.arrayToRGBA(strokeStyle); - if (maxWidth != null) { - this.setFontForMaxWidth(ctx, text, maxWidth); - } - ctx.strokeStyle = strokeStyle; + ////// 在某个canvas上绘制粗体 ////// + core.fillBoldText1 = function ( + name, + text, + x, + y, + style, + strokeStyle, + lineWidth, + font, + maxWidth + ) { + var ctx = this.getContextByName(name); + if (!ctx) return; + if (font) ctx.font = font; + if (!style) style = ctx.fillStyle; + style = core.arrayToRGBA(style); + if (!strokeStyle) strokeStyle = "#000000"; + strokeStyle = core.arrayToRGBA(strokeStyle); + if (maxWidth != null) { + this.setFontForMaxWidth(ctx, text, maxWidth); + } + ctx.strokeStyle = strokeStyle; - if (!lineWidth) lineWidth = 2; - ctx.lineWidth = lineWidth; - ctx.strokeText(text, x, y); - ctx.fillStyle = style; - ctx.fillText(text, x, y); - }; - ////// 绘制 WindowSkin - ui.prototype.drawWindowSkin = function ( - background, - ctx, - x, - y, - w, - h, - direction, - px, - py, - size = 1 - ) { - background = background || core.status.textAttribute.background; + if (!lineWidth) lineWidth = 2; + ctx.lineWidth = lineWidth; + ctx.strokeText(text, x, y); + ctx.fillStyle = style; + ctx.fillText(text, x, y); + }; + ////// 绘制 WindowSkin + ui.prototype.drawWindowSkin = function ( + background, + ctx, + x, + y, + w, + h, + direction, + px, + py, + size = 1 + ) { + background = background || core.status.textAttribute.background; - // 仿RM窗口皮肤 ↓ - // 绘制背景 - core.drawImage( - ctx, - background, - 0, - 0, - 128, - 128, - x + 2 * size, - y + 2 * size, - w - 4 * size, - h - 4 * size - ); - // 绘制边框 - // 上方 - core.drawImage( - ctx, - background, - 128, - 0, - 16, - 16, - x, - y, - 16 * size, - 16 * size - ); - for (var dx = 0; dx < w - 64 * size; dx += 32 * size) { - core.drawImage( - ctx, - background, - 144, - 0, - 32, - 16, - x + dx + 16 * size, - y, - 32 * size, - 16 * size - ); - core.drawImage( - ctx, - background, - 144, - 48, - 32, - 16, - x + dx + 16 * size, - y + h - 16 * size, - 32 * size, - 16 * size - ); - } - core.drawImage( - ctx, - background, - 144, - 0, - (w - dx - 32 * size) / size, - 16, - x + dx + 16 * size, - y, - w - dx - 32 * size, - 16 * size - ); - core.drawImage( - ctx, - background, - 144, - 48, - (w - dx - 32 * size) / size, - 16, - x + dx + 16 * size, - y + h - 16 * size, - w - dx - 32 * size, - 16 * size - ); - core.drawImage( - ctx, - background, - 176, - 0, - 16, - 16, - x + w - 16 * size, - y, - 16 * size, - 16 * size - ); - // 左右 - for (var dy = 0; dy < h - 64 * size; dy += 32 * size) { - core.drawImage( - ctx, - background, - 128, - 16, - 16, - 32, - x, - y + dy + 16 * size, - 16 * size, - 32 * size - ); - core.drawImage( - ctx, - background, - 176, - 16, - 16, - 32, - x + w - 16 * size, - y + dy + 16 * size, - 16 * size, - 32 * size - ); - } - core.drawImage( - ctx, - background, - 128, - 16, - 16, - (h - dy - 32 * size) / size, - x, - y + dy + 16 * size, - 16 * size, - h - dy - 32 * size - ); - core.drawImage( - ctx, - background, - 176, - 16, - 16, - (h - dy - 32 * size) / size, - x + w - 16 * size, - y + dy + 16 * size, - 16 * size, - h - dy - 32 * size - ); - //下方 - core.drawImage( - ctx, - background, - 128, - 48, - 16, - 16, - x, - y + h - 16 * size, - 16 * size, - 16 * size - ); - core.drawImage( - ctx, - background, - 176, - 48, - 16, - 16, - x + w - 16 * size, - y + h - 16 * size, - 16 * size, - 16 * size - ); + // 仿RM窗口皮肤 ↓ + // 绘制背景 + core.drawImage( + ctx, + background, + 0, + 0, + 128, + 128, + x + 2 * size, + y + 2 * size, + w - 4 * size, + h - 4 * size + ); + // 绘制边框 + // 上方 + core.drawImage( + ctx, + background, + 128, + 0, + 16, + 16, + x, + y, + 16 * size, + 16 * size + ); + for (var dx = 0; dx < w - 64 * size; dx += 32 * size) { + core.drawImage( + ctx, + background, + 144, + 0, + 32, + 16, + x + dx + 16 * size, + y, + 32 * size, + 16 * size + ); + core.drawImage( + ctx, + background, + 144, + 48, + 32, + 16, + x + dx + 16 * size, + y + h - 16 * size, + 32 * size, + 16 * size + ); + } + core.drawImage( + ctx, + background, + 144, + 0, + (w - dx - 32 * size) / size, + 16, + x + dx + 16 * size, + y, + w - dx - 32 * size, + 16 * size + ); + core.drawImage( + ctx, + background, + 144, + 48, + (w - dx - 32 * size) / size, + 16, + x + dx + 16 * size, + y + h - 16 * size, + w - dx - 32 * size, + 16 * size + ); + core.drawImage( + ctx, + background, + 176, + 0, + 16, + 16, + x + w - 16 * size, + y, + 16 * size, + 16 * size + ); + // 左右 + for (var dy = 0; dy < h - 64 * size; dy += 32 * size) { + core.drawImage( + ctx, + background, + 128, + 16, + 16, + 32, + x, + y + dy + 16 * size, + 16 * size, + 32 * size + ); + core.drawImage( + ctx, + background, + 176, + 16, + 16, + 32, + x + w - 16 * size, + y + dy + 16 * size, + 16 * size, + 32 * size + ); + } + core.drawImage( + ctx, + background, + 128, + 16, + 16, + (h - dy - 32 * size) / size, + x, + y + dy + 16 * size, + 16 * size, + h - dy - 32 * size + ); + core.drawImage( + ctx, + background, + 176, + 16, + 16, + (h - dy - 32 * size) / size, + x + w - 16 * size, + y + dy + 16 * size, + 16 * size, + h - dy - 32 * size + ); + //下方 + core.drawImage( + ctx, + background, + 128, + 48, + 16, + 16, + x, + y + h - 16 * size, + 16 * size, + 16 * size + ); + core.drawImage( + ctx, + background, + 176, + 48, + 16, + 16, + x + w - 16 * size, + y + h - 16 * size, + 16 * size, + 16 * size + ); - // arrow - if (px != null && py != null) { - if (direction == "up") { - core.drawImage( - ctx, - background, - 128, - 96, - 32, - 32, - px, - y + h - 3 * size, - 32 * size, - 32 * size - ); - } else if (direction == "down") { - core.drawImage( - ctx, - background, - 160, - 96, - 32, - 32, - px, - y - 29 * size, - 32 * size, - 32 * size - ); - } - } - // 仿RM窗口皮肤 ↑ - }; - events.prototype.battle = function (id, x, y, force, callback) { - core.saveAndStopAutomaticRoute(); - id = id || core.getBlockId(x, y); - const cls = core.getClsFromId(id) - if (!id || !cls || !(cls === 'enemys' || cls === 'enemy48')) return core.clearContinueAutomaticRoute(callback); - // 非强制战斗 - if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) { - core.stopSound(); - core.playSound("操作失败"); - core.drawTip("你打不过此怪物!", id); - return core.clearContinueAutomaticRoute(callback); - } - // 自动存档 - if (!core.status.event.id) core.autosave(true); - // 战前事件 - if (!this.beforeBattle(id, x, y)) - return core.clearContinueAutomaticRoute(callback); - // 战后事件 - this.afterBattle(id, x, y); - if (callback) callback(); - }; - -}, + // arrow + if (px != null && py != null) { + if (direction == "up") { + core.drawImage( + ctx, + background, + 128, + 96, + 32, + 32, + px, + y + h - 3 * size, + 32 * size, + 32 * size + ); + } else if (direction == "down") { + core.drawImage( + ctx, + background, + 160, + 96, + 32, + 32, + px, + y - 29 * size, + 32 * size, + 32 * size + ); + } + } + // 仿RM窗口皮肤 ↑ + }; + events.prototype.battle = function (id, x, y, force, callback) { + core.saveAndStopAutomaticRoute(); + id = id || core.getBlockId(x, y); + const cls = core.getClsFromId(id); + if (!id || !cls || !(cls === "enemys" || cls === "enemy48")) + return core.clearContinueAutomaticRoute(callback); + // 非强制战斗 + if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) { + core.stopSound(); + core.playSound("操作失败"); + core.drawTip("你打不过此怪物!", id); + return core.clearContinueAutomaticRoute(callback); + } + // 自动存档 + if (!core.status.event.id) core.autosave(true); + // 战前事件 + if (!this.beforeBattle(id, x, y)) + return core.clearContinueAutomaticRoute(callback); + // 战后事件 + this.afterBattle(id, x, y); + if (callback) callback(); + }; + }, "额外信息": function () { /* 宝石血瓶左下角显示数值 * 注意!!!不要在道具属性中直接操作flags,使用core.status.hero.flags或core.setFlag系列函数代替! @@ -8724,8 +8732,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }; }, "音频系统": function () { - // 在此增加新插件 - /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: + // 在此增加新插件 + /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: @@ -8733,2327 +8741,2081 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = */ - // 将__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))); - } - - 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; - } - - 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.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.playing = true; - - this.output.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.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.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - }); - audio.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.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.route.status === AudioStatus.Playing) { - this.route.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.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.output.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.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) { - source.route = this; - this.output = source.output; - - /** 效果器路由图 */ - this.effectRoute = []; - - /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ - this.endTime = 0; - /** 暂停时播放了多长时间 */ - this.pauseCurrentTime = 0; - /** 当前播放状态 */ - this.player = player; - this.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.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 从音频的什么时候开始播放,单位秒 - */ - async play(when = 0) { - if (this.status === AudioStatus.Playing) return; - this.link(); - await this.player.ac.resume(); - 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(); - if (this.status !== AudioStatus.Playing) { - this.status = AudioStatus.Playing; - } - } - - /** - * 暂停音频播放 - */ - 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 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; - } - - /** - * 继续当前的 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 || !id) 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) => { - bgm = core.getMappedName(bgm); - 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 () { - core.playBgm(bgmController.playingBgm || main.startBgm); - }; - 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 * 1000).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 () { - // 在此增加新插件 - // -------------------- 安装说明 -------------------- // - // 先安装两个在插件简介中说明的前置插件 - // 然后再将本插件复制到插件编写中即可 - // 插件自带一个打怪后显示碎裂特效的功能 - // -------------------- 使用说明 -------------------- // - /* - - 本插件的核心是一个名为 applyFragWith 的函数,打怪后的碎裂特效也是由它执行的。 - 我们来说明一下这个函数的使用方式。 - 1. 引入 - 你可以使用 const { applyFragWith } = core.plugin.frag; 在任何地方来引入这个函数。 - - 2. 函数的参数 - 该函数有三个参数,分别是canvas, length, time。 - 其中,第一个参数意思是,在执行碎裂时,其内容由该画布决定,这个特效并不会修改传入的画布,因此原画布的内容会依然存在 - 第二个参数指的是每个碎片的边长,虽然原则上每个碎片都是正方形,但边缘一周的碎片可能不是正方形,但中间的碎片一定是 - 第三个参数指的是这个特效要执行多长时间 - 第四个参数是一个对象,包含四种配置量,均为可选,分别是maxMoveLength,moveFlush,maxRotate,fragTiming - 当这些不存在时会默认取同名的常量作为默认值。这些值有什么用可以看下面的常量注释。 - - 3. 函数的返回值 - 这个函数会返回一个特效控制器对象,这个控制器共有三个属性。 - animation: 指的是当前特效的高级动画对象 - onEnd: 一个Promise,当这个特效执行完毕后会被 fulfilled - canvas: 特效所显示的画布。这个画布不会自动部署到样板中,需要你手动使用appendChild来部署,具体可参考打怪后碎裂的样例 - - 4. 修改一些常量 - 在下面有四个有注释的常量MAX_MOVE_LENGTH ~ FRAG_TIMING,你可以根据你自己的需要来更改。 - - */ - - if (main.replayChecking) return (core.plugin.frag = {}); - - const { Animation, linear, sleep } = core.plugin.animate; - const { has } = core.plugin.utils; - - /** 最大移动距离,最终位置距离中心的距离变成原来的几倍 */ - const MAX_MOVE_LENGTH = 1.15; - /** 移动距离波动,在最大移动距离的基础上加上多少倍距离的波动距离 */ - const MOVE_FLUSH = 0.7; - /** 最大旋转角,单位是弧度,每个碎片都会有自己的旋转程度,是随机的 */ - const MAX_ROTATE = 0.5; - /** 碎裂动画的速率曲线函数 */ - const FRAG_TIMING = linear(); - + // 将__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 {HTMLCanvasElement} canvas 要执行特效的画布 - * @param {number} length 切分成的碎片的边长,碎片为正方形 - * @param {number} time 特效持续时长 - * @returns 返回一个碎裂特效控制器,是一个对象,详见开头的使用注释 + * 检查一种音频类型是否能被播放 + * @param type 音频类型 AudioType */ - function applyFragWith(canvas, length = 4, time = 2000, config = {}) { - // 先切分图片 - const imgs = splitCanvas(canvas, length); - const cx = canvas.width / 2; - const cy = canvas.height / 2; - - let maxX = 0; - let maxY = 0; - const toMove = imgs.map((v) => { - const centerX = v.x + v.canvas.width / 2; - const centerY = v.y + v.canvas.height / 2; - const onX = centerX === cx; - const onY = centerY === cy; - const mml = config.maxMoveLength ?? MAX_MOVE_LENGTH; - const mf = config.moveFlush ?? MOVE_FLUSH; - const rate = mml - 1 + Math.random() ** 3 * mf; - let endX = onY ? 0 : (centerX - cx) * rate; - let endY = onX ? 0 : (centerY - cy) * rate; - const mx = Math.abs(endX + centerX) + Math.abs(v.canvas.width); - const my = Math.abs(endY + centerY) + Math.abs(v.canvas.height); - if (mx > maxX) maxX = mx; - if (my > maxY) maxY = my; - const r = config.maxRotate ?? MAX_ROTATE; - const endRad = Math.random() * r * 2 - r; - - return { - deltaX: endX, - deltaY: endY, - endRad, - x: centerX, - y: centerY, - canvas: v.canvas, - }; - }); - - // 再执行动画 - const frag = document.createElement("canvas"); - const ctx = frag.getContext("2d"); - const ani = new Animation(); - ani.register("rate", 0); - const ft = config.fragTiming ?? FRAG_TIMING; - ani.absolute().time(time).mode(ft).apply("rate", 1); - frag.width = maxX * 2; - frag.height = maxY * 2; - ctx.save(); - const dw = maxX - canvas.width / 2; - const dh = maxY - canvas.height / 2; - - const fragFn = () => { - const rate = ani.value.rate; - const opacity = 1 - rate; - ctx.globalAlpha = opacity; - ctx.clearRect(0, 0, frag.width, frag.height); - toMove.forEach((v) => { - ctx.save(); - const nx = v.deltaX * rate; - const ny = v.deltaY * rate; - const rotate = v.endRad * rate; - - ctx.translate(nx + v.x + dw, ny + v.y + dh); - ctx.rotate(rotate); - ctx.drawImage( - v.canvas, - nx - v.canvas.width / 2, - ny - v.canvas.height / 2 - ); - ctx.restore(); - }); - }; - const onEnd = () => {}; - ani.ticker.add(fragFn); - - return makeFragManager(frag, ani, time, onEnd); - } - - function makeFragManager(canvas, ani, time, onEnd) { - const promise = sleep(time + 50); - - return { - animation: ani, - - onEnd: promise.then(() => { - ani.ticker.destroy(); - onEnd(); - }), - canvas, - }; - } - - function withImage(image, sx, sy, sw, sh) { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - canvas.width = sw; - canvas.height = sh; - ctx.drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh); - return { canvas, x: sx, y: sy }; - } - - /** - * 切分画布 - * @param canvas 要被切分的画布 - * @param l 切分小块的边长 - */ - function splitCanvas(canvas, l) { - if (canvas.width / l < 2 || canvas.height / l < 2) { - console.warn("切分画布要求切分边长大于等于画布长宽的一半!"); - return []; + 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 w = canvas.width; - const h = canvas.height; - const numX = Math.floor(w / l); - const numY = Math.floor(h / l); - const rw = (w - numX * l) / 2; - const rh = (h - numY * l) / 2; + } - const res = []; + const typeMap = new Map([ + ["ogg", AudioType.Ogg], + ["mp3", AudioType.Mp3], + ["wav", AudioType.Wav], + ["flac", AudioType.Flac], + ["opus", AudioType.Opus], + ["aac", AudioType.Aac], + ]); - if (rw > 0) { - if (rh > 0) { - res.push( - withImage(canvas, 0, 0, rw, rh), - withImage(canvas, 0, canvas.height - rh, rw, rh), - withImage(canvas, canvas.width - rw, 0, rw, rh), - withImage(canvas, canvas.width - rw, canvas.height - rh, rw, rh) - ); - } - for (const x of [0, canvas.width - rw]) { - for (let ny = 0; ny < numY; ny++) { - res.push(withImage(canvas, x, rh + l * ny, rw, l)); + /** + * 根据文件名拓展猜测其类型 + * @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); } } } - if (rh > 0) { - for (const y of [0, canvas.height - rh]) { - for (let nx = 0; nx < numX; nx++) { - res.push(withImage(canvas, rw + l * nx, y, l, rh)); + } + + 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))); + } + + 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; } } } - for (let nx = 0; nx < numX; nx++) { - for (let ny = 0; ny < numY; ny++) { - res.push(withImage(canvas, rw + l * nx, rh + l * ny, l, l)); + + 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; + } } } - - return res; } - const origin = core.events.afterBattle; - core.events.afterBattle = function (enemyId, x, y) { - // 打怪特效 - if (has(x) && has(y)) { - const frame = core.status.globalAnimateStatus % 2; - // 生成怪物图像 - const canvas = document.createElement("canvas"); - canvas.width = 32; - canvas.height = 32; - core.drawIcon(canvas, enemyId, 0, 0, 32, 32, frame); - // 执行动画 - const manager = applyFragWith(canvas); - const frag = manager.canvas; - // 设置特效画布的css属性 - frag.style.imageRendering = "pixelated"; - frag.style.width = `${frag.width * core.domStyle.scale}px`; - frag.style.height = `${frag.height * core.domStyle.scale}px`; - const left = - (x * 32 + 16 - frag.width / 2 - core.bigmap.offsetX) * - core.domStyle.scale; - const top = - (y * 32 + 16 - frag.height / 2 - core.bigmap.offsetY) * - core.domStyle.scale; - frag.style.left = `${left}px`; - frag.style.top = `${top}px`; - frag.style.zIndex = "45"; - frag.style.position = "absolute"; - // 将特效画布部署到样板上 - core.dom.gameDraw.appendChild(frag); - // 当特效执行完毕后移除这个特效画布 - manager.onEnd.then(() => { - frag.remove(); - }); + class VorbisDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggVorbisDecoderWebWorker(); + await this.decoder.ready; } - return origin.apply(this, arguments); + /** + * 摧毁这个解码器 + */ + 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", }; - if ("frag" in core.plugin) { - throw new ReferenceError(`core.plugin上已存在名为frag的属性!`); + function isOggPage(data) { + return !isNil(data.isFirstPage); } - core.plugin.frag = { - applyFragWith, + 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; + } + + 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.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.playing = true; + + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.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.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + }); + audio.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.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.route.status === AudioStatus.Playing) { + this.route.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.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.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) { + source.route = this; + this.output = source.output; + + /** 效果器路由图 */ + this.effectRoute = []; + + /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ + this.endTime = 0; + /** 暂停时播放了多长时间 */ + this.pauseCurrentTime = 0; + /** 当前播放状态 */ + this.player = player; + this.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.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 从音频的什么时候开始播放,单位秒 + */ + async play(when = 0) { + if (this.status === AudioStatus.Playing) return; + this.link(); + await this.player.ac.resume(); + 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(); + if (this.status !== AudioStatus.Playing) { + this.status = AudioStatus.Playing; + } + } + + /** + * 暂停音频播放 + */ + 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 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; + } + + /** + * 继续当前的 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 || !id) 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) => { + bgm = core.getMappedName(bgm); + 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 () { + core.playBgm(bgmController.playingBgm || main.startBgm); + }; + 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 * 1000).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 () { @@ -13096,8 +12858,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.drawImage(ctx, img, 0, 0, 60, 60, fx, fy, w, h); const layer = info.upOrDown[id]; const min = Math.min(w, h); - if (core.getFlag("任务地点") && core.getFlag("任务地点") === id) - ctx.drawImage(tesk, fx + min / 4, fy + min / 4, min / 2, min / 2); + if (layer?.includes("upFloor")) core.drawIcon( ctx, @@ -13116,7 +12877,21 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = min / 2, min / 2 ); - + if (core.getFlag("任务地点") && core.getFlag("任务地点") === id) + ctx.drawImage(tesk, fx + min / 4, fy + min / 4, min / 2, min / 2); + if (id === core.status.floorId) + core.drawImage( + ctx, + "hero.webp", + 0, + 0, + 32, + 19, + fx + min / 4, + fy + (min * 5) / 16, + 32, + 19 + ); // 显示漏怪数量 if (core.getFlag("showEnemy")) { ctx.textAlign = "center"; @@ -13139,1124 +12914,1145 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = ); ctx.shadowBlur = 0; } + if (!core.hasVisitedFloor(id)) { + core.fillRect(ctx, fx, fy, w, h, "rgba(0,0,0,0.7)"); + core.fillText( + ctx, + "?", + fx + min / 2, + fy + (min * 3) / 4, + "#FFFFFF", + "bold 42px Verdana" + ); + } } }; }, "楼传": function () { - // 在此增加新插件 + // 在此增加新插件 - core.canMoveFloor = function () { - let canmove = false; - core.status.thisMap.blocks.forEach((block) => { - if ( - !block.disable && - (block.event.id == "upFloor" || block.event.id == "downFloor") - ) { - let automaticRoute = core.automaticRoute(block.x, block.y); - if (!core.flags.flyNearStair || automaticRoute.length > 0) { - let loc = automaticRoute.pop(); - loc = automaticRoute.pop(); - if (core.canMoveDirectly(loc?.x, loc?.y) >= 0 || !loc) { - canmove = true; - } - } - } - }); - return canmove; - }; - ui.prototype._drawViewMaps_drawHint = function () { - core.playSound("打开界面"); - }; + core.canMoveFloor = function () { + let canmove = false; + core.status.thisMap.blocks.forEach((block) => { + if ( + !block.disable && + (block.event.id == "upFloor" || block.event.id == "downFloor") + ) { + let automaticRoute = core.automaticRoute(block.x, block.y); + if (!core.flags.flyNearStair || automaticRoute.length > 0) { + let loc = automaticRoute.pop(); + loc = automaticRoute.pop(); + if (core.canMoveDirectly(loc?.x, loc?.y) >= 0 || !loc) { + canmove = true; + } + } + } + }); + return canmove; + }; + ui.prototype._drawViewMaps_drawHint = function () { + core.playSound("打开界面"); + }; - ////// 绘制浏览地图界面 ////// - ui.prototype._drawViewMaps = function (index, x, y) { - core.lockControl(); + ////// 绘制浏览地图界面 ////// + ui.prototype._drawViewMaps = function (index, x, y) { + core.lockControl(); - core.clearMap("data"); - core.status.event.id = "viewMaps"; - this.clearUI(); - //console.log(index) - if (index == null) index = core.floorIds.indexOf(core.status.floorId); - core.animateFrame.tip = null; - core.status.checkBlock.cache = {}; - let data = this._drawViewMaps_buildData(index, x, y); - core.drawWindowSkin("winskin1.webp", "ui", 0, 0, 416, 416); - let page = core.status.event.data.index; - let floorId = core.status.event.data.floorId; - core.ui.statusBar._update_map(floorId); - const bfs = core.plugin.bfsSearch(floorId, 1, true); - const mapdir = bfs.mapdir[floorId]; - core.setTextAlign("ui", "center"); - let size = (core.__PIXELS__ * 3) / 4; //312 - const areas = core.getFlag("areas"); + core.clearMap("data"); + core.status.event.id = "viewMaps"; + this.clearUI(); + //console.log(index) + if (index == null) index = core.floorIds.indexOf(core.status.floorId); + core.animateFrame.tip = null; + core.status.checkBlock.cache = {}; + let data = this._drawViewMaps_buildData(index, x, y); + core.drawWindowSkin("winskin1.webp", "ui", 0, 0, 416, 416); + let page = core.status.event.data.index; + let floorId = core.status.event.data.floorId; + core.ui.statusBar._update_map(floorId); + const bfs = core.plugin.bfsSearch(floorId, 1, true); + const mapdir = bfs.mapdir[floorId]; + core.setTextAlign("ui", "center"); + let size = (core.__PIXELS__ * 3) / 4; //312 + const areas = core.getFlag("areas"); - let i = areas.findIndex((v) => v.maps.includes(floorId)); - core.fillRoundRect("ui", 15 - 2, 15 - 2, 35 + 4, 35 + 4, 4, "#444444"); - core.strokeRoundRect( - "ui", - 15 - 4, - 15 - 4, - 35 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - core.fillBoldText1( - "ui", - "当前", - 13 + 20, - 17 + 20, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); + let i = areas.findIndex((v) => v.maps.includes(floorId)); + core.fillRoundRect("ui", 15 - 2, 15 - 2, 35 + 4, 35 + 4, 4, "#444444"); + core.strokeRoundRect( + "ui", + 15 - 4, + 15 - 4, + 35 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + core.fillBoldText1( + "ui", + "当前", + 13 + 20, + 17 + 20, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); - core.fillRoundRect( - "ui", - 15 - 2, - 15 - 2 + 35 + 8 + size + 8 - 54, - 35 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4, - 15 - 4 + 35 + 8 + size + 8 - 54, - 35 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - if ( - !core.status.maps[core.floorIds[page]].canFlyTo || - !core.hasVisitedFloor(core.floorIds[page]) - ) { - core.fillBoldText1( - "ui", - "预览", - 13 + 20, - 17 + 20 + 35 + 8 + size + 8 - 54, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "传送", - 13 + 20, - 17 + 20 + 35 + 8 + size + 8 - 54, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } - core.fillRoundRect( - "ui", - 15 - 4 + size - 2 + 45, - 15 - 2 + size - 4 + 45, - 35 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4 + 45, - 15 - 4 + size - 4 + 45, - 35 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - core.fillBoldText1( - "ui", - "离开", - 15 - 4 + size - 4 + 45 + 22, - 15 - 4 + size - 4 + 45 + 26, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); + core.fillRoundRect( + "ui", + 15 - 2, + 15 - 2 + 35 + 8 + size + 8 - 54, + 35 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4, + 15 - 4 + 35 + 8 + size + 8 - 54, + 35 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + if ( + !core.status.maps[core.floorIds[page]].canFlyTo || + !core.hasVisitedFloor(core.floorIds[page]) + ) { + core.fillBoldText1( + "ui", + "预览", + 13 + 20, + 17 + 20 + 35 + 8 + size + 8 - 54, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "传送", + 13 + 20, + 17 + 20 + 35 + 8 + size + 8 - 54, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } + core.fillRoundRect( + "ui", + 15 - 4 + size - 2 + 45, + 15 - 2 + size - 4 + 45, + 35 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4 + 45, + 15 - 4 + size - 4 + 45, + 35 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + core.fillBoldText1( + "ui", + "离开", + 15 - 4 + size - 4 + 45 + 22, + 15 - 4 + size - 4 + 45 + 26, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); - core.fillRoundRect( - "ui", - 15 + 44 - 2, - 15 - 2, - size + 4 - 58, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 + 44 - 4, - 15 - 4, - size + 8 - 58, - 35 + 8, - 4, - "#444444", - 1 - ); - if (mapdir.includes("up")) { - core.fillBoldText1( - "ui", - "北▲", - 30 + 145 + 10, - 17 + 20, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "北▲", - 30 + 145 + 10, - 17 + 20, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } + core.fillRoundRect( + "ui", + 15 + 44 - 2, + 15 - 2, + size + 4 - 58, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 + 44 - 4, + 15 - 4, + size + 8 - 58, + 35 + 8, + 4, + "#444444", + 1 + ); + if (mapdir.includes("up")) { + core.fillBoldText1( + "ui", + "北▲", + 30 + 145 + 10, + 17 + 20, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "北▲", + 30 + 145 + 10, + 17 + 20, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } - core.fillRoundRect( - "ui", - 15 - 2, - 59 - 2, - 35 + 4, - size + 4 - 58, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4, - 59 - 4, - 35 + 8, - size + 8 - 58, - 4, - "#444444", - 1 - ); - if (mapdir.includes("left")) { - core.fillBoldText1( - "ui", - "西", - 15 + 17, - 25 + 150, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - core.fillBoldText1( - "ui", - "◀", - 15 + 17, - 45 + 150, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "西", - 15 + 17, - 25 + 150, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - core.fillBoldText1( - "ui", - "◀", - 15 + 17, - 45 + 150, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } - core.fillRoundRect( - "ui", - 15 + 44 - 2, - 15 - 2 + size - 4, - size + 4 - 58, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 + 44 - 4, - 15 - 4 + size - 4, - size + 8 - 58, - 35 + 8, - 4, - "#444444", - 1 - ); - if (mapdir.includes("down")) { - core.fillBoldText1( - "ui", - "南▼", - 30 + 145 + 10, - 17 + 20 + size - 4, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "南▼", - 30 + 145 + 10, - 17 + 20 + size - 4, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } + core.fillRoundRect( + "ui", + 15 - 2, + 59 - 2, + 35 + 4, + size + 4 - 58, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4, + 59 - 4, + 35 + 8, + size + 8 - 58, + 4, + "#444444", + 1 + ); + if (mapdir.includes("left")) { + core.fillBoldText1( + "ui", + "西", + 15 + 17, + 25 + 150, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + core.fillBoldText1( + "ui", + "◀", + 15 + 17, + 45 + 150, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "西", + 15 + 17, + 25 + 150, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + core.fillBoldText1( + "ui", + "◀", + 15 + 17, + 45 + 150, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } + core.fillRoundRect( + "ui", + 15 + 44 - 2, + 15 - 2 + size - 4, + size + 4 - 58, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 + 44 - 4, + 15 - 4 + size - 4, + size + 8 - 58, + 35 + 8, + 4, + "#444444", + 1 + ); + if (mapdir.includes("down")) { + core.fillBoldText1( + "ui", + "南▼", + 30 + 145 + 10, + 17 + 20 + size - 4, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "南▼", + 30 + 145 + 10, + 17 + 20 + size - 4, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } - core.fillRoundRect( - "ui", - 15 - 2 + size - 4, - 59 - 2, - 35 + 4, - size + 4 - 58, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4, - 59 - 4, - 35 + 8, - size + 8 - 58, - 4, - "#444444", - 1 - ); - if (mapdir.includes("right")) { - core.fillBoldText1( - "ui", - "东", - 15 + 17 + size - 4, - 25 + 150, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - core.fillBoldText1( - "ui", - "▶", - 15 + 17 + size - 4, - 45 + 150, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "东", - 15 + 17 + size - 4, - 25 + 150, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - core.fillBoldText1( - "ui", - "▶", - 15 + 17 + size - 4, - 45 + 150, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } - core.fillRoundRect( - "ui", - 60 - 2, - 60 - 2, - size - 58 + 4, - size - 58 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 60 - 4, - 60 - 4, - size - 58 + 8, - size - 58 + 8, - 4, - "#444444", - 1 - ); - core.drawThumbnail(floorId, null, { - damage: data.damage, - ctx: "ui", - x: 58, - y: 58, - size: 0.62, - all: data.all, - }); - if ( - !core.status.maps[core.floorIds[page]].canFlyTo || - !core.hasVisitedFloor(core.floorIds[page]) - ) - core.drawImage( - "ui", - "lock.webp", - 0, - 0, - size, - size, + core.fillRoundRect( + "ui", + 15 - 2 + size - 4, + 59 - 2, + 35 + 4, + size + 4 - 58, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4, + 59 - 4, + 35 + 8, + size + 8 - 58, + 4, + "#444444", + 1 + ); + if (mapdir.includes("right")) { + core.fillBoldText1( + "ui", + "东", + 15 + 17 + size - 4, + 25 + 150, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + core.fillBoldText1( + "ui", + "▶", + 15 + 17 + size - 4, + 45 + 150, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "东", + 15 + 17 + size - 4, + 25 + 150, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + core.fillBoldText1( + "ui", + "▶", + 15 + 17 + size - 4, + 45 + 150, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } + core.fillRoundRect( + "ui", + 60 - 2, + 60 - 2, + size - 58 + 4, + size - 58 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 60 - 4, + 60 - 4, + size - 58 + 8, + size - 58 + 8, + 4, + "#444444", + 1 + ); + core.drawThumbnail(floorId, null, { + damage: data.damage, + ctx: "ui", + x: 58, + y: 58, + size: 0.62, + all: data.all, + }); + if ( + !core.status.maps[core.floorIds[page]].canFlyTo || + !core.hasVisitedFloor(core.floorIds[page]) + ) { + /*core.fillRect("ui", 58, 58, - 58, - size - 8, - size - 8 - ); + size - 50, + size - 50, "rgba(0,0,0,0.5)")*/ + core.getContextByName("ui").globalAlpha = 0.7; - core.fillRoundRect( - "ui", - 15 + 44 - 2, - 60 - 2 + size - 4, - size + 4 - 58, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 + 44 - 4, - 60 - 4 + size - 4, - size + 8 - 58, - 35 + 8, - 4, - "#444444", - 1 - ); - core.fillBoldText1( - "ui", - core.status.maps[floorId].areas, - 30 + 145 + 10, - 17 + 65 + size - 4, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - core.fillRoundRect( - "ui", - 15 - 2, - 60 - 2 + size - 4, - 35 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4, - 60 - 4 + size - 4, - 35 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - if (i === 0) { - core.fillBoldText1( - "ui", - "◀", - 30, - 17 + 65 + size - 4, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "◀", - 30, - 17 + 65 + size - 4, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } - core.fillRoundRect( - "ui", - 15 - 2 + size - 4, - 60 - 2 + size - 4, - 35 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4, - 60 - 4 + size - 4, - 35 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - if (i === areas.length - 1) { - core.fillBoldText1( - "ui", - "▶", - 30 + 300 + 10, - 17 + 65 + size - 4, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "▶", - 30 + 300 + 10, - 17 + 65 + size - 4, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } + core.drawImage( + "ui", + "miwu.webp", + 0, + 0, + size, + size, + 58, + 58, + size - 50, + size - 50 + ); + core.getContextByName("ui").globalAlpha = 1; + /*core.fillText("ui", '?', 188, + 278, + "rgba(255,255,255,0.2)", this._buildFont(250, true))*/ + } + core.fillRoundRect( + "ui", + 15 + 44 - 2, + 60 - 2 + size - 4, + size + 4 - 58, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 + 44 - 4, + 60 - 4 + size - 4, + size + 8 - 58, + 35 + 8, + 4, + "#444444", + 1 + ); + core.fillBoldText1( + "ui", + core.status.maps[floorId].areas, + 30 + 145 + 10, + 17 + 65 + size - 4, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + core.fillRoundRect( + "ui", + 15 - 2, + 60 - 2 + size - 4, + 35 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4, + 60 - 4 + size - 4, + 35 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + if (i === 0) { + core.fillBoldText1( + "ui", + "◀", + 30, + 17 + 65 + size - 4, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "◀", + 30, + 17 + 65 + size - 4, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } + core.fillRoundRect( + "ui", + 15 - 2 + size - 4, + 60 - 2 + size - 4, + 35 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4, + 60 - 4 + size - 4, + 35 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + if (i === areas.length - 1) { + core.fillBoldText1( + "ui", + "▶", + 30 + 300 + 10, + 17 + 65 + size - 4, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "▶", + 30 + 300 + 10, + 17 + 65 + size - 4, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } - core.fillRoundRect( - "ui", - 15 - 2 + size - 4, - 15 - 2, - 80 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4, - 15 - 4, - 80 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - if (mapdir.includes("upFloor")) { - core.fillBoldText1( - "ui", - "上楼", - 30 + 320 + 10, - 17 + 20, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "上楼", - 30 + 320 + 10, - 17 + 20, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } - core.fillRoundRect( - "ui", - 15 - 2 + size - 4, - 15 - 2 + size - 4, - 80 + 4, - 35 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4, - 15 - 4 + size - 4, - 80 + 8, - 35 + 8, - 4, - "#444444", - 1 - ); - if (mapdir.includes("downFloor")) { - core.fillBoldText1( - "ui", - "下楼", - 30 + 320 + 10, - 17 + 20 + size - 4, - "#FFFFFF", - "#000000", - 2, - this._buildFont(18, true) - ); - } else { - core.fillBoldText1( - "ui", - "下楼", - 30 + 320 + 10, - 17 + 20 + size - 4, - "#909090", - "#000000", - 2, - this._buildFont(18, true) - ); - } + core.fillRoundRect( + "ui", + 15 - 2 + size - 4, + 15 - 2, + 80 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4, + 15 - 4, + 80 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + if (mapdir.includes("upFloor")) { + core.fillBoldText1( + "ui", + "上楼", + 30 + 320 + 10, + 17 + 20, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "上楼", + 30 + 320 + 10, + 17 + 20, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } + core.fillRoundRect( + "ui", + 15 - 2 + size - 4, + 15 - 2 + size - 4, + 80 + 4, + 35 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4, + 15 - 4 + size - 4, + 80 + 8, + 35 + 8, + 4, + "#444444", + 1 + ); + if (mapdir.includes("downFloor")) { + core.fillBoldText1( + "ui", + "下楼", + 30 + 320 + 10, + 17 + 20 + size - 4, + "#FFFFFF", + "#000000", + 2, + this._buildFont(18, true) + ); + } else { + core.fillBoldText1( + "ui", + "下楼", + 30 + 320 + 10, + 17 + 20 + size - 4, + "#909090", + "#000000", + 2, + this._buildFont(18, true) + ); + } - core.fillRoundRect( - "ui", - 15 - 2 + size - 4 + 35 + 8, - 59 - 2, - 37 + 4, - (size - 58) / 2 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4 + 35 + 8, - 59 - 4, - 37 + 8, - (size - 58) / 2 + 8, - 4, - "#444444", - 1 - ); - const title = core.status.maps[floorId].title; - //const length = title.length - fillTextVertical( - "ui", - title, - 15 - 4 + size - 4 + 45, - 85, - "#FFFFFF", - "#000000", - 18 - ); - //const uictx = main.dom.gameCanvas.ui.getContext('2d') - core.fillRoundRect( - "ui", - 15 - 2 + size - 4 + 35 + 8, - 59 - 2 + (size - 58) / 2 + 8, - 37 + 4, - 119 + 4, - 4, - "#444444" - ); - core.strokeRoundRect( - "ui", - 15 - 4 + size - 4 + 35 + 8, - 59 - 4 + (size - 58) / 2 + 8, - 37 + 8, - 119 + 8, - 4, - "#444444", - 1 - ); - if (core.getFlag("showEnemy")) { - fillTextVertical( - "ui", - "关闭漏怪检测", - 15 - 4 + size - 4 + 45, - 220, - "#FFFFFF", - "#000000", - 18 - ); - } else { - fillTextVertical( - "ui", - "开启漏怪检测", - 15 - 4 + size - 4 + 45, - 220, - "#FFFFFF", - "#000000", - 18 - ); - } + core.fillRoundRect( + "ui", + 15 - 2 + size - 4 + 35 + 8, + 59 - 2, + 37 + 4, + (size - 58) / 2 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4 + 35 + 8, + 59 - 4, + 37 + 8, + (size - 58) / 2 + 8, + 4, + "#444444", + 1 + ); + const title = core.status.maps[floorId].title; + //const length = title.length + fillTextVertical( + "ui", + title, + 15 - 4 + size - 4 + 45, + 85, + core.hasVisitedFloor(floorId) ? "#FFFFFF" : "#444444", + "#000000", + 18 + ); + //const uictx = main.dom.gameCanvas.ui.getContext('2d') + core.fillRoundRect( + "ui", + 15 - 2 + size - 4 + 35 + 8, + 59 - 2 + (size - 58) / 2 + 8, + 37 + 4, + 119 + 4, + 4, + "#444444" + ); + core.strokeRoundRect( + "ui", + 15 - 4 + size - 4 + 35 + 8, + 59 - 4 + (size - 58) / 2 + 8, + 37 + 8, + 119 + 8, + 4, + "#444444", + 1 + ); + if (core.getFlag("showEnemy")) { + fillTextVertical( + "ui", + "关闭漏怪检测", + 15 - 4 + size - 4 + 45, + 220, + "#FFFFFF", + "#000000", + 18 + ); + } else { + fillTextVertical( + "ui", + "开启漏怪检测", + 15 - 4 + size - 4 + 45, + 220, + "#FFFFFF", + "#000000", + 18 + ); + } - //uictx.fillTextVertical(title, 15 - 4 + size - 4 + 35 + 29, 25 + 150) - //fillTextVertical('ui', title, 15 - 4 + size - 4 + 35 + 29, 25 + 150, '#FFFFFF', this._buildFont(18, true)) - }; + //uictx.fillTextVertical(title, 15 - 4 + size - 4 + 35 + 29, 25 + 150) + //fillTextVertical('ui', title, 15 - 4 + size - 4 + 35 + 29, 25 + 150, '#FFFFFF', this._buildFont(18, true)) + }; - function fillTextVertical(name, text, x, y, style, boldstyle, fontsize) { - //竖向文字绘制 + function fillTextVertical(name, text, x, y, style, boldstyle, fontsize) { + //竖向文字绘制 - const ctx = core.ui.getContextByName(name); - if (!ctx) return; - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - fontsize *= 3; - const length = text.length; - canvas.width = fontsize * 2; - canvas.height = fontsize * length * 2; - if (style) context.fillStyle = core.arrayToRGBA(style); - if (boldstyle) context.strokeStyle = core.arrayToRGBA(boldstyle); - context.lineWidth = 2; - if (fontsize) context.font = core.ui._buildFont(fontsize, true); - let arrText = text.split(""); + const ctx = core.ui.getContextByName(name); + if (!ctx) return; + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + fontsize *= 3; + const length = text.length; + canvas.width = fontsize * 2; + canvas.height = fontsize * length * 2; + if (style) context.fillStyle = core.arrayToRGBA(style); + if (boldstyle) context.strokeStyle = core.arrayToRGBA(boldstyle); + context.lineWidth = 2; + if (fontsize) context.font = core.ui._buildFont(fontsize, true); + let arrText = text.split(""); - let arrWidth = arrText.map(function (letter) { - return context.measureText(letter).width; - }); + let arrWidth = arrText.map(function (letter) { + return context.measureText(letter).width; + }); - let align = context.textAlign; + let align = context.textAlign; - let baseline = context.textBaseline; - let sx = fontsize, - sy = fontsize * length; - if (align == "left") { - sx = sx + Math.max.apply(null, arrWidth) / 2; - } else if (align == "right") { - sx = sx - Math.max.apply(null, arrWidth) / 2; - } + let baseline = context.textBaseline; + let sx = fontsize, + sy = fontsize * length; + if (align == "left") { + sx = sx + Math.max.apply(null, arrWidth) / 2; + } else if (align == "right") { + sx = sx - Math.max.apply(null, arrWidth) / 2; + } - if ( - baseline == "bottom" || - baseline == "alphabetic" || - baseline == "ideographic" - ) { - sy = sy - arrWidth[0] / 2; - } else if (baseline == "top" || baseline == "hanging") { - sy = sy + arrWidth[0] / 2; - } + if ( + baseline == "bottom" || + baseline == "alphabetic" || + baseline == "ideographic" + ) { + sy = sy - arrWidth[0] / 2; + } else if (baseline == "top" || baseline == "hanging") { + sy = sy + arrWidth[0] / 2; + } - context.textAlign = "center"; + context.textAlign = "center"; - context.textBaseline = "middle"; - context.lineWidth = 6; + context.textBaseline = "middle"; + context.lineWidth = 6; - // 开始逐字绘制 + // 开始逐字绘制 - arrText.forEach(function (letter, index) { - // 确定下一个字符的纵坐标位置 + arrText.forEach(function (letter, index) { + // 确定下一个字符的纵坐标位置 - context.strokeText(letter, sx, sy); - context.fillText(letter, sx, sy); - // 旋转坐标系还原成初始态 + context.strokeText(letter, sx, sy); + context.fillText(letter, sx, sy); + // 旋转坐标系还原成初始态 - context.setTransform(1, 0, 0, 1, 0, 0); + context.setTransform(1, 0, 0, 1, 0, 0); - // 确定下一个字符的纵坐标位置 + // 确定下一个字符的纵坐标位置 - var letterWidth = 54; + var letterWidth = 54; - sy = sy + letterWidth; - }); + sy = sy + letterWidth; + }); - // 水平垂直对齐方式还原 + // 水平垂直对齐方式还原 - context.textAlign = align; + context.textAlign = align; - context.textBaseline = baseline; + context.textBaseline = baseline; - //绘制到目标位置 - ctx.drawImage( - canvas, - x, - y - (fontsize / 3) * length, - canvas.width / 3, - canvas.height / 3 - ); - } + //绘制到目标位置 + ctx.drawImage( + canvas, + x, + y - (fontsize / 3) * length, + canvas.width / 3, + canvas.height / 3 + ); + } - ////// 点击楼层传送器时的打开操作 ////// - events.prototype.useFly = function (fromUserAction) { - if (!core.isPlaying()) return; - if (!core.status.maps[core.status.floorId].canFlyFrom) { - core.drawTip(core.material.items["fly"].name + "好像失效了", "fly"); - return; - } - // 从“浏览地图”页面:尝试直接传送到该层 - if (core.status.event.id == "viewMaps") { - if (!core.hasItem("fly")) { - core.playSound("操作失败"); - core.drawTip("你没有" + core.material.items["fly"].name, "fly"); - } else if ( - core.flags.flyNearStair && - !core.nearStair() && - !flags.canMoveFloor - ) { - core.playSound("操作失败"); - core.drawTip( - "无法到达楼梯边使用" + core.material.items["fly"].name, - "fly" - ); - } else { - core.flyTo(core.status.event.data.floorId); - core.updateStatusBar(); - } - return; - } + ////// 点击楼层传送器时的打开操作 ////// + events.prototype.useFly = function (fromUserAction) { + if (!core.isPlaying()) return; + if (!core.status.maps[core.status.floorId].canFlyFrom) { + core.drawTip(core.material.items["fly"].name + "好像失效了", "fly"); + return; + } + // 从“浏览地图”页面:尝试直接传送到该层 + if (core.status.event.id == "viewMaps") { + if (!core.hasItem("fly")) { + core.playSound("操作失败"); + core.drawTip("你没有" + core.material.items["fly"].name, "fly"); + } else if ( + core.flags.flyNearStair && + !core.nearStair() && + !flags.canMoveFloor + ) { + core.playSound("操作失败"); + core.drawTip( + "无法到达楼梯边使用" + core.material.items["fly"].name, + "fly" + ); + } else { + core.flyTo(core.status.event.data.floorId); + core.updateStatusBar(); + } + return; + } - if (!this._checkStatus("fly", fromUserAction, true)) return; - //if (core.flags.flyNearStair && !core.nearStair()) + if (!this._checkStatus("fly", fromUserAction, true)) return; + //if (core.flags.flyNearStair && !core.nearStair()) - if ( - (core.flags.flyNearStair && !core.nearStair()) || - !flags.canMoveFloor - ) { - core.playSound("操作失败"); - core.drawTip( - "无法到达楼梯边使用" + core.material.items["fly"].name, - "fly" - ); - core.unlockControl(); - core.status.event.data = null; - core.status.event.id = null; - return; - } - if (!core.canUseItem("fly")) { - core.playSound("操作失败"); - core.drawTip(core.material.items["fly"].name + "好像失效了", "fly"); - core.unlockControl(); - core.status.event.data = null; - core.status.event.id = null; - return; - } - core.playSound("打开界面"); - core.useItem("fly", true); - core.updateStatusBar(); - return; - }; - ////// 系统菜单栏界面时的点击操作 ////// - actions.prototype._clickSettings = function (x, y) { - if (this._out(x)) return; - var choices = core.status.event.ui.choices; - var topIndex = this._getChoicesTopIndex(choices.length); - if (y >= topIndex && y < topIndex + choices.length) { - var selection = y - topIndex; - core.status.event.selection = selection; - switch (selection) { - case 0: - core.status.event.selection = 0; - core.playSound("确定"); - core.ui._drawSwitchs(); - break; - case 1: - // core.playSound('确定'); - core.ui._drawKeyBoard(); - break; - case 2: - // core.playSound('确定'); - core.clearUI(); - core.useItem("fly"); - break; - case 3: - core.status.event.selection = 0; - core.playSound("确定"); - core.ui._drawNotes(); - break; - case 4: - core.status.event.selection = 0; - core.playSound("确定"); - core.ui._drawSyncSave(); - break; - case 5: - core.status.event.selection = 0; - core.playSound("确定"); - core.ui._drawGameInfo(); - break; - case 6: - return core.confirmRestart(); - case 7: - core.playSound("取消"); - core.ui.closePanel(); - break; - } - } - return; - }; - ////// 查看地图界面时的点击操作 ////// - actions.prototype._clickViewMaps = function (x, y, px, py) { - if (core.status.event.data == null) { - core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); - return; - } - let now = core.floorIds.indexOf(core.status.floorId); - let index = core.status.event.data.index; - let cx = core.status.event.data.x, - cy = core.status.event.data.y; - let floorId = core.floorIds[index], - mw = core.floors[floorId].width, - mh = core.floors[floorId].height; - let perpx = core.__PIXELS__ / 5, - cornerpx = (perpx * 3) / 4; - const bfs = core.plugin.bfsSearch(floorId, 1, true); - const mapdir = bfs.mapdir[floorId]; - const res = bfs.res; - const formto = {}; - for (let from in res) { - const to = res[from]; - const [fromfloorId, fromsx, fromsy, dir] = from.split("_"); - const [tofloorId, tosx, tosy] = to.split("_"); - if (!formto[fromfloorId]) formto[fromfloorId] = {}; - if (!formto[fromfloorId][dir]) formto[fromfloorId][dir] = tofloorId; - } - const areas = core.getFlag("areas"); - let i = areas.findIndex((v) => v.maps.includes(floorId)); + if ( + (core.flags.flyNearStair && !core.nearStair()) || + !flags.canMoveFloor + ) { + core.playSound("操作失败"); + core.drawTip( + "无法到达楼梯边使用" + core.material.items["fly"].name, + "fly" + ); + core.unlockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + if (!core.canUseItem("fly")) { + core.playSound("操作失败"); + core.drawTip(core.material.items["fly"].name + "好像失效了", "fly"); + core.unlockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + core.playSound("打开界面"); + core.useItem("fly", true); + core.updateStatusBar(); + return; + }; + ////// 系统菜单栏界面时的点击操作 ////// + actions.prototype._clickSettings = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound("确定"); + core.ui._drawSwitchs(); + break; + case 1: + // core.playSound('确定'); + core.ui._drawKeyBoard(); + break; + case 2: + // core.playSound('确定'); + core.clearUI(); + core.useItem("fly"); + break; + case 3: + core.status.event.selection = 0; + core.playSound("确定"); + core.ui._drawNotes(); + break; + case 4: + core.status.event.selection = 0; + core.playSound("确定"); + core.ui._drawSyncSave(); + break; + case 5: + core.status.event.selection = 0; + core.playSound("确定"); + core.ui._drawGameInfo(); + break; + case 6: + return core.confirmRestart(); + case 7: + core.playSound("取消"); + core.ui.closePanel(); + break; + } + } + return; + }; + ////// 查看地图界面时的点击操作 ////// + actions.prototype._clickViewMaps = function (x, y, px, py) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + let now = core.floorIds.indexOf(core.status.floorId); + let index = core.status.event.data.index; + let cx = core.status.event.data.x, + cy = core.status.event.data.y; + let floorId = core.floorIds[index], + mw = core.floors[floorId].width, + mh = core.floors[floorId].height; + let perpx = core.__PIXELS__ / 5, + cornerpx = (perpx * 3) / 4; + const bfs = core.plugin.bfsSearch(floorId, 1, true); + const mapdir = bfs.mapdir[floorId]; + const res = bfs.res; + const formto = {}; + for (let from in res) { + const to = res[from]; + const [fromfloorId, fromsx, fromsy, dir] = from.split("_"); + const [tofloorId, tosx, tosy] = to.split("_"); + if (!formto[fromfloorId]) formto[fromfloorId] = {}; + if (!formto[fromfloorId][dir]) formto[fromfloorId][dir] = tofloorId; + } + const areas = core.getFlag("areas"); + let i = areas.findIndex((v) => v.maps.includes(floorId)); - if (px >= 11 && px <= 54 && py >= 11 && py <= 54) { - core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); - } else if (px >= 362 && px <= 407 && py >= 191 && py <= 318) { - flags.showEnemy = !flags.showEnemy; - core.ui._drawViewMaps(index); - } else if (px >= 364 && px <= 407 && py >= 364 && py <= 407) { - core.clearMap("data"); - core.playSound("取消"); - core.ui.closePanel(); - core.getItemDetail(); - core.redrawMap(); - core.updateStatusBar(); - core.ui.statusBar._update_map(); - return; - } else if (px >= 55 && px <= 317 && py >= 11 && py <= 54) { - if (mapdir.includes("up")) - core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].up)); - } else if (px >= 55 && px <= 317 && py >= 319 && py <= 362) { - if (mapdir.includes("down")) - core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].down)); - } else if (px >= 11 && px <= 54 && py >= 55 && py <= 317) { - if (mapdir.includes("left")) - core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].left)); - } else if (px >= 319 && px <= 362 && py >= 55 && py <= 317) { - if (mapdir.includes("right")) - core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].right)); - } else if (px >= 319 && px <= 407 && py >= 11 && py <= 54) { - if (mapdir.includes("upFloor")) - core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].upFloor)); - } else if (px >= 319 && px <= 407 && py >= 319 && py <= 362) { - if (mapdir.includes("downFloor")) - core.ui._drawViewMaps( - core.floorIds.indexOf(formto[floorId].downFloor) - ); - } else if ( - px >= 55 && - px <= 317 && - py >= 55 && - py <= 317 && - core.isPlaying() - ) { - core.useFly(false); - return; - } else if (px >= 11 && px <= 54 && py >= 364 && py <= 407) { - if (i > 0) { - i -= 1; - core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0])); - } - } else if (px >= 319 && px <= 362 && py >= 364 && py <= 407) { - if (i < areas.length - 1) { - i += 1; - core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0])); - } - } - }; - const replayAction_fly = function (action) { - //楼层传送的录像操作 - if (action.indexOf("fly:") != 0) return false; - var floorId = action.substring(4); - var toIndex = core.floorIds.indexOf(floorId); - if ( - !core.canUseItem("fly") || - (core.flags.flyNearStair && !core.nearStair() && !flags.canMoveFloor) - ) - return false; - core.ui._drawViewMaps(toIndex); - if (core.status.replay.speed == 24) { - if (!core.flyTo(floorId, core.replay)) - core.control._replay_error(action); - return true; - } - setTimeout(function () { - if (!core.flyTo(floorId, core.replay)) - core.control._replay_error(action); - }, core.control.__replay_getTimeout()); - return true; - }; - core.registerReplayAction("fly", replayAction_fly); - ////// 查看地图界面时,放开某个键的操作 ////// + if (px >= 11 && px <= 54 && py >= 11 && py <= 54) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + } else if (px >= 362 && px <= 407 && py >= 191 && py <= 318) { + flags.showEnemy = !flags.showEnemy; + core.ui._drawViewMaps(index); + } else if (px >= 364 && px <= 407 && py >= 364 && py <= 407) { + core.clearMap("data"); + core.playSound("取消"); + core.ui.closePanel(); + core.getItemDetail(); + core.redrawMap(); + core.updateStatusBar(); + core.ui.statusBar._update_map(); + return; + } else if (px >= 55 && px <= 317 && py >= 11 && py <= 54) { + if (mapdir.includes("up")) + core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].up)); + } else if (px >= 55 && px <= 317 && py >= 319 && py <= 362) { + if (mapdir.includes("down")) + core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].down)); + } else if (px >= 11 && px <= 54 && py >= 55 && py <= 317) { + if (mapdir.includes("left")) + core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].left)); + } else if (px >= 319 && px <= 362 && py >= 55 && py <= 317) { + if (mapdir.includes("right")) + core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].right)); + } else if (px >= 319 && px <= 407 && py >= 11 && py <= 54) { + if (mapdir.includes("upFloor")) + core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].upFloor)); + } else if (px >= 319 && px <= 407 && py >= 319 && py <= 362) { + if (mapdir.includes("downFloor")) + core.ui._drawViewMaps( + core.floorIds.indexOf(formto[floorId].downFloor) + ); + } else if ( + px >= 55 && + px <= 317 && + py >= 55 && + py <= 317 && + core.isPlaying() + ) { + core.useFly(false); + return; + } else if (px >= 11 && px <= 54 && py >= 364 && py <= 407) { + if (i > 0) { + i -= 1; + core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0])); + } + } else if (px >= 319 && px <= 362 && py >= 364 && py <= 407) { + if (i < areas.length - 1) { + i += 1; + core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0])); + } + } + }; + const replayAction_fly = function (action) { + //楼层传送的录像操作 + if (action.indexOf("fly:") != 0) return false; + var floorId = action.substring(4); + var toIndex = core.floorIds.indexOf(floorId); + if ( + !core.canUseItem("fly") || + (core.flags.flyNearStair && !core.nearStair() && !flags.canMoveFloor) + ) + return false; + core.ui._drawViewMaps(toIndex); + if (core.status.replay.speed == 24) { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + return true; + } + setTimeout(function () { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + }, core.control.__replay_getTimeout()); + return true; + }; + core.registerReplayAction("fly", replayAction_fly); + ////// 查看地图界面时,放开某个键的操作 ////// - actions.prototype._keyUpViewMaps = function (keycode) { - if (core.status.event.data == null) { - core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); - return; - } - var floorId = core.floorIds[core.status.event.data.index]; + actions.prototype._keyUpViewMaps = function (keycode) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + var floorId = core.floorIds[core.status.event.data.index]; - if (keycode == 27 || keycode == 71) { - core.clearMap("data"); - core.playSound("取消"); - core.ui.closePanel(); - core.getItemDetail(); - core.redrawMap(); - core.ui.statusBar._update_map(); - core.updateStatusBar(); - return; - } + if (keycode == 27 || keycode == 71) { + core.clearMap("data"); + core.playSound("取消"); + core.ui.closePanel(); + core.getItemDetail(); + core.redrawMap(); + core.ui.statusBar._update_map(); + core.updateStatusBar(); + return; + } - if (keycode == 86) { - core.status.event.data.damage = !core.status.event.data.damage; - core.playSound("光标移动"); - core.ui._drawViewMaps(core.status.event.data); - return; - } - if (keycode == 66 || keycode == 88) { - if (core.isReplaying()) { - core.control._replay_book(); - } else { - core.openBook(false); - } - return; - } - if ( - (keycode == 13 || keycode == 32 || keycode == 67) && - !core.isReplaying() - ) { - core.useFly(false); - return; - } - return; - }; - actions.prototype._keyDownViewMaps = function (keycode) { - if (core.status.event.data == null) return; + if (keycode == 86) { + core.status.event.data.damage = !core.status.event.data.damage; + core.playSound("光标移动"); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 66 || keycode == 88) { + if (core.isReplaying()) { + core.control._replay_book(); + } else { + core.openBook(false); + } + return; + } + if ( + (keycode == 13 || keycode == 32 || keycode == 67) && + !core.isReplaying() + ) { + core.useFly(false); + return; + } + return; + }; + actions.prototype._keyDownViewMaps = function (keycode) { + if (core.status.event.data == null) return; - var floorId = core.floorIds[core.status.event.data.index], - mh = core.floors[floorId].height; + var floorId = core.floorIds[core.status.event.data.index], + mh = core.floors[floorId].height; - if (keycode == 39) this._clickViewMaps(9, 1, 330, 250); - if (keycode == 37) this._clickViewMaps(9, 8, 25, 200); - if (keycode == 40) this._clickViewMaps(9, 6, 250, 330); - if (keycode == 38) this._clickViewMaps(9, 3, 200, 25); - if (keycode == 34) this._clickViewMaps(9, 3, 350, 330); - if (keycode == 33) this._clickViewMaps(9, 3, 350, 25); - return; - }; + if (keycode == 39) this._clickViewMaps(9, 1, 330, 250); + if (keycode == 37) this._clickViewMaps(9, 8, 25, 200); + if (keycode == 40) this._clickViewMaps(9, 6, 250, 330); + if (keycode == 38) this._clickViewMaps(9, 3, 200, 25); + if (keycode == 34) this._clickViewMaps(9, 3, 350, 330); + if (keycode == 33) this._clickViewMaps(9, 3, 350, 25); + return; + }; - actions.prototype._sys_onmousewheel = function (direct) { - // 向下滚动是 -1 ,向上是 1 + actions.prototype._sys_onmousewheel = function (direct) { + // 向下滚动是 -1 ,向上是 1 - if (this._checkReplaying()) { - // 滚轮控制速度 - if (direct == 1) core.speedUpReplay(); - if (direct == -1) core.speedDownReplay(); - return; - } + if (this._checkReplaying()) { + // 滚轮控制速度 + if (direct == 1) core.speedUpReplay(); + if (direct == -1) core.speedDownReplay(); + return; + } - // 楼层飞行器 - if (core.status.lockControl && core.status.event.id == "fly") { - if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1)); - if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1)); - return; - } + // 楼层飞行器 + if (core.status.lockControl && core.status.event.id == "fly") { + if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1)); + if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1)); + return; + } - // 怪物手册 - if (core.status.lockControl && core.status.event.id == "book") { - var pageinfo = core.ui._drawBook_pageinfo(); - if (direct == 1) - core.ui.drawBook(core.status.event.data - pageinfo.per_page); - if (direct == -1) - core.ui.drawBook(core.status.event.data + pageinfo.per_page); - return; - } + // 怪物手册 + if (core.status.lockControl && core.status.event.id == "book") { + var pageinfo = core.ui._drawBook_pageinfo(); + if (direct == 1) + core.ui.drawBook(core.status.event.data - pageinfo.per_page); + if (direct == -1) + core.ui.drawBook(core.status.event.data + pageinfo.per_page); + return; + } - // 存读档 - if ( - core.status.lockControl && - (core.status.event.id == "save" || core.status.event.id == "load") - ) { - var index = - core.status.event.data.page * 10 + core.status.event.data.offset; - if (direct == 1) core.ui._drawSLPanel(index - 10); - if (direct == -1) core.ui._drawSLPanel(index + 10); - return; - } + // 存读档 + if ( + core.status.lockControl && + (core.status.event.id == "save" || core.status.event.id == "load") + ) { + var index = + core.status.event.data.page * 10 + core.status.event.data.offset; + if (direct == 1) core.ui._drawSLPanel(index - 10); + if (direct == -1) core.ui._drawSLPanel(index + 10); + return; + } - // 浏览地图 - if (core.status.lockControl && core.status.event.id == "viewMaps") { - let floorId = core.floorIds[core.status.event.data.index]; - if (!flags.__visited__[floorId]) floorId = core.status.floorId; - const visit = Object.keys(flags.__visited__); - let index = visit.indexOf(floorId); - if (direct == 1) { - if (index > 0) - core.ui._drawViewMaps(core.floorIds.indexOf(visit[index - 1])); - } - if (direct == -1) { - if (index < visit.length - 1) - core.ui._drawViewMaps(core.floorIds.indexOf(visit[index + 1])); - } - return; - } + // 浏览地图 + if (core.status.lockControl && core.status.event.id == "viewMaps") { + let floorId = core.floorIds[core.status.event.data.index]; + if (!flags.__visited__[floorId]) floorId = core.status.floorId; + const visit = Object.keys(flags.__visited__); + let index = visit.indexOf(floorId); + if (direct == 1) { + if (index > 0) + core.ui._drawViewMaps(core.floorIds.indexOf(visit[index - 1])); + } + if (direct == -1) { + if (index < visit.length - 1) + core.ui._drawViewMaps(core.floorIds.indexOf(visit[index + 1])); + } + return; + } - // wait事件 - if ( - core.status.lockControl && - core.status.event.id == "action" && - core.status.event.data.type == "wait" - ) { - var timeout = - Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; - core.setFlag("type", 0); - var keycode = direct == 1 ? 33 : 34; - core.setFlag("keycode", keycode); - core.setFlag("timeout", timeout); - var executed = core.events.__action_wait_afterGet( - core.status.event.data.current - ); - if (executed || !core.status.event.data.current.forceChild) { - core.status.route.push("input:" + (1e8 * timeout + keycode)); - clearTimeout(core.status.event.interval); - delete core.status.event.timeout; - core.doAction(); - } - return; - } - }; - core.registerAction( - "onmousewheel", - "_sys_onmousewheel", - actions.prototype._sys_onmousewheel, - 0 - ); -}, + // wait事件 + if ( + core.status.lockControl && + core.status.event.id == "action" && + core.status.event.data.type == "wait" + ) { + var timeout = + Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag("type", 0); + var keycode = direct == 1 ? 33 : 34; + core.setFlag("keycode", keycode); + core.setFlag("timeout", timeout); + var executed = core.events.__action_wait_afterGet( + core.status.event.data.current + ); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + keycode)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + }; + core.registerAction( + "onmousewheel", + "_sys_onmousewheel", + actions.prototype._sys_onmousewheel, + 0 + ); + }, "CG回廊": function () { // 在此增加新插件 const CGUI = document.createElement("canvas"); //CGui画布设置 @@ -15425,12 +15221,15 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = const pageupboxVertical = makeBox([18, 300], [100, 200]); const pagedownbox = makeBox([1500, 1130], [200, 100]); const pagedownboxVertical = makeBox([18, 1500], [100, 200]); + const soundbox = makeBox([550, 150], [100, 900]); + const soundboxVertical = makeBox([198, 550], [900, 100]); if (this.log) { if ( (core.domStyle.isVertical && inRect(pos, backboxVertical)) || (!core.domStyle.isVertical && inRect(pos, backbox)) ) { core.clearMap(logctx); + core.stopSound() main.dom.logcanvas.style.display = "none"; this.log = false; } else if ( @@ -15447,6 +15246,17 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.clearMap(logctx); if (this.page < this.overpage) this.page++; this.logdraw(this.page); + } else if ((core.domStyle.isVertical && inRect(pos, soundboxVertical)) || + (!core.domStyle.isVertical && inRect(pos, soundbox))) { + if (core.domStyle.isVertical) { + const sound = this.textList[(this.page - 1) * 6 + Math.min(Math.floor((px - 198) / 150), 5)][2] + core.stopSound() + core.playSound(sound) + } else { + const sound = this.textList[(this.page - 1) * 6 + Math.min(Math.floor((py - 150) / 150), 5)][2] + core.stopSound() + core.playSound(sound) + } } } else { if ( @@ -15758,6 +15568,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = const name = this.textList[i][0] ? "【" + this.textList[i][0] + "】" : ""; + const sound = this.textList[i][2] if (name) { core.fillBoldText1( logctx, @@ -15770,6 +15581,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.ui._buildFont(42, true) ); } + if (sound) core.drawImage(logctx, "sound.webp", 550, posy + 30) if (text.length < 30) { core.fillBoldText1( logctx, @@ -18096,7 +17908,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = music.loop = "loop"; core.material.bgms[name] = music;*/ }; - }, "横屏切换": function () { // 在此增加新插件 @@ -18210,7 +18021,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.setFlag("animate_" + name); }; let thistime = 0; - this.playanimate = function (name, x, y, hero, scalex, scaley) { + this.playanimate = function (name, x, y, hero, scalex, scaley, callback) { const data = { name: name, x: x, @@ -18219,6 +18030,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = scalex: scalex, scaley: scaley, farme: 0, + callback, }; core.plugin.playing.add(data); @@ -18357,21 +18169,26 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } }); one.farme++; - if (one.farme > data.allFarme) core.plugin.playing.delete(one); + if (one.farme > data.allFarme) { + core.plugin.playing.delete(one); + if (one.callback) { + one.callback(); + } + } } }); } }); }, "intro&loop": function () { - // 在此增加新插件 - this.introAndLoop = function (intro, time, loop) { - core.playBgm(intro); - setTimeout(() => { - core.playBgm(loop); - }, time * 1000); - }; -}, + // 在此增加新插件 + this.introAndLoop = function (intro, time, loop) { + core.playBgm(intro); + setTimeout(() => { + core.playBgm(loop); + }, time * 1000); + }; + }, "开局选项悬停": function () { // 在此增加新插件 @@ -18708,876 +18525,1044 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }; }, "回合战斗动画": function () { - // 在此增加新插件 - const animateAttack = document.createElement("canvas"); //画布设置 - animateAttack.style.zIndex = 80; - animateAttack.id = "animateAttack"; - animateAttack.classList.add("gameCanvas", "anti-aliasing"); - animateAttack.style.display = "block"; - animateAttack.width = 416; - animateAttack.height = 416; - animateAttack.style.width = core.__PIXELS__ * core.domStyle.scale + "px"; - animateAttack.style.height = core.__PIXELS__ * core.domStyle.scale + "px"; - main.dom.animateAttack = animateAttack; - const ctx = animateAttack.getContext("2d"); + // 在此增加新插件 + const animateAttack = document.createElement("canvas"); //画布设置 + animateAttack.style.zIndex = 80; + animateAttack.id = "animateAttack"; + animateAttack.classList.add("gameCanvas", "anti-aliasing"); + animateAttack.style.display = "block"; + animateAttack.width = 416; + animateAttack.height = 416; + animateAttack.style.width = core.__PIXELS__ * core.domStyle.scale + "px"; + animateAttack.style.height = core.__PIXELS__ * core.domStyle.scale + "px"; + main.dom.animateAttack = animateAttack; + const ctx = animateAttack.getContext("2d"); - main.dom.gameDraw.appendChild(animateAttack); + main.dom.gameDraw.appendChild(animateAttack); - this.drawAttackAnimate = function ( - heroInfo, - oneTurn, - enemyInfo, - equipInfo, - farme - ) { - core.clearMap(ctx); - let animate = Math.floor(farme / 30); - core.fillRect(ctx, 64, 64, 288, 288, "rgba(0,0,0,0.5)"); - core.strokeRect(ctx, 64, 64, 288, 288, "rgba(255,255,255,0.5)", 4); - core.setTextAlign(ctx, "center"); - core.fillBoldText( - ctx, - hero.name, - 127, - 123, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.setTextAlign(ctx, "left"); - core.drawIcon(ctx, 'hp', 70, 190, 16, 16) - core.drawIcon(ctx, 'atk', 70, 210, 16, 16) - core.drawIcon(ctx, 'def', 70, 230, 16, 16) - core.drawIcon(ctx, 'I374', 70, 250, 16, 16) - core.drawIcon(ctx, 'I375', 70, 270, 16, 16) - core.drawIcon(ctx, 'mdef', 70, 290, 16, 16) - core.drawIcon(ctx, 'amulet', 70, 310, 16, 16) - core.drawIcon(ctx, 'jumpShoes', 70, 330, 16, 16) - core.fillBoldText( - ctx, - '生命 ' + core.formatBigNumber(heroInfo.hp, true) + " / " + core.formatBigNumber(heroInfo.hpmax, true), - 90, - 205, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - '攻击 ' + core.formatBigNumber(heroInfo.atk), - 90, - 225, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - '防御 ' + core.formatBigNumber(heroInfo.def), - 90, - 245, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - "法强 " + core.formatBigNumber(heroInfo.spell), - 90, - 265, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - "法攻 " + core.formatBigNumber(heroInfo.matk / 100 * heroInfo.spell) + "(" + heroInfo.matk + "%)", - 90, - 285, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - '护盾 ' + core.formatBigNumber(heroInfo.mdef / 100 * heroInfo.spell) + "(" + heroInfo.mdef + "%)", - 90, - 305, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - '法抗 ' + heroInfo.spelldef + "%", - 90, - 325, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - '速度 ' + core.formatBigNumber(heroInfo.speed), - 90, - 345, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); + this.drawAttackAnimate = function ( + heroInfo, + oneTurn, + enemyInfo, + equipInfo, + farme, + damageInfo + ) { + let attack = false; + if (heroInfo.isAttack) attack = true; + if (enemyInfo.isAttack) attack = true; + equipInfo.forEach(function (v) { + if (v.isAttack) attack = true; + }); + core.clearMap(ctx); + let animate = Math.floor(farme / 30); + core.fillRect(ctx, 64, 64, 288, 288, "rgba(0,0,0,0.5)"); + core.strokeRect(ctx, 64, 64, 288, 288, "rgba(255,255,255,0.5)", 4); + core.setTextAlign(ctx, "center"); + core.fillBoldText( + ctx, + hero.name, + 127, + 128, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.setTextAlign(ctx, "left"); + core.drawIcon(ctx, "hp", 70, 190, 16, 16); + core.drawIcon(ctx, "atk", 70, 210, 16, 16); + core.drawIcon(ctx, "def", 70, 230, 16, 16); + core.drawIcon(ctx, "I374", 70, 250, 16, 16); + core.drawIcon(ctx, "I375", 70, 270, 16, 16); + core.drawIcon(ctx, "mdef", 70, 290, 16, 16); + core.drawIcon(ctx, "amulet", 70, 310, 16, 16); + core.drawIcon(ctx, "jumpShoes", 70, 330, 16, 16); + core.fillBoldText( + ctx, + "生命 " + + core.formatBigNumber(heroInfo.hp, true) + + " / " + + core.formatBigNumber(heroInfo.hpmax, true), + 90, + 205, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "攻击 " + core.formatBigNumber(heroInfo.atk), + 90, + 225, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "防御 " + core.formatBigNumber(heroInfo.def), + 90, + 245, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "法强 " + core.formatBigNumber(heroInfo.spell), + 90, + 265, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "法攻 " + + core.formatBigNumber((heroInfo.matk / 100) * heroInfo.spell) + + "(" + + heroInfo.matk + + "%)", + 90, + 285, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "护盾 " + + core.formatBigNumber((heroInfo.mhp / 100) * heroInfo.spell) + + "(" + + heroInfo.mhp + + "%)", + 90, + 305, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "法抗 " + heroInfo.mdef + "%", + 90, + 325, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "速度 " + core.formatBigNumber(heroInfo.speed), + 90, + 345, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); - core.strokeRect(ctx, 112, 139, 32, 48, "rgba(255,255,255,1)", 1); - core.drawImage( - ctx, - "hero.webp", - 32 * (animate % 4), - 0, - 32, - 48, - 112, - 139, - 32, - 48 - ); + core.strokeRect(ctx, 112, 139, 32, 48, "rgba(255,255,255,1)", 1); + core.drawImage( + ctx, + "hero.webp", + 32 * (animate % 4), + 0, + 32, + 48, + 112, + 139, + 32, + 48 + ); + core.setTextAlign(ctx, "center"); + core.fillBoldText( + ctx, + enemyInfo.name, + 289, + 128, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); - core.setTextAlign(ctx, "center"); - core.fillBoldText( - ctx, - enemyInfo.name, - 289, - 123, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.setTextAlign(ctx, "right"); - if (enemyInfo.cls === "enemys") { - core.strokeRect(ctx, 272, 155, 32, 32, "rgba(255,255,255,1)", 1); + core.setTextAlign(ctx, "right"); + if (enemyInfo.cls === "enemys") { + core.strokeRect(ctx, 272, 155, 32, 32, "rgba(255,255,255,1)", 1); - core.drawImage( - ctx, - core.getBlockInfo(enemyInfo.id).image, - 32 * (animate % 2), - core.getBlockInfo(enemyInfo.id).posY * 32, - 32, - 32, - 272, - 155, - 32, - 32 - ); - } else { - core.strokeRect(ctx, 272, 139, 32, 48, "rgba(255,255,255,1)", 1); + core.drawImage( + ctx, + core.getBlockInfo(enemyInfo.id).image, + 32 * (animate % 2), + core.getBlockInfo(enemyInfo.id).posY * 32, + 32, + 32, + 272, + 155, + 32, + 32 + ); + } else { + core.strokeRect(ctx, 272, 139, 32, 48, "rgba(255,255,255,1)", 1); - core.drawImage( - ctx, - core.getBlockInfo(enemyInfo.id).image, - 32 * (animate % 4), - core.getBlockInfo(enemyInfo.id).posY * 48, - 32, - 48, - 272, - 139, - 32, - 48 - ); - } - core.drawIcon(ctx, 'hp', 330, 190, 16, 16) - core.drawIcon(ctx, 'atk', 330, 210, 16, 16) - core.drawIcon(ctx, 'def', 330, 230, 16, 16) - core.drawIcon(ctx, 'amulet', 330, 250, 16, 16) - core.drawIcon(ctx, 'jumpShoes', 330, 270, 16, 16) - core.fillBoldText( - ctx, - core.formatBigNumber(enemyInfo.hp, true) + ' 生命', - 330, - 205, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - core.formatBigNumber(enemyInfo.atk) + ' 攻击', - 330, - 225, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - core.formatBigNumber(enemyInfo.def) + ' 防御', - 330, - 245, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); + core.drawImage( + ctx, + core.getBlockInfo(enemyInfo.id).image, + 32 * (animate % 4), + core.getBlockInfo(enemyInfo.id).posY * 48, + 32, + 48, + 272, + 139, + 32, + 48 + ); + } + core.drawIcon(ctx, "hp", 330, 190, 16, 16); + core.drawIcon(ctx, "atk", 330, 210, 16, 16); + core.drawIcon(ctx, "def", 330, 230, 16, 16); + core.drawIcon(ctx, "amulet", 330, 250, 16, 16); + core.drawIcon(ctx, "jumpShoes", 330, 270, 16, 16); + core.fillBoldText( + ctx, + core.formatBigNumber(enemyInfo.hp, true) + " 生命", + 330, + 205, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + core.formatBigNumber(enemyInfo.atk) + " 攻击", + 330, + 225, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + core.formatBigNumber(enemyInfo.def) + " 防御", + 330, + 245, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); - core.fillBoldText( - ctx, - (enemyInfo.mdef ?? 0) * 100 + "% 法抗", - 330, - 265, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText( - ctx, - core.formatBigNumber(enemyInfo.speed) + " 速度", - 330, - 285, - "#FFFFFF", - "#000000", - 'bold 14px pala' - ); - core.fillBoldText(ctx, - "V", - 219, - 163, - "#FFFFFF", - "#000000", - 'bold 48px pala') - core.fillBoldText(ctx, - "s", - 231, - 163, - "#FFFFFF", - "#000000", - 'bold 36px pala') - }; + core.fillBoldText( + ctx, + (enemyInfo.mdef ?? 0) * 100 + "% 法抗", + 330, + 265, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + core.formatBigNumber(enemyInfo.speed) + " 速度", + 330, + 285, + "#FFFFFF", + "#000000", + "bold 14px pala" + ); + core.fillBoldText( + ctx, + "V", + 219, + 163, + "#FFFFFF", + "#000000", + "bold 48px pala" + ); + core.fillBoldText( + ctx, + "s", + 231, + 163, + "#FFFFFF", + "#000000", + "bold 36px pala" + ); - this.attackAnimate = function ( - enemyId, - enemyInfo, - damageInfo, - equipInfo = [] - ) { - //参数分别为怪物真实属性,战斗信息,特殊装备(如火焰风衣)属性特殊装备属性为以元组{equipId,oneDamage,speed,now:0}构成的数组(列出每个需要计算的特殊装备,没有则为空数组或不填) - core.lockControl(); - core.clearMap(ctx); - core.status.event.id = "attackAnimate"; - let hero_hp = core.getRealStatusOrDefault(hero, "hp"), - hero_atk = core.getRealStatusOrDefault(hero, "atk"), - hero_def = core.getRealStatusOrDefault(hero, "def"), - hero_spell = core.getRealStatusOrDefault(hero, "spell"), - hero_matk = core.getRealStatusOrDefault(hero, "mdef"), - hero_mdef = core.getRealStatusOrDefault(hero, "mdef"), - hero_speed = core.getRealStatusOrDefault(hero, "speed"), - hero_hpmax = core.getRealStatusOrDefault(hero, "hpmax"), - hero_spelldef = core.getRealStatusOrDefault(hero, "spelldef"); - const heroInfo = { - hp: hero_hp, - hpmax: hero_hpmax, - atk: hero_atk, - def: hero_def, - spell: hero_spell, - spelldef: hero_spelldef, - matk: hero_matk, - mdef: hero_mdef, - speed: hero_speed, - now: 0, - }; - enemyInfo.id = enemyId; - enemyInfo.cls = core.getClsFromId(enemyId); - enemyInfo.name = core.material.enemys[enemyId].name; + if (!attack) enemyInfo.now += (enemyInfo.speed / oneTurn) * 215; + let enemynow = Math.min(100 + enemyInfo.now, 315); + ctx.fillStyle = "#FFFFFF"; + ctx.beginPath(); + ctx.moveTo(enemynow, 100); + ctx.lineTo(enemynow + 3, 90); + ctx.lineTo(enemynow - 3, 90); + ctx.closePath(); + ctx.fill(); - let oneTurn = heroInfo.speed + enemyInfo.speed; - if (equipInfo.length > 0) { - for (let i; i < equipInfo.length - 1; i++) { - oneTurn += equipInfo[i].speed; - } - } - oneTurn *= 100; - let time = 0, - farme = 0; - core.registerAnimationFrame("attackAnimate", true, (temptime) => { - if (temptime - time > 1000 / 60) { - time = temptime; - this.drawAttackAnimate( - heroInfo, - oneTurn, - enemyInfo, - equipInfo, - farme - ); - farme++; - } - }); - }; -}, + if (enemyInfo.cls === "enemys") { + core.drawImage( + ctx, + core.getBlockInfo(enemyInfo.id).image, + 32, + core.getBlockInfo(enemyInfo.id).posY * 32, + 32, + 32, + enemynow - 16, + 64, + 32, + 32 + ); + } else { + core.drawImage( + ctx, + core.getBlockInfo(enemyInfo.id).image, + 32, + core.getBlockInfo(enemyInfo.id).posY * 48, + 32, + 19, + enemynow - 16, + 70, + 32, + 19 + ); + } + core.drawLine(ctx, 100, 105, 315, 105, "#FFFFFF", 5); + equipInfo.forEach(function (v) { + if (!attack) v.now += (v.speed / oneTurn) * 215; + let vnow = Math.min(100 + v.now, 315); + ctx.beginPath(); + ctx.moveTo(vnow, 100); + ctx.lineTo(vnow + 3, 90); + ctx.lineTo(vnow - 3, 90); + ctx.closePath(); + + ctx.fill(); + + core.drawIcon(ctx, v.id, vnow - 16, 64, 32, 32); + }); + if (!attack) heroInfo.now += (heroInfo.speed / oneTurn) * 215; + let heronow = Math.min(100 + heroInfo.now, 315); + ctx.beginPath(); + ctx.moveTo(heronow, 100); + ctx.lineTo(heronow + 3, 90); + ctx.lineTo(heronow - 3, 90); + ctx.closePath(); + + ctx.fill(); + core.drawImage(ctx, "hero.webp", 0, 0, 32, 19, heronow - 16, 70, 32, 19); + + if (heroInfo.now >= 215 && !heroInfo.isAttack) { + heroInfo.isAttack = true; + animateOnAttack("sword", true, () => { + heroInfo.now -= 215; + heroInfo.isAttack = false; + }); + } + if (enemyInfo.now >= 215 && !enemyInfo.isAttack) { + enemyInfo.isAttack = true; + animateOnAttack("sword", false, () => { + enemyInfo.now -= 215; + enemyInfo.isAttack = false; + }); + } + equipInfo.forEach((v) => { + if (v.now >= 215 && !v.isAttack) { + v.isAttack = true; + + animateOnAttack("sword", true, () => { + v.now -= 215; + v.isAttack = false; + }); + } + }); + }; + + function animateOnAttack(name, onenemy, callback) { + if (onenemy) { + core.playanimate(name, 290, 160, null, 1, 1, callback); + } else { + core.playanimate(name, 130, 160, null, 1, 1, callback); + } + } + + this.attackAnimate = function ( + enemyId, + enemyInfo, + damageInfo, + equipInfo = [] + ) { + //参数分别为怪物真实属性,战斗信息,特殊装备(如火焰风衣)属性特殊装备属性为以元组{equipId,oneDamage,speed,now:0}构成的数组(列出每个需要计算的特殊装备,没有则为空数组或不填) + core.lockControl(); + core.clearMap(ctx); + core.status.event.id = "attackAnimate"; + let hero_hp = core.getRealStatusOrDefault(hero, "hp"), + hero_atk = core.getRealStatusOrDefault(hero, "atk"), + hero_def = core.getRealStatusOrDefault(hero, "def"), + hero_spell = core.getRealStatusOrDefault(hero, "spell"), + hero_matk = core.getRealStatusOrDefault(hero, "matk"), + hero_mdef = core.getRealStatusOrDefault(hero, "mdef"), + hero_speed = core.getRealStatusOrDefault(hero, "speed"), + hero_hpmax = core.getRealStatusOrDefault(hero, "hpmax"), + hero_mhp = core.getRealStatusOrDefault(hero, "mhp"); + const heroInfo = { + hp: hero_hp, + hpmax: hero_hpmax, + atk: hero_atk, + def: hero_def, + spell: hero_spell, + mhp: hero_mhp, + matk: hero_matk, + mdef: hero_mdef, + speed: hero_speed, + now: 0, + isAttack: false, + }; + enemyInfo.id = enemyId; + enemyInfo.cls = core.getClsFromId(enemyId); + enemyInfo.name = core.material.enemys[enemyId].name; + enemyInfo.now = 0; + enemyInfo.isAttack = false; + let oneTurn = heroInfo.speed + enemyInfo.speed; + if (equipInfo.length > 0) { + for (let i; i < equipInfo.length - 1; i++) { + equipInfo[i].now = 0; + equipInfo[i].isAttack = false; + oneTurn += equipInfo[i].speed; + } + } + oneTurn *= 25; + let time = 0, + farme = 0; + + core.registerAnimationFrame("attackAnimate", true, (temptime) => { + if (temptime - time > 1000 / 60) { + time = temptime; + this.drawAttackAnimate( + heroInfo, + oneTurn, + enemyInfo, + equipInfo, + farme, + damageInfo + ); + farme++; + } + }); + }; + }, "剧情内容": function () { - // 在此增加新插件 - // 每项为一个数组,第一项是名字,第二项是对话内容 - // 回放只会在同一个this下回放,进入剧情前请以事件块声明进入哪个剧情数组 - // 可以自由添加,但不能与已有插件及函数名相同,可以使用中文。 - this.chapter0 = [ - ["", "这些天,街道不曾下雨。"], - ["", "所以,那浸湿地面的,定是那些女孩们流落的鲜血无疑。"], - ["", "我蹲在充斥着铁锈味般恶臭的小巷中,悠闲地如是想着。"], - ["", "扑哧。"], - ["", "耳旁再次响起象征着某个女孩子死去的声音。"], - ["", "再一次——"], - ["", "再一次。"], - ["", "女子们被肢解成单纯的肉块。"], - ["", "我任由流下的血浸满全身,屏住自己的呼吸。"], - ["", "祈求自己能拥有从猎人手中逃脱的幸运。"], - ["", "扑哧。"], - ["", "直到刚才,我们还坐在去往娼馆的马车的路上。"], - ["", "而在这之中的某些人,已经不在这个世上了。"], - ["", "不,应该把“某些”换成“几乎所有”才更为恰当吧。"], - ["", "恐怕,不久之后我也会变成小巷中血腥的装饰品。"], - ["", "我是为了得到这种死法,才辛苦苟活至今的吗?"], - ["", "来个人告诉我啊——"], - ["", "谁都好。"], - ["", "来人啊!!"], - ["少女", "「呃······!?」"], - ["", "漆黑的物体充斥了我的整个视野"], - ["", "我很快意识到,那是只很大的脚。"], - ["", "必须要出声求救。"], - ["", "可是,耳中却只能听到自己的牙关不停交战的声音。"], - ["", "我是如此的无助。"], - ["", "逃跑也好,道歉也罢。"], - ["", "就连抬头看一眼将要杀掉我的人的面孔都做不到。"], - ["少女", "「······被杀」"], - ["", "会被杀。"], - ["", "会被杀!!"], - ["", "来自内心深处的冰冷预感,渐渐地在体内蔓延开来。"], - ["少女", "「不,不要······」"], - ["", "浮游都市,《诺瓦斯·艾蒂尔》。"], - ["", "《特别受灾地区》——"], - ["", "通称,《牢狱》"], - ["", "是被险峻的峭壁环绕,与世隔绝的,都市的最底部。"], - ["年轻人", "「放开我!」"], - ["年轻人", "「我只是在帮那个女人而已!」"], - ["年轻人", "「你们没听到吗!?」"], - ["年轻人", "「她是被受骗才会被卖到娼馆来的」"], - ["年轻人", "「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」"], - ["年轻人", "「给我说些什么啊」"], - ["凯伊姆", "「这些话等到了娼馆再说吧」"], - ["凯伊姆", "「我来抓你,只是受雇于人而已」"], - ["", "我走进娼馆《莉莉乌姆》的接待室。"], - ["", "正在桌旁整理账簿的奥兹停下手头的工作,抬起头向我看来。"], - ["奥兹", "「这不是凯伊姆先生吗,辛苦了」"], - ["奥兹", "「委托已经完成了吗?」"], - ["凯伊姆", "「啊啊,是这家伙没错吧」"], - ["", "奥兹用只要接触到就能杀人般的眼神在男人脸上搜过。"], - ["奥兹", "「没错,就是这个人」"], - ["凯伊姆", "「是么」"], - ["年轻人", "「你,你们要对我做什么」"], - ["奥兹", "「······」"], - ["", "奥兹用一个眼神,就让男人闭上了嘴。"], - ["", "然后,向我这边转过身来。"], - ["奥兹", "「抱歉啊,总是麻烦你去做这些无聊的事」"], - ["奥兹", "「都怪我们这边的年轻人太没用」"], - ["凯伊姆", "「客套话就免了」"], - ["奥兹", "「这还真是失礼了」"], - ["奥兹", "「喂,来个人」"], - ["光头男人", "「是」"], - ["奥兹", "「凯伊姆先生做完工作回来了」"], - ["光头男人", "「是,是,那个······」"], - ["奥兹", "「我是要你拿些酒来,这个蠢材!」"], - ["", "喀!"], - ["", "奥兹扔出的烟灰缸砸中了手下的额头。"], - ["", "鲜血四溅。"], - ["凯伊姆", "「不用这么麻烦」"], - ["凯伊姆", "「我接下来要去《菲诺列塔》」"], - ["奥兹", "「喔唷」"], - ["奥兹", "「既然如此,我就不留您在这里喝难饮的劣质酒了」"], - ["", "奥兹斜眼看着正捂住额头呻吟的手下,轻描淡写地说道。"], - ["凯伊姆", "「用这些钱去买药」"], - ["", "我将几枚铜钱仍在那个手下的身前。"], - ["奥兹", "「凯伊姆先生,不用对他们这么好」"], - ["凯伊姆", "「无妨」"], - ["凯伊姆", "「话说回来,那个要落跑的女人呢?」"], - ["奥兹", "「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」"], - ["奥兹", "「正好,趁此机会凯伊姆先生也来享受一番如何?」"], - ["年轻人", "「你,你们这些家伙,要对她做什么!?」"], - ["", "咣!"], - ["", "奥兹给了他一拳。"], - ["", "一击即倒。"], - ["", "喀,咚,咯!"], - ["", "奥兹毫不留情地向男人的脸上踩去。"], - ["年轻人", "「咕······呃咳······」"], - ["", "折断的牙齿伴着血泡被吐出。"], - ["", "这份白色在鲜红色的液体中格外显眼。"], - ["年轻人", "「你们以为做出这种事······卫兵会坐视不理吗······」"], - ["奥兹", "「啊啊,不会坐视不理的」"], - ["奥兹", "「应该会拿出你的钱包,和我们商量如何瓜分吧」"], - ["年轻人", "「那,那种事······」"], - ["", "这在牢狱是理所当然的事。"], - ["奥兹", "「怎么,头一回来牢狱么?」"], - ["", "男人点了点头。"], - ["奥兹", "「为了被骗的女人而来到牢狱,真是个规矩人啊」"], - ["奥兹", "「······前提是,被骗的人不是你」"], - ["年轻人", "「你说······我被骗了?」"], - ["年轻人", "「那,那是怎么回事!?」"], - ["奥兹", "「不用急,今天晚上会好好告诉你的」"], - ["", "奥兹抓起男人的脸。"], - ["", "为引诱客人的怜悯之心而装纯,是娼妇的惯用手段。"], - ["", "双亲被骗而借钱,结果作为抵押而将自己卖到这里,这是典型的说法。"], - [ - "", - "如果只是头脑发热而成为常客也就罢了,这次的男人热血过头,居然想出了要带女人私奔的计划。", - ], - [ - "", - "虽然女人半开玩笑地予以拒绝,但不知天高地厚的这家伙还是拉着她逃跑了。", - ], - ["", "不过,想要逃脱追击本来就是不可能的任务。"], - ["", "但即便如此,这种事情还是会一再的出现。"], - ["", "说谎的女人和被骗的男人。"], - ["", "在娼馆街,这是令人看到生厌的日常的风景。"], - ["凯伊姆", "「我要走了」"], - ["奥兹", "「好的,下次再麻烦您」"], - ["奥兹", "「之后吉克先生会将谢礼交给您的」"], - ["凯伊姆", "「啊啊」"], - ["", "我背向奥兹走出娼馆。"], - ["凯伊姆", "「······?」"], - ["", "从远方传来微弱的歌声。"], - ["", "是关卡广场的方向。"], - ["", "对了。"], - ["", "今天有觐见圣女的仪式。"], - ["", "当代的圣女伊莲——"], - ["", "俗称《盲眼之圣女》,据说即使在历代的圣女中,人气也是数一数二的。"], - ["", "广场上的人估计相当多吧。"], - ["", "虽然我也想去看看她长什么样,不过要在人潮中挤来挤去就免了。"], - ["", "还是老老实实去菲诺列塔喝烧酒吧。"], - ["", "正当我这样想着的时候,一个身影自小巷的那头走来。"], - ["凯伊姆", "「艾莉斯」"], - ]; - this.chapter01 = [ - ["艾莉斯", "「啊,凯伊姆」"], - ["艾莉斯", "「正好,我还想要去找你呢」"], - ["艾莉斯", "「没想到凯伊姆会主动出现······这是命运吗?」"], - ["凯伊姆", "「显然不是吧」"], - ["艾莉斯", "「啊,是么」"], - ["", "艾莉斯挑了挑整齐的双眉,微微地哼了一声。"], - [ - "", - "虽然是个相当引人注目的美人,但她这个将亲切儿子丢入无底深渊的性格,为自己扣了不少的分", - ], - ["", "给人印象最深的,就是那潭水般的双瞳。"], - ["", "在漆黑的瞳孔中,完全看不出感情的波动。"], - ["艾莉斯", "「喜欢我的眼睛吗?」"], - ["艾莉斯", "「如果想要的话就给你吧?」"], - ["凯伊姆", "「用不着」"], - ["艾莉斯", "「阿拉,可惜」"], - ["凯伊姆", "「那么,找我有什么事」"], - ["艾莉斯", "「梅尔特的钱好像被偷了」"], - ["凯伊姆", "「钱被偷了?都几岁了还这么没用」"], - ["艾莉斯", "「不要对我说啊」"], - ["凯伊姆", "「那家伙,该不会说要让我去抓那个小偷吧?」"], - ["艾莉斯", "「就是这样」"], - ["凯伊姆", "「笨蛋吗」"], - ["凯伊姆", "「如果是小钱的话,就当做是买个教训吧」"], - ["艾莉斯", "「说起来,被盗的是这个月的上纳金」"], - ["凯伊姆", "「你说什么?」"], - ["艾莉斯", "「用这些钱买教训,也太过奢侈了呢」"], - ["凯伊姆", "「知道了,我去找」"], - ["凯伊姆", "「小偷的特征呢」"], - ["艾莉斯", "「男孩子」"], - ["艾莉斯", "「······而且,背后有翅膀」"], - ["艾莉斯", "「虽然姑且是藏在身后,但是仔细观察的话是很明显的」"], - ["凯伊姆", "「羽化病吗」"], - [ - "艾莉斯", - "「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」", - ], - ["凯伊姆", "「注意到他逃窜的方向了吗?」"], - ["艾莉斯", "「广场那边」"], - ["艾莉斯", "「虽然刚才《不蚀金锁》的人去追了,不过多半是······」"], - ["凯伊姆", "「偏偏还是广场吗」"], - ["艾莉斯", "「今天是觐见圣女大人的日子」"], - ["凯伊姆", "「我知道」"], - ["凯伊姆", "「尽量找找看就好」"], - ]; - this.chapter02 = [ - ["不蚀金锁成员", "「凯伊姆先生,凯伊姆先生」"], - ["不蚀金锁成员", "「您已经和艾莉斯大夫见过面了吗?」"], - ["凯伊姆", "「啊啊,所以才会追过来的」"], - ["凯伊姆", "「看到小偷了吗?」"], - [ - "不蚀金锁成员", - "「没有,他向广场那边逃了过去,今天这么拥挤,我们也只能放弃了」", - ], - [ - "不蚀金锁成员", - "「不过,我也只是刚好在店里所以才追了过去,并不是受人所托」", - ], - ["不蚀金锁成员", "「我已经准备撤退了」"], - ["不蚀金锁成员", "「凯伊姆先生还要继续追吗?」"], - ["凯伊姆", "「啊啊」"], - ["", "做完情报交换之后,我跟男人道别。"], - ["凯伊姆", "「和我想的一样啊······」"], - ["", "在牢狱中最大的广场上,聚集着看不到尽头的人群。"], - ["", "就算是来参见圣女祈祷,这人数也太多了点吧。"], - ["", "自然,我也找不到逃跑的孩子。"], - ["", "是混杂到人群中了吧。"], - ["", "如果已经从广场上脱身了的话,就更难发现了。"], - ["", "只好赌他还在这里了。"], - ["", "我先移动到了一个视野良好的地方。"], - ["", "从这里,一眼就可以看到人群的变化。"], - ["", "广场还是沸腾起来。"], - ["", "抬头望去,原来是在天台之上出现了一个人影"], - ["", "但是,与周围的期待不同,现身的是一名中年的神官。"], - ["", "骂声四溢。"], - ["", "神官则是笑着摆正衣领"], - ["神官", "「从现在开始,举行谒见的仪式」"], - [ - "神官", - "「在参见那位大人之前,我希望牢狱的诸位再次思考这个《诺瓦斯·艾蒂尔》存在的意义······」", - ], - [ - "神官", - "「初代圣女伊莲大人,便是也难怪这崇高的祈祷之力,令《诺瓦斯·艾蒂尔》浮在空中,拯救了我们的祖先」", - ], - [ - "神官", - "「这之后的几百年来,传承了初代大人力量的历代圣女伊莲大人,让这里留在了空中」", - ], - [ - "神官", - "「这座都市是被圣女大人守护的人类最后的圣域,而我们则是被选召的虔诚的信徒」", - ], - ["神官", "「怀着对圣女的感激祈祷吧,感谢圣女伊莲吧!并献上祈祷!」"], - ["圣女", "「不忘感谢与祈祷,神才会拯救我们」"], - ["圣女", "「与我一起,向神虔诚地祈祷吧」"], - ["", "广场上欢声雷动。"], - ["", "圣女没有回应喧嚣的人声,而是静静地合上双眼面向广场。"], - ["", "虽然感觉有些冷淡,但总比像个傻瓜似的笑着向这群人挥手要强。"], - ["", "她掌握着这条街道,还有在这条街上生活的人的命运。"], - ["", "比起揽得人气,她更想要为了街道的继续存在而献出全力。"], - ["", "也是为了不让《大崩落》的惨剧再度发生。"], - ["", "十几年前的那场悲剧。"], - [ - "", - "虽然在我脑海中的记忆已经相当模糊,但哪怕只是稍有触及,不快的感觉都会在胸口蔓延开。", - ], - ["凯伊姆", "「······」"], - ["", "这时我才想起,现在不是我在这里看圣女的时候。"], - ["女声", "「——っ!?」"], - ["围观的女人", "「羽,羽化病人!?」"], - ["围观的中年人", "「喂,谁去叫下羽狩」"], - ["惊慌的观众", "「你这家伙不要靠近我,要是传染了可怎么办」"], - ["粗鲁的观众", "「你这小鬼赶快滚开」"], - ["凯伊姆", "「接下来」"], - ["圣女", "「发生什么事了?看上去似乎很嘈杂」"], - ["随从", "「似乎是某个人逃跑了······具体的我也不是很清楚」"], - ["神官", "「圣女大人,继续待在天台上可能会出事,请您先回到室内吧」"], - ["圣女", "「不用在意我,比起那个,我更关心究竟发生了什么事」"], - ["神官", "「对不起,我真的不知道」"], - ["圣女", "「······是吗」"], - ["男", "「恕我僭越,请准许我说明情况」"], - ["男", "「在来觐见的人群中出现了《羽化病》的患者」"], - ["男", "「围观的人群因而产生了骚动」"], - [ - "男", - "「现在,《防疫局》已经派遣了部队。我想不久之后,他们就会安静下来了」", - ], - ["圣女", "「羽化病······」"], - ["男", "「怎么了?」"], - ["圣女", "「没什么」"], - ["圣女", "「辛苦了,你的名字是?」"], - ["男", "「属下是在防疫局任职的,鲁基乌斯· 迪斯·米利尤」"], - ["神官", "「噢噢,阁下就是鲁基乌斯卿吗,我听说过你的传闻」"], - ["神官", "「阁下是在工作上相当出色的人呢」"], - ["鲁基乌斯", "「不敢当」"], - [ - "鲁基乌斯", - "「话说回来,这次是属下警备工作的失职。让圣女大人见到这不成体统的一面,请您见谅」", - ], - [ - "圣女", - "「即使是目不见物的我,也能感受到聚集于此的民众数量之多。警备工作难以展开也在情理之中」", - ], - ["鲁基乌斯", "「属下不胜惶恐」"], - ["鲁基乌斯", "「接下来属下还要回到工作岗位上,在这里就先告退了」"], - ["圣女", "「鲁基乌斯先生」"], - ["鲁基乌斯", "「属下在」"], - ["圣女", "「你是怎样看待羽狩的工作的呢?」"], - ["神官", "「圣,圣女大人」"], - [ - "鲁基乌斯", - "「防疫局的工作是国王陛下赐予的重要职务。属下非常荣幸能够为这个都市的繁荣尽一份微薄之力」", - ], - ["神官", "「不,不亏是鲁基乌斯卿,相当优秀的想法」"], - ["圣女", "「是吗。辛苦你了」"], - ["随从", "「圣女大人······」"], - ["鲁基乌斯", "「······」"], - ["鲁基乌斯", "「那么,属下就回岗位去了」"], - ]; - this.chapter03 = [ - ["", "从羽化病的少年纷乱的足音中,可以听得出相当的疲劳。"], - ["", "显然,他并没有想到我会捷足先登吧。"], - ["", "少年惶恐地回头看了一眼后,微微露出安心的表情,双手拄在膝盖上。。"], - ["凯伊姆", "「辛苦你了」"], - ["羽化病患少年", "「稀!?」"], - ["凯伊姆", "「逃到贫民区是个不错的想法」"], - ["羽化病患少年", "「你,你是,羽狩吗?」"], - ["凯伊姆", "「不是」"], - ["羽化病患少年", "「什,什么啊······混蛋,不要吓我啊」"], - ["凯伊姆", "「我对令你受惊这件事致以歉意」"], - ["凯伊姆", "「作为回报,麻烦你把从店里偷的钱交出来吧」"], - ["羽化病患少年", "「钱?你在说什么」"], - ["凯伊姆", "「你要找的腰上的东西,掉在你身后了」"], - ["羽化病患少年", "「哎?」"], - ["羽化病患少年", "「呃呀」"], - ["羽化病患少年", "「你······你这混蛋」"], - ["凯伊姆", "「······」"], - ["凯伊姆", "「把偷的钱交出来」"], - ["羽化病患少年", "「我不知道你在······咕」"], - ["羽化病患少年", "「你,你说是我偷的······有什么证据吗」"], - ["凯伊姆", "「你还挺倔的啊」"], - ["凯伊姆", "「不过,给我听好了」"], - ["凯伊姆", "「你偷的那些钱,是要上缴给《不蚀金锁》的上纳金」"], - ["凯伊姆", "「而且,钱的主人是从前和吉克颇有渊源的女人」"], - ["羽化病患少年", "「吉克?」"], - ["凯伊姆", "「他是《不蚀金锁》的主人,这么说你就明白了吧」"], - ["羽化病患少年", "「哎?哎?怎么会······」"], - ["凯伊姆", "「再问你一遍,钱在哪里?」"], - ["羽化病患少年", "「是,是,是,在我的怀里」"], - ["凯伊姆", "「你没有擅自拿掉一部分吧」"], - ["羽化病患少年", "「是,是的」"], - ["羽化病患少年", "「那,那个,您是《不蚀金锁》的人吗?」"], - ["凯伊姆", "「算是吧」"], - ["羽化病患少年", "「我什么都可以做,请您一定要帮帮我」"], - ["凯伊姆", "「抱歉,我并没有兴趣去帮助他人」"], - ["羽化病患少年", "「我什么······什么,都会做的······」"], - ["羽化病患少年", "「我一直都是生活在下层的」"], - [ - "羽化病患少年", - "「可是,不知何时染上了羽化病······背后长出了翅膀······」", - ], - ["羽化病患少年", "「被寄宿工作的店赶了出来,只得流落到牢狱这里」"], - ["羽化病患少年", "「因为独自实在是饿的不行了,所以才会偷这些钱的」"], - [ - "羽化病患少年", - "「我明明没有做任何坏事······为什么······会遇到这种事······」", - ], - ["凯伊姆", "「谁知道」"], - ["羽化病患少年", "「呜······呜呜······接下来,要对我做什么?」"], - ["凯伊姆", "「我要把你带到组织那里」"], - ["羽化病患少年", "「怎,怎么这样」"], - ["凯伊姆", "「不过,那样做的前提是你不是羽化病人」"], - ["凯伊姆", "「组织也没有笨到把羽化病人招待到家里的程度」"], - ["羽化病患少年", "「那么,是要放我逃走吗?」"], - ["凯伊姆", "「我要让你学到教训」"], - ["凯伊姆", "「如果换做是组织的制裁,至少要有断条胳膊的觉悟」"], - ["凯伊姆", "「你的运气不错」"], - ["羽化病患少年", "「唔······啊,是的······」"], - ["凯伊姆", "「滚」"], - ["羽化病患少年", "「非常感谢」"], - ["羽化病患少年", "「唔啊!?」"], - ["男", "「到这里就结束了,羽化病人」"], - ["男", "「确认他的翅膀」"], - ["", "趁还没有被卷入麻烦的事情之前,赶快离开这里吧"], - ["羽狩的队长", "「那边的那个人」"], - ["凯伊姆", "「······有什么事?」"], - ["羽狩的队长", "「可以稍微让我问几句话吗」"], - ["凯伊姆", "「······」"], - ["凯伊姆", "「啊啊,无妨」"], - ["羽狩的队长", "「感谢您的合作」"], - ["", "队长殷勤地致以谢礼。"], - ["", "而在他的眼前,少年的衣服已经被他的补下们扯破。"], - ["", "在瘦骨嶙峋的裸露后背上,长有纯白的羽翼。"], - ["红发的羽狩", "「副队长,确认翅膀的持有了」"], - ["羽狩的副队长", "「保护他」"], - ["羽化病患少年", "「不要······请原谅,我······」"], - [ - "羽狩的副队长", - "「我们只是要带你去治愈院治疗羽化病,不是什么应该感到害怕的事情」", - ], - ["羽化病患少年", "「可是,可是」"], - ["羽狩的副队长", "「没关系的」"], - ["羽化病患少年", "「······哥,哥哥」"], - ["羽狩的副队长", "「你是羽化病人的亲属吗?」"], - ["凯伊姆", "「只是路人而已」"], - ["凯伊姆", "「顺带一提,我也没有打算找你们的麻烦」"], - [ - "羽狩的副队长", - "「前几天,有个和你说了同样的话的人,在我们背向他的瞬间对我们发动了袭击」", - ], - ["羽狩的副队长", "「我的一个部下就此永久失去了半截胳膊」"], - ["凯伊姆", "「我表示同情」"], - ["凯伊姆", "「我马上就会消失的,这样就没问题了吧?」"], - ["羽狩的副队长", "「嘛,不要这么慌张」"], - ["", "副队长看着羽化的少年。"], - ["羽狩的副队长", "「你与这个人是什么关系?没有被他殴打吗?」"], - ["羽化病患少年", "「没,没有」"], - [ - "羽狩的副队长", - "「如何对我们保持合作,你就可以在治愈院得到优先的治疗」", - ], - ["羽化病患少年", "「······」"], - ["羽化病患少年", "「那个人,是《不蚀金锁》的组织成员······」"], - ["羽化病患少年", "「突然说让我拿出钱来,我刚一拒绝他就打我」"], - ["羽狩的副队长", "「原来如此······」"], - [ - "羽狩的副队长", - "「那位少年说你是《不蚀金锁》的一员,不知此事是否属实?」", - ], - ["凯伊姆", "「当然不是」"], - ["凯伊姆", "「我只是从那里接受工作而已,并不是他们的成员」"], - ["羽狩的副队长", "「你的意思是说,少年在说谎吗?」"], - ["凯伊姆", "「啊啊」"], - [ - "凯伊姆", - "「如果你们和组织有关系的话,只要问问我是不是那里的成员就能明白事实了吧」", - ], - ["羽狩的副队长", "「就算我去询问,也无法从他们那里得到事实」"], - [ - "羽狩的副队长", - "「《不蚀金锁》的那些人一向都不对我们合作,我对此深感困扰」", - ], - ["凯伊姆", "「真是辛苦啊」"], - ["羽狩的副队长", "「说的是啊」"], - ["羽狩的副队长", "「其实,砍下我部下胳膊的似乎也是组织的成员呢」"], - [ - "羽狩的副队长", - "「无需如此警戒,我只是想在看守所向你咨询一些事情而已」", - ], - [ - "羽狩的副队长", - "「如果能够知晓牢狱与组织的事情,我们也可以尽可能地对更多的羽化病人进行保护」", - ], - ["羽狩的副队长", "「那和整条街道的和平也是紧密相关的吧?」"], - ["凯伊姆", "「我知道,你们有逮捕干扰狩猎羽化病人的权力」"], - [ - "凯伊姆", - "「但是,我没有对你们做出任何干扰,为什么要对我如此纠缠不休呢」", - ], - ["羽狩的副队长", "「那些话,我们会在看守所对你详细说明的」"], - ["凯伊姆", "「······」"], - ["", "在这里起争执的话,就会被羽狩加害。"], - ["", "就算能从这里脱身,今后只要碰面就会产生纠纷也是摆明的事情。"], - ["", "就算逃跑,也没有好的结果。"], - ["", "正当我想要打圆场的时候,刚才的气氛一瞬间产生了转变。"], - ["", "发生了什么事······"], - ["???", "「我认为,那位先生是正确的」"], - ["", "羽狩们一起回头。"], - ["", "而在他们视线的焦点处,"], - ["", "伫立着一位女性。"], - ["", "在端正的容颜下,代表着强烈意志的双眉十分显眼。"], - ["", "身体的柔软与紧紧包裹在其身上的羽狩制服,两者显得十分的不搭配。"], - ["", "我还是第一次看到女性的羽狩。"], - ["羽狩的副队长", "「队长,这是获得《不蚀金锁》情报的好机会」"], - [ - "羽狩的队长", - "「兰格副队长,就算是为了获得情报,也不能做出恫吓的发言啊」", - ], - ["兰格副队长", "「我并没有打算去恫吓他······」"], - ["羽狩的队长", "「告诉我那个被砍掉胳膊的队员的名字」"], - ["羽狩的队长", "「我会去探望他的」"], - ["兰格副队长", "「那个是······」"], - ["羽狩的队长", "「我知道,你一直在为有所收获而努力工作」"], - ["羽狩的队长", "「但是,正因为我们的工作是为民众提供帮助」"], - ["羽狩的队长", "「所以就更不能损害人与人之间的信赖」"], - ["兰格副队长", "「我会铭记在心」"], - ["羽狩的队长", "「这位先生,我的部下失礼了」"], - ["凯伊姆", "「只要不对我再来一次就好」"], - ["羽狩的队长", "「请稍等」"], - ["凯伊姆", "「有什么事?」"], - ["羽狩的队长", "「我想确认一件事」"], - ["羽狩的队长", "「你真的不是《不蚀金锁》的成员吗?」"], - ["凯伊姆", "「真的」"], - ["凯伊姆", "「如果我说是的话,你有什么打算?」"], - ["羽狩的队长", "「我听过传闻,说他们是用依靠暴力而得的钱在生活」"], - ["凯伊姆", "「······这样啊」"], - ["凯伊姆", "「如果能有收获就好了啊」"], - ]; - this.chapter04 = [ - ["梅尔特", "「欢迎光临」"], - ["梅尔特", "「辛苦了」"], - ["梅尔特", "「抱歉,又拜托给你了个这么麻烦的工作」"], - ["凯伊姆", "「没什么,比想象中完成的更容易」"], - ["梅尔特", "「那就好」"], - ["梅尔特", "「这是我的一点谢意」"], - ["凯伊姆", "「味道有些变化啊」"], - ["梅尔特", "「啊,被发现了?」"], - ["梅尔特", "「最近,没能到手什么好的原料呢」"], - ["凯伊姆", "「去拜托吉克如何?」"], - [ - "梅尔特", - "「话是这么说,但是总不能用店里采购的这种小事去麻烦他吧······」", - ], - ["凯伊姆", "「那希望你也不要来麻烦我」"], - ["梅尔特", "「那 是 两 码 事」"], - ["梅尔特", "「再说,凯伊姆是靠着工作来生活的吧」"], - [ - "梅尔特", - "「而且,自己的钱被偷了这么害羞的事,向凯伊姆意外的其他人都说不出口」", - ], - ["凯伊姆", "「反正,也已经传到吉克的耳朵里了」"], - ["梅尔特", "「这是面子问题啊,面子问题」"], - ["凯伊姆", "「嘛,算了」"], - ["凯伊姆", "「这样就好了吧?」"], - ["梅尔特", "「这是钱包呢」"], - ["梅尔特", "「嗯,东西没少」"], - ["梅尔特", "「太好啦—这个月的上纳金,我可全部都放在里面了呢」"], - ["梅尔特", "「如果没有找到的话,说不定就又会被送到娼馆里了呢」"], - ["凯伊姆", "「在那边不是来钱更快吗?」"], - ["梅尔特", "「阿拉,你是在说我还能有魅力吗?」"], - ["凯伊姆", "「这是客套话而已」"], - ["梅尔特", "「欺负人」"], - ["梅尔特", "「总而言之,今天帮大忙了」"], - ["梅尔特", "「谢礼嘛······」"], - ["凯伊姆", "「就记在账单上吧」"], - ["梅尔特", "「了解—盛谢惠顾了哦?」"], - ["", "喀啷喀啷"], - ["", "门铃响起"], - ["", "喧哗瞬间安静下来。"], - ["", "进来的人是吉克。"], - ["", "是掌控着牢狱的组织之一,《不蚀金锁》的头目。"], - ["", "不仅组织的成员,就连店内一般的客人也对他以注目礼表示敬意。"], - ["吉克", "「各位继续吧」"], - ["", "仿佛停滞的时钟重新转动了一般,店内恢复了热闹的气氛。"], - ["吉克", "「抱歉,今天拜托你去做了无聊的工作」。"], - ["凯伊姆", "「不用介意」"], - ["", "吉克轻轻点了点头,在我右边坐了下来"], - ["凯伊姆", "「逃跑的男人怎么样了?」"], - ["吉克", "「嗯?已经不在这个世上了」。"], - ["吉克", "「有什么想要知道的事吗?」"], - ["凯伊姆", "「不,没什么」"], - ["吉克", "「那个无聊的家伙,完全没有趣味呢」"], - ["吉克", "「真希望他也替我负责清扫的部下也考虑考虑」"], - ["凯伊姆", "「真是灾难啊」"], - ["吉克", "「比起那个,我听说了哦。你去追羽化病人了啊」"], - ["凯伊姆", "「消息真灵通」"], - ["吉克", "「梅尔特也注意点」"], - ["吉克", "「你丢钱已经不是一回两回了」"], - ["梅尔特", "「好的—我会注意的。」"], - ["梅尔特", "「吉克还是平常的点单吧」"], - ["梅尔特", "「凯伊姆要再来一杯吗?」"], - ["", "我们用眼神点头示意后,梅尔特开始准备起酒来。"], - ["", "悠然地吐出眼圈后,吉克取出一个纸包放在柜台上。"], - ["吉克", "「这是抓捕逃跑男人的报酬」"], - ["凯伊姆", "「下次有什么事再告诉我」"], - ["梅尔特", "「来,久等了」"], - ["凯伊姆", "「话说回来梅尔特,为什么会被那种孩子偷到钱?」"], - ["吉克", "「让我猜猜看」"], - ["吉克", "「是那个吧,看某个特立独行的男人入迷了,所以就有了空隙?」"], - ["梅尔特", "「可惜—」"], - ["梅尔特", "「事实恰恰相反,是那家伙一直在纠缠我」"], - ["凯伊姆", "「完全把你当成新进的女佣了么」"], - ["梅尔特", "「我从前可是很有名的,不会被当成这种下人吧」"], - ["梅尔特", "「······而且,我没法对对我这么钟情的人发火啊」"], - ["凯伊姆&吉克", "「你傻啊」"], - ["梅尔特", "「异口同声呢,不亏是兄弟」"], - ["凯伊姆", "「别用这种称呼,怪恶心的」"], - ["吉克", "「说得没错」"], - ["吉克", "「······说起来······」"], - ["梅尔特", "「怎么了?」"], - ["吉克", "「有件事我一直很在意,我和凯伊姆,哪个是哥哥啊?」"], - ["凯伊姆", "「你也说这么无聊的话题」"], - ["吉克", "「不,这是很重要的事情」"], - ["吉克", "「梅尔特,事实是怎么样的?」"], - ["梅尔特", "「啊~是怎么样的呢~」"], - ["梅尔特", "「我忘记了」"], - ["吉克", "「骗人」"], - ]; -} + // 在此增加新插件 + // 每项为一个数组,第一项是名字,第二项是对话内容 + // 回放只会在同一个this下回放,进入剧情前请以事件块声明进入哪个剧情数组 + // 可以自由添加,但不能与已有插件及函数名相同,可以使用中文。 + this.chapter0 = [ + ["", "这些天,街道不曾下雨。"], + ["", "所以,那浸湿地面的,定是那些女孩们流落的鲜血无疑。"], + ["", "我蹲在充斥着铁锈味般恶臭的小巷中,悠闲地如是想着。"], + ["", "扑哧。"], + ["", "耳旁再次响起象征着某个女孩子死去的声音。"], + ["", "再一次——"], + ["", "再一次。"], + ["", "女子们被肢解成单纯的肉块。"], + ["", "我任由流下的血浸满全身,屏住自己的呼吸。"], + ["", "祈求自己能拥有从猎人手中逃脱的幸运。"], + ["", "扑哧。"], + ["", "直到刚才,我们还坐在去往娼馆的马车的路上。"], + ["", "而在这之中的某些人,已经不在这个世上了。"], + ["", "不,应该把“某些”换成“几乎所有”才更为恰当吧。"], + ["", "恐怕,不久之后我也会变成小巷中血腥的装饰品。"], + ["", "我是为了得到这种死法,才辛苦苟活至今的吗?"], + ["", "来个人告诉我啊——"], + ["", "谁都好。"], + ["", "来人啊!!"], + ["少女", "「呃······!?」", "aiy010000010.opus"], //小动物01 + ["", "漆黑的物体充斥了我的整个视野"], + ["", "我很快意识到,那是只很大的脚。"], + ["", "必须要出声求救。"], + ["", "可是,耳中却只能听到自己的牙关不停交战的声音。"], + ["", "我是如此的无助。"], + ["", "逃跑也好,道歉也罢。"], + ["", "就连抬头看一眼将要杀掉我的人的面孔都做不到。"], + ["少女", "「······被杀」", "aiy010000020.opus"], //小动物02 + ["", "会被杀。"], + ["", "会被杀!!"], + ["", "来自内心深处的冰冷预感,渐渐地在体内蔓延开来。"], + ["少女", "「不,不要······」", "aiy010000030.opus"], //小动物03 + ["", "浮游都市,《诺瓦斯·艾蒂尔》。"], + ["", "《特别受灾地区》——"], + ["", "通称,《牢狱》"], + ["", "是被险峻的峭壁环绕,与世隔绝的,都市的最底部。"], + ["年轻人", "「放开我!」", "aiy710000010.opus"], //龙套1-01 + ["年轻人", "「我只是在帮那个女人而已!」", "aiy710000020.opus"], //龙套1-02 + ["年轻人", "「你们没听到吗!?」", "aiy710000030.opus"], //龙套1-03 + ["年轻人", "「她是被受骗才会被卖到娼馆来的」", "aiy710000040.opus"], //龙套1-04 + [ + "年轻人", + "「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」", + "aiy710000050.opus", + ], //龙套1-05 + ["年轻人", "「给我说些什么啊」", "aiy710000060.opus"], //龙套1-06 + ["凯伊姆", "「这些话等到了娼馆再说吧」", "aiy310000010.opus"], //男主01 + ["凯伊姆", "「我来抓你,只是受雇于人而已」", "aiy310000020.opus"], //男主02 + ["", "我走进娼馆《莉莉乌姆》的接待室。"], + ["", "正在桌旁整理账簿的奥兹停下手头的工作,抬起头向我看来。"], + ["奥兹", "「这不是凯伊姆先生吗,辛苦了」", "aiy350000010.opus"], //金锁高官01 + ["奥兹", "「委托已经完成了吗?」", "aiy350000020.opus"], //金锁高官02 + ["凯伊姆", "「啊啊,是这家伙没错吧」", "aiy310000030.opus"], //男主03 + ["", "奥兹用只要接触到就能杀人般的眼神在男人脸上搜过。"], + ["奥兹", "「没错,就是这个人」", "aiy350000030.opus"], //金锁高官03 + ["凯伊姆", "「是么」", "aiy310000040.opus"], //男主04 + ["年轻人", "「你,你们要对我做什么」", "aiy710000070.opus"], //龙套1-07 + ["奥兹", "「······」", "aiy350000040.opus"], //金锁高官04 + ["", "奥兹用一个眼神,就让男人闭上了嘴。"], + ["", "然后,向我这边转过身来。"], + ["奥兹", "「抱歉啊,总是麻烦你去做这些无聊的事」", "aiy350000050.opus"], //金锁高官05 + ["奥兹", "「都怪我们这边的年轻人太没用」", "aiy350000060.opus"], //金锁高官06 + ["凯伊姆", "「客套话就免了」", "aiy310000050.opus"], //男主05 + ["奥兹", "「这还真是失礼了」", "aiy350000070.opus"], //金锁高官07 + ["奥兹", "「喂,来个人」", "aiy350000080.opus"], //金锁高官08 + ["光头男人", "「是」", "aiy820000010.opus"], //龙套2-01 + ["奥兹", "「凯伊姆先生做完工作回来了」", "aiy350000090.opus"], //金锁高官09 + ["光头男人", "「是,是,那个······」", "aiy820000020.opus"], //龙套2-02 + ["奥兹", "「我是要你拿些酒来,这个蠢材!」", "aiy350000100.opus"], //金锁高官10 + ["", "喀!"], + ["", "奥兹扔出的烟灰缸砸中了手下的额头。"], + ["", "鲜血四溅。"], + ["凯伊姆", "「不用这么麻烦」", "aiy310000060.opus"], //男主06 + ["凯伊姆", "「我接下来要去《菲诺列塔》」", "aiy310000070.opus"], //男主07 + ["奥兹", "「喔唷」", "aiy350000110.opus"], //金锁高官11 + [ + "奥兹", + "「既然如此,我就不留您在这里喝难饮的劣质酒了」", + "aiy350000120.opus", + ], //金锁高官12 + ["", "奥兹斜眼看着正捂住额头呻吟的手下,轻描淡写地说道。"], + ["凯伊姆", "「用这些钱去买药」", "aiy310000080.opus"], //男主08 + ["", "我将几枚铜钱仍在那个手下的身前。"], + ["奥兹", "「凯伊姆先生,不用对他们这么好」", "aiy350000130.opus"], //金锁高官13 + ["凯伊姆", "「无妨」", "aiy310000090.opus"], //男主09 + ["凯伊姆", "「话说回来,那个要落跑的女人呢?」", "aiy310000100.opus"], //男主10 + [ + "奥兹", + "「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」", + "aiy350000140.opus", + ], //金锁高官14 + [ + "奥兹", + "「正好,趁此机会凯伊姆先生也来享受一番如何?」", + "aiy350000150.opus", + ], //金锁高官15 + ["年轻人", "「你,你们这些家伙,要对她做什么!?」", "aiy710000080.opus"], //龙套1-08 + ["", "咣!"], + ["", "奥兹给了他一拳。"], + ["", "一击即倒。"], + ["", "喀,咚,咯!"], + ["", "奥兹毫不留情地向男人的脸上踩去。"], + ["年轻人", "「咕······呃咳······」", "aiy710000090.opus"], //龙套1-09 + ["", "折断的牙齿伴着血泡被吐出。"], + ["", "这份白色在鲜红色的液体中格外显眼。"], + [ + "年轻人", + "「你们以为做出这种事······卫兵会坐视不理吗······」", + "aiy710000100.opus", + ], //龙套1-10 + ["奥兹", "「啊啊,不会坐视不理的」", "aiy350000160.opus"], //金锁高官16 + [ + "奥兹", + "「应该会拿出你的钱包,和我们商量如何瓜分吧」", + "aiy350000170.opus", + ], //金锁高官17 + ["年轻人", "「那,那种事······」", "aiy710000110.opus"], //龙套1-11 + ["", "这在牢狱是理所当然的事。"], + ["奥兹", "「怎么,头一回来牢狱么?」", "aiy350000180.opus"], //金锁高官18 + ["", "男人点了点头。"], + [ + "奥兹", + "「为了被骗的女人而来到牢狱,真是个规矩人啊」", + "aiy350000190.opus", + ], //金锁高官19 + ["奥兹", "「······前提是,被骗的人不是你」", "aiy350000200.opus"], //金锁高官20 + ["年轻人", "「你说······我被骗了?」", "aiy710000120.opus"], //龙套1-12 + ["年轻人", "「那,那是怎么回事!?」", "aiy710000130.opus"], //龙套1-13 + ["奥兹", "「不用急,今天晚上会好好告诉你的」", "aiy350000210.opus"], //金锁高官21 + ["", "奥兹抓起男人的脸。"], + ["", "为引诱客人的怜悯之心而装纯,是娼妇的惯用手段。"], + ["", "双亲被骗而借钱,结果作为抵押而将自己卖到这里,这是典型的说法。"], + [ + "", + "如果只是头脑发热而成为常客也就罢了,这次的男人热血过头,居然想出了要带女人私奔的计划。", + ], + [ + "", + "虽然女人半开玩笑地予以拒绝,但不知天高地厚的这家伙还是拉着她逃跑了。", + ], + ["", "不过,想要逃脱追击本来就是不可能的任务。"], + ["", "但即便如此,这种事情还是会一再的出现。"], + ["", "说谎的女人和被骗的男人。"], + ["", "在娼馆街,这是令人看到生厌的日常的风景。"], + ["凯伊姆", "「我要走了」", "aiy310000110.opus"], //男主11 + ["奥兹", "「好的,下次再麻烦您」", "aiy350000220.opus"], //金锁高官22 + ["奥兹", "「之后吉克先生会将谢礼交给您的」", "aiy350000230.opus"], //金锁高官23 + ["凯伊姆", "「啊啊」", "aiy310000120.opus"], //男主12 + ["", "我背向奥兹走出娼馆。"], + ["凯伊姆", "「······?」", "aiy310000130.opus"], //男主13 + ["", "从远方传来微弱的歌声。"], + ["", "是关卡广场的方向。"], + ["", "对了。"], + ["", "今天有觐见圣女的仪式。"], + ["", "当代的圣女伊莲——"], + ["", "俗称《盲眼之圣女》,据说即使在历代的圣女中,人气也是数一数二的。"], + ["", "广场上的人估计相当多吧。"], + ["", "虽然我也想去看看她长什么样,不过要在人潮中挤来挤去就免了。"], + ["", "还是老老实实去菲诺列塔喝烧酒吧。"], + ["", "正当我这样想着的时候,一个身影自小巷的那头走来。"], + ["凯伊姆", "「艾莉斯」", "aiy310000140.opus"], //男主14 + ]; + this.chapter01 = [ + ["艾莉斯", "「啊,凯伊姆」", "aiy020000005.opus"], //医生00.5 + ["艾莉斯", "「正好,我还想要去找你呢」", "aiy020000010.opus"], //医生01 + [ + "艾莉斯", + "「没想到凯伊姆会主动出现······这是命运吗?」", + "aiy020000020.opus", + ], //医生02 + ["凯伊姆", "「显然不是吧」", "aiy310000150.opus"], //男主15 + ["艾莉斯", "「啊,是么」", "aiy020000030.opus"], //医生03 + ["", "艾莉斯挑了挑整齐的双眉,微微地哼了一声。"], + [ + "", + "虽然是个相当引人注目的美人,但她这个将亲切儿子丢入无底深渊的性格,为自己扣了不少的分", + ], + ["", "给人印象最深的,就是那潭水般的双瞳。"], + ["", "在漆黑的瞳孔中,完全看不出感情的波动。"], + ["艾莉斯", "「喜欢我的眼睛吗?」", "aiy020000040.opus"], //医生04 + ["艾莉斯", "「如果想要的话就给你吧?」", "aiy020000050.opus"], //医生05 + ["凯伊姆", "「用不着」", "aiy310000160.opus"], //男主16 + ["艾莉斯", "「阿拉,可惜」", "aiy020000060.opus"], //医生06 + ["凯伊姆", "「那么,找我有什么事」", "aiy310000170.opus"], //男主17 + ["艾莉斯", "「梅尔特的钱好像被偷了」", "aiy020000070.opus"], //医生07 + ["凯伊姆", "「钱被偷了?都几岁了还这么没用」", "aiy310000180.opus"], //男主18 + ["艾莉斯", "「不要对我说啊」", "aiy020000080.opus"], //医生08 + [ + "凯伊姆", + "「那家伙,该不会说要让我去抓那个小偷吧?」", + "aiy310000190.opus", + ], //男主19 + ["艾莉斯", "「就是这样」", "aiy020000090.opus"], //医生09 + ["凯伊姆", "「笨蛋吗」", "aiy310000200.opus"], //男主20 + ["凯伊姆", "「如果是小钱的话,就当做是买个教训吧」", "aiy310000210.opus"], //男主21 + ["艾莉斯", "「说起来,被盗的是这个月的上纳金」", "aiy020000100.opus"], //医生10 + ["凯伊姆", "「你说什么?」", "aiy310000220.opus"], //男主22 + ["艾莉斯", "「用这些钱买教训,也太过奢侈了呢」", "aiy020000110.opus"], //医生11 + ["凯伊姆", "「知道了,我去找」", "aiy310000230.opus"], //男主23 + ["凯伊姆", "「小偷的特征呢」", "aiy310000240.opus"], //男主24 + ["艾莉斯", "「男孩子」", "aiy020000120.opus"], //医生12 + ["艾莉斯", "「······而且,背后有翅膀」", "aiy020000130.opus"], //医生13 + [ + "艾莉斯", + "「虽然姑且是藏在身后,但是仔细观察的话是很明显的」", + "aiy020000140.opus", + ], //医生14 + ["凯伊姆", "「羽化病吗」", "aiy310000250.opus"], //男主25 + [ + "艾莉斯", + "「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」", + "aiy020000150.opus", + ], //医生15 + ["凯伊姆", "「注意到他逃窜的方向了吗?」", "aiy310000260.opus"], //男主26 + ["艾莉斯", "「广场那边」", "aiy020000160.opus"], //医生16 + [ + "艾莉斯", + "「虽然刚才《不蚀金锁》的人去追了,不过多半是······」", + "aiy020000170.opus", + ], //医生17 + ["凯伊姆", "「偏偏还是广场吗」", "aiy310000280.opus"], //男主28 + ["艾莉斯", "「今天是觐见圣女大人的日子」", "aiy020000180.opus"], //医生18 + ["凯伊姆", "「我知道」", "aiy310000290.opus"], //男主29 + ["凯伊姆", "「尽量找找看就好」", "aiy310000300.opus"], //男主30 + ]; + this.chapter02 = [ + ["不蚀金锁成员", "「凯伊姆先生,凯伊姆先生」"], + ["不蚀金锁成员", "「您已经和艾莉斯大夫见过面了吗?」"], + ["凯伊姆", "「啊啊,所以才会追过来的」"], //男主31 + ["凯伊姆", "「看到小偷了吗?」"], //男主32 + [ + "不蚀金锁成员", + "「没有,他向广场那边逃了过去,今天这么拥挤,我们也只能放弃了」", + ], + [ + "不蚀金锁成员", + "「不过,我也只是刚好在店里所以才追了过去,并不是受人所托」", + ], + ["不蚀金锁成员", "「我已经准备撤退了」"], + ["不蚀金锁成员", "「凯伊姆先生还要继续追吗?」"], + ["凯伊姆", "「啊啊」"], //男主33 + ["", "做完情报交换之后,我跟男人道别。"], + ["凯伊姆", "「和我想的一样啊······」"], //男主34 + ["", "在牢狱中最大的广场上,聚集着看不到尽头的人群。"], + ["", "就算是来参见圣女祈祷,这人数也太多了点吧。"], + ["", "自然,我也找不到逃跑的孩子。"], + ["", "是混杂到人群中了吧。"], + ["", "如果已经从广场上脱身了的话,就更难发现了。"], + ["", "只好赌他还在这里了。"], + ["", "我先移动到了一个视野良好的地方。"], + ["", "从这里,一眼就可以看到人群的变化。"], + ["", "广场还是沸腾起来。"], + ["", "抬头望去,原来是在天台之上出现了一个人影"], + ["", "但是,与周围的期待不同,现身的是一名中年的神官。"], + ["", "骂声四溢。"], + ["", "神官则是笑着摆正衣领"], + ["神官", "「从现在开始,举行谒见的仪式」"], + [ + "神官", + "「在参见那位大人之前,我希望牢狱的诸位再次思考这个《诺瓦斯·艾蒂尔》存在的意义······」", + ], + [ + "神官", + "「初代圣女伊莲大人,便是也难怪这崇高的祈祷之力,令《诺瓦斯·艾蒂尔》浮在空中,拯救了我们的祖先」", + ], + [ + "神官", + "「这之后的几百年来,传承了初代大人力量的历代圣女伊莲大人,让这里留在了空中」", + ], + [ + "神官", + "「这座都市是被圣女大人守护的人类最后的圣域,而我们则是被选召的虔诚的信徒」", + ], + ["神官", "「怀着对圣女的感激祈祷吧,感谢圣女伊莲吧!并献上祈祷!」"], + ["圣女", "「不忘感谢与祈祷,神才会拯救我们」"], + ["圣女", "「与我一起,向神虔诚地祈祷吧」"], + ["", "广场上欢声雷动。"], + ["", "圣女没有回应喧嚣的人声,而是静静地合上双眼面向广场。"], + ["", "虽然感觉有些冷淡,但总比像个傻瓜似的笑着向这群人挥手要强。"], + ["", "她掌握着这条街道,还有在这条街上生活的人的命运。"], + ["", "比起揽得人气,她更想要为了街道的继续存在而献出全力。"], + ["", "也是为了不让《大崩落》的惨剧再度发生。"], + ["", "十几年前的那场悲剧。"], + [ + "", + "虽然在我脑海中的记忆已经相当模糊,但哪怕只是稍有触及,不快的感觉都会在胸口蔓延开。", + ], + ["凯伊姆", "「······」"], //男主35 + ["", "这时我才想起,现在不是我在这里看圣女的时候。"], + ["女声", "「——っ!?」"], + ["围观的女人", "「羽,羽化病人!?」"], + ["围观的中年人", "「喂,谁去叫下羽狩」"], + ["惊慌的观众", "「你这家伙不要靠近我,要是传染了可怎么办」"], + ["粗鲁的观众", "「你这小鬼赶快滚开」"], + ["凯伊姆", "「接下来」"], + ["圣女", "「发生什么事了?看上去似乎很嘈杂」"], + ["随从", "「似乎是某个人逃跑了······具体的我也不是很清楚」"], + ["神官", "「圣女大人,继续待在天台上可能会出事,请您先回到室内吧」"], + ["圣女", "「不用在意我,比起那个,我更关心究竟发生了什么事」"], + ["神官", "「对不起,我真的不知道」"], + ["圣女", "「······是吗」"], + ["男", "「恕我僭越,请准许我说明情况」"], + ["男", "「在来觐见的人群中出现了《羽化病》的患者」"], + ["男", "「围观的人群因而产生了骚动」"], + [ + "男", + "「现在,《防疫局》已经派遣了部队。我想不久之后,他们就会安静下来了」", + ], + ["圣女", "「羽化病······」"], + ["男", "「怎么了?」"], + ["圣女", "「没什么」"], + ["圣女", "「辛苦了,你的名字是?」"], + ["男", "「属下是在防疫局任职的,鲁基乌斯· 迪斯·米利尤」"], + ["神官", "「噢噢,阁下就是鲁基乌斯卿吗,我听说过你的传闻」"], + ["神官", "「阁下是在工作上相当出色的人呢」"], + ["鲁基乌斯", "「不敢当」"], + [ + "鲁基乌斯", + "「话说回来,这次是属下警备工作的失职。让圣女大人见到这不成体统的一面,请您见谅」", + ], + [ + "圣女", + "「即使是目不见物的我,也能感受到聚集于此的民众数量之多。警备工作难以展开也在情理之中」", + ], + ["鲁基乌斯", "「属下不胜惶恐」"], + ["鲁基乌斯", "「接下来属下还要回到工作岗位上,在这里就先告退了」"], + ["圣女", "「鲁基乌斯先生」"], + ["鲁基乌斯", "「属下在」"], + ["圣女", "「你是怎样看待羽狩的工作的呢?」"], + ["神官", "「圣,圣女大人」"], + [ + "鲁基乌斯", + "「防疫局的工作是国王陛下赐予的重要职务。属下非常荣幸能够为这个都市的繁荣尽一份微薄之力」", + ], + ["神官", "「不,不亏是鲁基乌斯卿,相当优秀的想法」"], + ["圣女", "「是吗。辛苦你了」"], + ["随从", "「圣女大人······」"], + ["鲁基乌斯", "「······」"], + ["鲁基乌斯", "「那么,属下就回岗位去了」"], + ]; + this.chapter03 = [ + ["", "从羽化病的少年纷乱的足音中,可以听得出相当的疲劳。"], + ["", "显然,他并没有想到我会捷足先登吧。"], + ["", "少年惶恐地回头看了一眼后,微微露出安心的表情,双手拄在膝盖上。。"], + ["凯伊姆", "「辛苦你了」"], //男主37 + ["羽化病患少年", "「稀!?」"], + ["凯伊姆", "「逃到贫民区是个不错的想法」"], //男主38 + ["羽化病患少年", "「你,你是,羽狩吗?」"], + ["凯伊姆", "「不是」"], //男主39 + ["羽化病患少年", "「什,什么啊······混蛋,不要吓我啊」"], + ["凯伊姆", "「我对令你受惊这件事致以歉意」"], //男主40 + ["凯伊姆", "「作为回报,麻烦你把从店里偷的钱交出来吧」"], //男主41 + ["羽化病患少年", "「钱?你在说什么」"], + ["凯伊姆", "「你要找的腰上的东西,掉在你身后了」"], //男主42 + ["羽化病患少年", "「哎?」"], + ["羽化病患少年", "「呃呀」"], + ["羽化病患少年", "「你······你这混蛋」"], + ["凯伊姆", "「······」"], //男主43 + ["凯伊姆", "「把偷的钱交出来」"], //男主44 + ["羽化病患少年", "「我不知道你在······咕」"], + ["羽化病患少年", "「你,你说是我偷的······有什么证据吗」"], + ["凯伊姆", "「你还挺倔的啊」"], //男主45 + ["凯伊姆", "「不过,给我听好了」"], //男主46 + ["凯伊姆", "「你偷的那些钱,是要上缴给《不蚀金锁》的上纳金」"], //男主47 + ["凯伊姆", "「而且,钱的主人是从前和吉克颇有渊源的女人」"], //男主48 + ["羽化病患少年", "「吉克?」"], + ["凯伊姆", "「他是《不蚀金锁》的主人,这么说你就明白了吧」"], //男主49 + ["羽化病患少年", "「哎?哎?怎么会······」"], + ["凯伊姆", "「再问你一遍,钱在哪里?」"], //男主50 + ["羽化病患少年", "「是,是,是,在我的怀里」"], + ["凯伊姆", "「你没有擅自拿掉一部分吧」"], //男主51 + ["羽化病患少年", "「是,是的」"], + ["羽化病患少年", "「那,那个,您是《不蚀金锁》的人吗?」"], + ["凯伊姆", "「算是吧」"], //男主52 + ["羽化病患少年", "「我什么都可以做,请您一定要帮帮我」"], + ["凯伊姆", "「抱歉,我并没有兴趣去帮助他人」"], //男主53+54 + ["羽化病患少年", "「我什么······什么,都会做的······」"], + ["羽化病患少年", "「我一直都是生活在下层的」"], + [ + "羽化病患少年", + "「可是,不知何时染上了羽化病······背后长出了翅膀······」", + ], + ["羽化病患少年", "「被寄宿工作的店赶了出来,只得流落到牢狱这里」"], + ["羽化病患少年", "「因为独自实在是饿的不行了,所以才会偷这些钱的」"], + [ + "羽化病患少年", + "「我明明没有做任何坏事······为什么······会遇到这种事······」", + ], + ["凯伊姆", "「谁知道」"], //男主55 + ["羽化病患少年", "「呜······呜呜······接下来,要对我做什么?」"], + ["凯伊姆", "「我要把你带到组织那里」"], //男主56 + ["羽化病患少年", "「怎,怎么这样」"], + ["凯伊姆", "「不过,那样做的前提是你不是羽化病人」"], + ["凯伊姆", "「组织也没有笨到把羽化病人招待到家里的程度」"], + ["羽化病患少年", "「那么,是要放我逃走吗?」"], + ["凯伊姆", "「我要让你学到教训」"], + ["凯伊姆", "「如果换做是组织的制裁,至少要有断条胳膊的觉悟」"], + ["凯伊姆", "「你的运气不错」"], + ["羽化病患少年", "「唔······啊,是的······」"], + ["凯伊姆", "「滚」"], + ["羽化病患少年", "「非常感谢」"], + ["羽化病患少年", "「唔啊!?」"], + ["男", "「到这里就结束了,羽化病人」"], + ["男", "「确认他的翅膀」"], + ["", "趁还没有被卷入麻烦的事情之前,赶快离开这里吧"], + ["羽狩的队长", "「那边的那个人」"], + ["凯伊姆", "「······有什么事?」"], + ["羽狩的队长", "「可以稍微让我问几句话吗」"], + ["凯伊姆", "「······」"], + ["凯伊姆", "「啊啊,无妨」"], + ["羽狩的队长", "「感谢您的合作」"], + ["", "队长殷勤地致以谢礼。"], + ["", "而在他的眼前,少年的衣服已经被他的补下们扯破。"], + ["", "在瘦骨嶙峋的裸露后背上,长有纯白的羽翼。"], + ["红发的羽狩", "「副队长,确认翅膀的持有了」"], + ["羽狩的副队长", "「保护他」"], + ["羽化病患少年", "「不要······请原谅,我······」"], + [ + "羽狩的副队长", + "「我们只是要带你去治愈院治疗羽化病,不是什么应该感到害怕的事情」", + ], + ["羽化病患少年", "「可是,可是」"], + ["羽狩的副队长", "「没关系的」"], + ["羽化病患少年", "「······哥,哥哥」"], + ["羽狩的副队长", "「你是羽化病人的亲属吗?」"], + ["凯伊姆", "「只是路人而已」"], + ["凯伊姆", "「顺带一提,我也没有打算找你们的麻烦」"], + [ + "羽狩的副队长", + "「前几天,有个和你说了同样的话的人,在我们背向他的瞬间对我们发动了袭击」", + ], + ["羽狩的副队长", "「我的一个部下就此永久失去了半截胳膊」"], + ["凯伊姆", "「我表示同情」"], + ["凯伊姆", "「我马上就会消失的,这样就没问题了吧?」"], + ["羽狩的副队长", "「嘛,不要这么慌张」"], + ["", "副队长看着羽化的少年。"], + ["羽狩的副队长", "「你与这个人是什么关系?没有被他殴打吗?」"], + ["羽化病患少年", "「没,没有」"], + [ + "羽狩的副队长", + "「如何对我们保持合作,你就可以在治愈院得到优先的治疗」", + ], + ["羽化病患少年", "「······」"], + ["羽化病患少年", "「那个人,是《不蚀金锁》的组织成员······」"], + ["羽化病患少年", "「突然说让我拿出钱来,我刚一拒绝他就打我」"], + ["羽狩的副队长", "「原来如此······」"], + [ + "羽狩的副队长", + "「那位少年说你是《不蚀金锁》的一员,不知此事是否属实?」", + ], + ["凯伊姆", "「当然不是」"], + ["凯伊姆", "「我只是从那里接受工作而已,并不是他们的成员」"], + ["羽狩的副队长", "「你的意思是说,少年在说谎吗?」"], + ["凯伊姆", "「啊啊」"], + [ + "凯伊姆", + "「如果你们和组织有关系的话,只要问问我是不是那里的成员就能明白事实了吧」", + ], + ["羽狩的副队长", "「就算我去询问,也无法从他们那里得到事实」"], + [ + "羽狩的副队长", + "「《不蚀金锁》的那些人一向都不对我们合作,我对此深感困扰」", + ], + ["凯伊姆", "「真是辛苦啊」"], + ["羽狩的副队长", "「说的是啊」"], + ["羽狩的副队长", "「其实,砍下我部下胳膊的似乎也是组织的成员呢」"], + [ + "羽狩的副队长", + "「无需如此警戒,我只是想在看守所向你咨询一些事情而已」", + ], + [ + "羽狩的副队长", + "「如果能够知晓牢狱与组织的事情,我们也可以尽可能地对更多的羽化病人进行保护」", + ], + ["羽狩的副队长", "「那和整条街道的和平也是紧密相关的吧?」"], + ["凯伊姆", "「我知道,你们有逮捕干扰狩猎羽化病人的权力」"], + [ + "凯伊姆", + "「但是,我没有对你们做出任何干扰,为什么要对我如此纠缠不休呢」", + ], + ["羽狩的副队长", "「那些话,我们会在看守所对你详细说明的」"], + ["凯伊姆", "「······」"], + ["", "在这里起争执的话,就会被羽狩加害。"], + ["", "就算能从这里脱身,今后只要碰面就会产生纠纷也是摆明的事情。"], + ["", "就算逃跑,也没有好的结果。"], + ["", "正当我想要打圆场的时候,刚才的气氛一瞬间产生了转变。"], + ["", "发生了什么事······"], + ["???", "「我认为,那位先生是正确的」"], + ["", "羽狩们一起回头。"], + ["", "而在他们视线的焦点处,"], + ["", "伫立着一位女性。"], + ["", "在端正的容颜下,代表着强烈意志的双眉十分显眼。"], + ["", "身体的柔软与紧紧包裹在其身上的羽狩制服,两者显得十分的不搭配。"], + ["", "我还是第一次看到女性的羽狩。"], + ["羽狩的副队长", "「队长,这是获得《不蚀金锁》情报的好机会」"], + [ + "羽狩的队长", + "「兰格副队长,就算是为了获得情报,也不能做出恫吓的发言啊」", + ], + ["兰格副队长", "「我并没有打算去恫吓他······」"], + ["羽狩的队长", "「告诉我那个被砍掉胳膊的队员的名字」"], + ["羽狩的队长", "「我会去探望他的」"], + ["兰格副队长", "「那个是······」"], + ["羽狩的队长", "「我知道,你一直在为有所收获而努力工作」"], + ["羽狩的队长", "「但是,正因为我们的工作是为民众提供帮助」"], + ["羽狩的队长", "「所以就更不能损害人与人之间的信赖」"], + ["兰格副队长", "「我会铭记在心」"], + ["羽狩的队长", "「这位先生,我的部下失礼了」"], + ["凯伊姆", "「只要不对我再来一次就好」"], + ["羽狩的队长", "「请稍等」"], + ["凯伊姆", "「有什么事?」"], + ["羽狩的队长", "「我想确认一件事」"], + ["羽狩的队长", "「你真的不是《不蚀金锁》的成员吗?」"], + ["凯伊姆", "「真的」"], + ["凯伊姆", "「如果我说是的话,你有什么打算?」"], + ["羽狩的队长", "「我听过传闻,说他们是用依靠暴力而得的钱在生活」"], + ["凯伊姆", "「······这样啊」"], + ["凯伊姆", "「如果能有收获就好了啊」"], + ]; + this.chapter04 = [ + ["梅尔特", "「欢迎光临」"], + ["梅尔特", "「辛苦了」"], + ["梅尔特", "「抱歉,又拜托给你了个这么麻烦的工作」"], + ["凯伊姆", "「没什么,比想象中完成的更容易」"], + ["梅尔特", "「那就好」"], + ["梅尔特", "「这是我的一点谢意」"], + ["凯伊姆", "「味道有些变化啊」"], + ["梅尔特", "「啊,被发现了?」"], + ["梅尔特", "「最近,没能到手什么好的原料呢」"], + ["凯伊姆", "「去拜托吉克如何?」"], + [ + "梅尔特", + "「话是这么说,但是总不能用店里采购的这种小事去麻烦他吧······」", + ], + ["凯伊姆", "「那希望你也不要来麻烦我」"], + ["梅尔特", "「那 是 两 码 事」"], + ["梅尔特", "「再说,凯伊姆是靠着工作来生活的吧」"], + [ + "梅尔特", + "「而且,自己的钱被偷了这么害羞的事,向凯伊姆意外的其他人都说不出口」", + ], + ["凯伊姆", "「反正,也已经传到吉克的耳朵里了」"], + ["梅尔特", "「这是面子问题啊,面子问题」"], + ["凯伊姆", "「嘛,算了」"], + ["凯伊姆", "「这样就好了吧?」"], + ["梅尔特", "「这是钱包呢」"], + ["梅尔特", "「嗯,东西没少」"], + ["梅尔特", "「太好啦—这个月的上纳金,我可全部都放在里面了呢」"], + ["梅尔特", "「如果没有找到的话,说不定就又会被送到娼馆里了呢」"], + ["凯伊姆", "「在那边不是来钱更快吗?」"], + ["梅尔特", "「阿拉,你是在说我还能有魅力吗?」"], + ["凯伊姆", "「这是客套话而已」"], + ["梅尔特", "「欺负人」"], + ["梅尔特", "「总而言之,今天帮大忙了」"], + ["梅尔特", "「谢礼嘛······」"], + ["凯伊姆", "「就记在账单上吧」"], + ["梅尔特", "「了解—盛谢惠顾了哦?」"], + ["", "喀啷喀啷"], + ["", "门铃响起"], + ["", "喧哗瞬间安静下来。"], + ["", "进来的人是吉克。"], + ["", "是掌控着牢狱的组织之一,《不蚀金锁》的头目。"], + ["", "不仅组织的成员,就连店内一般的客人也对他以注目礼表示敬意。"], + ["吉克", "「各位继续吧」"], + ["", "仿佛停滞的时钟重新转动了一般,店内恢复了热闹的气氛。"], + ["吉克", "「抱歉,今天拜托你去做了无聊的工作」。"], + ["凯伊姆", "「不用介意」"], + ["", "吉克轻轻点了点头,在我右边坐了下来"], + ["凯伊姆", "「逃跑的男人怎么样了?」"], + ["吉克", "「嗯?已经不在这个世上了」。"], + ["吉克", "「有什么想要知道的事吗?」"], + ["凯伊姆", "「不,没什么」"], + ["吉克", "「那个无聊的家伙,完全没有趣味呢」"], + ["吉克", "「真希望他也替我负责清扫的部下也考虑考虑」"], + ["凯伊姆", "「真是灾难啊」"], + ["吉克", "「比起那个,我听说了哦。你去追羽化病人了啊」"], + ["凯伊姆", "「消息真灵通」"], + ["吉克", "「梅尔特也注意点」"], + ["吉克", "「你丢钱已经不是一回两回了」"], + ["梅尔特", "「好的—我会注意的。」"], + ["梅尔特", "「吉克还是平常的点单吧」"], + ["梅尔特", "「凯伊姆要再来一杯吗?」"], + ["", "我们用眼神点头示意后,梅尔特开始准备起酒来。"], + ["", "悠然地吐出眼圈后,吉克取出一个纸包放在柜台上。"], + ["吉克", "「这是抓捕逃跑男人的报酬」"], + ["凯伊姆", "「下次有什么事再告诉我」"], + ["梅尔特", "「来,久等了」"], + ["凯伊姆", "「话说回来梅尔特,为什么会被那种孩子偷到钱?」"], + ["吉克", "「让我猜猜看」"], + ["吉克", "「是那个吧,看某个特立独行的男人入迷了,所以就有了空隙?」"], + ["梅尔特", "「可惜—」"], + ["梅尔特", "「事实恰恰相反,是那家伙一直在纠缠我」"], + ["凯伊姆", "「完全把你当成新进的女佣了么」"], + ["梅尔特", "「我从前可是很有名的,不会被当成这种下人吧」"], + ["梅尔特", "「······而且,我没法对对我这么钟情的人发火啊」"], + ["凯伊姆&吉克", "「你傻啊」"], + ["梅尔特", "「异口同声呢,不亏是兄弟」"], + ["凯伊姆", "「别用这种称呼,怪恶心的」"], + ["吉克", "「说得没错」"], + ["吉克", "「······说起来······」"], + ["梅尔特", "「怎么了?」"], + ["吉克", "「有件事我一直很在意,我和凯伊姆,哪个是哥哥啊?」"], + ["凯伊姆", "「你也说这么无聊的话题」"], + ["吉克", "「不,这是很重要的事情」"], + ["吉克", "「梅尔特,事实是怎么样的?」"], + ["梅尔特", "「啊~是怎么样的呢~」"], + ["梅尔特", "「我忘记了」"], + ["吉克", "「骗人」"], + ]; + } } \ No newline at end of file diff --git a/styles.css b/styles.css index e7ba189..e300825 100644 --- a/styles.css +++ b/styles.css @@ -544,9 +544,9 @@ p#name { } /* 注释下面这三行以开启抗锯齿 */ -.anti-aliasing { +/* .anti-aliasing { image-rendering: pixelated; -} +} */ .warning { transition: left cubic-bezier(0, 0.9, 1, 0.1) 2.5s;