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..0674e05 100644
--- a/project/data.js
+++ b/project/data.js
@@ -437,12 +437,14 @@ 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",
@@ -1116,7 +1118,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"atk": 100,
"def": 100,
"mdef": 0,
- "speed": 0,
+ "speed": 10,
"money": 0,
"exp": 0,
"equipment": [],
@@ -1494,14 +1496,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..20f75d5 100644
--- a/project/events.js
+++ b/project/events.js
@@ -10883,6 +10883,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/functions.js b/project/functions.js
index ea0a448..d202ca9 100644
--- a/project/functions.js
+++ b/project/functions.js
@@ -892,171 +892,170 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
};
},
"getDamageInfo": function (enemy, hero, x, y, floorId) {
- // 获得战斗伤害信息(实际伤害计算函数)
- //
- // 参数说明:
- // enemy:该怪物信息
- // hero:勇士的当前数据;如果对应项不存在则会从core.status.hero中取。
- // x,y:该怪物的坐标(查看手册和强制战斗时为undefined)
- // floorId:该怪物所在的楼层
- // 后面三个参数主要是可以在光环等效果上可以适用
- floorId = floorId || core.status.floorId;
+ // 获得战斗伤害信息(实际伤害计算函数)
+ //
+ // 参数说明:
+ // enemy:该怪物信息
+ // hero:勇士的当前数据;如果对应项不存在则会从core.status.hero中取。
+ // x,y:该怪物的坐标(查看手册和强制战斗时为undefined)
+ // floorId:该怪物所在的楼层
+ // 后面三个参数主要是可以在光环等效果上可以适用
+ floorId = floorId || core.status.floorId;
- var hero_hp = core.getRealStatusOrDefault(hero, "hp"),
- hero_atk = core.getRealStatusOrDefault(hero, "atk"),
- hero_def = core.getRealStatusOrDefault(hero, "def"),
- hero_mdef = core.getRealStatusOrDefault(hero, "mdef"),
- hero_speed = core.getRealStatusOrDefault(hero, "speed"),
- hero_magic = core.getRealStatusOrDefault(hero, "magic"),
- origin_hero_hp = core.getStatusOrDefault(hero, "hp"),
- origin_hero_atk = core.getStatusOrDefault(hero, "atk"),
- origin_hero_def = core.getStatusOrDefault(hero, "def");
+ var hero_hp = core.getRealStatusOrDefault(hero, "hp"),
+ hero_atk = core.getRealStatusOrDefault(hero, "atk"),
+ hero_def = core.getRealStatusOrDefault(hero, "def"),
+ hero_mdef = core.getRealStatusOrDefault(hero, "mdef"),
+ hero_speed = core.getRealStatusOrDefault(hero, "speed"),
+ hero_magic = core.getRealStatusOrDefault(hero, "magic"),
+ origin_hero_hp = core.getStatusOrDefault(hero, "hp"),
+ origin_hero_atk = core.getStatusOrDefault(hero, "atk"),
+ origin_hero_def = core.getStatusOrDefault(hero, "def");
- // 怪物的各项数据
- // 对坚固模仿等处理扔到了脚本编辑-getEnemyInfo之中
- var enemyInfo = core.enemys.getEnemyInfo(enemy, hero, x, y, floorId);
- var mon_hp = enemyInfo.hp,
- mon_atk = enemyInfo.atk,
- mon_def = enemyInfo.def,
- mon_mdef = enemyInfo.mdef,
- mon_speed = enemyInfo.speed,
- mon_special = enemyInfo.special,
- mon_absorb_damage = enemyInfo.absorb,
- mon_barrier = enemyInfo.barrier;
+ // 怪物的各项数据
+ // 对坚固模仿等处理扔到了脚本编辑-getEnemyInfo之中
+ var enemyInfo = core.enemys.getEnemyInfo(enemy, hero, x, y, floorId);
+ var mon_hp = enemyInfo.hp,
+ mon_atk = enemyInfo.atk,
+ mon_def = enemyInfo.def,
+ mon_mdef = enemyInfo.mdef,
+ mon_speed = enemyInfo.speed,
+ mon_special = enemyInfo.special,
+ mon_absorb_damage = enemyInfo.absorb,
+ mon_barrier = enemyInfo.barrier;
- //---第一部分:静态属性修正---
- //此处写入静态影响勇士属性的勇士或怪物技能(静态影响怪物属性的技能于getEnemyInfo中写入)
- // 技能的处理
- if (core.getFlag("skill", 0) == 1) {
- // 开启了技能1:二倍斩
- hero_atk *= 2; // 计算时攻击力翻倍
- }
- //勇士属性取整
- hero_atk = Math.max(0, Math.floor(hero_atk));
- hero_def = Math.max(0, Math.floor(hero_def));
- hero_mdef = Math.max(0, Math.floor(hero_mdef));
- hero_speed = Math.max(0, Math.floor(hero_speed));
+ //---第一部分:静态属性修正---
+ //此处写入静态影响勇士属性的勇士或怪物技能(静态影响怪物属性的技能于getEnemyInfo中写入)
+ // 技能的处理
+ if (core.getFlag("skill", 0) == 1) {
+ // 开启了技能1:二倍斩
+ hero_atk *= 2; // 计算时攻击力翻倍
+ }
+ //勇士属性取整
+ hero_atk = Math.max(0, Math.floor(hero_atk));
+ hero_def = Math.max(0, Math.floor(hero_def));
+ hero_mdef = Math.max(0, Math.floor(hero_mdef));
+ hero_speed = Math.max(0, Math.floor(hero_speed));
- // 如果是无敌属性,且勇士未持有十字架
- if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross"))
- return null; // 不可战斗
+ // 如果是无敌属性,且勇士未持有十字架
+ if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross"))
+ return null; // 不可战斗
- // 战前造成的额外伤害(可被护盾抵消)
- let init_damage = 0;
+ // 战前造成的额外伤害(可被护盾抵消)
+ let init_damage = 0;
- // 吸血
- if (core.hasSpecial(mon_special, 11)) {
- let vampire_damage = hero_hp * enemy.vampire;
+ // 吸血
+ if (core.hasSpecial(mon_special, 11)) {
+ let vampire_damage = hero_hp * enemy.vampire;
- // 如果有神圣盾免疫吸血等可以在这里写
- // 也可以用hasItem和hasEquip来判定装备
- // if (core.hasFlag('shield5')) vampire_damage = 0;
+ // 如果有神圣盾免疫吸血等可以在这里写
+ // 也可以用hasItem和hasEquip来判定装备
+ // if (core.hasFlag('shield5')) vampire_damage = 0;
- vampire_damage = Math.floor(vampire_damage) || 0;
- // 加到自身
- if (enemy.add)
- // 如果加到自身
- mon_hp += vampire_damage;
+ vampire_damage = Math.floor(vampire_damage) || 0;
+ // 加到自身
+ if (enemy.add)
+ // 如果加到自身
+ mon_hp += vampire_damage;
- init_damage += vampire_damage;
- }
+ init_damage += vampire_damage;
+ }
- //——第二部分:变量定义和初始赋值——
- // 每回合怪物对勇士造成的战斗伤害
- let per_damage = Math.max(mon_atk - hero_def, 0);
- if (enemyInfo.magic) per_damage = Math.max(mon_atk - hero_mdef, 0);
+ //——第二部分:变量定义和初始赋值——
+ // 每回合怪物对勇士造成的战斗伤害
+ let per_damage = Math.max(mon_atk - hero_def, 0);
+ if (enemyInfo.magic) per_damage = Math.max(mon_atk - hero_mdef, 0);
- //
- let hero_per_damage = Math.max(hero_atk - mon_def, 0);
- if (hero_magic) hero_per_damage = Math.max(hero_atk - mon_mdef, 0);
+ //
+ let hero_per_damage = Math.max(hero_atk - mon_def, 0);
+ if (hero_magic) hero_per_damage = Math.max(hero_atk - mon_mdef, 0);
- let damage = 0,
- hero_turn = 0,
- mon_turn = 0;
- //---第三部分:递归开始---
- let length = hero_speed * mon_speed;
+ let damage = 0,
+ hero_turn = 0,
+ mon_turn = 0;
+ //---第三部分:递归开始---
+ let length = hero_speed * mon_speed;
- for (
- let now_mon_hp = mon_hp,
- last_mon_hp = mon_hp,
- mon_length = length,
- hero_length = length,
- mon_time = 0,
- hero_time = 0;
- now_mon_hp > 0;
+ for (
+ let now_mon_hp = mon_hp,
+ last_mon_hp = mon_hp,
+ mon_length = length,
+ hero_length = length,
+ mon_time = 0,
+ hero_time = 0; now_mon_hp > 0;
- ) {
- //勇士和怪物的长度
- mon_time = mon_length / mon_speed;
- hero_time = hero_length / hero_speed;
- if (
- mon_time < hero_time ||
- ((mon_time = hero_time) && mon_speed > hero_speed)
- ) {
- //怪物攻击的回合
- //这里计算怪物攻击时发生的各种变化
+ ) {
+ //勇士和怪物的长度
+ mon_time = mon_length / mon_speed;
+ hero_time = hero_length / hero_speed;
+ if (
+ mon_time < hero_time ||
+ ((mon_time = hero_time) && mon_speed > hero_speed)
+ ) {
+ //怪物攻击的回合
+ //这里计算怪物攻击时发生的各种变化
- //伤害与回合增加
- damage += per_damage;
- if (core.hasSpecial(mon_special, 6)) {
- mon_turn += enemy.n;
- } else {
- mon_turn += 1;
- }
- //重新为长度赋值
- mon_length = length;
- hero_length = hero_length - mon_time * hero_speed;
- } else {
- //勇士攻击的回合
- // 这里计算勇士攻击时发生的各种变化
+ //伤害与回合增加
+ damage += per_damage;
+ if (core.hasSpecial(mon_special, 6)) {
+ mon_turn += enemy.n;
+ } else {
+ mon_turn += 1;
+ }
+ //重新为长度赋值
+ mon_length = length;
+ hero_length = hero_length - mon_time * hero_speed;
+ } else {
+ //勇士攻击的回合
+ // 这里计算勇士攻击时发生的各种变化
- // 伤害与回合数增加
- now_mon_hp -= hero_per_damage;
- hero_turn += 1; //勇士回合+1,如果有勇士每回合多次攻击的情况,在这里写判断
- // 无法战斗计算
- if (hero_turn % 50 == 0) {
- if (now_mon_hp >= last_mon_hp) return null;
- last_mon_hp = now_mon_hp;
- }
- //重新为长度赋值
- hero_length = length;
- mon_length = mon_length - hero_time * mon_speed;
- }
- }
+ // 伤害与回合数增加
+ now_mon_hp -= hero_per_damage;
+ hero_turn += 1; //勇士回合+1,如果有勇士每回合多次攻击的情况,在这里写判断
+ // 无法战斗计算
+ if (hero_turn % 50 == 0) {
+ if (now_mon_hp >= last_mon_hp) return null;
+ last_mon_hp = now_mon_hp;
+ }
+ //重新为长度赋值
+ hero_length = length;
+ mon_length = mon_length - hero_time * mon_speed;
+ }
+ }
- //下面这些还没修改
- // 连击
- if (core.hasSpecial(mon_special, 6)) per_damage *= enemy.n || 2;
+ //下面这些还没修改
+ // 连击
+ if (core.hasSpecial(mon_special, 6)) per_damage *= enemy.n || 2;
- // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合
- let counterDamage = 0;
- if (core.hasSpecial(mon_special, 8))
- counterDamage += Math.floor(
- (enemy.counterAttack || core.values.counterAttack) * hero_atk
- );
+ // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合
+ let counterDamage = 0;
+ if (core.hasSpecial(mon_special, 8))
+ counterDamage += Math.floor(
+ (enemy.counterAttack || core.values.counterAttack) * hero_atk
+ );
- // 先攻
- if (core.hasSpecial(mon_special, 1)) init_damage += per_damage;
+ // 先攻
+ if (core.hasSpecial(mon_special, 1)) init_damage += per_damage;
- // 破甲
- if (core.hasSpecial(mon_special, 7))
- init_damage += Math.floor(
- (enemy.breakArmor || core.values.breakArmor) * hero_def
- );
+ // 破甲
+ if (core.hasSpecial(mon_special, 7))
+ init_damage += Math.floor(
+ (enemy.breakArmor || core.values.breakArmor) * hero_def
+ );
- // 净化
- if (core.hasSpecial(mon_special, 9))
- init_damage += Math.floor(
- (enemy.purify || core.values.purify) * hero_mdef
- );
- //上面这些还没修改
- //勇士护盾计算
- let barrier = hero_mdef;
- if (enemyInfo.magic) barrier = hero_def;
+ // 净化
+ if (core.hasSpecial(mon_special, 9))
+ init_damage += Math.floor(
+ (enemy.purify || core.values.purify) * hero_mdef
+ );
+ //上面这些还没修改
+ //勇士护盾计算
+ let barrier = hero_mdef;
+ if (enemyInfo.magic) barrier = hero_def;
- // ------ 支援 ----- //
- // 这个递归最好想明白为什么,flag:__extraTurn__是怎么用的
- /*var guards = core.getFlag("__guards__" + x + "_" + y, enemyInfo.guards);
+ // ------ 支援 ----- //
+ // 这个递归最好想明白为什么,flag:__extraTurn__是怎么用的
+ /*var guards = core.getFlag("__guards__" + x + "_" + y, enemyInfo.guards);
var guard_before_current_enemy = false; // ------ 支援怪是先打(true)还是后打(false)?
turn += core.getFlag("__extraTurn__", 0);
if (guards.length > 0) {
@@ -1084,43 +1083,43 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
}
}
core.removeFlag("__extraTurn__");*/
- // ------ 支援END ------ //
+ // ------ 支援END ------ //
- // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害
- damage += init_damage + hero_turn * counterDamage;
- // 再扣去护盾
- damage -= barrier;
+ // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害
+ damage += init_damage + hero_turn * counterDamage;
+ // 再扣去护盾
+ damage -= barrier;
- // 检查是否允许负伤
- if (!core.flags.enableNegativeDamage) damage = Math.max(0, damage);
+ // 检查是否允许负伤
+ if (!core.flags.enableNegativeDamage) damage = Math.max(0, damage);
- // 最后处理仇恨和固伤(因为这两个不能被护盾减伤)
- if (core.hasSpecial(mon_special, 17)) {
- // 仇恨
- damage += core.getFlag("hatred", 0);
- }
- if (core.hasSpecial(mon_special, 22)) {
- // 固伤
- damage += enemy.damage || 0;
- }
+ // 最后处理仇恨和固伤(因为这两个不能被护盾减伤)
+ if (core.hasSpecial(mon_special, 17)) {
+ // 仇恨
+ damage += core.getFlag("hatred", 0);
+ }
+ if (core.hasSpecial(mon_special, 22)) {
+ // 固伤
+ damage += enemy.damage || 0;
+ }
- return {
- mon_hp: Math.floor(mon_hp),
- mon_atk: Math.floor(mon_atk),
- mon_def: Math.floor(mon_def),
- mon_mdef: Math.floor(mon_mdef),
- init_damage: Math.floor(init_damage),
- per_damage: Math.floor(per_damage),
- hero_per_damage: Math.floor(hero_per_damage),
- turn: Math.floor(hero_turn),
- mon_turn: Math.floor(mon_turn),
- damage: Math.floor(damage),
- };
- /*TODO:怪物手册的修改(需要修改这里return的内容以及一些战后判断)
+ return {
+ mon_hp: Math.floor(mon_hp),
+ mon_atk: Math.floor(mon_atk),
+ mon_def: Math.floor(mon_def),
+ mon_mdef: Math.floor(mon_mdef),
+ init_damage: Math.floor(init_damage),
+ per_damage: Math.floor(per_damage),
+ hero_per_damage: Math.floor(hero_per_damage),
+ turn: Math.floor(hero_turn),
+ mon_turn: Math.floor(mon_turn),
+ damage: Math.floor(damage),
+ };
+ /*TODO:怪物手册的修改(需要修改这里return的内容以及一些战后判断)
1. 显示怪物是魔攻还是物攻(在怪物名字上做颜色变化,物攻是黄色,魔攻是蓝色)
2. 一防减伤是物防还是魔防(由怪物是物攻还是魔攻来转换)
3. 特殊战斗的怪物,在怪物手册里“伤害”写为“特殊战”*/
- }
+}
},
"actions": {
"onKeyUp": function (keyCode, altKey) {
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/plugins.js b/project/plugins.js
index 170978f..64da3ac 100644
--- a/project/plugins.js
+++ b/project/plugins.js
@@ -1,262 +1,264 @@
var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
{
"init": function () {
- this._afterLoadResources = function () {
- // 本函数将在所有资源加载完毕后,游戏开启前被执行
- core.ui.statusBar.init();
- core.dom.playGame.style.fontFamily = "pala";
- core.dom.loadGame.style.fontFamily = "pala";
- core.dom.CGMode.style.fontFamily = "pala";
- core.dom.musicMode.style.fontFamily = "pala";
- core.dom.replayGame.style.fontFamily = "pala";
- core.registerEvent("changeMouse", function (data) {
- if (!main.replayChecking && !core.isReplaying())
- core.changeMouse(
- data.icon,
- data.div,
- data.translate[0],
- data.translate[1],
- data.scale[0],
- data.scale[1],
- data.angel,
- data.px,
- data.py
- );
- core.doAction();
- });
- core.registerEvent("removeMouse", function (data) {
- if (!main.replayChecking && !core.isReplaying())
- core.removeMouse(data.div);
- core.doAction();
- });
- core.registerEvent("addPop", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- data.value = core.replaceText(data.value);
- core.addPop(
- data.value,
- data.px,
- data.py,
- data.color,
- data.boldColor,
- data.left,
- data.jump,
- data.time,
- data.show,
- data.font,
- data.speed
- );
- }
- core.doAction();
- });
- core.registerEvent("drawWarning", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- data.text = core.replaceText(data.text);
- data.text2 = core.replaceText(data.text2);
- core.drawWarning(
- data.x,
- data.y,
- data?.text,
- data?.text2,
- data?.warning,
- data.large,
- data.size
- );
+ this._afterLoadResources = function () {
+ // 本函数将在所有资源加载完毕后,游戏开启前被执行
+ core.ui.statusBar.init();
+ core.dom.playGame.style.fontFamily = "pala";
+ core.dom.loadGame.style.fontFamily = "pala";
+ core.dom.CGMode.style.fontFamily = "pala";
+ core.dom.musicMode.style.fontFamily = "pala";
+ core.dom.replayGame.style.fontFamily = "pala";
+ core.registerEvent("changeMouse", function (data) {
+ if (!main.replayChecking && !core.isReplaying())
+ core.changeMouse(
+ data.icon,
+ data.div,
+ data.translate[0],
+ data.translate[1],
+ data.scale[0],
+ data.scale[1],
+ data.angel,
+ data.px,
+ data.py
+ );
+ core.doAction();
+ });
+ core.registerEvent("removeMouse", function (data) {
+ if (!main.replayChecking && !core.isReplaying())
+ core.removeMouse(data.div);
+ core.doAction();
+ });
+ core.registerEvent("addPop", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ data.value = core.replaceText(data.value);
+ core.addPop(
+ data.value,
+ data.px,
+ data.py,
+ data.color,
+ data.boldColor,
+ data.left,
+ data.jump,
+ data.time,
+ data.show,
+ data.font,
+ data.speed
+ );
+ }
+ core.doAction();
+ });
+ core.registerEvent("drawWarning", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ data.text = core.replaceText(data.text);
+ data.text2 = core.replaceText(data.text2);
+ core.drawWarning(
+ data.x,
+ data.y,
+ data?.text,
+ data?.text2,
+ data?.warning,
+ data.large,
+ data.size
+ );
- setTimeout(() => core.doAction(), 3100);
- } else {
- core.doAction();
- }
- });
+ setTimeout(() => core.doAction(), 3100);
+ } else {
+ core.doAction();
+ }
+ });
- core.registerEvent("over", function (data) {
- let image = data.image ?? "";
- let time = data.time ?? 3000;
- let sound = data.sound ?? "";
- let textColor = data.textColor ?? "#FFFFFF";
- let boldColor = data.boldColor ?? "#000000";
- let font = data.font ?? "bold 48px Verdana";
- let text = data.text ?? "";
- let hidetime = data.hidetime ?? 100;
- if (!main.replayChecking && !core.isReplaying()) {
- core.over(
- image,
- data.memory,
- time,
- hidetime,
- sound,
- textColor,
- boldColor,
- font,
- text
- );
- } else {
- core.doAction();
- }
- });
- core.registerEvent("changebg", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.changebg(
- data.img1,
- data.memory1,
- data.img2,
- data.memory2,
- data.time,
- data.style
- );
- } else {
- core.doAction();
- }
- });
- core.registerEvent("overlist", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.overlist(
- data.image,
- data.memory,
- data.hidetime || 30,
- data.list || [{
- text: "",
- sound: "",
- time: 50,
- textColor: "#FFFFFF",
- boldColor: "#000000",
- font: "bold 48px Verdana",
- frame: 0,
- }, ]
- );
- } else {
- core.doAction();
- }
- });
- core.registerEvent("op", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.openvideo();
- } else {
- core.doAction();
- }
- });
- core.registerEvent("animationDrawable", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.animationDrawable(
- data.allFarme,
- data.color,
- data.globalAlpha,
- data.imageList,
- data.soundList
- );
- } else {
- core.doAction();
- }
- });
- core.registerEvent("setanimate", function (data) {
- data.px = data.px ?? 0;
- data.py = data.py ?? 0;
- core.setanimate(
- data.name,
- data.px,
- data.py,
- data.width,
- data.height,
- data.allFarme,
- data.imageList,
- data.soundList
- );
- core.doAction();
- });
- core.registerEvent("clearanimate", function (data) {
- core.plugin.playing.clear();
+ core.registerEvent("over", function (data) {
+ let image = data.image ?? "";
+ let time = data.time ?? 3000;
+ let sound = data.sound ?? "";
+ let textColor = data.textColor ?? "#FFFFFF";
+ let boldColor = data.boldColor ?? "#000000";
+ let font = data.font ?? "bold 48px Verdana";
+ let text = data.text ?? "";
+ let hidetime = data.hidetime ?? 100;
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.over(
+ image,
+ data.memory,
+ time,
+ hidetime,
+ sound,
+ textColor,
+ boldColor,
+ font,
+ text
+ );
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("changebg", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.changebg(
+ data.img1,
+ data.memory1,
+ data.img2,
+ data.memory2,
+ data.time,
+ data.style
+ );
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("overlist", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.overlist(
+ data.image,
+ data.memory,
+ data.hidetime || 30,
+ data.list || [
+ {
+ text: "",
+ sound: "",
+ time: 50,
+ textColor: "#FFFFFF",
+ boldColor: "#000000",
+ font: "bold 48px Verdana",
+ frame: 0,
+ },
+ ]
+ );
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("op", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.openvideo();
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("animationDrawable", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.animationDrawable(
+ data.allFarme,
+ data.color,
+ data.globalAlpha,
+ data.imageList,
+ data.soundList
+ );
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("setanimate", function (data) {
+ data.px = data.px ?? 0;
+ data.py = data.py ?? 0;
+ core.setanimate(
+ data.name,
+ data.px,
+ data.py,
+ data.width,
+ data.height,
+ data.allFarme,
+ data.imageList,
+ data.soundList
+ );
+ core.doAction();
+ });
+ core.registerEvent("clearanimate", function (data) {
+ core.plugin.playing.clear();
- core.doAction();
- });
- core.registerEvent("deleteanimate", function (data) {
- core.deleteanimate(data.name);
- core.doAction();
- });
- core.registerEvent("playanimate", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- data.x = data.x ?? 0;
- data.y = data.y ?? 0;
- data.scalex = data.scalex ?? 1;
- data.scaley = data.scaley ?? 1;
- core.playanimate(
- data.name,
- data.x,
- data.y,
- data.hero,
- data.scalex,
- data.scaley
- );
- core.doAction();
- } else {
- core.doAction();
- }
- });
- core.registerEvent("cgtextList", function (data) {
- core.ui.cgText.textList = core.plugin[data.textList];
- core.doAction();
- });
- core.registerEvent("cgtext", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.ui.cgText.image = data.bg;
- core.ui.cgText.memory = data.memory;
- core.ui.cgText.head = core.clone(data.head);
- core.ui.cgText.index = data.index;
- core.ui.cgText.name = core.ui.cgText.textList[data.index][0];
- core.ui.cgText.text = data.text ?
- data.text :
- core.ui.cgText.textList[data.index][1];
- 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.bodyList = core.clone(data.bodyList);
- main.dom.cgText.style.display = "block";
- core.ui.cgText.update();
- } else {
- core.doAction();
- }
- });
- core.registerEvent("introAndLoop", function (data) {
- if (!main.replayChecking && !core.isReplaying()) {
- core.plugin.introAndLoop(data.intro, data.time, data.loop);
- core.doAction();
- } else {
- core.doAction();
- }
- });
- core.registerEvent("setq", function (data) {
- core.setFlag("任务地点", data.id);
+ core.doAction();
+ });
+ core.registerEvent("deleteanimate", function (data) {
+ core.deleteanimate(data.name);
+ core.doAction();
+ });
+ core.registerEvent("playanimate", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ data.x = data.x ?? 0;
+ data.y = data.y ?? 0;
+ data.scalex = data.scalex ?? 1;
+ data.scaley = data.scaley ?? 1;
+ core.playanimate(
+ data.name,
+ data.x,
+ data.y,
+ data.hero,
+ data.scalex,
+ data.scaley
+ );
+ core.doAction();
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("cgtextList", function (data) {
+ core.ui.cgText.textList = core.plugin[data.textList];
+ core.doAction();
+ });
+ core.registerEvent("cgtext", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.ui.cgText.image = data.bg;
+ core.ui.cgText.memory = data.memory;
+ core.ui.cgText.head = core.clone(data.head);
+ core.ui.cgText.index = data.index;
+ core.ui.cgText.name = core.ui.cgText.textList[data.index][0];
+ core.ui.cgText.text = data.text
+ ? data.text
+ : core.ui.cgText.textList[data.index][1];
+ 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.bodyList = core.clone(data.bodyList);
+ main.dom.cgText.style.display = "block";
+ core.ui.cgText.update();
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("introAndLoop", function (data) {
+ if (!main.replayChecking && !core.isReplaying()) {
+ core.plugin.introAndLoop(data.intro, data.time, data.loop);
+ core.doAction();
+ } else {
+ core.doAction();
+ }
+ });
+ core.registerEvent("setq", function (data) {
+ core.setFlag("任务地点", data.id);
- core.doAction();
- });
+ core.doAction();
+ });
- core.registerEvent("setmusics", function (data) {
- if (
- (core.getLocalStorage("musics") &&
- core.getLocalStorage("musics").length === 0) ||
- !core.getLocalStorage("musics")
- )
- core.setLocalStorage("musics", ["theme.mp3"]);
- let a = core.getLocalStorage("musics");
- if (!data.bgm) {
- core.setLocalStorage("musics", ["theme.mp3"]);
- } else {
- if (!a.includes(data.bgm)) a.push(data.bgm);
- core.setLocalStorage("musics", a);
- }
- core.doAction();
- });
- core.registerEvent("setcgs", function (data) {
- if (!data.img) {
- core.setLocalStorage("cgs", []);
- } else {
- let a = core.getLocalStorage("cgs") ?? [];
- if (!a.includes(data.img)) a.push(data.img);
- core.setLocalStorage("cgs", a);
- }
- core.doAction();
- });
- };
-},
+ core.registerEvent("setmusics", function (data) {
+ if (
+ (core.getLocalStorage("musics") &&
+ core.getLocalStorage("musics").length === 0) ||
+ !core.getLocalStorage("musics")
+ )
+ core.setLocalStorage("musics", ["theme.mp3"]);
+ let a = core.getLocalStorage("musics");
+ if (!data.bgm) {
+ core.setLocalStorage("musics", ["theme.mp3"]);
+ } else {
+ if (!a.includes(data.bgm)) a.push(data.bgm);
+ core.setLocalStorage("musics", a);
+ }
+ core.doAction();
+ });
+ core.registerEvent("setcgs", function (data) {
+ if (!data.img) {
+ core.setLocalStorage("cgs", []);
+ } else {
+ let a = core.getLocalStorage("cgs") ?? [];
+ if (!a.includes(data.img)) a.push(data.img);
+ core.setLocalStorage("cgs", a);
+ }
+ core.doAction();
+ });
+ };
+ },
"drawLight": function () {
// 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...)
// 【参数说明】
@@ -2845,10 +2847,12 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
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,
@@ -3485,13 +3489,15 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
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"],
- ];
+ 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",
@@ -3731,588 +3737,591 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
// 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 +8733,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
};
},
"音频系统": function () {
- // 在此增加新插件
- /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行:
+ // 在此增加新插件
+ /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行:
@@ -8733,2327 +8742,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 () {
@@ -12724,424 +12487,434 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
};
},
"小地图": function () {
- // 在此增加新插件
- // ----- 不可自定义 杂七杂八的变量
- /** @type {{[x: string]: BFSResult}} */
- let mapCache = {}; // 地图缓存
- let drawCache = {}; // 绘制信息缓存
- let status = "none"; // 当前的绘制状态
- /** @type {{[x: string]: Sprite}} */
- let sprites = {}; // 当前所有的sprite
- /** @type {{[x: string]: Sprite}} */
- let canDrag = {}; // 可以拖拽的sprite
- /** @type {{[x: string]: Button}} */
- let areaSprite = {}; // 区域列表对应的sprite
- let clicking = false; // 是否正在点击,用于拖拽判定
- let drawingMap = ""; // 正在绘制的中心楼层
- let nowScale = 0; // 当前绘制的放缩比例
- let lastTouch = {}; // 上一次的单点点击信息
- let lastLength = 0; // 手机端缩放时上一次的两指间距离
- let nowDepth = 0; // 当前的遍历深度
- let drawedThumbnail = {}; // 已经绘制过的缩略图
- let moved = false; // 鼠标按下后是否移动了
- let noBorder = false; // 是否是无边框拼接模式
- let lastScale = 0; // 上一次缩放,用于优化缩略图绘制
- let areaPage = 0; // 区域显示的当前页数
- let nowArea = 0; // 当前区域index
- let selecting = ""; // 选择时当前正在选择的地图
+ // 在此增加新插件
+ // ----- 不可自定义 杂七杂八的变量
+ /** @type {{[x: string]: BFSResult}} */
+ let mapCache = {}; // 地图缓存
+ let drawCache = {}; // 绘制信息缓存
+ let status = "none"; // 当前的绘制状态
+ /** @type {{[x: string]: Sprite}} */
+ let sprites = {}; // 当前所有的sprite
+ /** @type {{[x: string]: Sprite}} */
+ let canDrag = {}; // 可以拖拽的sprite
+ /** @type {{[x: string]: Button}} */
+ let areaSprite = {}; // 区域列表对应的sprite
+ let clicking = false; // 是否正在点击,用于拖拽判定
+ let drawingMap = ""; // 正在绘制的中心楼层
+ let nowScale = 0; // 当前绘制的放缩比例
+ let lastTouch = {}; // 上一次的单点点击信息
+ let lastLength = 0; // 手机端缩放时上一次的两指间距离
+ let nowDepth = 0; // 当前的遍历深度
+ let drawedThumbnail = {}; // 已经绘制过的缩略图
+ let moved = false; // 鼠标按下后是否移动了
+ let noBorder = false; // 是否是无边框拼接模式
+ let lastScale = 0; // 上一次缩放,用于优化缩略图绘制
+ let areaPage = 0; // 区域显示的当前页数
+ let nowArea = 0; // 当前区域index
+ let selecting = ""; // 选择时当前正在选择的地图
- // ---- 不可自定义,常量
- /** @type {Area} */
- let areas = []; // 区域信息
- const perPage = Math.floor((core._PY_ - 60) / 30); // 区域的每页显示数量
+ // ---- 不可自定义,常量
+ /** @type {Area} */
+ let areas = []; // 区域信息
+ const perPage = Math.floor((core._PY_ - 60) / 30); // 区域的每页显示数量
- // ---- 可自定义,默认的切换地图的图块id
- const defaultChange = {
- left: "leftPortal", // 左箭头
- up: "upPortal", // 上箭头
- right: "rightPortal", // 右箭头
- down: "downPortal", // 下箭头
- upFloor: "upFloor", // 上楼
- downFloor: "downFloor", // 下楼
- };
- // ---- 可自定义,默认数值
- const defaultValue = {
- font: "Verdana", // 默认字体
- scale: 60, // 默认地图缩放比例
- depth: Infinity, // 默认的遍历深度
- };
+ // ---- 可自定义,默认的切换地图的图块id
+ const defaultChange = {
+ left: "leftPortal", // 左箭头
+ up: "upPortal", // 上箭头
+ right: "rightPortal", // 右箭头
+ down: "downPortal", // 下箭头
+ upFloor: "upFloor", // 上楼
+ downFloor: "downFloor", // 下楼
+ };
+ // ---- 可自定义,默认数值
+ const defaultValue = {
+ font: "Verdana", // 默认字体
+ scale: 60, // 默认地图缩放比例
+ depth: Infinity, // 默认的遍历深度
+ };
- // ---- 不可自定义,计算数据
- const dirData = {
- up: [1, 0],
- down: [-1, 0],
- left: [0, 1],
- right: [0, -1],
- upFloor: [0, 0],
- downFloor: [0, 0],
- };
- let ignoreEnemies = (this.ignoreEnemies = []);
+ // ---- 不可自定义,计算数据
+ const dirData = {
+ up: [1, 0],
+ down: [-1, 0],
+ left: [0, 1],
+ right: [0, -1],
+ upFloor: [0, 0],
+ downFloor: [0, 0],
+ };
+ let ignoreEnemies = (this.ignoreEnemies = []);
- let allChangeEntries = Object.entries(defaultChange);
+ let allChangeEntries = Object.entries(defaultChange);
- this.setq = function (floorId) {
- core.setFlag("任务地点", floorId);
- };
+ this.setq = function (floorId) {
+ core.setFlag("任务地点", floorId);
+ };
- const reset = core.events.resetGame;
- this.bfs = function () {
- areas = [];
- // 获取所有分区,使用异步函数,保证不会卡顿
- // 原理是用bfs扫,将所有连在一起的地图合并成一个区域
- (async function () {
- let all = core.floorIds.slice();
- const scanned = {
- [all[0]]: true,
- };
- while (all.length > 0) {
- let now = all.shift();
- if (core.status.maps[now].deleted) continue;
- if (!now) return;
- await new Promise((res) => {
- const result = core.plugin.bfsSearch(now, Infinity, true);
- mapCache[`${now}_Infinity_false`] = result;
- areas.push({ name: core.floors[now].areas, maps: result.order });
- for (const map of result.order) {
- scanned[map] = true;
- all = all.filter((v) => !result.order.includes(v));
- }
- res("success");
- }).then(() => {
- core.setFlag("areas", areas);
- });
- }
- })();
- };
- core.events.resetGame = function () {
- reset.apply(core.events, arguments);
- core.plugin.bfs();
- };
- /**
- * 广度优先搜索搜索地图路径
- * @param {string} center 中心地图的id
- * @param {number} depth 搜索深度
- * @param {boolean} noCache 是否不使用缓存
- * @returns {BFSResult} 格式:floorId_x_y_dir: floorId_x_y
- */
- this.bfsSearch = function bfsSearch(center, depth, noCache) {
- // 检查缓存
- const id = `${center}_${depth}_${noBorder}`;
- if (mapCache[id] && !noCache) return mapCache[id];
- const used = {
- [center]: true,
- }; // 搜索过的楼层
- let queue = [];
- let stack = [center]; // 当前栈
- let nowDepth = -1;
- const mapOrder = [center]; // 遍历顺序,顺便还能记录遍历了哪些楼层
+ const reset = core.events.resetGame;
+ this.bfs = function () {
+ areas = [];
+ // 获取所有分区,使用异步函数,保证不会卡顿
+ // 原理是用bfs扫,将所有连在一起的地图合并成一个区域
+ (async function () {
+ let all = core.floorIds.slice();
+ const scanned = {
+ [all[0]]: true,
+ };
+ while (all.length > 0) {
+ let now = all.shift();
+ if (core.status.maps[now].deleted) continue;
+ if (!now) return;
+ await new Promise((res) => {
+ const result = core.plugin.bfsSearch(now, Infinity, true);
+ mapCache[`${now}_Infinity_false`] = result;
+ areas.push({ name: core.floors[now].areas, maps: result.order });
+ for (const map of result.order) {
+ scanned[map] = true;
+ all = all.filter((v) => !result.order.includes(v));
+ }
+ res("success");
+ }).then(() => {
+ core.setFlag("areas", areas);
+ });
+ }
+ })();
+ };
+ core.events.resetGame = function () {
+ reset.apply(core.events, arguments);
+ core.plugin.bfs();
+ };
+ /**
+ * 广度优先搜索搜索地图路径
+ * @param {string} center 中心地图的id
+ * @param {number} depth 搜索深度
+ * @param {boolean} noCache 是否不使用缓存
+ * @returns {BFSResult} 格式:floorId_x_y_dir: floorId_x_y
+ */
+ this.bfsSearch = function bfsSearch(center, depth, noCache) {
+ // 检查缓存
+ const id = `${center}_${depth}_${noBorder}`;
+ if (mapCache[id] && !noCache) return mapCache[id];
+ const used = {
+ [center]: true,
+ }; // 搜索过的楼层
+ let queue = [];
+ let stack = [center]; // 当前栈
+ let nowDepth = -1;
+ const mapOrder = [center]; // 遍历顺序,顺便还能记录遍历了哪些楼层
- const res = {}; // 输出结果,格式:floorId_x_y_dir: floorId_x_y
- const enemies = {};
- const upOrDown = {};
- const mapdir = {};
- // 开始循环搜索
- while (nowDepth < depth && stack.length > 0) {
- const now = stack.shift(); // 当前id
- if (core.status.maps[now].deleted) continue;
- mapdir[now] = mapdir[now] ?? [];
- const blocks = core.getMapBlocksObj(now); // 获取当前地图的每点的事件
- enemies[now] = {};
- // 遍历,获取可以传送的点,只检测绿点事件,因此可用红点事件进行传送来实现分区功能
- for (const i in blocks) {
- const block = blocks[i];
- // 整合漏怪检测,所以要检测怪物
- if (block.event.trigger === "battle") {
- const id = block.event.id;
- if (ignoreEnemies.includes(id)) continue;
- else enemies[now][i] = block.event.id;
- continue;
- }
- // 检测触发器是否为切换楼层,不是则直接跳过
- if (block.event.trigger !== "changeFloor") continue;
- const dirEntries = allChangeEntries.find(
- (v) => v[1] === block.event.id
- );
- // 如果不是那六种传送门,直接忽略
- if (!dirEntries) continue;
- const data = block.event.data;
- const dir = dirEntries[0];
- const route = now + "_" + i.replace(",", "_") + "_" + dir;
- const target = data.floorId + "_" + data.loc.join("_");
+ const res = {}; // 输出结果,格式:floorId_x_y_dir: floorId_x_y
+ const enemies = {};
+ const upOrDown = {};
+ const mapdir = {};
+ // 开始循环搜索
+ while (nowDepth < depth && stack.length > 0) {
+ const now = stack.shift(); // 当前id
+ if (core.status.maps[now].deleted) continue;
+ mapdir[now] = mapdir[now] ?? [];
+ const blocks = core.getMapBlocksObj(now); // 获取当前地图的每点的事件
+ enemies[now] = {};
+ // 遍历,获取可以传送的点,只检测绿点事件,因此可用红点事件进行传送来实现分区功能
+ for (const i in blocks) {
+ const block = blocks[i];
+ // 整合漏怪检测,所以要检测怪物
+ if (block.event.trigger === "battle") {
+ const id = block.event.id;
+ if (ignoreEnemies.includes(id)) continue;
+ else enemies[now][i] = block.event.id;
+ continue;
+ }
+ // 检测触发器是否为切换楼层,不是则直接跳过
+ if (block.event.trigger !== "changeFloor") continue;
+ const dirEntries = allChangeEntries.find(
+ (v) => v[1] === block.event.id
+ );
+ // 如果不是那六种传送门,直接忽略
+ if (!dirEntries) continue;
+ const data = block.event.data;
+ const dir = dirEntries[0];
+ const route = now + "_" + i.replace(",", "_") + "_" + dir;
+ const target = data.floorId + "_" + data.loc.join("_");
- mapdir[now].push(dir);
- if (!used[data.floorId]) {
- if (dir === "upFloor" || dir === "downFloor") {
- upOrDown[now] = upOrDown[id] ?? [];
- upOrDown[now].push(dir);
- }
+ mapdir[now].push(dir);
+ if (!used[data.floorId]) {
+ if (dir === "upFloor" || dir === "downFloor") {
+ upOrDown[now] = upOrDown[id] ?? [];
+ upOrDown[now].push(dir);
+ }
- queue.push(data.floorId); // 没有搜索过,则加入栈中
- mapOrder.push(data.floorId);
- used[data.floorId] = true;
- }
- res[route] = target;
- }
- if (stack.length === 0) {
- stack = queue;
- queue = [];
- nowDepth++;
- }
- if (stack.length === 0 && queue.length === 0) break;
- }
- return { res, order: mapOrder, enemies, upOrDown, mapdir };
- };
- /**
- * 获取绘制信息
- * @param {string?} center 中心地图id
- * @param {number?} depth 搜索深度
- * @param {boolean?} noCache 是否不使用缓存
- * @returns {MapDrawInfo}
- */
- this.getMapDrawInfo = function (
- center = core.status.floorId,
- depth = defaultValue.depth,
- noCache = false
- ) {
- nowDepth = depth;
- drawingMap = center;
- const id = `${center}_${depth}_${noBorder}`;
- // 检查缓存
- if (drawCache[id] && !noCache) return drawCache[id];
- const map = core.plugin.bfsSearch(center, depth, noCache);
- mapCache[id] = map;
- const res = getDrawInfo(map.res, center, map.order);
- res.upOrDown = map.upOrDown;
- res.mapdir = map.mapdir;
- drawCache[id] = res;
- return res;
- };
- /**
- * 提供地图的绘制信息
- * @param {{[x: string]: string}} map 要绘制的地图,格式:floorId_x_y_dir: floorId_x_y
- * @param {string} center 中心地图的id
- * @param {string[]} order 遍历顺序
- * @returns {MapDrawInfo} 地图的绘制信息
- */
+ queue.push(data.floorId); // 没有搜索过,则加入栈中
+ mapOrder.push(data.floorId);
+ used[data.floorId] = true;
+ }
+ res[route] = target;
+ }
+ if (stack.length === 0) {
+ stack = queue;
+ queue = [];
+ nowDepth++;
+ }
+ if (stack.length === 0 && queue.length === 0) break;
+ }
+ return { res, order: mapOrder, enemies, upOrDown, mapdir };
+ };
+ /**
+ * 获取绘制信息
+ * @param {string?} center 中心地图id
+ * @param {number?} depth 搜索深度
+ * @param {boolean?} noCache 是否不使用缓存
+ * @returns {MapDrawInfo}
+ */
+ this.getMapDrawInfo = function (
+ center = core.status.floorId,
+ depth = defaultValue.depth,
+ noCache = false
+ ) {
+ nowDepth = depth;
+ drawingMap = center;
+ const id = `${center}_${depth}_${noBorder}`;
+ // 检查缓存
+ if (drawCache[id] && !noCache) return drawCache[id];
+ const map = core.plugin.bfsSearch(center, depth, noCache);
+ mapCache[id] = map;
+ const res = getDrawInfo(map.res, center, map.order);
+ res.upOrDown = map.upOrDown;
+ res.mapdir = map.mapdir;
+ drawCache[id] = res;
+ return res;
+ };
+ /**
+ * 提供地图的绘制信息
+ * @param {{[x: string]: string}} map 要绘制的地图,格式:floorId_x_y_dir: floorId_x_y
+ * @param {string} center 中心地图的id
+ * @param {string[]} order 遍历顺序
+ * @returns {MapDrawInfo} 地图的绘制信息
+ */
- function getDrawInfo(map, center, order) {
- // 先根据地图id分类,从而确定每个地图连接哪些地图,同时方便处理
- const links = {};
- for (const i in map) {
- const splitted = i.split("_");
- const id = splitted[0];
- if (!links[id]) links[id] = {};
- links[id][i] = map[i];
- }
+ function getDrawInfo(map, center, order) {
+ // 先根据地图id分类,从而确定每个地图连接哪些地图,同时方便处理
+ const links = {};
+ for (const i in map) {
+ const splitted = i.split("_");
+ const id = splitted[0];
+ if (!links[id]) links[id] = {};
+ links[id][i] = map[i];
+ }
- // 分类完毕,然后根据连接点先计算出各个地图的坐标,然后再进行判断
- const centerFloor = core.status.maps[center];
- const visitedCenter = core.hasVisitedFloor(center);
- const locs = {
- // 格式:[中心x, 中心y, 宽, 高, 是否到达过]
- [center]: [2, 2, 1, 1, visitedCenter],
- };
- // 可以上楼下楼的地图
- const upOrDown = {};
- for (const id of order) {
- const now = links[id];
- // 遍历每一个地图的连接情况
- for (const from in now) {
- const to = now[from];
- // 先根据from to计算物理位置
- const fromData = from.split("_"),
- toData = to.split("_");
- const dir = fromData[3];
- if (dir === "upFloor" || dir === "downFloor") continue;
- if (!defaultChange[dir]) continue;
- const v = dirData[dir][1], // 竖直数值
- h = dirData[dir][0], // 水平数值
- ha = Math.abs(h),
- va = Math.abs(v);
- const ff = id, // fromFloorId
- tf = toData[0]; // toFloorId
- const fromFloor = core.status.maps[ff],
- toFloor = core.status.maps[tf];
- const fhw = Math.floor(fromFloor.width / 2), // fromFloorHalfWidth
- fhh = Math.floor(fromFloor.height / 2),
- thw = Math.floor(toFloor.width / 2),
- thh = Math.floor(toFloor.height / 2);
- const fLoc = locs[id] ?? [0, 0];
- if (!locs[ff]) continue;
- let x, y;
- if (locs && locs[tf]) {
- x = locs[tf][0];
- y = locs[tf][1];
- } else {
- // 计算坐标,公式可以通过画图推断出
- x = fLoc[0] - v;
- y = fLoc[1] - h;
- }
- locs[tf] = locs[tf] ?? [x, y, 1, 1, core.hasVisitedFloor(tf)];
- }
- }
- // 获取地图绘制需要的长宽
- let width = 0,
- height = 0;
- let left, right, up, down;
- for (const id in locs) {
- const [x, y, w, h] = locs[id];
- if (left === void 0) {
- left = right = x;
- up = down = y;
- }
- left = Math.min(x - 1, left);
- right = Math.max(x + 1, right);
- up = Math.min(y - 1, up);
- down = Math.max(y + 1, down);
- }
- width = right - left;
- height = down - up;
+ // 分类完毕,然后根据连接点先计算出各个地图的坐标,然后再进行判断
+ const centerFloor = core.status.maps[center];
+ const visitedCenter = core.hasVisitedFloor(center);
+ const locs = {
+ // 格式:[中心x, 中心y, 宽, 高, 是否到达过]
+ [center]: [2, 2, 1, 1, visitedCenter],
+ };
+ // 可以上楼下楼的地图
+ const upOrDown = {};
+ for (const id of order) {
+ const now = links[id];
+ // 遍历每一个地图的连接情况
+ for (const from in now) {
+ const to = now[from];
+ // 先根据from to计算物理位置
+ const fromData = from.split("_"),
+ toData = to.split("_");
+ const dir = fromData[3];
+ if (dir === "upFloor" || dir === "downFloor") continue;
+ if (!defaultChange[dir]) continue;
+ const v = dirData[dir][1], // 竖直数值
+ h = dirData[dir][0], // 水平数值
+ ha = Math.abs(h),
+ va = Math.abs(v);
+ const ff = id, // fromFloorId
+ tf = toData[0]; // toFloorId
+ const fromFloor = core.status.maps[ff],
+ toFloor = core.status.maps[tf];
+ const fhw = Math.floor(fromFloor.width / 2), // fromFloorHalfWidth
+ fhh = Math.floor(fromFloor.height / 2),
+ thw = Math.floor(toFloor.width / 2),
+ thh = Math.floor(toFloor.height / 2);
+ const fLoc = locs[id] ?? [0, 0];
+ if (!locs[ff]) continue;
+ let x, y;
+ if (locs && locs[tf]) {
+ x = locs[tf][0];
+ y = locs[tf][1];
+ } else {
+ // 计算坐标,公式可以通过画图推断出
+ x = fLoc[0] - v;
+ y = fLoc[1] - h;
+ }
+ locs[tf] = locs[tf] ?? [x, y, 1, 1, core.hasVisitedFloor(tf)];
+ }
+ }
+ // 获取地图绘制需要的长宽
+ let width = 0,
+ height = 0;
+ let left, right, up, down;
+ for (const id in locs) {
+ const [x, y, w, h] = locs[id];
+ if (left === void 0) {
+ left = right = x;
+ up = down = y;
+ }
+ left = Math.min(x - 1, left);
+ right = Math.max(x + 1, right);
+ up = Math.min(y - 1, up);
+ down = Math.max(y + 1, down);
+ }
+ width = right - left;
+ height = down - up;
- return { locs, width, height, layer: upOrDown };
- }
+ return { locs, width, height, layer: upOrDown };
+ }
- function mapblock(mapdir) {
- let mb = "";
- if (mapdir.includes("up")) mb += "u";
- if (mapdir.includes("down")) mb += "d";
- if (mapdir.includes("left")) mb += "l";
- if (mapdir.includes("right")) mb += "r";
- return mb ? mb + ".webp" : "null.webp";
- }
- core.animateFrame.globalAlphaFloor = 0;
- core.animateFrame.globalAlphaFloorStatus = 1;
+ function mapblock(mapdir) {
+ let mb = "";
+ if (mapdir.includes("up")) mb += "u";
+ if (mapdir.includes("down")) mb += "d";
+ if (mapdir.includes("left")) mb += "l";
+ if (mapdir.includes("right")) mb += "r";
+ return mb ? mb + ".webp" : "null.webp";
+ }
+ core.animateFrame.globalAlphaFloor = 0;
+ core.animateFrame.globalAlphaFloorStatus = 1;
- const tesk = document.createElement("canvas");
- tesk.width = 300;
- tesk.height = 300;
- const teskctx = tesk.getContext("2d");
+ const tesk = document.createElement("canvas");
+ tesk.width = 300;
+ tesk.height = 300;
+ const teskctx = tesk.getContext("2d");
- let line = 50;
- teskctx.strokeStyle = "green";
- teskctx.fillStyle = "green";
- let now = 0;
- core.registerAnimationFrame("tesk", true, function (timestamp) {
- if (timestamp - now > 1000 / 60) {
- now = timestamp;
- core.clearMap(teskctx);
- teskctx.lineWidth = 150 - line;
+ let line = 50;
+ teskctx.strokeStyle = "green";
+ teskctx.fillStyle = "green";
+ let now = 0;
+ core.registerAnimationFrame("tesk", true, function (timestamp) {
+ if (timestamp - now > 1000 / 60) {
+ now = timestamp;
+ core.clearMap(teskctx);
+ teskctx.lineWidth = 150 - line;
- if (line <= 150) {
- teskctx.beginPath();
- teskctx.arc(150, 150, line, 0, Math.PI * 2);
- line += 2;
- teskctx.stroke();
- } else {
- teskctx.beginPath();
- teskctx.arc(150, 150, line - 150, 0, Math.PI * 2);
- line += 2;
- teskctx.fill();
- if (line >= 250) line = 50;
- }
- }
- });
+ if (line <= 150) {
+ teskctx.beginPath();
+ teskctx.arc(150, 150, line, 0, Math.PI * 2);
+ line += 2;
+ teskctx.stroke();
+ } else {
+ teskctx.beginPath();
+ teskctx.arc(150, 150, line - 150, 0, Math.PI * 2);
+ line += 2;
+ teskctx.fill();
+ if (line >= 250) line = 50;
+ }
+ }
+ });
- /**
- * 绘制小地图
- * @param {MapDrawInfo} info 地图绘制信息
- * @param {number} scale 地图的绘制比例
- */
- this.drawSmallMap = function (
- ctx,
- info,
- center,
- sx,
- sy,
- sw,
- sh,
- scale = defaultValue.scale
- ) {
- core.clearMap(ctx, sx, sy, sw + 40, sh + 60);
- if (core.domStyle.isVertical) {
- sy += 50;
- sx += 15;
- } else {
- sy += 60;
- sx += 30;
- }
- core.fillRect(ctx, sx, sy, sw, sh, "#000");
- core.strokeRect(ctx, sx, sy, sw, sh, "#fff", 5);
- core.setTextAlign("outerUI", "center");
- core.fillBoldText1(
- ctx,
- core.status.maps[center].areas,
- sx + sw / 2,
- sy - 10,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 42px Verdana"
- );
- const locs = info.locs;
- for (const id in locs) {
- const loc = locs[id];
- let color = "#000";
- if (!loc[4]) color = "#f0f";
- const [x, y, w, h] = loc.map((v) => typeof v === "number" && v * scale);
- const fx = x + sx,
- fy = y + sy;
- const mapdir = info.mapdir[id];
- const img = mapblock(mapdir);
- if (x < 0 || x > 4 * scale || y < 0 || y > 4 * scale) continue;
- 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,
- defaultChange.upFloor,
- fx + min / 4,
- fy + min / 4,
- min / 2,
- min / 2
- );
- if (layer?.includes("downFloor"))
- core.drawIcon(
- ctx,
- defaultChange.downFloor,
- fx + min / 4,
- fy + min / 4,
- min / 2,
- min / 2
- );
+ /**
+ * 绘制小地图
+ * @param {MapDrawInfo} info 地图绘制信息
+ * @param {number} scale 地图的绘制比例
+ */
+ this.drawSmallMap = function (
+ ctx,
+ info,
+ center,
+ sx,
+ sy,
+ sw,
+ sh,
+ scale = defaultValue.scale
+ ) {
+ core.clearMap(ctx, sx, sy, sw + 40, sh + 60);
+ if (core.domStyle.isVertical) {
+ sy += 50;
+ sx += 15;
+ } else {
+ sy += 60;
+ sx += 30;
+ }
+ core.fillRect(ctx, sx, sy, sw, sh, "#000");
+ core.strokeRect(ctx, sx, sy, sw, sh, "#fff", 5);
+ core.setTextAlign("outerUI", "center");
+ core.fillBoldText1(
+ ctx,
+ core.status.maps[center].areas,
+ sx + sw / 2,
+ sy - 10,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 42px Verdana"
+ );
+ const locs = info.locs;
+ for (const id in locs) {
+ const loc = locs[id];
+ let color = "#000";
+ if (!loc[4]) color = "#f0f";
+ const [x, y, w, h] = loc.map((v) => typeof v === "number" && v * scale);
+ const fx = x + sx,
+ fy = y + sy;
+ const mapdir = info.mapdir[id];
+ const img = mapblock(mapdir);
+ if (x < 0 || x > 4 * scale || y < 0 || y > 4 * scale) continue;
+ 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("showEnemy")) {
- ctx.textAlign = "center";
- ctx.textBaseline = "middle";
- const c = drawingMap + "_" + nowDepth + "_" + noBorder;
- const n = Object.keys(mapCache[c].enemies[id]).length;
- color = "#fff";
- if (n > 10) color = "#fc3";
- if (n > 20) color = "#f22";
- ctx.shadowBlur = 0.6 * nowScale;
- ctx.shadowColor = "#000";
- if (n > 0)
- core.fillText(
- ctx,
- n,
- fx + (w * 3) / 10,
- fy + (h * 7) / 10,
- color,
- 22 + "px normal"
- );
- ctx.shadowBlur = 0;
- }
- }
- };
- },
+
+ if (layer?.includes("upFloor"))
+ core.drawIcon(
+ ctx,
+ defaultChange.upFloor,
+ fx + min / 4,
+ fy + min / 4,
+ min / 2,
+ min / 2
+ );
+ if (layer?.includes("downFloor"))
+ core.drawIcon(
+ ctx,
+ defaultChange.downFloor,
+ fx + min / 4,
+ fy + min / 4,
+ 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";
+ ctx.textBaseline = "middle";
+ const c = drawingMap + "_" + nowDepth + "_" + noBorder;
+ const n = Object.keys(mapCache[c].enemies[id]).length;
+ color = "#fff";
+ if (n > 10) color = "#fc3";
+ if (n > 20) color = "#f22";
+ ctx.shadowBlur = 0.6 * nowScale;
+ ctx.shadowColor = "#000";
+ if (n > 0)
+ core.fillText(
+ ctx,
+ n,
+ fx + (w * 3) / 10,
+ fy + (h * 7) / 10,
+ color,
+ 22 + "px normal"
+ );
+ 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 () {
// 在此增加新插件
@@ -13528,20 +13301,30 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
if (
!core.status.maps[core.floorIds[page]].canFlyTo ||
!core.hasVisitedFloor(core.floorIds[page])
- )
+ ) {
+ /*core.fillRect("ui", 58,
+ 58,
+ size - 50,
+ size - 50, "rgba(0,0,0,0.5)")*/
+ core.getContextByName("ui").globalAlpha = 0.7
+
core.drawImage(
"ui",
- "lock.webp",
+ "miwu.webp",
0,
0,
size,
size,
58,
58,
- size - 8,
- size - 8
+ 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,
@@ -13767,7 +13550,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
title,
15 - 4 + size - 4 + 45,
85,
- "#FFFFFF",
+ core.hasVisitedFloor(floorId) ? "#FFFFFF" : "#444444",
"#000000",
18
);
@@ -14258,511 +14041,508 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
);
},
"CG回廊": function () {
- // 在此增加新插件
- const CGUI = document.createElement("canvas"); //CGui画布设置
- CGUI.style.position = "absolute";
- CGUI.style.zIndex = 300;
- CGUI.style.display = "none";
- CGUI.id = "CGUI";
- main.dom.gameGroup.insertAdjacentElement("afterend", CGUI);
- CGUI.style.top = "50%";
- CGUI.style.left = "50%";
- CGUI.style.transform = "translate(-50%,-50%)";
- const ctx = CGUI.getContext("2d");
- main.dom.CGUI = CGUI;
- let page = 0; //初始页面
- let show = false; //展示状态
- CGUI.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.CG.onclick(px * 3, py * 3);
- } catch (ee) {
- main.log(ee);
- }
- };
+ // 在此增加新插件
+ const CGUI = document.createElement("canvas"); //CGui画布设置
+ CGUI.style.position = "absolute";
+ CGUI.style.zIndex = 300;
+ CGUI.style.display = "none";
+ CGUI.id = "CGUI";
+ main.dom.gameGroup.insertAdjacentElement("afterend", CGUI);
+ CGUI.style.top = "50%";
+ CGUI.style.left = "50%";
+ CGUI.style.transform = "translate(-50%,-50%)";
+ const ctx = CGUI.getContext("2d");
+ main.dom.CGUI = CGUI;
+ let page = 0; //初始页面
+ let show = false; //展示状态
+ CGUI.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.CG.onclick(px * 3, py * 3);
+ } catch (ee) {
+ main.log(ee);
+ }
+ };
- class CG {
- constructor() {
- this.cgs;
- //cg列表
- this.UIMx = [
- //空位用‘none’填充,当前ui3*2
- [
- ["eve_010102.webp", "eve_010203.webp", "eve_010304.webp"],
- ["eve_010501.webp", "eve_010601.webp", "eve_010701.webp"],
- ],
- [
- ["eve_010801.webp", "eve_010902.webp", "eve_011001.webp"],
- ["eve_011101.webp", "eve_011202.webp", "eve_011302.webp"],
- ],
- [
- ["eve_011402.webp", "eve_020102.webp", "eve_020201.webp"],
- ["eve_020301.webp", "eve_020401.webp", "eve_020501.webp"],
- ],
- [
- ["eve_020605.webp", "eve_020701.webp", "eve_020801.webp"],
- ["eve_030101.webp", "eve_030206.webp", "eve_030302.webp"],
- ],
- [
- ["eve_030508.webp", "eve_030601.webp", "eve_030801.webp"],
- ["eve_030901.webp", "eve_031002.webp", "eve_031101.webp"],
- ],
- [
- ["eve_040201.webp", "eve_040401.webp", "eve_040501.webp"],
- ["eve_040601.webp", "eve_040702.webp", "eve_040801.webp"],
- ],
- [
- ["eve_050101.webp", "eve_050201.webp", "eve_050401.webp"],
- ["eve_050501.webp", "eve_050601.webp", "eve_050704.webp"],
- ],
- [
- ["eve_050801.webp", "eve_070101.webp", "bg_1511.webp"],
- ["bg_1521.webp", "bg_2011.webp", "bg_2521.webp"],
- ],
- [
- ["bg_3042.webp", "bg_3551.webp", "bg_3571.webp"],
- ["bg_3721.webp", "bg_5033.webp", "bg_5044.webp"],
- ],
- ];
- }
+ class CG {
+ constructor() {
+ this.cgs;
+ //cg列表
+ this.UIMx = [
+ //空位用‘none’填充,当前ui3*2
+ [
+ ["eve_010102.webp", "eve_010203.webp", "eve_010304.webp"],
+ ["eve_010501.webp", "eve_010601.webp", "eve_010701.webp"],
+ ],
+ [
+ ["eve_010801.webp", "eve_010902.webp", "eve_011001.webp"],
+ ["eve_011101.webp", "eve_011202.webp", "eve_011302.webp"],
+ ],
+ [
+ ["eve_011402.webp", "eve_020102.webp", "eve_020201.webp"],
+ ["eve_020301.webp", "eve_020401.webp", "eve_020501.webp"],
+ ],
+ [
+ ["eve_020605.webp", "eve_020701.webp", "eve_020801.webp"],
+ ["eve_030101.webp", "eve_030206.webp", "eve_030302.webp"],
+ ],
+ [
+ ["eve_030508.webp", "eve_030601.webp", "eve_030801.webp"],
+ ["eve_030901.webp", "eve_031002.webp", "eve_031101.webp"],
+ ],
+ [
+ ["eve_040201.webp", "eve_040401.webp", "eve_040501.webp"],
+ ["eve_040601.webp", "eve_040702.webp", "eve_040801.webp"],
+ ],
+ [
+ ["eve_050101.webp", "eve_050201.webp", "eve_050401.webp"],
+ ["eve_050501.webp", "eve_050601.webp", "eve_050704.webp"],
+ ],
+ [
+ ["eve_050801.webp", "eve_070101.webp", "bg_1511.webp"],
+ ["bg_1521.webp", "bg_2011.webp", "bg_2521.webp"],
+ ],
+ [
+ ["bg_3042.webp", "bg_3551.webp", "bg_3571.webp"],
+ ["bg_3721.webp", "bg_5033.webp", "bg_5044.webp"],
+ ],
+ ];
+ }
- //更新
- update() {
- this.background();
- this.drawUI();
- }
- background() {
- //画布大小设置
- if (core.domStyle.isVertical) {
- CGUI.width = 1248;
- CGUI.height = 2028;
- } else {
- CGUI.width = 2028;
- CGUI.height = 1248;
- }
- core.setTextAlign(ctx, "center");
- }
- onclick(px, py) {
- //点击
+ //更新
+ update() {
+ this.background();
+ this.drawUI();
+ }
+ background() {
+ //画布大小设置
+ if (core.domStyle.isVertical) {
+ CGUI.width = 1248;
+ CGUI.height = 2028;
+ } else {
+ CGUI.width = 2028;
+ CGUI.height = 1248;
+ }
+ core.setTextAlign(ctx, "center");
+ }
+ onclick(px, py) {
+ //点击
- if (show) {
- show = !show;
- core.clearMap(ctx);
- this.update();
- return;
- }
- const makeBox = ([x, y], [w, h]) => {
- return [
- [x, y],
- [x + w, y + h],
- ];
- };
- const inRect = ([x, y], [
- [sx, sy],
- [dx, dy]
- ]) => {
- return sx <= x && x <= dx && sy <= y && y <= dy;
- };
- const pos = [px, py];
- const backbox = makeBox([15, 35], [210, 90]);
- if (inRect(pos, backbox)) {
- //离开按钮是一致的,其余的记区分横竖屏
- CGUI.style.display = "none";
- core.clearMap(ctx);
- core.restart();
- return;
- }
- if (core.domStyle.isVertical) {
- //竖屏
- const pageupbox = makeBox([200, 1830], [200, 100]);
- const pagedownbox = makeBox([900, 1830], [200, 100]);
+ if (show) {
+ show = !show;
+ core.clearMap(ctx);
+ this.update();
+ return;
+ }
+ const makeBox = ([x, y], [w, h]) => {
+ return [
+ [x, y],
+ [x + w, y + h],
+ ];
+ };
+ const inRect = ([x, y], [[sx, sy], [dx, dy]]) => {
+ return sx <= x && x <= dx && sy <= y && y <= dy;
+ };
+ const pos = [px, py];
+ const backbox = makeBox([15, 35], [210, 90]);
+ if (inRect(pos, backbox)) {
+ //离开按钮是一致的,其余的记区分横竖屏
+ CGUI.style.display = "none";
+ core.clearMap(ctx);
+ core.restart();
+ return;
+ }
+ if (core.domStyle.isVertical) {
+ //竖屏
+ const pageupbox = makeBox([200, 1830], [200, 100]);
+ const pagedownbox = makeBox([900, 1830], [200, 100]);
- const imagebox0 = makeBox([50, 200], [560, 420]);
- const imagebox1 = makeBox([50, 750], [560, 420]);
- const imagebox2 = makeBox([50, 1300], [560, 420]);
+ const imagebox0 = makeBox([50, 200], [560, 420]);
+ const imagebox1 = makeBox([50, 750], [560, 420]);
+ const imagebox2 = makeBox([50, 1300], [560, 420]);
- const imagebox3 = makeBox([650, 200], [560, 420]);
- const imagebox4 = makeBox([650, 750], [560, 420]);
- const imagebox5 = makeBox([650, 1300], [560, 420]);
- if (inRect(pos, pagedownbox)) {
- //2代表当前最大页数-1
- if (page < this.UIMx.length - 1) {
- page++;
- core.clearMap(ctx);
- this.update();
- }
- } else if (inRect(pos, pageupbox)) {
- if (page > 0) {
- page--;
- core.clearMap(ctx);
- this.update();
- }
- } else if (inRect(pos, imagebox0)) {
- if (this.cgs.includes(this.UIMx[page][0][0])) {
- const img = core.material.images.images[this.UIMx[page][0][0]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox1)) {
- if (this.cgs.includes(this.UIMx[page][0][1])) {
- const img = core.material.images.images[this.UIMx[page][0][1]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox2)) {
- if (this.cgs.includes(this.UIMx[page][0][2])) {
- const img = core.material.images.images[this.UIMx[page][0][2]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox3)) {
- if (this.cgs.includes(this.UIMx[page][1][0])) {
- const img = core.material.images.images[this.UIMx[page][1][0]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox4)) {
- if (this.cgs.includes(this.UIMx[page][1][1])) {
- const img = core.material.images.images[this.UIMx[page][1][1]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox5)) {
- if (this.cgs.includes(this.UIMx[page][1][2])) {
- const img = core.material.images.images[this.UIMx[page][1][2]];
- if (img) {
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.restore(); //重置画布设置
- show = !show;
- }
- }
- }
- } else {
- const pageupbox = makeBox([200, 1110], [200, 100]);
- const pagedownbox = makeBox([1600, 1110], [200, 100]);
- const imagebox0 = makeBox([75, 150], [600, 450]);
- const imagebox1 = makeBox([725, 150], [600, 450]);
- const imagebox2 = makeBox([1300, 150], [600, 450]);
- const imagebox3 = makeBox([75, 650], [600, 450]);
- const imagebox4 = makeBox([725, 650], [600, 450]);
- const imagebox5 = makeBox([1375, 650], [600, 450]);
- if (inRect(pos, pagedownbox)) {
- if (page < this.UIMx.length - 1) {
- page++;
- core.clearMap(ctx);
- this.update();
- }
- } else if (inRect(pos, pageupbox)) {
- if (page > 0) {
- page--;
- core.clearMap(ctx);
- this.update();
- }
- } else if (inRect(pos, imagebox0)) {
- if (this.cgs.includes(this.UIMx[page][0][0])) {
- const img = core.material.images.images[this.UIMx[page][0][0]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox1)) {
- if (this.cgs.includes(this.UIMx[page][0][1])) {
- const img = core.material.images.images[this.UIMx[page][0][1]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox2)) {
- if (this.cgs.includes(this.UIMx[page][0][2])) {
- const img = core.material.images.images[this.UIMx[page][0][2]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox3)) {
- if (this.cgs.includes(this.UIMx[page][1][0])) {
- const img = core.material.images.images[this.UIMx[page][1][0]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox4)) {
- if (this.cgs.includes(this.UIMx[page][1][1])) {
- const img = core.material.images.images[this.UIMx[page][1][1]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- } else if (inRect(pos, imagebox5)) {
- if (this.cgs.includes(this.UIMx[page][1][2])) {
- const img = core.material.images.images[this.UIMx[page][1][2]];
- if (img) {
- ctx.drawImage(img, 0, 0, 2028, 1248);
- show = !show;
- }
- }
- }
- }
- }
- drawUI() {
- //绘制页面
- core.clearMap(CGUI);
- const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景
- const bg = core.material.images.images["bg_5043.webp"]; //横屏背景
+ const imagebox3 = makeBox([650, 200], [560, 420]);
+ const imagebox4 = makeBox([650, 750], [560, 420]);
+ const imagebox5 = makeBox([650, 1300], [560, 420]);
+ if (inRect(pos, pagedownbox)) {
+ //2代表当前最大页数-1
+ if (page < this.UIMx.length - 1) {
+ page++;
+ core.clearMap(ctx);
+ this.update();
+ }
+ } else if (inRect(pos, pageupbox)) {
+ if (page > 0) {
+ page--;
+ core.clearMap(ctx);
+ this.update();
+ }
+ } else if (inRect(pos, imagebox0)) {
+ if (this.cgs.includes(this.UIMx[page][0][0])) {
+ const img = core.material.images.images[this.UIMx[page][0][0]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox1)) {
+ if (this.cgs.includes(this.UIMx[page][0][1])) {
+ const img = core.material.images.images[this.UIMx[page][0][1]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox2)) {
+ if (this.cgs.includes(this.UIMx[page][0][2])) {
+ const img = core.material.images.images[this.UIMx[page][0][2]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox3)) {
+ if (this.cgs.includes(this.UIMx[page][1][0])) {
+ const img = core.material.images.images[this.UIMx[page][1][0]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox4)) {
+ if (this.cgs.includes(this.UIMx[page][1][1])) {
+ const img = core.material.images.images[this.UIMx[page][1][1]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox5)) {
+ if (this.cgs.includes(this.UIMx[page][1][2])) {
+ const img = core.material.images.images[this.UIMx[page][1][2]];
+ if (img) {
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.restore(); //重置画布设置
+ show = !show;
+ }
+ }
+ }
+ } else {
+ const pageupbox = makeBox([200, 1110], [200, 100]);
+ const pagedownbox = makeBox([1600, 1110], [200, 100]);
+ const imagebox0 = makeBox([75, 150], [600, 450]);
+ const imagebox1 = makeBox([725, 150], [600, 450]);
+ const imagebox2 = makeBox([1300, 150], [600, 450]);
+ const imagebox3 = makeBox([75, 650], [600, 450]);
+ const imagebox4 = makeBox([725, 650], [600, 450]);
+ const imagebox5 = makeBox([1375, 650], [600, 450]);
+ if (inRect(pos, pagedownbox)) {
+ if (page < this.UIMx.length - 1) {
+ page++;
+ core.clearMap(ctx);
+ this.update();
+ }
+ } else if (inRect(pos, pageupbox)) {
+ if (page > 0) {
+ page--;
+ core.clearMap(ctx);
+ this.update();
+ }
+ } else if (inRect(pos, imagebox0)) {
+ if (this.cgs.includes(this.UIMx[page][0][0])) {
+ const img = core.material.images.images[this.UIMx[page][0][0]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox1)) {
+ if (this.cgs.includes(this.UIMx[page][0][1])) {
+ const img = core.material.images.images[this.UIMx[page][0][1]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox2)) {
+ if (this.cgs.includes(this.UIMx[page][0][2])) {
+ const img = core.material.images.images[this.UIMx[page][0][2]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox3)) {
+ if (this.cgs.includes(this.UIMx[page][1][0])) {
+ const img = core.material.images.images[this.UIMx[page][1][0]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox4)) {
+ if (this.cgs.includes(this.UIMx[page][1][1])) {
+ const img = core.material.images.images[this.UIMx[page][1][1]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ } else if (inRect(pos, imagebox5)) {
+ if (this.cgs.includes(this.UIMx[page][1][2])) {
+ const img = core.material.images.images[this.UIMx[page][1][2]];
+ if (img) {
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ show = !show;
+ }
+ }
+ }
+ }
+ }
+ drawUI() {
+ //绘制页面
+ core.clearMap(CGUI);
+ const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景
+ const bg = core.material.images.images["bg_5043.webp"]; //横屏背景
- if (core.domStyle.isVertical) {
- //竖屏
+ if (core.domStyle.isVertical) {
+ //竖屏
- core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景
- ctx.globalAlpha = 0.5; //透明度
- if (bgVertical)
- ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片
- ctx.globalAlpha = 1; //恢复为不透明
+ core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景
+ ctx.globalAlpha = 0.5; //透明度
+ if (bgVertical)
+ ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片
+ ctx.globalAlpha = 1; //恢复为不透明
- core.setTextAlign(ctx, "center");
- core.fillBoldText1(
- ctx,
- "◀离开",
- 100,
- 110,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ core.setTextAlign(ctx, "center");
+ core.fillBoldText1(
+ ctx,
+ "◀离开",
+ 100,
+ 110,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- core.fillBoldText1(
- ctx,
- "上一页",
- 300,
- 1900,
- page === 0 ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ core.fillBoldText1(
+ ctx,
+ "上一页",
+ 300,
+ 1900,
+ page === 0 ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- core.fillBoldText1(
- ctx,
- page + 1 + "/" + this.UIMx.length,
- 650,
- 1900,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- core.fillBoldText1(
- ctx,
- "下一页",
- 1000,
- 1900,
- page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- // 添加向上翻页和向下翻页的按钮
+ core.fillBoldText1(
+ ctx,
+ page + 1 + "/" + this.UIMx.length,
+ 650,
+ 1900,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ core.fillBoldText1(
+ ctx,
+ "下一页",
+ 1000,
+ 1900,
+ page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ // 添加向上翻页和向下翻页的按钮
- // 添加3*2个4:3的画框,及图片
- for (let i = 0; i < 3; i++) {
- for (let j = 0; j < 2; j++) {
- const img = core.material.images.images[this.UIMx[page][j][i]];
- core.strokeRect(
- ctx,
- 50 + j * 600,
- 200 + i * 550,
- 560,
- 420,
- "#444444",
- 5
- );
- if (this.cgs.includes(this.UIMx[page][j][i])) {
- if (img)
- ctx.drawImage(
- img,
- 50 + j * 600 + 15,
- 200 + i * 550 + 15,
- 560 - 30,
- 420 - 30
- );
- } else {
- ctx.fillStyle = "#000000";
- ctx.fillRect(
- 50 + j * 600 + 15,
- 200 + i * 550 + 15,
- 560 - 30,
- 420 - 30
- );
- const img = core.material.images.images["LOGO.webp"];
- if (img)
- ctx.drawImage(
- img,
- 50 + j * 600 + 15,
- 200 + i * 550 + 15,
- 560 - 30,
- 420 - 30
- );
- }
- }
- }
- } else {
- //横屏
- core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景
- ctx.globalAlpha = 0.5; //透明度
- if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片
- ctx.globalAlpha = 1; //恢复为不透明
+ // 添加3*2个4:3的画框,及图片
+ for (let i = 0; i < 3; i++) {
+ for (let j = 0; j < 2; j++) {
+ const img = core.material.images.images[this.UIMx[page][j][i]];
+ core.strokeRect(
+ ctx,
+ 50 + j * 600,
+ 200 + i * 550,
+ 560,
+ 420,
+ "#444444",
+ 5
+ );
+ if (this.cgs.includes(this.UIMx[page][j][i])) {
+ if (img)
+ ctx.drawImage(
+ img,
+ 50 + j * 600 + 15,
+ 200 + i * 550 + 15,
+ 560 - 30,
+ 420 - 30
+ );
+ } else {
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(
+ 50 + j * 600 + 15,
+ 200 + i * 550 + 15,
+ 560 - 30,
+ 420 - 30
+ );
+ const img = core.material.images.images["LOGO.webp"];
+ if (img)
+ ctx.drawImage(
+ img,
+ 50 + j * 600 + 15,
+ 200 + i * 550 + 15,
+ 560 - 30,
+ 420 - 30
+ );
+ }
+ }
+ }
+ } else {
+ //横屏
+ core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景
+ ctx.globalAlpha = 0.5; //透明度
+ if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片
+ ctx.globalAlpha = 1; //恢复为不透明
- core.setTextAlign(ctx, "center");
- core.fillBoldText1(
- ctx,
- "◀离开",
- 110,
- 100,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ core.setTextAlign(ctx, "center");
+ core.fillBoldText1(
+ ctx,
+ "◀离开",
+ 110,
+ 100,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- // 添加向上翻页和向下翻页的按钮
- core.fillBoldText1(
- ctx,
- "上一页",
- 300,
- 1180,
- page === 0 ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ // 添加向上翻页和向下翻页的按钮
+ core.fillBoldText1(
+ ctx,
+ "上一页",
+ 300,
+ 1180,
+ page === 0 ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- core.fillBoldText1(
- ctx,
- page + 1 + "/" + this.UIMx.length,
- 1000,
- 1180,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- core.fillBoldText1(
- ctx,
- "下一页",
- 1700,
- 1180,
- page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ core.fillBoldText1(
+ ctx,
+ page + 1 + "/" + this.UIMx.length,
+ 1000,
+ 1180,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ core.fillBoldText1(
+ ctx,
+ "下一页",
+ 1700,
+ 1180,
+ page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- // 添加3*2个4:3的画框
- for (let i = 0; i < 2; i++) {
- for (let j = 0; j < 3; j++) {
- core.strokeRect(
- ctx,
- 75 + j * 650,
- 150 + i * 500,
- 600,
- 450,
- "#444444",
- 2
- );
- if (this.cgs.includes(this.UIMx[page][i][j])) {
- const img = core.material.images.images[this.UIMx[page][i][j]];
- if (img)
- ctx.drawImage(
- img,
- 75 + j * 650 + 15,
- 150 + i * 500 + 15,
- 600 - 30,
- 450 - 30
- );
- } else {
- ctx.fillStyle = "#000000";
- ctx.fillRect(
- 75 + j * 650 + 15,
- 150 + i * 500 + 15,
- 600 - 30,
- 450 - 30
- );
- const img = core.material.images.images["LOGO.webp"];
- if (img)
- ctx.drawImage(
- img,
- 75 + j * 650 + 15,
- 150 + i * 500 + 15,
- 600 - 30,
- 450 - 30
- );
- }
- }
- }
- }
- }
- }
- this.setcgs = function (img) {
- const a = core.getLocalStorage("cgs", []);
- if (img) {
- if (!a.includes(img)) a.push(img);
- core.setLocalStorage("cgs", a);
- } else core.setLocalStorage("cgs");
- };
- core.ui.CG = new CG();
- main.dom.CGMode.onclick = function () {
- //点击开始页面的CG MODE进入cg回廊
- main.core.control.checkBgm();
- page = 0;
- main.core.ui.CG.cgs = core.getLocalStorage("cgs", []);
- CGUI.style.display = "block";
- main.core.ui.CG.update();
- };
-},
+ // 添加3*2个4:3的画框
+ for (let i = 0; i < 2; i++) {
+ for (let j = 0; j < 3; j++) {
+ core.strokeRect(
+ ctx,
+ 75 + j * 650,
+ 150 + i * 500,
+ 600,
+ 450,
+ "#444444",
+ 2
+ );
+ if (this.cgs.includes(this.UIMx[page][i][j])) {
+ const img = core.material.images.images[this.UIMx[page][i][j]];
+ if (img)
+ ctx.drawImage(
+ img,
+ 75 + j * 650 + 15,
+ 150 + i * 500 + 15,
+ 600 - 30,
+ 450 - 30
+ );
+ } else {
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(
+ 75 + j * 650 + 15,
+ 150 + i * 500 + 15,
+ 600 - 30,
+ 450 - 30
+ );
+ const img = core.material.images.images["LOGO.webp"];
+ if (img)
+ ctx.drawImage(
+ img,
+ 75 + j * 650 + 15,
+ 150 + i * 500 + 15,
+ 600 - 30,
+ 450 - 30
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ this.setcgs = function (img) {
+ const a = core.getLocalStorage("cgs", []);
+ if (img) {
+ if (!a.includes(img)) a.push(img);
+ core.setLocalStorage("cgs", a);
+ } else core.setLocalStorage("cgs");
+ };
+ core.ui.CG = new CG();
+ main.dom.CGMode.onclick = function () {
+ //点击开始页面的CG MODE进入cg回廊
+ main.core.control.checkBgm();
+ page = 0;
+ main.core.ui.CG.cgs = core.getLocalStorage("cgs", []);
+ CGUI.style.display = "block";
+ main.core.ui.CG.update();
+ };
+ },
"光标设置": function () {
// 在此增加新插件
this.changeMouse = function (
@@ -15214,902 +14994,901 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
};
},
"剧情cg": function () {
- // 在此增加新插件
- // 在此增加新插件
- const cg = document.createElement("canvas"); //cg画布设置
- cg.style.position = "absolute";
- cg.style.zIndex = 300;
- cg.style.display = "none";
- cg.id = "cgText";
- main.dom.gameGroup.insertAdjacentElement("afterend", cg);
- cg.style.top = "50%";
- cg.style.left = "50%";
- cg.style.transform = "translate(-50%,-50%)";
- const ctx = cg.getContext("2d");
- main.dom.cgText = cg;
- const logcanvas = document.createElement("canvas"); //cg画布设置
- logcanvas.style.position = "absolute";
- logcanvas.style.zIndex = 301;
- logcanvas.style.display = "none";
- logcanvas.id = "cgText";
- main.dom.gameGroup.insertAdjacentElement("afterend", logcanvas);
- logcanvas.style.top = "50%";
- logcanvas.style.left = "50%";
- logcanvas.style.transform = "translate(-50%,-50%)";
- const logctx = logcanvas.getContext("2d");
- main.dom.logcanvas = logcanvas;
- logcanvas.onmouseup = function (e) {
- //鼠标抬起
+ // 在此增加新插件
+ // 在此增加新插件
+ const cg = document.createElement("canvas"); //cg画布设置
+ cg.style.position = "absolute";
+ cg.style.zIndex = 300;
+ cg.style.display = "none";
+ cg.id = "cgText";
+ main.dom.gameGroup.insertAdjacentElement("afterend", cg);
+ cg.style.top = "50%";
+ cg.style.left = "50%";
+ cg.style.transform = "translate(-50%,-50%)";
+ const ctx = cg.getContext("2d");
+ main.dom.cgText = cg;
+ const logcanvas = document.createElement("canvas"); //cg画布设置
+ logcanvas.style.position = "absolute";
+ logcanvas.style.zIndex = 301;
+ logcanvas.style.display = "none";
+ logcanvas.id = "cgText";
+ main.dom.gameGroup.insertAdjacentElement("afterend", logcanvas);
+ logcanvas.style.top = "50%";
+ logcanvas.style.left = "50%";
+ logcanvas.style.transform = "translate(-50%,-50%)";
+ const logctx = logcanvas.getContext("2d");
+ main.dom.logcanvas = logcanvas;
+ logcanvas.onmouseup = function (e) {
+ //鼠标抬起
- try {
- if (!core.isPlaying()) return false;
- core.unregisterAnimationFrame("skip");
- let a = core.getFlag("skip", false);
- core.setFlag("skip", false);
- if (a) {
- const data = core.clone(core.status.event.data.current);
+ try {
+ if (!core.isPlaying()) return false;
+ core.unregisterAnimationFrame("skip");
+ let a = core.getFlag("skip", false);
+ core.setFlag("skip", false);
+ if (a) {
+ const data = core.clone(core.status.event.data.current);
- core.insertAction(data);
- core.doAction();
- }
- } catch (ee) {
- console.error(ee);
- }
- };
- logcanvas.onmousedown = function (e) {
- //鼠标按下
- try {
- 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.cgText.click(px * 3, py * 3);
- } catch (ee) {
- main.log(ee);
- }
- };
- logcanvas.ontouchend = function (e) {
- //触摸抬起
+ core.insertAction(data);
+ core.doAction();
+ }
+ } catch (ee) {
+ console.error(ee);
+ }
+ };
+ logcanvas.onmousedown = function (e) {
+ //鼠标按下
+ try {
+ 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.cgText.click(px * 3, py * 3);
+ } catch (ee) {
+ main.log(ee);
+ }
+ };
+ logcanvas.ontouchend = function (e) {
+ //触摸抬起
- try {
- if (!core.isPlaying()) return false;
- core.unregisterAnimationFrame("skip");
- core.setFlag("skip", false);
- let a = core.getFlag("skip", false);
- core.setFlag("skip", false);
- if (a) {
- const data = core.clone(core.status.event.data.current);
+ try {
+ if (!core.isPlaying()) return false;
+ core.unregisterAnimationFrame("skip");
+ core.setFlag("skip", false);
+ let a = core.getFlag("skip", false);
+ core.setFlag("skip", false);
+ if (a) {
+ const data = core.clone(core.status.event.data.current);
- core.insertAction(data);
- core.doAction();
- }
- } catch (ee) {}
- };
- logcanvas.ontouchstart = function (e) {
- //触摸按下
- try {
- if (!core.isPlaying()) return false;
- const left = core.dom.gameGroup.offsetLeft;
- const top = core.dom.gameGroup.offsetTop;
- const px = Math.floor(
- (e.targetTouches[0].clientX - left) / core.domStyle.scale
- ),
- py = Math.floor(
- (e.targetTouches[0].clientY - top) / core.domStyle.scale
- );
- core.ui.cgText.click(px * 3, py * 3);
- } catch (ee) {
- main.log(ee);
- }
- };
- cg.onmouseup = function (e) {
- //鼠标抬起
+ core.insertAction(data);
+ core.doAction();
+ }
+ } catch (ee) {}
+ };
+ logcanvas.ontouchstart = function (e) {
+ //触摸按下
+ try {
+ if (!core.isPlaying()) return false;
+ const left = core.dom.gameGroup.offsetLeft;
+ const top = core.dom.gameGroup.offsetTop;
+ const px = Math.floor(
+ (e.targetTouches[0].clientX - left) / core.domStyle.scale
+ ),
+ py = Math.floor(
+ (e.targetTouches[0].clientY - top) / core.domStyle.scale
+ );
+ core.ui.cgText.click(px * 3, py * 3);
+ } catch (ee) {
+ main.log(ee);
+ }
+ };
+ cg.onmouseup = function (e) {
+ //鼠标抬起
- try {
- if (!core.isPlaying()) return false;
- core.unregisterAnimationFrame("skip");
- let a = core.getFlag("skip", false);
- core.setFlag("skip", false);
- if (a) {
- const data = core.clone(core.status.event.data.current);
+ try {
+ if (!core.isPlaying()) return false;
+ core.unregisterAnimationFrame("skip");
+ let a = core.getFlag("skip", false);
+ core.setFlag("skip", false);
+ if (a) {
+ const data = core.clone(core.status.event.data.current);
- core.insertAction(data);
- core.doAction();
- }
- } catch (ee) {
- console.error(ee);
- }
- };
- cg.onmousedown = function (e) {
- //鼠标按下
- try {
- 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.cgText.click(px * 3, py * 3);
- } catch (ee) {
- main.log(ee);
- }
- };
- cg.ontouchend = function (e) {
- //触摸抬起
+ core.insertAction(data);
+ core.doAction();
+ }
+ } catch (ee) {
+ console.error(ee);
+ }
+ };
+ cg.onmousedown = function (e) {
+ //鼠标按下
+ try {
+ 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.cgText.click(px * 3, py * 3);
+ } catch (ee) {
+ main.log(ee);
+ }
+ };
+ cg.ontouchend = function (e) {
+ //触摸抬起
- try {
- if (!core.isPlaying()) return false;
- core.unregisterAnimationFrame("skip");
- core.setFlag("skip", false);
- let a = core.getFlag("skip", false);
- core.setFlag("skip", false);
- if (a) {
- const data = core.clone(core.status.event.data.current);
+ try {
+ if (!core.isPlaying()) return false;
+ core.unregisterAnimationFrame("skip");
+ core.setFlag("skip", false);
+ let a = core.getFlag("skip", false);
+ core.setFlag("skip", false);
+ if (a) {
+ const data = core.clone(core.status.event.data.current);
- core.insertAction(data);
- core.doAction();
- }
- } catch (ee) {
- console.error(ee);
- }
- };
- cg.ontouchstart = function (e) {
- //触摸按下
- try {
- if (!core.isPlaying()) return false;
- const left = core.dom.gameGroup.offsetLeft;
- const top = core.dom.gameGroup.offsetTop;
- const px = Math.floor(
- (e.targetTouches[0].clientX - left) / core.domStyle.scale
- ),
- py = Math.floor(
- (e.targetTouches[0].clientY - top) / core.domStyle.scale
- );
- core.ui.cgText.click(px * 3, py * 3);
- } catch (ee) {
- main.log(ee);
- }
- };
- let auto = false;
+ core.insertAction(data);
+ core.doAction();
+ }
+ } catch (ee) {
+ console.error(ee);
+ }
+ };
+ cg.ontouchstart = function (e) {
+ //触摸按下
+ try {
+ if (!core.isPlaying()) return false;
+ const left = core.dom.gameGroup.offsetLeft;
+ const top = core.dom.gameGroup.offsetTop;
+ const px = Math.floor(
+ (e.targetTouches[0].clientX - left) / core.domStyle.scale
+ ),
+ py = Math.floor(
+ (e.targetTouches[0].clientY - top) / core.domStyle.scale
+ );
+ core.ui.cgText.click(px * 3, py * 3);
+ } catch (ee) {
+ main.log(ee);
+ }
+ };
+ let auto = false;
- class cgText {
- constructor() {
- //绘制需要的变量
- this.image = "";
- this.head = { name: "face_050445.webp", px: -300 };
- this.bodyList = [
- { name: "tati_050145a.webp", px: 100, filter: false },
- { name: "tati_120124.webp", px: 1100, filter: true },
- ];
- this.name = "";
- this.text = "";
- this.time = 0;
- this.WindowSkin = false;
- this.sound = "";
- this.beforeSound = 0;
- this.wait = 1000;
- this.memory = false;
- this.textList = [];
- this.page = 1;
- this.overpage = 1;
- this.log = false;
- this.index = 0;
- }
- click(px, py) {
- //点击效果
+ class cgText {
+ constructor() {
+ //绘制需要的变量
+ this.image = "";
+ this.head = { name: "face_050445.webp", px: -300 };
+ this.bodyList = [
+ { name: "tati_050145a.webp", px: 100, filter: false },
+ { name: "tati_120124.webp", px: 1100, filter: true },
+ ];
+ this.name = "";
+ this.text = "";
+ this.time = 0;
+ this.WindowSkin = false;
+ this.sound = "";
+ this.beforeSound = 0;
+ this.wait = 1000;
+ this.memory = false;
+ this.textList = [];
+ this.page = 1;
+ this.overpage = 1;
+ this.log = false;
+ this.index = 0;
+ }
+ click(px, py) {
+ //点击效果
- const makeBox = ([x, y], [w, h]) => {
- return [
- [x, y],
- [x + w, y + h],
- ];
- };
- const inRect = ([x, y], [
- [sx, sy],
- [dx, dy]
- ]) => {
- return sx <= x && x <= dx && sy <= y && y <= dy;
- };
- const pos = [px, py];
- const savebox = makeBox([1700, 1100], [192, 96]);
- const saveboxVertical = makeBox([52, 1700], [96, 192]);
- const skipbox = makeBox([1700, 1000], [192, 96]);
- const skipboxVertical = makeBox([152, 1700], [96, 192]);
- const autobox = makeBox([1700, 900], [192, 96]);
- const autoboxVertical = makeBox([252, 1700], [96, 192]);
- const textbox = makeBox([1700, 800], [192, 96]);
- const textboxVertical = makeBox([352, 1700], [96, 192]);
- const backbox = makeBox([15, 35], [210, 90]);
- const backboxVertical = makeBox([1123, 15], [90, 210]);
- const pageupbox = makeBox([300, 1130], [200, 100]);
- const pageupboxVertical = makeBox([18, 300], [100, 200]);
- const pagedownbox = makeBox([1500, 1130], [200, 100]);
- const pagedownboxVertical = makeBox([18, 1500], [100, 200]);
- if (this.log) {
- if (
- (core.domStyle.isVertical && inRect(pos, backboxVertical)) ||
- (!core.domStyle.isVertical && inRect(pos, backbox))
- ) {
- core.clearMap(logctx);
- main.dom.logcanvas.style.display = "none";
- this.log = false;
- } else if (
- (core.domStyle.isVertical && inRect(pos, pageupboxVertical)) ||
- (!core.domStyle.isVertical && inRect(pos, pageupbox))
- ) {
- core.clearMap(logctx);
- if (this.page > 1) this.page--;
- this.logdraw(this.page);
- } else if (
- (core.domStyle.isVertical && inRect(pos, pagedownboxVertical)) ||
- (!core.domStyle.isVertical && inRect(pos, pagedownbox))
- ) {
- core.clearMap(logctx);
- if (this.page < this.overpage) this.page++;
- this.logdraw(this.page);
- }
- } else {
- if (
- (core.domStyle.isVertical &&
- inRect(pos, skipboxVertical) &&
- !this.WindowSkin) ||
- (!core.domStyle.isVertical &&
- !this.WindowSkin &&
- inRect(pos, skipbox))
- ) {
- auto = false;
- let time = 0;
- core.stopSound(this.beforeSound);
- core.registerAnimationFrame("skip", true, (timestamp) => {
- if (timestamp > time + 50) {
- time = timestamp;
- if (
- core.status.event.id == "action" &&
- core.status.event.data.type == "cgtext"
- ) {
- core.setFlag("skip", true);
- main.dom.cgText.style.display = "none";
- core.doAction();
- }
- }
- });
- } else if (
- (core.domStyle.isVertical &&
- inRect(pos, textboxVertical) &&
- !this.WindowSkin) ||
- (!core.domStyle.isVertical &&
- !this.WindowSkin &&
- inRect(pos, textbox))
- ) {
- auto = false;
- this.log = true;
- this.overpage = Math.floor(this.index / 6) + 1;
- this.page = this.overpage;
- logcanvas.style.display = "block";
- const data = core.clone(core.status.event.data.current);
- data.showAll = true;
- data.time = 0;
- data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
- data.sound = "";
- core.insertAction(data);
- core.doAction();
- this.logdraw(this.page);
- } else if (
- (core.domStyle.isVertical &&
- inRect(pos, autoboxVertical) &&
- !this.WindowSkin) ||
- (!core.domStyle.isVertical &&
- !this.WindowSkin &&
- inRect(pos, autobox))
- ) {
- auto = !auto;
+ const makeBox = ([x, y], [w, h]) => {
+ return [
+ [x, y],
+ [x + w, y + h],
+ ];
+ };
+ const inRect = ([x, y], [[sx, sy], [dx, dy]]) => {
+ return sx <= x && x <= dx && sy <= y && y <= dy;
+ };
+ const pos = [px, py];
+ const savebox = makeBox([1700, 1100], [192, 96]);
+ const saveboxVertical = makeBox([52, 1700], [96, 192]);
+ const skipbox = makeBox([1700, 1000], [192, 96]);
+ const skipboxVertical = makeBox([152, 1700], [96, 192]);
+ const autobox = makeBox([1700, 900], [192, 96]);
+ const autoboxVertical = makeBox([252, 1700], [96, 192]);
+ const textbox = makeBox([1700, 800], [192, 96]);
+ const textboxVertical = makeBox([352, 1700], [96, 192]);
+ const backbox = makeBox([15, 35], [210, 90]);
+ const backboxVertical = makeBox([1123, 15], [90, 210]);
+ const pageupbox = makeBox([300, 1130], [200, 100]);
+ const pageupboxVertical = makeBox([18, 300], [100, 200]);
+ const pagedownbox = makeBox([1500, 1130], [200, 100]);
+ const pagedownboxVertical = makeBox([18, 1500], [100, 200]);
+ if (this.log) {
+ if (
+ (core.domStyle.isVertical && inRect(pos, backboxVertical)) ||
+ (!core.domStyle.isVertical && inRect(pos, backbox))
+ ) {
+ core.clearMap(logctx);
+ main.dom.logcanvas.style.display = "none";
+ this.log = false;
+ } else if (
+ (core.domStyle.isVertical && inRect(pos, pageupboxVertical)) ||
+ (!core.domStyle.isVertical && inRect(pos, pageupbox))
+ ) {
+ core.clearMap(logctx);
+ if (this.page > 1) this.page--;
+ this.logdraw(this.page);
+ } else if (
+ (core.domStyle.isVertical && inRect(pos, pagedownboxVertical)) ||
+ (!core.domStyle.isVertical && inRect(pos, pagedownbox))
+ ) {
+ core.clearMap(logctx);
+ if (this.page < this.overpage) this.page++;
+ this.logdraw(this.page);
+ }
+ } else {
+ if (
+ (core.domStyle.isVertical &&
+ inRect(pos, skipboxVertical) &&
+ !this.WindowSkin) ||
+ (!core.domStyle.isVertical &&
+ !this.WindowSkin &&
+ inRect(pos, skipbox))
+ ) {
+ auto = false;
+ let time = 0;
+ core.stopSound(this.beforeSound);
+ core.registerAnimationFrame("skip", true, (timestamp) => {
+ if (timestamp > time + 50) {
+ time = timestamp;
+ if (
+ core.status.event.id == "action" &&
+ core.status.event.data.type == "cgtext"
+ ) {
+ core.setFlag("skip", true);
+ main.dom.cgText.style.display = "none";
+ core.doAction();
+ }
+ }
+ });
+ } else if (
+ (core.domStyle.isVertical &&
+ inRect(pos, textboxVertical) &&
+ !this.WindowSkin) ||
+ (!core.domStyle.isVertical &&
+ !this.WindowSkin &&
+ inRect(pos, textbox))
+ ) {
+ auto = false;
+ this.log = true;
+ this.overpage = Math.floor(this.index / 6) + 1;
+ this.page = this.overpage;
+ logcanvas.style.display = "block";
+ const data = core.clone(core.status.event.data.current);
+ data.showAll = true;
+ data.time = 0;
+ data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
+ data.sound = "";
+ core.insertAction(data);
+ core.doAction();
+ this.logdraw(this.page);
+ } else if (
+ (core.domStyle.isVertical &&
+ inRect(pos, autoboxVertical) &&
+ !this.WindowSkin) ||
+ (!core.domStyle.isVertical &&
+ !this.WindowSkin &&
+ inRect(pos, autobox))
+ ) {
+ auto = !auto;
- const data = core.clone(core.status.event.data.current);
- data.showAll = true;
- data.time = 0;
- data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
- data.sound = "";
- core.insertAction(data);
- core.doAction();
- } else if (
- (core.domStyle.isVertical &&
- inRect(pos, saveboxVertical) &&
- !this.WindowSkin) ||
- (!core.domStyle.isVertical &&
- !this.WindowSkin &&
- inRect(pos, savebox))
- ) {
- //存档
- auto = false;
- if (core.status.event.animateUI) return;
- if (core.status.event.interval != null) return;
- const current = core.clone(core.status.event.data.current);
- current.showAll = true;
- current.time = 0;
- current.sound = "";
- current.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除当前事件所有打字机效果
- cg.style.display = "none";
- const data = [{ type: "callSave" }, current]; //插入存档事件
- core.insertAction(data);
+ const data = core.clone(core.status.event.data.current);
+ data.showAll = true;
+ data.time = 0;
+ data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
+ data.sound = "";
+ core.insertAction(data);
+ core.doAction();
+ } else if (
+ (core.domStyle.isVertical &&
+ inRect(pos, saveboxVertical) &&
+ !this.WindowSkin) ||
+ (!core.domStyle.isVertical &&
+ !this.WindowSkin &&
+ inRect(pos, savebox))
+ ) {
+ //存档
+ auto = false;
+ if (core.status.event.animateUI) return;
+ if (core.status.event.interval != null) return;
+ const current = core.clone(core.status.event.data.current);
+ current.showAll = true;
+ current.time = 0;
+ current.sound = "";
+ current.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除当前事件所有打字机效果
+ cg.style.display = "none";
+ const data = [{ type: "callSave" }, current]; //插入存档事件
+ core.insertAction(data);
- core.doAction();
- } else if (!core.status.event.data) {
- cg.style.display = "none";
- core.ui._animateUI("hide", null, () => {
- core.doAction();
- });
- } else {
- // 正在淡入淡出的话不执行
- if (core.status.event.animateUI) return;
- auto = false;
+ core.doAction();
+ } else if (!core.status.event.data) {
+ cg.style.display = "none";
+ core.ui._animateUI("hide", null, () => {
+ core.doAction();
+ });
+ } else {
+ // 正在淡入淡出的话不执行
+ if (core.status.event.animateUI) return;
+ auto = false;
- // 打字机效果显示全部文字
- if (core.status.event.interval != null) {
- const data = core.clone(core.status.event.data?.current);
- data.showAll = true;
- data.time = 0;
- data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
- data.sound = "";
- core.insertAction(data);
- core.doAction();
- return;
- } else {
- core.stopSound(this.beforeSound);
- }
+ // 打字机效果显示全部文字
+ if (core.status.event.interval != null) {
+ const data = core.clone(core.status.event.data?.current);
+ data.showAll = true;
+ data.time = 0;
+ data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
+ data.sound = "";
+ core.insertAction(data);
+ core.doAction();
+ return;
+ } else {
+ core.stopSound(this.beforeSound);
+ }
- cg.style.display = "none";
- core.ui._animateUI("hide", null, () => {
- core.doAction();
- });
- }
- }
- }
+ cg.style.display = "none";
+ core.ui._animateUI("hide", null, () => {
+ core.doAction();
+ });
+ }
+ }
+ }
- drawTextContent(ctx, content, config) {
- //绘制多行文字并执行打字机效果
+ drawTextContent(ctx, content, config) {
+ //绘制多行文字并执行打字机效果
- ctx = core.getContextByName(ctx);
- // 设置默认配置项
- var textAttribute =
- core.status.textAttribute || core.initStatus.textAttribute;
- var globalAttribute =
- core.status.globalAttribute || core.initStatus.globalAttribute;
- config = core.clone(config || {});
- config.left = config.left || 0;
- config.right =
- config.left + (config.maxWidth == null ? core._PX_ : config.maxWidth);
- config.top = config.top || 0;
- config.color = core.arrayToRGBA(config.color || textAttribute.text);
- if (config.bold == null) config.bold = textAttribute.bold;
- config.italic = config.italic || false;
- config.align = config.align || textAttribute.align || "left";
- config.fontSize = config.fontSize || textAttribute.textfont;
- config.lineHeight = config.lineHeight || config.fontSize * 1.3;
- config.defaultFont = config.font = config.font || globalAttribute.font;
- config.time = config.time || 0;
- config.letterSpacing =
- config.letterSpacing == null ?
- textAttribute.letterSpacing || 0 :
- config.letterSpacing;
+ ctx = core.getContextByName(ctx);
+ // 设置默认配置项
+ var textAttribute =
+ core.status.textAttribute || core.initStatus.textAttribute;
+ var globalAttribute =
+ core.status.globalAttribute || core.initStatus.globalAttribute;
+ config = core.clone(config || {});
+ config.left = config.left || 0;
+ config.right =
+ config.left + (config.maxWidth == null ? core._PX_ : config.maxWidth);
+ config.top = config.top || 0;
+ config.color = core.arrayToRGBA(config.color || textAttribute.text);
+ if (config.bold == null) config.bold = textAttribute.bold;
+ config.italic = config.italic || false;
+ config.align = config.align || textAttribute.align || "left";
+ config.fontSize = config.fontSize || textAttribute.textfont;
+ config.lineHeight = config.lineHeight || config.fontSize * 1.3;
+ config.defaultFont = config.font = config.font || globalAttribute.font;
+ config.time = config.time || 0;
+ config.letterSpacing =
+ config.letterSpacing == null
+ ? textAttribute.letterSpacing || 0
+ : config.letterSpacing;
- config.index = 0;
- config.currcolor = config.color;
- config.currfont = config.fontSize;
- config.lineMargin = Math.max(
- Math.round(config.fontSize / 4),
- config.lineHeight - config.fontSize
- );
- config.topMargin = parseInt(config.lineMargin / 2);
- config.lineMaxHeight = config.lineMargin + config.fontSize;
- config.offsetX = 0;
- config.offsetY = 0;
- config.line = 0;
- config.blocks = [];
- config.isHD = ctx == null || ctx.canvas.hasAttribute("isHD");
+ config.index = 0;
+ config.currcolor = config.color;
+ config.currfont = config.fontSize;
+ config.lineMargin = Math.max(
+ Math.round(config.fontSize / 4),
+ config.lineHeight - config.fontSize
+ );
+ config.topMargin = parseInt(config.lineMargin / 2);
+ config.lineMaxHeight = config.lineMargin + config.fontSize;
+ config.offsetX = 0;
+ config.offsetY = 0;
+ config.line = 0;
+ config.blocks = [];
+ config.isHD = ctx == null || ctx.canvas.hasAttribute("isHD");
- // 创建一个新的临时画布
- var tempCtx = document.createElement("canvas").getContext("2d");
- if (config.isHD && ctx) {
- core.maps._setHDCanvasSize(
- tempCtx,
- ctx.canvas.width,
- ctx.canvas.height
- );
- } else {
- tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
- tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
- }
+ // 创建一个新的临时画布
+ var tempCtx = document.createElement("canvas").getContext("2d");
+ if (config.isHD && ctx) {
+ core.maps._setHDCanvasSize(
+ tempCtx,
+ ctx.canvas.width,
+ ctx.canvas.height
+ );
+ } else {
+ tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
+ tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
+ }
- tempCtx.textBaseline = "top";
- tempCtx.font = core.ui._buildFont(
- config.fontSize,
- config.bold,
- config.italic,
- config.font
- );
- tempCtx.fillStyle = config.color;
- config = this._drawTextContent_draw(ctx, tempCtx, content, config);
- return config;
- }
- _drawTextContent_draw(ctx, tempCtx, content, config) {
- // Step 1: 绘制到tempCtx上,并记录下图块信息
- while (core.ui._drawTextContent_next(tempCtx, content, config));
+ tempCtx.textBaseline = "top";
+ tempCtx.font = core.ui._buildFont(
+ config.fontSize,
+ config.bold,
+ config.italic,
+ config.font
+ );
+ tempCtx.fillStyle = config.color;
+ config = this._drawTextContent_draw(ctx, tempCtx, content, config);
+ return config;
+ }
+ _drawTextContent_draw(ctx, tempCtx, content, config) {
+ // Step 1: 绘制到tempCtx上,并记录下图块信息
+ while (core.ui._drawTextContent_next(tempCtx, content, config));
- if (ctx == null) return config;
+ if (ctx == null) return config;
- // Step 2: 从tempCtx绘制到画布上
- config.index = 0;
- var _drawNext = function () {
- if (config.index >= config.blocks.length) return false;
- var block = config.blocks[config.index++];
- if (block != null) {
- // It works, why?
- const scale = config.isHD ?
- devicePixelRatio * core.domStyle.scale :
- 1;
- ctx.restore();
- ctx.save(); //保存设置
- if (core.domStyle.isVertical) {
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- }
- ctx.drawImage(
- tempCtx.canvas,
- block.left * scale,
- block.top * scale,
- block.width * scale,
- block.height * scale,
- config.left + block.left + block.marginLeft,
- config.top + block.top + block.marginTop,
- block.width,
- block.height
- );
- ctx.restore();
- }
- return true;
- };
+ // Step 2: 从tempCtx绘制到画布上
+ config.index = 0;
+ var _drawNext = function () {
+ if (config.index >= config.blocks.length) return false;
+ var block = config.blocks[config.index++];
+ if (block != null) {
+ // It works, why?
+ const scale = config.isHD
+ ? devicePixelRatio * core.domStyle.scale
+ : 1;
+ ctx.restore();
+ ctx.save(); //保存设置
+ if (core.domStyle.isVertical) {
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ }
+ ctx.drawImage(
+ tempCtx.canvas,
+ block.left * scale,
+ block.top * scale,
+ block.width * scale,
+ block.height * scale,
+ config.left + block.left + block.marginLeft,
+ config.top + block.top + block.marginTop,
+ block.width,
+ block.height
+ );
+ ctx.restore();
+ }
+ return true;
+ };
- if (config.time == 0) {
- while (_drawNext());
+ if (config.time == 0) {
+ while (_drawNext());
- if (
- (auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
- (core.ui.cgText.sound && !core.musicStatus.soundStatus)
- ) {
- setTimeout(() => {
- if (auto) {
- cg.style.display = "none";
- core.ui._animateUI("hide", null, () => {
- core.doAction();
- });
- }
- }, core.ui.cgText.wait);
- }
- } else {
- clearInterval(core.status.event.interval);
- core.status.event.interval = setInterval(function () {
- if (!_drawNext()) {
- clearInterval(core.status.event.interval);
- core.status.event.interval = null;
+ if (
+ (auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
+ (core.ui.cgText.sound && !core.musicStatus.soundStatus)
+ ) {
+ setTimeout(() => {
+ if (auto) {
+ cg.style.display = "none";
+ core.ui._animateUI("hide", null, () => {
+ core.doAction();
+ });
+ }
+ }, core.ui.cgText.wait);
+ }
+ } else {
+ clearInterval(core.status.event.interval);
+ core.status.event.interval = setInterval(function () {
+ if (!_drawNext()) {
+ clearInterval(core.status.event.interval);
+ core.status.event.interval = null;
- if (
- (auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
- (core.ui.cgText.sound && !core.musicStatus.soundStatus)
- )
- setTimeout(() => {
- if (auto) {
- cg.style.display = "none";
- core.ui._animateUI("hide", null, () => {
- core.doAction();
- });
- }
- }, core.ui.cgText.wait);
- }
- }, config.time);
- }
+ if (
+ (auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
+ (core.ui.cgText.sound && !core.musicStatus.soundStatus)
+ )
+ setTimeout(() => {
+ if (auto) {
+ cg.style.display = "none";
+ core.ui._animateUI("hide", null, () => {
+ core.doAction();
+ });
+ }
+ }, core.ui.cgText.wait);
+ }
+ }, config.time);
+ }
- return config;
- }
+ return config;
+ }
- update() {
- this.background();
- if (this.log) this.logdraw(this.page);
- }
- logdraw(page) {
- if (core.domStyle.isVertical) {
- logctx.canvas.width = 1248;
- logctx.canvas.height = 2028;
- logctx.save(); //保存设置
- logctx.translate(1248, 0); //重新定位右上角为基准
- logctx.rotate(Math.PI / 2); //旋转90度
- } else {
- logctx.canvas.width = 2028;
- logctx.canvas.height = 1248;
- }
- core.fillRect(logctx, 0, 0, 2028, 1248, "rgba(0,0,0,0.5)");
- core.setTextAlign(logctx, "center");
- core.fillBoldText1(
- logctx,
- "◀离开",
- 110,
- 100,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- logctx.strokeStyle = "#FFFFFF";
- logctx.lineWidth = 3;
- logctx.beginPath();
- logctx.moveTo(100, 150);
- logctx.lineTo(1928, 150);
+ update() {
+ this.background();
+ if (this.log) this.logdraw(this.page);
+ }
+ logdraw(page) {
+ if (core.domStyle.isVertical) {
+ logctx.canvas.width = 1248;
+ logctx.canvas.height = 2028;
+ logctx.save(); //保存设置
+ logctx.translate(1248, 0); //重新定位右上角为基准
+ logctx.rotate(Math.PI / 2); //旋转90度
+ } else {
+ logctx.canvas.width = 2028;
+ logctx.canvas.height = 1248;
+ }
+ core.fillRect(logctx, 0, 0, 2028, 1248, "rgba(0,0,0,0.5)");
+ core.setTextAlign(logctx, "center");
+ core.fillBoldText1(
+ logctx,
+ "◀离开",
+ 110,
+ 100,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ logctx.strokeStyle = "#FFFFFF";
+ logctx.lineWidth = 3;
+ logctx.beginPath();
+ logctx.moveTo(100, 150);
+ logctx.lineTo(1928, 150);
- logctx.stroke();
- let posy = 150;
- const indexList = this.textList;
- core.setTextAlign(logctx, "left");
- for (
- let i = (page - 1) * 6; i <= Math.min(this.index, page * 6 - 1); i++
- ) {
- const text = this.textList[i][1].replaceAll(
- /(\\(d|e|f|g|i|n|r|b|c|t|z))(\[.*?\])?/g,
- ""
- ); //取消打字机
- const name = this.textList[i][0] ?
- "【" + this.textList[i][0] + "】" :
- "";
- if (name) {
- core.fillBoldText1(
- logctx,
- name,
- 150,
- posy + 50,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- }
- if (text.length < 30) {
- core.fillBoldText1(
- logctx,
- text,
- 650,
- posy + 50,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- } else {
- let text1 = text.slice(0, 30);
- core.fillBoldText1(
- logctx,
- text1,
- 650,
- posy + 50,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- if (text.length > 60) {
- let text2 = text.slice(30, 60);
- let text3 = text.slice(60);
- core.fillBoldText1(
- logctx,
- text2,
- 650,
- posy + 100,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- core.fillBoldText1(
- logctx,
- text3,
- 650,
- posy + 150,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- } else {
- let text2 = text.slice(30);
- core.fillBoldText1(
- logctx,
- text2,
- 650,
- posy + 100,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(42, true)
- );
- }
- }
+ logctx.stroke();
+ let posy = 150;
+ const indexList = this.textList;
+ core.setTextAlign(logctx, "left");
+ for (
+ let i = (page - 1) * 6;
+ i <= Math.min(this.index, page * 6 - 1);
+ i++
+ ) {
+ const text = this.textList[i][1].replaceAll(
+ /(\\(d|e|f|g|i|n|r|b|c|t|z))(\[.*?\])?/g,
+ ""
+ ); //取消打字机
+ const name = this.textList[i][0]
+ ? "【" + this.textList[i][0] + "】"
+ : "";
+ if (name) {
+ core.fillBoldText1(
+ logctx,
+ name,
+ 150,
+ posy + 50,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ }
+ if (text.length < 30) {
+ core.fillBoldText1(
+ logctx,
+ text,
+ 650,
+ posy + 50,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ } else {
+ let text1 = text.slice(0, 30);
+ core.fillBoldText1(
+ logctx,
+ text1,
+ 650,
+ posy + 50,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ if (text.length > 60) {
+ let text2 = text.slice(30, 60);
+ let text3 = text.slice(60);
+ core.fillBoldText1(
+ logctx,
+ text2,
+ 650,
+ posy + 100,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ core.fillBoldText1(
+ logctx,
+ text3,
+ 650,
+ posy + 150,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ } else {
+ let text2 = text.slice(30);
+ core.fillBoldText1(
+ logctx,
+ text2,
+ 650,
+ posy + 100,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(42, true)
+ );
+ }
+ }
- logctx.strokeStyle = "#FFFFFF";
- logctx.lineWidth = 3;
- logctx.beginPath();
- logctx.moveTo(100, posy);
- logctx.lineTo(1928, posy);
- logctx.stroke();
+ logctx.strokeStyle = "#FFFFFF";
+ logctx.lineWidth = 3;
+ logctx.beginPath();
+ logctx.moveTo(100, posy);
+ logctx.lineTo(1928, posy);
+ logctx.stroke();
- posy += 160;
- }
- logctx.beginPath();
- logctx.moveTo(100, 1120);
- logctx.lineTo(1928, 1120);
- logctx.moveTo(100, 1110);
- logctx.lineTo(1928, 1110);
- logctx.stroke();
- core.fillBoldText1(
- logctx,
- "上一页",
- 300,
- 1200,
- page === 1 ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
+ posy += 160;
+ }
+ logctx.beginPath();
+ logctx.moveTo(100, 1120);
+ logctx.lineTo(1928, 1120);
+ logctx.moveTo(100, 1110);
+ logctx.lineTo(1928, 1110);
+ logctx.stroke();
+ core.fillBoldText1(
+ logctx,
+ "上一页",
+ 300,
+ 1200,
+ page === 1 ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
- core.fillBoldText1(
- logctx,
- page + "/" + this.overpage,
- 1000,
- 1200,
- "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- core.fillBoldText1(
- logctx,
- "下一页",
- 1500,
- 1200,
- page === this.overpage ? "#444444" : "#FFFFFF",
- "#000000",
- 6,
- core.ui._buildFont(66, true)
- );
- }
- background() {
- const img = core.material.images.images?.[this.image];
+ core.fillBoldText1(
+ logctx,
+ page + "/" + this.overpage,
+ 1000,
+ 1200,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ core.fillBoldText1(
+ logctx,
+ "下一页",
+ 1500,
+ 1200,
+ page === this.overpage ? "#444444" : "#FFFFFF",
+ "#000000",
+ 6,
+ core.ui._buildFont(66, true)
+ );
+ }
+ background() {
+ const img = core.material.images.images?.[this.image];
- if (core.domStyle.isVertical) {
- ctx.canvas.width = 1248;
- ctx.canvas.height = 2028;
- ctx.save(); //保存设置
- ctx.translate(1248, 0); //重新定位右上角为基准
- ctx.rotate(Math.PI / 2); //旋转90度
- } else {
- ctx.canvas.width = 2028;
- ctx.canvas.height = 1248;
- }
+ if (core.domStyle.isVertical) {
+ ctx.canvas.width = 1248;
+ ctx.canvas.height = 2028;
+ ctx.save(); //保存设置
+ ctx.translate(1248, 0); //重新定位右上角为基准
+ ctx.rotate(Math.PI / 2); //旋转90度
+ } else {
+ ctx.canvas.width = 2028;
+ ctx.canvas.height = 1248;
+ }
- if (img) {
- //绘制背景
- if (this.memory) ctx.filter = "sepia(50%)";
- ctx.drawImage(img, 0, 0, 2028, 1248);
- ctx.filter = "none";
- } else {
- core.fillRect(ctx, 0, 0, 2028, 1248);
- }
- this.bodyList.forEach((v) => {
- //绘制立绘
- const body = core.material.images.images?.[v.name];
- if (v.filter) ctx.filter = "brightness(50%)";
- if (body) {
- if (!v.w && !v.h && !v.scale) v.scale = 1.7;
- if (!v.w && !v.h) {
- ctx.drawImage(
- body,
- 0,
- 0,
- body.width,
- body.height,
- v.px,
- 1248 - body.height * v.scale,
- body.width * v.scale,
- body.height * v.scale
- );
- } else {
- ctx.drawImage(
- body,
- 0,
- 0,
- body.width,
- body.height,
- v.px,
- 1248 - (v.h ?? body.height),
- v.w ?? body.width,
- v.h ?? body.height
- );
- }
- }
- ctx.filter = "none";
- });
- if (core.isPlaying() && !this.WindowSkin)
- core.drawWindowSkin(
- "winskin.webp",
- ctx,
- 30,
- 780,
- 1968,
- 436,
- null,
- null,
- null,
- 3
- ); //绘制对话框
- const head = core.material.images.images?.[this.head.name];
- if (head) {
- //绘制头像
- ctx.drawImage(
- head,
- 0,
- 0,
- head.width,
- head.height,
- this.head.px,
- 1248 - head.height * 2.2,
- head.width * 2.2,
- head.height * 2.2
- );
- }
- if (core.isPlaying() && !this.WindowSkin) {
- core.drawWindowSkin(
- "winskin.webp",
- ctx,
- 1700,
- 800,
- 192,
- 96,
- null,
- null,
- null,
- 3
- );
- core.fillBoldText1(
- ctx,
- "记 录",
- 1736,
- 866,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 48px Verdana"
- );
- core.drawWindowSkin(
- "winskin.webp",
- ctx,
- 1700,
- 1100,
- 192,
- 96,
- null,
- null,
- null,
- 3
- );
- core.fillBoldText1(
- ctx,
- "存 档",
- 1736,
- 1166,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 48px Verdana"
- );
- core.drawWindowSkin(
- "winskin.webp",
- ctx,
- 1700,
- 1000,
- 192,
- 96,
- null,
- null,
- null,
- 3
- );
- core.fillBoldText1(
- ctx,
- "▶▶",
- 1756,
- 1066,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 48px Verdana"
- );
- core.drawWindowSkin(
- "winskin.webp",
- ctx,
- 1700,
- 900,
- 192,
- 96,
- null,
- null,
- null,
- 3
- );
- let autoText = "AUTO";
- if (auto) autoText = "STOP";
- core.fillBoldText1(
- ctx,
- autoText,
- 1722,
- 966,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 48px Verdana"
- );
- }
- if (this.name)
- core.fillBoldText1(
- ctx,
- `【${this.name}】`,
- 550,
- 880,
- "#FFFFFF",
- "#000000",
- 6,
- "bold 48px Verdana"
- ); //绘制名字
+ if (img) {
+ //绘制背景
+ if (this.memory) ctx.filter = "sepia(50%)";
+ ctx.drawImage(img, 0, 0, 2028, 1248);
+ ctx.filter = "none";
+ } else {
+ core.fillRect(ctx, 0, 0, 2028, 1248);
+ }
+ this.bodyList.forEach((v) => {
+ //绘制立绘
+ const body = core.material.images.images?.[v.name];
+ if (v.filter) ctx.filter = "brightness(50%)";
+ if (body) {
+ if (!v.w && !v.h && !v.scale) v.scale = 1.7;
+ if (!v.w && !v.h) {
+ ctx.drawImage(
+ body,
+ 0,
+ 0,
+ body.width,
+ body.height,
+ v.px,
+ 1248 - body.height * v.scale,
+ body.width * v.scale,
+ body.height * v.scale
+ );
+ } else {
+ ctx.drawImage(
+ body,
+ 0,
+ 0,
+ body.width,
+ body.height,
+ v.px,
+ 1248 - (v.h ?? body.height),
+ v.w ?? body.width,
+ v.h ?? body.height
+ );
+ }
+ }
+ ctx.filter = "none";
+ });
+ if (core.isPlaying() && !this.WindowSkin)
+ core.drawWindowSkin(
+ "winskin.webp",
+ ctx,
+ 30,
+ 780,
+ 1968,
+ 436,
+ null,
+ null,
+ null,
+ 3
+ ); //绘制对话框
+ const head = core.material.images.images?.[this.head.name];
+ if (head) {
+ //绘制头像
+ ctx.drawImage(
+ head,
+ 0,
+ 0,
+ head.width,
+ head.height,
+ this.head.px,
+ 1248 - head.height * 2.2,
+ head.width * 2.2,
+ head.height * 2.2
+ );
+ }
+ if (core.isPlaying() && !this.WindowSkin) {
+ core.drawWindowSkin(
+ "winskin.webp",
+ ctx,
+ 1700,
+ 800,
+ 192,
+ 96,
+ null,
+ null,
+ null,
+ 3
+ );
+ core.fillBoldText1(
+ ctx,
+ "记 录",
+ 1736,
+ 866,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 48px Verdana"
+ );
+ core.drawWindowSkin(
+ "winskin.webp",
+ ctx,
+ 1700,
+ 1100,
+ 192,
+ 96,
+ null,
+ null,
+ null,
+ 3
+ );
+ core.fillBoldText1(
+ ctx,
+ "存 档",
+ 1736,
+ 1166,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 48px Verdana"
+ );
+ core.drawWindowSkin(
+ "winskin.webp",
+ ctx,
+ 1700,
+ 1000,
+ 192,
+ 96,
+ null,
+ null,
+ null,
+ 3
+ );
+ core.fillBoldText1(
+ ctx,
+ "▶▶",
+ 1756,
+ 1066,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 48px Verdana"
+ );
+ core.drawWindowSkin(
+ "winskin.webp",
+ ctx,
+ 1700,
+ 900,
+ 192,
+ 96,
+ null,
+ null,
+ null,
+ 3
+ );
+ let autoText = "AUTO";
+ if (auto) autoText = "STOP";
+ core.fillBoldText1(
+ ctx,
+ autoText,
+ 1722,
+ 966,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 48px Verdana"
+ );
+ }
+ if (this.name)
+ core.fillBoldText1(
+ ctx,
+ `【${this.name}】`,
+ 550,
+ 880,
+ "#FFFFFF",
+ "#000000",
+ 6,
+ "bold 48px Verdana"
+ ); //绘制名字
- if (
- this.sound &&
- core.sounds &&
- !core.getFlag("skip", false) &&
- core.musicStatus.soundStatus
- ) {
- this.beforeSound = core.playSound(this.sound, null, () => {
- if (
- this.sound &&
- auto &&
- !this.WindowSkin &&
- core.musicStatus.soundStatus
- ) {
- setTimeout(() => {
- if (auto) {
- cg.style.display = "none";
- core.ui._animateUI("hide", null, () => {
- core.doAction();
- });
- }
- }, this.wait);
- }
- });
- }
- if (this.text && !core.getFlag("skip", false)) {
- //绘制对话
- this.drawTextContent(ctx, this.text, {
- left: 550,
- top: 950,
- bold: true,
- color: "#FFFFFF",
- align: "left",
- fontSize: 48,
- time: this.time || 0,
- font: "Verdana",
- maxWidth: 1000,
- });
- }
+ if (
+ this.sound &&
+ core.sounds &&
+ !core.getFlag("skip", false) &&
+ core.musicStatus.soundStatus
+ ) {
+ this.beforeSound = core.playSound(this.sound, null, () => {
+ if (
+ this.sound &&
+ auto &&
+ !this.WindowSkin &&
+ core.musicStatus.soundStatus
+ ) {
+ setTimeout(() => {
+ if (auto) {
+ cg.style.display = "none";
+ core.ui._animateUI("hide", null, () => {
+ core.doAction();
+ });
+ }
+ }, this.wait);
+ }
+ });
+ }
+ if (this.text && !core.getFlag("skip", false)) {
+ //绘制对话
+ this.drawTextContent(ctx, this.text, {
+ left: 550,
+ top: 950,
+ bold: true,
+ color: "#FFFFFF",
+ align: "left",
+ fontSize: 48,
+ time: this.time || 0,
+ font: "Verdana",
+ maxWidth: 1000,
+ });
+ }
- ctx.restore();
- }
- }
- core.ui.cgText = new cgText();
-},
+ ctx.restore();
+ }
+ }
+ core.ui.cgText = new cgText();
+ },
"旁白": function () {
// 在此增加新插件
const over = document.createElement("canvas"); //over画布设置
@@ -18096,7 +17875,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
music.loop = "loop";
core.material.bgms[name] = music;*/
};
-
},
"横屏切换": function () {
// 在此增加新插件
@@ -18168,210 +17946,214 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
};
},
"帧动画特效(游戏界面)": function () {
- // 在此增加新插件
- const animate2 = document.createElement("canvas"); //画布设置
- animate2.style.zIndex = 91;
- animate2.id = "animate2";
- animate2.classList.add("gameCanvas", "anti-aliasing");
- animate2.style.display = "block";
- animate2.width = 416;
- animate2.height = 416;
- animate2.style.width = core.__PIXELS__ * core.domStyle.scale + "px";
- animate2.style.height = core.__PIXELS__ * core.domStyle.scale + "px";
- main.dom.animate2 = animate2;
- const anctx = animate2.getContext("2d");
-
- main.dom.gameDraw.appendChild(animate2);
-
- core.plugin.playing = new Set();
-
- this.setanimate = function (
- name,
- px,
- py,
- width,
- height,
- allFarme,
- imageList,
- soundList
- ) {
- const data = {
- px: px,
- py: py,
- width: width,
- height: height,
- allFarme: allFarme,
- imageList: imageList,
- soundList: soundList,
- };
- core.setFlag("animate_" + name, data);
- };
- this.deleteanimate = function (name) {
- core.setFlag("animate_" + name);
- };
- let thistime = 0;
- this.playanimate = function (name, x, y, hero, scalex, scaley) {
- const data = {
- name: name,
- x: x,
- y: y,
- hero: hero,
- scalex: scalex,
- scaley: scaley,
- farme: 0,
- };
-
- core.plugin.playing.add(data);
- };
- core.registerAnimationFrame("animateonmap", true, function (timestamp) {
- if (timestamp - thistime > 1000 / 60) {
- thistime = timestamp;
- core.clearMap(anctx);
- core.plugin.playing.forEach((one) => {
- const data = flags["animate_" + one.name];
- if (!data) {
- core.plugin.playing.delete(one);
- } else {
- data.imageList.forEach(function (image) {
- if (
- one.farme >= (image.beforefarme ?? 0) &&
- one.farme <= (image.afterfarme ?? data.allFarme)
- ) {
- const img = core.material.images.images?.[image.image];
- if (img) {
- const gla = image.globalAlpha ?? 100;
- const agla = image.aglobalAlpha ?? gla,
- beforefarme = image.beforefarme ?? 0;
- const afterfarme = image.afterfarme ?? data.allFarme;
-
- anctx.globalAlpha =
- (gla +
- ((agla - gla) * (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1)) /
- 100;
-
- const cx =
- (image.cx ?? 0) +
- (((image.acx ?? 0) - (image.cx ?? 0)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- cy =
- (image.cy ?? 0) +
- (((image.acy ?? 0) - (image.cy ?? 0)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- cw =
- (image.cw ?? img.width) +
- (((image.acw ?? img.width) - (image.cw ?? img.width)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- ch =
- (image.ch ?? img.height) +
- (((image.acw ?? img.height) - (image.cw ?? img.height)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- x =
- (image.x ?? 0) +
- (((image.ax ?? 0) - (image.x ?? 0)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- y =
- (image.y ?? 0) +
- (((image.ay ?? 0) - (image.y ?? 0)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- w =
- (image.w ?? one.width) +
- (((image.aw ?? one.width) - (image.w ?? one.width)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- h =
- (image.h ?? one.height) +
- (((image.aw ?? one.height) - (image.w ?? one.height)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1),
- angle =
- (Math.PI *
- ((image.image.angle ?? 0) +
- (((image.aangle ?? 0) - (image.image.angle ?? 0)) *
- (one.farme - beforefarme)) /
- (afterfarme - beforefarme || 1))) /
- 180;
-
- if (one.hero) {
- let sx, sy;
- if (core.status.heroMoving < 0) {
- sx = 0;
- sy = 0;
- } else {
- sx =
- core.utils.scan[core.status.hero.loc.direction].x *
- 4 *
- core.status.heroMoving;
- sy =
- core.utils.scan[core.status.hero.loc.direction].y *
- 4 *
- core.status.heroMoving;
- }
- const herox = core.status.hero.loc.x * 32 + 16 + sx;
- const heroy = core.status.hero.loc.y * 32 + 16 + sy;
- core.drawImage(
- anctx,
- img,
- cx,
- cy,
- cw,
- ch,
- herox + (x - data.px) * one.scalex,
- heroy + (y - data.py) * one.scaley,
- w * one.scalex,
- h * one.scaley,
- angle
- );
- } else {
- core.drawImage(
- anctx,
- img,
- cx,
- cy,
- cw,
- ch,
- one.x + (x - data.px) * one.scalex,
- one.y + (y - data.py) * one.scaley,
- w * one.scalex,
- h * one.scaley,
- angle
- );
- }
- }
- }
- });
- data.soundList.forEach(function (sound) {
- const lisen =
- sound.sound &&
- core.sounds[sound.sound] &&
- core.musicStatus.soundStatus;
- if (one.farme == sound.startfarme && lisen) {
- if (sound.stopbefore) core.stopSound();
- core.playSound(sound.sound);
- }
- });
- one.farme++;
- if (one.farme > data.allFarme) core.plugin.playing.delete(one);
- }
- });
- }
- });
- },
- "intro&loop": function () {
// 在此增加新插件
- this.introAndLoop = function (intro, time, loop) {
- core.playBgm(intro);
- setTimeout(() => {
- core.playBgm(loop);
- }, time * 1000);
+ const animate2 = document.createElement("canvas"); //画布设置
+ animate2.style.zIndex = 91;
+ animate2.id = "animate2";
+ animate2.classList.add("gameCanvas", "anti-aliasing");
+ animate2.style.display = "block";
+ animate2.width = 416;
+ animate2.height = 416;
+ animate2.style.width = core.__PIXELS__ * core.domStyle.scale + "px";
+ animate2.style.height = core.__PIXELS__ * core.domStyle.scale + "px";
+ main.dom.animate2 = animate2;
+ const anctx = animate2.getContext("2d");
+
+ main.dom.gameDraw.appendChild(animate2);
+
+ core.plugin.playing = new Set();
+
+ this.setanimate = function (
+ name,
+ px,
+ py,
+ width,
+ height,
+ allFarme,
+ imageList,
+ soundList
+ ) {
+ const data = {
+ px: px,
+ py: py,
+ width: width,
+ height: height,
+ allFarme: allFarme,
+ imageList: imageList,
+ soundList: soundList,
+ };
+ core.setFlag("animate_" + name, data);
};
+ this.deleteanimate = function (name) {
+ core.setFlag("animate_" + name);
+ };
+ let thistime = 0;
+ this.playanimate = function (name, x, y, hero, scalex, scaley, callback) {
+ const data = {
+ name: name,
+ x: x,
+ y: y,
+ hero: hero,
+ scalex: scalex,
+ scaley: scaley,
+ farme: 0,
+ callback
+ };
+
+ core.plugin.playing.add(data);
+ };
+ core.registerAnimationFrame("animateonmap", true, function (timestamp) {
+ if (timestamp - thistime > 1000 / 60) {
+ thistime = timestamp;
+ core.clearMap(anctx);
+ core.plugin.playing.forEach((one) => {
+ const data = flags["animate_" + one.name];
+ if (!data) {
+ core.plugin.playing.delete(one);
+ } else {
+ data.imageList.forEach(function (image) {
+ if (
+ one.farme >= (image.beforefarme ?? 0) &&
+ one.farme <= (image.afterfarme ?? data.allFarme)
+ ) {
+ const img = core.material.images.images?.[image.image];
+ if (img) {
+ const gla = image.globalAlpha ?? 100;
+ const agla = image.aglobalAlpha ?? gla,
+ beforefarme = image.beforefarme ?? 0;
+ const afterfarme = image.afterfarme ?? data.allFarme;
+
+ anctx.globalAlpha =
+ (gla +
+ ((agla - gla) * (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1)) /
+ 100;
+
+ const cx =
+ (image.cx ?? 0) +
+ (((image.acx ?? 0) - (image.cx ?? 0)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ cy =
+ (image.cy ?? 0) +
+ (((image.acy ?? 0) - (image.cy ?? 0)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ cw =
+ (image.cw ?? img.width) +
+ (((image.acw ?? img.width) - (image.cw ?? img.width)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ ch =
+ (image.ch ?? img.height) +
+ (((image.acw ?? img.height) - (image.cw ?? img.height)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ x =
+ (image.x ?? 0) +
+ (((image.ax ?? 0) - (image.x ?? 0)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ y =
+ (image.y ?? 0) +
+ (((image.ay ?? 0) - (image.y ?? 0)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ w =
+ (image.w ?? one.width) +
+ (((image.aw ?? one.width) - (image.w ?? one.width)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ h =
+ (image.h ?? one.height) +
+ (((image.aw ?? one.height) - (image.w ?? one.height)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1),
+ angle =
+ (Math.PI *
+ ((image.image.angle ?? 0) +
+ (((image.aangle ?? 0) - (image.image.angle ?? 0)) *
+ (one.farme - beforefarme)) /
+ (afterfarme - beforefarme || 1))) /
+ 180;
+
+ if (one.hero) {
+ let sx, sy;
+ if (core.status.heroMoving < 0) {
+ sx = 0;
+ sy = 0;
+ } else {
+ sx =
+ core.utils.scan[core.status.hero.loc.direction].x *
+ 4 *
+ core.status.heroMoving;
+ sy =
+ core.utils.scan[core.status.hero.loc.direction].y *
+ 4 *
+ core.status.heroMoving;
+ }
+ const herox = core.status.hero.loc.x * 32 + 16 + sx;
+ const heroy = core.status.hero.loc.y * 32 + 16 + sy;
+ core.drawImage(
+ anctx,
+ img,
+ cx,
+ cy,
+ cw,
+ ch,
+ herox + (x - data.px) * one.scalex,
+ heroy + (y - data.py) * one.scaley,
+ w * one.scalex,
+ h * one.scaley,
+ angle
+ );
+ } else {
+ core.drawImage(
+ anctx,
+ img,
+ cx,
+ cy,
+ cw,
+ ch,
+ one.x + (x - data.px) * one.scalex,
+ one.y + (y - data.py) * one.scaley,
+ w * one.scalex,
+ h * one.scaley,
+ angle
+ );
+ }
+ }
+ }
+ });
+ data.soundList.forEach(function (sound) {
+ const lisen =
+ sound.sound &&
+ core.sounds[sound.sound] &&
+ core.musicStatus.soundStatus;
+ if (one.farme == sound.startfarme && lisen) {
+ if (sound.stopbefore) core.stopSound();
+ core.playSound(sound.sound);
+ }
+ });
+ one.farme++;
+ 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);
+ };
+ },
"开局选项悬停": function () {
// 在此增加新插件
@@ -18728,8 +18510,13 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
oneTurn,
enemyInfo,
equipInfo,
- farme
+ 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)");
@@ -18739,46 +18526,49 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
ctx,
hero.name,
127,
- 123,
+ 128,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "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.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),
+ "生命 " +
+ core.formatBigNumber(heroInfo.hp, true) +
+ " / " +
+ core.formatBigNumber(heroInfo.hpmax, true),
90,
205,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- '攻击 ' + core.formatBigNumber(heroInfo.atk),
+ "攻击 " + core.formatBigNumber(heroInfo.atk),
90,
225,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- '防御 ' + core.formatBigNumber(heroInfo.def),
+ "防御 " + core.formatBigNumber(heroInfo.def),
90,
245,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
@@ -18787,43 +18577,51 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
265,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- "法攻 " + core.formatBigNumber(heroInfo.matk / 100 * heroInfo.spell) + "(" + heroInfo.matk + "%)",
+ "法攻 " +
+ core.formatBigNumber((heroInfo.matk / 100) * heroInfo.spell) +
+ "(" +
+ heroInfo.matk +
+ "%)",
90,
285,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- '护盾 ' + core.formatBigNumber(heroInfo.mdef / 100 * heroInfo.spell) + "(" + heroInfo.mdef + "%)",
+ "护盾 " +
+ core.formatBigNumber((heroInfo.mhp / 100) * heroInfo.spell) +
+ "(" +
+ heroInfo.mhp +
+ "%)",
90,
305,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- '法抗 ' + heroInfo.spelldef + "%",
+ "法抗 " + heroInfo.mdef + "%",
90,
325,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- '速度 ' + core.formatBigNumber(heroInfo.speed),
+ "速度 " + core.formatBigNumber(heroInfo.speed),
90,
345,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.strokeRect(ctx, 112, 139, 32, 48, "rgba(255,255,255,1)", 1);
@@ -18840,17 +18638,17 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
48
);
-
core.setTextAlign(ctx, "center");
core.fillBoldText(
ctx,
enemyInfo.name,
289,
- 123,
+ 128,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
+
core.setTextAlign(ctx, "right");
if (enemyInfo.cls === "enemys") {
core.strokeRect(ctx, 272, 155, 32, 32, "rgba(255,255,255,1)", 1);
@@ -18883,37 +18681,37 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
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.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) + ' 生命',
+ core.formatBigNumber(enemyInfo.hp, true) + " 生命",
330,
205,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- core.formatBigNumber(enemyInfo.atk) + ' 攻击',
+ core.formatBigNumber(enemyInfo.atk) + " 攻击",
330,
225,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
- core.formatBigNumber(enemyInfo.def) + ' 防御',
+ core.formatBigNumber(enemyInfo.def) + " 防御",
330,
245,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
@@ -18923,7 +18721,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
265,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
core.fillBoldText(
ctx,
@@ -18932,24 +18730,147 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
285,
"#FFFFFF",
"#000000",
- 'bold 14px pala'
+ "bold 14px pala"
);
- core.fillBoldText(ctx,
+ core.fillBoldText(
+ ctx,
"V",
219,
163,
"#FFFFFF",
"#000000",
- 'bold 48px pala')
- core.fillBoldText(ctx,
+ "bold 48px pala"
+ );
+ core.fillBoldText(
+ ctx,
"s",
231,
163,
"#FFFFFF",
"#000000",
- 'bold 36px pala')
+ "bold 36px pala"
+ );
+
+ 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()
+
+ 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()
+ console.log(heronow)
+ core.drawImage(ctx,
+ "hero.webp",
+ 0,
+ 0,
+ 32,
+ 19,
+ heronow - 16,
+ 70,
+ 32,
+ 19)
+
+ if (heroInfo.now >= 215) {
+ heroInfo.isAttack = true;
+ animateOnAttack("sword", true, () => {
+ heroInfo.now -= 215
+ heroInfo.isAttack = false
+
+ })
+
+ }
+ if (enemyInfo.now >= 215) {
+ enemyInfo.isAttack = true
+ animateOnAttack("sword", false, () => {
+ enemyInfo.now -= 215
+ enemyInfo.isAttack = false
+
+ })
+ }
+ equipInfo.forEach(v => {
+ if (v.now >= 215) {
+ 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,
@@ -18964,36 +18885,43 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
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_matk = core.getRealStatusOrDefault(hero, "matk"),
hero_mdef = core.getRealStatusOrDefault(hero, "mdef"),
hero_speed = core.getRealStatusOrDefault(hero, "speed"),
hero_hpmax = core.getRealStatusOrDefault(hero, "hpmax"),
- hero_spelldef = core.getRealStatusOrDefault(hero, "spelldef");
+ hero_mhp = core.getRealStatusOrDefault(hero, "mhp");
const heroInfo = {
hp: hero_hp,
hpmax: hero_hpmax,
atk: hero_atk,
def: hero_def,
spell: hero_spell,
- spelldef: hero_spelldef,
+ 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 *= 100;
+ oneTurn *= 25;
let time = 0,
farme = 0;
+
+
+
core.registerAnimationFrame("attackAnimate", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
@@ -19002,7 +18930,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
oneTurn,
enemyInfo,
equipInfo,
- farme
+ farme,
+ damageInfo
);
farme++;
}
@@ -19010,574 +18939,574 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
};
},
"剧情内容": function () {
- // 在此增加新插件
- // 每项为一个数组,第一项是名字,第二项是对话内容
- // 回放只会在同一个this下回放,进入剧情前请以事件块声明进入哪个剧情数组
- // 可以自由添加,但不能与已有插件及函数名相同,可以使用中文。
- this.chapter0 = [
- ["", "这些天,街道不曾下雨。"],
- ["", "所以,那浸湿地面的,定是那些女孩们流落的鲜血无疑。"],
- ["", "我蹲在充斥着铁锈味般恶臭的小巷中,悠闲地如是想着。"],
- ["", "扑哧。"],
- ["", "耳旁再次响起象征着某个女孩子死去的声音。"],
- ["", "再一次——"],
- ["", "再一次。"],
- ["", "女子们被肢解成单纯的肉块。"],
- ["", "我任由流下的血浸满全身,屏住自己的呼吸。"],
- ["", "祈求自己能拥有从猎人手中逃脱的幸运。"],
- ["", "扑哧。"],
- ["", "直到刚才,我们还坐在去往娼馆的马车的路上。"],
- ["", "而在这之中的某些人,已经不在这个世上了。"],
- ["", "不,应该把“某些”换成“几乎所有”才更为恰当吧。"],
- ["", "恐怕,不久之后我也会变成小巷中血腥的装饰品。"],
- ["", "我是为了得到这种死法,才辛苦苟活至今的吗?"],
- ["", "来个人告诉我啊——"],
- ["", "谁都好。"],
- ["", "来人啊!!"],
- ["少女", "「呃······!?」"],
- ["", "漆黑的物体充斥了我的整个视野"],
- ["", "我很快意识到,那是只很大的脚。"],
- ["", "必须要出声求救。"],
- ["", "可是,耳中却只能听到自己的牙关不停交战的声音。"],
- ["", "我是如此的无助。"],
- ["", "逃跑也好,道歉也罢。"],
- ["", "就连抬头看一眼将要杀掉我的人的面孔都做不到。"],
- ["少女", "「······被杀」"],
- ["", "会被杀。"],
- ["", "会被杀!!"],
- ["", "来自内心深处的冰冷预感,渐渐地在体内蔓延开来。"],
- ["少女", "「不,不要······」"],
- ["", "浮游都市,《诺瓦斯·艾蒂尔》。"],
- ["", "《特别受灾地区》——"],
- ["", "通称,《牢狱》"],
- ["", "是被险峻的峭壁环绕,与世隔绝的,都市的最底部。"],
- ["年轻人", "「放开我!」"],
- ["年轻人", "「我只是在帮那个女人而已!」"],
- ["年轻人", "「你们没听到吗!?」"],
- ["年轻人", "「她是被受骗才会被卖到娼馆来的」"],
- ["年轻人", "「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」"],
- ["年轻人", "「给我说些什么啊」"],
- ["凯伊姆", "「这些话等到了娼馆再说吧」"],
- ["凯伊姆", "「我来抓你,只是受雇于人而已」"],
- ["", "我走进娼馆《莉莉乌姆》的接待室。"],
- ["", "正在桌旁整理账簿的奥兹停下手头的工作,抬起头向我看来。"],
- ["奥兹", "「这不是凯伊姆先生吗,辛苦了」"],
- ["奥兹", "「委托已经完成了吗?」"],
- ["凯伊姆", "「啊啊,是这家伙没错吧」"],
- ["", "奥兹用只要接触到就能杀人般的眼神在男人脸上搜过。"],
- ["奥兹", "「没错,就是这个人」"],
- ["凯伊姆", "「是么」"],
- ["年轻人", "「你,你们要对我做什么」"],
- ["奥兹", "「······」"],
- ["", "奥兹用一个眼神,就让男人闭上了嘴。"],
- ["", "然后,向我这边转过身来。"],
- ["奥兹", "「抱歉啊,总是麻烦你去做这些无聊的事」"],
- ["奥兹", "「都怪我们这边的年轻人太没用」"],
- ["凯伊姆", "「客套话就免了」"],
- ["奥兹", "「这还真是失礼了」"],
- ["奥兹", "「喂,来个人」"],
- ["光头男人", "「是」"],
- ["奥兹", "「凯伊姆先生做完工作回来了」"],
- ["光头男人", "「是,是,那个······」"],
- ["奥兹", "「我是要你拿些酒来,这个蠢材!」"],
- ["", "喀!"],
- ["", "奥兹扔出的烟灰缸砸中了手下的额头。"],
- ["", "鲜血四溅。"],
- ["凯伊姆", "「不用这么麻烦」"],
- ["凯伊姆", "「我接下来要去《菲诺列塔》」"],
- ["奥兹", "「喔唷」"],
- ["奥兹", "「既然如此,我就不留您在这里喝难饮的劣质酒了」"],
- ["", "奥兹斜眼看着正捂住额头呻吟的手下,轻描淡写地说道。"],
- ["凯伊姆", "「用这些钱去买药」"],
- ["", "我将几枚铜钱仍在那个手下的身前。"],
- ["奥兹", "「凯伊姆先生,不用对他们这么好」"],
- ["凯伊姆", "「无妨」"],
- ["凯伊姆", "「话说回来,那个要落跑的女人呢?」"],
- ["奥兹", "「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」"],
- ["奥兹", "「正好,趁此机会凯伊姆先生也来享受一番如何?」"],
- ["年轻人", "「你,你们这些家伙,要对她做什么!?」"],
- ["", "咣!"],
- ["", "奥兹给了他一拳。"],
- ["", "一击即倒。"],
- ["", "喀,咚,咯!"],
- ["", "奥兹毫不留情地向男人的脸上踩去。"],
- ["年轻人", "「咕······呃咳······」"],
- ["", "折断的牙齿伴着血泡被吐出。"],
- ["", "这份白色在鲜红色的液体中格外显眼。"],
- ["年轻人", "「你们以为做出这种事······卫兵会坐视不理吗······」"],
- ["奥兹", "「啊啊,不会坐视不理的」"],
- ["奥兹", "「应该会拿出你的钱包,和我们商量如何瓜分吧」"],
- ["年轻人", "「那,那种事······」"],
- ["", "这在牢狱是理所当然的事。"],
- ["奥兹", "「怎么,头一回来牢狱么?」"],
- ["", "男人点了点头。"],
- ["奥兹", "「为了被骗的女人而来到牢狱,真是个规矩人啊」"],
- ["奥兹", "「······前提是,被骗的人不是你」"],
- ["年轻人", "「你说······我被骗了?」"],
- ["年轻人", "「那,那是怎么回事!?」"],
- ["奥兹", "「不用急,今天晚上会好好告诉你的」"],
- ["", "奥兹抓起男人的脸。"],
- ["", "为引诱客人的怜悯之心而装纯,是娼妇的惯用手段。"],
- ["", "双亲被骗而借钱,结果作为抵押而将自己卖到这里,这是典型的说法。"],
- [
- "",
- "如果只是头脑发热而成为常客也就罢了,这次的男人热血过头,居然想出了要带女人私奔的计划。",
- ],
- [
- "",
- "虽然女人半开玩笑地予以拒绝,但不知天高地厚的这家伙还是拉着她逃跑了。",
- ],
- ["", "不过,想要逃脱追击本来就是不可能的任务。"],
- ["", "但即便如此,这种事情还是会一再的出现。"],
- ["", "说谎的女人和被骗的男人。"],
- ["", "在娼馆街,这是令人看到生厌的日常的风景。"],
- ["凯伊姆", "「我要走了」"],
- ["奥兹", "「好的,下次再麻烦您」"],
- ["奥兹", "「之后吉克先生会将谢礼交给您的」"],
- ["凯伊姆", "「啊啊」"],
- ["", "我背向奥兹走出娼馆。"],
- ["凯伊姆", "「······?」"],
- ["", "从远方传来微弱的歌声。"],
- ["", "是关卡广场的方向。"],
- ["", "对了。"],
- ["", "今天有觐见圣女的仪式。"],
- ["", "当代的圣女伊莲——"],
- ["", "俗称《盲眼之圣女》,据说即使在历代的圣女中,人气也是数一数二的。"],
- ["", "广场上的人估计相当多吧。"],
- ["", "虽然我也想去看看她长什么样,不过要在人潮中挤来挤去就免了。"],
- ["", "还是老老实实去菲诺列塔喝烧酒吧。"],
- ["", "正当我这样想着的时候,一个身影自小巷的那头走来。"],
- ["凯伊姆", "「艾莉斯」"],
- ];
- this.chapter01 = [
- ["艾莉斯", "「啊,凯伊姆」"],
- ["艾莉斯", "「正好,我还想要去找你呢」"],
- ["艾莉斯", "「没想到凯伊姆会主动出现······这是命运吗?」"],
- ["凯伊姆", "「显然不是吧」"],
- ["艾莉斯", "「啊,是么」"],
- ["", "艾莉斯挑了挑整齐的双眉,微微地哼了一声。"],
- [
- "",
- "虽然是个相当引人注目的美人,但她这个将亲切儿子丢入无底深渊的性格,为自己扣了不少的分",
- ],
- ["", "给人印象最深的,就是那潭水般的双瞳。"],
- ["", "在漆黑的瞳孔中,完全看不出感情的波动。"],
- ["艾莉斯", "「喜欢我的眼睛吗?」"],
- ["艾莉斯", "「如果想要的话就给你吧?」"],
- ["凯伊姆", "「用不着」"],
- ["艾莉斯", "「阿拉,可惜」"],
- ["凯伊姆", "「那么,找我有什么事」"],
- ["艾莉斯", "「梅尔特的钱好像被偷了」"],
- ["凯伊姆", "「钱被偷了?都几岁了还这么没用」"],
- ["艾莉斯", "「不要对我说啊」"],
- ["凯伊姆", "「那家伙,该不会说要让我去抓那个小偷吧?」"],
- ["艾莉斯", "「就是这样」"],
- ["凯伊姆", "「笨蛋吗」"],
- ["凯伊姆", "「如果是小钱的话,就当做是买个教训吧」"],
- ["艾莉斯", "「说起来,被盗的是这个月的上纳金」"],
- ["凯伊姆", "「你说什么?」"],
- ["艾莉斯", "「用这些钱买教训,也太过奢侈了呢」"],
- ["凯伊姆", "「知道了,我去找」"],
- ["凯伊姆", "「小偷的特征呢」"],
- ["艾莉斯", "「男孩子」"],
- ["艾莉斯", "「······而且,背后有翅膀」"],
- ["艾莉斯", "「虽然姑且是藏在身后,但是仔细观察的话是很明显的」"],
- ["凯伊姆", "「羽化病吗」"],
- [
- "艾莉斯",
- "「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」",
- ],
- ["凯伊姆", "「注意到他逃窜的方向了吗?」"],
- ["艾莉斯", "「广场那边」"],
- ["艾莉斯", "「虽然刚才《不蚀金锁》的人去追了,不过多半是······」"],
- ["凯伊姆", "「偏偏还是广场吗」"],
- ["艾莉斯", "「今天是觐见圣女大人的日子」"],
- ["凯伊姆", "「我知道」"],
- ["凯伊姆", "「尽量找找看就好」"],
- ];
- this.chapter02 = [
- ["不蚀金锁成员", "「凯伊姆先生,凯伊姆先生」"],
- ["不蚀金锁成员", "「您已经和艾莉斯大夫见过面了吗?」"],
- ["凯伊姆", "「啊啊,所以才会追过来的」"],
- ["凯伊姆", "「看到小偷了吗?」"],
- [
- "不蚀金锁成员",
- "「没有,他向广场那边逃了过去,今天这么拥挤,我们也只能放弃了」",
- ],
- [
- "不蚀金锁成员",
- "「不过,我也只是刚好在店里所以才追了过去,并不是受人所托」",
- ],
- ["不蚀金锁成员", "「我已经准备撤退了」"],
- ["不蚀金锁成员", "「凯伊姆先生还要继续追吗?」"],
- ["凯伊姆", "「啊啊」"],
- ["", "做完情报交换之后,我跟男人道别。"],
- ["凯伊姆", "「和我想的一样啊······」"],
- ["", "在牢狱中最大的广场上,聚集着看不到尽头的人群。"],
- ["", "就算是来参见圣女祈祷,这人数也太多了点吧。"],
- ["", "自然,我也找不到逃跑的孩子。"],
- ["", "是混杂到人群中了吧。"],
- ["", "如果已经从广场上脱身了的话,就更难发现了。"],
- ["", "只好赌他还在这里了。"],
- ["", "我先移动到了一个视野良好的地方。"],
- ["", "从这里,一眼就可以看到人群的变化。"],
- ["", "广场还是沸腾起来。"],
- ["", "抬头望去,原来是在天台之上出现了一个人影"],
- ["", "但是,与周围的期待不同,现身的是一名中年的神官。"],
- ["", "骂声四溢。"],
- ["", "神官则是笑着摆正衣领"],
- ["神官", "「从现在开始,举行谒见的仪式」"],
- [
- "神官",
- "「在参见那位大人之前,我希望牢狱的诸位再次思考这个《诺瓦斯·艾蒂尔》存在的意义······」",
- ],
- [
- "神官",
- "「初代圣女伊莲大人,便是也难怪这崇高的祈祷之力,令《诺瓦斯·艾蒂尔》浮在空中,拯救了我们的祖先」",
- ],
- [
- "神官",
- "「这之后的几百年来,传承了初代大人力量的历代圣女伊莲大人,让这里留在了空中」",
- ],
- [
- "神官",
- "「这座都市是被圣女大人守护的人类最后的圣域,而我们则是被选召的虔诚的信徒」",
- ],
- ["神官", "「怀着对圣女的感激祈祷吧,感谢圣女伊莲吧!并献上祈祷!」"],
- ["圣女", "「不忘感谢与祈祷,神才会拯救我们」"],
- ["圣女", "「与我一起,向神虔诚地祈祷吧」"],
- ["", "广场上欢声雷动。"],
- ["", "圣女没有回应喧嚣的人声,而是静静地合上双眼面向广场。"],
- ["", "虽然感觉有些冷淡,但总比像个傻瓜似的笑着向这群人挥手要强。"],
- ["", "她掌握着这条街道,还有在这条街上生活的人的命运。"],
- ["", "比起揽得人气,她更想要为了街道的继续存在而献出全力。"],
- ["", "也是为了不让《大崩落》的惨剧再度发生。"],
- ["", "十几年前的那场悲剧。"],
- [
- "",
- "虽然在我脑海中的记忆已经相当模糊,但哪怕只是稍有触及,不快的感觉都会在胸口蔓延开。",
- ],
- ["凯伊姆", "「······」"],
- ["", "这时我才想起,现在不是我在这里看圣女的时候。"],
- ["女声", "「——っ!?」"],
- ["围观的女人", "「羽,羽化病人!?」"],
- ["围观的中年人", "「喂,谁去叫下羽狩」"],
- ["惊慌的观众", "「你这家伙不要靠近我,要是传染了可怎么办」"],
- ["粗鲁的观众", "「你这小鬼赶快滚开」"],
- ["凯伊姆", "「接下来」"],
- ["圣女", "「发生什么事了?看上去似乎很嘈杂」"],
- ["随从", "「似乎是某个人逃跑了······具体的我也不是很清楚」"],
- ["神官", "「圣女大人,继续待在天台上可能会出事,请您先回到室内吧」"],
- ["圣女", "「不用在意我,比起那个,我更关心究竟发生了什么事」"],
- ["神官", "「对不起,我真的不知道」"],
- ["圣女", "「······是吗」"],
- ["男", "「恕我僭越,请准许我说明情况」"],
- ["男", "「在来觐见的人群中出现了《羽化病》的患者」"],
- ["男", "「围观的人群因而产生了骚动」"],
- [
- "男",
- "「现在,《防疫局》已经派遣了部队。我想不久之后,他们就会安静下来了」",
- ],
- ["圣女", "「羽化病······」"],
- ["男", "「怎么了?」"],
- ["圣女", "「没什么」"],
- ["圣女", "「辛苦了,你的名字是?」"],
- ["男", "「属下是在防疫局任职的,鲁基乌斯· 迪斯·米利尤」"],
- ["神官", "「噢噢,阁下就是鲁基乌斯卿吗,我听说过你的传闻」"],
- ["神官", "「阁下是在工作上相当出色的人呢」"],
- ["鲁基乌斯", "「不敢当」"],
- [
- "鲁基乌斯",
- "「话说回来,这次是属下警备工作的失职。让圣女大人见到这不成体统的一面,请您见谅」",
- ],
- [
- "圣女",
- "「即使是目不见物的我,也能感受到聚集于此的民众数量之多。警备工作难以展开也在情理之中」",
- ],
- ["鲁基乌斯", "「属下不胜惶恐」"],
- ["鲁基乌斯", "「接下来属下还要回到工作岗位上,在这里就先告退了」"],
- ["圣女", "「鲁基乌斯先生」"],
- ["鲁基乌斯", "「属下在」"],
- ["圣女", "「你是怎样看待羽狩的工作的呢?」"],
- ["神官", "「圣,圣女大人」"],
- [
- "鲁基乌斯",
- "「防疫局的工作是国王陛下赐予的重要职务。属下非常荣幸能够为这个都市的繁荣尽一份微薄之力」",
- ],
- ["神官", "「不,不亏是鲁基乌斯卿,相当优秀的想法」"],
- ["圣女", "「是吗。辛苦你了」"],
- ["随从", "「圣女大人······」"],
- ["鲁基乌斯", "「······」"],
- ["鲁基乌斯", "「那么,属下就回岗位去了」"],
- ];
- this.chapter03 = [
- ["", "从羽化病的少年纷乱的足音中,可以听得出相当的疲劳。"],
- ["", "显然,他并没有想到我会捷足先登吧。"],
- ["", "少年惶恐地回头看了一眼后,微微露出安心的表情,双手拄在膝盖上。。"],
- ["凯伊姆", "「辛苦你了」"],
- ["羽化病患少年", "「稀!?」"],
- ["凯伊姆", "「逃到贫民区是个不错的想法」"],
- ["羽化病患少年", "「你,你是,羽狩吗?」"],
- ["凯伊姆", "「不是」"],
- ["羽化病患少年", "「什,什么啊······混蛋,不要吓我啊」"],
- ["凯伊姆", "「我对令你受惊这件事致以歉意」"],
- ["凯伊姆", "「作为回报,麻烦你把从店里偷的钱交出来吧」"],
- ["羽化病患少年", "「钱?你在说什么」"],
- ["凯伊姆", "「你要找的腰上的东西,掉在你身后了」"],
- ["羽化病患少年", "「哎?」"],
- ["羽化病患少年", "「呃呀」"],
- ["羽化病患少年", "「你······你这混蛋」"],
- ["凯伊姆", "「······」"],
- ["凯伊姆", "「把偷的钱交出来」"],
- ["羽化病患少年", "「我不知道你在······咕」"],
- ["羽化病患少年", "「你,你说是我偷的······有什么证据吗」"],
- ["凯伊姆", "「你还挺倔的啊」"],
- ["凯伊姆", "「不过,给我听好了」"],
- ["凯伊姆", "「你偷的那些钱,是要上缴给《不蚀金锁》的上纳金」"],
- ["凯伊姆", "「而且,钱的主人是从前和吉克颇有渊源的女人」"],
- ["羽化病患少年", "「吉克?」"],
- ["凯伊姆", "「他是《不蚀金锁》的主人,这么说你就明白了吧」"],
- ["羽化病患少年", "「哎?哎?怎么会······」"],
- ["凯伊姆", "「再问你一遍,钱在哪里?」"],
- ["羽化病患少年", "「是,是,是,在我的怀里」"],
- ["凯伊姆", "「你没有擅自拿掉一部分吧」"],
- ["羽化病患少年", "「是,是的」"],
- ["羽化病患少年", "「那,那个,您是《不蚀金锁》的人吗?」"],
- ["凯伊姆", "「算是吧」"],
- ["羽化病患少年", "「我什么都可以做,请您一定要帮帮我」"],
- ["凯伊姆", "「抱歉,我并没有兴趣去帮助他人」"],
- ["羽化病患少年", "「我什么······什么,都会做的······」"],
- ["羽化病患少年", "「我一直都是生活在下层的」"],
- [
- "羽化病患少年",
- "「可是,不知何时染上了羽化病······背后长出了翅膀······」",
- ],
- ["羽化病患少年", "「被寄宿工作的店赶了出来,只得流落到牢狱这里」"],
- ["羽化病患少年", "「因为独自实在是饿的不行了,所以才会偷这些钱的」"],
- [
- "羽化病患少年",
- "「我明明没有做任何坏事······为什么······会遇到这种事······」",
- ],
- ["凯伊姆", "「谁知道」"],
- ["羽化病患少年", "「呜······呜呜······接下来,要对我做什么?」"],
- ["凯伊姆", "「我要把你带到组织那里」"],
- ["羽化病患少年", "「怎,怎么这样」"],
- ["凯伊姆", "「不过,那样做的前提是你不是羽化病人」"],
- ["凯伊姆", "「组织也没有笨到把羽化病人招待到家里的程度」"],
- ["羽化病患少年", "「那么,是要放我逃走吗?」"],
- ["凯伊姆", "「我要让你学到教训」"],
- ["凯伊姆", "「如果换做是组织的制裁,至少要有断条胳膊的觉悟」"],
- ["凯伊姆", "「你的运气不错」"],
- ["羽化病患少年", "「唔······啊,是的······」"],
- ["凯伊姆", "「滚」"],
- ["羽化病患少年", "「非常感谢」"],
- ["羽化病患少年", "「唔啊!?」"],
- ["男", "「到这里就结束了,羽化病人」"],
- ["男", "「确认他的翅膀」"],
- ["", "趁还没有被卷入麻烦的事情之前,赶快离开这里吧"],
- ["羽狩的队长", "「那边的那个人」"],
- ["凯伊姆", "「······有什么事?」"],
- ["羽狩的队长", "「可以稍微让我问几句话吗」"],
- ["凯伊姆", "「······」"],
- ["凯伊姆", "「啊啊,无妨」"],
- ["羽狩的队长", "「感谢您的合作」"],
- ["", "队长殷勤地致以谢礼。"],
- ["", "而在他的眼前,少年的衣服已经被他的补下们扯破。"],
- ["", "在瘦骨嶙峋的裸露后背上,长有纯白的羽翼。"],
- ["红发的羽狩", "「副队长,确认翅膀的持有了」"],
- ["羽狩的副队长", "「保护他」"],
- ["羽化病患少年", "「不要······请原谅,我······」"],
- [
- "羽狩的副队长",
- "「我们只是要带你去治愈院治疗羽化病,不是什么应该感到害怕的事情」",
- ],
- ["羽化病患少年", "「可是,可是」"],
- ["羽狩的副队长", "「没关系的」"],
- ["羽化病患少年", "「······哥,哥哥」"],
- ["羽狩的副队长", "「你是羽化病人的亲属吗?」"],
- ["凯伊姆", "「只是路人而已」"],
- ["凯伊姆", "「顺带一提,我也没有打算找你们的麻烦」"],
- [
- "羽狩的副队长",
- "「前几天,有个和你说了同样的话的人,在我们背向他的瞬间对我们发动了袭击」",
- ],
- ["羽狩的副队长", "「我的一个部下就此永久失去了半截胳膊」"],
- ["凯伊姆", "「我表示同情」"],
- ["凯伊姆", "「我马上就会消失的,这样就没问题了吧?」"],
- ["羽狩的副队长", "「嘛,不要这么慌张」"],
- ["", "副队长看着羽化的少年。"],
- ["羽狩的副队长", "「你与这个人是什么关系?没有被他殴打吗?」"],
- ["羽化病患少年", "「没,没有」"],
- [
- "羽狩的副队长",
- "「如何对我们保持合作,你就可以在治愈院得到优先的治疗」",
- ],
- ["羽化病患少年", "「······」"],
- ["羽化病患少年", "「那个人,是《不蚀金锁》的组织成员······」"],
- ["羽化病患少年", "「突然说让我拿出钱来,我刚一拒绝他就打我」"],
- ["羽狩的副队长", "「原来如此······」"],
- [
- "羽狩的副队长",
- "「那位少年说你是《不蚀金锁》的一员,不知此事是否属实?」",
- ],
- ["凯伊姆", "「当然不是」"],
- ["凯伊姆", "「我只是从那里接受工作而已,并不是他们的成员」"],
- ["羽狩的副队长", "「你的意思是说,少年在说谎吗?」"],
- ["凯伊姆", "「啊啊」"],
- [
- "凯伊姆",
- "「如果你们和组织有关系的话,只要问问我是不是那里的成员就能明白事实了吧」",
- ],
- ["羽狩的副队长", "「就算我去询问,也无法从他们那里得到事实」"],
- [
- "羽狩的副队长",
- "「《不蚀金锁》的那些人一向都不对我们合作,我对此深感困扰」",
- ],
- ["凯伊姆", "「真是辛苦啊」"],
- ["羽狩的副队长", "「说的是啊」"],
- ["羽狩的副队长", "「其实,砍下我部下胳膊的似乎也是组织的成员呢」"],
- [
- "羽狩的副队长",
- "「无需如此警戒,我只是想在看守所向你咨询一些事情而已」",
- ],
- [
- "羽狩的副队长",
- "「如果能够知晓牢狱与组织的事情,我们也可以尽可能地对更多的羽化病人进行保护」",
- ],
- ["羽狩的副队长", "「那和整条街道的和平也是紧密相关的吧?」"],
- ["凯伊姆", "「我知道,你们有逮捕干扰狩猎羽化病人的权力」"],
- [
- "凯伊姆",
- "「但是,我没有对你们做出任何干扰,为什么要对我如此纠缠不休呢」",
- ],
- ["羽狩的副队长", "「那些话,我们会在看守所对你详细说明的」"],
- ["凯伊姆", "「······」"],
- ["", "在这里起争执的话,就会被羽狩加害。"],
- ["", "就算能从这里脱身,今后只要碰面就会产生纠纷也是摆明的事情。"],
- ["", "就算逃跑,也没有好的结果。"],
- ["", "正当我想要打圆场的时候,刚才的气氛一瞬间产生了转变。"],
- ["", "发生了什么事······"],
- ["???", "「我认为,那位先生是正确的」"],
- ["", "羽狩们一起回头。"],
- ["", "而在他们视线的焦点处,"],
- ["", "伫立着一位女性。"],
- ["", "在端正的容颜下,代表着强烈意志的双眉十分显眼。"],
- ["", "身体的柔软与紧紧包裹在其身上的羽狩制服,两者显得十分的不搭配。"],
- ["", "我还是第一次看到女性的羽狩。"],
- ["羽狩的副队长", "「队长,这是获得《不蚀金锁》情报的好机会」"],
- [
- "羽狩的队长",
- "「兰格副队长,就算是为了获得情报,也不能做出恫吓的发言啊」",
- ],
- ["兰格副队长", "「我并没有打算去恫吓他······」"],
- ["羽狩的队长", "「告诉我那个被砍掉胳膊的队员的名字」"],
- ["羽狩的队长", "「我会去探望他的」"],
- ["兰格副队长", "「那个是······」"],
- ["羽狩的队长", "「我知道,你一直在为有所收获而努力工作」"],
- ["羽狩的队长", "「但是,正因为我们的工作是为民众提供帮助」"],
- ["羽狩的队长", "「所以就更不能损害人与人之间的信赖」"],
- ["兰格副队长", "「我会铭记在心」"],
- ["羽狩的队长", "「这位先生,我的部下失礼了」"],
- ["凯伊姆", "「只要不对我再来一次就好」"],
- ["羽狩的队长", "「请稍等」"],
- ["凯伊姆", "「有什么事?」"],
- ["羽狩的队长", "「我想确认一件事」"],
- ["羽狩的队长", "「你真的不是《不蚀金锁》的成员吗?」"],
- ["凯伊姆", "「真的」"],
- ["凯伊姆", "「如果我说是的话,你有什么打算?」"],
- ["羽狩的队长", "「我听过传闻,说他们是用依靠暴力而得的钱在生活」"],
- ["凯伊姆", "「······这样啊」"],
- ["凯伊姆", "「如果能有收获就好了啊」"],
- ];
- this.chapter04 = [
- ["梅尔特", "「欢迎光临」"],
- ["梅尔特", "「辛苦了」"],
- ["梅尔特", "「抱歉,又拜托给你了个这么麻烦的工作」"],
- ["凯伊姆", "「没什么,比想象中完成的更容易」"],
- ["梅尔特", "「那就好」"],
- ["梅尔特", "「这是我的一点谢意」"],
- ["凯伊姆", "「味道有些变化啊」"],
- ["梅尔特", "「啊,被发现了?」"],
- ["梅尔特", "「最近,没能到手什么好的原料呢」"],
- ["凯伊姆", "「去拜托吉克如何?」"],
- [
- "梅尔特",
- "「话是这么说,但是总不能用店里采购的这种小事去麻烦他吧······」",
- ],
- ["凯伊姆", "「那希望你也不要来麻烦我」"],
- ["梅尔特", "「那 是 两 码 事」"],
- ["梅尔特", "「再说,凯伊姆是靠着工作来生活的吧」"],
- [
- "梅尔特",
- "「而且,自己的钱被偷了这么害羞的事,向凯伊姆意外的其他人都说不出口」",
- ],
- ["凯伊姆", "「反正,也已经传到吉克的耳朵里了」"],
- ["梅尔特", "「这是面子问题啊,面子问题」"],
- ["凯伊姆", "「嘛,算了」"],
- ["凯伊姆", "「这样就好了吧?」"],
- ["梅尔特", "「这是钱包呢」"],
- ["梅尔特", "「嗯,东西没少」"],
- ["梅尔特", "「太好啦—这个月的上纳金,我可全部都放在里面了呢」"],
- ["梅尔特", "「如果没有找到的话,说不定就又会被送到娼馆里了呢」"],
- ["凯伊姆", "「在那边不是来钱更快吗?」"],
- ["梅尔特", "「阿拉,你是在说我还能有魅力吗?」"],
- ["凯伊姆", "「这是客套话而已」"],
- ["梅尔特", "「欺负人」"],
- ["梅尔特", "「总而言之,今天帮大忙了」"],
- ["梅尔特", "「谢礼嘛······」"],
- ["凯伊姆", "「就记在账单上吧」"],
- ["梅尔特", "「了解—盛谢惠顾了哦?」"],
- ["", "喀啷喀啷"],
- ["", "门铃响起"],
- ["", "喧哗瞬间安静下来。"],
- ["", "进来的人是吉克。"],
- ["", "是掌控着牢狱的组织之一,《不蚀金锁》的头目。"],
- ["", "不仅组织的成员,就连店内一般的客人也对他以注目礼表示敬意。"],
- ["吉克", "「各位继续吧」"],
- ["", "仿佛停滞的时钟重新转动了一般,店内恢复了热闹的气氛。"],
- ["吉克", "「抱歉,今天拜托你去做了无聊的工作」。"],
- ["凯伊姆", "「不用介意」"],
- ["", "吉克轻轻点了点头,在我右边坐了下来"],
- ["凯伊姆", "「逃跑的男人怎么样了?」"],
- ["吉克", "「嗯?已经不在这个世上了」。"],
- ["吉克", "「有什么想要知道的事吗?」"],
- ["凯伊姆", "「不,没什么」"],
- ["吉克", "「那个无聊的家伙,完全没有趣味呢」"],
- ["吉克", "「真希望他也替我负责清扫的部下也考虑考虑」"],
- ["凯伊姆", "「真是灾难啊」"],
- ["吉克", "「比起那个,我听说了哦。你去追羽化病人了啊」"],
- ["凯伊姆", "「消息真灵通」"],
- ["吉克", "「梅尔特也注意点」"],
- ["吉克", "「你丢钱已经不是一回两回了」"],
- ["梅尔特", "「好的—我会注意的。」"],
- ["梅尔特", "「吉克还是平常的点单吧」"],
- ["梅尔特", "「凯伊姆要再来一杯吗?」"],
- ["", "我们用眼神点头示意后,梅尔特开始准备起酒来。"],
- ["", "悠然地吐出眼圈后,吉克取出一个纸包放在柜台上。"],
- ["吉克", "「这是抓捕逃跑男人的报酬」"],
- ["凯伊姆", "「下次有什么事再告诉我」"],
- ["梅尔特", "「来,久等了」"],
- ["凯伊姆", "「话说回来梅尔特,为什么会被那种孩子偷到钱?」"],
- ["吉克", "「让我猜猜看」"],
- ["吉克", "「是那个吧,看某个特立独行的男人入迷了,所以就有了空隙?」"],
- ["梅尔特", "「可惜—」"],
- ["梅尔特", "「事实恰恰相反,是那家伙一直在纠缠我」"],
- ["凯伊姆", "「完全把你当成新进的女佣了么」"],
- ["梅尔特", "「我从前可是很有名的,不会被当成这种下人吧」"],
- ["梅尔特", "「······而且,我没法对对我这么钟情的人发火啊」"],
- ["凯伊姆&吉克", "「你傻啊」"],
- ["梅尔特", "「异口同声呢,不亏是兄弟」"],
- ["凯伊姆", "「别用这种称呼,怪恶心的」"],
- ["吉克", "「说得没错」"],
- ["吉克", "「······说起来······」"],
- ["梅尔特", "「怎么了?」"],
- ["吉克", "「有件事我一直很在意,我和凯伊姆,哪个是哥哥啊?」"],
- ["凯伊姆", "「你也说这么无聊的话题」"],
- ["吉克", "「不,这是很重要的事情」"],
- ["吉克", "「梅尔特,事实是怎么样的?」"],
- ["梅尔特", "「啊~是怎么样的呢~」"],
- ["梅尔特", "「我忘记了」"],
- ["吉克", "「骗人」"],
- ];
-}
+ // 在此增加新插件
+ // 每项为一个数组,第一项是名字,第二项是对话内容
+ // 回放只会在同一个this下回放,进入剧情前请以事件块声明进入哪个剧情数组
+ // 可以自由添加,但不能与已有插件及函数名相同,可以使用中文。
+ this.chapter0 = [
+ ["", "这些天,街道不曾下雨。"],
+ ["", "所以,那浸湿地面的,定是那些女孩们流落的鲜血无疑。"],
+ ["", "我蹲在充斥着铁锈味般恶臭的小巷中,悠闲地如是想着。"],
+ ["", "扑哧。"],
+ ["", "耳旁再次响起象征着某个女孩子死去的声音。"],
+ ["", "再一次——"],
+ ["", "再一次。"],
+ ["", "女子们被肢解成单纯的肉块。"],
+ ["", "我任由流下的血浸满全身,屏住自己的呼吸。"],
+ ["", "祈求自己能拥有从猎人手中逃脱的幸运。"],
+ ["", "扑哧。"],
+ ["", "直到刚才,我们还坐在去往娼馆的马车的路上。"],
+ ["", "而在这之中的某些人,已经不在这个世上了。"],
+ ["", "不,应该把“某些”换成“几乎所有”才更为恰当吧。"],
+ ["", "恐怕,不久之后我也会变成小巷中血腥的装饰品。"],
+ ["", "我是为了得到这种死法,才辛苦苟活至今的吗?"],
+ ["", "来个人告诉我啊——"],
+ ["", "谁都好。"],
+ ["", "来人啊!!"],
+ ["少女", "「呃······!?」"],
+ ["", "漆黑的物体充斥了我的整个视野"],
+ ["", "我很快意识到,那是只很大的脚。"],
+ ["", "必须要出声求救。"],
+ ["", "可是,耳中却只能听到自己的牙关不停交战的声音。"],
+ ["", "我是如此的无助。"],
+ ["", "逃跑也好,道歉也罢。"],
+ ["", "就连抬头看一眼将要杀掉我的人的面孔都做不到。"],
+ ["少女", "「······被杀」"],
+ ["", "会被杀。"],
+ ["", "会被杀!!"],
+ ["", "来自内心深处的冰冷预感,渐渐地在体内蔓延开来。"],
+ ["少女", "「不,不要······」"],
+ ["", "浮游都市,《诺瓦斯·艾蒂尔》。"],
+ ["", "《特别受灾地区》——"],
+ ["", "通称,《牢狱》"],
+ ["", "是被险峻的峭壁环绕,与世隔绝的,都市的最底部。"],
+ ["年轻人", "「放开我!」"],
+ ["年轻人", "「我只是在帮那个女人而已!」"],
+ ["年轻人", "「你们没听到吗!?」"],
+ ["年轻人", "「她是被受骗才会被卖到娼馆来的」"],
+ ["年轻人", "「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」"],
+ ["年轻人", "「给我说些什么啊」"],
+ ["凯伊姆", "「这些话等到了娼馆再说吧」"],
+ ["凯伊姆", "「我来抓你,只是受雇于人而已」"],
+ ["", "我走进娼馆《莉莉乌姆》的接待室。"],
+ ["", "正在桌旁整理账簿的奥兹停下手头的工作,抬起头向我看来。"],
+ ["奥兹", "「这不是凯伊姆先生吗,辛苦了」"],
+ ["奥兹", "「委托已经完成了吗?」"],
+ ["凯伊姆", "「啊啊,是这家伙没错吧」"],
+ ["", "奥兹用只要接触到就能杀人般的眼神在男人脸上搜过。"],
+ ["奥兹", "「没错,就是这个人」"],
+ ["凯伊姆", "「是么」"],
+ ["年轻人", "「你,你们要对我做什么」"],
+ ["奥兹", "「······」"],
+ ["", "奥兹用一个眼神,就让男人闭上了嘴。"],
+ ["", "然后,向我这边转过身来。"],
+ ["奥兹", "「抱歉啊,总是麻烦你去做这些无聊的事」"],
+ ["奥兹", "「都怪我们这边的年轻人太没用」"],
+ ["凯伊姆", "「客套话就免了」"],
+ ["奥兹", "「这还真是失礼了」"],
+ ["奥兹", "「喂,来个人」"],
+ ["光头男人", "「是」"],
+ ["奥兹", "「凯伊姆先生做完工作回来了」"],
+ ["光头男人", "「是,是,那个······」"],
+ ["奥兹", "「我是要你拿些酒来,这个蠢材!」"],
+ ["", "喀!"],
+ ["", "奥兹扔出的烟灰缸砸中了手下的额头。"],
+ ["", "鲜血四溅。"],
+ ["凯伊姆", "「不用这么麻烦」"],
+ ["凯伊姆", "「我接下来要去《菲诺列塔》」"],
+ ["奥兹", "「喔唷」"],
+ ["奥兹", "「既然如此,我就不留您在这里喝难饮的劣质酒了」"],
+ ["", "奥兹斜眼看着正捂住额头呻吟的手下,轻描淡写地说道。"],
+ ["凯伊姆", "「用这些钱去买药」"],
+ ["", "我将几枚铜钱仍在那个手下的身前。"],
+ ["奥兹", "「凯伊姆先生,不用对他们这么好」"],
+ ["凯伊姆", "「无妨」"],
+ ["凯伊姆", "「话说回来,那个要落跑的女人呢?」"],
+ ["奥兹", "「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」"],
+ ["奥兹", "「正好,趁此机会凯伊姆先生也来享受一番如何?」"],
+ ["年轻人", "「你,你们这些家伙,要对她做什么!?」"],
+ ["", "咣!"],
+ ["", "奥兹给了他一拳。"],
+ ["", "一击即倒。"],
+ ["", "喀,咚,咯!"],
+ ["", "奥兹毫不留情地向男人的脸上踩去。"],
+ ["年轻人", "「咕······呃咳······」"],
+ ["", "折断的牙齿伴着血泡被吐出。"],
+ ["", "这份白色在鲜红色的液体中格外显眼。"],
+ ["年轻人", "「你们以为做出这种事······卫兵会坐视不理吗······」"],
+ ["奥兹", "「啊啊,不会坐视不理的」"],
+ ["奥兹", "「应该会拿出你的钱包,和我们商量如何瓜分吧」"],
+ ["年轻人", "「那,那种事······」"],
+ ["", "这在牢狱是理所当然的事。"],
+ ["奥兹", "「怎么,头一回来牢狱么?」"],
+ ["", "男人点了点头。"],
+ ["奥兹", "「为了被骗的女人而来到牢狱,真是个规矩人啊」"],
+ ["奥兹", "「······前提是,被骗的人不是你」"],
+ ["年轻人", "「你说······我被骗了?」"],
+ ["年轻人", "「那,那是怎么回事!?」"],
+ ["奥兹", "「不用急,今天晚上会好好告诉你的」"],
+ ["", "奥兹抓起男人的脸。"],
+ ["", "为引诱客人的怜悯之心而装纯,是娼妇的惯用手段。"],
+ ["", "双亲被骗而借钱,结果作为抵押而将自己卖到这里,这是典型的说法。"],
+ [
+ "",
+ "如果只是头脑发热而成为常客也就罢了,这次的男人热血过头,居然想出了要带女人私奔的计划。",
+ ],
+ [
+ "",
+ "虽然女人半开玩笑地予以拒绝,但不知天高地厚的这家伙还是拉着她逃跑了。",
+ ],
+ ["", "不过,想要逃脱追击本来就是不可能的任务。"],
+ ["", "但即便如此,这种事情还是会一再的出现。"],
+ ["", "说谎的女人和被骗的男人。"],
+ ["", "在娼馆街,这是令人看到生厌的日常的风景。"],
+ ["凯伊姆", "「我要走了」"],
+ ["奥兹", "「好的,下次再麻烦您」"],
+ ["奥兹", "「之后吉克先生会将谢礼交给您的」"],
+ ["凯伊姆", "「啊啊」"],
+ ["", "我背向奥兹走出娼馆。"],
+ ["凯伊姆", "「······?」"],
+ ["", "从远方传来微弱的歌声。"],
+ ["", "是关卡广场的方向。"],
+ ["", "对了。"],
+ ["", "今天有觐见圣女的仪式。"],
+ ["", "当代的圣女伊莲——"],
+ ["", "俗称《盲眼之圣女》,据说即使在历代的圣女中,人气也是数一数二的。"],
+ ["", "广场上的人估计相当多吧。"],
+ ["", "虽然我也想去看看她长什么样,不过要在人潮中挤来挤去就免了。"],
+ ["", "还是老老实实去菲诺列塔喝烧酒吧。"],
+ ["", "正当我这样想着的时候,一个身影自小巷的那头走来。"],
+ ["凯伊姆", "「艾莉斯」"],
+ ];
+ this.chapter01 = [
+ ["艾莉斯", "「啊,凯伊姆」"],
+ ["艾莉斯", "「正好,我还想要去找你呢」"],
+ ["艾莉斯", "「没想到凯伊姆会主动出现······这是命运吗?」"],
+ ["凯伊姆", "「显然不是吧」"],
+ ["艾莉斯", "「啊,是么」"],
+ ["", "艾莉斯挑了挑整齐的双眉,微微地哼了一声。"],
+ [
+ "",
+ "虽然是个相当引人注目的美人,但她这个将亲切儿子丢入无底深渊的性格,为自己扣了不少的分",
+ ],
+ ["", "给人印象最深的,就是那潭水般的双瞳。"],
+ ["", "在漆黑的瞳孔中,完全看不出感情的波动。"],
+ ["艾莉斯", "「喜欢我的眼睛吗?」"],
+ ["艾莉斯", "「如果想要的话就给你吧?」"],
+ ["凯伊姆", "「用不着」"],
+ ["艾莉斯", "「阿拉,可惜」"],
+ ["凯伊姆", "「那么,找我有什么事」"],
+ ["艾莉斯", "「梅尔特的钱好像被偷了」"],
+ ["凯伊姆", "「钱被偷了?都几岁了还这么没用」"],
+ ["艾莉斯", "「不要对我说啊」"],
+ ["凯伊姆", "「那家伙,该不会说要让我去抓那个小偷吧?」"],
+ ["艾莉斯", "「就是这样」"],
+ ["凯伊姆", "「笨蛋吗」"],
+ ["凯伊姆", "「如果是小钱的话,就当做是买个教训吧」"],
+ ["艾莉斯", "「说起来,被盗的是这个月的上纳金」"],
+ ["凯伊姆", "「你说什么?」"],
+ ["艾莉斯", "「用这些钱买教训,也太过奢侈了呢」"],
+ ["凯伊姆", "「知道了,我去找」"],
+ ["凯伊姆", "「小偷的特征呢」"],
+ ["艾莉斯", "「男孩子」"],
+ ["艾莉斯", "「······而且,背后有翅膀」"],
+ ["艾莉斯", "「虽然姑且是藏在身后,但是仔细观察的话是很明显的」"],
+ ["凯伊姆", "「羽化病吗」"],
+ [
+ "艾莉斯",
+ "「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」",
+ ],
+ ["凯伊姆", "「注意到他逃窜的方向了吗?」"],
+ ["艾莉斯", "「广场那边」"],
+ ["艾莉斯", "「虽然刚才《不蚀金锁》的人去追了,不过多半是······」"],
+ ["凯伊姆", "「偏偏还是广场吗」"],
+ ["艾莉斯", "「今天是觐见圣女大人的日子」"],
+ ["凯伊姆", "「我知道」"],
+ ["凯伊姆", "「尽量找找看就好」"],
+ ];
+ this.chapter02 = [
+ ["不蚀金锁成员", "「凯伊姆先生,凯伊姆先生」"],
+ ["不蚀金锁成员", "「您已经和艾莉斯大夫见过面了吗?」"],
+ ["凯伊姆", "「啊啊,所以才会追过来的」"],
+ ["凯伊姆", "「看到小偷了吗?」"],
+ [
+ "不蚀金锁成员",
+ "「没有,他向广场那边逃了过去,今天这么拥挤,我们也只能放弃了」",
+ ],
+ [
+ "不蚀金锁成员",
+ "「不过,我也只是刚好在店里所以才追了过去,并不是受人所托」",
+ ],
+ ["不蚀金锁成员", "「我已经准备撤退了」"],
+ ["不蚀金锁成员", "「凯伊姆先生还要继续追吗?」"],
+ ["凯伊姆", "「啊啊」"],
+ ["", "做完情报交换之后,我跟男人道别。"],
+ ["凯伊姆", "「和我想的一样啊······」"],
+ ["", "在牢狱中最大的广场上,聚集着看不到尽头的人群。"],
+ ["", "就算是来参见圣女祈祷,这人数也太多了点吧。"],
+ ["", "自然,我也找不到逃跑的孩子。"],
+ ["", "是混杂到人群中了吧。"],
+ ["", "如果已经从广场上脱身了的话,就更难发现了。"],
+ ["", "只好赌他还在这里了。"],
+ ["", "我先移动到了一个视野良好的地方。"],
+ ["", "从这里,一眼就可以看到人群的变化。"],
+ ["", "广场还是沸腾起来。"],
+ ["", "抬头望去,原来是在天台之上出现了一个人影"],
+ ["", "但是,与周围的期待不同,现身的是一名中年的神官。"],
+ ["", "骂声四溢。"],
+ ["", "神官则是笑着摆正衣领"],
+ ["神官", "「从现在开始,举行谒见的仪式」"],
+ [
+ "神官",
+ "「在参见那位大人之前,我希望牢狱的诸位再次思考这个《诺瓦斯·艾蒂尔》存在的意义······」",
+ ],
+ [
+ "神官",
+ "「初代圣女伊莲大人,便是也难怪这崇高的祈祷之力,令《诺瓦斯·艾蒂尔》浮在空中,拯救了我们的祖先」",
+ ],
+ [
+ "神官",
+ "「这之后的几百年来,传承了初代大人力量的历代圣女伊莲大人,让这里留在了空中」",
+ ],
+ [
+ "神官",
+ "「这座都市是被圣女大人守护的人类最后的圣域,而我们则是被选召的虔诚的信徒」",
+ ],
+ ["神官", "「怀着对圣女的感激祈祷吧,感谢圣女伊莲吧!并献上祈祷!」"],
+ ["圣女", "「不忘感谢与祈祷,神才会拯救我们」"],
+ ["圣女", "「与我一起,向神虔诚地祈祷吧」"],
+ ["", "广场上欢声雷动。"],
+ ["", "圣女没有回应喧嚣的人声,而是静静地合上双眼面向广场。"],
+ ["", "虽然感觉有些冷淡,但总比像个傻瓜似的笑着向这群人挥手要强。"],
+ ["", "她掌握着这条街道,还有在这条街上生活的人的命运。"],
+ ["", "比起揽得人气,她更想要为了街道的继续存在而献出全力。"],
+ ["", "也是为了不让《大崩落》的惨剧再度发生。"],
+ ["", "十几年前的那场悲剧。"],
+ [
+ "",
+ "虽然在我脑海中的记忆已经相当模糊,但哪怕只是稍有触及,不快的感觉都会在胸口蔓延开。",
+ ],
+ ["凯伊姆", "「······」"],
+ ["", "这时我才想起,现在不是我在这里看圣女的时候。"],
+ ["女声", "「——っ!?」"],
+ ["围观的女人", "「羽,羽化病人!?」"],
+ ["围观的中年人", "「喂,谁去叫下羽狩」"],
+ ["惊慌的观众", "「你这家伙不要靠近我,要是传染了可怎么办」"],
+ ["粗鲁的观众", "「你这小鬼赶快滚开」"],
+ ["凯伊姆", "「接下来」"],
+ ["圣女", "「发生什么事了?看上去似乎很嘈杂」"],
+ ["随从", "「似乎是某个人逃跑了······具体的我也不是很清楚」"],
+ ["神官", "「圣女大人,继续待在天台上可能会出事,请您先回到室内吧」"],
+ ["圣女", "「不用在意我,比起那个,我更关心究竟发生了什么事」"],
+ ["神官", "「对不起,我真的不知道」"],
+ ["圣女", "「······是吗」"],
+ ["男", "「恕我僭越,请准许我说明情况」"],
+ ["男", "「在来觐见的人群中出现了《羽化病》的患者」"],
+ ["男", "「围观的人群因而产生了骚动」"],
+ [
+ "男",
+ "「现在,《防疫局》已经派遣了部队。我想不久之后,他们就会安静下来了」",
+ ],
+ ["圣女", "「羽化病······」"],
+ ["男", "「怎么了?」"],
+ ["圣女", "「没什么」"],
+ ["圣女", "「辛苦了,你的名字是?」"],
+ ["男", "「属下是在防疫局任职的,鲁基乌斯· 迪斯·米利尤」"],
+ ["神官", "「噢噢,阁下就是鲁基乌斯卿吗,我听说过你的传闻」"],
+ ["神官", "「阁下是在工作上相当出色的人呢」"],
+ ["鲁基乌斯", "「不敢当」"],
+ [
+ "鲁基乌斯",
+ "「话说回来,这次是属下警备工作的失职。让圣女大人见到这不成体统的一面,请您见谅」",
+ ],
+ [
+ "圣女",
+ "「即使是目不见物的我,也能感受到聚集于此的民众数量之多。警备工作难以展开也在情理之中」",
+ ],
+ ["鲁基乌斯", "「属下不胜惶恐」"],
+ ["鲁基乌斯", "「接下来属下还要回到工作岗位上,在这里就先告退了」"],
+ ["圣女", "「鲁基乌斯先生」"],
+ ["鲁基乌斯", "「属下在」"],
+ ["圣女", "「你是怎样看待羽狩的工作的呢?」"],
+ ["神官", "「圣,圣女大人」"],
+ [
+ "鲁基乌斯",
+ "「防疫局的工作是国王陛下赐予的重要职务。属下非常荣幸能够为这个都市的繁荣尽一份微薄之力」",
+ ],
+ ["神官", "「不,不亏是鲁基乌斯卿,相当优秀的想法」"],
+ ["圣女", "「是吗。辛苦你了」"],
+ ["随从", "「圣女大人······」"],
+ ["鲁基乌斯", "「······」"],
+ ["鲁基乌斯", "「那么,属下就回岗位去了」"],
+ ];
+ this.chapter03 = [
+ ["", "从羽化病的少年纷乱的足音中,可以听得出相当的疲劳。"],
+ ["", "显然,他并没有想到我会捷足先登吧。"],
+ ["", "少年惶恐地回头看了一眼后,微微露出安心的表情,双手拄在膝盖上。。"],
+ ["凯伊姆", "「辛苦你了」"],
+ ["羽化病患少年", "「稀!?」"],
+ ["凯伊姆", "「逃到贫民区是个不错的想法」"],
+ ["羽化病患少年", "「你,你是,羽狩吗?」"],
+ ["凯伊姆", "「不是」"],
+ ["羽化病患少年", "「什,什么啊······混蛋,不要吓我啊」"],
+ ["凯伊姆", "「我对令你受惊这件事致以歉意」"],
+ ["凯伊姆", "「作为回报,麻烦你把从店里偷的钱交出来吧」"],
+ ["羽化病患少年", "「钱?你在说什么」"],
+ ["凯伊姆", "「你要找的腰上的东西,掉在你身后了」"],
+ ["羽化病患少年", "「哎?」"],
+ ["羽化病患少年", "「呃呀」"],
+ ["羽化病患少年", "「你······你这混蛋」"],
+ ["凯伊姆", "「······」"],
+ ["凯伊姆", "「把偷的钱交出来」"],
+ ["羽化病患少年", "「我不知道你在······咕」"],
+ ["羽化病患少年", "「你,你说是我偷的······有什么证据吗」"],
+ ["凯伊姆", "「你还挺倔的啊」"],
+ ["凯伊姆", "「不过,给我听好了」"],
+ ["凯伊姆", "「你偷的那些钱,是要上缴给《不蚀金锁》的上纳金」"],
+ ["凯伊姆", "「而且,钱的主人是从前和吉克颇有渊源的女人」"],
+ ["羽化病患少年", "「吉克?」"],
+ ["凯伊姆", "「他是《不蚀金锁》的主人,这么说你就明白了吧」"],
+ ["羽化病患少年", "「哎?哎?怎么会······」"],
+ ["凯伊姆", "「再问你一遍,钱在哪里?」"],
+ ["羽化病患少年", "「是,是,是,在我的怀里」"],
+ ["凯伊姆", "「你没有擅自拿掉一部分吧」"],
+ ["羽化病患少年", "「是,是的」"],
+ ["羽化病患少年", "「那,那个,您是《不蚀金锁》的人吗?」"],
+ ["凯伊姆", "「算是吧」"],
+ ["羽化病患少年", "「我什么都可以做,请您一定要帮帮我」"],
+ ["凯伊姆", "「抱歉,我并没有兴趣去帮助他人」"],
+ ["羽化病患少年", "「我什么······什么,都会做的······」"],
+ ["羽化病患少年", "「我一直都是生活在下层的」"],
+ [
+ "羽化病患少年",
+ "「可是,不知何时染上了羽化病······背后长出了翅膀······」",
+ ],
+ ["羽化病患少年", "「被寄宿工作的店赶了出来,只得流落到牢狱这里」"],
+ ["羽化病患少年", "「因为独自实在是饿的不行了,所以才会偷这些钱的」"],
+ [
+ "羽化病患少年",
+ "「我明明没有做任何坏事······为什么······会遇到这种事······」",
+ ],
+ ["凯伊姆", "「谁知道」"],
+ ["羽化病患少年", "「呜······呜呜······接下来,要对我做什么?」"],
+ ["凯伊姆", "「我要把你带到组织那里」"],
+ ["羽化病患少年", "「怎,怎么这样」"],
+ ["凯伊姆", "「不过,那样做的前提是你不是羽化病人」"],
+ ["凯伊姆", "「组织也没有笨到把羽化病人招待到家里的程度」"],
+ ["羽化病患少年", "「那么,是要放我逃走吗?」"],
+ ["凯伊姆", "「我要让你学到教训」"],
+ ["凯伊姆", "「如果换做是组织的制裁,至少要有断条胳膊的觉悟」"],
+ ["凯伊姆", "「你的运气不错」"],
+ ["羽化病患少年", "「唔······啊,是的······」"],
+ ["凯伊姆", "「滚」"],
+ ["羽化病患少年", "「非常感谢」"],
+ ["羽化病患少年", "「唔啊!?」"],
+ ["男", "「到这里就结束了,羽化病人」"],
+ ["男", "「确认他的翅膀」"],
+ ["", "趁还没有被卷入麻烦的事情之前,赶快离开这里吧"],
+ ["羽狩的队长", "「那边的那个人」"],
+ ["凯伊姆", "「······有什么事?」"],
+ ["羽狩的队长", "「可以稍微让我问几句话吗」"],
+ ["凯伊姆", "「······」"],
+ ["凯伊姆", "「啊啊,无妨」"],
+ ["羽狩的队长", "「感谢您的合作」"],
+ ["", "队长殷勤地致以谢礼。"],
+ ["", "而在他的眼前,少年的衣服已经被他的补下们扯破。"],
+ ["", "在瘦骨嶙峋的裸露后背上,长有纯白的羽翼。"],
+ ["红发的羽狩", "「副队长,确认翅膀的持有了」"],
+ ["羽狩的副队长", "「保护他」"],
+ ["羽化病患少年", "「不要······请原谅,我······」"],
+ [
+ "羽狩的副队长",
+ "「我们只是要带你去治愈院治疗羽化病,不是什么应该感到害怕的事情」",
+ ],
+ ["羽化病患少年", "「可是,可是」"],
+ ["羽狩的副队长", "「没关系的」"],
+ ["羽化病患少年", "「······哥,哥哥」"],
+ ["羽狩的副队长", "「你是羽化病人的亲属吗?」"],
+ ["凯伊姆", "「只是路人而已」"],
+ ["凯伊姆", "「顺带一提,我也没有打算找你们的麻烦」"],
+ [
+ "羽狩的副队长",
+ "「前几天,有个和你说了同样的话的人,在我们背向他的瞬间对我们发动了袭击」",
+ ],
+ ["羽狩的副队长", "「我的一个部下就此永久失去了半截胳膊」"],
+ ["凯伊姆", "「我表示同情」"],
+ ["凯伊姆", "「我马上就会消失的,这样就没问题了吧?」"],
+ ["羽狩的副队长", "「嘛,不要这么慌张」"],
+ ["", "副队长看着羽化的少年。"],
+ ["羽狩的副队长", "「你与这个人是什么关系?没有被他殴打吗?」"],
+ ["羽化病患少年", "「没,没有」"],
+ [
+ "羽狩的副队长",
+ "「如何对我们保持合作,你就可以在治愈院得到优先的治疗」",
+ ],
+ ["羽化病患少年", "「······」"],
+ ["羽化病患少年", "「那个人,是《不蚀金锁》的组织成员······」"],
+ ["羽化病患少年", "「突然说让我拿出钱来,我刚一拒绝他就打我」"],
+ ["羽狩的副队长", "「原来如此······」"],
+ [
+ "羽狩的副队长",
+ "「那位少年说你是《不蚀金锁》的一员,不知此事是否属实?」",
+ ],
+ ["凯伊姆", "「当然不是」"],
+ ["凯伊姆", "「我只是从那里接受工作而已,并不是他们的成员」"],
+ ["羽狩的副队长", "「你的意思是说,少年在说谎吗?」"],
+ ["凯伊姆", "「啊啊」"],
+ [
+ "凯伊姆",
+ "「如果你们和组织有关系的话,只要问问我是不是那里的成员就能明白事实了吧」",
+ ],
+ ["羽狩的副队长", "「就算我去询问,也无法从他们那里得到事实」"],
+ [
+ "羽狩的副队长",
+ "「《不蚀金锁》的那些人一向都不对我们合作,我对此深感困扰」",
+ ],
+ ["凯伊姆", "「真是辛苦啊」"],
+ ["羽狩的副队长", "「说的是啊」"],
+ ["羽狩的副队长", "「其实,砍下我部下胳膊的似乎也是组织的成员呢」"],
+ [
+ "羽狩的副队长",
+ "「无需如此警戒,我只是想在看守所向你咨询一些事情而已」",
+ ],
+ [
+ "羽狩的副队长",
+ "「如果能够知晓牢狱与组织的事情,我们也可以尽可能地对更多的羽化病人进行保护」",
+ ],
+ ["羽狩的副队长", "「那和整条街道的和平也是紧密相关的吧?」"],
+ ["凯伊姆", "「我知道,你们有逮捕干扰狩猎羽化病人的权力」"],
+ [
+ "凯伊姆",
+ "「但是,我没有对你们做出任何干扰,为什么要对我如此纠缠不休呢」",
+ ],
+ ["羽狩的副队长", "「那些话,我们会在看守所对你详细说明的」"],
+ ["凯伊姆", "「······」"],
+ ["", "在这里起争执的话,就会被羽狩加害。"],
+ ["", "就算能从这里脱身,今后只要碰面就会产生纠纷也是摆明的事情。"],
+ ["", "就算逃跑,也没有好的结果。"],
+ ["", "正当我想要打圆场的时候,刚才的气氛一瞬间产生了转变。"],
+ ["", "发生了什么事······"],
+ ["???", "「我认为,那位先生是正确的」"],
+ ["", "羽狩们一起回头。"],
+ ["", "而在他们视线的焦点处,"],
+ ["", "伫立着一位女性。"],
+ ["", "在端正的容颜下,代表着强烈意志的双眉十分显眼。"],
+ ["", "身体的柔软与紧紧包裹在其身上的羽狩制服,两者显得十分的不搭配。"],
+ ["", "我还是第一次看到女性的羽狩。"],
+ ["羽狩的副队长", "「队长,这是获得《不蚀金锁》情报的好机会」"],
+ [
+ "羽狩的队长",
+ "「兰格副队长,就算是为了获得情报,也不能做出恫吓的发言啊」",
+ ],
+ ["兰格副队长", "「我并没有打算去恫吓他······」"],
+ ["羽狩的队长", "「告诉我那个被砍掉胳膊的队员的名字」"],
+ ["羽狩的队长", "「我会去探望他的」"],
+ ["兰格副队长", "「那个是······」"],
+ ["羽狩的队长", "「我知道,你一直在为有所收获而努力工作」"],
+ ["羽狩的队长", "「但是,正因为我们的工作是为民众提供帮助」"],
+ ["羽狩的队长", "「所以就更不能损害人与人之间的信赖」"],
+ ["兰格副队长", "「我会铭记在心」"],
+ ["羽狩的队长", "「这位先生,我的部下失礼了」"],
+ ["凯伊姆", "「只要不对我再来一次就好」"],
+ ["羽狩的队长", "「请稍等」"],
+ ["凯伊姆", "「有什么事?」"],
+ ["羽狩的队长", "「我想确认一件事」"],
+ ["羽狩的队长", "「你真的不是《不蚀金锁》的成员吗?」"],
+ ["凯伊姆", "「真的」"],
+ ["凯伊姆", "「如果我说是的话,你有什么打算?」"],
+ ["羽狩的队长", "「我听过传闻,说他们是用依靠暴力而得的钱在生活」"],
+ ["凯伊姆", "「······这样啊」"],
+ ["凯伊姆", "「如果能有收获就好了啊」"],
+ ];
+ 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;